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