sfxController.cpp 29 KB

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