浏览代码

Hot reload of audio clips

BearishSun 9 年之前
父节点
当前提交
fa03beab16

+ 11 - 1
Source/BansheeCore/Include/BsAudioSource.h

@@ -3,6 +3,7 @@
 #pragma once
 
 #include "BsCorePrerequisites.h"
+#include "BsIResourceListener.h"
 #include "BsVector3.h"
 
 namespace BansheeEngine
@@ -26,7 +27,7 @@ namespace BansheeEngine
 	 * Whether or not an audio source is spatial is controlled by the assigned AudioClip. The volume and the pitch of a
 	 * spatial audio source is controlled by its position and the AudioListener's position/direction/velocity.
 	 */
-	class BS_CORE_EXPORT AudioSource
+	class BS_CORE_EXPORT AudioSource : public IResourceListener
 	{
 	public:
 		virtual ~AudioSource() { }
@@ -138,6 +139,15 @@ namespace BansheeEngine
 	protected:
 		AudioSource();
 
+		/** @copydoc IResourceListener::getListenerResources */
+		void getListenerResources(Vector<HResource>& resources) override;
+
+		/** @copydoc IResourceListener::notifyResourceChanged */
+		void notifyResourceChanged(const HResource& resource) override;
+
+		/** Triggered by the resources system whenever the attached audio clip changed (e.g. was reimported.) */
+		virtual void onClipChanged() { }
+
 		HAudioClip mAudioClip;
 		Vector3 mPosition;
 		Vector3 mVelocity;

+ 3 - 0
Source/BansheeCore/Include/BsMeshCollider.h

@@ -48,6 +48,9 @@ namespace BansheeEngine
 		/** @copydoc IResourceListener::notifyResourceChanged */
 		void notifyResourceChanged(const HResource& resource) override;
 
+		/** 
+		 * Triggered by the resources system whenever the attached collision mesh changed (e.g. was reimported) or loaded. 
+		 */
 		virtual void onMeshChanged() { }
 
 		HPhysicsMesh mMesh;

+ 13 - 0
Source/BansheeCore/Source/BsAudioSource.cpp

@@ -15,6 +15,8 @@ namespace BansheeEngine
 	void AudioSource::setClip(const HAudioClip& clip)
 	{
 		mAudioClip = clip;
+
+		markListenerResourcesDirty();
 	}
 
 	void AudioSource::setPosition(const Vector3& position)
@@ -61,4 +63,15 @@ namespace BansheeEngine
 	{
 		return gAudio().createSource();
 	}
+
+	void AudioSource::getListenerResources(Vector<HResource>& resources)
+	{
+		if (mAudioClip != nullptr)
+			resources.push_back(mAudioClip);
+	}
+
+	void AudioSource::notifyResourceChanged(const HResource& resource)
+	{
+		onClipChanged();
+	}
 }

+ 1 - 0
Source/BansheeCore/Source/BsResources.cpp

@@ -564,6 +564,7 @@ namespace BansheeEngine
 		}
 
 		onResourceModified(handle);
+		ResourceListenerManager::instance().notifyListeners(uuid);
 	}
 
 	Vector<String> Resources::getDependencies(const Path& filePath)

+ 3 - 0
Source/BansheeFMOD/Include/BsFMODAudioSource.h

@@ -64,6 +64,9 @@ namespace BansheeEngine
 		/** Pauses or resumes audio playback due to the global pause setting. */
 		void setGlobalPause(bool pause);
 
+		/** @copydoc IResourceListener::onClipChanged */
+		void onClipChanged() override;
+
 		FMOD::Channel* mChannel;
 		FMOD::Sound* mStreamingSound;
 

+ 16 - 2
Source/BansheeFMOD/Source/BsFMODAudioSource.cpp

@@ -161,8 +161,6 @@ namespace BansheeEngine
 
 		if(mStreamingSound != nullptr)
 		{
-			FMODAudioClip* fmodClip = static_cast<FMODAudioClip*>(mAudioClip.get());
-
 			FMODAudioClip::releaseStreamingSound(mStreamingSound);
 			mStreamingSound = nullptr;
 		}
