123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "sfx/sfxSoundscape.h"
- #include "sfx/sfxAmbience.h"
- #include "sfx/sfxEnvironment.h"
- #include "sfx/sfxState.h"
- #include "sfx/sfxSource.h"
- #include "sfx/sfxSystem.h"
- //#define DEBUG_SPEW
- //FIXME: fix reverb resetting
- //=============================================================================
- // SFXSoundscape.
- //=============================================================================
- // MARK: ---- SFXSoundscape ----
- //-----------------------------------------------------------------------------
- SFXSoundscape::SFXSoundscape( SFXAmbience* ambience )
- : mAmbience( ambience )
- {
- mDirtyBits.set( AmbienceDirty );
- mFlags.set( FlagUnique );
-
- dMemset( mStates, 0, sizeof( mStates ) );
- }
- //-----------------------------------------------------------------------------
- void SFXSoundscape::setAmbience( SFXAmbience* ambience )
- {
- AssertFatal( ambience != NULL, "SFXSoundscape::setAmbience - ambience cannot be NULL!" );
- mDirtyBits.set( AmbienceDirty );
- mAmbience = ambience;
- }
- //=============================================================================
- // SFXSoundscapeManager.
- //=============================================================================
- // MARK: ---- SFXSoundscapeManager ----
- //-----------------------------------------------------------------------------
- SFXSoundscapeManager::SFXSoundscapeManager()
- : mCurrentReverbIndex( -1 )
- {
- VECTOR_SET_ASSOCIATION( mStack );
- VECTOR_SET_ASSOCIATION( mFadeStack );
-
- #ifndef TORQUE_SHIPPING
- // Hook on to the ambience change signal (used
- // to respond to editing of ambiences).
-
- SFXAmbience::getChangeSignal().notify
- ( this, &SFXSoundscapeManager::_notifyAmbienceChanged );
-
- #endif
- // Push the global ambience.
-
- mDefaultGlobalAmbience = new SFXAmbience;
- SFXSoundscape* global = mChunker.alloc();
- constructInPlace( global, mDefaultGlobalAmbience );
- mStack.push_back( global );
- }
- //-----------------------------------------------------------------------------
- SFXSoundscapeManager::~SFXSoundscapeManager()
- {
- #ifndef TORQUE_SHIPPING
- // Remove the hook on the ambience change signal.
-
- SFXAmbience::getChangeSignal().remove
- ( this, &SFXSoundscapeManager::_notifyAmbienceChanged );
-
- #endif
- mDefaultGlobalAmbience->deleteObject();
- }
- //-----------------------------------------------------------------------------
- void SFXSoundscapeManager::update()
- {
- // Make sure the topmost reverb on the stack is active.
-
- S32 reverbIndex = _findTopmostReverbOnStack( mStack );
- if( mCurrentReverbIndex != reverbIndex )
- {
- if( reverbIndex == -1 )
- {
- // No ambience on the stack has reverb settings so reset
- // to default.
- SFX->setReverb( SFXReverbProperties() );
- }
- else
- {
- SFXAmbience* ambience = mStack[ reverbIndex ]->getAmbience();
- AssertFatal( ambience->getEnvironment(), "SFXSoundscapeManager::update - Reverb lookup return ambience without reverb!" );
- SFX->setRolloffFactor( ambience->getRolloffFactor() );
- SFX->setDopplerFactor( ambience->getDopplerFactor() );
- SFX->setReverb( ambience->getEnvironment()->getReverb() );
- }
- mCurrentReverbIndex = reverbIndex;
- }
-
- // Update the active soundscapes.
-
- for( U32 i = 0; i < mStack.size(); ++ i )
- {
- SFXSoundscape* soundscape = mStack[ i ];
-
- // If the soundscape's associated ambience has changed
-
- if( soundscape->mDirtyBits.test( SFXSoundscape::AmbienceDirty ) )
- {
- SFXAmbience* ambience = soundscape->getAmbience();
-
- // Start playing the ambient audio track if it isn't
- // already playing and if the soundscape isn't overridden
- // by an instance lower down the stack.
-
- if( !soundscape->_isOverridden() )
- {
- SFXTrack* track = ambience->getSoundTrackProfile();
- if( !soundscape->mSource || soundscape->mSource->getTrack() != track )
- {
- if( soundscape->mSource != NULL )
- {
- soundscape->mSource->stop();
- soundscape->mSource = NULL;
- }
-
- if( track )
- soundscape->mSource = SFX->playOnce( track );
- }
- else if( soundscape->mSource != NULL )
- {
- // Make sure to revert a fade-out running on the source
- // if it has been taken from the fade stack.
-
- soundscape->mSource->play();
- }
- }
-
- // Activate SFXStates on the ambience. For state slots that
- // have changed, deactivate states that we have already activated.
-
- for( U32 ambState = 0; ambState < SFXAmbience::MaxStates; ++ambState)
- {
- SFXState* state = ambience->getState(ambState);
- if( soundscape->mStates[ambState] != state )
- {
- if( soundscape->mStates[ambState] )
- soundscape->mStates[ambState]->deactivate();
- if( state )
- state->activate();
-
- soundscape->mStates[ambState] = state;
- }
- }
-
- soundscape->mDirtyBits.clear( SFXSoundscape::AmbienceDirty );
- }
- }
-
- // Clean out the fade stack.
-
- for( U32 i = 0; i < mFadeStack.size(); )
- if( mFadeStack[ i ]->mSource == NULL )
- {
- SFXSoundscape* soundscape = mFadeStack[ i ];
- mFadeStack.erase_fast( i );
-
- #ifdef DEBUG_SPEW
- Platform::outputDebugString( "[SFXSoundscapeManager] Deleting faded instance of '%s'",
- soundscape->getAmbience()->getName() );
- #endif
-
- // Free the soundscape.
-
- destructInPlace( soundscape );
- mChunker.free( soundscape );
- }
- else
- ++ i;
- }
- //-----------------------------------------------------------------------------
- SFXSoundscape* SFXSoundscapeManager::insertSoundscape( U32 index, SFXAmbience* ambience )
- {
- AssertFatal( index <= mStack.size(), "SFXSoundscapeManager::insertSoundscape - index out of range" );
- AssertFatal( index != 0, "SFXSoundscapeManager::insertSoundscape - cannot insert before global soundscape" );
- AssertFatal( ambience != NULL, "SFXSoundscapeManager::insertSoundscape - got a NULL ambience" );
-
- // Look for an existing soundscape that is assigned the
- // same ambience.
-
- S32 ambientInstanceIndex = _findOnStack( ambience, mStack );
-
- // Push a soundscape unto the stack. If there is an instance
- // on the fade stack that is tied to the given ambience, bring that
- // soundscape over to the active stack. Otherwise create a new
- // soundscape instance.
-
- SFXSoundscape* soundscape = NULL;
- if( ambientInstanceIndex == -1 )
- {
- S32 fadeIndex = _findOnStack( ambience, mFadeStack );
- if( fadeIndex != -1 )
- {
- soundscape = mFadeStack[ fadeIndex ];
- mFadeStack.erase_fast( fadeIndex );
-
- // Make sure the soundscape will get re-evaluated
- // on the next update.
-
- soundscape->mDirtyBits.set( SFXSoundscape::AmbienceDirty );
- #ifdef DEBUG_SPEW
- Platform::outputDebugString( "[SFXSoundscapeManager] Moving ambience '%s' from fade stack to #%i (total: %i)",
- ambience->getName(), index, mStack.size() + 1 );
- #endif
- }
- }
-
- if( !soundscape )
- {
- #ifdef DEBUG_SPEW
- Platform::outputDebugString( "[SFXSoundscapeManager] Adding new instance for '%s' at #%i (total: %i)",
- ambience->getName(), index, mStack.size() + 1 );
- #endif
-
- soundscape = mChunker.alloc();
- constructInPlace( soundscape, ambience );
- }
-
- mStack.insert( index, soundscape );
-
- // If there is an existing soundscape that is assigned the
- // same ambience and it is lower on the stack, steal its sound
- // source if it has one. If it is higher up the stack, simply
- // mark this soundscape as being overridden.
-
- if( ambientInstanceIndex != -1 )
- {
- SFXSoundscape* existingSoundscape = mStack[ ambientInstanceIndex ];
-
- existingSoundscape->mFlags.clear( SFXSoundscape::FlagUnique );
- soundscape->mFlags.clear( SFXSoundscape::FlagUnique );
-
- if( ambientInstanceIndex < index )
- {
- existingSoundscape->mFlags.set( SFXSoundscape::FlagOverridden );
- SFXSource* source = existingSoundscape->mSource;
- existingSoundscape->mSource = NULL;
-
- if( source && source->isPlaying() )
- soundscape->mSource = source;
- }
- else
- {
- soundscape->mFlags.set( SFXSoundscape::FlagOverridden );
- }
- }
-
- return soundscape;
- }
- //-----------------------------------------------------------------------------
- void SFXSoundscapeManager::removeSoundscape( SFXSoundscape* soundscape )
- {
- AssertFatal( soundscape != getGlobalSoundscape(),
- "SFXSoundscapeManager::removeSoundscape() - trying to remove the global soundscape" );
-
- // Find the soundscape on the stack.
-
- U32 index = 1;
- for( ; index < mStack.size(); ++ index )
- if( mStack[ index ] == soundscape )
- break;
-
- AssertFatal( index < mStack.size(),
- "SFXSoundscapeManager::removeSoundscape() - soundscape not on stack" );
-
- // Find out if the soundscape has the current reverb
- // environment. If so, we need to change the reverb to
- // the next one higher up the stack.
- const bool isCurrentReverb = ( _findTopmostReverbOnStack( mStack ) == index );
-
- // Remove the soundscape from the stack.
- mStack.erase( index );
-
- // Update reverb, if necessary.
-
- if( isCurrentReverb )
- {
- S32 reverbIndex = _findTopmostReverbOnStack( mStack );
- if( reverbIndex != -1 )
- SFX->setReverb( mStack[ reverbIndex ]->getAmbience()->getEnvironment()->getReverb() );
- }
-
- // Deactivate states.
-
- for( U32 i = 0; i < SFXAmbience::MaxStates; ++ i )
- if( soundscape->mStates[ i ] )
- {
- soundscape->mStates[ i ]->deactivate();
- soundscape->mStates[ i ] = NULL;
- }
-
- // If the soundscape is the only instance of its ambience, move
- // it to the fade stack. Otherwise delete the soundscape.
-
- if( soundscape->_isUnique() && soundscape->mSource != NULL )
- {
- #ifdef DEBUG_SPEW
- Platform::outputDebugString( "[SFXSoundscapeManager] Moving ambience '%s' at index #%i to fade stack",
- soundscape->getAmbience()->getName(), index );
- #endif
-
- soundscape->mSource->stop();
- mFadeStack.push_back( soundscape );
- }
- else
- {
- if( !soundscape->_isUnique() )
- {
- // If this is the overriding soundscape, transfer its state
- // to the ambient instance lower down the stack.
-
- if( !soundscape->_isOverridden() )
- {
- S32 overrideeIndex = index - 1;
- for( ; overrideeIndex >= 0; -- overrideeIndex )
- if( soundscape->getAmbience() == mStack[ overrideeIndex ]->getAmbience() )
- break;
-
- AssertFatal( overrideeIndex >= 0,
- "SFXSoundscapeManager::removeSoundscape() - could not find ambience being overridden on stack" );
-
- // Pass the source on to the previous soundscape.
-
- mStack[ overrideeIndex ]->mSource = soundscape->mSource;
- soundscape->mSource = NULL;
- }
-
- #ifdef DEBUG_SPEW
- Platform::outputDebugString( "[SFXSoundscapeManager] Removing instance of '%s' at index #%i",
- soundscape->getAmbience()->getName(), index );
- #endif
-
- // If there's only one instance of this ambience is
- // left, mark it as being unique now.
-
- U32 numInstances = 0;
- for( U32 i = 0; i < mStack.size(); ++ i )
- if( mStack[ i ]->getAmbience() == soundscape->getAmbience() )
- ++ numInstances;
-
- if( numInstances == 1 )
- mStack[ _findOnStack( soundscape->getAmbience(), mStack ) ]->mFlags.set( SFXSoundscape::FlagUnique );
- }
-
- // Free the soundscape.
-
- destructInPlace( soundscape );
- mChunker.free( soundscape );
- }
- }
- //-----------------------------------------------------------------------------
- S32 SFXSoundscapeManager::_findOnStack( SFXAmbience* ambience, const Vector< SFXSoundscape* >& stack )
- {
- // Search the stack top to bottom so we always find
- // the uppermost instance of the ambience on the stack.
-
- for( S32 i = stack.size() - 1; i >= 0; -- i )
- if( stack[ i ]->getAmbience() == ambience )
- return i;
-
- return -1;
- }
- //-----------------------------------------------------------------------------
- S32 SFXSoundscapeManager::_findTopmostReverbOnStack( const Vector< SFXSoundscape* >& stack )
- {
- for( S32 i = stack.size() - 1; i >= 0; -- i )
- if( stack[ i ]->getAmbience()->getEnvironment() )
- return i;
-
- return -1;
- }
- //-----------------------------------------------------------------------------
- void SFXSoundscapeManager::_notifyAmbienceChanged( SFXAmbience* ambience )
- {
- //RDTODO: fade stack?
-
- // Set the ambience dirty bit on all soundscapes
- // tied to the given ambience.
-
- for( U32 i = 0; i < mStack.size(); ++ i )
- if( mStack[ i ]->getAmbience() == ambience )
- {
- mStack[ i ]->mDirtyBits.set( SFXSoundscape::AmbienceDirty );
-
- #ifdef DEBUG_SPEW
- Platform::outputDebugString( "[SFXSoundscapeManager] Ambience '%s' at #%i changed",
- ambience->getName(), i );
- #endif
- }
- }
|