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