DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

snd_emitter.cpp (27800B)


      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 
     29 #pragma hdrstop
     30 #include "../idlib/precompiled.h"
     31 
     32 #include "snd_local.h"
     33 
     34 idCVar s_singleEmitter( "s_singleEmitter", "0", CVAR_INTEGER, "mute all sounds but this emitter" );
     35 idCVar s_showStartSound( "s_showStartSound", "0", CVAR_BOOL, "print a message every time a sound starts/stops" );
     36 idCVar s_useOcclusion( "s_useOcclusion", "1", CVAR_BOOL, "Attenuate sounds based on walls" );
     37 idCVar s_centerFractionVO( "s_centerFractionVO", "0.75", CVAR_FLOAT, "Portion of VO sounds routed to the center channel" );
     38 
     39 extern idCVar s_playDefaultSound;
     40 extern idCVar s_noSound;
     41 
     42 /*
     43 ================================================================================================
     44 
     45 	idSoundFade
     46 
     47 ================================================================================================
     48 */
     49 
     50 /*
     51 ========================
     52 idSoundFade::Clear
     53 ========================
     54 */
     55 void idSoundFade::Clear() {
     56 	fadeStartTime = 0;
     57 	fadeEndTime = 0;
     58 	fadeStartVolume = 0.0f;
     59 	fadeEndVolume = 0.0f;
     60 }
     61 
     62 /*
     63 ========================
     64 idSoundFade::SetVolume
     65 ========================
     66 */
     67 void idSoundFade::SetVolume( float to ) {
     68 	fadeStartVolume = to;
     69 	fadeEndVolume = to;
     70 	fadeStartTime = 0;
     71 	fadeEndTime = 0;
     72 }
     73 
     74 /*
     75 ========================
     76 idSoundFade::Fade
     77 ========================
     78 */
     79 void idSoundFade::Fade( float to, int length, int soundTime ) {
     80 	int startTime = soundTime;
     81 	// if it is already fading to this volume at this rate, don't change it
     82 	if ( fadeEndTime == startTime + length && fadeEndVolume == to ) {
     83 		return;
     84 	}
     85 	fadeStartVolume = GetVolume( soundTime );
     86 	fadeEndVolume = to;
     87 	fadeStartTime = startTime;
     88 	fadeEndTime = startTime + length;
     89 }
     90 
     91 /*
     92 ========================
     93 idSoundFade::GetVolume
     94 ========================
     95 */
     96 float idSoundFade::GetVolume( const int soundTime ) const {
     97 	const float fadeDuration = ( fadeEndTime - fadeStartTime );
     98 	const int currentTime = soundTime;
     99 	const float playTime = ( currentTime - fadeStartTime );
    100 	if ( fadeDuration <= 0.0f ) {
    101 		return fadeEndVolume;
    102 	} else if ( currentTime >= fadeEndTime ) {
    103 		return fadeEndVolume;
    104 	} else if ( currentTime > fadeStartTime ) {
    105 		return fadeStartVolume + ( fadeEndVolume - fadeStartVolume ) * playTime / fadeDuration;
    106 	} else {
    107 		return fadeStartVolume;
    108 	}
    109 }
    110 
    111 /*
    112 ========================
    113 idSoundChannel::idSoundChannel
    114 ========================
    115 */
    116 idSoundChannel::idSoundChannel() {
    117 	emitter = NULL;
    118 	hardwareVoice = NULL;
    119 
    120 	startTime = 0;
    121 	endTime = 0;
    122 	leadinSample = NULL;
    123 	loopingSample = NULL;
    124 	logicalChannel = SCHANNEL_ANY;
    125 	allowSlow = false;
    126 	soundShader = NULL;
    127 
    128 	volumeFade.Clear();
    129 
    130 	volumeDB = DB_SILENCE;
    131 	currentAmplitude = 0.0f;
    132 }
    133 
    134 /*
    135 ========================
    136 idSoundChannel::~idSoundChannel
    137 ========================
    138 */
    139 idSoundChannel::~idSoundChannel() {
    140 }
    141 
    142 /*
    143 ========================
    144 idSoundChannel::CanMute
    145 Never actually mute VO because we can't restart them precisely enough for lip syncing to not fuck up
    146 ========================
    147 */
    148 bool idSoundChannel::CanMute() const {
    149 	return true;
    150 }
    151 
    152 /*
    153 ========================
    154 idSoundChannel::Mute
    155 
    156 A muted sound is considered still running, and can restart when a listener
    157 gets close enough.
    158 ========================
    159 */
    160 void idSoundChannel::Mute() {
    161 	if ( hardwareVoice != NULL ) {
    162 		soundSystemLocal.FreeVoice( hardwareVoice );
    163 		hardwareVoice = NULL;
    164 	}
    165 }
    166 
    167 /*
    168 ========================
    169 idSoundChannel::IsLooping
    170 ========================
    171 */
    172 bool idSoundChannel::IsLooping() const {
    173 	return ( parms.soundShaderFlags & SSF_LOOPING ) != 0;
    174 }
    175 
    176 /*
    177 ========================
    178 idSoundChannel::CheckForCompletion
    179 ========================
    180 */
    181 bool idSoundChannel::CheckForCompletion( int currentTime ) {
    182 	if ( leadinSample == NULL ) {
    183 		return true;
    184 	}
    185 	// endTime of 0 indicates a sound should loop forever
    186 	if ( endTime > 0 && endTime < currentTime ) {
    187 		return true;
    188 	}
    189 	return false;
    190 }
    191 
    192 /*
    193 ========================
    194 idSoundChannel::UpdateVolume
    195 ========================
    196 */
    197 void idSoundChannel::UpdateVolume( int currentTime ) {
    198 	idSoundWorldLocal * soundWorld = emitter->soundWorld;
    199 
    200 	volumeDB = DB_SILENCE;
    201 	currentAmplitude = 0.0f;
    202 
    203 	if ( leadinSample == NULL ) {
    204 		return;
    205 	}
    206 	if ( startTime > currentTime ) {
    207 		return;
    208 	}
    209 	if ( endTime > 0 && endTime < currentTime ) {
    210 		return;
    211 	}
    212 
    213 	// if you don't want to hear all the beeps from missing sounds
    214 	if ( leadinSample->IsDefault() && !s_playDefaultSound.GetBool() ) {
    215 		return;
    216 	}
    217 
    218 	bool emitterIsListener = ( emitter->emitterId == soundWorld->listener.id );
    219 
    220 	// if it is a private sound, set the volume to zero unless we match the listener.id
    221 	if ( parms.soundShaderFlags & SSF_PRIVATE_SOUND ) {
    222 		if ( !emitterIsListener ) {
    223 			return;
    224 		}
    225 	}
    226 	if ( parms.soundShaderFlags & SSF_ANTI_PRIVATE_SOUND ) {
    227 		if ( emitterIsListener ) {
    228 			return;
    229 		}
    230 	}
    231 
    232 	// volume fading
    233 	float newVolumeDB = parms.volume;
    234 	newVolumeDB += volumeFade.GetVolume( currentTime );
    235 	newVolumeDB += soundWorld->volumeFade.GetVolume( currentTime );
    236 	newVolumeDB += soundWorld->pauseFade.GetVolume( currentTime );
    237 	if ( parms.soundClass >= 0 && parms.soundClass < SOUND_MAX_CLASSES ) {
    238 		newVolumeDB += soundWorld->soundClassFade[parms.soundClass].GetVolume( currentTime );
    239 	}
    240 
    241 	bool global = ( parms.soundShaderFlags & SSF_GLOBAL ) != 0;
    242 
    243 	// attenuation
    244 	if ( !global && !emitterIsListener ) {
    245 		float distance = ( parms.soundShaderFlags & SSF_NO_OCCLUSION ) == 0 ? emitter->spatializedDistance : emitter->directDistance;
    246 		float mindist = parms.minDistance;
    247 		float maxdist = parms.maxDistance;
    248 		if ( distance >= maxdist ) {
    249 			newVolumeDB = DB_SILENCE;
    250 		} else if ( ( distance > mindist ) && ( maxdist > mindist ) ) {
    251 			float f = ( distance - mindist ) / ( maxdist - mindist );
    252 			newVolumeDB += LinearToDB( Square( 1.0f - f ) );
    253 		}
    254 	}
    255 
    256 	if ( soundSystemLocal.musicMuted && ( parms.soundShaderFlags & SSF_MUSIC ) != 0 ) {
    257 		newVolumeDB = DB_SILENCE;
    258 	}
    259 
    260 	// store the new volume on the channel
    261 	volumeDB = newVolumeDB;
    262 
    263 	// keep track of the maximum volume
    264 	float currentVolumeDB = newVolumeDB;
    265 	if ( hardwareVoice != NULL ) {
    266 		float amplitude = hardwareVoice->GetAmplitude();
    267 		if ( amplitude <= 0.0f ) {
    268 			currentVolumeDB = DB_SILENCE;
    269 		} else {
    270 			currentVolumeDB += LinearToDB( amplitude );
    271 		}
    272 		currentAmplitude = amplitude;
    273 	}
    274 }
    275 
    276 /*
    277 ========================
    278 idSoundChannel::UpdateHardware
    279 ========================
    280 */
    281 void idSoundChannel::UpdateHardware( float volumeAdd, int currentTime ) {
    282 	idSoundWorldLocal * soundWorld = emitter->soundWorld;
    283 
    284 	if ( soundWorld == NULL ) {
    285 		return;
    286 	}
    287 	if ( leadinSample == NULL ) {
    288 		return;
    289 	}
    290 	if ( startTime > currentTime ) {
    291 		return;
    292 	}
    293 	if ( endTime > 0 && endTime < currentTime ) {
    294 		return;
    295 	}
    296 
    297 	// convert volumes from decibels to linear
    298 	float volume = Max( 0.0f, DBtoLinear( volumeDB + volumeAdd ) );
    299 
    300 	if ( ( parms.soundShaderFlags & SSF_UNCLAMPED ) == 0 ) {
    301 		volume = Min( 1.0f, volume );
    302 	}
    303 
    304 	bool global = ( parms.soundShaderFlags & SSF_GLOBAL ) != 0;
    305 	bool omni = ( parms.soundShaderFlags & SSF_OMNIDIRECTIONAL ) != 0;
    306 	bool emitterIsListener = ( emitter->emitterId == soundWorld->listener.id );
    307 
    308 	int startOffset = 0;
    309 	bool issueStart = false;
    310 
    311 	if ( hardwareVoice == NULL ) {
    312 		if ( volume <= 0.00001f ) {
    313 			return;
    314 		}
    315 
    316 		hardwareVoice = soundSystemLocal.AllocateVoice( leadinSample, loopingSample );
    317 
    318 		if ( hardwareVoice == NULL ) {
    319 			return;
    320 		}
    321 
    322 		issueStart = true;
    323 		startOffset = currentTime - startTime;
    324 	}
    325 
    326 	if ( omni || global || emitterIsListener ) {
    327 		hardwareVoice->SetPosition( vec3_zero );
    328 	} else {
    329 		hardwareVoice->SetPosition( ( emitter->spatializedOrigin - soundWorld->listener.pos ) * soundWorld->listener.axis.Transpose() );
    330 	}
    331 	if ( parms.soundShaderFlags & SSF_VO ) {
    332 		hardwareVoice->SetCenterChannel( s_centerFractionVO.GetFloat() );
    333 	} else {
    334 		hardwareVoice->SetCenterChannel( 0.0f );
    335 	}
    336 
    337 	extern idCVar timescale;
    338 
    339 	hardwareVoice->SetGain( volume );
    340 	hardwareVoice->SetInnerRadius( parms.minDistance * METERS_TO_DOOM );
    341 	hardwareVoice->SetPitch( soundWorld->slowmoSpeed * idMath::ClampFloat( 0.2f, 5.0f, timescale.GetFloat() ) );
    342 
    343 	if ( soundWorld->enviroSuitActive ) {
    344 		hardwareVoice->SetOcclusion( 0.5f );
    345 	} else {
    346 		hardwareVoice->SetOcclusion( 0.0f );
    347 	}
    348 
    349 	if ( issueStart ) {
    350 		hardwareVoice->Start( startOffset, parms.soundShaderFlags | ( parms.shakes == 0.0f ? SSF_NO_FLICKER : 0 ) );
    351 	} else {
    352 		hardwareVoice->Update();
    353 	}
    354 }
    355 
    356 /*
    357 ================================================================================================
    358 
    359 	idSoundEmitterLocal
    360 
    361 ================================================================================================
    362 */
    363 
    364 /*
    365 ========================
    366 idSoundEmitterLocal::idSoundEmitterLocal
    367 ========================
    368 */
    369 idSoundEmitterLocal::idSoundEmitterLocal() {
    370 	Init( 0, NULL );
    371 }
    372 
    373 /*
    374 ========================
    375 idSoundEmitterLocal::~idSoundEmitterLocal
    376 ========================
    377 */
    378 idSoundEmitterLocal::~idSoundEmitterLocal() {
    379 	assert( channels.Num() == 0 );
    380 }
    381 
    382 /*
    383 ========================
    384 idSoundEmitterLocal::Clear
    385 ========================
    386 */
    387 void idSoundEmitterLocal::Init( int i, idSoundWorldLocal * sw ) {
    388 	index = i;
    389 	soundWorld = sw;
    390 
    391 	// Init should only be called on a freshly constructed sound emitter or in a Reset()
    392 	assert( channels.Num() == 0 );
    393 
    394 	canFree = false;
    395 	origin.Zero();
    396 	emitterId = 0;
    397 
    398 	directDistance = 0.0f;
    399 	lastValidPortalArea = -1;
    400 	spatializedDistance = 0.0f;
    401 	spatializedOrigin.Zero();
    402 
    403 	memset( &parms, 0, sizeof( parms ) );
    404 }
    405 
    406 /*
    407 ========================
    408 idSoundEmitterLocal::Reset
    409 ========================
    410 */
    411 void idSoundEmitterLocal::Reset() {
    412 	for ( int i = 0; i < channels.Num(); i++ ) {
    413 		soundWorld->FreeSoundChannel( channels[i] );
    414 	}
    415 	channels.Clear();
    416 	Init( index, soundWorld );
    417 }
    418 
    419 /*
    420 ==================
    421 idSoundEmitterLocal::OverrideParms
    422 ==================
    423 */
    424 void idSoundEmitterLocal::OverrideParms( const soundShaderParms_t * base, const soundShaderParms_t * over, soundShaderParms_t * out ) {
    425 	if ( !over ) {
    426 		*out = *base;
    427 		return;
    428 	}
    429 	if ( over->minDistance ) {
    430 		out->minDistance = over->minDistance;
    431 	} else {
    432 		out->minDistance = base->minDistance;
    433 	}
    434 	if ( over->maxDistance ) {
    435 		out->maxDistance = over->maxDistance;
    436 	} else {
    437 		out->maxDistance = base->maxDistance;
    438 	}
    439 	if ( over->shakes ) {
    440 		out->shakes = over->shakes;
    441 	} else {
    442 		out->shakes = base->shakes;
    443 	}
    444 	if ( over->volume ) {
    445 		out->volume = over->volume;
    446 	} else {
    447 		out->volume = base->volume;
    448 	}
    449 	if ( over->soundClass ) {
    450 		out->soundClass = over->soundClass;
    451 	} else {
    452 		out->soundClass = base->soundClass;
    453 	}
    454 	out->soundShaderFlags = base->soundShaderFlags | over->soundShaderFlags;
    455 }
    456 
    457 /*
    458 ========================
    459 idSoundEmitterLocal::CheckForCompletion
    460 
    461 Checks to see if any of the channels have completed, removing them as they do
    462 
    463 This will also play any postSounds on the same channel as their owner.
    464 
    465 Returns true if the emitter should be freed.
    466 ========================
    467 */
    468 bool idSoundEmitterLocal::CheckForCompletion( int currentTime ) {
    469 	for ( int i = channels.Num() - 1; i >= 0 ; i-- ) {
    470 		idSoundChannel * chan = channels[i];
    471 
    472 		if ( chan->CheckForCompletion( currentTime ) ) {
    473 			channels.RemoveIndex( i );
    474 			soundWorld->FreeSoundChannel( chan );
    475 		}
    476 	}
    477 	return ( canFree && channels.Num() == 0 );
    478 }
    479 
    480 /*
    481 ========================
    482 idSoundEmitterLocal::Update
    483 ========================
    484 */
    485 void idSoundEmitterLocal::Update( int currentTime ) {
    486 	if ( channels.Num() == 0 ) {
    487 		return;
    488 	}
    489 
    490 	directDistance = ( soundWorld->listener.pos - origin ).LengthFast() * DOOM_TO_METERS;
    491 
    492 	spatializedDistance = directDistance;
    493 	spatializedOrigin = origin;
    494 
    495 	// Initialize all channels to silence
    496 	for ( int i = 0; i < channels.Num(); i++ ) {
    497 		channels[i]->volumeDB = DB_SILENCE;
    498 	}
    499 
    500 	if ( s_singleEmitter.GetInteger() > 0 && s_singleEmitter.GetInteger() != index ) {
    501 		return;
    502 	}
    503 	if ( soundWorld->listener.area == -1 ) {
    504 		// listener is outside the world
    505 		return;
    506 	}
    507 	if ( soundSystemLocal.muted || soundWorld != soundSystemLocal.currentSoundWorld ) {
    508 		return;
    509 	}
    510 	float maxDistance = 0.0f;
    511 	bool maxDistanceValid = false;
    512 	bool useOcclusion = false;
    513 	if ( emitterId != soundWorld->listener.id ) {
    514 		for ( int i = 0; i < channels.Num(); i++ ) {
    515 			idSoundChannel * chan = channels[i];
    516 			if ( ( chan->parms.soundShaderFlags & SSF_GLOBAL ) != 0 ) {
    517 				continue;
    518 			}
    519 			useOcclusion = useOcclusion || ( ( chan->parms.soundShaderFlags & SSF_NO_OCCLUSION ) == 0 );
    520 			maxDistanceValid = true;
    521 			if ( maxDistance < channels[i]->parms.maxDistance ) {
    522 				maxDistance = channels[i]->parms.maxDistance;
    523 			}
    524 		}
    525 	}
    526 	if ( maxDistanceValid && directDistance >= maxDistance ) {
    527 		// too far away to possibly hear it
    528 		return;
    529 	}
    530 	if ( useOcclusion && s_useOcclusion.GetBool() ) {
    531 		// work out virtual origin and distance, which may be from a portal instead of the actual origin
    532 		if ( soundWorld->renderWorld != NULL ) {
    533 			// we have a valid renderWorld
    534 			int soundInArea = soundWorld->renderWorld->PointInArea( origin );
    535 			if ( soundInArea == -1 ) {
    536 				soundInArea = lastValidPortalArea;
    537 			} else {
    538 				lastValidPortalArea = soundInArea;
    539 			}
    540 			if ( soundInArea != -1 && soundInArea != soundWorld->listener.area ) {
    541 				spatializedDistance = maxDistance * METERS_TO_DOOM;
    542 				soundWorld->ResolveOrigin( 0, NULL, soundInArea, 0.0f, origin, this );
    543 				spatializedDistance *= DOOM_TO_METERS;
    544 			}
    545 		}
    546 	}
    547 
    548 	for ( int j = 0; j < channels.Num(); j++ ) {
    549 		channels[j]->UpdateVolume( currentTime );
    550 	}
    551 
    552 	return;
    553 }
    554 
    555 /*
    556 ========================
    557 idSoundEmitterLocal::Index
    558 ========================
    559 */
    560 int idSoundEmitterLocal::Index() const {
    561 	assert( soundWorld );
    562 	assert( soundWorld->emitters[this->index] == this );
    563 
    564 	return index;
    565 }
    566 
    567 /*
    568 ========================
    569 idSoundEmitterLocal::Free
    570 
    571 Doesn't free it until the next update.
    572 ========================
    573 */
    574 void idSoundEmitterLocal::Free( bool immediate ) {
    575 	assert( soundWorld );
    576 	assert( soundWorld->emitters[this->index] == this );
    577 
    578 	if ( canFree ) {
    579 		// Double free
    580 		return;
    581 	}
    582 	if ( soundWorld && soundWorld->writeDemo ) {
    583 		soundWorld->writeDemo->WriteInt( DS_SOUND );
    584 		soundWorld->writeDemo->WriteInt( SCMD_FREE );
    585 		soundWorld->writeDemo->WriteInt( index );
    586 		soundWorld->writeDemo->WriteInt( immediate );
    587 	}
    588 
    589 	if ( immediate ) {
    590 		Reset();
    591 	}
    592 
    593 	canFree = true;
    594 }
    595 
    596 /*
    597 ========================
    598 idSoundEmitterLocal::UpdateEmitter
    599 ========================
    600 */
    601 void idSoundEmitterLocal::UpdateEmitter( const idVec3 &origin, int listenerId, const soundShaderParms_t *parms ) {
    602 	assert( soundWorld != NULL );
    603 	assert( soundWorld->emitters[this->index] == this );
    604 
    605 	if ( soundWorld && soundWorld->writeDemo ) {
    606 		soundWorld->writeDemo->WriteInt( DS_SOUND );
    607 		soundWorld->writeDemo->WriteInt( SCMD_UPDATE );
    608 		soundWorld->writeDemo->WriteInt( index );
    609 		soundWorld->writeDemo->WriteVec3( origin );
    610 		soundWorld->writeDemo->WriteInt( listenerId );
    611 		soundWorld->writeDemo->WriteFloat( parms->minDistance );
    612 		soundWorld->writeDemo->WriteFloat( parms->maxDistance );
    613 		soundWorld->writeDemo->WriteFloat( parms->volume );
    614 		soundWorld->writeDemo->WriteFloat( parms->shakes );
    615 		soundWorld->writeDemo->WriteInt( parms->soundShaderFlags );
    616 		soundWorld->writeDemo->WriteInt( parms->soundClass );
    617 	}
    618 
    619 	this->origin = origin;
    620 	this->emitterId = listenerId;
    621 	this->parms = *parms;
    622 }
    623 
    624 /*
    625 ========================
    626 idSoundEmitterLocal::StartSound
    627 
    628 in most cases play sounds immediately, however
    629   intercept sounds using SSF_FINITE_SPEED_OF_SOUND
    630   and schedule them for playback later
    631 
    632 return: int	- the length of the started sound in msec.
    633 ========================
    634 */
    635 int idSoundEmitterLocal::StartSound( const idSoundShader * shader, const s_channelType channel, float diversity, int shaderFlags, bool allowSlow ) {
    636 	assert( soundWorld != NULL );
    637 	assert( soundWorld->emitters[this->index] == this );
    638 
    639 	if ( shader == NULL ) {
    640 		return 0;
    641 	}
    642 
    643 	if ( soundWorld && soundWorld->writeDemo ) {
    644 		soundWorld->writeDemo->WriteInt( DS_SOUND );
    645 		soundWorld->writeDemo->WriteInt( SCMD_START );
    646 		soundWorld->writeDemo->WriteInt( index );
    647 
    648 		soundWorld->writeDemo->WriteHashString( shader->GetName() );
    649 
    650 		soundWorld->writeDemo->WriteInt( channel );
    651 		soundWorld->writeDemo->WriteFloat( diversity );
    652 		soundWorld->writeDemo->WriteInt( shaderFlags );
    653 	}
    654 
    655 	if ( s_noSound.GetBool() ) {
    656 		return 0;
    657 	}
    658 
    659 	int currentTime = soundWorld->GetSoundTime();
    660 
    661 	bool showStartSound = s_showStartSound.GetBool();
    662 	if ( showStartSound ) {
    663 		idLib::Printf( "%dms: StartSound(%d:%d): %s: ", currentTime, index, channel, shader->GetName() );
    664 	}
    665 
    666 	// build the channel parameters by taking the shader parms and optionally overriding
    667 	soundShaderParms_t	chanParms;
    668 	chanParms = shader->parms;
    669 	OverrideParms( &chanParms, &parms, &chanParms );
    670 	chanParms.soundShaderFlags |= shaderFlags;
    671 
    672 	if ( shader->entries.Num() == 0 ) {
    673 		if ( showStartSound ) {
    674 			idLib::Printf( S_COLOR_RED "No Entries\n" );
    675 		}
    676 		return 0;
    677 	}
    678 
    679 	// PLAY_ONCE sounds will never be restarted while they are running
    680 	if ( chanParms.soundShaderFlags & SSF_PLAY_ONCE ) {
    681 		for ( int i = 0; i < channels.Num(); i++ ) {
    682 			idSoundChannel * chan = channels[i];
    683 			if ( chan->soundShader == shader && !chan->CheckForCompletion( currentTime ) ) {
    684 				if ( showStartSound ) {
    685 					idLib::Printf( S_COLOR_YELLOW "Not started because of playOnce\n" );
    686 				}
    687 				return 0;
    688 			}
    689 		}
    690 	}
    691 
    692 	// never play the same sound twice with the same starting time, even
    693 	// if they are on different channels
    694 	for ( int i = 0; i < channels.Num(); i++ ) {
    695 		idSoundChannel * chan = channels[i];
    696 		if ( chan->soundShader == shader && chan->startTime == currentTime && chan->endTime != 1 ) {
    697 			if ( showStartSound ) {
    698 				idLib::Printf( S_COLOR_RED "Already started this frame\n" );
    699 			}
    700 			return 0;
    701 		}
    702 	}
    703 
    704 	// kill any sound that is currently playing on this channel
    705 	if ( channel != SCHANNEL_ANY ) {
    706 		for ( int i = 0; i < channels.Num(); i++ ) {
    707 			idSoundChannel * chan = channels[i];
    708 			if ( chan->soundShader && chan->logicalChannel == channel ) {
    709 				if ( showStartSound ) {
    710 					idLib::Printf( S_COLOR_YELLOW "OVERRIDE %s: ", chan->soundShader->GetName() );
    711 				}
    712 				channels.RemoveIndex( i );
    713 				soundWorld->FreeSoundChannel( chan );
    714 				break;
    715 			}
    716 		}
    717 	}
    718 
    719 	idSoundSample * leadinSample = NULL;
    720 	idSoundSample * loopingSample = NULL;
    721 
    722 	if ( shader->leadin && ( chanParms.soundShaderFlags & SSF_LOOPING ) ) {
    723 		leadinSample = shader->entries[0];
    724 		loopingSample = shader->entries.Num() > 1 ? shader->entries[1] : NULL;
    725 	} else {
    726 		if ( shader->entries.Num() == 1 ) {
    727 			leadinSample = shader->entries[0];
    728 		} else {
    729 			int choice;
    730 			if ( chanParms.soundShaderFlags & SSF_NO_DUPS ) {
    731 				// Don't select the most recently played entry
    732 				int mostRecentTime = 0;
    733 				int mostRecent = 0;
    734 				for ( int i = 0; i < shader->entries.Num(); i++ ) {
    735 					int entryTime = shader->entries[i]->GetLastPlayedTime();
    736 					if ( entryTime > mostRecentTime ) {
    737 						mostRecentTime = entryTime;
    738 						mostRecent = i;
    739 					}
    740 				}
    741 				choice = (int)( diversity * ( shader->entries.Num() - 1 ) );
    742 				if ( choice >= mostRecent ) {
    743 					choice++;
    744 				}
    745 			} else {
    746 				// pick a sound from the list based on the passed diversity
    747 				choice = (int)( diversity * shader->entries.Num() );
    748 			}
    749 			choice = idMath::ClampInt( 0, shader->entries.Num() - 1, choice );
    750 			leadinSample = shader->entries[choice];
    751 			leadinSample->SetLastPlayedTime( soundWorld->GetSoundTime() );
    752 		}
    753 		if ( chanParms.soundShaderFlags & SSF_LOOPING ) {
    754 			loopingSample = leadinSample;
    755 		}
    756 	}
    757 
    758 	// set all the channel parameters here,
    759 	// a hardware voice will be allocated next update if the volume is high enough to be audible
    760 	if ( channels.Num() == channels.Max() ) {
    761 		CheckForCompletion( currentTime );	// as a last chance try to release finished sounds here
    762  		if ( channels.Num() == channels.Max() ) {
    763 			if ( showStartSound ) {
    764 				idLib::Printf( S_COLOR_RED "No free emitter channels!\n" );
    765 			}
    766  			return 0;
    767 		}
    768 	}
    769 	idSoundChannel * chan = soundWorld->AllocSoundChannel();
    770 	if ( chan == NULL ) {
    771 		if ( showStartSound ) {
    772 			idLib::Printf( S_COLOR_RED "No free global channels!\n" );
    773 		}
    774 		return 0;
    775 	}
    776 	channels.Append( chan );
    777 	chan->emitter = this;
    778 	chan->parms = chanParms;
    779 	chan->soundShader = shader;
    780 	chan->logicalChannel = channel;
    781 	chan->leadinSample = leadinSample;
    782 	chan->loopingSample = loopingSample;
    783 	chan->allowSlow = allowSlow;
    784 
    785 	// return length of sound in milliseconds
    786 	int length = chan->leadinSample->LengthInMsec();
    787 
    788 	// adjust the start time based on diversity for looping sounds, so they don't all start at the same point
    789 	int startOffset = 0;
    790 
    791 	if ( chan->IsLooping() && !shader->leadin ) {
    792 		// looping sounds start at a random point...
    793 		startOffset = soundSystemLocal.random.RandomInt( length );
    794 	}
    795 
    796 	chan->startTime = currentTime - startOffset;
    797 
    798 	if ( ( chanParms.soundShaderFlags & SSF_LOOPING ) != 0 ) {
    799 		// This channel will never end!
    800 		chan->endTime = 0;
    801 	} else {
    802 		// This channel will automatically end at this time
    803 		chan->endTime = chan->startTime + length + 100;
    804 	}
    805 	if ( showStartSound ) {
    806 		if ( loopingSample == NULL || leadinSample == loopingSample ) {
    807 			idLib::Printf( "Playing %s @ %d\n", leadinSample->GetName(), startOffset );
    808 		} else {
    809 			idLib::Printf( "Playing %s then looping %s\n", leadinSample->GetName(), loopingSample->GetName() );
    810 		}
    811 	}
    812 
    813 	return length;
    814 }
    815 
    816 
    817 /*
    818 ========================
    819 idSoundEmitterLocal::OnReloadSound
    820 
    821 This is a shortened version of StartSound, called whenever a sound shader is reloaded.
    822 If the emitter is currently playing the given sound shader, restart it so
    823 a change in the sound sample used for a given sound shader will be picked up.
    824 ========================
    825 */
    826 void idSoundEmitterLocal::OnReloadSound( const idDecl *decl ) {
    827 }
    828 
    829 /*
    830 ========================
    831 idSoundEmitterLocal::StopSound
    832 
    833 Can pass SCHANNEL_ANY.
    834 ========================
    835 */
    836 void idSoundEmitterLocal::StopSound( const s_channelType channel ) {
    837 	assert( soundWorld != NULL );
    838 	assert( soundWorld->emitters[this->index] == this );
    839 
    840 	if ( soundWorld && soundWorld->writeDemo ) {
    841 		soundWorld->writeDemo->WriteInt( DS_SOUND );
    842 		soundWorld->writeDemo->WriteInt( SCMD_STOP );
    843 		soundWorld->writeDemo->WriteInt( index );
    844 		soundWorld->writeDemo->WriteInt( channel );
    845 	}
    846 
    847 	for( int i = 0; i < channels.Num(); i++ ) {
    848 		idSoundChannel * chan = channels[i];
    849 
    850 		if ( channel != SCHANNEL_ANY && chan->logicalChannel != channel ) {
    851 			continue;
    852 		}
    853 		if ( s_showStartSound.GetBool() ) {
    854 			idLib::Printf( "%dms: StopSound(%d:%d): %s\n", soundWorld->GetSoundTime(), index, channel, chan->soundShader->GetName() );
    855 		}
    856 
    857 		// This forces CheckForCompletion to return true
    858 		chan->endTime = 1;
    859 	}
    860 }
    861 
    862 /*
    863 ========================
    864 idSoundEmitterLocal::ModifySound
    865 ========================
    866 */
    867 void idSoundEmitterLocal::ModifySound( const s_channelType channel, const soundShaderParms_t *parms ) {
    868 	assert( soundWorld != NULL );
    869 	assert( soundWorld->emitters[this->index] == this );
    870 
    871 	if ( soundWorld && soundWorld->writeDemo ) {
    872 		soundWorld->writeDemo->WriteInt( DS_SOUND );
    873 		soundWorld->writeDemo->WriteInt( SCMD_MODIFY );
    874 		soundWorld->writeDemo->WriteInt( index );
    875 		soundWorld->writeDemo->WriteInt( channel );
    876 		soundWorld->writeDemo->WriteFloat( parms->minDistance );
    877 		soundWorld->writeDemo->WriteFloat( parms->maxDistance );
    878 		soundWorld->writeDemo->WriteFloat( parms->volume );
    879 		soundWorld->writeDemo->WriteFloat( parms->shakes );
    880 		soundWorld->writeDemo->WriteInt( parms->soundShaderFlags );
    881 		soundWorld->writeDemo->WriteInt( parms->soundClass );
    882 	}
    883 
    884 	for ( int i = channels.Num() - 1; i >= 0; i-- ) {
    885 		idSoundChannel * chan = channels[i];
    886 		if ( channel != SCHANNEL_ANY && chan->logicalChannel != channel ) {
    887 			continue;
    888 		}
    889 		if ( s_showStartSound.GetBool() ) {
    890 			idLib::Printf( "%dms: ModifySound(%d:%d): %s\n", soundWorld->GetSoundTime(), index, channel, chan->soundShader->GetName() );
    891 		}
    892 		OverrideParms( &chan->parms, parms, &chan->parms );
    893 	}
    894 }
    895 
    896 /*
    897 ========================
    898 idSoundEmitterLocal::FadeSound
    899 ========================
    900 */
    901 void idSoundEmitterLocal::FadeSound( const s_channelType channel, float to, float over ) {
    902 	assert( soundWorld != NULL );
    903 	assert( soundWorld->emitters[this->index] == this );
    904 
    905 	if ( soundWorld->writeDemo ) {
    906 		soundWorld->writeDemo->WriteInt( DS_SOUND );
    907 		soundWorld->writeDemo->WriteInt( SCMD_FADE );
    908 		soundWorld->writeDemo->WriteInt( index );
    909 		soundWorld->writeDemo->WriteInt( channel );
    910 		soundWorld->writeDemo->WriteFloat( to );
    911 		soundWorld->writeDemo->WriteFloat( over );
    912 	}
    913 
    914 	int overMSec = SEC2MS( over );
    915 
    916 	for ( int i = 0; i < channels.Num(); i++ ) {
    917 		idSoundChannel * chan = channels[i];
    918 
    919 		if ( channel != SCHANNEL_ANY && chan->logicalChannel != channel ) {
    920 			continue;
    921 		}
    922 		if ( s_showStartSound.GetBool() ) {
    923 			idLib::Printf( "%dms: FadeSound(%d:%d): %s to %.2fdb over %.2f seconds\n", soundWorld->GetSoundTime(), index, channel, chan->soundShader->GetName(), to, over );
    924 		}
    925 
    926 		// fade it
    927 		chan->volumeFade.Fade( to - chan->parms.volume, overMSec, soundWorld->GetSoundTime() );
    928 	}
    929 }
    930 
    931 /*
    932 ========================
    933 idSoundEmitterLocal::CurrentlyPlaying
    934 ========================
    935 */
    936 bool idSoundEmitterLocal::CurrentlyPlaying( const s_channelType channel ) const {
    937 
    938 	if ( channel == SCHANNEL_ANY ) {
    939 		return ( channels.Num() > 0 );
    940 	}
    941 
    942 	for ( int i = 0; i < channels.Num(); ++i ) {
    943 		if ( channels[i] != NULL && channels[i]->logicalChannel == channel ) {
    944 			if ( channels[i]->endTime == 1 ) {
    945 				return false;
    946 			} else {
    947 				return true;
    948 			}			
    949 		}
    950 	}
    951 
    952 	return false;
    953 }
    954 
    955 /*
    956 ========================
    957 idSoundEmitterLocal::CurrentAmplitude
    958 ========================
    959 */
    960 float idSoundEmitterLocal::CurrentAmplitude() {
    961 	float amplitude = 0.0f;
    962 	int currentTime = soundWorld->GetSoundTime();
    963 	for ( int i = 0; i < channels.Num(); i++ ) {
    964 		idSoundChannel * chan = channels[i];
    965 		if ( chan == NULL || currentTime < chan->startTime || ( chan->endTime > 0 && currentTime >= chan->endTime ) ) {
    966 			continue;
    967 		}
    968 		int relativeTime = currentTime - chan->startTime;
    969 		int leadinLength = chan->leadinSample->LengthInMsec();
    970 		if ( relativeTime < leadinLength ) {
    971 			amplitude = Max( amplitude, chan->leadinSample->GetAmplitude( relativeTime ) );
    972 		} else if ( chan->loopingSample != NULL ) {
    973 			amplitude = Max( amplitude, chan->loopingSample->GetAmplitude( ( relativeTime - leadinLength ) % chan->loopingSample->LengthInMsec() ) );
    974 		}
    975 	}
    976 	return amplitude;
    977 }