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