From 852ed8f2258c186fd27399be115734d1a1a2d884 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Sat, 21 Oct 2023 23:19:02 +0100 Subject: [PATCH 1/2] 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 --- Engine/source/T3D/assets/SoundAsset.cpp | 295 +++++++++++++++--- Engine/source/T3D/assets/SoundAsset.h | 81 +++-- Engine/source/T3D/fx/explosion.cpp | 2 +- Engine/source/T3D/fx/splash.cpp | 2 +- .../T3D/gameBase/gameConnectionEvents.cpp | 4 +- Engine/source/T3D/sfx/sfxEmitter.cpp | 33 +- Engine/source/T3D/sfx/sfxEmitter.h | 2 +- Engine/source/T3D/shapeBase.cpp | 2 +- Engine/source/T3D/shapeImage.cpp | 4 +- Engine/source/sfx/sfxController.cpp | 13 +- Engine/source/sfx/sfxPlayList.cpp | 247 +++++++++------ Engine/source/sfx/sfxPlayList.h | 46 +-- Engine/source/sfx/sfxSound.h | 2 +- Engine/source/sfx/sfxSource.cpp | 62 ++-- 14 files changed, 550 insertions(+), 245 deletions(-) diff --git a/Engine/source/T3D/assets/SoundAsset.cpp b/Engine/source/T3D/assets/SoundAsset.cpp index dd6ab9a0a..3f3a3bf25 100644 --- a/Engine/source/T3D/assets/SoundAsset.cpp +++ b/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(); + addArray("slots", SFXPlayList::SFXPlaylistSettings::NUM_SLOTS); + addProtectedField("soundFile", TypeAssetLooseFilePath, Offset(mSoundFile, SoundAsset), + &_setSoundFile, &_getSoundFile, SFXPlayList::SFXPlaylistSettings::NUM_SLOTS, "Path to the sound file."); - addProtectedField("soundFile", TypeAssetLooseFilePath, Offset(mSoundFile, SoundAsset), - &setSoundFile, &getSoundFile, "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 = getOwned() ? expandAssetFilePath(mSoundFile) : mSoundPath; + mSoundPath[i] = getOwned() ? expandAssetFilePath(mSoundFile[i]) : mSoundPath[i]; + } + + //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(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(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(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 diff --git a/Engine/source/T3D/assets/SoundAsset.h b/Engine/source/T3D/assets/SoundAsset.h index 63f81194a..ccd10627b 100644 --- a/Engine/source/T3D/assets/SoundAsset.h +++ b/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 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 mSoundResource; @@ -132,17 +150,20 @@ public: virtual void copyTo(SimObject* object); //SFXResource* getSound() { return mSoundResource; } - Resource getSoundResource() { loadSound(); return mSFXProfile.getResource(); } + Resource 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(&mPlaylist) : dynamic_cast(&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(obj)->setSoundFile(data); return false; } - static const char* getSoundFile(void* obj, const char* data) { return static_cast(obj)->getSoundFile(); } + static bool _setSoundFile(void *obj, const char *index, const char *data) { static_cast(obj)->setSoundFile(data, index ? dAtoi(index) : 0); return false; } + static const char* _getSoundFile(void* obj, const char* data) { return static_cast(obj)->getSoundFile(data); } }; DefineConsoleType(TypeSoundAssetPtr, SoundAsset) @@ -175,7 +196,7 @@ DefineConsoleType(TypeSoundAssetId, String) StringTableEntry m##name##Name; \ StringTableEntry m##name##AssetId;\ AssetPtr 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 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\ {\ diff --git a/Engine/source/T3D/fx/explosion.cpp b/Engine/source/T3D/fx/explosion.cpp index d9cfc1367..d4d621cdf 100644 --- a/Engine/source/T3D/fx/explosion.cpp +++ b/Engine/source/T3D/fx/explosion.cpp @@ -1403,7 +1403,7 @@ bool Explosion::explode() resetWorldBox(); } - SFXProfile* sound_prof = mDataBlock->getSoundProfile(); + SFXProfile* sound_prof = static_cast(mDataBlock->getSoundProfile()); if (sound_prof) { soundProfile_clone = sound_prof->cloneAndPerformSubstitutions(ss_object, ss_index); diff --git a/Engine/source/T3D/fx/splash.cpp b/Engine/source/T3D/fx/splash.cpp index 3b8c6da24..07c3a52f3 100644 --- a/Engine/source/T3D/fx/splash.cpp +++ b/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()); diff --git a/Engine/source/T3D/gameBase/gameConnectionEvents.cpp b/Engine/source/T3D/gameBase/gameConnectionEvents.cpp index 184d504bf..a24a80c87 100644 --- a/Engine/source/T3D/gameBase/gameConnectionEvents.cpp +++ b/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()); } diff --git a/Engine/source/T3D/sfx/sfxEmitter.cpp b/Engine/source/T3D/sfx/sfxEmitter.cpp index 2ff998eec..961e3efb1 100644 --- a/Engine/source/T3D/sfx/sfxEmitter.cpp +++ b/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. diff --git a/Engine/source/T3D/sfx/sfxEmitter.h b/Engine/source/T3D/sfx/sfxEmitter.h index 134b6828c..103a92faf 100644 --- a/Engine/source/T3D/sfx/sfxEmitter.h +++ b/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; diff --git a/Engine/source/T3D/shapeBase.cpp b/Engine/source/T3D/shapeBase.cpp index c130b57b4..e6656fb15 100644 --- a/Engine/source/T3D/shapeBase.cpp +++ b/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(); } diff --git a/Engine/source/T3D/shapeImage.cpp b/Engine/source/T3D/shapeImage.cpp index c71c6f938..9ecddfc21 100644 --- a/Engine/source/T3D/shapeImage.cpp +++ b/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::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) { diff --git a/Engine/source/sfx/sfxController.cpp b/Engine/source/sfx/sfxController.cpp index 0de38ad39..2e1c71b7f 100644 --- a/Engine/source/sfx/sfxController.cpp +++ b/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. diff --git a/Engine/source/sfx/sfxPlayList.cpp b/Engine/source/sfx/sfxPlayList.cpp index 4cc6a56e3..b763e4a14 100644 --- a/Engine/source/sfx/sfxPlayList.cpp +++ b/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;iisTempClone()) + { + 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(); diff --git a/Engine/source/sfx/sfxPlayList.h b/Engine/source/sfx/sfxPlayList.h index 1043bf988..8fa0df89c 100644 --- a/Engine/source/sfx/sfxPlayList.h +++ b/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 ); diff --git a/Engine/source/sfx/sfxSound.h b/Engine/source/sfx/sfxSound.h index 364f2ed59..7494e3846 100644 --- a/Engine/source/sfx/sfxSound.h +++ b/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; diff --git a/Engine/source/sfx/sfxSource.cpp b/Engine/source/sfx/sfxSource.cpp index 51e09d795..4a1765c53 100644 --- a/Engine/source/sfx/sfxSource.cpp +++ b/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 ); From 54959f0d194f42ec78cecbb50add8b91e4dc74f7 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Sun, 22 Oct 2023 00:09:19 +0100 Subject: [PATCH 2/2] SFXEmitter play pause and stop buttons (#1115) -Add buttons to the inspector for SFXEmitter that will play pause and stop the sfxEmitter. NOTE: Purely effects the state of the emitter when in the editor this will not effect sfxEmitter functionality in a level. --- Engine/source/T3D/sfx/sfxEmitter.cpp | 146 ++++++++++++++++++ Engine/source/T3D/sfx/sfxEmitter.h | 22 ++- .../images/toolbar/pausebutton_h.png | Bin 0 -> 237 bytes .../toolbar/pausebutton_h_image.asset.taml | 8 + .../images/toolbar/pausebutton_n.png | Bin 0 -> 145 bytes .../toolbar/pausebutton_n_image.asset.taml | 8 + .../images/toolbar/stopbutton_h.png | Bin 0 -> 221 bytes .../toolbar/stopbutton_h_image.asset.taml | 8 + .../images/toolbar/stopbutton_n.png | Bin 0 -> 143 bytes .../toolbar/stopbutton_n_image.asset.taml | 8 + 10 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_h_image.asset.taml create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_n_image.asset.taml create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/stopbutton_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/stopbutton_h_image.asset.taml create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/stopbutton_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/stopbutton_n_image.asset.taml diff --git a/Engine/source/T3D/sfx/sfxEmitter.cpp b/Engine/source/T3D/sfx/sfxEmitter.cpp index 961e3efb1..967da296a 100644 --- a/Engine/source/T3D/sfx/sfxEmitter.cpp +++ b/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() @@ -192,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." ); @@ -366,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; @@ -491,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(); } @@ -1000,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 ) @@ -1114,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" ) @@ -1131,3 +1276,4 @@ DefineEngineMethod( SFXEmitter, getSource, SFXSource*, (),, { return object->getSource(); } + diff --git a/Engine/source/T3D/sfx/sfxEmitter.h b/Engine/source/T3D/sfx/sfxEmitter.h index 103a92faf..067471820 100644 --- a/Engine/source/T3D/sfx/sfxEmitter.h +++ b/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, }; @@ -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(); diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_h.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_h.png new file mode 100644 index 0000000000000000000000000000000000000000..6a02d7d425e3d809fb4d5232fa0018ecdaac59fc GIT binary patch literal 237 zcmeAS@N?(olHy`uVBq!ia0vp^vOp}&!3HE7PPGUDDb50q$YKTtZeb8+WSBKa0w_4& z)5S5wqx0>wjl3-eJg)l%gYO(ZFVpv*va#-zx^OYWar7N6eUtRc?+E&r~@aV7I0`9$uhvc39urE5}@!#yo i{LPp30$zPQ{GUOcU)kBJaUn0zsSKX3elF{r5}E*0lv{8B literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_h_image.asset.taml b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_h_image.asset.taml new file mode 100644 index 000000000..7a643ed81 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_h_image.asset.taml @@ -0,0 +1,8 @@ + diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_n.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_n.png new file mode 100644 index 0000000000000000000000000000000000000000..6fefd102bed1a96f860d016b903e5e3c49fc329f GIT binary patch literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^vOp}&!3HE7PPGUDDb50q$YKTtZeb8+WSBKa0x0P0 z>EamT(V6_`|9^Yty^O+lb7i)cL`0ox@>VMP7rN$TRplZxWz{T63AdmReNk3fFIOyG pJgqOvtNwt+Ed%MTB`2b|7#d~x4Eg@fVFVh-;OXk;vd$@?2>@WxE5HB% literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_n_image.asset.taml b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_n_image.asset.taml new file mode 100644 index 000000000..dc5c3ebb4 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/pausebutton_n_image.asset.taml @@ -0,0 +1,8 @@ + diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/stopbutton_h.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/stopbutton_h.png new file mode 100644 index 0000000000000000000000000000000000000000..bcb3b3f89f5045999a0a867d5d1aa9f9bf26397d GIT binary patch literal 221 zcmeAS@N?(olHy`uVBq!ia0vp^vOp}&!3HE7PPGUDDb50q$YKTtZeb8+WSBKa0w~z; z>EamT(fM}TLEZxj9Ip1g!CRCqCUU><@q6GRd(bq?-T(a9?=cE3kw`wA zV##pAGJbP|=F+M&O@+FRR;v2XYZw%snT9#Psr{$?tCL-7iq*z5p8YO diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/stopbutton_n.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/stopbutton_n.png new file mode 100644 index 0000000000000000000000000000000000000000..c45cd993c3c140d962dac6513df54c9956a07920 GIT binary patch literal 143 zcmeAS@N?(olHy`uVBq!ia0vp^vOp}&!3HE7PPGUDDb50q$YKTtZeb8+WSBKa0x0O{ z>EamT(V6_`|9^Yty^O+lb7i)cL`0ox@_v++qH^?PRQ1myx12}H>J{#~vRg~8TnhAd m*IjC#Ad@?zH_9qSmz9CblgqG_?FT>5Fa}RoKbLh*2~7a#%qboK literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/stopbutton_n_image.asset.taml b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/stopbutton_n_image.asset.taml new file mode 100644 index 000000000..11eed81f7 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/stopbutton_n_image.asset.taml @@ -0,0 +1,8 @@ +