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