]> icculus.org git repositories - divverent/darkplaces.git/blob - snd_win.c
winquake.h is gone, absorbed into the respective files which used it, also cleaned...
[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 <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->channels = 2;
198         shm->samplebits = 16;
199         i = COM_CheckParm ("-sndspeed"); // LordHavoc: -sndspeed option
200         if (i && i != (com_argc - 1))
201                 shm->speed = atoi(com_argv[i+1]);
202         else
203                 shm->speed = 44100;
204
205         memset (&format, 0, sizeof(format));
206         format.wFormatTag = WAVE_FORMAT_PCM;
207     format.nChannels = shm->channels;
208     format.wBitsPerSample = shm->samplebits;
209     format.nSamplesPerSec = shm->speed;
210     format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
211     format.cbSize = 0;
212     format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
213
214         if (!hInstDS)
215         {
216                 hInstDS = LoadLibrary("dsound.dll");
217
218                 if (hInstDS == NULL)
219                 {
220                         Con_SafePrintf ("Couldn't load dsound.dll\n");
221                         return SIS_FAILURE;
222                 }
223
224                 pDirectSoundCreate = (void *)GetProcAddress(hInstDS,"DirectSoundCreate");
225
226                 if (!pDirectSoundCreate)
227                 {
228                         Con_SafePrintf ("Couldn't get DS proc addr\n");
229                         return SIS_FAILURE;
230                 }
231         }
232
233         while ((hresult = iDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK)
234         {
235                 if (hresult != DSERR_ALLOCATED)
236                 {
237                         Con_SafePrintf ("DirectSound create failed\n");
238                         return SIS_FAILURE;
239                 }
240
241                 if (MessageBox (NULL,
242                                                 "The sound hardware is in use by another app.\n\n"
243                                             "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
244                                                 "Sound not available",
245                                                 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
246                 {
247                         Con_SafePrintf ("DirectSoundCreate failure\n"
248                                                         "  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_SafePrintf ("Couldn't get DS caps\n");
258         }
259
260         if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
261         {
262                 Con_SafePrintf ("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_SafePrintf ("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         if (!COM_CheckParm ("-snoforceformat"))
287         {
288                 if (DS_OK == pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL))
289                 {
290                         pformat = format;
291
292                         if (DS_OK != pDSPBuf->lpVtbl->SetFormat (pDSPBuf, &pformat))
293                         {
294                                 if (snd_firsttime)
295                                         Con_SafePrintf ("Set primary sound buffer format: no\n");
296                         }
297                         else
298                         {
299                                 if (snd_firsttime)
300                                         Con_SafePrintf ("Set primary sound buffer format: yes\n");
301
302                                 primary_format_set = true;
303                         }
304                 }
305         }
306
307         if (!primary_format_set || !COM_CheckParm ("-primarysound"))
308         {
309         // create the secondary buffer we'll actually work with
310                 memset (&dsbuf, 0, sizeof(dsbuf));
311                 dsbuf.dwSize = sizeof(DSBUFFERDESC);
312                 dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE;
313                 dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE;
314                 dsbuf.lpwfxFormat = &format;
315
316                 memset(&dsbcaps, 0, sizeof(dsbcaps));
317                 dsbcaps.dwSize = sizeof(dsbcaps);
318
319                 if (DS_OK != pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL))
320                 {
321                         Con_SafePrintf ("DS:CreateSoundBuffer Failed");
322                         FreeSound ();
323                         return SIS_FAILURE;
324                 }
325
326                 shm->channels = format.nChannels;
327                 shm->samplebits = format.wBitsPerSample;
328                 shm->speed = format.nSamplesPerSec;
329
330                 if (DS_OK != pDSBuf->lpVtbl->GetCaps (pDSBuf, &dsbcaps))
331                 {
332                         Con_SafePrintf ("DS:GetCaps failed\n");
333                         FreeSound ();
334                         return SIS_FAILURE;
335                 }
336
337                 if (snd_firsttime)
338                         Con_SafePrintf ("Using secondary sound buffer\n");
339         }
340         else
341         {
342                 if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY))
343                 {
344                         Con_SafePrintf ("Set coop level failed\n");
345                         FreeSound ();
346                         return SIS_FAILURE;
347                 }
348
349                 if (DS_OK != pDSPBuf->lpVtbl->GetCaps (pDSPBuf, &dsbcaps))
350                 {
351                         Con_Printf ("DS:GetCaps failed\n");
352                         return SIS_FAILURE;
353                 }
354
355                 pDSBuf = pDSPBuf;
356                 Con_SafePrintf ("Using primary sound buffer\n");
357         }
358
359         // Make sure mixer is active
360         pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
361
362         if (snd_firsttime)
363                 Con_SafePrintf("   %d channel(s)\n"
364                                "   %d bits/sample\n"
365                                            "   %d samples/sec\n",
366                                            shm->channels, shm->samplebits, shm->speed);
367
368         gSndBufSize = dsbcaps.dwBufferBytes;
369
370 // initialize the buffer
371         reps = 0;
372
373         while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK)
374         {
375                 if (hresult != DSERR_BUFFERLOST)
376                 {
377                         Con_SafePrintf ("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n");
378                         FreeSound ();
379                         return SIS_FAILURE;
380                 }
381
382                 if (++reps > 10000)
383                 {
384                         Con_SafePrintf ("SNDDMA_InitDirect: DS: couldn't restore buffer\n");
385                         FreeSound ();
386                         return SIS_FAILURE;
387                 }
388
389         }
390
391         memset(lpData, 0, dwSize);
392
393         pDSBuf->lpVtbl->Unlock(pDSBuf, lpData, dwSize, NULL, 0);
394
395         /* we don't want anyone to access the buffer directly w/o locking it first. */
396         lpData = NULL;
397
398         pDSBuf->lpVtbl->Stop(pDSBuf);
399         pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmstarttime.u.sample, &dwWrite);
400         pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
401
402         shm->samples = gSndBufSize/(shm->samplebits/8);
403         shm->samplepos = 0;
404         shm->buffer = (unsigned char *) lpData;
405         sample16 = (shm->samplebits/8) - 1;
406
407         dsound_init = true;
408
409         return SIS_SUCCESS;
410 }
411
412
413 /*
414 ==================
415 SNDDM_InitWav
416
417 Crappy windows multimedia base
418 ==================
419 */
420 qboolean SNDDMA_InitWav (void)
421 {
422         WAVEFORMATEX  format;
423         int                             i;
424         HRESULT                 hr;
425
426         snd_sent = 0;
427         snd_completed = 0;
428
429         memset((void *)shm, 0, sizeof(*shm));
430         shm->channels = 2;
431         shm->samplebits = 16;
432         i = COM_CheckParm ("-sndspeed"); // LordHavoc: -sndspeed option
433         if (i && i != (com_argc - 1))
434                 shm->speed = atoi(com_argv[i+1]);
435         else
436                 shm->speed = 44100;
437
438         memset (&format, 0, sizeof(format));
439         format.wFormatTag = WAVE_FORMAT_PCM;
440         format.nChannels = shm->channels;
441         format.wBitsPerSample = shm->samplebits;
442         format.nSamplesPerSec = shm->speed;
443         format.nBlockAlign = format.nChannels
444                 *format.wBitsPerSample / 8;
445         format.cbSize = 0;
446         format.nAvgBytesPerSec = format.nSamplesPerSec
447                 *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_SafePrintf ("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_SafePrintf ("waveOutOpen failure;\n"
467                                                         "  hardware already in use\n");
468                         return false;
469                 }
470         }
471
472         /*
473          * Allocate and lock memory for the waveform data. The memory
474          * for waveform data must be globally allocated with
475          * GMEM_MOVEABLE and GMEM_SHARE flags.
476
477         */
478         gSndBufSize = WAV_BUFFERS*WAV_BUFFER_SIZE;
479         hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize);
480         if (!hData)
481         {
482                 Con_SafePrintf ("Sound: Out of memory.\n");
483                 FreeSound ();
484                 return false;
485         }
486         lpData = GlobalLock(hData);
487         if (!lpData)
488         {
489                 Con_SafePrintf ("Sound: Failed to lock.\n");
490                 FreeSound ();
491                 return false;
492         }
493         memset (lpData, 0, gSndBufSize);
494
495         /*
496          * Allocate and lock memory for the header. This memory must
497          * also be globally allocated with GMEM_MOVEABLE and
498          * GMEM_SHARE flags.
499          */
500         hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
501                 (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS);
502
503         if (hWaveHdr == NULL)
504         {
505                 Con_SafePrintf ("Sound: Failed to Alloc header.\n");
506                 FreeSound ();
507                 return false;
508         }
509
510         lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
511
512         if (lpWaveHdr == NULL)
513         {
514                 Con_SafePrintf ("Sound: Failed to lock header.\n");
515                 FreeSound ();
516                 return false;
517         }
518
519         memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
520
521         /* After allocation, set up and prepare headers. */
522         for (i=0 ; i<WAV_BUFFERS ; i++)
523         {
524                 lpWaveHdr[i].dwBufferLength = WAV_BUFFER_SIZE;
525                 lpWaveHdr[i].lpData = lpData + i*WAV_BUFFER_SIZE;
526
527                 if (waveOutPrepareHeader(hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)) !=
528                                 MMSYSERR_NOERROR)
529                 {
530                         Con_SafePrintf ("Sound: failed to prepare wave headers\n");
531                         FreeSound ();
532                         return false;
533                 }
534         }
535
536         shm->samples = gSndBufSize/(shm->samplebits/8);
537         shm->samplepos = 0;
538         shm->buffer = (unsigned char *) lpData;
539         sample16 = (shm->samplebits/8) - 1;
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
555 qboolean SNDDMA_Init(void)
556 {
557         sndinitstat     stat;
558
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_SafePrintf ("DirectSound initialized\n");
579                         }
580                         else
581                         {
582                                 snd_isdirect = false;
583                                 Con_SafePrintf ("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_SafePrintf ("Wave sound initialized\n");
603                         }
604                         else
605                         {
606                                 Con_SafePrintf ("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         MMTIME  mmtime;
631         int             s;
632         DWORD   dwWrite;
633
634         if (dsound_init)
635         {
636                 mmtime.wType = TIME_SAMPLES;
637                 pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmtime.u.sample, &dwWrite);
638                 s = mmtime.u.sample - mmstarttime.u.sample;
639         }
640         else if (wav_init)
641         {
642                 s = snd_sent * WAV_BUFFER_SIZE;
643         }
644         else
645                 s = 0;
646
647
648         s >>= sample16;
649
650         s &= (shm->samples-1);
651
652         return s;
653 }
654
655 /*
656 ==============
657 SNDDMA_Submit
658
659 Send sound to device if buffer isn't really the dma buffer
660 ===============
661 */
662 void SNDDMA_Submit(void)
663 {
664         LPWAVEHDR       h;
665         int                     wResult;
666
667         if (!wav_init)
668                 return;
669
670         //
671         // find which sound blocks have completed
672         //
673         while (1)
674         {
675                 if ( snd_completed == snd_sent )
676                 {
677                         Con_DPrintf ("Sound overrun\n");
678                         break;
679                 }
680
681                 if ( ! (lpWaveHdr[ snd_completed & WAV_MASK].dwFlags & WHDR_DONE) )
682                 {
683                         break;
684                 }
685
686                 snd_completed++;        // this buffer has been played
687         }
688
689         //
690         // submit two new sound blocks
691         //
692         while (((snd_sent - snd_completed) >> sample16) < 4)
693         {
694                 h = lpWaveHdr + ( snd_sent&WAV_MASK );
695
696                 snd_sent++;
697                 /*
698                  * Now the data block can be sent to the output device. The
699                  * waveOutWrite function returns immediately and waveform
700                  * data is sent to the output device in the background.
701                  */
702                 wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR));
703
704                 if (wResult != MMSYSERR_NOERROR)
705                 {
706                         Con_SafePrintf ("Failed to write block to device\n");
707                         FreeSound ();
708                         return;
709                 }
710         }
711 }
712
713 /*
714 ==============
715 SNDDMA_Shutdown
716
717 Reset the sound device for exiting
718 ===============
719 */
720 void SNDDMA_Shutdown(void)
721 {
722         FreeSound ();
723 }
724
725 DWORD dsound_dwSize;
726 DWORD dsound_dwSize2;
727 DWORD *dsound_pbuf;
728 DWORD *dsound_pbuf2;
729 void *S_LockBuffer(void)
730 {
731         int reps;
732         HRESULT hresult;
733
734         if (pDSBuf)
735         {
736                 reps = 0;
737
738                 while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&dsound_pbuf, &dsound_dwSize, (LPVOID*)&dsound_pbuf2, &dsound_dwSize2, 0)) != DS_OK)
739                 {
740                         if (hresult != DSERR_BUFFERLOST)
741                         {
742                                 Con_Printf ("S_LockBuffer: DS::Lock Sound Buffer Failed\n");
743                                 S_Shutdown ();
744                                 S_Startup ();
745                                 return NULL;
746                         }
747
748                         if (++reps > 10000)
749                         {
750                                 Con_Printf ("S_LockBuffer: DS: couldn't restore buffer\n");
751                                 S_Shutdown ();
752                                 S_Startup ();
753                                 return NULL;
754                         }
755                 }
756                 return dsound_pbuf;
757         }
758         else
759                 return shm->buffer;
760 }
761
762 void S_UnlockBuffer(void)
763 {
764         if (pDSBuf)
765                 pDSBuf->lpVtbl->Unlock(pDSBuf, dsound_pbuf, dsound_dwSize, dsound_pbuf2, dsound_dwSize2);
766 }
767