Torque3D Documentation / _generateds / sfxController.cpp

sfxController.cpp

Engine/source/sfx/sfxController.cpp

More...

Public Functions

DefineEngineMethod(SFXController , getCurrentSlot , S32 , () , "Get the index of the playlist slot currently processed by the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">controller.\n</a>" "@return The slot index currently being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">played.\n</a>" "@see <a href="/coding/class/classsfxplaylist/">SFXPlayList</a>" )
DefineEngineMethod(SFXController , setCurrentSlot , void , (S32 index) , "Set the index of the playlist slot <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> play by the controller. This can be used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> seek in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">playlist.\n</a>" "@param index Index of the playlist slot." )

Detailed Description

Public Functions

ConsoleDocClass(SFXController )

DefineEngineMethod(SFXController , getCurrentSlot , S32 , () , "Get the index of the playlist slot currently processed by the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">controller.\n</a>" "@return The slot index currently being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">played.\n</a>" "@see <a href="/coding/class/classsfxplaylist/">SFXPlayList</a>" )

DefineEngineMethod(SFXController , setCurrentSlot , void , (S32 index) , "Set the index of the playlist slot <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> play by the controller. This can be used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> seek in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">playlist.\n</a>" "@param index Index of the playlist slot." )

IMPLEMENT_CONOBJECT(SFXController )

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