sfxController.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "sfx/sfxController.h"
  23. #include "sfx/sfxPlayList.h"
  24. #include "sfx/sfxProfile.h"
  25. #include "sfx/sfxSource.h"
  26. #include "sfx/sfxSystem.h"
  27. #include "sfx/sfxState.h"
  28. #include "sfx/sfxDescription.h"
  29. #include "console/engineAPI.h"
  30. #include "math/mRandom.h"
  31. IMPLEMENT_CONOBJECT( SFXController );
  32. ConsoleDocClass( SFXController,
  33. "@brief A sound source that drives multi-source playback.\n\n"
  34. "This class acts as an interpreter for SFXPlayLists. It goes through the slots of the playlist it is "
  35. "attached to and performs the actions described by each of the slots in turn.\n"
  36. "As SFXControllers are created implicitly by the SFX system when instantiating a source for a play list it is "
  37. "in most cases not necessary to directly deal with the class.\n"
  38. "The following example demonstrates how a controller would commonly be created.\n"
  39. "@tsexample\n"
  40. "// Create a play list from two SFXProfiles.\n"
  41. "%playList = new SFXPlayList()\n"
  42. "{\n"
  43. " // Use a looped description so the list playback will loop.\n"
  44. " description = AudioMusicLoop2D;\n"
  45. "\n"
  46. " track[ 0 ] = Profile1;\n"
  47. " track[ 1 ] = Profile2;\n"
  48. "};\n"
  49. "\n"
  50. "// Play the list. This will implicitly create a controller.\n"
  51. "sfxPlayOnce( %playList );\n"
  52. "@endtsexample\n\n"
  53. "@note Play lists are updated at regular intervals by the sound system. This processing determines the granularity at "
  54. "which playlist action timing takes place.\n"
  55. "@note This class cannot be instantiated directly. Use sfxPlayOnce() or sfxCreateSource() with the playlist "
  56. "you want to play to create an instance of this class.\n"
  57. "@see SFXPlayList\n"
  58. "@ingroup SFX\n"
  59. );
  60. //-----------------------------------------------------------------------------
  61. SFXController::SFXController( SFXPlayList* playList )
  62. : Parent( playList ),
  63. mTrace( playList->trace() ),
  64. mLoopCounter(0)
  65. {
  66. VECTOR_SET_ASSOCIATION( mInsns );
  67. VECTOR_SET_ASSOCIATION( mSources );
  68. VECTOR_SET_ASSOCIATION( mParameters );
  69. _compileList( playList );
  70. }
  71. //-----------------------------------------------------------------------------
  72. SFXController::~SFXController()
  73. {
  74. }
  75. //-----------------------------------------------------------------------------
  76. void SFXController::initPersistFields()
  77. {
  78. addGroup( "Debug" );
  79. addField( "trace", TypeBool, Offset( mTrace, SFXController ),
  80. "If true, the controller logs its operation to the console.\n"
  81. "This is a non-networked field that will work locally only." );
  82. endGroup( "Debug" );
  83. Parent::initPersistFields();
  84. }
  85. //-----------------------------------------------------------------------------
  86. SFXController* SFXController::_create( SFXPlayList* playList )
  87. {
  88. AssertFatal( playList != NULL, "SFXController::_create() - got a NULL playlist!" );
  89. SFXController* controller = new SFXController( playList );
  90. controller->registerObject();
  91. return controller;
  92. }
  93. //-----------------------------------------------------------------------------
  94. void SFXController::_compileList( SFXPlayList* playList )
  95. {
  96. mInsns.clear();
  97. const bool isLooping = playList->getDescription()->mIsLooping;
  98. // Create a slot list that determines the order the slots will be
  99. // played in.
  100. U32 slotList[ SFXPlayList::NUM_SLOTS ];
  101. bool isOrderedRandom = false;
  102. switch( playList->getRandomMode() )
  103. {
  104. case SFXPlayList::RANDOM_OrderedRandom:
  105. isOrderedRandom = true;
  106. /* fallthrough */
  107. case SFXPlayList::RANDOM_NotRandom:
  108. // Generate sequence 1-NUM_SLOTS.
  109. for( U32 i = 0; i < SFXPlayList::NUM_SLOTS; ++ i )
  110. slotList[ i ] = i;
  111. if( isOrderedRandom )
  112. {
  113. // Randomly exchange slots in the list.
  114. for( U32 i = 0; i < SFXPlayList::NUM_SLOTS; ++ i )
  115. T3D::swap( slotList[ gRandGen.randI( 0, SFXPlayList::NUM_SLOTS - 1 ) ], slotList[ i ] );
  116. }
  117. break;
  118. case SFXPlayList::RANDOM_StrictRandom:
  119. // Randomly generate NUM_SLOTS slot indices.
  120. for( U32 i = 0; i < SFXPlayList::NUM_SLOTS; ++ i )
  121. slotList[ i ] = gRandGen.randI( 0, SFXPlayList::NUM_SLOTS - 1 );
  122. break;
  123. }
  124. // Generate the instruction list.
  125. U32 slotCount = 0;
  126. for( U32 i = 0; i < SFXPlayList::NUM_SLOTS; ++ i )
  127. {
  128. const U32 slotIndex = slotList[ i ];
  129. const U32 slotStartIp = mInsns.size();
  130. SFXState* state = playList->getSlots().mState[ slotIndex ];
  131. // If there's no track in this slot, ignore it.
  132. if( !playList->getSlots().mTrack[ slotIndex ] )
  133. continue;
  134. // If this is a looped slot and the list is not set to loop
  135. // indefinitly on single slots, start a loop.
  136. S32 loopStartIp = -1;
  137. if( playList->getSlots().mRepeatCount[ slotIndex ] > 0
  138. && ( !isLooping || playList->getLoopMode() != SFXPlayList::LOOP_Single ) )
  139. {
  140. Insn insn( OP_LoopBegin, slotIndex, state );
  141. insn.mArg.mLoopCount = playList->getSlots().mRepeatCount[ slotIndex ];
  142. mInsns.push_back( insn );
  143. loopStartIp = mInsns.size();
  144. }
  145. // Add in-delay, if any.
  146. if( playList->getSlots().mDelayTimeIn.mValue[ slotIndex ] > 0.0f )
  147. {
  148. Insn insn( OP_Delay, slotIndex, state );
  149. insn.mArg.mDelayTime.mValue[ 0 ] = playList->getSlots().mDelayTimeIn.mValue[ slotIndex ];
  150. insn.mArg.mDelayTime.mVariance[ 0 ][ 0 ] = playList->getSlots().mDelayTimeIn.mVariance[ slotIndex ][ 0 ];
  151. insn.mArg.mDelayTime.mVariance[ 0 ][ 1 ] = playList->getSlots().mDelayTimeIn.mVariance[ slotIndex ][ 1 ];
  152. mInsns.push_back( insn );
  153. }
  154. // Add the in-transition.
  155. const SFXPlayList::ETransitionMode transitionIn = playList->getSlots().mTransitionIn[ slotIndex ];
  156. if( transitionIn != SFXPlayList::TRANSITION_None )
  157. {
  158. Insn insn( slotIndex, state );
  159. _genTransition( insn, transitionIn );
  160. mInsns.push_back( insn );
  161. }
  162. // Add the play instruction.
  163. {
  164. Insn insn( OP_Play, slotIndex, state );
  165. mInsns.push_back( insn );
  166. }
  167. // Add out-delay, if any.
  168. if( playList->getSlots().mDelayTimeOut.mValue[ slotIndex ] > 0.0f )
  169. {
  170. Insn insn( OP_Delay, slotIndex, state );
  171. insn.mArg.mDelayTime.mValue[ 0 ] = playList->getSlots().mDelayTimeOut.mValue[ slotIndex ];
  172. insn.mArg.mDelayTime.mVariance[ 0 ][ 0 ] = playList->getSlots().mDelayTimeOut.mVariance[ slotIndex ][ 0 ];
  173. insn.mArg.mDelayTime.mVariance[ 0 ][ 1 ] = playList->getSlots().mDelayTimeOut.mVariance[ slotIndex ][ 1 ];
  174. mInsns.push_back( insn );
  175. }
  176. // Add the out-transition.
  177. const SFXPlayList::ETransitionMode transitionOut = playList->getSlots().mTransitionOut[ slotIndex ];
  178. if( transitionOut != SFXPlayList::TRANSITION_None )
  179. {
  180. Insn insn( slotIndex, state );
  181. _genTransition( insn, transitionOut );
  182. mInsns.push_back( insn );
  183. }
  184. // Loop, if necessary.
  185. if( loopStartIp != -1 )
  186. {
  187. Insn insn( OP_LoopEnd, slotIndex, state );
  188. insn.mArg.mJumpIp = loopStartIp;
  189. mInsns.push_back( insn );
  190. }
  191. // If the list is on repeat-single, unconditionally
  192. // loop over the instruction sequence of each slot.
  193. if( isLooping && playList->getLoopMode() == SFXPlayList::LOOP_Single )
  194. {
  195. Insn insn( OP_Jump, slotIndex, state );
  196. insn.mArg.mJumpIp = slotStartIp;
  197. mInsns.push_back( insn );
  198. }
  199. // If we have reached the limit of slots to play,
  200. // stop generating.
  201. slotCount ++;
  202. if( playList->getNumSlotsToPlay() == slotCount )
  203. break;
  204. }
  205. // Set up for execution.
  206. mIp = 0;
  207. if( !mInsns.empty() )
  208. _initInsn();
  209. }
  210. //-----------------------------------------------------------------------------
  211. void SFXController::_genTransition( Insn& insn, SFXPlayList::ETransitionMode transition )
  212. {
  213. switch( transition )
  214. {
  215. case SFXPlayList::TRANSITION_Wait:
  216. insn.mOpcode = OP_WaitSingle;
  217. break;
  218. case SFXPlayList::TRANSITION_WaitAll:
  219. insn.mOpcode = OP_WaitAll;
  220. break;
  221. case SFXPlayList::TRANSITION_Stop:
  222. insn.mOpcode = OP_StopSingle;
  223. break;
  224. case SFXPlayList::TRANSITION_StopAll:
  225. insn.mOpcode = OP_StopAll;
  226. break;
  227. default:
  228. AssertFatal( false, "SFXController::_addTransition() - should not reach here" );
  229. }
  230. }
  231. //-----------------------------------------------------------------------------
  232. void SFXController::_initInsn()
  233. {
  234. Insn& insn = mInsns[ mIp ];
  235. switch( insn.mOpcode )
  236. {
  237. case OP_Delay:
  238. mDelayEndTime = Platform::getVirtualMilliseconds()
  239. + U32( insn.mArg.mDelayTime.getValue( 0, 0.0f ) * 1000.f );
  240. break;
  241. default:
  242. break;
  243. }
  244. if( mTrace )
  245. _printInsn(insn );
  246. }
  247. //-----------------------------------------------------------------------------
  248. void SFXController::_printInsn( Insn& insn)
  249. {
  250. switch( insn.mOpcode )
  251. {
  252. case OP_Delay:
  253. Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: Delay %f:%f:%f",
  254. mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "",
  255. insn.mArg.mDelayTime.mValue[ 0 ],
  256. insn.mArg.mDelayTime.mVariance[ 0 ][ 0 ],
  257. insn.mArg.mDelayTime.mVariance[ 0 ][ 1 ]
  258. );
  259. break;
  260. case OP_WaitSingle:
  261. Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: WaitSingle",
  262. mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "" );
  263. break;
  264. case OP_WaitAll:
  265. Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: WaitAll",
  266. mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "" );
  267. break;
  268. case OP_StopSingle:
  269. Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: StopSingle",
  270. mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "" );
  271. break;
  272. case OP_StopAll:
  273. Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: StopAll",
  274. mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "" );
  275. break;
  276. case OP_Play:
  277. Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: Play",
  278. mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "" );
  279. break;
  280. case OP_Jump:
  281. Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: Jump %i",
  282. mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "", insn.mArg.mJumpIp );
  283. break;
  284. case OP_LoopBegin:
  285. Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: LoopBegin %i",
  286. mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "", insn.mArg.mLoopCount );
  287. break;
  288. case OP_LoopEnd:
  289. Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: LoopEnd",
  290. mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "" );
  291. break;
  292. }
  293. }
  294. //-----------------------------------------------------------------------------
  295. bool SFXController::_execInsn()
  296. {
  297. bool endUpdate = false;
  298. Insn& insn = mInsns[ mIp ];
  299. switch( insn.mOpcode )
  300. {
  301. case OP_Delay:
  302. {
  303. if( Platform::getVirtualMilliseconds() < mDelayEndTime )
  304. endUpdate = true;
  305. else
  306. _advanceIp();
  307. break;
  308. }
  309. case OP_Play:
  310. {
  311. SFXPlayList* playList = getPlayList();
  312. SFXTrack* track = playList->getSlots().mTrack[ insn.mSlotIndex ];
  313. // Handle existing sources playing on this slot and find
  314. // whether we need to start a new source.
  315. //
  316. // Go through the list top-down so we can push sources we re-use
  317. // to the top of the list. A side-effect of doing it this way is
  318. // that the order of the sources that are preserved gets reversed,
  319. // i.e. older sources will end up higher up the stack.
  320. bool startNew = true;
  321. SFXPlayList::EReplayMode replayMode = playList->getSlots().mReplayMode[ insn.mSlotIndex ];
  322. if( replayMode != SFXPlayList::REPLAY_IgnorePlaying )
  323. for( S32 i = mSources.size() - 1; i >= 0; -- i )
  324. {
  325. Source& source = mSources[ i ];
  326. if( source.mSlotIndex != insn.mSlotIndex )
  327. continue;
  328. // If the play-once source has expired, remove the entry
  329. // and go on.
  330. if( source.mPtr == NULL )
  331. {
  332. mSources.erase( i );
  333. ++ i;
  334. continue;
  335. }
  336. // Decide what to do with the still-playing source.
  337. if( replayMode == SFXPlayList::REPLAY_RestartPlaying
  338. || replayMode == SFXPlayList::REPLAY_KeepPlaying )
  339. {
  340. // Restart the source or keep playing it.
  341. // Either way, move it to the top of the stack.
  342. startNew = false;
  343. Source src = mSources[ i ];
  344. mSources.erase( i );
  345. //RDTODO: add a method to restart cleanly in the presence of fades; this here
  346. // just cuts the current playback short
  347. if( replayMode == SFXPlayList::REPLAY_RestartPlaying )
  348. src.mPtr->stop( 0.f );
  349. src.mPtr->play();
  350. // Move the source to the top of the stack.
  351. mSources.increment();
  352. mSources.last() = src;
  353. }
  354. else if( replayMode == SFXPlayList::REPLAY_StartNew )
  355. {
  356. // Kill off existing source.
  357. source.mPtr->stop();
  358. mSources.erase( i );
  359. ++ i;
  360. }
  361. else if( replayMode == SFXPlayList::REPLAY_SkipIfPlaying )
  362. {
  363. startNew = false;
  364. break;
  365. }
  366. }
  367. if( startNew )
  368. {
  369. // Create a new source.
  370. SFXSource* source = SFX->createSource(
  371. track,
  372. &getTransform(),
  373. &getVelocity()
  374. );
  375. // Append the source to the list of playing sources.
  376. if( source )
  377. {
  378. mSources.increment();
  379. Source& src = mSources.last();
  380. // Determine fade times.
  381. F32 fadeInTime = -1;
  382. F32 fadeOutTime = -1;
  383. if( playList->getSlots().mFadeTimeIn.mValue[ insn.mSlotIndex ] != -1 )
  384. fadeInTime = playList->getSlots().mFadeTimeIn.getValue( insn.mSlotIndex, 0.f );
  385. if( playList->getSlots().mFadeTimeOut.mValue[ insn.mSlotIndex ] != -1 )
  386. fadeOutTime = playList->getSlots().mFadeTimeOut.getValue( insn.mSlotIndex, 0.f );
  387. if( fadeInTime != -1 || fadeOutTime != -1 )
  388. source->setFadeTimes( fadeInTime, fadeOutTime );
  389. // Set up source record.
  390. src.mPtr = source;
  391. src.mSlotIndex = insn.mSlotIndex;
  392. src.mVolumeScale = playList->getSlots().mVolumeScale.getValue( insn.mSlotIndex, 0.f, 1.f );
  393. src.mPitchScale = playList->getSlots().mPitchScale.getValue( insn.mSlotIndex );
  394. src.mFadeInTime = fadeInTime;
  395. src.mFadeOutTime = fadeOutTime;
  396. SFXPlayList::EStateMode stateMode = playList->getSlots().mStateMode[ insn.mSlotIndex ];
  397. if( stateMode != SFXPlayList::STATE_IgnoreInactive )
  398. src.mState = insn.mState;
  399. // Set the source's volume and pitch. Either is scaled by our own
  400. // assigned value and the scale factors from the playlist slot.
  401. source->setModulativeVolume( mAttenuatedVolume * src.mVolumeScale );
  402. source->setModulativePitch( mEffectivePitch * src.mPitchScale );
  403. // Set min and max range.
  404. const SFXPlayList::VariantFloat& minDistance = playList->getSlots().mMinDistance;
  405. const SFXPlayList::VariantFloat& maxDistance = playList->getSlots().mMaxDistance;
  406. if( minDistance.mValue[ insn.mSlotIndex ] >= 0.f
  407. && maxDistance.mValue[ insn.mSlotIndex ] >= 0.f )
  408. source->setMinMaxDistance(
  409. minDistance.getValue( insn.mSlotIndex, 0.f ),
  410. maxDistance.getValue( insn.mSlotIndex, 0.f )
  411. );
  412. // Start the source.
  413. source->play();
  414. SFX->deleteWhenStopped( source );
  415. }
  416. }
  417. _advanceIp();
  418. break;
  419. }
  420. case OP_WaitSingle:
  421. {
  422. if( !mSources.empty() && mSources.last().mPtr != NULL && mSources.last().mPtr->isPlaying() )
  423. endUpdate = true;
  424. else
  425. {
  426. if( !mSources.empty() )
  427. mSources.decrement();
  428. _advanceIp();
  429. }
  430. break;
  431. }
  432. case OP_WaitAll:
  433. {
  434. for( U32 i = 0; i < mSources.size(); ++ i )
  435. if( mSources[ i ].mPtr != NULL && mSources[ i ].mPtr->isStopped() )
  436. {
  437. mSources.erase( i );
  438. -- i;
  439. }
  440. if( !mSources.empty() )
  441. endUpdate = true;
  442. else
  443. _advanceIp();
  444. break;
  445. }
  446. case OP_StopSingle:
  447. {
  448. if( !mSources.empty() )
  449. {
  450. if( mSources.last().mPtr != NULL )
  451. mSources.last().mPtr->stop();
  452. mSources.decrement();
  453. }
  454. _advanceIp();
  455. break;
  456. }
  457. case OP_StopAll:
  458. {
  459. while( !mSources.empty() )
  460. {
  461. if( mSources.last().mPtr != NULL )
  462. mSources.last().mPtr->stop();
  463. mSources.decrement();
  464. }
  465. _advanceIp();
  466. break;
  467. }
  468. case OP_Jump:
  469. {
  470. mIp = insn.mArg.mJumpIp;
  471. _initInsn();
  472. break;
  473. }
  474. case OP_LoopBegin:
  475. {
  476. mLoopCounter = insn.mArg.mLoopCount;
  477. _advanceIp();
  478. break;
  479. }
  480. case OP_LoopEnd:
  481. {
  482. -- mLoopCounter;
  483. if( mLoopCounter > 0 )
  484. {
  485. mIp = insn.mArg.mJumpIp;
  486. _initInsn();
  487. }
  488. else
  489. _advanceIp();
  490. break;
  491. }
  492. }
  493. return endUpdate;
  494. }
  495. //-----------------------------------------------------------------------------
  496. void SFXController::_advanceIp()
  497. {
  498. mIp ++;
  499. if( mIp < mInsns.size() )
  500. _initInsn();
  501. }
  502. //-----------------------------------------------------------------------------
  503. void SFXController::_onParameterEvent( SFXParameter* parameter, SFXParameterEvent event )
  504. {
  505. Parent::_onParameterEvent( parameter, event );
  506. // Implement cursor semantic.
  507. if( event == SFXParameterEvent_ValueChanged
  508. && parameter->getChannel() == SFXChannelCursor )
  509. {
  510. U32 slot = U32( mFloor( parameter->getValue() ) );
  511. if( slot != getCurrentSlot() )
  512. setCurrentSlot( slot );
  513. }
  514. }
  515. //-----------------------------------------------------------------------------
  516. SFXPlayList* SFXController::getPlayList() const
  517. {
  518. return static_cast< SFXPlayList* >( mTrack.getPointer() );
  519. }
  520. //-----------------------------------------------------------------------------
  521. U32 SFXController::getCurrentSlot() const
  522. {
  523. if( mIp >= mInsns.size() )
  524. return 0;
  525. else
  526. return mInsns[ mIp ].mSlotIndex;
  527. }
  528. //-----------------------------------------------------------------------------
  529. void SFXController::setCurrentSlot( U32 index )
  530. {
  531. mIp = 0;
  532. while( mIp < mInsns.size() && mInsns[ mIp ].mSlotIndex != index )
  533. ++ mIp;
  534. if( mIp >= mInsns.size() )
  535. mIp = 0;
  536. if( !mInsns.empty() )
  537. _initInsn();
  538. }
  539. //-----------------------------------------------------------------------------
  540. void SFXController::_play()
  541. {
  542. Parent::_play();
  543. // Unpause sources, if we are paused.
  544. if( mStatus == SFXStatusPaused )
  545. {
  546. for( U32 i = 0; i < mSources.size(); ++ i )
  547. if( mSources[ i ].mPtr != NULL )
  548. mSources[ i ].mPtr->play( 0.f ); // We want our fade values to take effect.
  549. else
  550. {
  551. mSources.erase( i );
  552. -- i;
  553. }
  554. }
  555. }
  556. //-----------------------------------------------------------------------------
  557. void SFXController::_pause()
  558. {
  559. Parent::_pause();
  560. // Pause all playing sources.
  561. for( U32 i = 0; i < mSources.size(); ++ i )
  562. if( mSources[ i ].mPtr != NULL )
  563. mSources[ i ].mPtr->pause( 0.f ); // We want our fade values to take effect.
  564. else
  565. {
  566. mSources.erase( i );
  567. -- i;
  568. }
  569. }
  570. //-----------------------------------------------------------------------------
  571. void SFXController::_stop()
  572. {
  573. Parent::_stop();
  574. // Stop all playing sources.
  575. while( !mSources.empty() )
  576. {
  577. if( mSources.last().mPtr != NULL )
  578. mSources.last().mPtr->stop( 0.f ); // We want our fade values to take effect.
  579. mSources.decrement();
  580. }
  581. // Reset execution.
  582. mIp = 0;
  583. if( !mInsns.empty() )
  584. _initInsn();
  585. }
  586. //-----------------------------------------------------------------------------
  587. void SFXController::_updateVolume( const MatrixF& listener )
  588. {
  589. F32 oldAttenuatedVolume = mAttenuatedVolume;
  590. Parent::_updateVolume( listener );
  591. // If the attenuated volume has changed, pass it off
  592. // as the modulative volume to all our sources.
  593. if( oldAttenuatedVolume != mAttenuatedVolume )
  594. for( U32 i = 0; i < mSources.size(); ++ i )
  595. {
  596. Source& source = mSources[ i ];
  597. if( source.mPtr != NULL )
  598. source.mPtr->setModulativeVolume( mAttenuatedVolume * source.mVolumeScale );
  599. else
  600. {
  601. mSources.erase( i );
  602. -- i;
  603. }
  604. }
  605. }
  606. //-----------------------------------------------------------------------------
  607. void SFXController::_updatePitch()
  608. {
  609. F32 oldEffectivePitch = mEffectivePitch;
  610. Parent::_updatePitch();
  611. if( mEffectivePitch != oldEffectivePitch )
  612. for( U32 i = 0; i < mSources.size(); ++ i )
  613. {
  614. Source& source = mSources[ i ];
  615. if( source.mPtr != NULL )
  616. source.mPtr->setModulativePitch( mEffectivePitch * source.mPitchScale );
  617. else
  618. {
  619. mSources.erase( i );
  620. -- i;
  621. }
  622. }
  623. }
  624. //-----------------------------------------------------------------------------
  625. void SFXController::_updatePriority()
  626. {
  627. F32 oldEffectivePriority = mEffectivePriority;
  628. Parent::_updatePriority();
  629. if( mEffectivePriority != oldEffectivePriority )
  630. for( U32 i = 0; i < mSources.size(); ++ i )
  631. {
  632. Source& source = mSources[ i ];
  633. if( source.mPtr != NULL )
  634. source.mPtr->setModulativePriority( mEffectivePriority );
  635. else
  636. {
  637. mSources.erase( i );
  638. -- i;
  639. }
  640. }
  641. }
  642. //-----------------------------------------------------------------------------
  643. void SFXController::_update()
  644. {
  645. Parent::_update();
  646. SFXPlayList* playList = getPlayList();
  647. // Check all sources against the current state setup and
  648. // take appropriate actions.
  649. for( U32 i = 0; i < mSources.size(); ++ i )
  650. {
  651. Source& source = mSources[ i ];
  652. // If the source has already stopped playing,
  653. // remove it.
  654. if( !source.mPtr )
  655. {
  656. mSources.erase( i );
  657. -- i;
  658. continue;
  659. }
  660. if( !source.mState )
  661. continue;
  662. SFXPlayList::EStateMode stateMode = playList->getSlots().mStateMode[ mSources[ i ].mSlotIndex ];
  663. if( !source.mState->isActive() )
  664. {
  665. if( source.mPtr->isPlaying() )
  666. {
  667. // The source is playing in an incompatible state.
  668. if( stateMode == SFXPlayList::STATE_PauseInactive )
  669. source.mPtr->pause();
  670. else if( stateMode == SFXPlayList::STATE_StopInactive )
  671. {
  672. source.mPtr->stop();
  673. mSources.erase( i );
  674. -- i;
  675. }
  676. }
  677. }
  678. else
  679. {
  680. // Unpause a source that had its state become active again.
  681. if( source.mPtr->isPaused() && stateMode == SFXPlayList::STATE_PauseInactive )
  682. source.mPtr->play();
  683. }
  684. }
  685. // Update interpreter.
  686. bool endUpdate = false;
  687. while( !endUpdate )
  688. {
  689. if( mIp >= mInsns.size() )
  690. {
  691. // End of list reached.
  692. if( playList->getDescription()->mIsLooping &&
  693. playList->getLoopMode() == SFXPlayList::LOOP_All )
  694. {
  695. // The play list is set to repeat-all.
  696. // If it is also random, generate a new instruction list
  697. // so we get a new playing order. Otherwise just reset.
  698. if( playList->getRandomMode() != SFXPlayList::RANDOM_NotRandom )
  699. _compileList( playList );
  700. else
  701. {
  702. mIp = 0;
  703. if( !mInsns.empty() )
  704. _initInsn();
  705. }
  706. // Reset play timer.
  707. mPlayTimer.reset();
  708. mPlayTimer.start();
  709. }
  710. else
  711. {
  712. // Moved to stopped state.
  713. mPlayTimer.stop();
  714. _setStatus( SFXStatusStopped );
  715. mIp = 0;
  716. }
  717. // End this update. This limits playlist to at most one complete
  718. // cycle per update.
  719. break;
  720. }
  721. Insn& insn = mInsns[ mIp ];
  722. if( insn.mState && !insn.mState->isActive() )
  723. {
  724. // The state associated with the slot is inactive. Skip
  725. // the instructions.
  726. _advanceIp();
  727. }
  728. else
  729. endUpdate = _execInsn();
  730. }
  731. }
  732. //=============================================================================
  733. // Console Methods.
  734. //=============================================================================
  735. // MARK: ---- Console Methods ----
  736. //-----------------------------------------------------------------------------
  737. DefineEngineMethod( SFXController, getCurrentSlot, S32, (),,
  738. "Get the index of the playlist slot currently processed by the controller.\n"
  739. "@return The slot index currently being played.\n"
  740. "@see SFXPlayList" )
  741. {
  742. return object->getCurrentSlot();
  743. }
  744. //-----------------------------------------------------------------------------
  745. DefineEngineMethod( SFXController, setCurrentSlot, void, ( S32 index ),,
  746. "Set the index of the playlist slot to play by the controller. This can be used to seek in the playlist.\n"
  747. "@param index Index of the playlist slot." )
  748. {
  749. object->setCurrentSlot( index );
  750. }