]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - snd_win.c
revised commandline option comments for better categorization when sorted
[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 <windows.h>
22 #include <dsound.h>
23
24 extern HWND mainwindow;
25
26 #define iDirectSoundCreate(a,b,c)       pDirectSoundCreate(a,b,c)
27
28 HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);
29
30 // 64K is > 1 second at 16-bit, 22050 Hz
31 #define WAV_BUFFERS                             64
32 #define WAV_MASK                                0x3F
33 #define WAV_BUFFER_SIZE                 0x0400
34 #define SECONDARY_BUFFER_SIZE   0x10000
35
36 typedef enum {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat;
37
38 static qboolean wavonly;
39 static qboolean dsound_init;
40 static qboolean wav_init;
41 static qboolean snd_firsttime = true, snd_isdirect, snd_iswave;
42 static qboolean primary_format_set;
43
44 static int      sample16;
45 static int      snd_sent, snd_completed;
46
47
48 /*
49  * Global variables. Must be visible to window-procedure function
50  *  so it can unlock and free the data block after it has been played.
51  */
52
53 HANDLE          hData;
54 HPSTR           lpData, lpData2;
55
56 HGLOBAL         hWaveHdr;
57 LPWAVEHDR       lpWaveHdr;
58
59 HWAVEOUT    hWaveOut;
60
61 WAVEOUTCAPS     wavecaps;
62
63 DWORD   gSndBufSize;
64
65 MMTIME          mmstarttime;
66
67 LPDIRECTSOUND pDS;
68 LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
69
70 HINSTANCE hInstDS;
71
72 qboolean SNDDMA_InitWav (void);
73 sndinitstat SNDDMA_InitDirect (void);
74
75 /*
76 ==================
77 S_BlockSound
78 ==================
79 */
80 void S_BlockSound (void)
81 {
82
83 // DirectSound takes care of blocking itself
84         if (snd_iswave)
85         {
86                 snd_blocked++;
87
88                 if (snd_blocked == 1)
89                 {
90                         waveOutReset (hWaveOut);
91                 }
92         }
93 }
94
95
96 /*
97 ==================
98 S_UnblockSound
99 ==================
100 */
101 void S_UnblockSound (void)
102 {
103
104 // DirectSound takes care of blocking itself
105         if (snd_iswave)
106         {
107                 snd_blocked--;
108         }
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, dwWrite;
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_SafePrint("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_SafePrint("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_SafePrint("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_SafePrint("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_SafePrint("Couldn't get DS caps\n");
258         }
259
260         if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
261         {
262                 Con_SafePrint("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_SafePrint("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_SafePrint("Set primary sound buffer format: no\n");
297                         }
298                         else
299                         {
300                                 if (snd_firsttime)
301                                         Con_SafePrint("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_SafePrint("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_SafePrint("DS:GetCaps failed\n");
335                         FreeSound ();
336                         return SIS_FAILURE;
337                 }
338
339                 if (snd_firsttime)
340                         Con_SafePrint("Using secondary sound buffer\n");
341         }
342         else
343         {
344                 if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY))
345                 {
346                         Con_SafePrint("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_SafePrint("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_SafePrintf("   %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_SafePrint("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n");
380                         FreeSound ();
381                         return SIS_FAILURE;
382                 }
383
384                 if (++reps > 10000)
385                 {
386                         Con_SafePrint("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, &mmstarttime.u.sample, &dwWrite);
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         sample16 = shm->format.width - 1;
408
409         dsound_init = true;
410
411         return SIS_SUCCESS;
412 }
413
414
415 /*
416 ==================
417 SNDDM_InitWav
418
419 Crappy windows multimedia base
420 ==================
421 */
422 qboolean SNDDMA_InitWav (void)
423 {
424         WAVEFORMATEX  format;
425         int                             i;
426         HRESULT                 hr;
427
428         snd_sent = 0;
429         snd_completed = 0;
430
431         memset((void *)shm, 0, sizeof(*shm));
432         shm->format.channels = 2;
433         shm->format.width = 2;
434 // COMMANDLINEOPTION: Windows Sound: -sndspeed <hz> chooses 44100 hz, 22100 hz, or 11025 hz sound output rate
435         i = COM_CheckParm ("-sndspeed"); // LordHavoc: -sndspeed option
436         if (i && i != (com_argc - 1))
437                 shm->format.speed = atoi(com_argv[i+1]);
438         else
439                 shm->format.speed = 44100;
440
441         memset (&format, 0, sizeof(format));
442         format.wFormatTag = WAVE_FORMAT_PCM;
443         format.nChannels = shm->format.channels;
444         format.wBitsPerSample = shm->format.width * 8;
445         format.nSamplesPerSec = shm->format.speed;
446         format.nBlockAlign = format.nChannels
447                 *format.wBitsPerSample / 8;
448         format.cbSize = 0;
449         format.nAvgBytesPerSec = format.nSamplesPerSec
450                 *format.nBlockAlign;
451
452         /* Open a waveform device for output using window callback. */
453         while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER,
454                                         &format,
455                                         0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)
456         {
457                 if (hr != MMSYSERR_ALLOCATED)
458                 {
459                         Con_SafePrint("waveOutOpen failed\n");
460                         return false;
461                 }
462
463                 if (MessageBox (NULL,
464                                                 "The sound hardware is in use by another app.\n\n"
465                                             "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
466                                                 "Sound not available",
467                                                 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
468                 {
469                         Con_SafePrint("waveOutOpen failure;\n  hardware already in use\n");
470                         return false;
471                 }
472         }
473
474         /*
475          * Allocate and lock memory for the waveform data. The memory
476          * for waveform data must be globally allocated with
477          * GMEM_MOVEABLE and GMEM_SHARE flags.
478
479         */
480         gSndBufSize = WAV_BUFFERS*WAV_BUFFER_SIZE;
481         hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize);
482         if (!hData)
483         {
484                 Con_SafePrint("Sound: Out of memory.\n");
485                 FreeSound ();
486                 return false;
487         }
488         lpData = GlobalLock(hData);
489         if (!lpData)
490         {
491                 Con_SafePrint("Sound: Failed to lock.\n");
492                 FreeSound ();
493                 return false;
494         }
495         memset (lpData, 0, gSndBufSize);
496
497         /*
498          * Allocate and lock memory for the header. This memory must
499          * also be globally allocated with GMEM_MOVEABLE and
500          * GMEM_SHARE flags.
501          */
502         hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
503                 (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS);
504
505         if (hWaveHdr == NULL)
506         {
507                 Con_SafePrint("Sound: Failed to Alloc header.\n");
508                 FreeSound ();
509                 return false;
510         }
511
512         lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
513
514         if (lpWaveHdr == NULL)
515         {
516                 Con_SafePrint("Sound: Failed to lock header.\n");
517                 FreeSound ();
518                 return false;
519         }
520
521         memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
522
523         /* After allocation, set up and prepare headers. */
524         for (i=0 ; i<WAV_BUFFERS ; i++)
525         {
526                 lpWaveHdr[i].dwBufferLength = WAV_BUFFER_SIZE;
527                 lpWaveHdr[i].lpData = lpData + i*WAV_BUFFER_SIZE;
528
529                 if (waveOutPrepareHeader(hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)) !=
530                                 MMSYSERR_NOERROR)
531                 {
532                         Con_SafePrint("Sound: failed to prepare wave headers\n");
533                         FreeSound ();
534                         return false;
535                 }
536         }
537
538         shm->samples = gSndBufSize / shm->format.width;
539         shm->samplepos = 0;
540         shm->buffer = (unsigned char *) lpData;
541         sample16 = shm->format.width - 1;
542
543         wav_init = true;
544
545         return true;
546 }
547
548 /*
549 ==================
550 SNDDMA_Init
551
552 Try to find a sound device to mix for.
553 Returns false if nothing is found.
554 ==================
555 */
556
557 qboolean SNDDMA_Init(void)
558 {
559         sndinitstat     stat;
560
561 // COMMANDLINEOPTION: Windows Sound: -wavonly uses wave sound instead of DirectSound
562         if (COM_CheckParm ("-wavonly"))
563                 wavonly = true;
564
565         dsound_init = wav_init = 0;
566
567         stat = SIS_FAILURE;     // assume DirectSound won't initialize
568
569         /* Init DirectSound */
570         if (!wavonly)
571         {
572                 if (snd_firsttime || snd_isdirect)
573                 {
574                         stat = SNDDMA_InitDirect ();
575
576                         if (stat == SIS_SUCCESS)
577                         {
578                                 snd_isdirect = true;
579
580                                 if (snd_firsttime)
581                                         Con_SafePrint("DirectSound initialized\n");
582                         }
583                         else
584                         {
585                                 snd_isdirect = false;
586                                 Con_SafePrint("DirectSound failed to init\n");
587                         }
588                 }
589         }
590
591 // if DirectSound didn't succeed in initializing, try to initialize
592 // waveOut sound, unless DirectSound failed because the hardware is
593 // already allocated (in which case the user has already chosen not
594 // to have sound)
595         if (!dsound_init && (stat != SIS_NOTAVAIL))
596         {
597                 if (snd_firsttime || snd_iswave)
598                 {
599
600                         snd_iswave = SNDDMA_InitWav ();
601
602                         if (snd_iswave)
603                         {
604                                 if (snd_firsttime)
605                                         Con_SafePrint("Wave sound initialized\n");
606                         }
607                         else
608                         {
609                                 Con_SafePrint("Wave sound failed to init\n");
610                         }
611                 }
612         }
613
614         snd_firsttime = false;
615
616         if (!dsound_init && !wav_init)
617                 return 0;
618
619         return 1;
620 }
621
622 /*
623 ==============
624 SNDDMA_GetDMAPos
625
626 return the current sample position (in mono samples read)
627 inside the recirculating dma buffer, so the mixing code will know
628 how many sample are required to fill it up.
629 ===============
630 */
631 int SNDDMA_GetDMAPos(void)
632 {
633         MMTIME  mmtime;
634         int             s;
635         DWORD   dwWrite;
636
637         if (dsound_init)
638         {
639                 mmtime.wType = TIME_SAMPLES;
640                 pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmtime.u.sample, &dwWrite);
641                 s = mmtime.u.sample - mmstarttime.u.sample;
642         }
643         else if (wav_init)
644         {
645                 s = snd_sent * WAV_BUFFER_SIZE;
646         }
647         else
648                 s = 0;
649
650
651         s >>= sample16;
652
653         s &= (shm->samples-1);
654
655         return s;
656 }
657
658 /*
659 ==============
660 SNDDMA_Submit
661
662 Send sound to device if buffer isn't really the dma buffer
663 ===============
664 */
665 void SNDDMA_Submit(void)
666 {
667         LPWAVEHDR       h;
668         int                     wResult;
669
670         if (!wav_init)
671                 return;
672
673         //
674         // find which sound blocks have completed
675         //
676         while (1)
677         {
678                 if ( snd_completed == snd_sent )
679                 {
680                         Con_DPrint("Sound overrun\n");
681                         break;
682                 }
683
684                 if ( ! (lpWaveHdr[ snd_completed & WAV_MASK].dwFlags & WHDR_DONE) )
685                 {
686                         break;
687                 }
688
689                 snd_completed++;        // this buffer has been played
690         }
691
692         //
693         // submit two new sound blocks
694         //
695         while (((snd_sent - snd_completed) >> sample16) < 4)
696         {
697                 h = lpWaveHdr + ( snd_sent&WAV_MASK );
698
699                 snd_sent++;
700                 /*
701                  * Now the data block can be sent to the output device. The
702                  * waveOutWrite function returns immediately and waveform
703                  * data is sent to the output device in the background.
704                  */
705                 wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR));
706
707                 if (wResult != MMSYSERR_NOERROR)
708                 {
709                         Con_SafePrint("Failed to write block to device\n");
710                         FreeSound ();
711                         return;
712                 }
713         }
714 }
715
716 /*
717 ==============
718 SNDDMA_Shutdown
719
720 Reset the sound device for exiting
721 ===============
722 */
723 void SNDDMA_Shutdown(void)
724 {
725         FreeSound ();
726 }
727
728 DWORD dsound_dwSize;
729 DWORD dsound_dwSize2;
730 DWORD *dsound_pbuf;
731 DWORD *dsound_pbuf2;
732 void *S_LockBuffer(void)
733 {
734         int reps;
735         HRESULT hresult;
736
737         if (pDSBuf)
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