sfxController.cpp
Engine/source/sfx/sfxController.cpp
Public Functions
DefineEngineMethod(SFXController , getCurrentSlot , S32 , () , "Get the index of the playlist slot currently processed by the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">controller.\n</a>" "@return The slot index currently being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">played.\n</a>" "@see <a href="/coding/class/classsfxplaylist/">SFXPlayList</a>" )
DefineEngineMethod(SFXController , setCurrentSlot , void , (S32 index) , "Set the index of the playlist slot <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> play by the controller. This can be used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> seek in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">playlist.\n</a>" "@param index Index of the playlist slot." )
Detailed Description
Public Functions
ConsoleDocClass(SFXController )
DefineEngineMethod(SFXController , getCurrentSlot , S32 , () , "Get the index of the playlist slot currently processed by the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">controller.\n</a>" "@return The slot index currently being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">played.\n</a>" "@see <a href="/coding/class/classsfxplaylist/">SFXPlayList</a>" )
DefineEngineMethod(SFXController , setCurrentSlot , void , (S32 index) , "Set the index of the playlist slot <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> play by the controller. This can be used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> seek in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">playlist.\n</a>" "@param index Index of the playlist slot." )
IMPLEMENT_CONOBJECT(SFXController )
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/sfxController.h" 25#include "sfx/sfxPlayList.h" 26#include "sfx/sfxProfile.h" 27#include "sfx/sfxSource.h" 28#include "sfx/sfxSystem.h" 29#include "sfx/sfxState.h" 30#include "sfx/sfxDescription.h" 31#include "console/engineAPI.h" 32#include "math/mRandom.h" 33 34 35 36IMPLEMENT_CONOBJECT( SFXController ); 37 38 39ConsoleDocClass( SFXController, 40 "@brief A sound source that drives multi-source playback.\n\n" 41 42 "This class acts as an interpreter for SFXPlayLists. It goes through the slots of the playlist it is " 43 "attached to and performs the actions described by each of the slots in turn.\n" 44 45 "As SFXControllers are created implicitly by the SFX system when instantiating a source for a play list it is " 46 "in most cases not necessary to directly deal with the class.\n" 47 48 "The following example demonstrates how a controller would commonly be created.\n" 49 50 "@tsexample\n" 51 "// Create a play list from two SFXProfiles.\n" 52 "%playList = new SFXPlayList()\n" 53 "{\n" 54 " // Use a looped description so the list playback will loop.\n" 55 " description = AudioMusicLoop2D;\n" 56 "\n" 57 " track[ 0 ] = Profile1;\n" 58 " track[ 1 ] = Profile2;\n" 59 "};\n" 60 "\n" 61 "// Play the list. This will implicitly create a controller.\n" 62 "sfxPlayOnce( %playList );\n" 63 "@endtsexample\n\n" 64 65 "@note Play lists are updated at regular intervals by the sound system. This processing determines the granularity at " 66 "which playlist action timing takes place.\n" 67 "@note This class cannot be instantiated directly. Use sfxPlayOnce() or sfxCreateSource() with the playlist " 68 "you want to play to create an instance of this class.\n" 69 70 "@see SFXPlayList\n" 71 "@ingroup SFX\n" 72); 73 74 75//----------------------------------------------------------------------------- 76 77SFXController::SFXController( SFXPlayList* playList ) 78 : Parent( playList ), 79 mTrace( playList->trace() ), 80 mLoopCounter(0) 81{ 82 VECTOR_SET_ASSOCIATION( mInsns ); 83 VECTOR_SET_ASSOCIATION( mSources ); 84 VECTOR_SET_ASSOCIATION( mParameters ); 85 86 _compileList( playList ); 87} 88 89//----------------------------------------------------------------------------- 90 91SFXController::~SFXController() 92{ 93} 94 95//----------------------------------------------------------------------------- 96 97void SFXController::initPersistFields() 98{ 99 addGroup( "Debug" ); 100 addField( "trace", TypeBool, Offset( mTrace, SFXController ), 101 "If true, the controller logs its operation to the console.\n" 102 "This is a non-networked field that will work locally only." ); 103 endGroup( "Debug" ); 104 105 Parent::initPersistFields(); 106} 107 108//----------------------------------------------------------------------------- 109 110SFXController* SFXController::_create( SFXPlayList* playList ) 111{ 112 AssertFatal( playList != NULL, "SFXController::_create() - got a NULL playlist!" ); 113 114 SFXController* controller = new SFXController( playList ); 115 controller->registerObject(); 116 117 return controller; 118} 119 120//----------------------------------------------------------------------------- 121 122void SFXController::_compileList( SFXPlayList* playList ) 123{ 124 mInsns.clear(); 125 const bool isLooping = playList->getDescription()->mIsLooping; 126 127 // Create a slot list that determines the order the slots will be 128 // played in. 129 130 U32 slotList[ SFXPlayList::NUM_SLOTS ]; 131 bool isOrderedRandom = false; 132 switch( playList->getRandomMode() ) 133 { 134 case SFXPlayList::RANDOM_OrderedRandom: 135 isOrderedRandom = true; 136 /* fallthrough */ 137 138 case SFXPlayList::RANDOM_NotRandom: 139 // Generate sequence 1-NUM_SLOTS. 140 for( U32 i = 0; i < SFXPlayList::NUM_SLOTS; ++ i ) 141 slotList[ i ] = i; 142 143 if( isOrderedRandom ) 144 { 145 // Randomly exchange slots in the list. 146 for( U32 i = 0; i < SFXPlayList::NUM_SLOTS; ++ i ) 147 T3D::swap( slotList[ gRandGen.randI( 0, SFXPlayList::NUM_SLOTS - 1 ) ], slotList[ i ] ); 148 } 149 break; 150 151 case SFXPlayList::RANDOM_StrictRandom: 152 // Randomly generate NUM_SLOTS slot indices. 153 for( U32 i = 0; i < SFXPlayList::NUM_SLOTS; ++ i ) 154 slotList[ i ] = gRandGen.randI( 0, SFXPlayList::NUM_SLOTS - 1 ); 155 break; 156 } 157 158 // Generate the instruction list. 159 160 U32 slotCount = 0; 161 for( U32 i = 0; i < SFXPlayList::NUM_SLOTS; ++ i ) 162 { 163 const U32 slotIndex = slotList[ i ]; 164 const U32 slotStartIp = mInsns.size(); 165 166 SFXState* state = playList->getSlots().mState[ slotIndex ]; 167 168 // If there's no track in this slot, ignore it. 169 170 if( !playList->getSlots().mTrack[ slotIndex ] ) 171 continue; 172 173 // If this is a looped slot and the list is not set to loop 174 // indefinitly on single slots, start a loop. 175 176 S32 loopStartIp = -1; 177 if( playList->getSlots().mRepeatCount[ slotIndex ] > 0 178 && ( !isLooping || playList->getLoopMode() != SFXPlayList::LOOP_Single ) ) 179 { 180 Insn insn( OP_LoopBegin, slotIndex, state ); 181 insn.mArg.mLoopCount = playList->getSlots().mRepeatCount[ slotIndex ]; 182 mInsns.push_back( insn ); 183 184 loopStartIp = mInsns.size(); 185 } 186 187 // Add in-delay, if any. 188 189 if( playList->getSlots().mDelayTimeIn.mValue[ slotIndex ] > 0.0f ) 190 { 191 Insn insn( OP_Delay, slotIndex, state ); 192 insn.mArg.mDelayTime.mValue[ 0 ] = playList->getSlots().mDelayTimeIn.mValue[ slotIndex ]; 193 insn.mArg.mDelayTime.mVariance[ 0 ][ 0 ] = playList->getSlots().mDelayTimeIn.mVariance[ slotIndex ][ 0 ]; 194 insn.mArg.mDelayTime.mVariance[ 0 ][ 1 ] = playList->getSlots().mDelayTimeIn.mVariance[ slotIndex ][ 1 ]; 195 196 mInsns.push_back( insn ); 197 } 198 199 // Add the in-transition. 200 201 const SFXPlayList::ETransitionMode transitionIn = playList->getSlots().mTransitionIn[ slotIndex ]; 202 if( transitionIn != SFXPlayList::TRANSITION_None ) 203 { 204 Insn insn( slotIndex, state ); 205 _genTransition( insn, transitionIn ); 206 mInsns.push_back( insn ); 207 } 208 209 // Add the play instruction. 210 211 { 212 Insn insn( OP_Play, slotIndex, state ); 213 mInsns.push_back( insn ); 214 } 215 216 // Add out-delay, if any. 217 218 if( playList->getSlots().mDelayTimeOut.mValue[ slotIndex ] > 0.0f ) 219 { 220 Insn insn( OP_Delay, slotIndex, state ); 221 insn.mArg.mDelayTime.mValue[ 0 ] = playList->getSlots().mDelayTimeOut.mValue[ slotIndex ]; 222 insn.mArg.mDelayTime.mVariance[ 0 ][ 0 ] = playList->getSlots().mDelayTimeOut.mVariance[ slotIndex ][ 0 ]; 223 insn.mArg.mDelayTime.mVariance[ 0 ][ 1 ] = playList->getSlots().mDelayTimeOut.mVariance[ slotIndex ][ 1 ]; 224 225 mInsns.push_back( insn ); 226 } 227 228 // Add the out-transition. 229 230 const SFXPlayList::ETransitionMode transitionOut = playList->getSlots().mTransitionOut[ slotIndex ]; 231 if( transitionOut != SFXPlayList::TRANSITION_None ) 232 { 233 Insn insn( slotIndex, state ); 234 _genTransition( insn, transitionOut ); 235 mInsns.push_back( insn ); 236 } 237 238 // Loop, if necessary. 239 240 if( loopStartIp != -1 ) 241 { 242 Insn insn( OP_LoopEnd, slotIndex, state ); 243 insn.mArg.mJumpIp = loopStartIp; 244 mInsns.push_back( insn ); 245 } 246 247 // If the list is on repeat-single, unconditionally 248 // loop over the instruction sequence of each slot. 249 250 if( isLooping && playList->getLoopMode() == SFXPlayList::LOOP_Single ) 251 { 252 Insn insn( OP_Jump, slotIndex, state ); 253 insn.mArg.mJumpIp = slotStartIp; 254 mInsns.push_back( insn ); 255 } 256 257 // If we have reached the limit of slots to play, 258 // stop generating. 259 260 slotCount ++; 261 if( playList->getNumSlotsToPlay() == slotCount ) 262 break; 263 } 264 265 // Set up for execution. 266 267 mIp = 0; 268 if( !mInsns.empty() ) 269 _initInsn(); 270} 271 272//----------------------------------------------------------------------------- 273 274void SFXController::_genTransition( Insn& insn, SFXPlayList::ETransitionMode transition ) 275{ 276 switch( transition ) 277 { 278 case SFXPlayList::TRANSITION_Wait: 279 insn.mOpcode = OP_WaitSingle; 280 break; 281 282 case SFXPlayList::TRANSITION_WaitAll: 283 insn.mOpcode = OP_WaitAll; 284 break; 285 286 case SFXPlayList::TRANSITION_Stop: 287 insn.mOpcode = OP_StopSingle; 288 break; 289 290 case SFXPlayList::TRANSITION_StopAll: 291 insn.mOpcode = OP_StopAll; 292 break; 293 294 default: 295 AssertFatal( false, "SFXController::_addTransition() - should not reach here" ); 296 } 297} 298 299//----------------------------------------------------------------------------- 300 301void SFXController::_initInsn() 302{ 303 Insn& insn = mInsns[ mIp ]; 304 switch( insn.mOpcode ) 305 { 306 case OP_Delay: 307 mDelayEndTime = Platform::getVirtualMilliseconds() 308 + U32( insn.mArg.mDelayTime.getValue( 0, 0.0f ) * 1000.f ); 309 break; 310 311 default: 312 break; 313 } 314 315 if( mTrace ) 316 _printInsn(insn ); 317} 318 319//----------------------------------------------------------------------------- 320 321void SFXController::_printInsn( Insn& insn) 322{ 323 switch( insn.mOpcode ) 324 { 325 case OP_Delay: 326 Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: Delay %f:%f:%f", 327 mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "", 328 insn.mArg.mDelayTime.mValue[ 0 ], 329 insn.mArg.mDelayTime.mVariance[ 0 ][ 0 ], 330 insn.mArg.mDelayTime.mVariance[ 0 ][ 1 ] 331 ); 332 break; 333 334 case OP_WaitSingle: 335 Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: WaitSingle", 336 mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "" ); 337 break; 338 339 case OP_WaitAll: 340 Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: WaitAll", 341 mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "" ); 342 break; 343 344 case OP_StopSingle: 345 Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: StopSingle", 346 mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "" ); 347 break; 348 349 case OP_StopAll: 350 Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: StopAll", 351 mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "" ); 352 break; 353 354 case OP_Play: 355 Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: Play", 356 mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "" ); 357 break; 358 359 case OP_Jump: 360 Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: Jump %i", 361 mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "", insn.mArg.mJumpIp ); 362 break; 363 364 case OP_LoopBegin: 365 Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: LoopBegin %i", 366 mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "", insn.mArg.mLoopCount ); 367 break; 368 369 case OP_LoopEnd: 370 Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: LoopEnd", 371 mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "" ); 372 break; 373 } 374} 375 376//----------------------------------------------------------------------------- 377 378bool SFXController::_execInsn() 379{ 380 bool endUpdate = false; 381 Insn& insn = mInsns[ mIp ]; 382 383 switch( insn.mOpcode ) 384 { 385 case OP_Delay: 386 { 387 if( Platform::getVirtualMilliseconds() < mDelayEndTime ) 388 endUpdate = true; 389 else 390 _advanceIp(); 391 break; 392 } 393 394 case OP_Play: 395 { 396 SFXPlayList* playList = getPlayList(); 397 SFXTrack* track = playList->getSlots().mTrack[ insn.mSlotIndex ]; 398 399 // Handle existing sources playing on this slot and find 400 // whether we need to start a new source. 401 // 402 // Go through the list top-down so we can push sources we re-use 403 // to the top of the list. A side-effect of doing it this way is 404 // that the order of the sources that are preserved gets reversed, 405 // i.e. older sources will end up higher up the stack. 406 407 bool startNew = true; 408 SFXPlayList::EReplayMode replayMode = playList->getSlots().mReplayMode[ insn.mSlotIndex ]; 409 if( replayMode != SFXPlayList::REPLAY_IgnorePlaying ) 410 for( S32 i = mSources.size() - 1; i >= 0; -- i ) 411 { 412 Source& source = mSources[ i ]; 413 if( source.mSlotIndex != insn.mSlotIndex ) 414 continue; 415 416 // If the play-once source has expired, remove the entry 417 // and go on. 418 419 if( source.mPtr == NULL ) 420 { 421 mSources.erase( i ); 422 ++ i; 423 continue; 424 } 425 426 // Decide what to do with the still-playing source. 427 428 if( replayMode == SFXPlayList::REPLAY_RestartPlaying 429 || replayMode == SFXPlayList::REPLAY_KeepPlaying ) 430 { 431 // Restart the source or keep playing it. 432 // Either way, move it to the top of the stack. 433 434 startNew = false; 435 436 Source src = mSources[ i ]; 437 mSources.erase( i ); 438 439 //RDTODO: add a method to restart cleanly in the presence of fades; this here 440 // just cuts the current playback short 441 442 if( replayMode == SFXPlayList::REPLAY_RestartPlaying ) 443 src.mPtr->stop( 0.f ); 444 445 src.mPtr->play(); 446 447 // Move the source to the top of the stack. 448 449 mSources.increment(); 450 mSources.last() = src; 451 } 452 else if( replayMode == SFXPlayList::REPLAY_StartNew ) 453 { 454 // Kill off existing source. 455 456 source.mPtr->stop(); 457 mSources.erase( i ); 458 ++ i; 459 } 460 else if( replayMode == SFXPlayList::REPLAY_SkipIfPlaying ) 461 { 462 startNew = false; 463 break; 464 } 465 } 466 467 if( startNew ) 468 { 469 // Create a new source. 470 471 SFXSource* source = SFX->createSource( 472 track, 473 &getTransform(), 474 &getVelocity() 475 ); 476 477 // Append the source to the list of playing sources. 478 479 if( source ) 480 { 481 mSources.increment(); 482 Source& src = mSources.last(); 483 484 // Determine fade times. 485 486 F32 fadeInTime = -1; 487 F32 fadeOutTime = -1; 488 489 if( playList->getSlots().mFadeTimeIn.mValue[ insn.mSlotIndex ] != -1 ) 490 fadeInTime = playList->getSlots().mFadeTimeIn.getValue( insn.mSlotIndex, 0.f ); 491 if( playList->getSlots().mFadeTimeOut.mValue[ insn.mSlotIndex ] != -1 ) 492 fadeOutTime = playList->getSlots().mFadeTimeOut.getValue( insn.mSlotIndex, 0.f ); 493 494 if( fadeInTime != -1 || fadeOutTime != -1 ) 495 source->setFadeTimes( fadeInTime, fadeOutTime ); 496 497 // Set up source record. 498 499 src.mPtr = source; 500 src.mSlotIndex = insn.mSlotIndex; 501 src.mVolumeScale = playList->getSlots().mVolumeScale.getValue( insn.mSlotIndex, 0.f, 1.f ); 502 src.mPitchScale = playList->getSlots().mPitchScale.getValue( insn.mSlotIndex ); 503 src.mFadeInTime = fadeInTime; 504 src.mFadeOutTime = fadeOutTime; 505 506 SFXPlayList::EStateMode stateMode = playList->getSlots().mStateMode[ insn.mSlotIndex ]; 507 if( stateMode != SFXPlayList::STATE_IgnoreInactive ) 508 src.mState = insn.mState; 509 510 // Set the source's volume and pitch. Either is scaled by our own 511 // assigned value and the scale factors from the playlist slot. 512 513 source->setModulativeVolume( mAttenuatedVolume * src.mVolumeScale ); 514 source->setModulativePitch( mEffectivePitch * src.mPitchScale ); 515 516 // Set min and max range. 517 518 const SFXPlayList::VariantFloat& minDistance = playList->getSlots().mMinDistance; 519 const SFXPlayList::VariantFloat& maxDistance = playList->getSlots().mMaxDistance; 520 521 if( minDistance.mValue[ insn.mSlotIndex ] >= 0.f 522 && maxDistance.mValue[ insn.mSlotIndex ] >= 0.f ) 523 source->setMinMaxDistance( 524 minDistance.getValue( insn.mSlotIndex, 0.f ), 525 maxDistance.getValue( insn.mSlotIndex, 0.f ) 526 ); 527 528 // Start the source. 529 530 source->play(); 531 SFX->deleteWhenStopped( source ); 532 } 533 } 534 535 _advanceIp(); 536 break; 537 } 538 539 case OP_WaitSingle: 540 { 541 if( !mSources.empty() && mSources.last().mPtr != NULL && mSources.last().mPtr->isPlaying() ) 542 endUpdate = true; 543 else 544 { 545 if( !mSources.empty() ) 546 mSources.decrement(); 547 _advanceIp(); 548 } 549 break; 550 } 551 552 case OP_WaitAll: 553 { 554 for( U32 i = 0; i < mSources.size(); ++ i ) 555 if( mSources[ i ].mPtr != NULL && mSources[ i ].mPtr->isStopped() ) 556 { 557 mSources.erase( i ); 558 -- i; 559 } 560 561 if( !mSources.empty() ) 562 endUpdate = true; 563 else 564 _advanceIp(); 565 566 break; 567 } 568 569 case OP_StopSingle: 570 { 571 if( !mSources.empty() ) 572 { 573 if( mSources.last().mPtr != NULL ) 574 mSources.last().mPtr->stop(); 575 mSources.decrement(); 576 } 577 578 _advanceIp(); 579 break; 580 } 581 582 case OP_StopAll: 583 { 584 while( !mSources.empty() ) 585 { 586 if( mSources.last().mPtr != NULL ) 587 mSources.last().mPtr->stop(); 588 mSources.decrement(); 589 } 590 591 _advanceIp(); 592 break; 593 } 594 595 case OP_Jump: 596 { 597 mIp = insn.mArg.mJumpIp; 598 _initInsn(); 599 break; 600 } 601 602 case OP_LoopBegin: 603 { 604 mLoopCounter = insn.mArg.mLoopCount; 605 _advanceIp(); 606 break; 607 } 608 609 case OP_LoopEnd: 610 { 611 -- mLoopCounter; 612 if( mLoopCounter > 0 ) 613 { 614 mIp = insn.mArg.mJumpIp; 615 _initInsn(); 616 } 617 else 618 _advanceIp(); 619 620 break; 621 } 622 } 623 624 return endUpdate; 625} 626 627//----------------------------------------------------------------------------- 628 629void SFXController::_advanceIp() 630{ 631 mIp ++; 632 if( mIp < mInsns.size() ) 633 _initInsn(); 634} 635 636//----------------------------------------------------------------------------- 637 638void SFXController::_onParameterEvent( SFXParameter* parameter, SFXParameterEvent event ) 639{ 640 Parent::_onParameterEvent( parameter, event ); 641 642 // Implement cursor semantic. 643 644 if( event == SFXParameterEvent_ValueChanged 645 && parameter->getChannel() == SFXChannelCursor ) 646 { 647 U32 slot = U32( mFloor( parameter->getValue() ) ); 648 if( slot != getCurrentSlot() ) 649 setCurrentSlot( slot ); 650 } 651} 652 653//----------------------------------------------------------------------------- 654 655SFXPlayList* SFXController::getPlayList() const 656{ 657 return static_cast< SFXPlayList* >( mTrack.getPointer() ); 658} 659 660//----------------------------------------------------------------------------- 661 662U32 SFXController::getCurrentSlot() const 663{ 664 if( mIp >= mInsns.size() ) 665 return 0; 666 else 667 return mInsns[ mIp ].mSlotIndex; 668} 669 670//----------------------------------------------------------------------------- 671 672void SFXController::setCurrentSlot( U32 index ) 673{ 674 mIp = 0; 675 while( mIp < mInsns.size() && mInsns[ mIp ].mSlotIndex != index ) 676 ++ mIp; 677 678 if( mIp >= mInsns.size() ) 679 mIp = 0; 680 681 if( !mInsns.empty() ) 682 _initInsn(); 683} 684 685//----------------------------------------------------------------------------- 686 687void SFXController::_play() 688{ 689 Parent::_play(); 690 691 // Unpause sources, if we are paused. 692 693 if( mStatus == SFXStatusPaused ) 694 { 695 for( U32 i = 0; i < mSources.size(); ++ i ) 696 if( mSources[ i ].mPtr != NULL ) 697 mSources[ i ].mPtr->play( 0.f ); // We want our fade values to take effect. 698 else 699 { 700 mSources.erase( i ); 701 -- i; 702 } 703 } 704} 705 706//----------------------------------------------------------------------------- 707 708void SFXController::_pause() 709{ 710 Parent::_pause(); 711 712 // Pause all playing sources. 713 714 for( U32 i = 0; i < mSources.size(); ++ i ) 715 if( mSources[ i ].mPtr != NULL ) 716 mSources[ i ].mPtr->pause( 0.f ); // We want our fade values to take effect. 717 else 718 { 719 mSources.erase( i ); 720 -- i; 721 } 722} 723 724//----------------------------------------------------------------------------- 725 726void SFXController::_stop() 727{ 728 Parent::_stop(); 729 730 // Stop all playing sources. 731 732 while( !mSources.empty() ) 733 { 734 if( mSources.last().mPtr != NULL ) 735 mSources.last().mPtr->stop( 0.f ); // We want our fade values to take effect. 736 mSources.decrement(); 737 } 738 739 // Reset execution. 740 741 mIp = 0; 742 if( !mInsns.empty() ) 743 _initInsn(); 744} 745 746//----------------------------------------------------------------------------- 747 748void SFXController::_updateVolume( const MatrixF& listener ) 749{ 750 F32 oldAttenuatedVolume = mAttenuatedVolume; 751 Parent::_updateVolume( listener ); 752 753 // If the attenuated volume has changed, pass it off 754 // as the modulative volume to all our sources. 755 756 if( oldAttenuatedVolume != mAttenuatedVolume ) 757 for( U32 i = 0; i < mSources.size(); ++ i ) 758 { 759 Source& source = mSources[ i ]; 760 if( source.mPtr != NULL ) 761 source.mPtr->setModulativeVolume( mAttenuatedVolume * source.mVolumeScale ); 762 else 763 { 764 mSources.erase( i ); 765 -- i; 766 } 767 } 768} 769 770//----------------------------------------------------------------------------- 771 772void SFXController::_updatePitch() 773{ 774 F32 oldEffectivePitch = mEffectivePitch; 775 Parent::_updatePitch(); 776 777 if( mEffectivePitch != oldEffectivePitch ) 778 for( U32 i = 0; i < mSources.size(); ++ i ) 779 { 780 Source& source = mSources[ i ]; 781 if( source.mPtr != NULL ) 782 source.mPtr->setModulativePitch( mEffectivePitch * source.mPitchScale ); 783 else 784 { 785 mSources.erase( i ); 786 -- i; 787 } 788 } 789} 790 791//----------------------------------------------------------------------------- 792 793void SFXController::_updatePriority() 794{ 795 F32 oldEffectivePriority = mEffectivePriority; 796 Parent::_updatePriority(); 797 798 if( mEffectivePriority != oldEffectivePriority ) 799 for( U32 i = 0; i < mSources.size(); ++ i ) 800 { 801 Source& source = mSources[ i ]; 802 if( source.mPtr != NULL ) 803 source.mPtr->setModulativePriority( mEffectivePriority ); 804 else 805 { 806 mSources.erase( i ); 807 -- i; 808 } 809 } 810} 811 812//----------------------------------------------------------------------------- 813 814void SFXController::_update() 815{ 816 Parent::_update(); 817 818 SFXPlayList* playList = getPlayList(); 819 820 // Check all sources against the current state setup and 821 // take appropriate actions. 822 823 for( U32 i = 0; i < mSources.size(); ++ i ) 824 { 825 Source& source = mSources[ i ]; 826 827 // If the source has already stopped playing, 828 // remove it. 829 830 if( !source.mPtr ) 831 { 832 mSources.erase( i ); 833 -- i; 834 continue; 835 } 836 837 if( !source.mState ) 838 continue; 839 840 SFXPlayList::EStateMode stateMode = playList->getSlots().mStateMode[ mSources[ i ].mSlotIndex ]; 841 if( !source.mState->isActive() ) 842 { 843 if( source.mPtr->isPlaying() ) 844 { 845 // The source is playing in an incompatible state. 846 847 if( stateMode == SFXPlayList::STATE_PauseInactive ) 848 source.mPtr->pause(); 849 else if( stateMode == SFXPlayList::STATE_StopInactive ) 850 { 851 source.mPtr->stop(); 852 mSources.erase( i ); 853 854 -- i; 855 } 856 } 857 } 858 else 859 { 860 // Unpause a source that had its state become active again. 861 862 if( source.mPtr->isPaused() && stateMode == SFXPlayList::STATE_PauseInactive ) 863 source.mPtr->play(); 864 } 865 } 866 867 // Update interpreter. 868 869 bool endUpdate = false; 870 while( !endUpdate ) 871 { 872 if( mIp >= mInsns.size() ) 873 { 874 // End of list reached. 875 876 if( playList->getDescription()->mIsLooping && 877 playList->getLoopMode() == SFXPlayList::LOOP_All ) 878 { 879 // The play list is set to repeat-all. 880 // If it is also random, generate a new instruction list 881 // so we get a new playing order. Otherwise just reset. 882 883 if( playList->getRandomMode() != SFXPlayList::RANDOM_NotRandom ) 884 _compileList( playList ); 885 else 886 { 887 mIp = 0; 888 if( !mInsns.empty() ) 889 _initInsn(); 890 } 891 892 // Reset play timer. 893 894 mPlayTimer.reset(); 895 mPlayTimer.start(); 896 } 897 else 898 { 899 // Moved to stopped state. 900 901 mPlayTimer.stop(); 902 _setStatus( SFXStatusStopped ); 903 mIp = 0; 904 } 905 906 // End this update. This limits playlist to at most one complete 907 // cycle per update. 908 909 break; 910 } 911 912 Insn& insn = mInsns[ mIp ]; 913 914 if( insn.mState && !insn.mState->isActive() ) 915 { 916 // The state associated with the slot is inactive. Skip 917 // the instructions. 918 _advanceIp(); 919 } 920 else 921 endUpdate = _execInsn(); 922 } 923} 924 925//============================================================================= 926// Console Methods. 927//============================================================================= 928// MARK: ---- Console Methods ---- 929 930//----------------------------------------------------------------------------- 931 932DefineEngineMethod( SFXController, getCurrentSlot, S32, (),, 933 "Get the index of the playlist slot currently processed by the controller.\n" 934 "@return The slot index currently being played.\n" 935 "@see SFXPlayList" ) 936{ 937 return object->getCurrentSlot(); 938} 939 940//----------------------------------------------------------------------------- 941 942DefineEngineMethod( SFXController, setCurrentSlot, void, ( S32 index ),, 943 "Set the index of the playlist slot to play by the controller. This can be used to seek in the playlist.\n" 944 "@param index Index of the playlist slot." ) 945{ 946 object->setCurrentSlot( index ); 947} 948