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