Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

snd_win.c (18155B)


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