snd_system.cpp (16241B)
1 /* 2 =========================================================================== 3 4 Doom 3 BFG Edition GPL Source Code 5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 6 7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). 8 9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation, either version 3 of the License, or 12 (at your option) any later version. 13 14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>. 21 22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. 23 24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. 25 26 =========================================================================== 27 */ 28 #pragma hdrstop 29 #include "../idlib/precompiled.h" 30 31 #include "snd_local.h" 32 33 idCVar s_noSound( "s_noSound", "0", CVAR_BOOL, "returns NULL for all sounds loaded and does not update the sound rendering" ); 34 35 #ifdef ID_RETAIL 36 idCVar s_useCompression( "s_useCompression", "1", CVAR_BOOL, "Use compressed sound files (mp3/xma)" ); 37 idCVar s_playDefaultSound( "s_playDefaultSound", "0", CVAR_BOOL, "play a beep for missing sounds" ); 38 idCVar s_maxSamples( "s_maxSamples", "5", CVAR_INTEGER, "max samples to load per shader" ); 39 #else 40 idCVar s_useCompression( "s_useCompression", "1", CVAR_BOOL, "Use compressed sound files (mp3/xma)" ); 41 idCVar s_playDefaultSound( "s_playDefaultSound", "1", CVAR_BOOL, "play a beep for missing sounds" ); 42 idCVar s_maxSamples( "s_maxSamples", "5", CVAR_INTEGER, "max samples to load per shader" ); 43 #endif 44 45 idCVar preLoad_Samples( "preLoad_Samples", "1", CVAR_SYSTEM | CVAR_BOOL, "preload samples during beginlevelload" ); 46 47 idSoundSystemLocal soundSystemLocal; 48 idSoundSystem * soundSystem = &soundSystemLocal; 49 50 /* 51 ================================================================================================ 52 53 idSoundSystemLocal 54 55 ================================================================================================ 56 */ 57 58 /* 59 ======================== 60 TestSound_f 61 62 This is called from the main thread. 63 ======================== 64 */ 65 void TestSound_f( const idCmdArgs & args ) { 66 if ( args.Argc() != 2 ) { 67 idLib::Printf( "Usage: testSound <file>\n" ); 68 return; 69 } 70 if ( soundSystemLocal.currentSoundWorld ) { 71 soundSystemLocal.currentSoundWorld->PlayShaderDirectly( args.Argv( 1 ) ); 72 } 73 } 74 75 /* 76 ======================== 77 RestartSound_f 78 ======================== 79 */ 80 void RestartSound_f( const idCmdArgs & args ) { 81 soundSystemLocal.Restart(); 82 } 83 84 /* 85 ======================== 86 ListSamples_f 87 88 ======================== 89 */ 90 void ListSamples_f( const idCmdArgs & args ) { 91 idLib::Printf( "Sound samples\n-------------\n" ); 92 int totSize = 0; 93 for ( int i = 0; i < soundSystemLocal.samples.Num(); i++ ) { 94 idLib::Printf( "%05dkb\t%s\n", soundSystemLocal.samples[ i ]->BufferSize() / 1024, soundSystemLocal.samples[ i ]->GetName() ); 95 totSize += soundSystemLocal.samples[ i ]->BufferSize(); 96 } 97 idLib::Printf( "--------------------------\n" ); 98 idLib::Printf( "%05dkb total size\n", totSize / 1024 ); 99 } 100 101 /* 102 ======================== 103 idSoundSystemLocal::Restart 104 ======================== 105 */ 106 void idSoundSystemLocal::Restart() { 107 108 // Mute all channels in all worlds 109 for ( int i = 0; i < soundWorlds.Num(); i++ ) { 110 idSoundWorldLocal * sw = soundWorlds[i]; 111 for ( int e = 0; e < sw->emitters.Num(); e++ ) { 112 idSoundEmitterLocal * emitter = sw->emitters[e]; 113 for ( int c = 0; c < emitter->channels.Num(); c++ ) { 114 emitter->channels[c]->Mute(); 115 } 116 } 117 } 118 // Shutdown sound hardware 119 hardware.Shutdown(); 120 // Reinitialize sound hardware 121 if ( !s_noSound.GetBool() ) { 122 hardware.Init(); 123 } 124 125 InitStreamBuffers(); 126 } 127 128 /* 129 ======================== 130 idSoundSystemLocal::Init 131 132 Initialize the SoundSystem. 133 ======================== 134 */ 135 void idSoundSystemLocal::Init() { 136 137 idLib::Printf( "----- Initializing Sound System ------\n" ); 138 139 soundTime = Sys_Milliseconds(); 140 random.SetSeed( soundTime ); 141 142 if ( !s_noSound.GetBool() ) { 143 hardware.Init(); 144 InitStreamBuffers(); 145 } 146 147 cmdSystem->AddCommand( "testSound", TestSound_f, 0, "tests a sound", idCmdSystem::ArgCompletion_SoundName ); 148 cmdSystem->AddCommand( "s_restart", RestartSound_f, 0, "restart sound system" ); 149 cmdSystem->AddCommand( "listSamples", ListSamples_f, 0, "lists all loaded sound samples" ); 150 151 idLib::Printf( "sound system initialized.\n" ); 152 idLib::Printf( "--------------------------------------\n" ); 153 } 154 155 /* 156 ======================== 157 idSoundSystemLocal::InitStreamBuffers 158 ======================== 159 */ 160 void idSoundSystemLocal::InitStreamBuffers() { 161 streamBufferMutex.Lock(); 162 const bool empty = ( bufferContexts.Num() == 0 ); 163 if ( empty ) { 164 bufferContexts.SetNum( MAX_SOUND_BUFFERS ); 165 for ( int i = 0; i < MAX_SOUND_BUFFERS; i++ ) { 166 freeStreamBufferContexts.Append( &( bufferContexts[ i ] ) ); 167 } 168 } else { 169 for ( int i = 0; i < activeStreamBufferContexts.Num(); i++ ) { 170 freeStreamBufferContexts.Append( activeStreamBufferContexts[ i ] ); 171 } 172 activeStreamBufferContexts.Clear(); 173 } 174 assert( bufferContexts.Num() == MAX_SOUND_BUFFERS ); 175 assert( freeStreamBufferContexts.Num() == MAX_SOUND_BUFFERS ); 176 assert( activeStreamBufferContexts.Num() == 0 ); 177 streamBufferMutex.Unlock(); 178 } 179 180 /* 181 ======================== 182 idSoundSystemLocal::FreeStreamBuffers 183 ======================== 184 */ 185 void idSoundSystemLocal::FreeStreamBuffers() { 186 streamBufferMutex.Lock(); 187 bufferContexts.Clear(); 188 freeStreamBufferContexts.Clear(); 189 activeStreamBufferContexts.Clear(); 190 streamBufferMutex.Unlock(); 191 } 192 193 /* 194 ======================== 195 idSoundSystemLocal::Shutdown 196 ======================== 197 */ 198 void idSoundSystemLocal::Shutdown() { 199 hardware.Shutdown(); 200 FreeStreamBuffers(); 201 samples.DeleteContents( true ); 202 sampleHash.Free(); 203 } 204 205 /* 206 ======================== 207 idSoundSystemLocal::ObtainStreamBuffer 208 209 Get a stream buffer from the free pool, returns NULL if none are available 210 ======================== 211 */ 212 idSoundSystemLocal::bufferContext_t * idSoundSystemLocal::ObtainStreamBufferContext() { 213 bufferContext_t * bufferContext = NULL; 214 streamBufferMutex.Lock(); 215 if ( freeStreamBufferContexts.Num() != 0 ) { 216 bufferContext = freeStreamBufferContexts[ freeStreamBufferContexts.Num() - 1 ]; 217 freeStreamBufferContexts.SetNum( freeStreamBufferContexts.Num() - 1 ); 218 activeStreamBufferContexts.Append( bufferContext ); 219 } 220 streamBufferMutex.Unlock(); 221 return bufferContext; 222 } 223 224 /* 225 ======================== 226 idSoundSystemLocal::ReleaseStreamBuffer 227 228 Releases a stream buffer back to the free pool 229 ======================== 230 */ 231 void idSoundSystemLocal::ReleaseStreamBufferContext( bufferContext_t * bufferContext ) { 232 streamBufferMutex.Lock(); 233 if ( activeStreamBufferContexts.Remove( bufferContext ) ) { 234 freeStreamBufferContexts.Append( bufferContext ); 235 } 236 streamBufferMutex.Unlock(); 237 } 238 239 /* 240 ======================== 241 idSoundSystemLocal::AllocSoundWorld 242 ======================== 243 */ 244 idSoundWorld * idSoundSystemLocal::AllocSoundWorld( idRenderWorld *rw ) { 245 idSoundWorldLocal * local = new (TAG_AUDIO) idSoundWorldLocal; 246 local->renderWorld = rw; 247 soundWorlds.Append( local ); 248 return local; 249 } 250 251 /* 252 ======================== 253 idSoundSystemLocal::FreeSoundWorld 254 ======================== 255 */ 256 void idSoundSystemLocal::FreeSoundWorld( idSoundWorld *sw ) { 257 idSoundWorldLocal *local = static_cast<idSoundWorldLocal*>( sw ); 258 soundWorlds.Remove( local ); 259 delete local; 260 } 261 262 /* 263 ======================== 264 idSoundSystemLocal::SetPlayingSoundWorld 265 266 Specifying NULL will cause silence to be played. 267 ======================== 268 */ 269 void idSoundSystemLocal::SetPlayingSoundWorld( idSoundWorld *soundWorld ) { 270 if ( currentSoundWorld == soundWorld ) { 271 return; 272 } 273 idSoundWorldLocal * oldSoundWorld = currentSoundWorld; 274 275 currentSoundWorld = static_cast<idSoundWorldLocal *>( soundWorld ); 276 277 if ( oldSoundWorld != NULL ) { 278 oldSoundWorld->Update(); 279 } 280 } 281 282 /* 283 ======================== 284 idSoundSystemLocal::GetPlayingSoundWorld 285 ======================== 286 */ 287 idSoundWorld * idSoundSystemLocal::GetPlayingSoundWorld() { 288 return currentSoundWorld; 289 } 290 291 /* 292 ======================== 293 idSoundSystemLocal::Render 294 ======================== 295 */ 296 void idSoundSystemLocal::Render() { 297 298 if ( s_noSound.GetBool() ) { 299 return; 300 } 301 302 if ( needsRestart ) { 303 needsRestart = false; 304 Restart(); 305 } 306 307 SCOPED_PROFILE_EVENT( "SoundSystem::Render" ); 308 309 if ( currentSoundWorld != NULL ) { 310 currentSoundWorld->Update(); 311 } 312 313 hardware.Update(); 314 315 // The sound system doesn't use game time or anything like that because the sounds are decoded in real time. 316 soundTime = Sys_Milliseconds(); 317 } 318 319 /* 320 ======================== 321 idSoundSystemLocal::OnReloadSound 322 ======================== 323 */ 324 void idSoundSystemLocal::OnReloadSound( const idDecl* sound ) { 325 for ( int i = 0; i < soundWorlds.Num(); i++ ) { 326 soundWorlds[i]->OnReloadSound( sound ); 327 } 328 } 329 330 /* 331 ======================== 332 idSoundSystemLocal::StopAllSounds 333 ======================== 334 */ 335 void idSoundSystemLocal::StopAllSounds() { 336 for ( int i = 0; i < soundWorlds.Num(); i++ ) { 337 idSoundWorld *sw = soundWorlds[i]; 338 if ( sw ) { 339 sw->StopAllSounds(); 340 } 341 } 342 hardware.Update(); 343 } 344 345 /* 346 ======================== 347 idSoundSystemLocal::GetIXAudio2 348 ======================== 349 */ 350 void * idSoundSystemLocal::GetIXAudio2() const { 351 return (void *)hardware.GetIXAudio2(); 352 } 353 354 /* 355 ======================== 356 idSoundSystemLocal::SoundTime 357 ======================== 358 */ 359 int idSoundSystemLocal::SoundTime() const { 360 return soundTime; 361 } 362 363 /* 364 ======================== 365 idSoundSystemLocal::AllocateVoice 366 ======================== 367 */ 368 idSoundVoice * idSoundSystemLocal::AllocateVoice( const idSoundSample * leadinSample, const idSoundSample * loopingSample ) { 369 return hardware.AllocateVoice( leadinSample, loopingSample ); 370 } 371 372 /* 373 ======================== 374 idSoundSystemLocal::FreeVoice 375 ======================== 376 */ 377 void idSoundSystemLocal::FreeVoice( idSoundVoice * voice ) { 378 hardware.FreeVoice( voice ); 379 } 380 381 /* 382 ======================== 383 idSoundSystemLocal::LoadSample 384 ======================== 385 */ 386 idSoundSample * idSoundSystemLocal::LoadSample( const char * name ) { 387 idStrStatic< MAX_OSPATH > canonical = name; 388 canonical.ToLower(); 389 canonical.BackSlashesToSlashes(); 390 canonical.StripFileExtension(); 391 int hashKey = idStr::Hash( canonical ); 392 for ( int i = sampleHash.First( hashKey ); i != -1; i = sampleHash.Next( i ) ) { 393 if ( idStr::Cmp( samples[i]->GetName(), canonical ) == 0 ) { 394 samples[i]->SetLevelLoadReferenced(); 395 return samples[i]; 396 } 397 } 398 idSoundSample * sample = new (TAG_AUDIO) idSoundSample; 399 sample->SetName( canonical ); 400 sampleHash.Add( hashKey, samples.Append( sample ) ); 401 if ( !insideLevelLoad ) { 402 // Sound sample referenced before any map is loaded 403 sample->SetNeverPurge(); 404 sample->LoadResource(); 405 } else { 406 sample->SetLevelLoadReferenced(); 407 } 408 409 if ( cvarSystem->GetCVarBool( "fs_buildgame" ) ) { 410 fileSystem->AddSamplePreload( canonical ); 411 } 412 413 return sample; 414 } 415 416 /* 417 ======================== 418 idSoundSystemLocal::StopVoicesWithSample 419 420 A sample is about to be freed, make sure the hardware isn't mixing from it. 421 ======================== 422 */ 423 void idSoundSystemLocal::StopVoicesWithSample( const idSoundSample * const sample ) { 424 for ( int w = 0; w < soundWorlds.Num(); w++ ) { 425 idSoundWorldLocal * sw = soundWorlds[w]; 426 if ( sw == NULL ) { 427 continue; 428 } 429 for ( int e = 0; e < sw->emitters.Num(); e++ ) { 430 idSoundEmitterLocal * emitter = sw->emitters[e]; 431 if ( emitter == NULL ) { 432 continue; 433 } 434 for ( int i = 0; i < emitter->channels.Num(); i++ ) { 435 if ( emitter->channels[i]->leadinSample == sample || emitter->channels[i]->loopingSample == sample ) { 436 emitter->channels[i]->Mute(); 437 } 438 } 439 } 440 } 441 } 442 443 /* 444 ======================== 445 idSoundSystemLocal::FreeVoice 446 ======================== 447 */ 448 cinData_t idSoundSystemLocal::ImageForTime( const int milliseconds, const bool waveform ) { 449 cinData_t cd; 450 cd.imageY = NULL; 451 cd.imageCr = NULL; 452 cd.imageCb = NULL; 453 cd.imageWidth = 0; 454 cd.imageHeight = 0; 455 cd.status = FMV_IDLE; 456 return cd; 457 } 458 459 /* 460 ======================== 461 idSoundSystemLocal::BeginLevelLoad 462 ======================== 463 */ 464 void idSoundSystemLocal::BeginLevelLoad() { 465 insideLevelLoad = true; 466 for ( int i = 0; i < samples.Num(); i++ ) { 467 if ( samples[i]->GetNeverPurge() ) { 468 continue; 469 } 470 samples[i]->FreeData(); 471 samples[i]->ResetLevelLoadReferenced(); 472 } 473 } 474 475 476 477 /* 478 ======================== 479 idSoundSystemLocal::Preload 480 ======================== 481 */ 482 void idSoundSystemLocal::Preload( idPreloadManifest & manifest ) { 483 484 idStrStatic< MAX_OSPATH > filename; 485 486 int start = Sys_Milliseconds(); 487 int numLoaded = 0; 488 489 idList< preloadSort_t > preloadSort; 490 preloadSort.Resize( manifest.NumResources() ); 491 for ( int i = 0; i < manifest.NumResources(); i++ ) { 492 const preloadEntry_s & p = manifest.GetPreloadByIndex( i ); 493 idResourceCacheEntry rc; 494 // FIXME: write these out sorted 495 if ( p.resType == PRELOAD_SAMPLE ) { 496 if ( p.resourceName.Find( "/vo/", false ) >= 0 ) { 497 continue; 498 } 499 filename = "generated/"; 500 filename += p.resourceName; 501 filename.SetFileExtension( "idwav" ); 502 if ( fileSystem->GetResourceCacheEntry( filename, rc ) ) { 503 preloadSort_t ps = {}; 504 ps.idx = i; 505 ps.ofs = rc.offset; 506 preloadSort.Append( ps ); 507 } 508 } 509 } 510 511 preloadSort.SortWithTemplate( idSort_Preload() ); 512 513 for ( int i = 0; i < preloadSort.Num(); i++ ) { 514 const preloadSort_t & ps = preloadSort[ i ]; 515 const preloadEntry_s & p = manifest.GetPreloadByIndex( ps.idx ); 516 filename = p.resourceName; 517 filename.Replace( "generated/", "" ); 518 numLoaded++; 519 idSoundSample *sample = LoadSample( filename ); 520 if ( sample != NULL && !sample->IsLoaded() ) { 521 sample->LoadResource(); 522 sample->SetLevelLoadReferenced(); 523 } 524 } 525 526 int end = Sys_Milliseconds(); 527 common->Printf( "%05d sounds preloaded in %5.1f seconds\n", numLoaded, ( end - start ) * 0.001 ); 528 common->Printf( "----------------------------------------\n" ); 529 } 530 531 /* 532 ======================== 533 idSoundSystemLocal::EndLevelLoad 534 ======================== 535 */ 536 void idSoundSystemLocal::EndLevelLoad() { 537 538 insideLevelLoad = false; 539 540 common->Printf( "----- idSoundSystemLocal::EndLevelLoad -----\n" ); 541 int start = Sys_Milliseconds(); 542 int keepCount = 0; 543 int loadCount = 0; 544 545 idList< preloadSort_t > preloadSort; 546 preloadSort.Resize( samples.Num() ); 547 548 for ( int i = 0; i < samples.Num(); i++ ) { 549 common->UpdateLevelLoadPacifier(); 550 551 552 if ( samples[i]->GetNeverPurge() ) { 553 continue; 554 } 555 if ( samples[i]->IsLoaded() ) { 556 keepCount++; 557 continue; 558 } 559 if ( samples[i]->GetLevelLoadReferenced() ) { 560 idStrStatic< MAX_OSPATH > filename = "generated/"; 561 filename += samples[ i ]->GetName(); 562 filename.SetFileExtension( "idwav" ); 563 preloadSort_t ps = {}; 564 ps.idx = i; 565 idResourceCacheEntry rc; 566 if ( fileSystem->GetResourceCacheEntry( filename, rc ) ) { 567 ps.ofs = rc.offset; 568 } else { 569 ps.ofs = 0; 570 } 571 preloadSort.Append( ps ); 572 loadCount++; 573 } 574 } 575 preloadSort.SortWithTemplate( idSort_Preload() ); 576 for ( int i = 0; i < preloadSort.Num(); i++ ) { 577 common->UpdateLevelLoadPacifier(); 578 579 580 samples[ preloadSort[ i ].idx ]->LoadResource(); 581 } 582 int end = Sys_Milliseconds(); 583 584 common->Printf( "%5i sounds loaded in %5.1f seconds\n", loadCount, (end-start) * 0.001 ); 585 common->Printf( "----------------------------------------\n" ); 586 } 587 588 589 590 /* 591 ======================== 592 idSoundSystemLocal::FreeVoice 593 ======================== 594 */ 595 void idSoundSystemLocal::PrintMemInfo( MemInfo_t *mi ) { 596 }