Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

win_snd.c (9489B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 #include <float.h>
     23 
     24 #include "../client/snd_local.h"
     25 #include "win_local.h"
     26 
     27 HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);
     28 #define iDirectSoundCreate(a,b,c)	pDirectSoundCreate(a,b,c)
     29 
     30 #define SECONDARY_BUFFER_SIZE	0x10000
     31 
     32 
     33 static qboolean	dsound_init;
     34 static int		sample16;
     35 static DWORD	gSndBufSize;
     36 static DWORD	locksize;
     37 static LPDIRECTSOUND pDS;
     38 static LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
     39 static HINSTANCE hInstDS;
     40 
     41 
     42 static const char *DSoundError( int error ) {
     43 	switch ( error ) {
     44 	case DSERR_BUFFERLOST:
     45 		return "DSERR_BUFFERLOST";
     46 	case DSERR_INVALIDCALL:
     47 		return "DSERR_INVALIDCALLS";
     48 	case DSERR_INVALIDPARAM:
     49 		return "DSERR_INVALIDPARAM";
     50 	case DSERR_PRIOLEVELNEEDED:
     51 		return "DSERR_PRIOLEVELNEEDED";
     52 	}
     53 
     54 	return "unknown";
     55 }
     56 
     57 /*
     58 ==================
     59 SNDDMA_Shutdown
     60 ==================
     61 */
     62 void SNDDMA_Shutdown( void ) {
     63 	Com_DPrintf( "Shutting down sound system\n" );
     64 
     65 	if ( pDS ) {
     66 		Com_DPrintf( "Destroying DS buffers\n" );
     67 		if ( pDS )
     68 		{
     69 			Com_DPrintf( "...setting NORMAL coop level\n" );
     70 			pDS->lpVtbl->SetCooperativeLevel( pDS, g_wv.hWnd, DSSCL_PRIORITY );
     71 		}
     72 
     73 		if ( pDSBuf )
     74 		{
     75 			Com_DPrintf( "...stopping and releasing sound buffer\n" );
     76 			pDSBuf->lpVtbl->Stop( pDSBuf );
     77 			pDSBuf->lpVtbl->Release( pDSBuf );
     78 		}
     79 
     80 		// only release primary buffer if it's not also the mixing buffer we just released
     81 		if ( pDSPBuf && ( pDSBuf != pDSPBuf ) )
     82 		{
     83 			Com_DPrintf( "...releasing primary buffer\n" );
     84 			pDSPBuf->lpVtbl->Release( pDSPBuf );
     85 		}
     86 		pDSBuf = NULL;
     87 		pDSPBuf = NULL;
     88 
     89 		dma.buffer = NULL;
     90 
     91 		Com_DPrintf( "...releasing DS object\n" );
     92 		pDS->lpVtbl->Release( pDS );
     93 	}
     94 
     95 	if ( hInstDS ) {
     96 		Com_DPrintf( "...freeing DSOUND.DLL\n" );
     97 		FreeLibrary( hInstDS );
     98 		hInstDS = NULL;
     99 	}
    100 
    101 	pDS = NULL;
    102 	pDSBuf = NULL;
    103 	pDSPBuf = NULL;
    104 	dsound_init = qfalse;
    105 	memset ((void *)&dma, 0, sizeof (dma));
    106 	CoUninitialize( );
    107 }
    108 
    109 /*
    110 ==================
    111 SNDDMA_Init
    112 
    113 Initialize direct sound
    114 Returns false if failed
    115 ==================
    116 */
    117 qboolean SNDDMA_Init(void) {
    118 
    119 	memset ((void *)&dma, 0, sizeof (dma));
    120 	dsound_init = 0;
    121 
    122 	CoInitialize(NULL);
    123 
    124 	if ( !SNDDMA_InitDS () ) {
    125 		return qfalse;
    126 	}
    127 
    128 	dsound_init = qtrue;
    129 
    130 	Com_DPrintf("Completed successfully\n" );
    131 
    132     return qtrue;
    133 }
    134 
    135 #undef DEFINE_GUID
    136 
    137 #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
    138         EXTERN_C const GUID name \
    139                 = { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } }
    140 
    141 // DirectSound Component GUID {47D4D946-62E8-11CF-93BC-444553540000}
    142 DEFINE_GUID(CLSID_DirectSound, 0x47d4d946, 0x62e8, 0x11cf, 0x93, 0xbc, 0x44, 0x45, 0x53, 0x54, 0x0, 0x0);
    143 
    144 // DirectSound 8.0 Component GUID {3901CC3F-84B5-4FA4-BA35-AA8172B8A09B}
    145 DEFINE_GUID(CLSID_DirectSound8, 0x3901cc3f, 0x84b5, 0x4fa4, 0xba, 0x35, 0xaa, 0x81, 0x72, 0xb8, 0xa0, 0x9b);
    146 
    147 DEFINE_GUID(IID_IDirectSound8, 0xC50A7E93, 0xF395, 0x4834, 0x9E, 0xF6, 0x7F, 0xA9, 0x9D, 0xE5, 0x09, 0x66);
    148 DEFINE_GUID(IID_IDirectSound, 0x279AFA83, 0x4981, 0x11CE, 0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60);
    149 
    150 
    151 int SNDDMA_InitDS ()
    152 {
    153 	HRESULT			hresult;
    154 	DSBUFFERDESC	dsbuf;
    155 	DSBCAPS			dsbcaps;
    156 	WAVEFORMATEX	format;
    157 	int				use8;
    158 
    159 	Com_Printf( "Initializing DirectSound\n");
    160 
    161 	use8 = 1;
    162     // Create IDirectSound using the primary sound device
    163     if( FAILED( hresult = CoCreateInstance(&CLSID_DirectSound8, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectSound8, (void **)&pDS))) {
    164 		use8 = 0;
    165 	    if( FAILED( hresult = CoCreateInstance(&CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectSound, (void **)&pDS))) {
    166 			Com_Printf ("failed\n");
    167 			SNDDMA_Shutdown ();
    168 			return qfalse;
    169 		}
    170 	}
    171 
    172 	hresult = pDS->lpVtbl->Initialize( pDS, NULL);
    173 
    174 	Com_DPrintf( "ok\n" );
    175 
    176 	Com_DPrintf("...setting DSSCL_PRIORITY coop level: " );
    177 
    178 	if ( DS_OK != pDS->lpVtbl->SetCooperativeLevel( pDS, g_wv.hWnd, DSSCL_PRIORITY ) )	{
    179 		Com_Printf ("failed\n");
    180 		SNDDMA_Shutdown ();
    181 		return qfalse;
    182 	}
    183 	Com_DPrintf("ok\n" );
    184 
    185 
    186 	// create the secondary buffer we'll actually work with
    187 	dma.channels = 2;
    188 	dma.samplebits = 16;
    189 
    190 //	if (s_khz->integer == 44)
    191 //		dma.speed = 44100;
    192 //	else if (s_khz->integer == 22)
    193 //		dma.speed = 22050;
    194 //	else
    195 //		dma.speed = 11025;
    196 
    197 	dma.speed = 22050;
    198 	memset (&format, 0, sizeof(format));
    199 	format.wFormatTag = WAVE_FORMAT_PCM;
    200     format.nChannels = dma.channels;
    201     format.wBitsPerSample = dma.samplebits;
    202     format.nSamplesPerSec = dma.speed;
    203     format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
    204     format.cbSize = 0;
    205     format.nAvgBytesPerSec = format.nSamplesPerSec*format.nBlockAlign; 
    206 
    207 	memset (&dsbuf, 0, sizeof(dsbuf));
    208 	dsbuf.dwSize = sizeof(DSBUFFERDESC);
    209 
    210 	// Micah: take advantage of 2D hardware.if available.
    211 	dsbuf.dwFlags = DSBCAPS_LOCHARDWARE;
    212 	if (use8) {
    213 		dsbuf.dwFlags |= DSBCAPS_GETCURRENTPOSITION2;
    214 	}
    215 	dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE;
    216 	dsbuf.lpwfxFormat = &format;
    217 	
    218 	memset(&dsbcaps, 0, sizeof(dsbcaps));
    219 	dsbcaps.dwSize = sizeof(dsbcaps);
    220 	
    221 	Com_DPrintf( "...creating secondary buffer: " );
    222 	if (DS_OK == pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL)) {
    223 		Com_Printf( "locked hardware.  ok\n" );
    224 	}
    225 	else {
    226 		// Couldn't get hardware, fallback to software.
    227 		dsbuf.dwFlags = DSBCAPS_LOCSOFTWARE;
    228 		if (use8) {
    229 			dsbuf.dwFlags |= DSBCAPS_GETCURRENTPOSITION2;
    230 		}
    231 		if (DS_OK != pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL)) {
    232 			Com_Printf( "failed\n" );
    233 			SNDDMA_Shutdown ();
    234 			return qfalse;
    235 		}
    236 		Com_DPrintf( "forced to software.  ok\n" );
    237 	}
    238 		
    239 	// Make sure mixer is active
    240 	if ( DS_OK != pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING) ) {
    241 		Com_Printf ("*** Looped sound play failed ***\n");
    242 		SNDDMA_Shutdown ();
    243 		return qfalse;
    244 	}
    245 
    246 	// get the returned buffer size
    247 	if ( DS_OK != pDSBuf->lpVtbl->GetCaps (pDSBuf, &dsbcaps) ) {
    248 		Com_Printf ("*** GetCaps failed ***\n");
    249 		SNDDMA_Shutdown ();
    250 		return qfalse;
    251 	}
    252 	
    253 	gSndBufSize = dsbcaps.dwBufferBytes;
    254 
    255 	dma.channels = format.nChannels;
    256 	dma.samplebits = format.wBitsPerSample;
    257 	dma.speed = format.nSamplesPerSec;
    258 	dma.samples = gSndBufSize/(dma.samplebits/8);
    259 	dma.submission_chunk = 1;
    260 	dma.buffer = NULL;			// must be locked first
    261 
    262 	sample16 = (dma.samplebits/8) - 1;
    263 
    264 	SNDDMA_BeginPainting ();
    265 	if (dma.buffer)
    266 		memset(dma.buffer, 0, dma.samples * dma.samplebits/8);
    267 	SNDDMA_Submit ();
    268 	return 1;
    269 }
    270 /*
    271 ==============
    272 SNDDMA_GetDMAPos
    273 
    274 return the current sample position (in mono samples read)
    275 inside the recirculating dma buffer, so the mixing code will know
    276 how many sample are required to fill it up.
    277 ===============
    278 */
    279 int SNDDMA_GetDMAPos( void ) {
    280 	MMTIME	mmtime;
    281 	int		s;
    282 	DWORD	dwWrite;
    283 
    284 	if ( !dsound_init ) {
    285 		return 0;
    286 	}
    287 
    288 	mmtime.wType = TIME_SAMPLES;
    289 	pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmtime.u.sample, &dwWrite);
    290 
    291 	s = mmtime.u.sample;
    292 
    293 	s >>= sample16;
    294 
    295 	s &= (dma.samples-1);
    296 
    297 	return s;
    298 }
    299 
    300 /*
    301 ==============
    302 SNDDMA_BeginPainting
    303 
    304 Makes sure dma.buffer is valid
    305 ===============
    306 */
    307 void SNDDMA_BeginPainting( void ) {
    308 	int		reps;
    309 	DWORD	dwSize2;
    310 	DWORD	*pbuf, *pbuf2;
    311 	HRESULT	hresult;
    312 	DWORD	dwStatus;
    313 
    314 	if ( !pDSBuf ) {
    315 		return;
    316 	}
    317 
    318 	// if the buffer was lost or stopped, restore it and/or restart it
    319 	if ( pDSBuf->lpVtbl->GetStatus (pDSBuf, &dwStatus) != DS_OK ) {
    320 		Com_Printf ("Couldn't get sound buffer status\n");
    321 	}
    322 	
    323 	if (dwStatus & DSBSTATUS_BUFFERLOST)
    324 		pDSBuf->lpVtbl->Restore (pDSBuf);
    325 	
    326 	if (!(dwStatus & DSBSTATUS_PLAYING))
    327 		pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
    328 
    329 	// lock the dsound buffer
    330 
    331 	reps = 0;
    332 	dma.buffer = NULL;
    333 
    334 	while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pbuf, &locksize, 
    335 								   &pbuf2, &dwSize2, 0)) != DS_OK)
    336 	{
    337 		if (hresult != DSERR_BUFFERLOST)
    338 		{
    339 			Com_Printf( "SNDDMA_BeginPainting: Lock failed with error '%s'\n", DSoundError( hresult ) );
    340 			S_Shutdown ();
    341 			return;
    342 		}
    343 		else
    344 		{
    345 			pDSBuf->lpVtbl->Restore( pDSBuf );
    346 		}
    347 
    348 		if (++reps > 2)
    349 			return;
    350 	}
    351 	dma.buffer = (unsigned char *)pbuf;
    352 }
    353 
    354 /*
    355 ==============
    356 SNDDMA_Submit
    357 
    358 Send sound to device if buffer isn't really the dma buffer
    359 Also unlocks the dsound buffer
    360 ===============
    361 */
    362 void SNDDMA_Submit( void ) {
    363     // unlock the dsound buffer
    364 	if ( pDSBuf ) {
    365 		pDSBuf->lpVtbl->Unlock(pDSBuf, dma.buffer, locksize, NULL, 0);
    366 	}
    367 }
    368 
    369 
    370 /*
    371 =================
    372 SNDDMA_Activate
    373 
    374 When we change windows we need to do this
    375 =================
    376 */
    377 void SNDDMA_Activate( void ) {
    378 	if ( !pDS ) {
    379 		return;
    380 	}
    381 
    382 	if ( DS_OK != pDS->lpVtbl->SetCooperativeLevel( pDS, g_wv.hWnd, DSSCL_PRIORITY ) )	{
    383 		Com_Printf ("sound SetCooperativeLevel failed\n");
    384 		SNDDMA_Shutdown ();
    385 	}
    386 }
    387 
    388