Browse Source

Merge remote-tracking branch 'upstream/development' into AppleToolchainTest

marauder2k7 1 year ago
parent
commit
2b2ce0229c

+ 252 - 47
Engine/source/T3D/assets/SoundAsset.cpp

@@ -44,6 +44,10 @@
 #include "sfx/sfxSource.h"
 #endif
 
+#ifndef _SFXPROFILE_H_
+#include "sfx/sfxProfile.h"
+#endif // !_SFXPROFILE_H_
+
 // Debug Profiling.
 #include "platform/profiler.h"
 #include "sfx/sfxTypes.h"
@@ -119,8 +123,28 @@ const String SoundAsset::mErrCodeStrings[] =
 SoundAsset::SoundAsset()
    : AssetBase()
 {
-   mSoundFile = StringTable->EmptyString();
-   mSoundPath = StringTable->EmptyString();
+   dMemset(mPlaylist.mSlots.mReplayMode, 0, sizeof(mPlaylist.mSlots.mReplayMode));
+   dMemset(mPlaylist.mSlots.mTransitionIn, 0, sizeof(mPlaylist.mSlots.mTransitionIn));
+   dMemset(mPlaylist.mSlots.mRepeatCount, 0, sizeof(mPlaylist.mSlots.mRepeatCount));
+   dMemset(mPlaylist.mSlots.mState, 0, sizeof(mPlaylist.mSlots.mState));
+   dMemset(mPlaylist.mSlots.mTrack, 0, sizeof(mPlaylist.mSlots.mTrack));
+   dMemset(mPlaylist.mSlots.mStateMode, 0, sizeof(mPlaylist.mSlots.mStateMode));
+
+   for (U32 i = 0; i < SFXPlayList::NUM_SLOTS; i++)
+   {
+      mSoundFile[i] = StringTable->EmptyString();
+      mSoundPath[i] = StringTable->EmptyString();
+
+      mPlaylist.mSlots.mTransitionOut[i] = SFXPlayList::TRANSITION_Wait;
+      mPlaylist.mSlots.mVolumeScale.mValue[i] = 1.f;
+      mPlaylist.mSlots.mPitchScale.mValue[i] = 1.f;
+      mPlaylist.mSlots.mFadeTimeIn.mValue[i] = -1.f;  // Don't touch by default.
+      mPlaylist.mSlots.mFadeTimeOut.mValue[i] = -1.f;  // Don't touch by default.
+      mPlaylist.mSlots.mMinDistance.mValue[i] = -1.f;  // Don't touch by default.
+      mPlaylist.mSlots.mMaxDistance.mValue[i] = -1.f;  // Don't touch by default.
+
+   }
+
    mSubtitleString = StringTable->EmptyString();
 
    mLoadedState = AssetErrCode::NotLoaded;
@@ -143,6 +167,14 @@ SoundAsset::SoundAsset()
    mProfileDesc.mPriority = 1.0f;
    mProfileDesc.mSourceGroup = NULL;
 
+   mIsPlaylist = false;
+
+   mPlaylist.mNumSlotsToPlay = SFXPlayList::SFXPlaylistSettings::NUM_SLOTS;
+   mPlaylist.mRandomMode = SFXPlayList::RANDOM_NotRandom;
+   mPlaylist.mTrace = false;
+   mPlaylist.mLoopMode = SFXPlayList::LOOP_All;
+   mPlaylist.mActiveSlots = 12;
+
 }
 
 //-----------------------------------------------------------------------------
@@ -158,9 +190,79 @@ void SoundAsset::initPersistFields()
    docsURL;
    // Call parent.
    Parent::initPersistFields();
