DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

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 }