@@ -232,4 +230,20 @@ namespace BansheeEngine
 
 		return 0.0f;
 	}
+
+	void FMODAudioSource::onClipChanged()
+	{
+		AudioSourceState state = getState();
+		float savedTime = getTime();
+
+		stop();
+
+		setTime(savedTime);
+
+		if (state != AudioSourceState::Stopped)
+			play();
+
+		if (state == AudioSourceState::Paused)
+			pause();
+	}
 }

+ 6 - 0
Source/BansheeOpenAudio/Include/BsOAAudioSource.h

@@ -99,6 +99,12 @@ namespace BansheeEngine
 		/** Fills the provided buffer with streaming data. */
 		bool fillBuffer(UINT32 buffer, AudioDataInfo& info, UINT32 maxNumSamples);
 
+		/** Makes the current audio clip active. Should be called whenever the audio clip changes. */
+		void applyClip();
+
+		/** @copydoc IResourceListener::onClipChanged */
+		void onClipChanged() override;
+
 		Vector<UINT32> mSourceIDs;
 		float mSavedTime;
 		AudioSourceState mState;

+ 50 - 24
Source/BansheeOpenAudio/Source/BsOAAudioSource.cpp

@@ -28,30 +28,7 @@ namespace BansheeEngine
 		Lock(mMutex);
 		AudioSource::setClip(clip);
 
-		auto& contexts = gOAAudio()._getContexts();
-		UINT32 numContexts = (UINT32)contexts.size();
-		for (UINT32 i = 0; i < numContexts; i++)
-		{
-			if (contexts.size() > 1) // If only one context is available it is guaranteed it is always active, so we can avoid setting it
-				alcMakeContextCurrent(contexts[i]);
-
-			alSourcei(mSourceIDs[i], AL_SOURCE_RELATIVE, !is3D());
-
-			if (!requiresStreaming())
-			{
-				UINT32 oaBuffer = 0;
-				if (clip.isLoaded())
-				{
-					OAAudioClip* oaClip = static_cast<OAAudioClip*>(clip.get());
-					oaBuffer = oaClip->_getOpenALBuffer();
-				}
-				
-				alSourcei(mSourceIDs[i], AL_BUFFER, oaBuffer);
-			}
-		}
-
-		// Looping is influenced by streaming mode, so re-apply it in case it changed
-		setIsLooping(mLoop);
+		applyClip();
 	}
 
 	void OAAudioSource::setPosition(const Vector3& position)
@@ -587,6 +564,55 @@ namespace BansheeEngine
 		return true;
 	}
 
+	void OAAudioSource::applyClip()
+	{
+		auto& contexts = gOAAudio()._getContexts();
+		UINT32 numContexts = (UINT32)contexts.size();
+		for (UINT32 i = 0; i < numContexts; i++)
+		{
+			if (contexts.size() > 1) // If only one context is available it is guaranteed it is always active, so we can avoid setting it
+				alcMakeContextCurrent(contexts[i]);
+
+			alSourcei(mSourceIDs[i], AL_SOURCE_RELATIVE, !is3D());
+
+			if (!requiresStreaming())
+			{
+				UINT32 oaBuffer = 0;
+				if (mAudioClip.isLoaded())
+				{
+					OAAudioClip* oaClip = static_cast<OAAudioClip*>(mAudioClip.get());
+					oaBuffer = oaClip->_getOpenALBuffer();
+				}
+
+				alSourcei(mSourceIDs[i], AL_BUFFER, oaBuffer);
+			}
+		}
+
+		// Looping is influenced by streaming mode, so re-apply it in case it changed
+		setIsLooping(mLoop);
+	}
+
+	void OAAudioSource::onClipChanged()
+	{
+		AudioSourceState state = getState();
+		float savedTime = getTime();
+
+		stop();
+
+		{
+			Lock(mMutex);
+			applyClip();
+		}
+
+		setTime(savedTime);
+
+		if (state != AudioSourceState::Stopped)
+			play();
+
+		if (state == AudioSourceState::Paused)
+			pause();
+	}
+
 	bool OAAudioSource::is3D() const
 	{
 		if (!mAudioClip.isLoaded())