sfxSoundscape.cpp
Engine/source/sfx/sfxSoundscape.cpp
Detailed Description
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "sfx/sfxSoundscape.h" 25#include "sfx/sfxAmbience.h" 26#include "sfx/sfxEnvironment.h" 27#include "sfx/sfxState.h" 28#include "sfx/sfxSource.h" 29#include "sfx/sfxSystem.h" 30 31 32//#define DEBUG_SPEW 33 34//FIXME: fix reverb resetting 35 36//============================================================================= 37// SFXSoundscape. 38//============================================================================= 39// MARK: ---- SFXSoundscape ---- 40 41//----------------------------------------------------------------------------- 42 43SFXSoundscape::SFXSoundscape( SFXAmbience* ambience ) 44 : mAmbience( ambience ) 45{ 46 mDirtyBits.set( AmbienceDirty ); 47 mFlags.set( FlagUnique ); 48 49 dMemset( mStates, 0, sizeof( mStates ) ); 50} 51 52//----------------------------------------------------------------------------- 53 54void SFXSoundscape::setAmbience( SFXAmbience* ambience ) 55{ 56 AssertFatal( ambience != NULL, "SFXSoundscape::setAmbience - ambience cannot be NULL!" ); 57 mDirtyBits.set( AmbienceDirty ); 58 mAmbience = ambience; 59} 60 61//============================================================================= 62// SFXSoundscapeManager. 63//============================================================================= 64// MARK: ---- SFXSoundscapeManager ---- 65 66//----------------------------------------------------------------------------- 67 68SFXSoundscapeManager::SFXSoundscapeManager() 69 : mCurrentReverbIndex( -1 ) 70{ 71 VECTOR_SET_ASSOCIATION( mStack ); 72 VECTOR_SET_ASSOCIATION( mFadeStack ); 73 74#ifndef TORQUE_SHIPPING 75 76 // Hook on to the ambience change signal (used 77 // to respond to editing of ambiences). 78 79 SFXAmbience::getChangeSignal().notify 80 ( this, &SFXSoundscapeManager::_notifyAmbienceChanged ); 81 82#endif 83 84 // Push the global ambience. 85 86 mDefaultGlobalAmbience = new SFXAmbience; 87 SFXSoundscape* global = mChunker.alloc(); 88 constructInPlace( global, mDefaultGlobalAmbience ); 89 mStack.push_back( global ); 90} 91 92//----------------------------------------------------------------------------- 93 94SFXSoundscapeManager::~SFXSoundscapeManager() 95{ 96#ifndef TORQUE_SHIPPING 97 98 // Remove the hook on the ambience change signal. 99 100 SFXAmbience::getChangeSignal().remove 101 ( this, &SFXSoundscapeManager::_notifyAmbienceChanged ); 102 103#endif 104 105 mDefaultGlobalAmbience->deleteObject(); 106} 107 108//----------------------------------------------------------------------------- 109 110void SFXSoundscapeManager::update() 111{ 112 // Make sure the topmost reverb on the stack is active. 113 114 S32 reverbIndex = _findTopmostReverbOnStack( mStack ); 115 if( mCurrentReverbIndex != reverbIndex ) 116 { 117 if( reverbIndex == -1 ) 118 { 119 // No ambience on the stack has reverb settings so reset 120 // to default. 121 SFX->setReverb( SFXReverbProperties() ); 122 } 123 else 124 { 125 SFXAmbience* ambience = mStack[ reverbIndex ]->getAmbience(); 126 AssertFatal( ambience->getEnvironment(), "SFXSoundscapeManager::update - Reverb lookup return ambience without reverb!" ); 127 128 SFX->setRolloffFactor( ambience->getRolloffFactor() ); 129 SFX->setDopplerFactor( ambience->getDopplerFactor() ); 130 SFX->setReverb( ambience->getEnvironment()->getReverb() ); 131 } 132 133 mCurrentReverbIndex = reverbIndex; 134 } 135 136 // Update the active soundscapes. 137 138 for( U32 i = 0; i < mStack.size(); ++ i ) 139 { 140 SFXSoundscape* soundscape = mStack[ i ]; 141 142 // If the soundscape's associated ambience has changed 143 144 if( soundscape->mDirtyBits.test( SFXSoundscape::AmbienceDirty ) ) 145 { 146 SFXAmbience* ambience = soundscape->getAmbience(); 147 148 // Start playing the ambient audio track if it isn't 149 // already playing and if the soundscape isn't overridden 150 // by an instance lower down the stack. 151 152 if( !soundscape->_isOverridden() ) 153 { 154 SFXTrack* track = ambience->getSoundTrack(); 155 if( !soundscape->mSource || soundscape->mSource->getTrack() != track ) 156 { 157 if( soundscape->mSource != NULL ) 158 { 159 soundscape->mSource->stop(); 160 soundscape->mSource = NULL; 161 } 162 163 if( track ) 164 soundscape->mSource = SFX->playOnce( track ); 165 } 166 else if( soundscape->mSource != NULL ) 167 { 168 // Make sure to revert a fade-out running on the source 169 // if it has been taken from the fade stack. 170 171 soundscape->mSource->play(); 172 } 173 } 174 175 // Activate SFXStates on the ambience. For state slots that 176 // have changed, deactivate states that we have already activated. 177 178 for( U32 ambState = 0; ambState < SFXAmbience::MaxStates; ++ambState) 179 { 180 SFXState* state = ambience->getState(ambState); 181 if( soundscape->mStates[ambState] != state ) 182 { 183 if( soundscape->mStates[ambState] ) 184 soundscape->mStates[ambState]->deactivate(); 185 186 if( state ) 187 state->activate(); 188 189 soundscape->mStates[ambState] = state; 190 } 191 } 192 193 soundscape->mDirtyBits.clear( SFXSoundscape::AmbienceDirty ); 194 } 195 } 196 197 // Clean out the fade stack. 198 199 for( U32 i = 0; i < mFadeStack.size(); ) 200 if( mFadeStack[ i ]->mSource == NULL ) 201 { 202 SFXSoundscape* soundscape = mFadeStack[ i ]; 203 mFadeStack.erase_fast( i ); 204 205 #ifdef DEBUG_SPEW 206 Platform::outputDebugString( "[SFXSoundscapeManager] Deleting faded instance of '%s'", 207 soundscape->getAmbience()->getName() ); 208 #endif 209 210 // Free the soundscape. 211 212 destructInPlace( soundscape ); 213 mChunker.free( soundscape ); 214 } 215 else 216 ++ i; 217} 218 219//----------------------------------------------------------------------------- 220 221SFXSoundscape* SFXSoundscapeManager::insertSoundscape( U32 index, SFXAmbience* ambience ) 222{ 223 AssertFatal( index <= mStack.size(), "SFXSoundscapeManager::insertSoundscape - index out of range" ); 224 AssertFatal( index != 0, "SFXSoundscapeManager::insertSoundscape - cannot insert before global soundscape" ); 225 AssertFatal( ambience != NULL, "SFXSoundscapeManager::insertSoundscape - got a NULL ambience" ); 226 227 // Look for an existing soundscape that is assigned the 228 // same ambience. 229 230 S32 ambientInstanceIndex = _findOnStack( ambience, mStack ); 231 232 // Push a soundscape unto the stack. If there is an instance 233 // on the fade stack that is tied to the given ambience, bring that 234 // soundscape over to the active stack. Otherwise create a new 235 // soundscape instance. 236 237 SFXSoundscape* soundscape = NULL; 238 if( ambientInstanceIndex == -1 ) 239 { 240 S32 fadeIndex = _findOnStack( ambience, mFadeStack ); 241 if( fadeIndex != -1 ) 242 { 243 soundscape = mFadeStack[ fadeIndex ]; 244 mFadeStack.erase_fast( fadeIndex ); 245 246 // Make sure the soundscape will get re-evaluated 247 // on the next update. 248 249 soundscape->mDirtyBits.set( SFXSoundscape::AmbienceDirty ); 250 251 #ifdef DEBUG_SPEW 252 Platform::outputDebugString( "[SFXSoundscapeManager] Moving ambience '%s' from fade stack to #%i (total: %i)", 253 ambience->getName(), index, mStack.size() + 1 ); 254 #endif 255 } 256 } 257 258 if( !soundscape ) 259 { 260 #ifdef DEBUG_SPEW 261 Platform::outputDebugString( "[SFXSoundscapeManager] Adding new instance for '%s' at #%i (total: %i)", 262 ambience->getName(), index, mStack.size() + 1 ); 263 #endif 264 265 soundscape = mChunker.alloc(); 266 constructInPlace( soundscape, ambience ); 267 } 268 269 mStack.insert( index, soundscape ); 270 271 // If there is an existing soundscape that is assigned the 272 // same ambience and it is lower on the stack, steal its sound 273 // source if it has one. If it is higher up the stack, simply 274 // mark this soundscape as being overridden. 275 276 if( ambientInstanceIndex != -1 ) 277 { 278 SFXSoundscape* existingSoundscape = mStack[ ambientInstanceIndex ]; 279 280 existingSoundscape->mFlags.clear( SFXSoundscape::FlagUnique ); 281 soundscape->mFlags.clear( SFXSoundscape::FlagUnique ); 282 283 if( ambientInstanceIndex < index ) 284 { 285 existingSoundscape->mFlags.set( SFXSoundscape::FlagOverridden ); 286 287 SFXSource* source = existingSoundscape->mSource; 288 existingSoundscape->mSource = NULL; 289 290 if( source && source->isPlaying() ) 291 soundscape->mSource = source; 292 } 293 else 294 { 295 soundscape->mFlags.set( SFXSoundscape::FlagOverridden ); 296 } 297 } 298 299 return soundscape; 300} 301 302//----------------------------------------------------------------------------- 303 304void SFXSoundscapeManager::removeSoundscape( SFXSoundscape* soundscape ) 305{ 306 AssertFatal( soundscape != getGlobalSoundscape(), 307 "SFXSoundscapeManager::removeSoundscape() - trying to remove the global soundscape" ); 308 309 // Find the soundscape on the stack. 310 311 U32 index = 1; 312 for( ; index < mStack.size(); ++ index ) 313 if( mStack[ index ] == soundscape ) 314 break; 315 316 AssertFatal( index < mStack.size(), 317 "SFXSoundscapeManager::removeSoundscape() - soundscape not on stack" ); 318 319 // Find out if the soundscape has the current reverb 320 // environment. If so, we need to change the reverb to 321 // the next one higher up the stack. 322 323 const bool isCurrentReverb = ( _findTopmostReverbOnStack( mStack ) == index ); 324 325 // Remove the soundscape from the stack. 326 327 mStack.erase( index ); 328 329 // Update reverb, if necessary. 330 331 if( isCurrentReverb ) 332 { 333 S32 reverbIndex = _findTopmostReverbOnStack( mStack ); 334 if( reverbIndex != -1 ) 335 SFX->setReverb( mStack[ reverbIndex ]->getAmbience()->getEnvironment()->getReverb() ); 336 } 337 338 // Deactivate states. 339 340 for( U32 i = 0; i < SFXAmbience::MaxStates; ++ i ) 341 if( soundscape->mStates[ i ] ) 342 { 343 soundscape->mStates[ i ]->deactivate(); 344 soundscape->mStates[ i ] = NULL; 345 } 346 347 // If the soundscape is the only instance of its ambience, move 348 // it to the fade stack. Otherwise delete the soundscape. 349 350 if( soundscape->_isUnique() && soundscape->mSource != NULL ) 351 { 352 #ifdef DEBUG_SPEW 353 Platform::outputDebugString( "[SFXSoundscapeManager] Moving ambience '%s' at index #%i to fade stack", 354 soundscape->getAmbience()->getName(), index ); 355 #endif 356 357 soundscape->mSource->stop(); 358 mFadeStack.push_back( soundscape ); 359 } 360 else 361 { 362 if( !soundscape->_isUnique() ) 363 { 364 // If this is the overriding soundscape, transfer its state 365 // to the ambient instance lower down the stack. 366 367 if( !soundscape->_isOverridden() ) 368 { 369 S32 overrideeIndex = index - 1; 370 for( ; overrideeIndex >= 0; -- overrideeIndex ) 371 if( soundscape->getAmbience() == mStack[ overrideeIndex ]->getAmbience() ) 372 break; 373 374 AssertFatal( overrideeIndex >= 0, 375 "SFXSoundscapeManager::removeSoundscape() - could not find ambience being overridden on stack" ); 376 377 // Pass the source on to the previous soundscape. 378 379 mStack[ overrideeIndex ]->mSource = soundscape->mSource; 380 soundscape->mSource = NULL; 381 } 382 383 #ifdef DEBUG_SPEW 384 Platform::outputDebugString( "[SFXSoundscapeManager] Removing instance of '%s' at index #%i", 385 soundscape->getAmbience()->getName(), index ); 386 #endif 387 388 // If there's only one instance of this ambience is 389 // left, mark it as being unique now. 390 391 U32 numInstances = 0; 392 for( U32 i = 0; i < mStack.size(); ++ i ) 393 if( mStack[ i ]->getAmbience() == soundscape->getAmbience() ) 394 ++ numInstances; 395 396 if( numInstances == 1 ) 397 mStack[ _findOnStack( soundscape->getAmbience(), mStack ) ]->mFlags.set( SFXSoundscape::FlagUnique ); 398 } 399 400 // Free the soundscape. 401 402 destructInPlace( soundscape ); 403 mChunker.free( soundscape ); 404 } 405} 406 407//----------------------------------------------------------------------------- 408 409S32 SFXSoundscapeManager::_findOnStack( SFXAmbience* ambience, const Vector< SFXSoundscape*>& stack ) 410{ 411 // Search the stack top to bottom so we always find 412 // the uppermost instance of the ambience on the stack. 413 414 for( S32 i = stack.size() - 1; i >= 0; -- i ) 415 if( stack[ i ]->getAmbience() == ambience ) 416 return i; 417 418 return -1; 419} 420 421//----------------------------------------------------------------------------- 422 423S32 SFXSoundscapeManager::_findTopmostReverbOnStack( const Vector< SFXSoundscape*>& stack ) 424{ 425 for( S32 i = stack.size() - 1; i >= 0; -- i ) 426 if( stack[ i ]->getAmbience()->getEnvironment() ) 427 return i; 428 429 return -1; 430} 431 432//----------------------------------------------------------------------------- 433 434void SFXSoundscapeManager::_notifyAmbienceChanged( SFXAmbience* ambience ) 435{ 436 //RDTODO: fade stack? 437 438 // Set the ambience dirty bit on all soundscapes 439 // tied to the given ambience. 440 441 for( U32 i = 0; i < mStack.size(); ++ i ) 442 if( mStack[ i ]->getAmbience() == ambience ) 443 { 444 mStack[ i ]->mDirtyBits.set( SFXSoundscape::AmbienceDirty ); 445 446 #ifdef DEBUG_SPEW 447 Platform::outputDebugString( "[SFXSoundscapeManager] Ambience '%s' at #%i changed", 448 ambience->getName(), i ); 449 #endif 450 } 451} 452