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