sfxController.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956
  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->getSlots().mTrack[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. if (playList == NULL)
  314. {
  315. endUpdate = true;
  316. break;
  317. }
  318. SFXTrack* track = playList->getSlots().mTrack[insn.mSlotIndex];
  319. // Handle existing sources playing on this slot and find
  320. // whether we need to start a new source.
  321. //
  322. // Go through the list top-down so we can push sources we re-use
  323. // to the top of the list. A side-effect of doing it this way is
  324. // that the order of the sources that are preserved gets reversed,
  325. // i.e. older sources will end up higher up the stack.
  326. bool startNew = true;
  327. SFXPlayList::EReplayMode replayMode = playList->getSlots().mReplayMode[ insn.mSlotIndex ];
  328. if( replayMode != SFXPlayList::REPLAY_IgnorePlaying )
  329. for( S32 i = mSources.size() - 1; i >= 0; -- i )
  330. {
  331. Source& source = mSources[ i ];
  332. if( source.mSlotIndex != insn.mSlotIndex )
  333. continue;
  334. // If the play-once source has expired, remove the entry
  335. // and go on.
  336. if( source.mPtr == NULL )
  337. {
  338. mSources.erase( i );
  339. ++ i;
  340. continue;
  341. }
  342. // Decide what to do with the still-playing source.
  343. if( replayMode == SFXPlayList::REPLAY_RestartPlaying
  344. || replayMode == SFXPlayList::REPLAY_KeepPlaying )
  345. {
  346. // Restart the source or keep playing it.
  347. // Either way, move it to the top of the stack.
  348. startNew = false;
  349. Source src = mSources[ i ];
  350. mSources.erase( i );
  351. //RDTODO: add a method to restart cleanly in the presence of fades; this here
  352. // just cuts the current playback short
  353. if( replayMode == SFXPlayList::REPLAY_RestartPlaying )
  354. src.mPtr->stop( 0.f );
  355. src.mPtr->play();
  356. // Move the source to the top of the stack.
  357. mSources.increment();
  358. mSources.last() = src;
  359. }
  360. else if( replayMode == SFXPlayList::REPLAY_StartNew )
  361. {
  362. // Kill off existing source.
  363. source.mPtr->stop();
  364. mSources.erase( i );
  365. ++ i;
  366. }
  367. else if( replayMode == SFXPlayList::REPLAY_SkipIfPlaying )
  368. {
  369. startNew = false;
  370. break;
  371. }
  372. }
  373. if( startNew )
  374. {
  375. // Create a new source.
  376. SFXSource* source = SFX->createSource(
  377. track,
  378. &getTransform(),
  379. &getVelocity()
  380. );
  381. // Append the source to the list of playing sources.
  382. if( source )
  383. {
  384. mSources.increment();
  385. Source& src = mSources.last();
  386. // Determine fade times.
  387. F32 fadeInTime = -1;
  388. F32 fadeOutTime = -1;
  389. if( playList->getSlots().mFadeTimeIn.mValue[ insn.mSlotIndex ] != -1 )
  390. fadeInTime = playList->getSlots().mFadeTimeIn.getValue( insn.mSlotIndex, 0.f );
  391. if( playList->getSlots().mFadeTimeOut.mValue[ insn.mSlotIndex ] != -1 )
  392. fadeOutTime = playList->getSlots().mFadeTimeOut.getValue( insn.mSlotIndex, 0.f );
  393. if( fadeInTime != -1 || fadeOutTime != -1 )
  394. source->setFadeTimes( fadeInTime, fadeOutTime );
  395. // Set up source record.
  396. src.mPtr = source;
  397. src.mSlotIndex = insn.mSlotIndex;
  398. src.mVolumeScale = playList->getSlots().mVolumeScale.getValue( insn.mSlotIndex, 0.f, 1.f );
  399. src.mPitchScale = playList->getSlots().mPitchScale.getValue( insn.mSlotIndex );
  400. src.mFadeInTime = fadeInTime;
  401. src.mFadeOutTime = fadeOutTime;
  402. SFXPlayList::EStateMode stateMode = playList->getSlots().mStateMode[ insn.mSlotIndex ];
  403. if( stateMode != SFXPlayList::STATE_IgnoreInactive )
  404. src.mState = insn.mState;
  405. // Set the source's volume and pitch. Either is scaled by our own
  406. // assigned value and the scale factors from the playlist slot.
  407. source->setModulativeVolume( mAttenuatedVolume * src.mVolumeScale );
  408. source->setModulativePitch( mEffectivePitch * src.mPitchScale );
  409. // Set min and max range.
  410. const SFXPlayList::VariantFloat& minDistance = playList->getSlots().mMinDistance;
  411. const SFXPlayList::VariantFloat& maxDistance = playList->getSlots().mMaxDistance;
  412. if( minDistance.mValue[ insn.mSlotIndex ] >= 0.f
  413. && maxDistance.mValue[ insn.mSlotIndex ] >= 0.f )
  414. source->setMinMaxDistance(
  415. minDistance.getValue( insn.mSlotIndex, 0.f ),
  416. maxDistance.getValue( insn.mSlotIndex, 0.f )
  417. );
  418. // Start the source.
  419. source->play();
  420. SFX->deleteWhenStopped( source );
  421. }
  422. }
  423. _advanceIp();
  424. break;
  425. }
  426. case OP_WaitSingle:
  427. {
  428. if( !mSources.empty() && mSources.last().mPtr != NULL && mSources.last().mPtr->isPlaying() )
  429. endUpdate = true;
  430. else
  431. {
  432. if( !mSources.empty() )
  433. mSources.decrement();
  434. _advanceIp();
  435. }
  436. break;
  437. }
  438. case OP_WaitAll:
  439. {
  440. for( U32 i = 0; i < mSources.size(); ++ i )
  441. if( mSources[ i ].mPtr != NULL && mSources[ i ].mPtr->isStopped() )
  442. {
  443. mSources.erase( i );
  444. -- i;
  445. }
  446. if( !mSources.empty() )
  447. endUpdate = true;
  448. else
  449. _advanceIp();
  450. break;
  451. }
  452. case OP_StopSingle:
  453. {
  454. if( !mSources.empty() )
  455. {
  456. if( mSources.last().mPtr != NULL )
  457. mSources.last().mPtr->stop();
  458. mSources.decrement();
  459. }
  460. _advanceIp();
  461. break;
  462. }
  463. case OP_StopAll:
  464. {
  465. while( !mSources.empty() )
  466. {
  467. if( mSources.last().mPtr != NULL )
  468. mSources.last().mPtr->stop();
  469. mSources.decrement();
  470. }
  471. _advanceIp();
  472. break;
  473. }
  474. case OP_Jump:
  475. {
  476. mIp = insn.mArg.mJumpIp;
  477. _initInsn();
  478. break;
  479. }
  480. case OP_LoopBegin:
  481. {
  482. mLoopCounter = insn.mArg.mLoopCount;
  483. _advanceIp();
  484. break;
  485. }
  486. case OP_LoopEnd:
  487. {
  488. -- mLoopCounter;
  489. if( mLoopCounter > 0 )
  490. {
  491. mIp = insn.mArg.mJumpIp;
  492. _initInsn();
  493. }
  494. else
  495. _advanceIp();
  496. break;
  497. }
  498. }
  499. return endUpdate;
  500. }
  501. //-----------------------------------------------------------------------------
  502. void SFXController::_advanceIp()
  503. {
  504. mIp ++;
  505. if( mIp < mInsns.size() )
  506. _initInsn();
  507. }
  508. //-----------------------------------------------------------------------------
  509. void SFXController::_onParameterEvent( SFXParameter* parameter, SFXParameterEvent event )
  510. {
  511. Parent::_onParameterEvent( parameter, event );
  512. // Implement cursor semantic.
  513. if( event == SFXParameterEvent_ValueChanged
  514. && parameter->getChannel() == SFXChannelCursor )
  515. {
  516. U32 slot = U32( mFloor( parameter->getValue() ) );
  517. if( slot != getCurrentSlot() )
  518. setCurrentSlot( slot );
  519. }
  520. }
  521. //-----------------------------------------------------------------------------
  522. SFXPlayList* SFXController::getPlayList() const
  523. {
  524. return static_cast< SFXPlayList* >( mTrack.getPointer() );
  525. }
  526. //-----------------------------------------------------------------------------
  527. U32 SFXController::getCurrentSlot() const
  528. {
  529. if( mIp >= mInsns.size() )
  530. return 0;
  531. else
  532. return mInsns[ mIp ].mSlotIndex;
  533. }
  534. //-----------------------------------------------------------------------------
  535. void SFXController::setCurrentSlot( U32 index )
  536. {
  537. mIp = 0;
  538. while( mIp < mInsns.size() && mInsns[ mIp ].mSlotIndex != index )
  539. ++ mIp;
  540. if( mIp >= mInsns.size() )
  541. mIp = 0;
  542. if( !mInsns.empty() )
  543. _initInsn();
  544. }
  545. //-----------------------------------------------------------------------------
  546. void SFXController::_play()
  547. {
  548. Parent::_play();
  549. // Unpause sources, if we are paused.
  550. if( mStatus == SFXStatusPaused )
  551. {
  552. for( U32 i = 0; i < mSources.size(); ++ i )
  553. if( mSources[ i ].mPtr != NULL )
  554. mSources[ i ].mPtr->play( 0.f ); // We want our fade values to take effect.
  555. else
  556. {
  557. mSources.erase( i );
  558. -- i;
  559. }
  560. }
  561. }
  562. //-----------------------------------------------------------------------------
  563. void SFXController::_pause()
  564. {
  565. Parent::_pause();
  566. // Pause all playing sources.
  567. for( U32 i = 0; i < mSources.size(); ++ i )
  568. if( mSources[ i ].mPtr != NULL )
  569. mSources[ i ].mPtr->pause( 0.f ); // We want our fade values to take effect.
  570. else
  571. {
  572. mSources.erase( i );
  573. -- i;
  574. }
  575. }
  576. //-----------------------------------------------------------------------------
  577. void SFXController::_stop()
  578. {
  579. Parent::_stop();
  580. // Stop all playing sources.
  581. while( !mSources.empty() )
  582. {
  583. if( mSources.last().mPtr != NULL )
  584. mSources.last().mPtr->stop( 0.f ); // We want our fade values to take effect.
  585. mSources.decrement();
  586. }
  587. // Reset execution.
  588. mIp = 0;
  589. if( !mInsns.empty() )
  590. _initInsn();
  591. }
  592. //-----------------------------------------------------------------------------
  593. void SFXController::_updateVolume( const MatrixF& listener )
  594. {
  595. F32 oldAttenuatedVolume = mAttenuatedVolume;
  596. Parent::_updateVolume( listener );
  597. // If the attenuated volume has changed, pass it off
  598. // as the modulative volume to all our sources.
  599. if( oldAttenuatedVolume != mAttenuatedVolume )
  600. for( U32 i = 0; i < mSources.size(); ++ i )
  601. {
  602. Source& source = mSources[ i ];
  603. if( source.mPtr != NULL )
  604. source.mPtr->setModulativeVolume( mAttenuatedVolume * source.mVolumeScale );
  605. else
  606. {
  607. mSources.erase( i );
  608. -- i;
  609. }
  610. }
  611. }
  612. //-----------------------------------------------------------------------------
  613. void SFXController::_updatePitch()
  614. {
  615. F32 oldEffectivePitch = mEffectivePitch;
  616. Parent::_updatePitch();
  617. if( mEffectivePitch != oldEffectivePitch )
  618. for( U32 i = 0; i < mSources.size(); ++ i )
  619. {
  620. Source& source = mSources[ i ];
  621. if( source.mPtr != NULL )
  622. source.mPtr->setModulativePitch( mEffectivePitch * source.mPitchScale );
  623. else
  624. {
  625. mSources.erase( i );
  626. -- i;
  627. }
  628. }
  629. }
  630. //-----------------------------------------------------------------------------
  631. void SFXController::_updatePriority()
  632. {
  633. F32 oldEffectivePriority = mEffectivePriority;
  634. Parent::_updatePriority();
  635. if( mEffectivePriority != oldEffectivePriority )
  636. for( U32 i = 0; i < mSources.size(); ++ i )
  637. {
  638. Source& source = mSources[ i ];
  639. if( source.mPtr != NULL )
  640. source.mPtr->setModulativePriority( mEffectivePriority );
  641. else
  642. {
  643. mSources.erase( i );
  644. -- i;
  645. }
  646. }
  647. }
  648. //-----------------------------------------------------------------------------
  649. void SFXController::_update()
  650. {
  651. Parent::_update();
  652. SFXPlayList* playList = getPlayList();
  653. if (!playList)
  654. Parent::stop();
  655. // Check all sources against the current state setup and
  656. // take appropriate actions.
  657. for( U32 i = 0; i < mSources.size(); ++ i )
  658. {
  659. Source& source = mSources[ i ];
  660. // If the source has already stopped playing,
  661. // remove it.
  662. if( !source.mPtr )
  663. {
  664. mSources.erase( i );
  665. -- i;
  666. continue;
  667. }
  668. if( !source.mState )
  669. continue;
  670. SFXPlayList::EStateMode stateMode = playList->getSlots().mStateMode[ mSources[ i ].mSlotIndex ];
  671. if( !source.mState->isActive() )
  672. {
  673. if( source.mPtr->isPlaying() )
  674. {
  675. // The source is playing in an incompatible state.
  676. if( stateMode == SFXPlayList::STATE_PauseInactive )
  677. source.mPtr->pause();
  678. else if( stateMode == SFXPlayList::STATE_StopInactive )
  679. {
  680. source.mPtr->stop();
  681. mSources.erase( i );
  682. -- i;
  683. }
  684. }
  685. }
  686. else
  687. {
  688. // Unpause a source that had its state become active again.
  689. if( source.mPtr->isPaused() && stateMode == SFXPlayList::STATE_PauseInactive )
  690. source.mPtr->play();
  691. }
  692. }
  693. // Update interpreter.
  694. bool endUpdate = false;
  695. while( !endUpdate )
  696. {
  697. if( mIp >= mInsns.size() )
  698. {
  699. // End of list reached.
  700. if( playList->getDescription()->mIsLooping &&
  701. playList->getLoopMode() == SFXPlayList::LOOP_All )
  702. {
  703. // The play list is set to repeat-all.
  704. // If it is also random, generate a new instruction list
  705. // so we get a new playing order. Otherwise just reset.
  706. if( playList->getRandomMode() != SFXPlayList::RANDOM_NotRandom )
  707. _compileList( playList );
  708. else
  709. {
  710. mIp = 0;
  711. if( !mInsns.empty() )
  712. _initInsn();
  713. }
  714. // Reset play timer.
  715. mPlayTimer.reset();
  716. mPlayTimer.start();
  717. }
  718. else
  719. {
  720. // Moved to stopped state.
  721. mPlayTimer.stop();
  722. _setStatus( SFXStatusStopped );
  723. mIp = 0;
  724. }
  725. // End this update. This limits playlist to at most one complete
  726. // cycle per update.
  727. break;
  728. }
  729. Insn& insn = mInsns[ mIp ];
  730. if( insn.mState && !insn.mState->isActive() )
  731. {
  732. // The state associated with the slot is inactive. Skip
  733. // the instructions.
  734. _advanceIp();
  735. }
  736. else
  737. endUpdate = _execInsn();
  738. }
  739. }
  740. //=============================================================================
  741. // Console Methods.
  742. //=============================================================================
  743. // MARK: ---- Console Methods ----
  744. //-----------------------------------------------------------------------------
  745. DefineEngineMethod( SFXController, getCurrentSlot, S32, (),,
  746. "Get the index of the playlist slot currently processed by the controller.\n"
  747. "@return The slot index currently being played.\n"
  748. "@see SFXPlayList" )
  749. {
  750. return object->getCurrentSlot();
  751. }
  752. //-----------------------------------------------------------------------------
  753. DefineEngineMethod( SFXController, setCurrentSlot, void, ( S32 index ),,
  754. "Set the index of the playlist slot to play by the controller. This can be used to seek in the playlist.\n"
  755. "@param index Index of the playlist slot." )
  756. {
  757. object->setCurrentSlot( index );
  758. }