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