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