Преглед изворни кода

Registration of audio source/listener with OAAudio manager, their creation and reset (multiple contexts)

BearishSun пре 9 година
родитељ
комит
50428d2fe2

+ 1 - 1
Source/BansheeCore/Include/BsAudio.h

@@ -34,7 +34,7 @@ namespace BansheeEngine
 		virtual AudioDevice getActiveDevice() const = 0;
 		
 		virtual AudioDevice getDefaultDevice() const = 0;
-		virtual Vector<AudioDevice> getAllDevices() const = 0;
+		virtual const Vector<AudioDevice>& getAllDevices() const = 0;
 
 	protected:
 		friend class AudioClip;

+ 25 - 4
Source/BansheeOpenAudio/Include/BsOAAudio.h

@@ -37,24 +37,45 @@ namespace BansheeEngine
 		void setActiveDevice(const AudioDevice& device) override;
 
 		/** @copydoc Audio::getActiveDevice */
-		AudioDevice getActiveDevice() const override;
+		AudioDevice getActiveDevice() const override { return mActiveDevice; }
 
 		/** @copydoc Audio::getDefaultDevice */
-		AudioDevice getDefaultDevice() const override;
+		AudioDevice getDefaultDevice() const override { return mDefaultDevice; }
 
 		/** @copydoc Audio::getAllDevices */
-		Vector<AudioDevice> getAllDevices() const override;
+		const Vector<AudioDevice>& getAllDevices() const override { return mAllDevices; };
 
 		/** @name Internal 
 		 *  @{
 		 */
 
-		bool isExtensionSupported(const String& extension) const;
+		bool _isExtensionSupported(const String& extension) const;
+
+		void _registerListener(OAAudioListener* listener);
+		void _unregisterListener(OAAudioListener* listener);
+
+		void _registerSource(OAAudioSource* source);
+		void _unregisterSource(OAAudioSource* source);
+
+		const Vector<ALCcontext*>& _getContexts() const { return mContexts; }
 
 		/** @} */
 
 	private:
+		void rebuildContexts();
+		void clearContexts();
+
+		float mVolume;
+
 		ALCdevice* mDevice;
+		Vector<AudioDevice> mAllDevices;
+		AudioDevice mDefaultDevice;
+		AudioDevice mActiveDevice;
+
+		Vector<OAAudioListener*> mListeners;
+		Vector<ALCcontext*> mContexts;
+		UnorderedSet<OAAudioSource*> mSources;
+
 	};
 
 	/** Provides easier access to OAAudio. */

+ 11 - 0
Source/BansheeOpenAudio/Include/BsOAAudioListener.h

@@ -28,6 +28,17 @@ namespace BansheeEngine
 
 		/** @copydoc AudioListener::setVelocity */
 		void setVelocity(const Vector3& velocity) override;
+
+	private:
+		friend class OAAudio;
+
+		void rebuild();
+
+		inline std::array<float, 6> getOrientation() const;
+		inline void updatePosition();
+		inline void updateOrientation(const std::array<float, 6>& orientation);
+		inline void updateVelocity();
+		inline void updateVolume(float volume);
 	};
 
 	/** @} */

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

@@ -52,6 +52,14 @@ namespace BansheeEngine
 
 		/** @copydoc AudioSource::stop */
 		void stop() override;
+
+	private:
+		friend class OAAudio;
+
+		void clear();
+		void rebuild();
+
+		Vector<UINT32> mSourceIDs;
 	};
 
 	/** @} */

+ 3 - 0
Source/BansheeOpenAudio/Include/BsOAPrerequisites.h

@@ -30,6 +30,9 @@ namespace BansheeEngine
 		UINT32 numChannels; /**< Number of channels. Each channel has its own set of samples. */
 		UINT32 bitDepth; /**< Number of bits per sample. */
 	};
