1 /*
2 Copyright (C) 1997-2001 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 <float.h>
21 
22 #include "../client/client.h"
23 
24 #include "winquake.h"
25 #include "../client/snd_loc.h"
26 
27 
28 #define CINTERFACE 1
29 #include <dsound.h>
30 
31 #define iDirectSoundCreate(a,b,c)	pDirectSoundCreate(a,b,c)
32 
33 HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);
34 //int (WINAPI *pDirectSoundCreate8)(GUID FAR *lpGUID, LPDIRECTSOUND8 FAR *lplpDS, IUnknown FAR *pUnkOuter);
35 
36 
37 // 64K is > 1 second at 16-bit, 22050 Hz
38 #define	WAV_BUFFERS				128
39 #define	WAV_MASK				0x3F
40 #define	WAV_BUFFER_SIZE			0x1000
41 #define SECONDARY_BUFFER_SIZE	0x10000
42 
43 typedef enum {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat;
44 
45 cvar_t	*s_wavonly;
46 
47 static qboolean	dsound_init;
48 static qboolean	wav_init;
49 static qboolean	snd_firsttime = true, snd_isdirect, snd_iswave;
50 static qboolean	primary_format_set;
51 
52 // starts at 0 for disabled
53 //static int	snd_buffer_count = 0;
54 static int	sample16;
55 static int	snd_sent, snd_completed;
56 
57 //extern cvar_t *s_dx8;
58 
59 /*
60  * Global variables. Must be visible to window-procedure function
61  *  so it can unlock and free the data block after it has been played.
62  */
63 
64 
65 HANDLE		hData;
66 HPSTR		lpData, lpData2;
67 
68 HGLOBAL		hWaveHdr;
69 LPWAVEHDR	lpWaveHdr;
70 
71 HWAVEOUT    hWaveOut;
72 
73 WAVEOUTCAPS	wavecaps;
74 
75 DWORD	gSndBufSize;
76 
77 MMTIME		mmstarttime;
78 
79 LPDIRECTSOUND pDS;
80 LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
81 
82 //HINSTANCE hInstDS;
83 
84 qboolean SNDDMA_InitDirect (void);
85 qboolean SNDDMA_InitWav (void);
86 
87 void FreeSound( void );
88 
DSoundError(int error)89 static const char *DSoundError( int error )
90 {
91 	switch ( error )
92 	{
93 		case DSERR_BUFFERLOST:
94 			return "DSERR_BUFFERLOST";
95 		case DSERR_INVALIDCALL:
96 			return "DSERR_INVALIDCALL";
97 		case DSERR_INVALIDPARAM:
98 			return "DSERR_INVALIDPARAM";
99 		case DSERR_PRIOLEVELNEEDED:
100 			return "DSERR_PRIOLEVELNEEDED";
101 		case DSERR_OTHERAPPHASPRIO:
102 			return "DSERR_OTHERAPPHASPRIO";
103 		case DSERR_BADFORMAT:
104 			return "DSERR_BADFORMAT";
105 		case DSERR_OUTOFMEMORY:
106 			return "DSERR_OUTOFMEMORY";
107 		case DSERR_UNSUPPORTED:
108 			return "DSERR_UNSUPPORTED";
109 		case DSERR_GENERIC:
110 			return "DSERR_GENERIC";
111 		case DSERR_ALLOCATED:
112 			return "DSERR_ALLOCATED";
113 		case DSERR_FXUNAVAILABLE:
114 			return "DSERR_FXUNAVAILABLE";
115 		case DSERR_ACCESSDENIED:
116 			return "DSERR_ACCESSDENIED";
117 	}
118 
119 	return va("dx errno %d", error);
120 }
121 
122 /*
123 ** DS_CreateBuffers
124 */
DS_CreateBuffers(void)125 static qboolean DS_CreateBuffers( void )
126 {
127 	int ret;
128 
129 	DSBUFFERDESC	dsbuf;
130 	DSBCAPS			dsbcaps;
131 	WAVEFORMATEX	pformat, format;
132 	DWORD			dwWrite;
133 
134 	memset (&format, 0, sizeof(format));
135 	format.wFormatTag = WAVE_FORMAT_PCM;
136     format.nChannels = dma.channels;
137     format.wBitsPerSample = dma.samplebits;
138     format.nSamplesPerSec = dma.speed;
139     format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
140     format.cbSize = sizeof(WAVEFORMATEX);
141     format.nAvgBytesPerSec = format.nSamplesPerSec*format.nBlockAlign;
142 
143 	Com_DPrintf( "Creating DS buffers\n");
144 
145 	Com_DPrintf("...setting EXCLUSIVE coop level: " );
146 
147 	ret = pDS->lpVtbl->SetCooperativeLevel(pDS, cl_hwnd, DSSCL_EXCLUSIVE);
148 	if (ret != DS_OK)
149 	{
150 		Com_Printf ("failed (%s)\n", LOG_CLIENT, DSoundError(ret));
151 		FreeSound ();
152 		return false;
153 	}
154 	Com_DPrintf("ok\n" );
155 
156 // get access to the primary buffer, if possible, so we can set the
157 // sound hardware format
158 	memset (&dsbuf, 0, sizeof(dsbuf));
159 
160 	dsbuf.dwSize = sizeof(DSBUFFERDESC);
161 	dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER;
162 	dsbuf.dwBufferBytes = 0;
163 	dsbuf.lpwfxFormat = NULL;
164 
165 	memset(&dsbcaps, 0, sizeof(dsbcaps));
166 	dsbcaps.dwSize = sizeof(dsbcaps);
167 	primary_format_set = false;
168 
169 	Com_DPrintf( "...creating primary buffer: " );
170 	ret = pDS->lpVtbl->CreateSoundBuffer (pDS, &dsbuf, &pDSPBuf, NULL);
171 	if (DS_OK == ret)
172 	{
173 		pformat = format;
174 
175 		Com_DPrintf( "ok\n" );
176 
177 		ret = pDSPBuf->lpVtbl->SetFormat (pDSPBuf, &pformat);
178 		if (DS_OK != ret)
179 		{
180 			if (snd_firsttime)
181 			{
182 				Com_DPrintf ("...setting primary sound format: ");
183 				Com_Printf ("failed (%s)\n", LOG_CLIENT, DSoundError(ret));
184 			}
185 		}
186 		else
187 		{
188 			if (snd_firsttime)
189 				Com_DPrintf ("...setting primary sound format: ok\n");
190 
191 			primary_format_set = true;
192 		}
193 	}
194 	else
195 	{
196 		Com_Printf ("failed (%s)\n", LOG_CLIENT, DSoundError(ret));
197 	}
198 
199 	if ( !primary_format_set || !s_primary->intvalue)
200 	{
201 		// create the secondary buffer we'll actually work with
202 		memset (&dsbuf, 0, sizeof(dsbuf));
203 		dsbuf.dwSize = sizeof(DSBUFFERDESC);
204 		dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE;
205 		dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE;
206 		dsbuf.lpwfxFormat = &format;
207 
208 		memset(&dsbcaps, 0, sizeof(dsbcaps));
209 		dsbcaps.dwSize = sizeof(dsbcaps);
210 
211 		Com_DPrintf( "...creating secondary buffer: " );
212 		ret = pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL);
213 		if (DS_OK != ret)
214 		{
215 			Com_Printf ("failed (%s)\n", LOG_CLIENT, DSoundError(ret));
216 			FreeSound ();
217 			return false;
218 		}
219 		Com_DPrintf( "ok\n" );
220 
221 		dma.channels = format.nChannels;
222 		dma.samplebits = format.wBitsPerSample;
223 		dma.speed = format.nSamplesPerSec;
224 
225 		if (DS_OK != pDSBuf->lpVtbl->GetCaps (pDSBuf, &dsbcaps))
226 		{
227 			Com_Printf ("*** GetCaps failed ***\n", LOG_CLIENT|LOG_ERROR);
228 			FreeSound ();
229 			return false;
230 		}
231 
232 		Com_DPrintf ("...using secondary sound buffer\n");
233 	}
234 	else
235 	{
236 		Com_DPrintf( "...using primary buffer\n" );
237 
238 		Com_DPrintf( "...setting WRITEPRIMARY coop level: " );
239 		pDS->lpVtbl->SetCooperativeLevel (pDS, cl_hwnd, DSSCL_WRITEPRIMARY);
240 		if (DS_OK != ret)
241 		{
242 			Com_Printf ("failed (%s)\n", LOG_CLIENT, DSoundError(ret));
243 			FreeSound ();
244 			return false;
245 		}
246 		Com_DPrintf( "ok\n" );
247 
248 		if (DS_OK != pDSPBuf->lpVtbl->GetCaps (pDSPBuf, &dsbcaps))
249 		{
250 			Com_Printf ("*** GetCaps failed ***\n", LOG_CLIENT|LOG_ERROR);
251 			return false;
252 		}
253 
254 		pDSBuf = pDSPBuf;
255 	}
256 
257 	// Make sure mixer is active
258 	pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
259 
260 	if (snd_firsttime)
261 		if (!cl_quietstartup->intvalue || developer->intvalue)
262 			Com_Printf("   %d channel(s)\n"
263 		               "   %d bits/sample\n"
264 					   "   %d bytes/sec\n", LOG_CLIENT|LOG_NOTICE,
265 					   dma.channels, dma.samplebits, dma.speed);
266 
267 	gSndBufSize = dsbcaps.dwBufferBytes;
268 
269 	/* we don't want anyone to access the buffer directly w/o locking it first. */
270 	lpData = NULL;
271 
272 	pDSBuf->lpVtbl->Stop(pDSBuf);
273 	pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmstarttime.u.sample, &dwWrite);
274 	pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
275 
276 	dma.samples = gSndBufSize/(dma.samplebits/8);
277 	dma.samplepos = 0;
278 	dma.submission_chunk = 1;
279 	dma.buffer = (unsigned char *) lpData;
280 	sample16 = (dma.samplebits/8) - 1;
281 
282 	return true;
283 }
284 
285 /*
286 ** DS_DestroyBuffers
287 */
DS_DestroyBuffers(void)288 static void DS_DestroyBuffers( void )
289 {
290 	Com_DPrintf( "Destroying DS buffers\n" );
291 	if ( pDS )
292 	{
293 		Com_DPrintf( "...setting NORMAL coop level\n" );
294 		pDS->lpVtbl->SetCooperativeLevel( pDS, cl_hwnd, DSSCL_NORMAL );
295 	}
296 
297 	if ( pDSBuf )
298 	{
299 		Com_DPrintf( "...stopping and releasing sound buffer\n" );
300 		pDSBuf->lpVtbl->Stop( pDSBuf );
301 		pDSBuf->lpVtbl->Release( pDSBuf );
302 	}
303 
304 	// only release primary buffer if it's not also the mixing buffer we just released
305 	if ( pDSPBuf && ( pDSBuf != pDSPBuf ) )
306 	{
307 		Com_DPrintf( "...releasing primary buffer\n" );
308 		pDSPBuf->lpVtbl->Release( pDSPBuf );
309 	}
310 	pDSBuf = NULL;
311 	pDSPBuf = NULL;
312 
313 	dma.buffer = NULL;
314 }
315 
316 /*
317 ==================
318 FreeSound
319 ==================
320 */
FreeSound(void)321 void FreeSound (void)
322 {
323 	int		i;
324 
325 	Com_DPrintf( "Shutting down sound system\n" );
326 
327 	if ( pDS )
328 		DS_DestroyBuffers();
329 
330 	if ( hWaveOut )
331 	{
332 		Com_DPrintf( "...resetting waveOut\n" );
333 		waveOutReset (hWaveOut);
334 
335 		if (lpWaveHdr)
336 		{
337 			Com_DPrintf( "...unpreparing headers\n" );
338 			for (i=0 ; i< WAV_BUFFERS ; i++)
339 				waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR));
340 		}
341 
342 		Com_DPrintf( "...closing waveOut\n" );
343 		waveOutClose (hWaveOut);
344 
345 		if (hWaveHdr)
346 		{
347 			Com_DPrintf( "...freeing WAV header\n" );
348 			GlobalUnlock(hWaveHdr);
349 			GlobalFree(hWaveHdr);
350 		}
351 
352 		if (hData)
353 		{
354 			Com_DPrintf( "...freeing WAV buffer\n" );
355 			GlobalUnlock(hData);
356 			GlobalFree(hData);
357 		}
358 
359 	}
360 
361 	if ( pDS )
362 	{
363 		Com_DPrintf( "...releasing DS object\n" );
364 		pDS->lpVtbl->Release( pDS );
365 	}
366 
367 /*	if ( hInstDS )
368 	{
369 		Com_DPrintf( "...freeing DSOUND.DLL\n" );
370 		FreeLibrary( hInstDS );
371 		hInstDS = NULL;
372 	}*/
373 
374 	pDS = NULL;
375 	pDSBuf = NULL;
376 	pDSPBuf = NULL;
377 	hWaveOut = 0;
378 	hData = 0;
379 	hWaveHdr = 0;
380 	lpData = NULL;
381 	lpWaveHdr = NULL;
382 	dsound_init = false;
383 	wav_init = false;
384 }
385 
386 /*
387 ==================
388 SNDDMA_InitDirect
389 
390 Direct-Sound support
391 ==================
392 */
SNDDMA_InitDirect(void)393 sndinitstat SNDDMA_InitDirect (void)
394 {
395 	DSCAPS			dscaps;
396 	HRESULT			hresult;
397 
398 	dma.channels = 2;
399 	dma.samplebits = 16;
400 
401 	if (s_khz->intvalue == 48)
402 		dma.speed = 48000;
403 	else if (s_khz->intvalue == 44)
404 		dma.speed = 44100;
405 	else if (s_khz->intvalue == 22)
406 		dma.speed = 22050;
407 	else
408 		dma.speed = 11025;
409 
410 	if (!cl_quietstartup->intvalue || developer->intvalue)
411 		Com_Printf( "Initializing DirectSound\n", LOG_CLIENT|LOG_NOTICE);
412 
413 	/*if ( !hInstDS )
414 	{
415 		Com_DPrintf( "...loading dsound.dll: " );
416 
417 		hInstDS = LoadLibrary("dsound.dll");
418 
419 		if (hInstDS == NULL)
420 		{
421 			Com_Printf ("failed (%d)\n", LOG_CLIENT, GetLastError());
422 			return SIS_FAILURE;
423 		}
424 
425 		Com_DPrintf ("ok\n");
426 
427 		//if (s_dx8->value)
428 		//	pDirectSoundCreate8 = (int (__stdcall *)(struct _GUID *,struct IDirectSound8 ** ,struct IUnknown *))GetProcAddress(hInstDS,"DirectSoundCreate8");
429 		//else
430 		pDirectSoundCreate = (HRESULT (__stdcall *)(GUID *, LPDIRECTSOUND *, IUnknown *))GetProcAddress(hInstDS,"DirectSoundCreate");
431 
432 		if (!pDirectSoundCreate)
433 		{
434 			Com_Printf ("*** couldn't get DS procaddr (do you have DirectX installed properly?) ***\n", LOG_CLIENT|LOG_ERROR);
435 			return SIS_FAILURE;
436 		}
437 	}*/
438 
439 	Com_DPrintf( "...creating DS object: " );
440 	while ( ( hresult = DirectSoundCreate ( NULL, &pDS, NULL ) ) != DS_OK )
441 	{
442 		if (hresult != DSERR_ALLOCATED)
443 		{
444 			Com_Printf( "failed (%d)\n", LOG_CLIENT, hresult );
445 			return SIS_FAILURE;
446 		}
447 
448 		if (MessageBox (NULL,
449 						"The sound hardware is in use by another app.\n\n"
450 					    "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
451 						"Sound not available",
452 						MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
453 		{
454 			Com_Printf ("failed to setup directsound, hardware already in use\n", LOG_CLIENT );
455 			return SIS_NOTAVAIL;
456 		}
457 	}
458 	Com_DPrintf( "ok\n" );
459 
460 	dscaps.dwSize = sizeof(dscaps);
461 
462 	if ( DS_OK != pDS->lpVtbl->GetCaps( pDS, &dscaps ) )
463 	{
464 		Com_Printf ("*** couldn't get DS caps ***\n", LOG_CLIENT|LOG_ERROR);
465 	}
466 
467 	if ( dscaps.dwFlags & DSCAPS_EMULDRIVER )
468 	{
469 		Com_DPrintf ("...no DSound driver found\n" );
470 		FreeSound();
471 		return SIS_FAILURE;
472 	}
473 
474 	if ( !DS_CreateBuffers() )
475 		return SIS_FAILURE;
476 
477 	dsound_init = true;
478 
479 	Com_DPrintf("...completed successfully\n" );
480 
481 	return SIS_SUCCESS;
482 }
483 
484 
485 /*
486 ==================
487 SNDDM_InitWav
488 
489 Crappy windows multimedia base
490 ==================
491 */
SNDDMA_InitWav(void)492 qboolean SNDDMA_InitWav (void)
493 {
494 	WAVEFORMATEX  format;
495 	int				i;
496 	HRESULT			hr;
497 
498 	Com_Printf( "Initializing wave sound\n", LOG_CLIENT|LOG_NOTICE);
499 
500 	snd_sent = 0;
501 	snd_completed = 0;
502 
503 	dma.channels = 2;
504 	dma.samplebits = 16;
505 
506 	if (s_khz->intvalue == 44)
507 		dma.speed = 44100;
508 	else if (s_khz->intvalue == 22)
509 		dma.speed = 22050;
510 	else
511 		dma.speed = 11025;
512 
513 	memset (&format, 0, sizeof(format));
514 	format.wFormatTag = WAVE_FORMAT_PCM;
515 	format.nChannels = dma.channels;
516 	format.wBitsPerSample = dma.samplebits;
517 	format.nSamplesPerSec = dma.speed;
518 	format.nBlockAlign = format.nChannels
519 		*format.wBitsPerSample / 8;
520 	format.cbSize = 0;
521 	format.nAvgBytesPerSec = format.nSamplesPerSec
522 		*format.nBlockAlign;
523 
524 	/* Open a waveform device for output using window callback. */
525 	Com_DPrintf ("...opening waveform device: ");
526 	while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER,
527 					&format,
528 					0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)
529 	{
530 		if (!SUCCEEDED (hr))
531 		{
532 			Com_Printf ("failed\n", LOG_CLIENT);
533 			return false;
534 		}
535 
536 		if (MessageBox (NULL,
537 						"The sound hardware is in use by another app.\n\n"
538 					    "Select Retry to try to start sound again or Cancel to run Quake 2 with no sound.",
539 						"Sound not available",
540 						MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
541 		{
542 			Com_Printf ("hw in use\n", LOG_CLIENT);
543 			return false;
544 		}
545 	}
546 	Com_DPrintf( "ok\n" );
547 
548 	/*
549 	 * Allocate and lock memory for the waveform data. The memory
550 	 * for waveform data must be globally allocated with
551 	 * GMEM_MOVEABLE and GMEM_SHARE flags.
552 
553 	*/
554 	Com_DPrintf ("...allocating waveform buffer: ");
555 	gSndBufSize = WAV_BUFFERS*WAV_BUFFER_SIZE;
556 	hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize);
557 	if (!hData)
558 	{
559 		Com_Printf( " failed\n", LOG_CLIENT);
560 		FreeSound ();
561 		return false;
562 	}
563 	Com_DPrintf( "ok\n" );
564 
565 	Com_DPrintf ("...locking waveform buffer: ");
566 	lpData = GlobalLock(hData);
567 	if (!lpData)
568 	{
569 		Com_Printf( " failed\n", LOG_CLIENT);
570 		FreeSound ();
571 		return false;
572 	}
573 	memset (lpData, 0, gSndBufSize);
574 	Com_DPrintf( "ok\n" );
575 
576 	/*
577 	 * Allocate and lock memory for the header. This memory must
578 	 * also be globally allocated with GMEM_MOVEABLE and
579 	 * GMEM_SHARE flags.
580 	 */
581 	Com_DPrintf ("...allocating waveform header: ");
582 	hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
583 		(DWORD) sizeof(WAVEHDR) * WAV_BUFFERS);
584 
585 	if (hWaveHdr == NULL)
586 	{
587 		Com_Printf( "failed\n", LOG_CLIENT);
588 		FreeSound ();
589 		return false;
590 	}
591 	Com_DPrintf( "ok\n" );
592 
593 	Com_DPrintf ("...locking waveform header: ");
594 	lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
595 
596 	if (lpWaveHdr == NULL)
597 	{
598 		Com_Printf( "failed\n", LOG_CLIENT);
599 		FreeSound ();
600 		return false;
601 	}
602 	memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
603 	Com_DPrintf( "ok\n" );
604 
605 	/* After allocation, set up and prepare headers. */
606 	Com_DPrintf ("...preparing headers: ");
607 	for (i=0 ; i<WAV_BUFFERS ; i++)
608 	{
609 		lpWaveHdr[i].dwBufferLength = WAV_BUFFER_SIZE;
610 		lpWaveHdr[i].lpData = lpData + i*WAV_BUFFER_SIZE;
611 
612 		if (waveOutPrepareHeader(hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)) !=
613 				MMSYSERR_NOERROR)
614 		{
615 			Com_Printf ("failed\n", LOG_CLIENT);
616 			FreeSound ();
617 			return false;
618 		}
619 	}
620 	Com_DPrintf ("ok\n");
621 
622 	dma.samples = gSndBufSize/(dma.samplebits/8);
623 	dma.samplepos = 0;
624 	dma.submission_chunk = 512;
625 	dma.buffer = (unsigned char *) lpData;
626 	sample16 = (dma.samplebits/8) - 1;
627 
628 	wav_init = true;
629 
630 	return true;
631 }
632 
633 /*
634 ==================
635 SNDDMA_Init
636 
637 Try to find a sound device to mix for.
638 Returns false if nothing is found.
639 ==================
640 */
SNDDMA_Init(int fullInit)641 int SNDDMA_Init (int fullInit)
642 {
643 	sndinitstat	stat;
644 
645 	if (fullInit != -1)
646 		snd_firsttime = fullInit;
647 
648 	memset ((void *)&dma, 0, sizeof (dma));
649 
650 	s_wavonly = Cvar_Get ("s_wavonly", "0", 0);
651 
652 	dsound_init = wav_init = 0;
653 
654 	stat = SIS_FAILURE;	// assume DirectSound won't initialize
655 
656 	/* Init DirectSound */
657 	if (!s_wavonly->intvalue)
658 	{
659 		if (snd_firsttime || snd_isdirect)
660 		{
661 			stat = SNDDMA_InitDirect ();
662 
663 			if (stat == SIS_SUCCESS)
664 			{
665 				snd_isdirect = true;
666 
667 				if (snd_firsttime)
668 					if (!cl_quietstartup->intvalue || developer->intvalue)
669 						Com_Printf ("dsound init succeeded\n", LOG_CLIENT|LOG_NOTICE );
670 			}
671 			else
672 			{
673 				snd_isdirect = false;
674 				Com_Printf ("*** dsound init failed ***\n", LOG_CLIENT|LOG_ERROR);
675 			}
676 		}
677 	}
678 
679 // if DirectSound didn't succeed in initializing, try to initialize
680 // waveOut sound, unless DirectSound failed because the hardware is
681 // already allocated (in which case the user has already chosen not
682 // to have sound)
683 	if (!dsound_init && (stat != SIS_NOTAVAIL))
684 	{
685 		Con_Print ("Initializing waveOut. Use snd_restart 1 to retry full sound init.\n");
686 		if (snd_firsttime || snd_iswave)
687 		{
688 
689 			snd_iswave = SNDDMA_InitWav ();
690 
691 			if (snd_iswave)
692 			{
693 				if (snd_firsttime)
694 					Com_Printf ("Wave sound init succeeded\n", LOG_CLIENT|LOG_NOTICE);
695 			}
696 			else
697 			{
698 				Com_Printf ("Wave sound init failed\n", LOG_CLIENT);
699 			}
700 		}
701 	}
702 
703 	snd_firsttime = false;
704 
705 	//snd_buffer_count = 1;
706 
707 	if (!dsound_init && !wav_init)
708 	{
709 		if (snd_firsttime)
710 			Com_Printf ("*** No sound device initialized ***\n", LOG_CLIENT|LOG_NOTICE);
711 
712 		return 0;
713 	}
714 
715 	return 1;
716 }
717 
718 /*
719 ==============
720 SNDDMA_GetDMAPos
721 
722 return the current sample position (in mono samples read)
723 inside the recirculating dma buffer, so the mixing code will know
724 how many sample are required to fill it up.
725 ===============
726 */
SNDDMA_GetDMAPos(void)727 int SNDDMA_GetDMAPos(void)
728 {
729 	MMTIME	mmtime;
730 	int		s;
731 	DWORD	dwWrite;
732 
733 	if (dsound_init)
734 	{
735 		mmtime.wType = TIME_SAMPLES;
736 		pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmtime.u.sample, &dwWrite);
737 		s = mmtime.u.sample - mmstarttime.u.sample;
738 	}
739 	else if (wav_init)
740 	{
741 		s = snd_sent * WAV_BUFFER_SIZE;
742 	}
743 
744 
745 	s >>= sample16;
746 
747 	s &= (dma.samples-1);
748 
749 	return s;
750 }
751 
752 /*
753 ==============
754 SNDDMA_BeginPainting
755 
756 Makes sure dma.buffer is valid
757 ===============
758 */
759 DWORD	locksize;
SNDDMA_BeginPainting(void)760 void SNDDMA_BeginPainting (void)
761 {
762 	int		reps;
763 	DWORD	dwSize2;
764 	DWORD	*pbuf, *pbuf2;
765 	HRESULT	hresult;
766 	DWORD	dwStatus;
767 
768 	if (!pDSBuf)
769 		return;
770 
771 	// if the buffer was lost or stopped, restore it and/or restart it
772 	if (pDSBuf->lpVtbl->GetStatus (pDSBuf, &dwStatus) != DS_OK)
773 		Com_Printf ("Couldn't get sound buffer status\n", LOG_CLIENT);
774 
775 	if (dwStatus & DSBSTATUS_BUFFERLOST)
776 		pDSBuf->lpVtbl->Restore (pDSBuf);
777 
778 	if (!(dwStatus & DSBSTATUS_PLAYING))
779 		pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
780 
781 	// lock the dsound buffer
782 
783 	reps = 0;
784 	dma.buffer = NULL;
785 
786 	while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, (LPVOID *)&pbuf, &locksize,
787 								   (LPVOID *)&pbuf2, &dwSize2, 0)) != DS_OK)
788 	{
789 		if (hresult != DSERR_BUFFERLOST)
790 		{
791 			Com_Printf( "S_TransferStereo16: Lock failed with error '%s'\n", LOG_CLIENT, DSoundError( hresult ) );
792 			S_Shutdown ();
793 			return;
794 		}
795 		else
796 		{
797 			pDSBuf->lpVtbl->Restore( pDSBuf );
798 		}
799 
800 		if (++reps > 2)
801 			return;
802 	}
803 	dma.buffer = (unsigned char *)pbuf;
804 }
805 
806 /*
807 ==============
808 SNDDMA_Submit
809 
810 Send sound to device if buffer isn't really the dma buffer
811 Also unlocks the dsound buffer
812 ===============
813 */
SNDDMA_Submit(void)814 void SNDDMA_Submit(void)
815 {
816 	LPWAVEHDR	h;
817 	int			wResult;
818 
819 	if (!dma.buffer)
820 		return;
821 
822 	// unlock the dsound buffer
823 	if (pDSBuf)
824 		pDSBuf->lpVtbl->Unlock(pDSBuf, dma.buffer, locksize, NULL, 0);
825 
826 	if (!wav_init)
827 		return;
828 
829 	//
830 	// find which sound blocks have completed
831 	//
832 	for (;;)
833 	{
834 		if ( snd_completed == snd_sent )
835 		{
836 			Com_DPrintf ("Sound overrun\n");
837 			break;
838 		}
839 
840 		if ( ! (lpWaveHdr[ snd_completed & WAV_MASK].dwFlags & WHDR_DONE) )
841 		{
842 			break;
843 		}
844 
845 		snd_completed++;	// this buffer has been played
846 	}
847 
848 //Com_Printf ("completed %i\n", snd_completed);
849 	//
850 	// submit a few new sound blocks
851 	//
852 	while (((snd_sent - snd_completed) >> sample16) < 8)
853 	{
854 		h = lpWaveHdr + ( snd_sent&WAV_MASK );
855 	if (paintedtime/256 <= snd_sent)
856 		break;	//	Com_Printf ("submit overrun\n");
857 //Com_Printf ("send %i\n", snd_sent);
858 		snd_sent++;
859 		/*
860 		 * Now the data block can be sent to the output device. The
861 		 * waveOutWrite function returns immediately and waveform
862 		 * data is sent to the output device in the background.
863 		 */
864 		wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR));
865 
866 		if (wResult != MMSYSERR_NOERROR)
867 		{
868 			Com_Printf ("Failed to write block to device\n", LOG_CLIENT);
869 			FreeSound ();
870 			return;
871 		}
872 	}
873 }
874 
875 /*
876 ==============
877 SNDDMA_Shutdown
878 
879 Reset the sound device for exiting
880 ===============
881 */
SNDDMA_Shutdown(void)882 void SNDDMA_Shutdown(void)
883 {
884 	FreeSound ();
885 }
886 
887 
888 /*
889 ===========
890 S_Activate
891 
892 Called when the main window gains or loses focus.
893 The window have been destroyed and recreated
894 between a deactivate and an activate.
895 ===========
896 */
S_Activate(qboolean active)897 void S_Activate (qboolean active)
898 {
899 	if ( active )
900 	{
901 		if ( pDS && cl_hwnd && snd_isdirect )
902 		{
903 			DS_CreateBuffers();
904 		}
905 	}
906 	else
907 	{
908 		if ( pDS && cl_hwnd && snd_isdirect )
909 		{
910 			DS_DestroyBuffers();
911 		}
912 	}
913 }
914 
915