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 #ifdef SUPPORTDIRECTX
24 #ifndef DIRECTSOUND_VERSION
25 #	define DIRECTSOUND_VERSION 0x0500  /* Version 5.0 */
26 #endif
27 #endif
28 #include <windows.h>
29 #include <mmsystem.h>
30 #ifdef SUPPORTDIRECTX
31 #include <dsound.h>
32 #endif
33 
34 // ==============================================================================
35 
36 #ifndef _WAVEFORMATEXTENSIBLE_
37 #define _WAVEFORMATEXTENSIBLE_
38 typedef struct
39 {
40 	WAVEFORMATEX Format;
41 	union
42 	{
43 		WORD wValidBitsPerSample;	// bits of precision
44 		WORD wSamplesPerBlock;		// valid if wBitsPerSample==0
45 		WORD wReserved;				// If neither applies, set to zero
46 	} Samples;
47     DWORD dwChannelMask;			// which channels are present in stream
48     GUID SubFormat;
49 } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
50 #endif
51 
52 #if !defined(WAVE_FORMAT_EXTENSIBLE)
53 #	define WAVE_FORMAT_EXTENSIBLE 0xFFFE
54 #endif
55 
56 // Some speaker positions
57 #ifndef SPEAKER_FRONT_LEFT
58 #	define SPEAKER_FRONT_LEFT				0x1
59 #	define SPEAKER_FRONT_RIGHT				0x2
60 #	define SPEAKER_FRONT_CENTER				0x4
61 #	define SPEAKER_LOW_FREQUENCY			0x8
62 #	define SPEAKER_BACK_LEFT				0x10
63 #	define SPEAKER_BACK_RIGHT				0x20
64 #	define SPEAKER_FRONT_LEFT_OF_CENTER		0x40
65 #	define SPEAKER_FRONT_RIGHT_OF_CENTER	0x80
66 // ... we never use the other values
67 #endif
68 
69 // KSDATAFORMAT_SUBTYPE_PCM = GUID "00000001-0000-0010-8000-00aa00389b71"
70 static const GUID MY_KSDATAFORMAT_SUBTYPE_PCM =
71 {
72 	0x00000001,
73 	0x0000,
74 	0x0010,
75 	{
76 		0x80, 0x00,
77 		0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
78 	}
79 };
80 
81 
82 // ==============================================================================
83 
84 extern HWND mainwindow;
85 
86 #ifdef SUPPORTDIRECTX
87 HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);
88 #endif
89 
90 // Wave output: how much buffer time, and how many partitions in that time
91 #define WAV_BUFFERTIME	0.125
92 #define	WAV_BUFFERS		16
93 #define	WAV_MASK		(WAV_BUFFERS - 1)
94 static unsigned int wav_buffer_size;
95 
96 // DirectSound output: 64KB in 1 buffer
97 //#define SECONDARY_BUFFER_SIZE(fmt_ptr) ((fmt_ptr)->width * (fmt_ptr)->channels * (fmt_ptr)->speed / 2)
98 // LordHavoc: changed this to be a multiple of 32768
99 #define SECONDARY_BUFFER_SIZE(fmt_ptr) ((fmt_ptr)->channels * 32768)
100 
101 typedef enum sndinitstat_e {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat;
102 
103 #ifdef SUPPORTDIRECTX
104 static qboolean	dsound_init;
105 static unsigned int dsound_time;
106 static qboolean	primary_format_set;
107 #endif
108 
109 static qboolean	wav_init;
110 
111 static int	snd_sent, snd_completed;
112 
113 static int prev_painted;
114 static unsigned int paintpot;
115 
116 
117 
118 /*
119  * Global variables. Must be visible to window-procedure function
120  *  so it can unlock and free the data block after it has been played.
121  */
122 
123 HANDLE		hData;
124 HPSTR		lpData, lpData2;
125 
126 HGLOBAL		hWaveHdr;
127 LPWAVEHDR	lpWaveHdr;
128 
129 HWAVEOUT    hWaveOut;
130 
131 WAVEOUTCAPS	wavecaps;
132 
133 DWORD	gSndBufSize;
134 
135 DWORD	dwStartTime;
136 
137 #ifdef SUPPORTDIRECTX
138 LPDIRECTSOUND pDS;
139 LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
140 
141 HINSTANCE hInstDS;
142 #endif
143 
144 qboolean SNDDMA_InitWav (void);
145 #ifdef SUPPORTDIRECTX
146 sndinitstat SNDDMA_InitDirect (void);
147 #endif
148 
149 
150 /*
151 ==================
152 SndSys_BuildWaveFormat
153 ==================
154 */
SndSys_BuildWaveFormat(const snd_format_t * requested,WAVEFORMATEXTENSIBLE * fmt_ptr)155 static qboolean SndSys_BuildWaveFormat (const snd_format_t* requested, WAVEFORMATEXTENSIBLE* fmt_ptr)
156 {
157 	WAVEFORMATEX* pfmtex;
158 
159 	memset (fmt_ptr, 0, sizeof(*fmt_ptr));
160 
161 	pfmtex = &fmt_ptr->Format;
162 	pfmtex->nChannels = requested->channels;
163     pfmtex->wBitsPerSample = requested->width * 8;
164     pfmtex->nSamplesPerSec = requested->speed;
165 	pfmtex->nBlockAlign = pfmtex->nChannels * pfmtex->wBitsPerSample / 8;
166 	pfmtex->nAvgBytesPerSec = pfmtex->nSamplesPerSec * pfmtex->nBlockAlign;
167 
168 	// LordHavoc: disabled this WAVE_FORMAT_EXTENSIBLE support because it does not seem to be working
169 #if 0
170 	if (requested->channels <= 2)
171 	{
172 #endif
173 		pfmtex->wFormatTag = WAVE_FORMAT_PCM;
174 		pfmtex->cbSize = 0;
175 #if 0
176 	}
177 	else
178 	{
179 		pfmtex->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
180 		pfmtex->cbSize = sizeof(*fmt_ptr) - sizeof(fmt_ptr->Format);
181 		fmt_ptr->Samples.wValidBitsPerSample = fmt_ptr->Format.wBitsPerSample;
182 		fmt_ptr->SubFormat = MY_KSDATAFORMAT_SUBTYPE_PCM;
183 
184 		// Build the channel mask
185 		fmt_ptr->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
186 		switch (requested->channels)
187 		{
188 			case 8:
189 				fmt_ptr->dwChannelMask |= SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER;
190 				// no break
191 			case 6:
192 				fmt_ptr->dwChannelMask |= SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY;
193 				// no break
194 			case 4:
195 				fmt_ptr->dwChannelMask |= SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
196 				break;
197 
198 			default:
199 				Con_Printf("SndSys_BuildWaveFormat: invalid number of channels (%hu)\n", requested->channels);
200 				return false;
201 		}
202 	}
203 #endif
204 
205 	return true;
206 }
207 
208 
209 #ifdef SUPPORTDIRECTX
210 /*
211 ==================
212 SndSys_InitDirectSound
213 
214 DirectSound 5 support
215 ==================
216 */
SndSys_InitDirectSound(const snd_format_t * requested)217 static sndinitstat SndSys_InitDirectSound (const snd_format_t* requested)
218 {
219 	DSBUFFERDESC			dsbuf;
220 	DSBCAPS					dsbcaps;
221 	DWORD					dwSize;
222 	DSCAPS					dscaps;
223 	WAVEFORMATEXTENSIBLE	format, pformat;
224 	HRESULT					hresult;
225 	int						reps;
226 
227 	if (! SndSys_BuildWaveFormat(requested, &format))
228 		return SIS_FAILURE;
229 
230 	if (!hInstDS)
231 	{
232 		hInstDS = LoadLibrary("dsound.dll");
233 
234 		if (hInstDS == NULL)
235 		{
236 			Con_Print("Couldn't load dsound.dll\n");
237 			return SIS_FAILURE;
238 		}
239 
240 		pDirectSoundCreate = (HRESULT (__stdcall *)(GUID *, LPDIRECTSOUND *,IUnknown *))GetProcAddress(hInstDS,"DirectSoundCreate");
241 
242 		if (!pDirectSoundCreate)
243 		{
244 			Con_Print("Couldn't get DS proc addr\n");
245 			return SIS_FAILURE;
246 		}
247 	}
248 
249 	while ((hresult = pDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK)
250 	{
251 		if (hresult != DSERR_ALLOCATED)
252 		{
253 			Con_Print("DirectSound create failed\n");
254 			return SIS_FAILURE;
255 		}
256 
257 		if (MessageBox (NULL,
258 						"The sound hardware is in use by another app.\n\n"
259 					    "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
260 						"Sound not available",
261 						MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
262 		{
263 			Con_Print("DirectSoundCreate failure\n  hardware already in use\n");
264 			return SIS_NOTAVAIL;
265 		}
266 	}
267 
268 	dscaps.dwSize = sizeof(dscaps);
269 
270 	if (DS_OK != IDirectSound_GetCaps (pDS, &dscaps))
271 	{
272 		Con_Print("Couldn't get DS caps\n");
273 	}
274 
275 	if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
276 	{
277 		Con_Print("No DirectSound driver installed\n");
278 		SndSys_Shutdown ();
279 		return SIS_FAILURE;
280 	}
281 
282 	if (DS_OK != IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE))
283 	{
284 		Con_Print("Set coop level failed\n");
285 		SndSys_Shutdown ();
286 		return SIS_FAILURE;
287 	}
288 
289 	// get access to the primary buffer, if possible, so we can set the
290 	// sound hardware format
291 	memset (&dsbuf, 0, sizeof(dsbuf));
292 	dsbuf.dwSize = sizeof(DSBUFFERDESC);
293 	dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER;
294 	dsbuf.dwBufferBytes = 0;
295 	dsbuf.lpwfxFormat = NULL;
296 
297 	memset(&dsbcaps, 0, sizeof(dsbcaps));
298 	dsbcaps.dwSize = sizeof(dsbcaps);
299 	primary_format_set = false;
300 
301 // COMMANDLINEOPTION: Windows DirectSound: -snoforceformat uses the format that DirectSound returns, rather than forcing it
302 	if (!COM_CheckParm ("-snoforceformat"))
303 	{
304 		if (DS_OK == IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL))
305 		{
306 			pformat = format;
307 
308 			if (DS_OK != IDirectSoundBuffer_SetFormat (pDSPBuf, (WAVEFORMATEX*)&pformat))
309 			{
310 				Con_Print("Set primary sound buffer format: no\n");
311 			}
312 			else
313 			{
314 				Con_Print("Set primary sound buffer format: yes\n");
315 
316 				primary_format_set = true;
317 			}
318 		}
319 	}
320 
321 // COMMANDLINEOPTION: Windows DirectSound: -primarysound locks the sound hardware for exclusive use
322 	if (!primary_format_set || !COM_CheckParm ("-primarysound"))
323 	{
324 		HRESULT result;
325 
326 		// create the secondary buffer we'll actually work with
327 		memset (&dsbuf, 0, sizeof(dsbuf));
328 		dsbuf.dwSize = sizeof(DSBUFFERDESC);
329 		dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE;
330 		dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE(requested);
331 		dsbuf.lpwfxFormat = (WAVEFORMATEX*)&format;
332 
333 		memset(&dsbcaps, 0, sizeof(dsbcaps));
334 		dsbcaps.dwSize = sizeof(dsbcaps);
335 
336 		result = IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL);
337 		if (result != DS_OK ||
338 			requested->channels != format.Format.nChannels ||
339 			requested->width != format.Format.wBitsPerSample / 8 ||
340 			requested->speed != format.Format.nSamplesPerSec)
341 		{
342 			Con_Printf("DS:CreateSoundBuffer Failed (%d): channels=%u, width=%u, speed=%u\n",
343 					   (int)result, (unsigned)format.Format.nChannels, (unsigned)format.Format.wBitsPerSample / 8, (unsigned)format.Format.nSamplesPerSec);
344 			SndSys_Shutdown ();
345 			return SIS_FAILURE;
346 		}
347 
348 		if (DS_OK != IDirectSoundBuffer_GetCaps (pDSBuf, &dsbcaps))
349 		{
350 			Con_Print("DS:GetCaps failed\n");
351 			SndSys_Shutdown ();
352 			return SIS_FAILURE;
353 		}
354 
355 		Con_Print("Using secondary sound buffer\n");
356 	}
357 	else
358 	{
359 		if (DS_OK != IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY))
360 		{
361 			Con_Print("Set coop level failed\n");
362 			SndSys_Shutdown ();
363 			return SIS_FAILURE;
364 		}
365 
366 		if (DS_OK != IDirectSoundBuffer_GetCaps (pDSPBuf, &dsbcaps))
367 		{
368 			Con_Print("DS:GetCaps failed\n");
369 			return SIS_FAILURE;
370 		}
371 
372 		pDSBuf = pDSPBuf;
373 		Con_Print("Using primary sound buffer\n");
374 	}
375 
376 	// Make sure mixer is active
377 	IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
378 
379 	Con_Printf("   %d channel(s)\n"
380 				"   %d bits/sample\n"
381 				"   %d samples/sec\n",
382 				requested->channels, requested->width * 8, requested->speed);
383 
384 	gSndBufSize = dsbcaps.dwBufferBytes;
385 
386 	// initialize the buffer
387 	reps = 0;
388 
389 	while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&lpData, &dwSize, NULL, NULL, 0)) != DS_OK)
390 	{
391 		if (hresult != DSERR_BUFFERLOST)
392 		{
393 			Con_Print("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n");
394 			SndSys_Shutdown ();
395 			return SIS_FAILURE;
396 		}
397 
398 		if (++reps > 10000)
399 		{
400 			Con_Print("SNDDMA_InitDirect: DS: couldn't restore buffer\n");
401 			SndSys_Shutdown ();
402 			return SIS_FAILURE;
403 		}
404 
405 	}
406 
407 	memset(lpData, 0, dwSize);
408 	IDirectSoundBuffer_Unlock(pDSBuf, lpData, dwSize, NULL, 0);
409 
410 	IDirectSoundBuffer_Stop(pDSBuf);
411 	IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
412 
413 	dwStartTime = 0;
414 	dsound_time = 0;
415 	snd_renderbuffer = Snd_CreateRingBuffer(requested, gSndBufSize / (requested->width * requested->channels), lpData);
416 
417 	dsound_init = true;
418 
419 	return SIS_SUCCESS;
420 }
421 #endif
422 
423 
424 /*
425 ==================
426 SndSys_InitMmsystem
427 
428 Crappy windows multimedia base
429 ==================
430 */
SndSys_InitMmsystem(const snd_format_t * requested)431 static qboolean SndSys_InitMmsystem (const snd_format_t* requested)
432 {
433 	WAVEFORMATEXTENSIBLE format;
434 	int				i;
435 	HRESULT			hr;
436 
437 	if (! SndSys_BuildWaveFormat(requested, &format))
438 		return false;
439 
440 	// Open a waveform device for output using window callback
441 	while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER, (WAVEFORMATEX*)&format,
442 					0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)
443 	{
444 		if (hr != MMSYSERR_ALLOCATED)
445 		{
446 			Con_Print("waveOutOpen failed\n");
447 			return false;
448 		}
449 
450 		if (MessageBox (NULL,
451 						"The sound hardware is in use by another app.\n\n"
452 					    "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
453 						"Sound not available",
454 						MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
455 		{
456 			Con_Print("waveOutOpen failure;\n  hardware already in use\n");
457 			return false;
458 		}
459 	}
460 
461 	wav_buffer_size = ((int)(requested->speed * WAV_BUFFERTIME) / WAV_BUFFERS) * requested->channels * requested->width;
462 
463 	/*
464 	 * Allocate and lock memory for the waveform data. The memory
465 	 * for waveform data must be globally allocated with
466 	 * GMEM_MOVEABLE and GMEM_SHARE flags.
467 	 */
468 	gSndBufSize = WAV_BUFFERS * wav_buffer_size;
469 	hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize);
470 	if (!hData)
471 	{
472 		Con_Print("Sound: Out of memory.\n");
473 		SndSys_Shutdown ();
474 		return false;
475 	}
476 	lpData = (HPSTR)GlobalLock(hData);
477 	if (!lpData)
478 	{
479 		Con_Print("Sound: Failed to lock.\n");
480 		SndSys_Shutdown ();
481 		return false;
482 	}
483 	memset (lpData, 0, gSndBufSize);
484 
485 	/*
486 	 * Allocate and lock memory for the header. This memory must
487 	 * also be globally allocated with GMEM_MOVEABLE and
488 	 * GMEM_SHARE flags.
489 	 */
490 	hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS);
491 
492 	if (hWaveHdr == NULL)
493 	{
494 		Con_Print("Sound: Failed to Alloc header.\n");
495 		SndSys_Shutdown ();
496 		return false;
497 	}
498 
499 	lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
500 
501 	if (lpWaveHdr == NULL)
502 	{
503 		Con_Print("Sound: Failed to lock header.\n");
504 		SndSys_Shutdown ();
505 		return false;
506 	}
507 
508 	memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
509 
510 	// After allocation, set up and prepare headers
511 	for (i=0 ; i<WAV_BUFFERS ; i++)
512 	{
513 		lpWaveHdr[i].dwBufferLength = wav_buffer_size;
514 		lpWaveHdr[i].lpData = lpData + i * wav_buffer_size;
515 
516 		if (waveOutPrepareHeader(hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)) != MMSYSERR_NOERROR)
517 		{
518 			Con_Print("Sound: failed to prepare wave headers\n");
519 			SndSys_Shutdown ();
520 			return false;
521 		}
522 	}
523 
524 	snd_renderbuffer = Snd_CreateRingBuffer(requested, gSndBufSize / (requested->width * requested->channels), lpData);
525 
526 	prev_painted = 0;
527 	paintpot = 0;
528 
529 	snd_sent = 0;
530 	snd_completed = 0;
531 
532 	wav_init = true;
533 
534 	return true;
535 }
536 
537 
538 /*
539 ====================
540 SndSys_Init
541 
542 Create "snd_renderbuffer" with the proper sound format if the call is successful
543 May return a suggested format if the requested format isn't available
544 ====================
545 */
SndSys_Init(const snd_format_t * requested,snd_format_t * suggested)546 qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
547 {
548 #ifdef SUPPORTDIRECTX
549 	qboolean wavonly;
550 #endif
551 	sndinitstat	stat;
552 
553 	Con_Print ("SndSys_Init: using the Win32 module\n");
554 
555 #ifdef SUPPORTDIRECTX
556 // COMMANDLINEOPTION: Windows Sound: -wavonly uses wave sound instead of DirectSound
557 	wavonly = (COM_CheckParm ("-wavonly") != 0);
558 	dsound_init = false;
559 #endif
560 	wav_init = false;
561 
562 	stat = SIS_FAILURE;	// assume DirectSound won't initialize
563 
564 #ifdef SUPPORTDIRECTX
565 	// Init DirectSound
566 	if (!wavonly)
567 	{
568 		stat = SndSys_InitDirectSound (requested);
569 
570 		if (stat == SIS_SUCCESS)
571 			Con_Print("DirectSound initialized\n");
572 		else
573 			Con_Print("DirectSound failed to init\n");
574 	}
575 #endif
576 
577 	// if DirectSound didn't succeed in initializing, try to initialize
578 	// waveOut sound, unless DirectSound failed because the hardware is
579 	// already allocated (in which case the user has already chosen not
580 	// to have sound)
581 #ifdef SUPPORTDIRECTX
582 	if (!dsound_init && (stat != SIS_NOTAVAIL))
583 #endif
584 	{
585 		if (SndSys_InitMmsystem (requested))
586 			Con_Print("Wave sound (MMSYSTEM) initialized\n");
587 		else
588 			Con_Print("Wave sound failed to init\n");
589 	}
590 
591 #ifdef SUPPORTDIRECTX
592 	return (dsound_init || wav_init);
593 #else
594 	return wav_init;
595 #endif
596 }
597 
598 
599 /*
600 ====================
601 SndSys_Shutdown
602 
603 Stop the sound card, delete "snd_renderbuffer" and free its other resources
604 ====================
605 */
SndSys_Shutdown(void)606 void SndSys_Shutdown (void)
607 {
608 #ifdef SUPPORTDIRECTX
609 	if (pDSBuf)
610 	{
611 		IDirectSoundBuffer_Stop(pDSBuf);
612 		IDirectSoundBuffer_Release(pDSBuf);
613 	}
614 
615 	// only release primary buffer if it's not also the mixing buffer we just released
616 	if (pDSPBuf && (pDSBuf != pDSPBuf))
617 	{
618 		IDirectSoundBuffer_Release(pDSPBuf);
619 	}
620 
621 	if (pDS)
622 	{
623 		IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL);
624 		IDirectSound_Release(pDS);
625 	}
626 #endif
627 
628 	if (hWaveOut)
629 	{
630 		waveOutReset (hWaveOut);
631 
632 		if (lpWaveHdr)
633 		{
634 			unsigned int i;
635 
636 			for (i=0 ; i< WAV_BUFFERS ; i++)
637 				waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR));
638 		}
639 
640 		waveOutClose (hWaveOut);
641 
642 		if (hWaveHdr)
643 		{
644 			GlobalUnlock(hWaveHdr);
645 			GlobalFree(hWaveHdr);
646 		}
647 
648 		if (hData)
649 		{
650 			GlobalUnlock(hData);
651 			GlobalFree(hData);
652 		}
653 	}
654 
655 	if (snd_renderbuffer != NULL)
656 	{
657 		Mem_Free(snd_renderbuffer);
658 		snd_renderbuffer = NULL;
659 	}
660 
661 #ifdef SUPPORTDIRECTX
662 	pDS = NULL;
663 	pDSBuf = NULL;
664 	pDSPBuf = NULL;
665 	dsound_init = false;
666 #endif
667 	hWaveOut = 0;
668 	hData = 0;
669 	hWaveHdr = 0;
670 	lpData = NULL;
671 	lpWaveHdr = NULL;
672 	wav_init = false;
673 }
674 
675 
676 /*
677 ====================
678 SndSys_Submit
679 
680 Submit the contents of "snd_renderbuffer" to the sound card
681 ====================
682 */
SndSys_Submit(void)683 void SndSys_Submit (void)
684 {
685 	LPWAVEHDR	h;
686 	int			wResult;
687 
688 	// DirectSound doesn't need this
689 	if (!wav_init)
690 		return;
691 
692 	paintpot += (snd_renderbuffer->endframe - prev_painted) * snd_renderbuffer->format.channels * snd_renderbuffer->format.width;
693 	if (paintpot > WAV_BUFFERS * wav_buffer_size)
694 		paintpot = WAV_BUFFERS * wav_buffer_size;
695 	prev_painted = snd_renderbuffer->endframe;
696 
697 	// submit new sound blocks
698 	while (paintpot > wav_buffer_size)
699 	{
700 		h = lpWaveHdr + (snd_sent & WAV_MASK);
701 
702 		/*
703 		 * Now the data block can be sent to the output device. The
704 		 * waveOutWrite function returns immediately and waveform
705 		 * data is sent to the output device in the background.
706 		 */
707 		wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR));
708 		if (wResult == MMSYSERR_NOERROR)
709 			snd_sent++;
710 		else if (wResult == WAVERR_STILLPLAYING)
711 		{
712 			if(developer.integer >= 1000)
713 				Con_Print("waveOutWrite failed (too much sound data)\n");
714 			//h->dwFlags |= WHDR_DONE;
715 			//snd_sent++;
716 		}
717 		else
718 		{
719 			Con_Printf("waveOutWrite failed, error code %d\n", (int) wResult);
720 			SndSys_Shutdown ();
721 			return;
722 		}
723 
724 		paintpot -= wav_buffer_size;
725 	}
726 
727 }
728 
729 
730 /*
731 ====================
732 SndSys_GetSoundTime
733 
734 Returns the number of sample frames consumed since the sound started
735 ====================
736 */
SndSys_GetSoundTime(void)737 unsigned int SndSys_GetSoundTime (void)
738 {
739 	unsigned int factor;
740 
741 	factor = snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
742 
743 #ifdef SUPPORTDIRECTX
744 	if (dsound_init)
745 	{
746 		DWORD dwTime;
747 		unsigned int diff;
748 
749 		IDirectSoundBuffer_GetCurrentPosition(pDSBuf, &dwTime, NULL);
750 		diff = (unsigned int)(dwTime - dwStartTime) % (unsigned int)gSndBufSize;
751 		dwStartTime = dwTime;
752 
753 		dsound_time += diff / factor;
754 		return dsound_time;
755 	}
756 #endif
757 
758 	if (wav_init)
759 	{
760 		// Find which sound blocks have completed
761 		for (;;)
762 		{
763 			if (snd_completed == snd_sent)
764 			{
765 				Con_DPrint("Sound overrun\n");
766 				break;
767 			}
768 
769 			if (!(lpWaveHdr[snd_completed & WAV_MASK].dwFlags & WHDR_DONE))
770 				break;
771 
772 			snd_completed++;	// this buffer has been played
773 		}
774 
775 		return (snd_completed * wav_buffer_size) / factor;
776 
777 		/*
778 		 * S_PaintAndSubmit: WARNING: newsoundtime (soundtime (275 < 134217707)
779 		 * apparently this sound time wraps quite early?
780 		{
781 		MMRESULT res;
782 		MMTIME mmtime;
783 
784 		mmtime.wType = TIME_SAMPLES;
785 		res = waveOutGetPosition(hWaveOut, &mmtime, sizeof(mmtime));
786 		if(res == MMSYSERR_NOERROR)
787 			return mmtime.u.sample;
788 		}
789 		*/
790 	}
791 
792 	return 0;
793 }
794 
795 
796 #ifdef SUPPORTDIRECTX
797 static DWORD dsound_dwSize;
798 static DWORD dsound_dwSize2;
799 static DWORD *dsound_pbuf;
800 static DWORD *dsound_pbuf2;
801 #endif
802 
803 /*
804 ====================
805 SndSys_LockRenderBuffer
806 
807 Get the exclusive lock on "snd_renderbuffer"
808 ====================
809 */
SndSys_LockRenderBuffer(void)810 qboolean SndSys_LockRenderBuffer (void)
811 {
812 #ifdef SUPPORTDIRECTX
813 	int reps;
814 	HRESULT hresult;
815 	DWORD	dwStatus;
816 
817 	if (pDSBuf)
818 	{
819 		// if the buffer was lost or stopped, restore it and/or restart it
820 		if (IDirectSoundBuffer_GetStatus (pDSBuf, &dwStatus) != DS_OK)
821 			Con_Print("Couldn't get sound buffer status\n");
822 
823 		if (dwStatus & DSBSTATUS_BUFFERLOST)
824 		{
825 			Con_Print("DSound buffer is lost!!\n");
826 			IDirectSoundBuffer_Restore (pDSBuf);
827 		}
828 
829 		if (!(dwStatus & DSBSTATUS_PLAYING))
830 			IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
831 
832 		reps = 0;
833 
834 		while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID*)&dsound_pbuf, &dsound_dwSize, (LPVOID*)&dsound_pbuf2, &dsound_dwSize2, 0)) != DS_OK)
835 		{
836 			if (hresult != DSERR_BUFFERLOST)
837 			{
838 				Con_Print("S_LockBuffer: DS: Lock Sound Buffer Failed\n");
839 				S_Shutdown ();
840 				S_Startup ();
841 				return false;
842 			}
843 
844 			if (++reps > 10000)
845 			{
846 				Con_Print("S_LockBuffer: DS: couldn't restore buffer\n");
847 				S_Shutdown ();
848 				S_Startup ();
849 				return false;
850 			}
851 		}
852 
853 		if ((void*)dsound_pbuf != snd_renderbuffer->ring)
854 			Sys_Error("SndSys_LockRenderBuffer: the ring address has changed!!!\n");
855 		return true;
856 	}
857 #endif
858 
859 	return wav_init;
860 }
861 
862 
863 /*
864 ====================
865 SndSys_UnlockRenderBuffer
866 
867 Release the exclusive lock on "snd_renderbuffer"
868 ====================
869 */
SndSys_UnlockRenderBuffer(void)870 void SndSys_UnlockRenderBuffer (void)
871 {
872 #ifdef SUPPORTDIRECTX
873 	if (pDSBuf)
874 		IDirectSoundBuffer_Unlock(pDSBuf, dsound_pbuf, dsound_dwSize, dsound_pbuf2, dsound_dwSize2);
875 #endif
876 }
877 
878 /*
879 ====================
880 SndSys_SendKeyEvents
881 
882 Send keyboard events originating from the sound system (e.g. MIDI)
883 ====================
884 */
SndSys_SendKeyEvents(void)885 void SndSys_SendKeyEvents(void)
886 {
887 	// not supported
888 }
889