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