Torque3D/Engine/source/sfx/sfxController.cpp

947 lines
29 KiB
C++
Raw Normal View History

2012-09-19 15:15:01 +00:00
//-----------------------------------------------------------------------------
// 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 ),
2020-05-11 20:24:49 +00:00
mTrace( playList->trace() ),
mLoopCounter(0)
2012-09-19 15:15:01 +00:00
{
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 )
T3D::swap( slotList[ gRandGen.randI( 0, SFXPlayList::NUM_SLOTS - 1 ) ], slotList[ i ] );
2012-09-19 15:15:01 +00:00
}
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->getTrackProfile(slotIndex))
2012-09-19 15:15:01 +00:00
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 ][ 0 ],
insn.mArg.mDelayTime.mVariance[ 0 ][ 1 ]
2012-09-19 15:15:01 +00:00
);
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->getTrackProfile(insn.mSlotIndex);
2012-09-19 15:15:01 +00:00
// 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 )
2016-10-14 22:57:20 +00:00
{
2012-09-19 15:15:01 +00:00
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;
}
2016-10-14 22:57:20 +00:00
}
2012-09-19 15:15:01 +00:00
}
//-----------------------------------------------------------------------------
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 );
}