-
-   addProtectedField("soundFile", TypeAssetLooseFilePath, Offset(mSoundFile, SoundAsset),
-      &setSoundFile, &getSoundFile, "Path to the sound file.");
+   addArray("slots", SFXPlayList::SFXPlaylistSettings::NUM_SLOTS);
+      addProtectedField("soundFile", TypeAssetLooseFilePath, Offset(mSoundFile, SoundAsset),
+         &_setSoundFile, &_getSoundFile, SFXPlayList::SFXPlaylistSettings::NUM_SLOTS, "Path to the sound file.");
+
+      addField("replay", TYPEID< SFXPlayList::EReplayMode >(), Offset(mPlaylist.mSlots.mReplayMode, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS,
+         "Behavior when an already playing sound is encountered on this slot from a previous cycle.\n"
+         "Each slot can have an arbitrary number of sounds playing on it from previous cycles.  This field determines "
+         "how SFXController will handle these sources.");
+      addField("transitionIn", TYPEID< SFXPlayList::ETransitionMode >(), Offset(mPlaylist.mSlots.mTransitionIn, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS,
+         "Behavior when moving into this slot.\n"
+         "After the delayIn time has expired (if any), this slot determines what the controller "
+         "will do before actually playing the slot.");
+      addField("transitionOut", TYPEID< SFXPlayList::ETransitionMode >(), Offset(mPlaylist.mSlots.mTransitionOut, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS,
+         "Behavior when moving out of this slot.\n"
+         "After the #detailTimeOut has expired (if any), this slot determines what the controller "
+         "will do before moving on to the next slot.");
+      addField("delayTimeIn", TypeF32, Offset(mPlaylist.mSlots.mDelayTimeIn.mValue, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS,
+         "Seconds to wait after moving into slot before #transitionIn.");
+      addField("delayTimeInVariance", TypePoint2F, Offset(mPlaylist.mSlots.mDelayTimeIn.mVariance, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS,
+         "Bounds on randomization of #delayTimeIn.\n\n"
+         "@ref SFXPlayList_randomization\n");
+      addField("delayTimeOut", TypeF32, Offset(mPlaylist.mSlots.mDelayTimeOut.mValue, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS,
+         "Seconds to wait before moving out of slot after #transitionOut.");
+      addField("delayTimeOutVariance", TypePoint2F, Offset(mPlaylist.mSlots.mDelayTimeOut.mVariance, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS,
+         "Bounds on randomization of #delayTimeOut.\n\n"
+         "@ref SFXPlayList_randomization\n");
+      addField("fadeTimeIn", TypeF32, Offset(mPlaylist.mSlots.mFadeTimeIn.mValue, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS,
+         "Seconds to fade sound in (-1 to use the track's own fadeInTime.)\n"
+         "@see SFXDescription::fadeTimeIn");
+      addField("fadeTimeInVariance", TypePoint2F, Offset(mPlaylist.mSlots.mFadeTimeIn.mVariance, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS,
+         "Bounds on randomization of #fadeInTime.\n\n"
+         "@ref SFXPlayList_randomization\n");
+      addField("fadeTimeOut", TypeF32, Offset(mPlaylist.mSlots.mFadeTimeOut.mValue, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS,
+         "Seconds to fade sound out (-1 to use the track's own fadeOutTime.)\n"
+         "@see SFXDescription::fadeTimeOut");
+      addField("fadeTimeOutVariance", TypePoint2F, Offset(mPlaylist.mSlots.mFadeTimeOut.mVariance, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS,
+         "Bounds on randomization of #fadeOutTime\n\n"
+         "@ref SFXPlayList_randomization\n");
+      addField("referenceDistance", TypeF32, Offset(mPlaylist.mSlots.mMinDistance.mValue, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS,
+         "@c referenceDistance to set for 3D sounds in this slot (<1 to use @c referenceDistance of track's own description).\n"
+         "@see SFXDescription::referenceDistance");
+      addField("referenceDistanceVariance", TypePoint2F, Offset(mPlaylist.mSlots.mMinDistance.mVariance, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS,
+         "Bounds on randomization of #referenceDistance.\n\n"
+         "@ref SFXPlayList_randomization\n");
+      addField("maxDistance", TypeF32, Offset(mPlaylist.mSlots.mMaxDistance.mValue, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS,
+         "@c maxDistance to apply to 3D sounds in this slot (<1 to use @c maxDistance of track's own description).\n"
+         "@see SFXDescription::maxDistance");
+      addField("maxDistanceVariance", TypePoint2F, Offset(mPlaylist.mSlots.mMaxDistance.mVariance, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS,
+         "Bounds on randomization of #maxDistance.\n\n"
+         "@ref SFXPlayList_randomization\n");
+      addField("volumeScale", TypeF32, Offset(mPlaylist.mSlots.mVolumeScale.mValue, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS,
+         "Scale factor to apply to volume of sounds played on this list slot.\n"
+         "This value will scale the actual volume level set on the track assigned to the slot, i.e. a value of 0.5 will "
+         "cause the track to play at half-volume.");
+      addField("volumeScaleVariance", TypePoint2F, Offset(mPlaylist.mSlots.mVolumeScale.mVariance, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS,
+         "Bounds on randomization of #volumeScale.\n\n"
+         "@ref SFXPlayList_randomization\n");
+      addField("pitchScale", TypeF32, Offset(mPlaylist.mSlots.mPitchScale.mValue, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS,
+         "Scale factor to apply to pitch of sounds played on this list slot.\n"
+         "This value will scale the actual pitch set on the track assigned to the slot, i.e. a value of 0.5 will "
+         "cause the track to play at half its assigned speed.");
+      addField("pitchScaleVariance", TypePoint2F, Offset(mPlaylist.mSlots.mPitchScale.mVariance, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS,
+         "Bounds on randomization of #pitchScale.\n\n"
+         "@ref SFXPlayList_randomization\n");
+      addField("repeatCount", TypeS32, Offset(mPlaylist.mSlots.mRepeatCount, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS,
+         "Number of times to loop this slot.");
+      addField("state", TypeSFXStateName, Offset(mPlaylist.mSlots.mState, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS,
+         "State that must be active for this slot to play.\n\n"
+         "@ref SFXPlayList_states");
+      addField("stateMode", TYPEID< SFXPlayList::EStateMode >(), Offset(mPlaylist.mSlots.mStateMode, SoundAsset), SFXPlayList::SFXPlaylistSettings::NUM_SLOTS,
+         "Behavior when assigned state is deactivated while slot is playing.\n\n"
+         "@ref SFXPlayList_states");
+   endArray("slots");
 
    addField("pitchAdjust", TypeF32, Offset(mProfileDesc.mPitch, SoundAsset), "Adjustment of the pitch value 1 is default.");
    addField("volumeAdjust", TypeF32, Offset(mProfileDesc.mVolume, SoundAsset), "Adjustment to the volume.");
@@ -170,16 +272,33 @@ void SoundAsset::initPersistFields()
    addField("isStreaming", TypeBool, Offset(mProfileDesc.mIsStreaming, SoundAsset), "Use streaming.");
    //....why?
    addField("useHardware", TypeBool, Offset(mProfileDesc.mUseHardware, SoundAsset), "Use hardware mixing for this sound.");
-   addField("minDistance", TypeF32, Offset(mProfileDesc.mMinDistance, SoundAsset), "Minimum distance for sound.");
-   // more like it.
-   addField("maxDistance", TypeF32, Offset(mProfileDesc.mMaxDistance, SoundAsset), "Max distance for sound.");
-   addField("coneInsideAngle", TypeS32, Offset(mProfileDesc.mConeInsideAngle, SoundAsset), "Cone inside angle.");
-   addField("coneOutsideAngle", TypeS32, Offset(mProfileDesc.mConeOutsideAngle, SoundAsset), "Cone outside angle.");
-   addField("coneOutsideVolume", TypeF32, Offset(mProfileDesc.mConeOutsideVolume, SoundAsset), "Cone outside volume.");
-   addField("rolloffFactor", TypeF32, Offset(mProfileDesc.mRolloffFactor, SoundAsset), "Rolloff factor.");
-   addField("scatterDistance", TypePoint3F, Offset(mProfileDesc.mScatterDistance, SoundAsset), "Randomization to the spacial position of the sound.");
    addField("sourceGroup", TypeSFXSourceName, Offset(mProfileDesc.mSourceGroup, SoundAsset), "Group that sources playing with this description should be put into.");
-
+   addField("preload", TypeBool, Offset(mPreload, SoundAsset), "Whether to preload sound data when the profile is added to system.");
+
+   addGroup("Fading");
+      addField("fadeInTime", TypeF32, Offset(mProfileDesc.mFadeInTime, SoundAsset), "Number of seconds to gradually fade in volume from zero when playback starts.");
+      addField("fadeOutTime", TypeF32, Offset(mProfileDesc.mFadeOutTime, SoundAsset), "Number of seconds to gradually fade out volume down to zero when playback is stopped or paused.");
+      addField("fadeInEase", TypeEaseF, Offset(mProfileDesc.mFadeInEase, SoundAsset), "Easing curve for fade-in transition.");
+      addField("fadeOutEase", TypeEaseF, Offset(mProfileDesc.mFadeOutEase, SoundAsset), "Easing curve for fade-out transition.");
+      addField("fadeLoops", TypeBool, Offset(mProfileDesc.mFadeLoops, SoundAsset), "Fade each cycle of a loop in and/or out; otherwise only fade-in first cycle.");
+   endGroup("Fading");
+
+   addGroup("3D");
+      addField("minDistance", TypeF32, Offset(mProfileDesc.mMinDistance, SoundAsset), "Minimum distance for sound.");
+      addField("maxDistance", TypeF32, Offset(mProfileDesc.mMaxDistance, SoundAsset), "Max distance for sound.");
+      addField("coneInsideAngle", TypeS32, Offset(mProfileDesc.mConeInsideAngle, SoundAsset), "Cone inside angle.");
+      addField("coneOutsideAngle", TypeS32, Offset(mProfileDesc.mConeOutsideAngle, SoundAsset), "Cone outside angle.");
+      addField("coneOutsideVolume", TypeF32, Offset(mProfileDesc.mConeOutsideVolume, SoundAsset), "Cone outside volume.");
+      addField("rolloffFactor", TypeF32, Offset(mProfileDesc.mRolloffFactor, SoundAsset), "Rolloff factor.");
+      addField("scatterDistance", TypePoint3F, Offset(mProfileDesc.mScatterDistance, SoundAsset), "Randomization to the spacial position of the sound.");
+   endGroup("3D");
+
+   addGroup("Playlist settings");
+      addField("random", TYPEID< SFXPlayList::ERandomMode >(), Offset(mPlaylist.mRandomMode, SoundAsset), "Slot playback order randomization pattern.");
+      addField("loopMode", TYPEID< SFXPlayList::ELoopMode >(), Offset(mPlaylist.mLoopMode, SoundAsset), "Behavior when description has looping enabled.");
+      addField("numSlotsToPlay", TypeS32, Offset(mPlaylist.mNumSlotsToPlay, SoundAsset), "Number of slots to play.");
+      addField("trace", TypeBool, Offset(mPlaylist.mTrace, SoundAsset), "Enable/disable execution tracing for this playlist (local only).");
+   endGroup("Playlist settings");
 }
 
 //------------------------------------------------------------------------------
@@ -193,67 +312,153 @@ void SoundAsset::copyTo(SimObject* object)
 void SoundAsset::initializeAsset(void)
 {
    Parent::initializeAsset();
+   for (U32 i = 0; i < SFXPlayList::SFXPlaylistSettings::NUM_SLOTS; i++)
+   {
+      if (i == 0 && mSoundFile[i] == StringTable->EmptyString())
+         return;
 
-   if (mSoundFile == StringTable->EmptyString())
-      return;
+      if (mSoundFile[i] == StringTable->EmptyString())
+         break;
+
+      mSoundPath[i] = getOwned() ? expandAssetFilePath(mSoundFile[i]) : mSoundPath[i];
+   }
 
-   mSoundPath = getOwned() ? expandAssetFilePath(mSoundFile) : mSoundPath;
+   //loadSound(slotCount);
+   //mSoundPath = getOwned() ? expandAssetFilePath(mSoundFile) : mSoundPath;
    //loadSound();
 }
 
 void SoundAsset::_onResourceChanged(const Torque::Path &path)
 {
-   if (path != Torque::Path(mSoundPath))
-      return;
+   for (U32 i = 0; i < SFXPlayList::NUM_SLOTS; i++)
+   {
 
+      if (path != Torque::Path(mSoundPath[i]))
+         return;
+   }
    refreshAsset();
 
+   //loadSound(slotCount);
    //loadSound();
 }
 
 void SoundAsset::onAssetRefresh(void)
 {
-   if (mSoundFile == StringTable->EmptyString())
-      return;
+   for (U32 i = 0; i < SFXPlayList::SFXPlaylistSettings::NUM_SLOTS; i++)
+   {
+      if (i == 0 && mSoundFile[i] == StringTable->EmptyString())
+         return;
+
+      if (mSoundFile[i] == StringTable->EmptyString())
+         break;
 
+      mSoundPath[i] = getOwned() ? expandAssetFilePath(mSoundFile[i]) : mSoundPath[i];
+   }
+
+   //loadSound(slotCount);
    //Update
-   mSoundPath = getOwned() ? expandAssetFilePath(mSoundFile) : mSoundPath;
+   //mSoundPath = getOwned() ? expandAssetFilePath(mSoundFile) : mSoundPath;
    //loadSound();
 }
 
 bool SoundAsset::loadSound()
 {
    if (mLoadedState == AssetErrCode::Ok) return true;
-   if (mSoundPath)
+
+   // find out how many active slots we have.
+   U32 numSlots = 0;
+   for (U32 i = 0; i < SFXPlayList::SFXPlaylistSettings::NUM_SLOTS; i++)
    {
-      if (!Torque::FS::IsFile(mSoundPath))
-      {
-         Con::errorf("SoundAsset::initializeAsset: Attempted to load file %s but it was not valid!", mSoundFile);
-         mLoadedState = BadFileReference;
-         mSFXProfile.setDescription(NULL);
-         mSFXProfile.setSoundFileName(StringTable->insert(StringTable->EmptyString()));
-         mSFXProfile.setPreload(false);
+      if (i == 0 && mSoundPath[i] == StringTable->EmptyString())
          return false;
-      }
-      else
-      {// = new SFXProfile(mProfileDesc, mSoundFile, mPreload);
-         if (mProfileDesc.mSourceGroup == NULL)
-            mProfileDesc.mSourceGroup = dynamic_cast<SFXSource*>(Sim::findObject("AudioChannelMaster"));
-         mSFXProfile.setDescription(&mProfileDesc);
-         mSFXProfile.setSoundFileName(mSoundPath);
-         mSFXProfile.setPreload(mPreload);
-
-         //give it a nudge to preload if required
-         mSFXProfile.getBuffer();
+
+      if (mSoundPath[i] == StringTable->EmptyString())
+         break;
+
+      numSlots++;
+   }
+
+
+   if (numSlots > 1)
+   {
+      mIsPlaylist = true;
+
+      for (U32 i = 0; i < numSlots; i++)
+      {
+         if (mSoundPath[i])
+         {
+            if (!Torque::FS::IsFile(mSoundPath[i]))
+            {
+               Con::errorf("SoundAsset::initializeAsset: Attempted to load file %s but it was not valid!", mSoundFile[i]);
+               mLoadedState = BadFileReference;
+               mSFXProfile[i].setDescription(NULL);
+               mSFXProfile[i].setSoundFileName(StringTable->insert(StringTable->EmptyString()));
+               mSFXProfile[i].setPreload(false);
+               return false;
+            }
+            else
+            {// = new SFXProfile(mProfileDesc, mSoundFile, mPreload);
+               if (mProfileDesc.mSourceGroup == NULL)
+                  mProfileDesc.mSourceGroup = dynamic_cast<SFXSource*>(Sim::findObject("AudioChannelMaster"));
+               SFXProfile* trackProfile = new SFXProfile();
+               trackProfile->setDescription(&mProfileDesc);
+               trackProfile->setSoundFileName(mSoundPath[i]);
+               trackProfile->setPreload(mPreload);
+               trackProfile->getBuffer();
+
+               mSFXProfile[i] = *trackProfile;
+
+               mPlaylist.mSlots.mTrack[i] = trackProfile;
+               
+            }
+         }
       }
 
+      mPlaylist.setDescription(&mProfileDesc);
    }
+   else
+   {
+      if (mSoundPath[0])
+      {
+         if (!Torque::FS::IsFile(mSoundPath[0]))
+         {
+            Con::errorf("SoundAsset::initializeAsset: Attempted to load file %s but it was not valid!", mSoundFile[0]);
+            mLoadedState = BadFileReference;
+            mSFXProfile[0].setDescription(NULL);
+            mSFXProfile[0].setSoundFileName(StringTable->insert(StringTable->EmptyString()));
+            mSFXProfile[0].setPreload(false);
+            return false;
+         }
+         else
+         {// = new SFXProfile(mProfileDesc, mSoundFile, mPreload);
+            if (mProfileDesc.mSourceGroup == NULL)
+               mProfileDesc.mSourceGroup = dynamic_cast<SFXSource*>(Sim::findObject("AudioChannelMaster"));
+            mSFXProfile[0].setDescription(&mProfileDesc);
+            mSFXProfile[0].setSoundFileName(mSoundPath[0]);
+            mSFXProfile[0].setPreload(mPreload);
+
+            //give it a nudge to preload if required
+            mSFXProfile[0].getBuffer();
+         }
+
+      }
+   }
+
    mChangeSignal.trigger();
    mLoadedState = Ok;
    return true;
 }
 
-void SoundAsset::setSoundFile(const char* pSoundFile)
+StringTableEntry SoundAsset::getSoundFile(const char* pSoundFile, const U32 slotId)
+{
+   for (U32 i = 0; i < 12; i++)
+   {
+      if(mSoundFile[i] == pSoundFile)
+         return mSoundFile[i];
+   }
+}
+
+void SoundAsset::setSoundFile(const char* pSoundFile, const U32 slotId)
 {
    // Sanity!
    AssertFatal(pSoundFile != NULL, "Cannot use a NULL sound file.");
@@ -261,12 +466,12 @@ void SoundAsset::setSoundFile(const char* pSoundFile)
    // Fetch sound file.
    pSoundFile = StringTable->insert(pSoundFile, true);
 
-   // Ignore no change,
-   if (pSoundFile == mSoundFile)
+   //Ignore no change,
+   if (pSoundFile == mSoundFile[slotId])
       return;
 
    // Update.
-   mSoundFile = getOwned() ? expandAssetFilePath(pSoundFile) : pSoundFile;
+   mSoundFile[slotId] = getOwned() ? expandAssetFilePath(pSoundFile) : pSoundFile;
 
    // Refresh the asset.
    refreshAsset();
@@ -353,11 +558,11 @@ DefineEngineMethod(SoundAsset, playSound, S32, (Point3F position), (Point3F::Zer
    "Plays the sound for this asset.\n"
    "@return (sound plays).\n")
 {
-   if (object->getSfxProfile())
+   if (object->getSFXTrack())
    {
       MatrixF transform;
       transform.setPosition(position);
-      SFXSource* source = SFX->playOnce(object->getSfxProfile(), &transform, NULL, -1);
+      SFXSource* source = SFX->playOnce(object->getSFXTrack(), &transform, NULL, -1);
       if(source)
          return source->getId();
       else

+ 57 - 24
Engine/source/T3D/assets/SoundAsset.h

@@ -55,6 +55,11 @@
 #include "sfx/sfxDescription.h"
 #endif // !_SFXDESCRIPTION_H_
 
+
+#ifndef _SFXTRACK_H_
+#include "sfx/sfxTrack.h"
+#endif
+
 #ifndef _SFXPROFILE_H_
 #include "sfx/sfxProfile.h"
 #endif // !_SFXPROFILE_H_
@@ -63,8 +68,17 @@
 #include "core/resourceManager.h"
 #endif
 
+#ifndef _SFXPLAYLIST_H_
+#include "sfx/sfxPlayList.h"
+#endif
+
+#ifndef _SFXTYPES_H_
+#include "sfx/sfxTypes.h"
+#endif
+
 #include "assetMacroHelpers.h"
 class SFXResource;
+class SFXPlayList;
 
 //-----------------------------------------------------------------------------
 class SoundAsset : public AssetBase
@@ -73,13 +87,17 @@ class SoundAsset : public AssetBase
    typedef AssetPtr<SoundAsset> ConcreteAssetPtr;
 
 protected:
-   StringTableEntry        mSoundFile;
-   StringTableEntry        mSoundPath;
-   SFXProfile              mSFXProfile;
+   StringTableEntry        mSoundFile[12];
+   StringTableEntry        mSoundPath[12];
+   SFXProfile              mSFXProfile[12];
+
    SFXDescription          mProfileDesc;
+   SFXPlayList             mPlaylist;
    // subtitles
    StringTableEntry        mSubtitleString;
    bool                    mPreload;
+   bool                    mIsPlaylist;
+   //SFXPlayList::SlotData   mSlots;
 
    /*These will be needed in the refactor!
    Resource<SFXResource>   mSoundResource;
@@ -132,17 +150,20 @@ public:
    virtual void copyTo(SimObject* object);
 
    //SFXResource* getSound() { return mSoundResource; }
-   Resource<SFXResource> getSoundResource() { loadSound(); return mSFXProfile.getResource(); }
+   Resource<SFXResource> getSoundResource(const U32 slotId = 0) { loadSound(); return mSFXProfile[slotId].getResource(); }
 
    /// Declare Console Object.
    DECLARE_CONOBJECT(SoundAsset);
 
-   void                    setSoundFile(const char* pSoundFile);
+   void setSoundFile(const char* pSoundFile, const U32 slotId = 0);
    bool loadSound();
-   inline StringTableEntry getSoundFile(void) const { return mSoundFile; };
-   inline StringTableEntry getSoundPath(void) const { return mSoundPath; };
-   SFXProfile* getSfxProfile() { return &mSFXProfile; }
+   StringTableEntry getSoundFile(const char* pSoundFile, const U32 slotId = 0);
+   inline StringTableEntry getSoundPath(const U32 slotId = 0) const { return mSoundPath[slotId]; };
+   SFXProfile* getSfxProfile(const U32 slotId = 0) { return &mSFXProfile[slotId]; }
+   SFXPlayList* getSfxPlaylist() { return &mPlaylist; }
+   SFXTrack* getSFXTrack() { return mIsPlaylist ? dynamic_cast<SFXTrack*>(&mPlaylist) : dynamic_cast<SFXTrack*>(&mSFXProfile[0]); }
    SFXDescription* getSfxDescription() { return &mProfileDesc; }
+   bool isPlaylist(){ return mIsPlaylist; }
 
    bool isLoop() { return mProfileDesc.mIsLooping; }
    bool is3D() { return mProfileDesc.mIs3D; }
@@ -156,8 +177,8 @@ protected:
    void _onResourceChanged(const Torque::Path & path);
    virtual void            onAssetRefresh(void);
 
-   static bool setSoundFile(void *obj, const char *index, const char *data) { static_cast<SoundAsset*>(obj)->setSoundFile(data); return false; }
-   static const char* getSoundFile(void* obj, const char* data) { return static_cast<SoundAsset*>(obj)->getSoundFile(); }
+  static bool _setSoundFile(void *obj, const char *index, const char *data) { static_cast<SoundAsset*>(obj)->setSoundFile(data, index ? dAtoi(index) : 0); return false; }
+  static const char* _getSoundFile(void* obj, const char* data) { return static_cast<SoundAsset*>(obj)->getSoundFile(data); }
 };
 
 DefineConsoleType(TypeSoundAssetPtr, SoundAsset)
@@ -175,7 +196,7 @@ DefineConsoleType(TypeSoundAssetId, String)
    StringTableEntry m##name##Name; \
    StringTableEntry m##name##AssetId;\
    AssetPtr<SoundAsset> m##name##Asset = NULL;\
-   SFXProfile* m##name##Profile = NULL;\
+   SFXTrack* m##name##Profile = NULL;\
    SFXDescription* m##name##Desc = NULL;\
    SimObjectId m##name##SFXId = 0;\
 public: \
@@ -268,11 +289,12 @@ public: \
    {\
       return m##name;\
    }\
-   SFXProfile* get##name##Profile()\
+   SFXTrack* get##name##Profile()\
    {\
       if (get##name() != StringTable->EmptyString() && m##name##Asset.notNull()){\
-         m##name##Profile = m##name##Asset->getSfxProfile();\
-         return m##name##Profile;}\
+            m##name##Profile = m##name##Asset->getSFXTrack(); \
+            return m##name##Profile;\
+      }\
       return NULL;\
    }\
    SFXDescription* get##name##Description()\
@@ -308,9 +330,9 @@ public: \
    {\
       if(stream->writeFlag(Sim::findObject(m##name##Name)))\
       {\
-         SFXTrack* sndTrack;\
-         Sim::findObject(m##name##Name, sndTrack);\
+         SFXTrack* sndTrack = get##name##Profile();\
          stream->writeRangedU32(SimObjectId(sndTrack->getId()), DataBlockObjectIdFirst, DataBlockObjectIdLast);\
+         sfxWrite(stream, sndTrack);\
       }\
       else\
       {\
@@ -330,7 +352,10 @@ public: \
    {\
       if(stream->readFlag())\
       {\
+         String errorStr;\
          m##name##SFXId = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );\
+         sfxReadAndResolve(stream, &m##name##Profile, errorStr);\
+         Con::errorf("%s", errorStr.c_str());\
       }\
       else\
       {\
@@ -359,7 +384,7 @@ public: \
    StringTableEntry m##name##Name[max]; \
    StringTableEntry m##name##AssetId[max];\
    AssetPtr<SoundAsset> m##name##Asset[max];\
-   SFXProfile* m##name##Profile[max];\
+   SFXTrack* m##name##Profile[max];\
    SimObjectId m##name##SFXId[max];\
 public: \
    const StringTableEntry get##name##File(const U32& index) const { return m##name##Name[index]; }\
@@ -461,10 +486,10 @@ public: \
          return ResourceManager::get().load( "" );\
       return m##name[id];\
    }\
-   SFXProfile* get##name##Profile(const U32& id)\
+   SFXTrack* get##name##Profile(const U32& id)\
    {\
-      if (get##name(id) != StringTable->EmptyString() && m##name##Asset[id].notNull())\
-         return m##name##Asset[id]->getSfxProfile();\
+         if (m##name##Asset[id].notNull())\
+            return  m##name##Asset[id]->getSFXTrack(); \
       return NULL;\
    }\
    bool is##name##Valid(const U32& id) {return (get##name(id) != StringTable->EmptyString() && m##name##Asset[id] && m##name##Asset[id]->getStatus() == AssetBase::Ok); }
@@ -518,9 +543,12 @@ if (m##name##AssetId[index] != StringTable->EmptyString())\
    {\
       if(stream->writeFlag(Sim::findObject(m##name##Name[index])))\
       {\
-         SFXTrack* sndTrack;\
-         Sim::findObject(m##name##Name[index], sndTrack);\
-         stream->writeRangedU32(SimObjectId(sndTrack->getId()), DataBlockObjectIdFirst, DataBlockObjectIdLast);\
+         SFXTrack* sndTrack = get##name##Profile(index);\
+         if(stream->writeFlag(sndTrack != nullptr))\
+         {\
+            stream->writeRangedU32(SimObjectId(sndTrack->getId()), DataBlockObjectIdFirst, DataBlockObjectIdLast);\
+            sfxWrite(stream, sndTrack);\
+         }\
       }\
       else\
       {\
@@ -540,7 +568,12 @@ if (m##name##AssetId[index] != StringTable->EmptyString())\
    {\
       if(stream->readFlag())\
       {\
-         m##name##SFXId[index] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );\
+         String errorStr;\
+         if(stream->readFlag())\
+         {\
+            m##name##SFXId[index] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );\
+            sfxReadAndResolve(stream, &m##name##Profile[index], errorStr);\
+         }\
       }\
       else\
       {\

+ 1 - 1
Engine/source/T3D/fx/explosion.cpp

@@ -1403,7 +1403,7 @@ bool Explosion::explode()
       resetWorldBox();
    }
 
-   SFXProfile* sound_prof = mDataBlock->getSoundProfile();
+   SFXProfile* sound_prof = static_cast<SFXProfile*>(mDataBlock->getSoundProfile());
    if (sound_prof)
    {
       soundProfile_clone = sound_prof->cloneAndPerformSubstitutions(ss_object, ss_index);

+ 1 - 1
Engine/source/T3D/fx/splash.cpp

@@ -686,7 +686,7 @@ void Splash::spawnExplosion()
 
    /// could just play the explosion one, but explosion could be weapon specific,
    /// splash sound could be liquid specific. food for thought.
-   SFXProfile* sound_prof = mDataBlock->getSoundProfile();
+   SFXTrack* sound_prof = mDataBlock->getSoundProfile();
    if (sound_prof)
    {
       SFX->playOnce(sound_prof, &getTransform());

+ 2 - 2
Engine/source/T3D/gameBase/gameConnectionEvents.cpp

@@ -387,9 +387,9 @@ void SimSoundAssetEvent::process(NetConnection* con)
 {
 
    if (mAsset->is3D())
-      SFX->playOnce(mAsset->getSfxProfile(), &mTransform);
+      SFX->playOnce(mAsset->getSFXTrack(), &mTransform);
    else
-      SFX->playOnce(mAsset->getSfxProfile());
+      SFX->playOnce(mAsset->getSFXTrack());
 
 }
 

+ 163 - 16
Engine/source/T3D/sfx/sfxEmitter.cpp

@@ -89,6 +89,120 @@ ColorI SFXEmitter::smRenderColorOutsideVolume( 255, 0, 0, 255 );
 ColorI SFXEmitter::smRenderColorRangeSphere( 200, 0, 0, 90 );
 
 
+
+//-----------------------------------------------------------------------------
+
+ConsoleType(SoundControls, TypeSoundControls, bool, "")
+
+ConsoleGetType(TypeSoundControls)
+{
+   return "";
+}
+ConsoleSetType(TypeSoundControls)
+{
+}
+
+IMPLEMENT_CONOBJECT(GuiInspectorTypeSoundControls);
+ConsoleDocClass(GuiInspectorTypeSoundControls,
+   "@brief Inspector field type for Controlling playback of sounds\n\n"
+   "Editor use only.\n\n"
+   "@internal"
+);
+
+void GuiInspectorTypeSoundControls::consoleInit()
+{
+   Parent::consoleInit();
+
+   ConsoleBaseType::getType(TypeSoundControls)->setInspectorFieldType("GuiInspectorTypeSoundControls");
+}
+
+GuiControl* GuiInspectorTypeSoundControls::constructEditControl()
+{
+   // Create base filename edit controls
+   GuiControl* retCtrl = Parent::constructEditControl();
+   if (retCtrl == NULL)
+      return retCtrl;
+
+   char szBuffer[512];
+
+   setDataField(StringTable->insert("targetObject"), NULL, mInspector->getInspectObject()->getIdString());
+
+   mPlayButton = new GuiBitmapButtonCtrl();
+   dSprintf(szBuffer, sizeof(szBuffer), "%d.play();", mInspector->getInspectObject()->getId());
+   mPlayButton->setField("Command", szBuffer);
+
+   mPlayButton->setBitmap(StringTable->insert("ToolsModule:playbutton_n_image"));
+
+   mPlayButton->setDataField(StringTable->insert("Profile"), NULL, "GuiButtonProfile");
+   mPlayButton->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile");
+   mPlayButton->setDataField(StringTable->insert("hovertime"), NULL, "1000");
+   mPlayButton->setDataField(StringTable->insert("tooltip"), NULL, "Play this sound emitter");
+
+   mPlayButton->registerObject();
+   addObject(mPlayButton);
+
+   mPauseButton = new GuiBitmapButtonCtrl();
+   dSprintf(szBuffer, sizeof(szBuffer), "%d.pause();", mInspector->getInspectObject()->getId());
+   mPauseButton->setField("Command", szBuffer);
+
+   mPauseButton->setBitmap(StringTable->insert("ToolsModule:pausebutton_n_image"));
+
+   mPauseButton->setDataField(StringTable->insert("Profile"), NULL, "GuiButtonProfile");
+   mPauseButton->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile");
+   mPauseButton->setDataField(StringTable->insert("hovertime"), NULL, "1000");
+   mPauseButton->setDataField(StringTable->insert("tooltip"), NULL, "Pause this sound emitter");
+
+   mPauseButton->registerObject();
+   addObject(mPauseButton);
+
+   mStopButton = new GuiBitmapButtonCtrl();
+   dSprintf(szBuffer, sizeof(szBuffer), "%d.stop();", mInspector->getInspectObject()->getId());
+   mStopButton->setField("Command", szBuffer);
+
+   mStopButton->setBitmap(StringTable->insert("ToolsModule:stopbutton_n_image"));
+
+   mStopButton->setDataField(StringTable->insert("Profile"), NULL, "GuiButtonProfile");
+   mStopButton->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile");
+   mStopButton->setDataField(StringTable->insert("hovertime"), NULL, "1000");
+   mStopButton->setDataField(StringTable->insert("tooltip"), NULL, "Stop this sound emitter");
+
+   mStopButton->registerObject();
+   addObject(mStopButton);
+
+   return retCtrl;
+}
+
+bool GuiInspectorTypeSoundControls::updateRects()
+{
+   S32 dividerPos, dividerMargin;
+   mInspector->getDivider(dividerPos, dividerMargin);
+   Point2I fieldExtent = getExtent();
+   Point2I fieldPos = getPosition();
+
+   bool resized = mEdit->resize(mEditCtrlRect.point, mEditCtrlRect.extent);
+
+   if (mPlayButton != NULL)
+   {
+      RectI shapeEdRect(2, 2, 16, 16);
+      resized |= mPlayButton->resize(shapeEdRect.point, shapeEdRect.extent);
+   }
+
+   if (mPauseButton != NULL)
+   {
+      RectI shapeEdRect(20, 2, 16, 16);
+      resized |= mPauseButton->resize(shapeEdRect.point, shapeEdRect.extent);
+   }
+
+   if (mStopButton != NULL)
+   {
+      RectI shapeEdRect(38, 2, 16, 16);
+      resized |= mStopButton->resize(shapeEdRect.point, shapeEdRect.extent);
+   }
+
+   return resized;
+}
+
+
 //-----------------------------------------------------------------------------
 
 SFXEmitter::SFXEmitter()
@@ -106,8 +220,7 @@ SFXEmitter::SFXEmitter()
    mDescription.mFadeInTime = -1.f;
    mDescription.mFadeOutTime = -1.f;
    mInstanceDescription = &mDescription;
-   mLocalProfile.mFilename = StringTable->EmptyString();
-   mLocalProfile._registerSignals();
+   mLocalProfile = NULL;
 
    INIT_ASSET(Sound);
 
@@ -119,7 +232,9 @@ SFXEmitter::SFXEmitter()
 
 SFXEmitter::~SFXEmitter()
 {
-   mLocalProfile.onRemove();
+   if(mLocalProfile != NULL)
+      mLocalProfile->onRemove();
+
    SFX_DELETE( mSource );
 }
 
@@ -191,6 +306,8 @@ void SFXEmitter::initPersistFields()
 
    addGroup( "Sound" );
 
+      addField("Controls", TypeSoundControls, 0, "");
+
       addField( "playOnAdd",           TypeBool,      Offset( mPlayOnAdd, SFXEmitter ),
          "Whether playback of the emitter's sound should start as soon as the emitter object is added to the level.\n"
          "If this is true, the emitter will immediately start to play when the level is loaded." );
@@ -365,6 +482,7 @@ U32 SFXEmitter::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
 
    // Write the source playback state.
    stream->writeFlag( mask & SourcePlayMask );
+   stream->writeFlag( mask & SourcePauseMask );
    stream->writeFlag( mask & SourceStopMask );
 
    return retMask;
@@ -490,6 +608,8 @@ void SFXEmitter::unpackUpdate( NetConnection *conn, BitStream *stream )
    // Check the source playback masks.
    if ( stream->readFlag() ) // SourcePlayMask
       play();
+   if (stream->readFlag()) //SourcePauseMask
+      pause();
    if ( stream->readFlag() ) // SourceStopMask
       stop();
 }
@@ -653,7 +773,7 @@ void SFXEmitter::_update()
    SFXStatus prevState = mSource ? mSource->getStatus() : SFXStatusNull;
 
    // are we overriding the asset properties?
-   bool useTrackDescriptionOnly = (mUseTrackDescriptionOnly && mSoundAsset.notNull() && mSoundAsset->getSfxProfile());
+   bool useTrackDescriptionOnly = (mUseTrackDescriptionOnly && mSoundAsset.notNull() && getSoundProfile());
 
    if (mSoundAsset.notNull())
    {
@@ -662,12 +782,12 @@ void SFXEmitter::_update()
       else
          mInstanceDescription = &mDescription;
 
-      mLocalProfile = *mSoundAsset->getSfxProfile();
-   }
-   // Make sure all the settings are valid.
-   mInstanceDescription->validate();
-   mLocalProfile.setDescription(mInstanceDescription);
+      mLocalProfile = getSoundProfile();
 
+      // Make sure all the settings are valid.
+      mInstanceDescription->validate();
+      mLocalProfile->setDescription(mInstanceDescription);
+   }
 
    const MatrixF& transform = getTransform();
    const VectorF& velocity = getVelocity();
@@ -676,12 +796,12 @@ void SFXEmitter::_update()
    if( mDirty.test( Track | Is3D | IsLooping | IsStreaming | TrackOnly ) )
    {
       SFX_DELETE( mSource );
-      if (mLocalProfile.getSoundFileName().isNotEmpty())
+      if (getSoundProfile())
       {
-         mSource = SFX->createSource(&mLocalProfile, &transform, &velocity);
+         mSource = SFX->createSource(mLocalProfile, &transform, &velocity);
          if (!mSource)
             Con::errorf("SFXEmitter::_update() - failed to create sound for track %i (%s)",
-               mSoundAsset->getSfxProfile()->getId(), mSoundAsset->getSfxProfile()->getName());
+               getSoundProfile()->getId(), getSoundProfile()->getName());
 
          // If we're supposed to play when the emitter is 
          // added to the scene then also restart playback 
@@ -999,6 +1119,23 @@ void SFXEmitter::play()
 
 //-----------------------------------------------------------------------------
 
+void SFXEmitter::pause()
+{
+   if (mSource)
+      mSource->pause();
+   else
+   {
+      // By clearing the playback masks first we
+      // ensure the last playback command called 
+      // within a single tick is the one obeyed.
+      clearMaskBits(AllSourceMasks);
+
+      setMaskBits(SourcePauseMask);
+   }
+}
+
+//-----------------------------------------------------------------------------
+
 void SFXEmitter::stop()
 {
    if ( mSource )
@@ -1043,8 +1180,8 @@ SFXStatus SFXEmitter::_getPlaybackStatus() const
 
 bool SFXEmitter::is3D() const
 {
-   if( mSoundAsset.notNull() && mSoundAsset->getSfxProfile() != NULL )
-      return mSoundAsset->getSfxProfile()->getDescription()->mIs3D;
+   if( mSoundAsset.notNull() )
+      return mSoundAsset->getSfxDescription()->mIs3D;
    else
       return mInstanceDescription->mIs3D;
 }
@@ -1080,8 +1217,8 @@ void SFXEmitter::setScale( const VectorF &scale )
 {
    F32 maxDistance;
    
-   if( mUseTrackDescriptionOnly && mSoundAsset.notNull() && mSoundAsset->getSfxProfile())
-      maxDistance = mSoundAsset->getSfxProfile()->getDescription()->mMaxDistance;
+   if( mUseTrackDescriptionOnly && mSoundAsset.notNull() && getSoundProfile())
+      maxDistance = mSoundAsset->getSfxDescription()->mMaxDistance;
    else
    {
       // Use the average of the three coords.
@@ -1113,6 +1250,15 @@ DefineEngineMethod( SFXEmitter, play, void, (),,
 
 //-----------------------------------------------------------------------------
 
+DefineEngineMethod(SFXEmitter, pause, void, (), ,
+   "Manually pause playback of the emitter's sound.\n"
+   "If this is called on the server-side object, the pause command will be related to all client-side ghosts.\n")
+{
+   object->pause();
+}
+
+//-----------------------------------------------------------------------------
+
 DefineEngineMethod( SFXEmitter, stop, void, (),,
    "Manually stop playback of the emitter's sound.\n"
    "If this is called on the server-side object, the stop command will be related to all client-side ghosts.\n" )
@@ -1130,3 +1276,4 @@ DefineEngineMethod( SFXEmitter, getSource, SFXSource*, (),,
 {
    return object->getSource();
 }
+

+ 22 - 2
Engine/source/T3D/sfx/sfxEmitter.h

@@ -41,6 +41,21 @@
 class SFXSource;
 class SFXTrack;
 
+DefineConsoleType(TypeSoundControls, bool)
+class GuiInspectorTypeSoundControls : public GuiInspectorField
+{
+   typedef GuiInspectorField Parent;
+public:
+   GuiBitmapButtonCtrl* mPlayButton;
+   GuiBitmapButtonCtrl* mPauseButton;
+   GuiBitmapButtonCtrl* mStopButton;
+
+   DECLARE_CONOBJECT(GuiInspectorTypeSoundControls);
+   static void consoleInit();
+
+   virtual GuiControl* constructEditControl();
+   virtual bool updateRects();
+};
 //RDTODO: make 3D sound emitters yield their source when being culled
 
 /// The SFXEmitter is used to place 2D or 3D sounds into a 
@@ -69,7 +84,8 @@ class SFXEmitter : public SceneObject
          DirtyUpdateMask      = BIT(2),
 
          SourcePlayMask       = BIT(3),
-         SourceStopMask       = BIT(4),
+         SourcePauseMask       = BIT(4),
+         SourceStopMask       = BIT(5),
 
          AllSourceMasks = SourcePlayMask | SourceStopMask,
       };
@@ -116,7 +132,7 @@ class SFXEmitter : public SceneObject
 
       /// A local profile object used to coax the
       /// sound system to play a custom sound.
-      SFXProfile mLocalProfile;
+      SFXTrack* mLocalProfile;
 
       /// The description used by the local profile.
       SFXDescription mDescription;
@@ -219,6 +235,10 @@ class SFXEmitter : public SceneObject
       /// the emitter source is not already playing.
       void play();
 
+      /// Sends network event to pause playback if 
+      /// the emitter source is already playing.
+      void pause();
+
       /// Sends network event to stop emitter 
       /// playback on all ghosted clients.
       void stop();

+ 1 - 1
Engine/source/T3D/shapeBase.cpp

@@ -2303,7 +2303,7 @@ void ShapeBase::updateAudioState(SoundThread& st)
          // if asset is valid, play
          if (st.asset->isAssetValid() )
          {
-            st.sound = SFX->createSource( st.asset->getSfxProfile() , &getTransform() );
+            st.sound = SFX->createSource( st.asset->getSFXTrack() , &getTransform() );
             if ( st.sound )
                st.sound->play();
          }

+ 2 - 2
Engine/source/T3D/shapeImage.cpp

@@ -2785,7 +2785,7 @@ void ShapeBase::setImageState(U32 imageSlot, U32 newState, bool force)
    // Delete any loooping sounds that were in the previous state.
    // this is the crazy bit =/ needs to know prev state in order to stop sounds.
    // lastState does not return an id for the prev state so we keep track of it.
-   if (lastState->sound && lastState->sound->getSfxProfile()->getDescription()->mIsLooping)
+   if (lastState->sound && lastState->sound->getSFXTrack()->getDescription()->mIsLooping)
    {  
       for (Vector<SFXSource*>::iterator i = image.mSoundSources.begin(); i != image.mSoundSources.end(); i++)
          SFX_DELETE((*i));    
@@ -2799,7 +2799,7 @@ void ShapeBase::setImageState(U32 imageSlot, U32 newState, bool force)
       if (stateData.sound)
    {
       const Point3F& velocity         = getVelocity();
-         image.addSoundSource(SFX->createSource(stateData.sound->getSfxProfile(), &getRenderTransform(), &velocity));
+         image.addSoundSource(SFX->createSource(stateData.sound->getSFXTrack(), &getRenderTransform(), &velocity));
       }
       if (stateData.soundTrack)
       {

+ 11 - 2
Engine/source/sfx/sfxController.cpp

@@ -167,7 +167,7 @@ void SFXController::_compileList( SFXPlayList* playList )
       
       // If there's no track in this slot, ignore it.
       
-      if( !playList->getTrackProfile(slotIndex))
+      if( !playList->getSlots().mTrack[slotIndex])
          continue;
          
       // If this is a looped slot and the list is not set to loop
@@ -394,7 +394,13 @@ bool SFXController::_execInsn()
       case OP_Play:
       {
          SFXPlayList* playList = getPlayList();
-         SFXTrack* track = playList->getTrackProfile(insn.mSlotIndex);
+         if (playList == NULL)
+         {
+            endUpdate = true;
+            break;
+         }
+
+         SFXTrack* track = playList->getSlots().mTrack[insn.mSlotIndex];
          
          // Handle existing sources playing on this slot and find
          // whether we need to start a new source.
@@ -817,6 +823,9 @@ void SFXController::_update()
    
    SFXPlayList* playList = getPlayList();
 
+   if (!playList)
+      Parent::stop();
+
    // Check all sources against the current state setup and
    // take appropriate actions.
  

+ 149 - 98
Engine/source/sfx/sfxPlayList.cpp

@@ -23,6 +23,7 @@
 #include "sfx/sfxPlayList.h"
 #include "sfx/sfxState.h"
 #include "sfx/sfxTypes.h"
+#include "sfx/sfxDescription.h"
 #include "core/stream/bitStream.h"
 #include "math/mRandom.h"
 #include "math/mathTypes.h"
@@ -218,10 +219,23 @@ SFXPlayList::SFXPlayList()
    : mRandomMode( RANDOM_NotRandom ),
      mLoopMode( LOOP_All ),
      mTrace( false ),
-     mNumSlotsToPlay( NUM_SLOTS )
+     mNumSlotsToPlay( NUM_SLOTS ),
+   mActiveSlots(12)
 {
-   for (U32 i=0;i<NUM_SLOTS;i++)
-      INIT_SOUNDASSET_ARRAY(Track, i);
+}
+
+SFXPlayList::~SFXPlayList()
+{
+   if (!isTempClone())
+      return;
+
+   // cleanup after a temp-clone
+
+   if (mDescription && mDescription->isTempClone())
+   {
+      delete mDescription;
+      mDescription = 0;
+   }
 }
 
 //-----------------------------------------------------------------------------
@@ -250,10 +264,10 @@ void SFXPlayList::initPersistFields()
    
       addArray( "slots", NUM_SLOTS );
       
-         INITPERSISTFIELD_SOUNDASSET_ARRAY( Track, NUM_SLOTS, SFXPlayList,
+         addField("track", TypeSFXTrackName, Offset(mSlots.mTrack, SFXPlayList), NUM_SLOTS,
             "Track to play in this slot.\n"
             "This must be set for the slot to be considered for playback.  Other settings for a slot "
-            "will not take effect except this field is set." );
+            "will not take effect except this field is set.");
          addField( "replay",                 TYPEID< EReplayMode >(), Offset( mSlots.mReplayMode, SFXPlayList ), NUM_SLOTS,
             "Behavior when an already playing sound is encountered on this slot from a previous cycle.\n"
             "Each slot can have an arbitrary number of sounds playing on it from previous cycles.  This field determines "
@@ -340,32 +354,64 @@ void SFXPlayList::initPersistFields()
 
 //-----------------------------------------------------------------------------
 
+U32 SFXPlayList::getNumSlots()
+{
+   U32 trackCount = 0;
+   for (U32 i = 0; i < NUM_SLOTS; i++)
+   {
+      if (mSlots.mTrack[i] == NULL)
+      {
+         return i;
+      }
+      trackCount++;
+   }
+
+   return trackCount;
+}
+
+bool SFXPlayList::isLooping() const
+{
+   // pretty useless in playlist, looping handled differently.
+   return false;
+}
+
+bool SFXPlayList::onAdd()
+{
+   if (!Parent::onAdd())
+      return false;
+
+   mActiveSlots = getNumSlots();
+
+   validate();
+
+   return true;
+}
+
+void SFXPlayList::onRemove()
+{
+   Parent::onRemove();
+}
+
 bool SFXPlayList::preload( bool server, String& errorStr )
 {
    if( !Parent::preload( server, errorStr ) )
       return false;
+
+   mActiveSlots = getNumSlots();
       
    validate();
       
    // Resolve SFXTracks and SFXStates on client.
-      
+   
    if( !server )
    {
-      for( U32 i = 0; i < NUM_SLOTS; ++ i )
+      for( U32 i = 0; i < mActiveSlots; ++ i )
       {
-         StringTableEntry track = getTrack(i);
-         if (track != StringTable->EmptyString())
-         {
-            _setTrack(getTrack(i), i);
-         if (!getTrackProfile(i))
-         {
-            Con::errorf("SFXPlayList::Preload() - unable to find sfxProfile for asset %s", mTrackAssetId[i]);
+         if (!sfxResolve(&mSlots.mTrack[i], errorStr))
             return false;
-         }
             
-            if (!sfxResolve(&mSlots.mState[i], errorStr))
+         if (!sfxResolve(&mSlots.mState[i], errorStr))
             return false;
-         }
       }
    }
       
@@ -382,55 +428,57 @@ void SFXPlayList::packData( BitStream* stream )
    stream->writeInt( mLoopMode, NUM_LOOP_MODE_BITS );
    stream->writeInt( mNumSlotsToPlay, NUM_SLOTS_TO_PLAY_BITS );
    
-   #define FOR_EACH_SLOT \
-      for( U32 i = 0; i < NUM_SLOTS; ++ i )
-   
-   FOR_EACH_SLOT stream->writeInt( mSlots.mReplayMode[ i ], NUM_REPLAY_MODE_BITS );
-   FOR_EACH_SLOT stream->writeInt( mSlots.mTransitionIn[ i ], NUM_TRANSITION_MODE_BITS );
-   FOR_EACH_SLOT stream->writeInt( mSlots.mTransitionOut[ i ], NUM_TRANSITION_MODE_BITS );
-   FOR_EACH_SLOT stream->writeInt( mSlots.mStateMode[ i ], NUM_STATE_MODE_BITS );
-      
-   FOR_EACH_SLOT if (stream->writeFlag(mSlots.mFadeTimeIn.mValue[ i ] != -1 ))
-      stream->write( mSlots.mFadeTimeIn.mValue[ i ] );
-   FOR_EACH_SLOT if (stream->writeFlag( mSlots.mFadeTimeIn.mVariance[ i ][ 0 ] > 0))
-      stream->write(mSlots.mFadeTimeIn.mVariance[ i ][ 0 ] );
-   FOR_EACH_SLOT if (stream->writeFlag( mSlots.mFadeTimeIn.mVariance[ i ][ 1 ] > 0))
-      stream->write(mSlots.mFadeTimeIn.mVariance[ i ][ 1 ] );
-   FOR_EACH_SLOT if (stream->writeFlag(mSlots.mFadeTimeOut.mValue[ i ] != -1 ))
-      stream->write( mSlots.mFadeTimeOut.mValue[ i ] );
-   FOR_EACH_SLOT if (stream->writeFlag(mSlots.mFadeTimeOut.mVariance[i][0] > 0))
-      stream->write(mSlots.mFadeTimeOut.mVariance[i][0]);
-   FOR_EACH_SLOT if (stream->writeFlag(mSlots.mFadeTimeOut.mVariance[i][1] > 0))
-      stream->write(mSlots.mFadeTimeOut.mVariance[i][1]);
-   FOR_EACH_SLOT if (stream->writeFlag(mSlots.mDelayTimeIn.mValue[ i ] > 0))
-      stream->write(mSlots.mDelayTimeIn.mValue[ i ] );
-   FOR_EACH_SLOT if (stream->writeFlag(mSlots.mDelayTimeIn.mVariance[ i ][ 0 ] > 0))
-      stream->write(mSlots.mDelayTimeIn.mVariance[ i ][ 0 ] );
-   FOR_EACH_SLOT if (stream->writeFlag(mSlots.mDelayTimeIn.mVariance[ i ][ 1 ] > 0))
-      stream->write(mSlots.mDelayTimeIn.mVariance[ i ][ 1 ] );
-   FOR_EACH_SLOT if (stream->writeFlag(mSlots.mDelayTimeOut.mValue[ i ] > 0))
-      stream->write(mSlots.mDelayTimeOut.mValue[ i ] );
-   FOR_EACH_SLOT if (stream->writeFlag(mSlots.mDelayTimeOut.mVariance[ i ][ 0 ] > 0))
-      stream->write(mSlots.mDelayTimeOut.mVariance[ i ][ 0 ] );
-   FOR_EACH_SLOT if (stream->writeFlag(mSlots.mDelayTimeOut.mVariance[ i ][ 1 ] > 0))
-      stream->write(mSlots.mDelayTimeOut.mVariance[ i ][ 1 ] );
-   FOR_EACH_SLOT if (stream->writeFlag(mSlots.mVolumeScale.mValue[ i ] != 1))
-      stream->write(mSlots.mVolumeScale.mValue[ i ] );
-   FOR_EACH_SLOT if (stream->writeFlag(mSlots.mVolumeScale.mVariance[ i ][ 0 ] > 0))
-      stream->write(mSlots.mVolumeScale.mVariance[ i ][ 0 ] );
-   FOR_EACH_SLOT if (stream->writeFlag(mSlots.mVolumeScale.mVariance[ i ][ 1 ] > 0))
-      stream->write(mSlots.mVolumeScale.mVariance[ i ][ 1 ] );
-   FOR_EACH_SLOT if (stream->writeFlag(mSlots.mPitchScale.mValue[ i ] != 1))
-      stream->write(mSlots.mPitchScale.mValue[ i ] );
-   FOR_EACH_SLOT if (stream->writeFlag(mSlots.mPitchScale.mVariance[ i ][ 0 ] > 0))
-      stream->write(mSlots.mPitchScale.mVariance[ i ][ 0 ] );
-   FOR_EACH_SLOT if (stream->writeFlag(mSlots.mPitchScale.mVariance[ i ][ 1 ] > 0))
-      stream->write(mSlots.mPitchScale.mVariance[ i ][ 1 ] );
-   FOR_EACH_SLOT if (stream->writeFlag( mSlots.mRepeatCount[ i ] > 0))
-      stream->write( mSlots.mRepeatCount[ i ] );
-      
-   FOR_EACH_SLOT sfxWrite( stream, mSlots.mState[ i ] );
-   FOR_EACH_SLOT PACKDATA_SOUNDASSET_ARRAY(Track, i);
+   stream->writeInt(mActiveSlots, 8);
+
+   for (U32 i = 0; i < mActiveSlots; ++i)
+   {
+       stream->writeInt(mSlots.mReplayMode[i], NUM_REPLAY_MODE_BITS);
+       stream->writeInt(mSlots.mTransitionIn[i], NUM_TRANSITION_MODE_BITS);
+       stream->writeInt(mSlots.mTransitionOut[i], NUM_TRANSITION_MODE_BITS);
+       stream->writeInt(mSlots.mStateMode[i], NUM_STATE_MODE_BITS);
+
+       if (stream->writeFlag(mSlots.mFadeTimeIn.mValue[i] != -1))
+         stream->write(mSlots.mFadeTimeIn.mValue[i]);
+       if (stream->writeFlag(mSlots.mFadeTimeIn.mVariance[i][0] > 0))
+         stream->write(mSlots.mFadeTimeIn.mVariance[i][0]);
+       if (stream->writeFlag(mSlots.mFadeTimeIn.mVariance[i][1] > 0))
+         stream->write(mSlots.mFadeTimeIn.mVariance[i][1]);
+       if (stream->writeFlag(mSlots.mFadeTimeOut.mValue[i] != -1))
+         stream->write(mSlots.mFadeTimeOut.mValue[i]);
+       if (stream->writeFlag(mSlots.mFadeTimeOut.mVariance[i][0] > 0))
+         stream->write(mSlots.mFadeTimeOut.mVariance[i][0]);
+       if (stream->writeFlag(mSlots.mFadeTimeOut.mVariance[i][1] > 0))
+         stream->write(mSlots.mFadeTimeOut.mVariance[i][1]);
+       if (stream->writeFlag(mSlots.mDelayTimeIn.mValue[i] > 0))
+         stream->write(mSlots.mDelayTimeIn.mValue[i]);
+       if (stream->writeFlag(mSlots.mDelayTimeIn.mVariance[i][0] > 0))
+         stream->write(mSlots.mDelayTimeIn.mVariance[i][0]);
+       if (stream->writeFlag(mSlots.mDelayTimeIn.mVariance[i][1] > 0))
+         stream->write(mSlots.mDelayTimeIn.mVariance[i][1]);
+       if (stream->writeFlag(mSlots.mDelayTimeOut.mValue[i] > 0))
+         stream->write(mSlots.mDelayTimeOut.mValue[i]);
+       if (stream->writeFlag(mSlots.mDelayTimeOut.mVariance[i][0] > 0))
+         stream->write(mSlots.mDelayTimeOut.mVariance[i][0]);
+       if (stream->writeFlag(mSlots.mDelayTimeOut.mVariance[i][1] > 0))
+         stream->write(mSlots.mDelayTimeOut.mVariance[i][1]);
+       if (stream->writeFlag(mSlots.mVolumeScale.mValue[i] != 1))
+         stream->write(mSlots.mVolumeScale.mValue[i]);
+       if (stream->writeFlag(mSlots.mVolumeScale.mVariance[i][0] > 0))
+         stream->write(mSlots.mVolumeScale.mVariance[i][0]);
+       if (stream->writeFlag(mSlots.mVolumeScale.mVariance[i][1] > 0))
+         stream->write(mSlots.mVolumeScale.mVariance[i][1]);
+       if (stream->writeFlag(mSlots.mPitchScale.mValue[i] != 1))
+         stream->write(mSlots.mPitchScale.mValue[i]);
+       if (stream->writeFlag(mSlots.mPitchScale.mVariance[i][0] > 0))
+         stream->write(mSlots.mPitchScale.mVariance[i][0]);
+       if (stream->writeFlag(mSlots.mPitchScale.mVariance[i][1] > 0))
+         stream->write(mSlots.mPitchScale.mVariance[i][1]);
+       if (stream->writeFlag(mSlots.mRepeatCount[i] > 0))
+         stream->write(mSlots.mRepeatCount[i]);
+
+       sfxWrite(stream, mSlots.mState[i]);
+       sfxWrite(stream, mSlots.mTrack[i]);
+   }
 }
 
 //-----------------------------------------------------------------------------
@@ -442,36 +490,39 @@ void SFXPlayList::unpackData( BitStream* stream )
    mRandomMode          = ( ERandomMode ) stream->readInt( NUM_RANDOM_MODE_BITS );
    mLoopMode            = ( ELoopMode ) stream->readInt( NUM_LOOP_MODE_BITS );
    mNumSlotsToPlay      = stream->readInt( NUM_SLOTS_TO_PLAY_BITS );
-   
-   FOR_EACH_SLOT mSlots.mReplayMode[ i ]     = ( EReplayMode ) stream->readInt( NUM_REPLAY_MODE_BITS );
-   FOR_EACH_SLOT mSlots.mTransitionIn[ i ]   = ( ETransitionMode ) stream->readInt( NUM_TRANSITION_MODE_BITS );
-   FOR_EACH_SLOT mSlots.mTransitionOut[ i ]  = ( ETransitionMode ) stream->readInt( NUM_TRANSITION_MODE_BITS );
-   FOR_EACH_SLOT mSlots.mStateMode[ i ]      = ( EStateMode ) stream->readInt( NUM_STATE_MODE_BITS );
-      
-   FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mFadeTimeIn.mValue[ i ] );}
-   FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mFadeTimeIn.mVariance[ i ][ 0 ] );}
-   FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mFadeTimeIn.mVariance[ i ][ 1 ] );}
-   FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mFadeTimeOut.mValue[ i ] );}
-   FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mFadeTimeOut.mVariance[ i ][ 0 ] );}
-   FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mFadeTimeOut.mVariance[ i ][ 1 ] );}
-   FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mDelayTimeIn.mValue[ i ] );}
-   FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mDelayTimeIn.mVariance[ i ][ 0 ] );}
-   FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mDelayTimeIn.mVariance[ i ][ 1 ] );}
-   FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mDelayTimeOut.mValue[ i ] );}
-   FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mDelayTimeOut.mVariance[ i ][ 0 ] );}
-   FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mDelayTimeOut.mVariance[ i ][ 1 ] );}
-   FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mVolumeScale.mValue[ i ] );}
-   FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mVolumeScale.mVariance[ i ][ 0 ] );}
-   FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mVolumeScale.mVariance[ i ][ 1 ] );}
-   FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mPitchScale.mValue[ i ] );}
-   FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mPitchScale.mVariance[ i ][ 0 ] );}
-   FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mPitchScale.mVariance[ i ][ 1 ] );}
-   FOR_EACH_SLOT if(stream->readFlag()){ stream->read( &mSlots.mRepeatCount[ i ] );}
-      
-   FOR_EACH_SLOT sfxRead( stream, &mSlots.mState[ i ] );
-   FOR_EACH_SLOT UNPACKDATA_SOUNDASSET_ARRAY(Track, i);
-   
-   #undef FOR_EACH_SLOT
+
+   mActiveSlots = stream->readInt(8);
+
+   for (U32 i = 0; i < mActiveSlots; ++i)
+   {
+      mSlots.mReplayMode[i] = (EReplayMode)stream->readInt(NUM_REPLAY_MODE_BITS);
+      mSlots.mTransitionIn[i] = (ETransitionMode)stream->readInt(NUM_TRANSITION_MODE_BITS);
+      mSlots.mTransitionOut[i] = (ETransitionMode)stream->readInt(NUM_TRANSITION_MODE_BITS);
+      mSlots.mStateMode[i] = (EStateMode)stream->readInt(NUM_STATE_MODE_BITS);
+
+      if (stream->readFlag()) { stream->read(&mSlots.mFadeTimeIn.mValue[i]); }
+      if (stream->readFlag()) { stream->read(&mSlots.mFadeTimeIn.mVariance[i][0]); }
+      if (stream->readFlag()) { stream->read(&mSlots.mFadeTimeIn.mVariance[i][1]); }
+      if (stream->readFlag()) { stream->read(&mSlots.mFadeTimeOut.mValue[i]); }
+      if (stream->readFlag()) { stream->read(&mSlots.mFadeTimeOut.mVariance[i][0]); }
+      if (stream->readFlag()) { stream->read(&mSlots.mFadeTimeOut.mVariance[i][1]); }
+      if (stream->readFlag()) { stream->read(&mSlots.mDelayTimeIn.mValue[i]); }
+      if (stream->readFlag()) { stream->read(&mSlots.mDelayTimeIn.mVariance[i][0]); }
+      if (stream->readFlag()) { stream->read(&mSlots.mDelayTimeIn.mVariance[i][1]); }
+      if (stream->readFlag()) { stream->read(&mSlots.mDelayTimeOut.mValue[i]); }
+      if (stream->readFlag()) { stream->read(&mSlots.mDelayTimeOut.mVariance[i][0]); }
+      if (stream->readFlag()) { stream->read(&mSlots.mDelayTimeOut.mVariance[i][1]); }
+      if (stream->readFlag()) { stream->read(&mSlots.mVolumeScale.mValue[i]); }
+      if (stream->readFlag()) { stream->read(&mSlots.mVolumeScale.mVariance[i][0]); }
+      if (stream->readFlag()) { stream->read(&mSlots.mVolumeScale.mVariance[i][1]); }
+      if (stream->readFlag()) { stream->read(&mSlots.mPitchScale.mValue[i]); }
+      if (stream->readFlag()) { stream->read(&mSlots.mPitchScale.mVariance[i][0]); }
+      if (stream->readFlag()) { stream->read(&mSlots.mPitchScale.mVariance[i][1]); }
+      if (stream->readFlag()) { stream->read(&mSlots.mRepeatCount[i]); }
+
+      sfxRead(stream, &mSlots.mState[i]);
+      sfxRead(stream, &mSlots.mTrack[i]);
+   }
 }
 
 //-----------------------------------------------------------------------------
@@ -486,8 +537,8 @@ void SFXPlayList::inspectPostApply()
 
 void SFXPlayList::validate()
 {
-   if( mNumSlotsToPlay > NUM_SLOTS )
-      mNumSlotsToPlay = NUM_SLOTS;
+   if( mNumSlotsToPlay > mActiveSlots )
+      mNumSlotsToPlay = mActiveSlots;
       
    mSlots.mFadeTimeIn.validate();
    mSlots.mFadeTimeOut.validate();

+ 26 - 20
Engine/source/sfx/sfxPlayList.h

@@ -30,13 +30,8 @@
    #include "sfx/sfxTrack.h"
 #endif
 
-#ifndef SOUND_ASSET_H
-#include "T3D/assets/SoundAsset.h"
-#endif
-
-
 class SFXState;
-
+class SFXDescription;
 
 /// A playback list of SFXTracks.
 ///
@@ -79,7 +74,7 @@ class SFXPlayList : public SFXTrack
    
       typedef SFXTrack Parent;
       
-      enum
+      enum SFXPlaylistSettings
       {
          /// Number of slots in a playlist.
          ///
@@ -261,6 +256,9 @@ class SFXPlayList : public SFXTrack
          /// is playing.
          EStateMode mStateMode[ NUM_SLOTS ];
 
+         /// Track to play in this slot.
+         SFXTrack* mTrack[NUM_SLOTS];
+
          SlotData()
          {
             dMemset( mReplayMode, 0, sizeof( mReplayMode ) );
@@ -268,6 +266,7 @@ class SFXPlayList : public SFXTrack
             dMemset( mTransitionOut, 0, sizeof( mTransitionOut ) );
             dMemset( mRepeatCount, 0, sizeof( mRepeatCount ) );
             dMemset( mState, 0, sizeof( mState ) );
+            dMemset( mTrack, 0, sizeof( mTrack ) );
             dMemset( mStateMode, 0, sizeof( mStateMode ) );
             
             for( U32 i = 0; i < NUM_SLOTS; ++ i )
@@ -282,31 +281,33 @@ class SFXPlayList : public SFXTrack
             }
          }
       };
-      DECLARE_SOUNDASSET_ARRAY(SFXPlayList, Track, NUM_SLOTS);
-      DECLARE_ASSET_ARRAY_SETGET(SFXPlayList, Track);
 
-   protected:
-   
+   public:
+      // moved to public for soundasset
+
       /// Trace interpreter execution.  This field is not networked.
       bool mTrace;
-   
+
       /// Select slots at random.
       ERandomMode mRandomMode;
-         
+
       /// Loop over slots in this list.
       ELoopMode mLoopMode;
-      
+
       /// Number of slots to play from list.  This can be used, for example,
       /// to create a list of tracks where only a single track is selected and
       /// played for each cycle.
       U32 mNumSlotsToPlay;
-      
+
       /// Data for each of the playlist slots.
       SlotData mSlots;
-               
-   public:
-   
+
+      U32 mActiveSlots;
+
       SFXPlayList();
+
+      /// The destructor.
+      virtual ~SFXPlayList();
       
       /// Make all settings conform to constraints.
       void validate();
@@ -324,7 +325,7 @@ class SFXPlayList : public SFXTrack
       ELoopMode getLoopMode() const { return mLoopMode; }
       
       /// Return the total number of slots in the list.
-      U32 getNumSlots() const { return NUM_SLOTS; }
+      U32 getNumSlots();
       
       /// Return the slot data for this list.
       const SlotData& getSlots() const { return mSlots; }
@@ -332,8 +333,13 @@ class SFXPlayList : public SFXTrack
       DECLARE_CONOBJECT( SFXPlayList );
       DECLARE_CATEGORY( "SFX" );
       DECLARE_DESCRIPTION( "A playback list of SFXProfiles or nested SFXPlayLists." );
-      
+
+      // SFXTrack.
+      virtual bool isLooping() const;
+
       // SimDataBlock.
+      bool onAdd();
+      void onRemove();
       virtual bool preload( bool server, String& errorStr );
       virtual void packData( BitStream* stream );
       virtual void unpackData( BitStream* stream );

+ 1 - 1
Engine/source/sfx/sfxSound.h

@@ -143,7 +143,7 @@ class SFXSound : public SFXSource,
       bool isBlocked() const { return ( mVoice && mVoice->getStatus() == SFXStatusBlocked ); }
       
       /// Returns true if this is a continuously streaming source.
-      bool isStreaming() const { return mDescription->mIsStreaming; }
+      bool isStreaming() const { return mDescription ? mDescription->mIsStreaming : false; }
 
       /// Returns true if the source's associated data is ready for playback.
       bool isReady() const;

+ 31 - 31
Engine/source/sfx/sfxSource.cpp

@@ -192,18 +192,18 @@ SFXSource::SFXSource()
      mSavedStatus( SFXStatusNull ),
      mStatusCallback( NULL ),
      mDescription( NULL ),
-     mVolume( 1.f ),
-     mPreFadeVolume( 1.f ),
-     mFadedVolume( 1.f ),
-     mModulativeVolume( 1.f ),
-     mPreAttenuatedVolume( 1.f ),
-     mAttenuatedVolume( 1.f ),
+     mVolume( 1.0f ),
+     mPreFadeVolume( 1.0f ),
+     mFadedVolume( 1.0f ),
+     mModulativeVolume( 1.0f ),
+     mPreAttenuatedVolume( 1.0f ),
+     mAttenuatedVolume( 1.0f ),
      mPriority( 0 ),
-     mModulativePriority( 1.f ),
+     mModulativePriority( 1.0f ),
      mEffectivePriority( 0 ),
      mPitch( 1.f ),
-     mModulativePitch( 1.f ),
-     mEffectivePitch( 1.f ),
+     mModulativePitch( 1.0f ),
+     mEffectivePitch( 1.0f ),
      mTransform( true ),
      mVelocity( 0, 0, 0 ),
      mMinDistance( 1 ),
@@ -213,14 +213,14 @@ SFXSource::SFXSource()
      mConeOutsideVolume( 1 ),
      mDistToListener( 0.f ),
      mTransformScattered( false ),
-     mFadeInTime( 0.f ),
-     mFadeOutTime( 0.f ),
-     mFadeInPoint( -1.f ),
-     mFadeOutPoint( -1.f ),
+     mFadeInTime( 0.0f ),
+     mFadeOutTime( 0.0f ),
+     mFadeInPoint( -1.0f ),
+     mFadeOutPoint( -1.0f ),
      mFadeSegmentType( FadeSegmentNone ),
      mFadeSegmentEase( NULL ),
-     mFadeSegmentStartPoint( 0.f ),
-     mFadeSegmentEndPoint( 0.f ),
+     mFadeSegmentStartPoint( 0.0f ),
+     mFadeSegmentEndPoint( 0.0f ),
      mSavedFadeTime( -1.f ),
      mPlayStartTick( 0 )
 {
@@ -236,17 +236,17 @@ SFXSource::SFXSource( SFXTrack* track, SFXDescription* description )
      mTrack( track ),
      mDescription( description ),
      mVolume( 1.f ),
-     mPreFadeVolume( 1.f ),
-     mFadedVolume( 1.f ),
-     mModulativeVolume( 1.f ),
-     mPreAttenuatedVolume( 1.f ),
-     mAttenuatedVolume( 1.f ),
+     mPreFadeVolume( 1.0f ),
+     mFadedVolume( 1.0f ),
+     mModulativeVolume( 1.0f ),
+     mPreAttenuatedVolume( 1.0f ),
+     mAttenuatedVolume( 1.0f ),
      mPriority( 0 ),
-     mModulativePriority( 1.f ),
+     mModulativePriority( 1.0f ),
      mEffectivePriority( 0 ),
-     mPitch( 1.f ),
-     mModulativePitch( 1.f ),
-     mEffectivePitch( 1.f ),
+     mPitch( 1.0f ),
+     mModulativePitch( 1.0f ),
+     mEffectivePitch( 1.0f ),
      mTransform( true ),
      mVelocity( 0, 0, 0 ),
      mMinDistance( 1 ),
@@ -256,15 +256,15 @@ SFXSource::SFXSource( SFXTrack* track, SFXDescription* description )
      mConeOutsideVolume( 1 ),
      mDistToListener( 0.f ),
      mTransformScattered( false ),
-     mFadeInTime( 0.f ),
-     mFadeOutTime( 0.f ),
-     mFadeInPoint( -1.f ),
-     mFadeOutPoint( -1.f ),
+     mFadeInTime( 0.0f ),
+     mFadeOutTime( 0.0f ),
+     mFadeInPoint( -1.0f ),
+     mFadeOutPoint( -1.0f ),
      mFadeSegmentType( FadeSegmentNone ),
      mFadeSegmentEase( NULL ),
-     mFadeSegmentStartPoint( 0.f ),
-     mFadeSegmentEndPoint( 0.f ),
-     mSavedFadeTime( -1.f ),
+     mFadeSegmentStartPoint( 0.0f ),
+     mFadeSegmentEndPoint( 0.0f ),
+     mSavedFadeTime( -1.0f ),
      mPlayStartTick( 0 )
 {
    VECTOR_SET_ASSOCIATION( mParameters );

BIN
Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_h.png


+ 8 - 0
Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_h_image.asset.taml

@@ -0,0 +1,8 @@
+<ImageAsset
+    canSave="true"
+    canSaveDynamicFields="true"
+    AssetName="pausebutton_h_image"
+    imageFile="@assetFile=pausebutton_h.png"
+    UseMips="true"
+    isHDRImage="false"
+    imageType="Albedo" />

BIN
Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_n.png


+ 8 - 0
Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_n_image.asset.taml

@@ -0,0 +1,8 @@
+<ImageAsset
+    canSave="true"
+    canSaveDynamicFields="true"
+    AssetName="pausebutton_n_image"
+    imageFile="@assetFile=pausebutton_n.png"
+    UseMips="true"
+    isHDRImage="false"
+    imageType="Albedo" />

BIN
Templates/BaseGame/game/tools/worldEditor/images/toolbar/stopbutton_h.png


+ 8 - 0
Templates/BaseGame/game/tools/worldEditor/images/toolbar/stopbutton_h_image.asset.taml

@@ -0,0 +1,8 @@
+<ImageAsset
+    canSave="true"
+    canSaveDynamicFields="true"
+    AssetName="stopbutton_h_image"
+    imageFile="@assetFile=stopbutton_h.png"
+    UseMips="true"
+    isHDRImage="false"
+    imageType="Albedo" />

BIN
Templates/BaseGame/game/tools/worldEditor/images/toolbar/stopbutton_n.png


+ 8 - 0
Templates/BaseGame/game/tools/worldEditor/images/toolbar/stopbutton_n_image.asset.taml

@@ -0,0 +1,8 @@
+<ImageAsset
+    canSave="true"
+    canSaveDynamicFields="true"
+    AssetName="stopbutton_n_image"
+    imageFile="@assetFile=stopbutton_n.png"
+    UseMips="true"
+    isHDRImage="false"
+    imageType="Albedo" />