]> icculus.org git repositories - divverent/darkplaces.git/blob - snd_win.c
Merge commit 'origin/master' into div0/traceboxbox
[divverent/darkplaces.git] / snd_win.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 #include "quakedef.h"
21 #include "snd_main.h"
22
23 #ifdef SUPPORTDIRECTX
24 #ifndef DIRECTSOUND_VERSION
25 #       define DIRECTSOUND_VERSION 0x0500  /* Version 5.0 */
26 #endif
27 #endif
28 #include <windows.h>
29 #include <mmsystem.h>
30 #ifdef SUPPORTDIRECTX
31 #include <dsound.h>
32 #endif
33
34 // ==============================================================================
35
36 #ifndef _WAVEFORMATEXTENSIBLE_
37 #define _WAVEFORMATEXTENSIBLE_
38 typedef struct
39 {
40         WAVEFORMATEX Format;
41         union
42         {
43                 WORD wValidBitsPerSample;       // bits of precision
44                 WORD wSamplesPerBlock;          // valid if wBitsPerSample==0
45                 WORD wReserved;                         // If neither applies, set to zero
46         } Samples;
47     DWORD dwChannelMask;                        // which channels are present in stream
48     GUID SubFormat;
49 } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
50 #endif
51
52 #if !defined(WAVE_FORMAT_EXTENSIBLE)
53 #       define WAVE_FORMAT_EXTENSIBLE 0xFFFE
54 #endif
55
56 // Some speaker positions
57 #ifndef SPEAKER_FRONT_LEFT
58 #       define SPEAKER_FRONT_LEFT                               0x1
59 #       define SPEAKER_FRONT_RIGHT                              0x2
60 #       define SPEAKER_FRONT_CENTER                             0x4
61 #       define SPEAKER_LOW_FREQUENCY                    0x8
62 #       define SPEAKER_BACK_LEFT                                0x10
63 #       define SPEAKER_BACK_RIGHT                               0x20
64 #       define SPEAKER_FRONT_LEFT_OF_CENTER             0x40
65 #       define SPEAKER_FRONT_RIGHT_OF_CENTER    0x80
66 // ... we never use the other values
67 #endif
68
69 // KSDATAFORMAT_SUBTYPE_PCM = GUID "00000001-0000-0010-8000-00aa00389b71"
70 static const GUID MY_KSDATAFORMAT_SUBTYPE_PCM =
71 {
72         0x00000001,
73         0x0000,
74         0x0010,
75         {
76                 0x80, 0x00,
77                 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
78         }
79 };
80
81
82 // ==============================================================================
83
84 extern HWND mainwindow;
85 static cvar_t snd_wav_partitionsize = {CVAR_SAVE, "snd_wav_partitionsize", "1024", "controls sound delay in samples, values too low will cause crackling, too high will cause delayed sounds"};
86 static qboolean sndsys_registeredcvars = false;
87
88 #ifdef SUPPORTDIRECTX
89 HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);
90 #endif
91
92 // Wave output: queue of this many sound buffers to play, reused cyclically
93 #define WAV_BUFFERS             16
94 #define WAV_MASK                (WAV_BUFFERS - 1)
95 static unsigned int wav_buffer_size;
96
97 // DirectSound output: 64KB in 1 buffer
98 //#define SECONDARY_BUFFER_SIZE(fmt_ptr) ((fmt_ptr)->width * (fmt_ptr)->channels * (fmt_ptr)->speed / 2)
99 // LordHavoc: changed this to be a multiple of 32768
100 #define SECONDARY_BUFFER_SIZE(fmt_ptr) ((fmt_ptr)->channels * 32768)
101
102 typedef enum sndinitstat_e {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat;
103
104 #ifdef SUPPORTDIRECTX
105 static qboolean dsound_init;
106 static unsigned int dsound_time;
107 static qboolean primary_format_set;
108 #endif
109
110 static qboolean wav_init;
111
112 static int      snd_sent, snd_completed;
113
114 static int prev_painted;
115 static unsigned int paintpot;
116
117
118
119 /*
120  * Global variables. Must be visible to window-procedure function
121  *  so it can unlock and free the data block after it has been played.
122  */
123
124 HANDLE          hData;
125 HPSTR           lpData, lpData2;
126
127 HGLOBAL         hWaveHdr;
128 LPWAVEHDR       lpWaveHdr;
129
130 HWAVEOUT    hWaveOut;
131
132 WAVEOUTCAPS     wavecaps;
133
134 DWORD   gSndBufSize;
135
136 DWORD   dwStartTime;
137
138 #ifdef SUPPORTDIRECTX
139 LPDIRECTSOUND pDS;
140 LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
141
142 HINSTANCE hInstDS;
143 #endif
144
145 qboolean SNDDMA_InitWav (void);
146 #ifdef SUPPORTDIRECTX
147 sndinitstat SNDDMA_InitDirect (void);
148 #endif
149
150
151 /*
152 ==================
153 SndSys_BuildWaveFormat
154 ==================
155 */
156 static qboolean SndSys_BuildWaveFormat (const snd_format_t* requested, WAVEFORMATEXTENSIBLE* fmt_ptr)
157 {
158         WAVEFORMATEX* pfmtex;
159
160         memset (fmt_ptr, 0, sizeof(*fmt_ptr));
161
162         pfmtex = &fmt_ptr->Format;
163         pfmtex->nChannels = requested->channels;
164     pfmtex->wBitsPerSample = requested->width * 8;
165     pfmtex->nSamplesPerSec = requested->speed;
166         pfmtex->nBlockAlign = pfmtex->nChannels * pfmtex->wBitsPerSample / 8;
167         pfmtex->nAvgBytesPerSec = pfmtex->nSamplesPerSec * pfmtex->nBlockAlign;
168
169         // LordHavoc: disabled this WAVE_FORMAT_EXTENSIBLE support because it does not seem to be working
170 #if 0
171         if (requested->channels <= 2)
172         {
173 #endif
174                 pfmtex->wFormatTag = WAVE_FORMAT_PCM;
175                 pfmtex->cbSize = 0;
176 #if 0
177         }
178         else
179         {
180                 pfmtex->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
181                 pfmtex->cbSize = sizeof(*fmt_ptr) - sizeof(fmt_ptr->Format);
182                 fmt_ptr->Samples.wValidBitsPerSample = fmt_ptr->Format.wBitsPerSample;
183                 fmt_ptr->SubFormat = MY_KSDATAFORMAT_SUBTYPE_PCM;
184
185                 // Build the channel mask
186                 fmt_ptr->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
187                 switch (requested->channels)
188                 {
189                         case 8:
190                                 fmt_ptr->dwChannelMask |= SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER;
191                                 // no break
192                         case 6:
193                                 fmt_ptr->dwChannelMask |= SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY;
194                                 // no break
195                         case 4:
196                                 fmt_ptr->dwChannelMask |= SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
197                                 break;
198
199                         default:
200                                 Con_Printf("SndSys_BuildWaveFormat: invalid number of channels (%hu)\n", requested->channels);
201                                 return false;
202                 }
203         }
204 #endif
205
206         return true;
207 }
208
209
210 #ifdef SUPPORTDIRECTX
211 /*
212 ==================
213 SndSys_InitDirectSound
214
215 DirectSound 5 support
216 ==================
217 */
218 static sndinitstat SndSys_InitDirectSound (const snd_format_t* requested)
219 {
220         DSBUFFERDESC                    dsbuf;
221         DSBCAPS                                 dsbcaps;
222         DWORD                                   dwSize;
223         DSCAPS                                  dscaps;
224         WAVEFORMATEXTENSIBLE    format, pformat;
225         HRESULT                                 hresult;
226         int                                             reps;
227
228         if (! SndSys_BuildWaveFormat(requested, &format))
229                 return SIS_FAILURE;
230
231         if (!hInstDS)
232         {
233                 hInstDS = LoadLibrary("dsound.dll");
234
235                 if (hInstDS == NULL)
236                 {
237                         Con_Print("Couldn't load dsound.dll\n");
238                         return SIS_FAILURE;
239                 }
240
241                 pDirectSoundCreate = (HRESULT (__stdcall *)(GUID *, LPDIRECTSOUND *,IUnknown *))GetProcAddress(hInstDS,"DirectSoundCreate");
242
243                 if (!pDirectSoundCreate)
244                 {
245                         Con_Print("Couldn't get DS proc addr\n");
246                         return SIS_FAILURE;
247                 }
248         }
249
250         while ((hresult = pDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK)
251         {
252                 if (hresult != DSERR_ALLOCATED)
253                 {
254                         Con_Print("DirectSound create failed\n");
255                         return SIS_FAILURE;
256                 }
257
258                 if (MessageBox (NULL,
259                                                 "The sound hardware is in use by another app.\n\n"
260                                             "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
261                                                 "Sound not available",
262                                                 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
263                 {
264                         Con_Print("DirectSoundCreate failure\n  hardware already in use\n");
265                         return SIS_NOTAVAIL;
266                 }
267         }
268
269         dscaps.dwSize = sizeof(dscaps);
270
271         if (DS_OK != IDirectSound_GetCaps (pDS, &dscaps))
272         {
273                 Con_Print("Couldn't get DS caps\n");
274         }
275
276         if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
277         {
278                 Con_Print("No DirectSound driver installed\n");
279                 SndSys_Shutdown ();
280                 return SIS_FAILURE;
281         }
282
283         if (DS_OK != IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE))
284         {
285                 Con_Print("Set coop level failed\n");
286                 SndSys_Shutdown ();
287                 return SIS_FAILURE;
288         }
289
290         // get access to the primary buffer, if possible, so we can set the
291         // sound hardware format
292         memset (&dsbuf, 0, sizeof(dsbuf));
293         dsbuf.dwSize = sizeof(DSBUFFERDESC);
294         dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER;
295         dsbuf.dwBufferBytes = 0;
296         dsbuf.lpwfxFormat = NULL;
297
298         memset(&dsbcaps, 0, sizeof(dsbcaps));
299         dsbcaps.dwSize = sizeof(dsbcaps);
300         primary_format_set = false;
301
302 // COMMANDLINEOPTION: Windows DirectSound: -snoforceformat uses the format that DirectSound returns, rather than forcing it
303         if (!COM_CheckParm ("-snoforceformat"))
304         {
305                 if (DS_OK == IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL))
306                 {
307                         pformat = format;
308
309                         if (DS_OK != IDirectSoundBuffer_SetFormat (pDSPBuf, (WAVEFORMATEX*)&pformat))
310                         {
311                                 Con_Print("Set primary sound buffer format: no\n");
312                         }
313                         else
314                         {
315                                 Con_Print("Set primary sound buffer format: yes\n");
316
317                                 primary_format_set = true;
318                         }
319                 }
320         }
321
322 // COMMANDLINEOPTION: Windows DirectSound: -primarysound locks the sound hardware for exclusive use
323         if (!primary_format_set || !COM_CheckParm ("-primarysound"))
324         {
325                 HRESULT result;
326
327                 // create the secondary buffer we'll actually work with
328                 memset (&dsbuf, 0, sizeof(dsbuf));
329                 dsbuf.dwSize = sizeof(DSBUFFERDESC);
330                 dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE;
331                 dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE(requested);
332                 dsbuf.lpwfxFormat = (WAVEFORMATEX*)&format;
333
334                 memset(&dsbcaps, 0, sizeof(dsbcaps));
335                 dsbcaps.dwSize = sizeof(dsbcaps);
336
337                 result = IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL);
338                 if (result != DS_OK ||
339                         requested->channels != format.Format.nChannels ||
340                         requested->width != format.Format.wBitsPerSample / 8 ||
341                         requested->speed != format.Format.nSamplesPerSec)
342                 {
343                         Con_Printf("DS:CreateSoundBuffer Failed (%d): channels=%u, width=%u, speed=%u\n",
344                                            (int)result, (unsigned)format.Format.nChannels, (unsigned)format.Format.wBitsPerSample / 8, (unsigned)format.Format.nSamplesPerSec);
345                         SndSys_Shutdown ();
346                         return SIS_FAILURE;
347                 }
348
349                 if (DS_OK != IDirectSoundBuffer_GetCaps (pDSBuf, &dsbcaps))
350                 {
351                         Con_Print("DS:GetCaps failed\n");
352                         SndSys_Shutdown ();
353                         return SIS_FAILURE;
354                 }
355
356                 Con_Print("Using secondary sound buffer\n");
357         }
358         else
359         {
360                 if (DS_OK != IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY))
361                 {
362                         Con_Print("Set coop level failed\n");
363                         SndSys_Shutdown ();
364                         return SIS_FAILURE;
365                 }
366
367                 if (DS_OK != IDirectSoundBuffer_GetCaps (pDSPBuf, &dsbcaps))
368                 {
369                         Con_Print("DS:GetCaps failed\n");
370                         return SIS_FAILURE;
371                 }
372
373                 pDSBuf = pDSPBuf;
374                 Con_Print("Using primary sound buffer\n");
375         }
376
377         // Make sure mixer is active
378         IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
379
380         Con_Printf("   %d channel(s)\n"
381                                 "   %d bits/sample\n"
382                                 "   %d samples/sec\n",
383                                 requested->channels, requested->width * 8, requested->speed);
384
385         gSndBufSize = dsbcaps.dwBufferBytes;
386
387         // initialize the buffer
388         reps = 0;
389
390         while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK)
391         {
392                 if (hresult != DSERR_BUFFERLOST)
393                 {
394                         Con_Print("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n");
395                         SndSys_Shutdown ();
396                         return SIS_FAILURE;
397                 }
398
399                 if (++reps > 10000)
400                 {
401                         Con_Print("SNDDMA_InitDirect: DS: couldn't restore buffer\n");
402                         SndSys_Shutdown ();
403                         return SIS_FAILURE;
404                 }
405
406         }
407
408         memset(lpData, 0, dwSize);
409         IDirectSoundBuffer_Unlock(pDSBuf, lpData, dwSize, NULL, 0);
410
411         IDirectSoundBuffer_Stop(pDSBuf);
412         IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
413
414         dwStartTime = 0;
415         dsound_time = 0;
416         snd_renderbuffer = Snd_CreateRingBuffer(requested, gSndBufSize / (requested->width * requested->channels), lpData);
417
418         dsound_init = true;
419
420         return SIS_SUCCESS;
421 }
422 #endif
423
424
425 /*
426 ==================
427 SndSys_InitMmsystem
428
429 Crappy windows multimedia base
430 ==================
431 */
432 static qboolean SndSys_InitMmsystem (const snd_format_t* requested)
433 {
434         WAVEFORMATEXTENSIBLE format;
435         int                             i;
436         HRESULT                 hr;
437
438         if (! SndSys_BuildWaveFormat(requested, &format))
439                 return false;
440
441         // Open a waveform device for output using window callback
442         while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER, (WAVEFORMATEX*)&format,
443                                         0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)
444         {
445                 if (hr != MMSYSERR_ALLOCATED)
446                 {
447                         Con_Print("waveOutOpen failed\n");
448                         return false;
449                 }
450
451                 if (MessageBox (NULL,
452                                                 "The sound hardware is in use by another app.\n\n"
453                                             "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
454                                                 "Sound not available",
455                                                 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
456                 {
457                         Con_Print("waveOutOpen failure;\n  hardware already in use\n");
458                         return false;
459                 }
460         }
461
462         wav_buffer_size = bound(128, snd_wav_partitionsize.integer, 8192) * requested->channels * requested->width;
463
464         /*
465          * Allocate and lock memory for the waveform data. The memory
466          * for waveform data must be globally allocated with
467          * GMEM_MOVEABLE and GMEM_SHARE flags.
468          */
469         gSndBufSize = WAV_BUFFERS * wav_buffer_size;
470         hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize);
471         if (!hData)
472         {
473                 Con_Print("Sound: Out of memory.\n");
474                 SndSys_Shutdown ();
475                 return false;
476         }
477         lpData = (HPSTR)GlobalLock(hData);
478         if (!lpData)
479         {
480                 Con_Print("Sound: Failed to lock.\n");
481                 SndSys_Shutdown ();
482                 return false;
483         }
484         memset (lpData, 0, gSndBufSize);
485
486         /*
487          * Allocate and lock memory for the header. This memory must
488          * also be globally allocated with GMEM_MOVEABLE and
489          * GMEM_SHARE flags.
490          */
491         hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS);
492
493         if (hWaveHdr == NULL)
494         {
495                 Con_Print("Sound: Failed to Alloc header.\n");
496                 SndSys_Shutdown ();
497                 return false;
498         }
499
500         lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
501
502         if (lpWaveHdr == NULL)
503         {
504                 Con_Print("Sound: Failed to lock header.\n");
505                 SndSys_Shutdown ();
506                 return false;
507         }
508
509         memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
510
511         // After allocation, set up and prepare headers
512         for (i=0 ; i<WAV_BUFFERS ; i++)
513         {
514                 lpWaveHdr[i].dwBufferLength = wav_buffer_size;
515                 lpWaveHdr[i].lpData = lpData + i * wav_buffer_size;
516
517                 if (waveOutPrepareHeader(hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)) != MMSYSERR_NOERROR)
518                 {
519                         Con_Print("Sound: failed to prepare wave headers\n");
520                         SndSys_Shutdown ();
521                         return false;
522                 }
523         }
524
525         snd_renderbuffer = Snd_CreateRingBuffer(requested, gSndBufSize / (requested->width * requested->channels), lpData);
526
527         prev_painted = 0;
528         paintpot = 0;
529
530         snd_sent = 0;
531         snd_completed = 0;
532
533         wav_init = true;
534
535         return true;
536 }
537
538
539 /*
540 ====================
541 SndSys_Init
542
543 Create "snd_renderbuffer" with the proper sound format if the call is successful
544 May return a suggested format if the requested format isn't available
545 ====================
546 */
547 qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
548 {
549 #ifdef SUPPORTDIRECTX
550         qboolean wavonly;
551 #endif
552         sndinitstat     stat;
553
554         if (!sndsys_registeredcvars)
555         {
556                 sndsys_registeredcvars = true;
557                 Cvar_RegisterVariable(&snd_wav_partitionsize);
558         }
559
560         Con_Print ("SndSys_Init: using the Win32 module\n");
561
562 #ifdef SUPPORTDIRECTX
563 // COMMANDLINEOPTION: Windows Sound: -wavonly uses wave sound instead of DirectSound
564         wavonly = (COM_CheckParm ("-wavonly") != 0);
565         dsound_init = false;
566 #endif
567         wav_init = false;
568
569         stat = SIS_FAILURE;     // assume DirectSound won't initialize
570
571 #ifdef SUPPORTDIRECTX
572         // Init DirectSound
573         if (!wavonly)
574         {
575                 stat = SndSys_InitDirectSound (requested);
576
577                 if (stat == SIS_SUCCESS)
578                         Con_Print("DirectSound initialized\n");
579                 else
580                         Con_Print("DirectSound failed to init\n");
581         }
582 #endif
583
584         // if DirectSound didn't succeed in initializing, try to initialize
585         // waveOut sound, unless DirectSound failed because the hardware is
586         // already allocated (in which case the user has already chosen not
587         // to have sound)
588 #ifdef SUPPORTDIRECTX
589         if (!dsound_init && (stat != SIS_NOTAVAIL))
590 #endif
591         {
592                 if (SndSys_InitMmsystem (requested))
593                         Con_Print("Wave sound (MMSYSTEM) initialized\n");
594                 else
595                         Con_Print("Wave sound failed to init\n");
596         }
597
598 #ifdef SUPPORTDIRECTX
599         return (dsound_init || wav_init);
600 #else
601         return wav_init;
602 #endif
603 }
604
605
606 /*
607 ====================
608 SndSys_Shutdown
609
610 Stop the sound card, delete "snd_renderbuffer" and free its other resources
611 ====================
612 */
613 void SndSys_Shutdown (void)
614 {
615 #ifdef SUPPORTDIRECTX
616         if (pDSBuf)
617         {
618                 IDirectSoundBuffer_Stop(pDSBuf);
619                 IDirectSoundBuffer_Release(pDSBuf);
620         }
621
622         // only release primary buffer if it's not also the mixing buffer we just released
623         if (pDSPBuf && (pDSBuf != pDSPBuf))
624         {
625                 IDirectSoundBuffer_Release(pDSPBuf);
626         }
627
628         if (pDS)
629         {
630                 IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL);
631                 IDirectSound_Release(pDS);
632         }
633 #endif
634
635         if (hWaveOut)
636         {
637                 waveOutReset (hWaveOut);
638
639                 if (lpWaveHdr)
640                 {
641                         unsigned int i;
642
643                         for (i=0 ; i< WAV_BUFFERS ; i++)
644                                 waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR));
645                 }
646
647                 waveOutClose (hWaveOut);
648
649                 if (hWaveHdr)
650                 {
651                         GlobalUnlock(hWaveHdr);
652                         GlobalFree(hWaveHdr);
653                 }
654
655                 if (hData)
656                 {
657                         GlobalUnlock(hData);
658                         GlobalFree(hData);
659                 }
660         }
661
662         if (snd_renderbuffer != NULL)
663         {
664                 Mem_Free(snd_renderbuffer);
665                 snd_renderbuffer = NULL;
666         }
667
668 #ifdef SUPPORTDIRECTX
669         pDS = NULL;
670         pDSBuf = NULL;
671         pDSPBuf = NULL;
672         dsound_init = false;
673 #endif
674         hWaveOut = 0;
675         hData = 0;
676         hWaveHdr = 0;
677         lpData = NULL;
678         lpWaveHdr = NULL;
679         wav_init = false;
680 }
681
682
683 /*
684 ====================
685 SndSys_Submit
686
687 Submit the contents of "snd_renderbuffer" to the sound card
688 ====================
689 */
690 void SndSys_Submit (void)
691 {
692         LPWAVEHDR       h;
693         int                     wResult;
694
695         // DirectSound doesn't need this
696         if (!wav_init)
697                 return;
698
699         paintpot += (snd_renderbuffer->endframe - prev_painted) * snd_renderbuffer->format.channels * snd_renderbuffer->format.width;
700         if (paintpot > WAV_BUFFERS * wav_buffer_size)
701                 paintpot = WAV_BUFFERS * wav_buffer_size;
702         prev_painted = snd_renderbuffer->endframe;
703
704         // submit new sound blocks
705         while (paintpot > wav_buffer_size)
706         {
707                 h = lpWaveHdr + (snd_sent & WAV_MASK);
708
709                 /*
710                  * Now the data block can be sent to the output device. The
711                  * waveOutWrite function returns immediately and waveform
712                  * data is sent to the output device in the background.
713                  */
714                 wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR));
715                 if (wResult == MMSYSERR_NOERROR)
716                         snd_sent++;
717                 else if (wResult == WAVERR_STILLPLAYING)
718                 {
719                         if(developer_insane.integer)
720                                 Con_DPrint("waveOutWrite failed (too much sound data)\n");
721                         //h->dwFlags |= WHDR_DONE;
722                         //snd_sent++;
723                 }
724                 else
725                 {
726                         Con_Printf("waveOutWrite failed, error code %d\n", (int) wResult);
727                         SndSys_Shutdown ();
728                         return;
729                 }
730
731                 paintpot -= wav_buffer_size;
732         }
733
734 }
735
736
737 /*
738 ====================
739 SndSys_GetSoundTime
740
741 Returns the number of sample frames consumed since the sound started
742 ====================
743 */
744 unsigned int SndSys_GetSoundTime (void)
745 {
746         unsigned int factor;
747
748         factor = snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
749
750 #ifdef SUPPORTDIRECTX
751         if (dsound_init)
752         {
753                 DWORD dwTime;
754                 unsigned int diff;
755
756                 IDirectSoundBuffer_GetCurrentPosition(pDSBuf, &dwTime, NULL);
757                 diff = (unsigned int)(dwTime - dwStartTime) % (unsigned int)gSndBufSize;
758                 dwStartTime = dwTime;
759
760                 dsound_time += diff / factor;
761                 return dsound_time;
762         }
763 #endif
764
765         if (wav_init)
766         {
767                 // Find which sound blocks have completed
768                 for (;;)
769                 {
770                         if (snd_completed == snd_sent)
771                         {
772                         //      Con_DPrint("Sound overrun\n");
773                                 break;
774                         }
775
776                         if (!(lpWaveHdr[snd_completed & WAV_MASK].dwFlags & WHDR_DONE))
777                                 break;
778
779                         snd_completed++;        // this buffer has been played
780                 }
781
782                 return (snd_completed * wav_buffer_size) / factor;
783
784                 /*
785                  * S_PaintAndSubmit: WARNING: newsoundtime (soundtime (275 < 134217707)
786                  * apparently this sound time wraps quite early?
787                 {
788                 MMRESULT res;
789                 MMTIME mmtime;
790
791                 mmtime.wType = TIME_SAMPLES;
792                 res = waveOutGetPosition(hWaveOut, &mmtime, sizeof(mmtime));
793                 if(res == MMSYSERR_NOERROR)
794                         return mmtime.u.sample;
795                 }
796                 */
797         }
798
799         return 0;
800 }
801
802
803 #ifdef SUPPORTDIRECTX
804 static DWORD dsound_dwSize;
805 static DWORD dsound_dwSize2;
806 static DWORD *dsound_pbuf;
807 static DWORD *dsound_pbuf2;
808 #endif
809
810 /*
811 ====================
812 SndSys_LockRenderBuffer
813
814 Get the exclusive lock on "snd_renderbuffer"
815 ====================
816 */
817 qboolean SndSys_LockRenderBuffer (void)
818 {
819 #ifdef SUPPORTDIRECTX
820         int reps;
821         HRESULT hresult;
822         DWORD   dwStatus;
823
824         if (pDSBuf)
825         {
826                 // if the buffer was lost or stopped, restore it and/or restart it
827                 if (IDirectSoundBuffer_GetStatus (pDSBuf, &dwStatus) != DS_OK)
828                         Con_Print("Couldn't get sound buffer status\n");
829
830                 if (dwStatus & DSBSTATUS_BUFFERLOST)
831                 {
832                         Con_Print("DSound buffer is lost!!\n");
833                         IDirectSoundBuffer_Restore (pDSBuf);
834                 }
835
836                 if (!(dwStatus & DSBSTATUS_PLAYING))
837                         IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
838
839                 reps = 0;
840
841                 while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&dsound_pbuf, &dsound_dwSize, (LPVOID*)&dsound_pbuf2, &dsound_dwSize2, 0)) != DS_OK)
842                 {
843                         if (hresult != DSERR_BUFFERLOST)
844                         {
845                                 Con_Print("S_LockBuffer: DS: Lock Sound Buffer Failed\n");
846                                 S_Shutdown ();
847                                 S_Startup ();
848                                 return false;
849                         }
850
851                         if (++reps > 10000)
852                         {
853                                 Con_Print("S_LockBuffer: DS: couldn't restore buffer\n");
854                                 S_Shutdown ();
855                                 S_Startup ();
856                                 return false;
857                         }
858                 }
859
860                 if ((void*)dsound_pbuf != snd_renderbuffer->ring)
861                         Sys_Error("SndSys_LockRenderBuffer: the ring address has changed!!!\n");
862                 return true;
863         }
864 #endif
865
866         return wav_init;
867 }
868
869
870 /*
871 ====================
872 SndSys_UnlockRenderBuffer
873
874 Release the exclusive lock on "snd_renderbuffer"
875 ====================
876 */
877 void SndSys_UnlockRenderBuffer (void)
878 {
879 #ifdef SUPPORTDIRECTX
880         if (pDSBuf)
881                 IDirectSoundBuffer_Unlock(pDSBuf, dsound_pbuf, dsound_dwSize, dsound_pbuf2, dsound_dwSize2);
882 #endif
883 }
884
885 /*
886 ====================
887 SndSys_SendKeyEvents
888
889 Send keyboard events originating from the sound system (e.g. MIDI)
890 ====================
891 */
892 void SndSys_SendKeyEvents(void)
893 {
894         // not supported
895 }