sfxController.cpp 29 KB

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