+
+	class OAAudioListener;
+	class OAAudioSource;
 }
 
 /** @addtogroup Plugins

+ 138 - 20
Source/BansheeOpenAudio/Source/BsOAAudio.cpp

@@ -1,29 +1,86 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "BsOAAudio.h"
+#include "BsOAAudioListener.h"
+#include "BsOAAudioSource.h"
+#include "BsMath.h"
 #include "AL\al.h"
 
 namespace BansheeEngine
 {
 	OAAudio::OAAudio()
+		:mVolume(1.0f)
 	{
-		
+		bool enumeratedDevices;
+		if(_isExtensionSupported("ALC_ENUMERATE_ALL_EXT"))
+		{
+			const ALCchar* defaultDevice = alcGetString(nullptr, ALC_DEFAULT_ALL_DEVICES_SPECIFIER);
+			mDefaultDevice.name = toWString(defaultDevice);
+
+			const ALCchar* devices = alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER);
+
+			Vector<wchar_t> deviceName;
+			while(true)
+			{
+				if(*devices == 0)
+				{
+					if (deviceName.size() == 0)
+						break;
+
+					mAllDevices.push_back({ WString(deviceName.data(), deviceName.size()) });
+					deviceName.clear();
+
+					continue;
+				}
+
+				deviceName.push_back(*devices);
+				devices++;
+			}
+
+			enumeratedDevices = true;
+		}
+		else
+		{
+			mAllDevices.push_back({ L"" });
+			enumeratedDevices = false;
+		}
+
+		mActiveDevice = mDefaultDevice;
+
+		String defaultDeviceName = toString(mDefaultDevice.name);
+		if(enumeratedDevices)
+			mDevice = alcOpenDevice(defaultDeviceName.c_str());
+		else
+			mDevice = alcOpenDevice(nullptr);
+
+		if (mDevice == nullptr)
+		{
+			BS_EXCEPT(InternalErrorException, "Failed to open OpenAL device: " + defaultDeviceName);
+			return;
+		}
+
+		rebuildContexts();
 	}
 
 	OAAudio::~OAAudio()
 	{
-		
+		assert(mListeners.size() == 0 && mSources.size() == 0); // Everything should be destroyed at this point
+		clearContexts();
+
+		alcCloseDevice(mDevice);
 	}
 
 	void OAAudio::setVolume(float volume)
 	{
-		// TODO
+		mVolume = Math::clamp01(volume);
+		
+		for (auto& listener : mListeners)
+			listener->rebuild();
 	}
 
 	float OAAudio::getVolume() const
 	{
-		// TODO
-		return 1.0f;
+		return mVolume;
 	}
 
 	void OAAudio::setPaused(bool paused)
@@ -44,33 +101,94 @@ namespace BansheeEngine
 
 	void OAAudio::setActiveDevice(const AudioDevice& device)
 	{
-		// TODO
+		if (mAllDevices.size() == 1)
+			return; // No devices to change to, keep the active device as is
+
+		clearContexts();
+
+		alcCloseDevice(mDevice);
+		mActiveDevice = device;
+
+		String narrowName = toString(device.name);
+		mDevice = alcOpenDevice(narrowName.c_str());
+		if (mDevice == nullptr)
+		{
+			BS_EXCEPT(InternalErrorException, "Failed to open OpenAL device: " + narrowName);
+			return;
+		}
+
+		rebuildContexts();
 	}
 
-	AudioDevice OAAudio::getActiveDevice() const
+	bool OAAudio::_isExtensionSupported(const String& extension) const
 	{
-		// TODO
-		return AudioDevice();
+		if ((extension.length() > 2) && (extension.substr(0, 3) == "ALC"))
+			return alcIsExtensionPresent(mDevice, extension.c_str()) != AL_FALSE;
+		else
+			return alIsExtensionPresent(extension.c_str()) != AL_FALSE;
 	}
 
-	AudioDevice OAAudio::getDefaultDevice() const
+	void OAAudio::_registerListener(OAAudioListener* listener)
 	{
-		// TODO
-		return AudioDevice();
+		mListeners.push_back(listener);
+
+		rebuildContexts();
 	}
 
-	Vector<AudioDevice> OAAudio::getAllDevices() const
+	void OAAudio::_unregisterListener(OAAudioListener* listener)
 	{
-		// TODO
-		return Vector<AudioDevice>();
+		auto iterFind = std::find(mListeners.begin(), mListeners.end(), listener);
+		if (iterFind != mListeners.end())
+			mListeners.erase(iterFind);
+
+		rebuildContexts();
 	}
 
-	bool OAAudio::isExtensionSupported(const String& extension) const
+	void OAAudio::_registerSource(OAAudioSource* source)
 	{
-		if ((extension.length() > 2) && (extension.substr(0, 3) == "ALC"))
-			return alcIsExtensionPresent(mDevice, extension.c_str()) != AL_FALSE;
-		else
-			return alIsExtensionPresent(extension.c_str()) != AL_FALSE;
+		mSources.insert(source);
+	}
+
+	void OAAudio::_unregisterSource(OAAudioSource* source)
+	{
+		mSources.erase(source);
+	}
+
+	void OAAudio::rebuildContexts()
+	{
+		clearContexts();
+
+		UINT32 numListeners = (UINT32)mListeners.size();
+		UINT32 numContexts = numListeners > 1 ? numListeners : 1;
+
+		for (auto& source : mSources)
+			source->clear();
+
+		for(UINT32 i = 0; i < numContexts; i++)
+		{
+			ALCcontext* context = alcCreateContext(mDevice, nullptr);
+			mContexts.push_back(context);
+		}
+
+		// If only one context is available keep it active as an optimization. Audio listeners and sources will avoid
+		// excessive context switching in such case.
+		alcMakeContextCurrent(mContexts[0]);
+
+		for (auto& listener : mListeners)
+			listener->rebuild();
+
+		for (auto& source : mSources)
+			source->rebuild();
+	}
+
+	void OAAudio::clearContexts()
+	{
+		alcMakeContextCurrent(nullptr);
+
+		for (auto& context : mContexts)
+			alcDestroyContext(context);
+
+		mContexts.clear();
 	}
 
 	OAAudio& gOAAudio()

+ 1 - 1
Source/BansheeOpenAudio/Source/BsOAAudioClip.cpp

@@ -79,7 +79,7 @@ namespace BansheeEngine
 		{
 			if (info.bitDepth > 16)
 			{
-				if (gOAAudio().isExtensionSupported("AL_EXT_float32"))
+				if (gOAAudio()._isExtensionSupported("AL_EXT_float32"))
 				{
 					UINT32 bufferSize = info.numSamples * sizeof(float);
 					float* sampleBufferFloat = (float*)bs_stack_alloc(bufferSize);

+ 91 - 6
Source/BansheeOpenAudio/Source/BsOAAudioListener.cpp

@@ -1,44 +1,129 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "BsOAAudioListener.h"
+#include "BsOAAudio.h"
+#include "AL/al.h"
 
 namespace BansheeEngine
 {
 	OAAudioListener::OAAudioListener()
 	{
-		
+		gOAAudio()._registerListener(this);
+		rebuild();
 	}
 
 	OAAudioListener::~OAAudioListener()
 	{
-		
+		gOAAudio()._unregisterListener(this);
 	}
 
 	void OAAudioListener::setPosition(const Vector3& position)
 	{
 		AudioListener::setPosition(position);
 
-		// TODO
+		auto& contexts = gOAAudio()._getContexts();
+		for (auto& context : contexts)
+		{
+			if (contexts.size() > 1) // If only one context is available it is guaranteed it is always active, so we can avoid setting it
+				alcMakeContextCurrent(context);
+
+			updatePosition();
+		}
 	}
 
 	void OAAudioListener::setDirection(const Vector3& direction)
 	{
 		AudioListener::setDirection(direction);
 
-		// TODO
+		std::array<float, 6> orientation = getOrientation();
+		auto& contexts = gOAAudio()._getContexts();
+		for (auto& context : contexts)
+		{
+			if (contexts.size() > 1) // If only one context is available it is guaranteed it is always active, so we can avoid setting it
+				alcMakeContextCurrent(context);
+
+			updateOrientation(orientation);
+		}
 	}
 
 	void OAAudioListener::setUp(const Vector3& up)
 	{
 		AudioListener::setUp(up);
 
-		// TODO
+		std::array<float, 6> orientation = getOrientation();
+		auto& contexts = gOAAudio()._getContexts();
+		for (auto& context : contexts)
+		{
+			if (contexts.size() > 1) // If only one context is available it is guaranteed it is always active, so we can avoid setting it
+				alcMakeContextCurrent(context);
+
+			updateOrientation(orientation);
+		}
 	}
 
 	void OAAudioListener::setVelocity(const Vector3& velocity)
 	{
 		AudioListener::setVelocity(velocity);
 
-		// TODO
+		auto& contexts = gOAAudio()._getContexts();
+		for (auto& context : contexts)
+		{
+			if (contexts.size() > 1) // If only one context is available it is guaranteed it is always active, so we can avoid setting it
+				alcMakeContextCurrent(context);
+
+			updateVelocity();
+		}
+	}
+
+	void OAAudioListener::rebuild()
+	{
+		auto& contexts = gOAAudio()._getContexts();
+
+		float globalVolume = gAudio().getVolume();
+		std::array<float, 6> orientation = getOrientation();
+
+		for (auto& context : contexts)
+		{
+			if (contexts.size() > 1) // If only one context is available it is guaranteed it is always active, so we can avoid setting it
+				alcMakeContextCurrent(context);
+
+			updatePosition();
+			updateOrientation(orientation);
+			updateVelocity();
+			updateVolume(globalVolume);
+		}
+	}
+
+	std::array<float, 6> OAAudioListener::getOrientation() const
+	{
+		return
+		{
+			mDirection.x,
+			mDirection.y,
+			mDirection.z,
+			mUp.x,
+			mUp.y,
+			mUp.z
+		};
+	}
+
+	void OAAudioListener::updatePosition()
+	{
+		alListener3f(AL_POSITION, mPosition.x, mPosition.y, mPosition.z);
+	}
+
+	void OAAudioListener::updateOrientation(const std::array<float, 6>& orientation)
+	{
+		alListenerfv(AL_ORIENTATION, orientation.data());
+	}
+
+	void OAAudioListener::updateVelocity()
+	{
+		alListener3f(AL_VELOCITY, mVelocity.x, mVelocity.y, mVelocity.z);
+	}
+
+	void OAAudioListener::updateVolume(float volume)
+	{
+		alListenerf(AL_GAIN, volume);
 	}
 }

+ 27 - 2
Source/BansheeOpenAudio/Source/BsOAAudioSource.cpp

@@ -1,17 +1,20 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "BsOAAudioSource.h"
+#include "BsOAAudio.h"
+#include "AL\al.h"
 
 namespace BansheeEngine
 {
 	OAAudioSource::OAAudioSource()
 	{
-		
+		gOAAudio()._registerSource(this);
+		rebuild();
 	}
 
 	OAAudioSource::~OAAudioSource()
 	{
-		
+		gOAAudio()._unregisterSource(this);
 	}
 
 	void OAAudioSource::setClip(const HAudioClip& clip)
@@ -97,4 +100,26 @@ namespace BansheeEngine
 
 		// TODO
 	}
+
+	void OAAudioSource::clear()
+	{
+		for (auto& source : mSourceIDs)
+			alSourcei(source, AL_BUFFER, 0);
+
+		alDeleteSources((UINT32)mSourceIDs.size(), mSourceIDs.data());
+		mSourceIDs.clear();
+	}
+
+	void OAAudioSource::rebuild()
+	{
+		auto& contexts = gOAAudio()._getContexts();
+
+		for (auto& context : contexts)
+		{
+			if (contexts.size() > 1) // If only one context is available it is guaranteed it is always active, so we can avoid setting it
+				alcMakeContextCurrent(context);
+
+			// TODO - (Re)apply source settings
+		}
+	}
 }

+ 1 - 2
Source/BansheeUtility/Include/BsException.h

@@ -173,8 +173,7 @@ namespace BansheeEngine
 	 * Macro for throwing exceptions that will automatically fill out function name, file name and line number of the 
 	 * exception.
 	 */
-	// The exception thrown at the end isn't actually ever getting executed, it is just to notify the compiler that execution
-	// won't continue past this point (for example if a function needs to return a value otherwise).
+	// Banshee doesn't actually use exceptions, so we just emulate the unhandled exception handler by crashing the application.
 #ifndef BS_EXCEPT
 #define BS_EXCEPT(type, desc)	\
 		{                           \