瀏覽代碼

Sfx playlist asset working (#1109)

* GroundWork

-Reverted SFXPlaylist since it is going to be made from an asset now instead.
-Added extra options to soundAssets description.
-SFXPlaylist may need an onAdd function

* Update sfxController.cpp

* SFXPlaylist data

-Added sfxPlaylist init persist fields for the slots to sound asset
-Added logic to fil sfxPlaylist if more than 1 slot is filled

* Update SoundAsset.cpp

to stop git ci complaining, assetImporter........

* Update SoundAsset.h

* sfxPlaylist

-Fix: incomplete type error
-Added onAdd and onRemove to playlist
-SoundAsset getProfile define now returns playlist if the asset is a playlist.

* Update SoundAsset.h

-updated asset array to return playlist or profile depending on what the asset is

* SFXPlaylist working

-SFXPlaylist works
AudioChannelDefault gets its volume set to 0 for some reason and was throwing off making sfxPlaylist inaudible.

Still an exception when closing if using a playlist trips on line 355 of sfxSound

* Update sfxSound.h

* setSoundFile index null fix

* Update SoundAsset.h

* Update SoundAsset.h

* netstream safety in case of a null asset assignment

* Update sfxController.cpp

added safeties around a null playlist trying to play.

* Update with Az's asset err code changes

---------

Co-authored-by: AzaezelX <[email protected]>
marauder2k7 1 年之前
父節點
當前提交
852ed8f225

+ 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());
 
 }
 

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

@@ -106,8 +106,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 +118,9 @@ SFXEmitter::SFXEmitter()
 
 SFXEmitter::~SFXEmitter()
 {
-   mLocalProfile.onRemove();
+   if(mLocalProfile != NULL)
+      mLocalProfile->onRemove();
+
    SFX_DELETE( mSource );
 }
 
@@ -653,7 +654,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 +663,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 +677,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 
@@ -1043,8 +1044,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 +1081,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.

+ 1 - 1
Engine/source/T3D/sfx/sfxEmitter.h

@@ -116,7 +116,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;

+ 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 );