123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945 |
- //-----------------------------------------------------------------------------
- // 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/sfxController.h"
- #include "sfx/sfxPlayList.h"
- #include "sfx/sfxProfile.h"
- #include "sfx/sfxSource.h"
- #include "sfx/sfxSystem.h"
- #include "sfx/sfxState.h"
- #include "sfx/sfxDescription.h"
- #include "console/engineAPI.h"
- #include "math/mRandom.h"
- IMPLEMENT_CONOBJECT( SFXController );
- ConsoleDocClass( SFXController,
- "@brief A sound source that drives multi-source playback.\n\n"
-
- "This class acts as an interpreter for SFXPlayLists. It goes through the slots of the playlist it is "
- "attached to and performs the actions described by each of the slots in turn.\n"
-
- "As SFXControllers are created implicitly by the SFX system when instantiating a source for a play list it is "
- "in most cases not necessary to directly deal with the class.\n"
-
- "The following example demonstrates how a controller would commonly be created.\n"
-
- "@tsexample\n"
- "// Create a play list from two SFXProfiles.\n"
- "%playList = new SFXPlayList()\n"
- "{\n"
- " // Use a looped description so the list playback will loop.\n"
- " description = AudioMusicLoop2D;\n"
- "\n"
- " track[ 0 ] = Profile1;\n"
- " track[ 1 ] = Profile2;\n"
- "};\n"
- "\n"
- "// Play the list. This will implicitly create a controller.\n"
- "sfxPlayOnce( %playList );\n"
- "@endtsexample\n\n"
-
- "@note Play lists are updated at regular intervals by the sound system. This processing determines the granularity at "
- "which playlist action timing takes place.\n"
- "@note This class cannot be instantiated directly. Use sfxPlayOnce() or sfxCreateSource() with the playlist "
- "you want to play to create an instance of this class.\n"
-
- "@see SFXPlayList\n"
- "@ingroup SFX\n"
- );
- //-----------------------------------------------------------------------------
- SFXController::SFXController( SFXPlayList* playList )
- : Parent( playList ),
- mTrace( playList->trace() )
- {
- VECTOR_SET_ASSOCIATION( mInsns );
- VECTOR_SET_ASSOCIATION( mSources );
- VECTOR_SET_ASSOCIATION( mParameters );
-
- _compileList( playList );
- }
- //-----------------------------------------------------------------------------
- SFXController::~SFXController()
- {
- }
- //-----------------------------------------------------------------------------
- void SFXController::initPersistFields()
- {
- addGroup( "Debug" );
- addField( "trace", TypeBool, Offset( mTrace, SFXController ),
- "If true, the controller logs its operation to the console.\n"
- "This is a non-networked field that will work locally only." );
- endGroup( "Debug" );
-
- Parent::initPersistFields();
- }
- //-----------------------------------------------------------------------------
- SFXController* SFXController::_create( SFXPlayList* playList )
- {
- AssertFatal( playList != NULL, "SFXController::_create() - got a NULL playlist!" );
-
- SFXController* controller = new SFXController( playList );
- controller->registerObject();
-
- return controller;
- }
- //-----------------------------------------------------------------------------
- void SFXController::_compileList( SFXPlayList* playList )
- {
- mInsns.clear();
- const bool isLooping = playList->getDescription()->mIsLooping;
-
- // Create a slot list that determines the order the slots will be
- // played in.
-
- U32 slotList[ SFXPlayList::NUM_SLOTS ];
- bool isOrderedRandom = false;
- switch( playList->getRandomMode() )
- {
- case SFXPlayList::RANDOM_OrderedRandom:
- isOrderedRandom = true;
- /* fallthrough */
-
- case SFXPlayList::RANDOM_NotRandom:
- // Generate sequence 1-NUM_SLOTS.
- for( U32 i = 0; i < SFXPlayList::NUM_SLOTS; ++ i )
- slotList[ i ] = i;
-
- if( isOrderedRandom )
- {
- // Randomly exchange slots in the list.
- for( U32 i = 0; i < SFXPlayList::NUM_SLOTS; ++ i )
- swap( slotList[ gRandGen.randI( 0, SFXPlayList::NUM_SLOTS - 1 ) ], slotList[ i ] );
- }
- break;
-
- case SFXPlayList::RANDOM_StrictRandom:
- // Randomly generate NUM_SLOTS slot indices.
- for( U32 i = 0; i < SFXPlayList::NUM_SLOTS; ++ i )
- slotList[ i ] = gRandGen.randI( 0, SFXPlayList::NUM_SLOTS - 1 );
- break;
- }
-
- // Generate the instruction list.
-
- U32 slotCount = 0;
- for( U32 i = 0; i < SFXPlayList::NUM_SLOTS; ++ i )
- {
- const U32 slotIndex = slotList[ i ];
- const U32 slotStartIp = mInsns.size();
-
- SFXState* state = playList->getSlots().mState[ slotIndex ];
-
- // If there's no track in this slot, ignore it.
-
- if( !playList->getSlots().mTrack[ slotIndex ] )
- continue;
-
- // If this is a looped slot and the list is not set to loop
- // indefinitly on single slots, start a loop.
-
- S32 loopStartIp = -1;
- if( playList->getSlots().mRepeatCount[ slotIndex ] > 0
- && ( !isLooping || playList->getLoopMode() != SFXPlayList::LOOP_Single ) )
- {
- Insn insn( OP_LoopBegin, slotIndex, state );
- insn.mArg.mLoopCount = playList->getSlots().mRepeatCount[ slotIndex ];
- mInsns.push_back( insn );
-
- loopStartIp = mInsns.size();
- }
-
- // Add in-delay, if any.
-
- if( playList->getSlots().mDelayTimeIn.mValue[ slotIndex ] > 0.0f )
- {
- Insn insn( OP_Delay, slotIndex, state );
- insn.mArg.mDelayTime.mValue[ 0 ] = playList->getSlots().mDelayTimeIn.mValue[ slotIndex ];
- insn.mArg.mDelayTime.mVariance[ 0 ][ 0 ] = playList->getSlots().mDelayTimeIn.mVariance[ slotIndex ][ 0 ];
- insn.mArg.mDelayTime.mVariance[ 0 ][ 1 ] = playList->getSlots().mDelayTimeIn.mVariance[ slotIndex ][ 1 ];
-
- mInsns.push_back( insn );
- }
-
- // Add the in-transition.
-
- const SFXPlayList::ETransitionMode transitionIn = playList->getSlots().mTransitionIn[ slotIndex ];
- if( transitionIn != SFXPlayList::TRANSITION_None )
- {
- Insn insn( slotIndex, state );
- _genTransition( insn, transitionIn );
- mInsns.push_back( insn );
- }
-
- // Add the play instruction.
-
- {
- Insn insn( OP_Play, slotIndex, state );
- mInsns.push_back( insn );
- }
-
- // Add out-delay, if any.
-
- if( playList->getSlots().mDelayTimeOut.mValue[ slotIndex ] > 0.0f )
- {
- Insn insn( OP_Delay, slotIndex, state );
- insn.mArg.mDelayTime.mValue[ 0 ] = playList->getSlots().mDelayTimeOut.mValue[ slotIndex ];
- insn.mArg.mDelayTime.mVariance[ 0 ][ 0 ] = playList->getSlots().mDelayTimeOut.mVariance[ slotIndex ][ 0 ];
- insn.mArg.mDelayTime.mVariance[ 0 ][ 1 ] = playList->getSlots().mDelayTimeOut.mVariance[ slotIndex ][ 1 ];
-
- mInsns.push_back( insn );
- }
-
- // Add the out-transition.
-
- const SFXPlayList::ETransitionMode transitionOut = playList->getSlots().mTransitionOut[ slotIndex ];
- if( transitionOut != SFXPlayList::TRANSITION_None )
- {
- Insn insn( slotIndex, state );
- _genTransition( insn, transitionOut );
- mInsns.push_back( insn );
- }
-
- // Loop, if necessary.
-
- if( loopStartIp != -1 )
- {
- Insn insn( OP_LoopEnd, slotIndex, state );
- insn.mArg.mJumpIp = loopStartIp;
- mInsns.push_back( insn );
- }
-
- // If the list is on repeat-single, unconditionally
- // loop over the instruction sequence of each slot.
-
- if( isLooping && playList->getLoopMode() == SFXPlayList::LOOP_Single )
- {
- Insn insn( OP_Jump, slotIndex, state );
- insn.mArg.mJumpIp = slotStartIp;
- mInsns.push_back( insn );
- }
-
- // If we have reached the limit of slots to play,
- // stop generating.
-
- slotCount ++;
- if( playList->getNumSlotsToPlay() == slotCount )
- break;
- }
-
- // Set up for execution.
- mIp = 0;
- if( !mInsns.empty() )
- _initInsn();
- }
- //-----------------------------------------------------------------------------
- void SFXController::_genTransition( Insn& insn, SFXPlayList::ETransitionMode transition )
- {
- switch( transition )
- {
- case SFXPlayList::TRANSITION_Wait:
- insn.mOpcode = OP_WaitSingle;
- break;
-
- case SFXPlayList::TRANSITION_WaitAll:
- insn.mOpcode = OP_WaitAll;
- break;
-
- case SFXPlayList::TRANSITION_Stop:
- insn.mOpcode = OP_StopSingle;
- break;
- case SFXPlayList::TRANSITION_StopAll:
- insn.mOpcode = OP_StopAll;
- break;
-
- default:
- AssertFatal( false, "SFXController::_addTransition() - should not reach here" );
- }
- }
- //-----------------------------------------------------------------------------
- void SFXController::_initInsn()
- {
- Insn& insn = mInsns[ mIp ];
- switch( insn.mOpcode )
- {
- case OP_Delay:
- mDelayEndTime = Platform::getVirtualMilliseconds()
- + U32( insn.mArg.mDelayTime.getValue( 0, 0.0f ) * 1000.f );
- break;
-
- default:
- break;
- }
-
- if( mTrace )
- _printInsn(insn );
- }
- //-----------------------------------------------------------------------------
- void SFXController::_printInsn( Insn& insn)
- {
- switch( insn.mOpcode )
- {
- case OP_Delay:
- Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: Delay %f:%f:%f",
- mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "",
- insn.mArg.mDelayTime.mValue[ 0 ],
- insn.mArg.mDelayTime.mVariance[ 0 ],
- insn.mArg.mDelayTime.mVariance[ 1 ]
- );
- break;
-
- case OP_WaitSingle:
- Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: WaitSingle",
- mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "" );
- break;
- case OP_WaitAll:
- Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: WaitAll",
- mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "" );
- break;
- case OP_StopSingle:
- Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: StopSingle",
- mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "" );
- break;
- case OP_StopAll:
- Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: StopAll",
- mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "" );
- break;
-
- case OP_Play:
- Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: Play",
- mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "" );
- break;
-
- case OP_Jump:
- Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: Jump %i",
- mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "", insn.mArg.mJumpIp );
- break;
-
- case OP_LoopBegin:
- Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: LoopBegin %i",
- mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "", insn.mArg.mLoopCount );
- break;
-
- case OP_LoopEnd:
- Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: LoopEnd",
- mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "" );
- break;
- }
- }
- //-----------------------------------------------------------------------------
- bool SFXController::_execInsn()
- {
- bool endUpdate = false;
- Insn& insn = mInsns[ mIp ];
-
- switch( insn.mOpcode )
- {
- case OP_Delay:
- {
- if( Platform::getVirtualMilliseconds() < mDelayEndTime )
- endUpdate = true;
- else
- _advanceIp();
- break;
- }
-
- case OP_Play:
- {
- SFXPlayList* playList = getPlayList();
- SFXTrack* track = playList->getSlots().mTrack[ insn.mSlotIndex ];
-
- // Handle existing sources playing on this slot and find
- // whether we need to start a new source.
- //
- // Go through the list top-down so we can push sources we re-use
- // to the top of the list. A side-effect of doing it this way is
- // that the order of the sources that are preserved gets reversed,
- // i.e. older sources will end up higher up the stack.
-
- bool startNew = true;
- SFXPlayList::EReplayMode replayMode = playList->getSlots().mReplayMode[ insn.mSlotIndex ];
- if( replayMode != SFXPlayList::REPLAY_IgnorePlaying )
- for( S32 i = mSources.size() - 1; i >= 0; -- i )
- {
- Source& source = mSources[ i ];
- if( source.mSlotIndex != insn.mSlotIndex )
- continue;
-
- // If the play-once source has expired, remove the entry
- // and go on.
-
- if( source.mPtr == NULL )
- {
- mSources.erase( i );
- ++ i;
- continue;
- }
-
- // Decide what to do with the still-playing source.
-
- if( replayMode == SFXPlayList::REPLAY_RestartPlaying
- || replayMode == SFXPlayList::REPLAY_KeepPlaying )
- {
- // Restart the source or keep playing it.
- // Either way, move it to the top of the stack.
-
- startNew = false;
-
- Source src = mSources[ i ];
- mSources.erase( i );
-
- //RDTODO: add a method to restart cleanly in the presence of fades; this here
- // just cuts the current playback short
- if( replayMode == SFXPlayList::REPLAY_RestartPlaying )
- src.mPtr->stop( 0.f );
-
- src.mPtr->play();
-
- // Move the source to the top of the stack.
-
- mSources.increment();
- mSources.last() = src;
- }
- else if( replayMode == SFXPlayList::REPLAY_StartNew )
- {
- // Kill off existing source.
-
- source.mPtr->stop();
- mSources.erase( i );
- ++ i;
- }
- else if( replayMode == SFXPlayList::REPLAY_SkipIfPlaying )
- {
- startNew = false;
- break;
- }
- }
-
- if( startNew )
- {
- // Create a new source.
- SFXSource* source = SFX->createSource(
- track,
- &getTransform(),
- &getVelocity()
- );
- // Append the source to the list of playing sources.
- if( source )
- {
- mSources.increment();
- Source& src = mSources.last();
- // Determine fade times.
-
- F32 fadeInTime = -1;
- F32 fadeOutTime = -1;
-
- if( playList->getSlots().mFadeTimeIn.mValue[ insn.mSlotIndex ] != -1 )
- fadeInTime = playList->getSlots().mFadeTimeIn.getValue( insn.mSlotIndex, 0.f );
- if( playList->getSlots().mFadeTimeOut.mValue[ insn.mSlotIndex ] != -1 )
- fadeOutTime = playList->getSlots().mFadeTimeOut.getValue( insn.mSlotIndex, 0.f );
-
- if( fadeInTime != -1 || fadeOutTime != -1 )
- source->setFadeTimes( fadeInTime, fadeOutTime );
- // Set up source record.
-
- src.mPtr = source;
- src.mSlotIndex = insn.mSlotIndex;
- src.mVolumeScale = playList->getSlots().mVolumeScale.getValue( insn.mSlotIndex, 0.f, 1.f );
- src.mPitchScale = playList->getSlots().mPitchScale.getValue( insn.mSlotIndex );
- src.mFadeInTime = fadeInTime;
- src.mFadeOutTime = fadeOutTime;
-
- SFXPlayList::EStateMode stateMode = playList->getSlots().mStateMode[ insn.mSlotIndex ];
- if( stateMode != SFXPlayList::STATE_IgnoreInactive )
- src.mState = insn.mState;
-
- // Set the source's volume and pitch. Either is scaled by our own
- // assigned value and the scale factors from the playlist slot.
-
- source->setModulativeVolume( mAttenuatedVolume * src.mVolumeScale );
- source->setModulativePitch( mEffectivePitch * src.mPitchScale );
-
- // Set min and max range.
-
- const SFXPlayList::VariantFloat& minDistance = playList->getSlots().mMinDistance;
- const SFXPlayList::VariantFloat& maxDistance = playList->getSlots().mMaxDistance;
-
- if( minDistance.mValue[ insn.mSlotIndex ] >= 0.f
- && maxDistance.mValue[ insn.mSlotIndex ] >= 0.f )
- source->setMinMaxDistance(
- minDistance.getValue( insn.mSlotIndex, 0.f ),
- maxDistance.getValue( insn.mSlotIndex, 0.f )
- );
-
- // Start the source.
-
- source->play();
- SFX->deleteWhenStopped( source );
- }
- }
-
- _advanceIp();
- break;
- }
-
- case OP_WaitSingle:
- {
- if( !mSources.empty() && mSources.last().mPtr != NULL && mSources.last().mPtr->isPlaying() )
- endUpdate = true;
- else
- {
- if( !mSources.empty() )
- mSources.decrement();
- _advanceIp();
- }
- break;
- }
- case OP_WaitAll:
- {
- for( U32 i = 0; i < mSources.size(); ++ i )
- if( mSources[ i ].mPtr != NULL && mSources[ i ].mPtr->isStopped() )
- {
- mSources.erase( i );
- -- i;
- }
-
- if( !mSources.empty() )
- endUpdate = true;
- else
- _advanceIp();
-
- break;
- }
-
- case OP_StopSingle:
- {
- if( !mSources.empty() )
- {
- if( mSources.last().mPtr != NULL )
- mSources.last().mPtr->stop();
- mSources.decrement();
- }
-
- _advanceIp();
- break;
- }
-
- case OP_StopAll:
- {
- while( !mSources.empty() )
- {
- if( mSources.last().mPtr != NULL )
- mSources.last().mPtr->stop();
- mSources.decrement();
- }
-
- _advanceIp();
- break;
- }
-
- case OP_Jump:
- {
- mIp = insn.mArg.mJumpIp;
- _initInsn();
- break;
- }
-
- case OP_LoopBegin:
- {
- mLoopCounter = insn.mArg.mLoopCount;
- _advanceIp();
- break;
- }
-
- case OP_LoopEnd:
- {
- -- mLoopCounter;
- if( mLoopCounter > 0 )
- {
- mIp = insn.mArg.mJumpIp;
- _initInsn();
- }
- else
- _advanceIp();
-
- break;
- }
- }
-
- return endUpdate;
- }
- //-----------------------------------------------------------------------------
- void SFXController::_advanceIp()
- {
- mIp ++;
- if( mIp < mInsns.size() )
- _initInsn();
- }
- //-----------------------------------------------------------------------------
- void SFXController::_onParameterEvent( SFXParameter* parameter, SFXParameterEvent event )
- {
- Parent::_onParameterEvent( parameter, event );
-
- // Implement cursor semantic.
-
- if( event == SFXParameterEvent_ValueChanged
- && parameter->getChannel() == SFXChannelCursor )
- {
- U32 slot = U32( mFloor( parameter->getValue() ) );
- if( slot != getCurrentSlot() )
- setCurrentSlot( slot );
- }
- }
- //-----------------------------------------------------------------------------
- SFXPlayList* SFXController::getPlayList() const
- {
- return static_cast< SFXPlayList* >( mTrack.getPointer() );
- }
- //-----------------------------------------------------------------------------
- U32 SFXController::getCurrentSlot() const
- {
- if( mIp >= mInsns.size() )
- return 0;
- else
- return mInsns[ mIp ].mSlotIndex;
- }
- //-----------------------------------------------------------------------------
- void SFXController::setCurrentSlot( U32 index )
- {
- mIp = 0;
- while( mIp < mInsns.size() && mInsns[ mIp ].mSlotIndex != index )
- ++ mIp;
-
- if( mIp >= mInsns.size() )
- mIp = 0;
-
- if( !mInsns.empty() )
- _initInsn();
- }
- //-----------------------------------------------------------------------------
- void SFXController::_play()
- {
- Parent::_play();
- // Unpause sources, if we are paused.
-
- if( mStatus == SFXStatusPaused )
- {
- for( U32 i = 0; i < mSources.size(); ++ i )
- if( mSources[ i ].mPtr != NULL )
- mSources[ i ].mPtr->play( 0.f ); // We want our fade values to take effect.
- else
- {
- mSources.erase( i );
- -- i;
- }
- }
- }
- //-----------------------------------------------------------------------------
- void SFXController::_pause()
- {
- Parent::_pause();
- // Pause all playing sources.
-
- for( U32 i = 0; i < mSources.size(); ++ i )
- if( mSources[ i ].mPtr != NULL )
- mSources[ i ].mPtr->pause( 0.f ); // We want our fade values to take effect.
- else
- {
- mSources.erase( i );
- -- i;
- }
- }
- //-----------------------------------------------------------------------------
- void SFXController::_stop()
- {
- Parent::_stop();
- // Stop all playing sources.
-
- while( !mSources.empty() )
- {
- if( mSources.last().mPtr != NULL )
- mSources.last().mPtr->stop( 0.f ); // We want our fade values to take effect.
- mSources.decrement();
- }
-
- // Reset execution.
-
- mIp = 0;
- if( !mInsns.empty() )
- _initInsn();
- }
- //-----------------------------------------------------------------------------
- void SFXController::_updateVolume( const MatrixF& listener )
- {
- F32 oldAttenuatedVolume = mAttenuatedVolume;
- Parent::_updateVolume( listener );
-
- // If the attenuated volume has changed, pass it off
- // as the modulative volume to all our sources.
-
- if( oldAttenuatedVolume != mAttenuatedVolume )
- for( U32 i = 0; i < mSources.size(); ++ i )
- {
- Source& source = mSources[ i ];
- if( source.mPtr != NULL )
- source.mPtr->setModulativeVolume( mAttenuatedVolume * source.mVolumeScale );
- else
- {
- mSources.erase( i );
- -- i;
- }
- }
- }
- //-----------------------------------------------------------------------------
- void SFXController::_updatePitch()
- {
- F32 oldEffectivePitch = mEffectivePitch;
- Parent::_updatePitch();
-
- if( mEffectivePitch != oldEffectivePitch )
- for( U32 i = 0; i < mSources.size(); ++ i )
- {
- Source& source = mSources[ i ];
- if( source.mPtr != NULL )
- source.mPtr->setModulativePitch( mEffectivePitch * source.mPitchScale );
- else
- {
- mSources.erase( i );
- -- i;
- }
- }
- }
- //-----------------------------------------------------------------------------
- void SFXController::_updatePriority()
- {
- F32 oldEffectivePriority = mEffectivePriority;
- Parent::_updatePriority();
-
- if( mEffectivePriority != oldEffectivePriority )
- for( U32 i = 0; i < mSources.size(); ++ i )
- {
- Source& source = mSources[ i ];
- if( source.mPtr != NULL )
- source.mPtr->setModulativePriority( mEffectivePriority );
- else
- {
- mSources.erase( i );
- -- i;
- }
- }
- }
- //-----------------------------------------------------------------------------
- void SFXController::_update()
- {
- Parent::_update();
-
- SFXPlayList* playList = getPlayList();
- // Check all sources against the current state setup and
- // take appropriate actions.
-
- for( U32 i = 0; i < mSources.size(); ++ i )
- {
- Source& source = mSources[ i ];
-
- // If the source has already stopped playing,
- // remove it.
-
- if( !source.mPtr )
- {
- mSources.erase( i );
- -- i;
- continue;
- }
-
- if( !source.mState )
- continue;
-
- SFXPlayList::EStateMode stateMode = playList->getSlots().mStateMode[ mSources[ i ].mSlotIndex ];
- if( !source.mState->isActive() )
- {
- if( source.mPtr->isPlaying() )
- {
- // The source is playing in an incompatible state.
-
- if( stateMode == SFXPlayList::STATE_PauseInactive )
- source.mPtr->pause();
- else if( stateMode == SFXPlayList::STATE_StopInactive )
- {
- source.mPtr->stop();
- mSources.erase( i );
-
- -- i;
- }
- }
- }
- else
- {
- // Unpause a source that had its state become active again.
-
- if( source.mPtr->isPaused() && stateMode == SFXPlayList::STATE_PauseInactive )
- source.mPtr->play();
- }
- }
-
- // Update interpreter.
-
- bool endUpdate = false;
- while( !endUpdate )
- {
- if( mIp >= mInsns.size() )
- {
- // End of list reached.
-
- if( playList->getDescription()->mIsLooping &&
- playList->getLoopMode() == SFXPlayList::LOOP_All )
- {
- // The play list is set to repeat-all.
- // If it is also random, generate a new instruction list
- // so we get a new playing order. Otherwise just reset.
-
- if( playList->getRandomMode() != SFXPlayList::RANDOM_NotRandom )
- _compileList( playList );
- else
- {
- mIp = 0;
- if( !mInsns.empty() )
- _initInsn();
- }
-
- // Reset play timer.
-
- mPlayTimer.reset();
- mPlayTimer.start();
- }
- else
- {
- // Moved to stopped state.
-
- mPlayTimer.stop();
- _setStatus( SFXStatusStopped );
- mIp = 0;
- }
-
- // End this update. This limits playlist to at most one complete
- // cycle per update.
-
- break;
- }
-
- Insn& insn = mInsns[ mIp ];
-
- if( insn.mState && !insn.mState->isActive() )
- {
- // The state associated with the slot is inactive. Skip
- // the instructions.
- _advanceIp();
- }
- else
- endUpdate = _execInsn();
- }
- }
- //=============================================================================
- // Console Methods.
- //=============================================================================
- // MARK: ---- Console Methods ----
- //-----------------------------------------------------------------------------
- DefineEngineMethod( SFXController, getCurrentSlot, S32, (),,
- "Get the index of the playlist slot currently processed by the controller.\n"
- "@return The slot index currently being played.\n"
- "@see SFXPlayList" )
- {
- return object->getCurrentSlot();
- }
- //-----------------------------------------------------------------------------
- DefineEngineMethod( SFXController, setCurrentSlot, void, ( S32 index ),,
- "Set the index of the playlist slot to play by the controller. This can be used to seek in the playlist.\n"
- "@param index Index of the playlist slot." )
- {
- object->setCurrentSlot( index );
- }
|