|
@@ -1,29 +1,86 @@
|
|
|
//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
|
|
//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
|
|
|
//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
|
|
//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
|
|
|
#include "BsOAAudio.h"
|
|
#include "BsOAAudio.h"
|
|
|
|
|
+#include "BsOAAudioListener.h"
|
|
|
|
|
+#include "BsOAAudioSource.h"
|
|
|
|
|
+#include "BsMath.h"
|
|
|
#include "AL\al.h"
|
|
#include "AL\al.h"
|
|
|
|
|
|
|
|
namespace BansheeEngine
|
|
namespace BansheeEngine
|
|
|
{
|
|
{
|
|
|
OAAudio::OAAudio()
|
|
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()
|
|
OAAudio::~OAAudio()
|
|
|
{
|
|
{
|
|
|
-
|
|
|
|
|
|
|
+ assert(mListeners.size() == 0 && mSources.size() == 0); // Everything should be destroyed at this point
|
|
|
|
|
+ clearContexts();
|
|
|
|
|
+
|
|
|
|
|
+ alcCloseDevice(mDevice);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void OAAudio::setVolume(float volume)
|
|
void OAAudio::setVolume(float volume)
|
|
|
{
|
|
{
|
|
|
- // TODO
|
|
|
|
|
|
|
+ mVolume = Math::clamp01(volume);
|
|
|
|
|
+
|
|
|
|
|
+ for (auto& listener : mListeners)
|
|
|
|
|
+ listener->rebuild();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
float OAAudio::getVolume() const
|
|
float OAAudio::getVolume() const
|
|
|
{
|
|
{
|
|
|
- // TODO
|
|
|
|
|
- return 1.0f;
|
|
|
|
|
|
|
+ return mVolume;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void OAAudio::setPaused(bool paused)
|
|
void OAAudio::setPaused(bool paused)
|
|
@@ -44,33 +101,94 @@ namespace BansheeEngine
|
|
|
|
|
|
|
|
void OAAudio::setActiveDevice(const AudioDevice& device)
|
|
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()
|
|
OAAudio& gOAAudio()
|