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 }