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