Audio: Fix loading of OpenAL extensions (pointers are context-local)

This commit is contained in:
SirLynix 2023-12-04 00:23:39 +01:00
parent 6059f608c0
commit f75a00efe2
12 changed files with 388 additions and 142 deletions

View File

@ -80,8 +80,6 @@ jobs:
- name: Run unit tests
if: matrix.confs.mode != 'releasedbg'
run: xmake run UnitTests
env:
NAZARA_NO_AUDIO: 1
# Setup installation configuration
- name: Configure xmake for installation

View File

@ -22,7 +22,7 @@ namespace Nz
class NAZARA_AUDIO_API OpenALBuffer final : public AudioBuffer
{
public:
inline OpenALBuffer(std::shared_ptr<AudioDevice> device, OpenALLibrary& library, ALuint bufferId);
inline OpenALBuffer(std::shared_ptr<AudioDevice> device, ALuint bufferId);
OpenALBuffer(const OpenALBuffer&) = delete;
OpenALBuffer(OpenALBuffer&&) = delete;
~OpenALBuffer();
@ -44,7 +44,6 @@ namespace Nz
const OpenALDevice& GetDevice() const;
ALuint m_bufferId;
OpenALLibrary& m_library;
};
}

View File

@ -6,10 +6,9 @@
namespace Nz
{
inline OpenALBuffer::OpenALBuffer(std::shared_ptr<AudioDevice> device, OpenALLibrary& library, ALuint bufferId) :
inline OpenALBuffer::OpenALBuffer(std::shared_ptr<AudioDevice> device, ALuint bufferId) :
AudioBuffer(std::move(device)),
m_bufferId(bufferId),
m_library(library)
m_bufferId(bufferId)
{
}

View File

@ -31,9 +31,13 @@ namespace Nz
Max = SourceLatency
};
using ALFunction = void(*)(void);
class NAZARA_AUDIO_API OpenALDevice : public AudioDevice
{
friend OpenALLibrary;
struct SymbolLoader;
friend SymbolLoader;
public:
OpenALDevice(OpenALLibrary& library, ALCdevice* device);
@ -41,11 +45,18 @@ namespace Nz
OpenALDevice(OpenALDevice&&) = delete;
~OpenALDevice();
bool ClearErrorFlag() const;
std::shared_ptr<AudioBuffer> CreateBuffer() override;
std::shared_ptr<AudioSource> CreateSource() override;
inline bool DidLastCallSucceed() const;
float GetDopplerFactor() const override;
inline ALFunction GetFunctionByIndex(std::size_t funcIndex) const;
float GetGlobalVolume() const override;
inline OpenALLibrary& GetLibrary();
inline const OpenALLibrary& GetLibrary() const;
Vector3f GetListenerDirection(Vector3f* up = nullptr) const override;
Vector3f GetListenerPosition() const override;
Quaternionf GetListenerRotation() const override;
@ -58,6 +69,9 @@ namespace Nz
void MakeContextCurrent() const;
template<typename... Args> void PrintFunctionCall(std::size_t funcIndex, Args... args) const;
bool ProcessErrorFlag() const;
void SetDopplerFactor(float dopplerFactor) override;
void SetGlobalVolume(float volume) override;
void SetListenerDirection(const Vector3f& direction, const Vector3f& up = Vector3f::Up()) override;
@ -70,14 +84,30 @@ namespace Nz
OpenALDevice& operator=(const OpenALDevice&) = delete;
OpenALDevice& operator=(OpenALDevice&&) = delete;
// We give each device its own set of function pointer, even though regular OpenAL extensions are always the same (for a set library).
// This makes it easier to wrap them (for error handling), and extension pointers are device-local anyway.
#define NAZARA_AUDIO_AL_ALC_FUNCTION(name) decltype(&::name) name;
#include <Nazara/Audio/OpenALFunctions.hpp>
private:
EnumArray<AudioFormat, ALenum> m_audioFormatValues;
EnumArray<OpenALExtension, ALenum> m_extensionStatus;
enum class FunctionIndex
{
#define NAZARA_AUDIO_AL_ALC_FUNCTION(name) name,
#include <Nazara/Audio/OpenALFunctions.hpp>
Count
};
std::array<ALFunction, UnderlyingCast(FunctionIndex::Count)> m_originalFunctionPointer;
std::string m_renderer;
std::string m_vendor;
EnumArray<AudioFormat, ALenum> m_audioFormatValues;
EnumArray<OpenALExtension, ALenum> m_extensionStatus;
OpenALLibrary& m_library;
MovablePtr<ALCcontext> m_context;
MovablePtr<ALCdevice> m_device;
mutable bool m_didCollectErrors;
mutable bool m_hadAnyError;
};
}

View File

@ -7,6 +7,30 @@
namespace Nz
{
inline bool OpenALDevice::DidLastCallSucceed() const
{
if (!m_didCollectErrors)
ProcessErrorFlag();
return !m_hadAnyError;
}
inline ALFunction OpenALDevice::GetFunctionByIndex(std::size_t funcIndex) const
{
assert(funcIndex < m_originalFunctionPointer.size());
return m_originalFunctionPointer[funcIndex];
}
inline OpenALLibrary& OpenALDevice::GetLibrary()
{
return m_library;
}
inline const OpenALLibrary& OpenALDevice::GetLibrary() const
{
return m_library;
}
inline bool OpenALDevice::IsExtensionSupported(OpenALExtension extension) const
{
return m_extensionStatus[extension];

View File

@ -44,6 +44,7 @@ namespace Nz
OpenALLibrary& operator=(OpenALLibrary&&) = delete;
#define NAZARA_AUDIO_AL_ALC_FUNCTION(name) decltype(&::name) name;
#define NAZARA_AUDIO_AL_EXT_FUNCTION(name)
#include <Nazara/Audio/OpenALFunctions.hpp>
private:

View File

@ -23,7 +23,7 @@ namespace Nz
class NAZARA_AUDIO_API OpenALSource final : public AudioSource
{
public:
inline OpenALSource(std::shared_ptr<AudioDevice> device, OpenALLibrary& library, ALuint sourceId);
inline OpenALSource(std::shared_ptr<AudioDevice> device, ALuint sourceId);
OpenALSource(const OpenALSource&) = delete;
OpenALSource(OpenALSource&&) = delete;
~OpenALSource();
@ -76,7 +76,6 @@ namespace Nz
std::shared_ptr<OpenALBuffer> m_currentBuffer;
std::vector<std::shared_ptr<OpenALBuffer>> m_queuedBuffers;
ALuint m_sourceId;
OpenALLibrary& m_library;
};
}

View File

@ -6,10 +6,9 @@
namespace Nz
{
inline OpenALSource::OpenALSource(std::shared_ptr<AudioDevice> device, OpenALLibrary& library, ALuint sourceId) :
inline OpenALSource::OpenALSource(std::shared_ptr<AudioDevice> device, ALuint sourceId) :
AudioSource(std::move(device)),
m_sourceId(sourceId),
m_library(library)
m_sourceId(sourceId)
{
}
}

View File

@ -12,18 +12,20 @@ namespace Nz
{
OpenALBuffer::~OpenALBuffer()
{
GetDevice().MakeContextCurrent();
OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
m_library.alDeleteBuffers(1, &m_bufferId);
device.alDeleteBuffers(1, &m_bufferId);
}
UInt64 OpenALBuffer::GetSampleCount() const
{
GetDevice().MakeContextCurrent();
const OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
ALint bits, size;
m_library.alGetBufferi(m_bufferId, AL_BITS, &bits);
m_library.alGetBufferi(m_bufferId, AL_SIZE, &size);
device.alGetBufferi(m_bufferId, AL_BITS, &bits);
device.alGetBufferi(m_bufferId, AL_SIZE, &size);
UInt64 sampleCount = 0;
if (bits != 0)
@ -34,20 +36,22 @@ namespace Nz
UInt64 OpenALBuffer::GetSize() const
{
GetDevice().MakeContextCurrent();
const OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
ALint size;
m_library.alGetBufferi(m_bufferId, AL_SIZE, &size);
device.alGetBufferi(m_bufferId, AL_SIZE, &size);
return SafeCast<UInt64>(size);
}
UInt32 OpenALBuffer::GetSampleRate() const
{
GetDevice().MakeContextCurrent();
const OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
ALint sampleRate;
m_library.alGetBufferi(m_bufferId, AL_FREQUENCY, &sampleRate);
device.alGetBufferi(m_bufferId, AL_FREQUENCY, &sampleRate);
return SafeCast<UInt32>(sampleRate);
}
@ -55,7 +59,7 @@ namespace Nz
bool OpenALBuffer::IsCompatibleWith(const AudioDevice& device) const
{
// OpenAL buffers are shared among contexts and thus devices
return device.GetSubSystemIdentifier() == &m_library;
return device.GetSubSystemIdentifier() == &GetDevice().GetLibrary();
}
bool OpenALBuffer::Reset(AudioFormat format, UInt64 sampleCount, UInt32 sampleRate, const void* samples)
@ -72,11 +76,11 @@ namespace Nz
device.MakeContextCurrent();
// We empty the error stack
while (m_library.alGetError() != AL_NO_ERROR);
while (device.alGetError() != AL_NO_ERROR);
m_library.alBufferData(m_bufferId, alFormat, samples, SafeCast<ALsizei>(sampleCount * sizeof(Int16)), SafeCast<ALsizei>(sampleRate));
device.alBufferData(m_bufferId, alFormat, samples, SafeCast<ALsizei>(sampleCount * sizeof(Int16)), SafeCast<ALsizei>(sampleRate));
if (ALenum lastError = m_library.alGetError(); lastError != AL_NO_ERROR)
if (ALenum lastError = device.alGetError(); lastError != AL_NO_ERROR)
{
NazaraErrorFmt("failed to reset OpenAL buffer: {0}", std::to_string(lastError));
return false;

View File

@ -7,29 +7,134 @@
#include <Nazara/Audio/OpenALLibrary.hpp>
#include <Nazara/Audio/OpenALSource.hpp>
#include <Nazara/Audio/OpenALUtils.hpp>
#include <Nazara/Core/Log.hpp>
#include <NazaraUtils/Algorithm.hpp>
#include <array>
#include <cstring>
#include <stdexcept>
#include <Nazara/Audio/Debug.hpp>
namespace Nz
{
namespace
namespace NAZARA_ANONYMOUS_NAMESPACE
{
thread_local ALCcontext* s_currentContext;
constexpr std::array s_functionNames = {
#define NAZARA_AUDIO_AL_ALC_FUNCTION(name) #name,
#include <Nazara/Audio/OpenALFunctions.hpp>
};
thread_local const OpenALDevice* s_currentALDevice;
template<typename FuncType, std::size_t FuncIndex, typename>
struct ALWrapper;
template<typename FuncType, std::size_t FuncIndex, typename Ret, typename... Args>
struct ALWrapper<FuncType, FuncIndex, Ret(AL_APIENTRY*)(Args...)>
{
static auto WrapErrorHandling()
{
return [](Args... args) -> Ret
{
const OpenALDevice* device = s_currentALDevice; //< pay TLS cost once
assert(device);
FuncType funcPtr = reinterpret_cast<FuncType>(device->GetFunctionByIndex(FuncIndex));
if constexpr (std::is_same_v<Ret, void>)
{
funcPtr(args...);
if (device->ProcessErrorFlag())
device->PrintFunctionCall(FuncIndex, args...);
}
else
{
Ret r = funcPtr(args...);
if (device->ProcessErrorFlag())
device->PrintFunctionCall(FuncIndex, args...);
return r;
}
};
}
};
}
struct OpenALDevice::SymbolLoader
{
SymbolLoader(OpenALDevice& parent) :
device(parent)
{
}
template<typename FuncType, std::size_t FuncIndex, bool ContextFunction, typename Func>
bool Load(Func& func, const char* funcName, FuncType libraryPtr)
{
NAZARA_USE_ANONYMOUS_NAMESPACE
ALFunction originalFuncPtr;
if constexpr (ContextFunction)
originalFuncPtr = BitCast<ALFunction>(device.m_library.alcGetProcAddress(device.m_device, funcName));
else
originalFuncPtr = BitCast<ALFunction>(device.m_library.alGetProcAddress(funcName));
// Fallback in case of faulty OpenAL implementations not returning core functions through alGetProcAddress/alcGetProcAddress
if (!originalFuncPtr)
originalFuncPtr = reinterpret_cast<ALFunction>(libraryPtr);
func = reinterpret_cast<FuncType>(originalFuncPtr);
if (func && wrapErrorHandling)
{
if constexpr (
FuncIndex != UnderlyingCast(FunctionIndex::alGetError) && //< Prevent infinite recursion
FuncIndex != UnderlyingCast(FunctionIndex::alcCloseDevice) && //< alcDestroyContext is called with no context
FuncIndex != UnderlyingCast(FunctionIndex::alcDestroyContext)) //< alcDestroyContext is called with no context
{
using Wrapper = ALWrapper<FuncType, FuncIndex, FuncType>;
func = Wrapper::WrapErrorHandling();
}
}
device.m_originalFunctionPointer[FuncIndex] = originalFuncPtr;
return func != nullptr;
}
OpenALDevice& device;
bool wrapErrorHandling = false;
};
OpenALDevice::OpenALDevice(OpenALLibrary& library, ALCdevice* device) :
m_library(library),
m_device(device)
{
NAZARA_USE_ANONYMOUS_NAMESPACE
m_context = m_library.alcCreateContext(device, nullptr);
if (!m_context)
throw std::runtime_error("failed to create OpenAL context");
MakeContextCurrent();
// Don't use MakeContextCurrent as device pointers are not loaded yet
if (m_library.alcMakeContextCurrent(m_context) != AL_TRUE)
throw std::runtime_error("failed to activate OpenAL context");
m_renderer = reinterpret_cast<const char*>(m_library.alGetString(AL_RENDERER));
m_vendor = reinterpret_cast<const char*>(m_library.alGetString(AL_VENDOR));
s_currentALDevice = this;
SymbolLoader loader(*this);
#ifdef NAZARA_DEBUG
loader.wrapErrorHandling = true;
#endif
#define NAZARA_AUDIO_AL_FUNCTION(name) loader.Load<decltype(&::name), UnderlyingCast(FunctionIndex:: name), false>(name, #name, library.name);
#define NAZARA_AUDIO_ALC_FUNCTION(name) loader.Load<decltype(&::name), UnderlyingCast(FunctionIndex:: name), true>(name, #name, library.name);
#define NAZARA_AUDIO_AL_EXT_FUNCTION(name) loader.Load<decltype(&::name), UnderlyingCast(FunctionIndex:: name), false>(name, #name, nullptr);
#include <Nazara/Audio/OpenALFunctions.hpp>
m_renderer = reinterpret_cast<const char*>(alGetString(AL_RENDERER));
m_vendor = reinterpret_cast<const char*>(alGetString(AL_VENDOR));
// We complete the formats table
m_audioFormatValues.fill(0);
@ -40,13 +145,13 @@ namespace Nz
// "The presence of an enum value does not guarantee the applicability of an extension to the current context."
if (library.alIsExtensionPresent("AL_EXT_MCFORMATS"))
{
m_audioFormatValues[AudioFormat::I16_Quad] = m_library.alGetEnumValue("AL_FORMAT_QUAD16");
m_audioFormatValues[AudioFormat::I16_5_1] = m_library.alGetEnumValue("AL_FORMAT_51CHN16");
m_audioFormatValues[AudioFormat::I16_6_1] = m_library.alGetEnumValue("AL_FORMAT_61CHN16");
m_audioFormatValues[AudioFormat::I16_7_1] = m_library.alGetEnumValue("AL_FORMAT_71CHN16");
m_audioFormatValues[AudioFormat::I16_Quad] = alGetEnumValue("AL_FORMAT_QUAD16");
m_audioFormatValues[AudioFormat::I16_5_1] = alGetEnumValue("AL_FORMAT_51CHN16");
m_audioFormatValues[AudioFormat::I16_6_1] = alGetEnumValue("AL_FORMAT_61CHN16");
m_audioFormatValues[AudioFormat::I16_7_1] = alGetEnumValue("AL_FORMAT_71CHN16");
}
else if (library.alIsExtensionPresent("AL_LOKI_quadriphonic"))
m_audioFormatValues[AudioFormat::I16_Quad] = m_library.alGetEnumValue("AL_FORMAT_QUAD16_LOKI");
m_audioFormatValues[AudioFormat::I16_Quad] = alGetEnumValue("AL_FORMAT_QUAD16_LOKI");
m_extensionStatus.fill(false);
if (library.alIsExtensionPresent("AL_SOFT_source_latency"))
@ -57,49 +162,65 @@ namespace Nz
OpenALDevice::~OpenALDevice()
{
NAZARA_USE_ANONYMOUS_NAMESPACE
MakeContextCurrent();
m_library.alcDestroyContext(m_context);
m_library.alcCloseDevice(m_device);
alcDestroyContext(m_context);
alcCloseDevice(m_device);
if (s_currentContext == m_context)
s_currentContext = nullptr;
if (s_currentALDevice == this)
s_currentALDevice = nullptr;
}
bool OpenALDevice::ClearErrorFlag() const
{
NAZARA_USE_ANONYMOUS_NAMESPACE
assert(s_currentALDevice == this);
alGetError();
m_didCollectErrors = false;
m_hadAnyError = false;
return true;
}
std::shared_ptr<AudioBuffer> OpenALDevice::CreateBuffer()
{
MakeContextCurrent();
m_library.alGetError(); // Clear error flags
ClearErrorFlag();
ALuint bufferId = 0;
m_library.alGenBuffers(1, &bufferId);
alGenBuffers(1, &bufferId);
if (ALenum lastError = m_library.alGetError(); lastError != AL_NO_ERROR)
if (!DidLastCallSucceed())
{
NazaraErrorFmt("failed to create OpenAL buffer: {0}", TranslateOpenALError(lastError));
NazaraError("failed to create OpenAL buffer");
return {};
}
return std::make_shared<OpenALBuffer>(shared_from_this(), m_library, bufferId);
return std::make_shared<OpenALBuffer>(shared_from_this(), bufferId);
}
std::shared_ptr<AudioSource> OpenALDevice::CreateSource()
{
MakeContextCurrent();
m_library.alGetError(); // Clear error flags
ClearErrorFlag();
ALuint sourceId = 0;
m_library.alGenSources(1, &sourceId);
alGenSources(1, &sourceId);
if (ALenum lastError = m_library.alGetError(); lastError != AL_NO_ERROR)
if (!DidLastCallSucceed())
{
NazaraErrorFmt("failed to create OpenAL source: {0}", TranslateOpenALError(lastError));
NazaraError("failed to create OpenAL buffer");
return {};
}
return std::make_shared<OpenALSource>(shared_from_this(), m_library, sourceId);
return std::make_shared<OpenALSource>(shared_from_this(), sourceId);
}
/*!
@ -110,7 +231,7 @@ namespace Nz
{
MakeContextCurrent();
return m_library.alGetFloat(AL_DOPPLER_FACTOR);
return alGetFloat(AL_DOPPLER_FACTOR);
}
/*!
@ -122,7 +243,7 @@ namespace Nz
MakeContextCurrent();
ALfloat gain = 0.f;
m_library.alGetListenerf(AL_GAIN, &gain);
alGetListenerf(AL_GAIN, &gain);
return gain;
}
@ -140,7 +261,7 @@ namespace Nz
MakeContextCurrent();
ALfloat orientation[6];
m_library.alGetListenerfv(AL_ORIENTATION, orientation);
alGetListenerfv(AL_ORIENTATION, orientation);
if (up)
(*up) = Vector3f(orientation[3], orientation[4], orientation[5]);
@ -159,7 +280,7 @@ namespace Nz
MakeContextCurrent();
Vector3f position;
m_library.alGetListenerfv(AL_POSITION, &position.x);
alGetListenerfv(AL_POSITION, &position.x);
return position;
}
@ -177,7 +298,7 @@ namespace Nz
MakeContextCurrent();
ALfloat orientation[6];
m_library.alGetListenerfv(AL_ORIENTATION, orientation);
alGetListenerfv(AL_ORIENTATION, orientation);
Vector3f forward(orientation[0], orientation[1], orientation[2]);
Vector3f up(orientation[3], orientation[4], orientation[5]);
@ -196,20 +317,68 @@ namespace Nz
MakeContextCurrent();
Vector3f velocity;
m_library.alGetListenerfv(AL_VELOCITY, &velocity.x);
alGetListenerfv(AL_VELOCITY, &velocity.x);
return velocity;
}
void OpenALDevice::MakeContextCurrent() const
{
if (s_currentContext != m_context)
NAZARA_USE_ANONYMOUS_NAMESPACE
if (s_currentALDevice != this)
{
m_library.alcMakeContextCurrent(m_context);
s_currentContext = m_context;
s_currentALDevice = this;
}
}
template<typename... Args>
void OpenALDevice::PrintFunctionCall(std::size_t funcIndex, Args... args) const
{
NAZARA_USE_ANONYMOUS_NAMESPACE
std::stringstream ss;
ss << s_functionNames[funcIndex] << "(";
if constexpr (sizeof...(args) > 0)
{
bool first = true;
auto PrintParam = [&](auto value)
{
if (!first)
ss << ", ";
ss << +value;
first = false;
};
(PrintParam(args), ...);
}
ss << ")";
NazaraDebug(ss.str());
}
bool OpenALDevice::ProcessErrorFlag() const
{
NAZARA_USE_ANONYMOUS_NAMESPACE
assert(s_currentALDevice == this);
bool hasAnyError = false;
if (ALuint lastError = alGetError(); lastError != AL_NO_ERROR)
{
hasAnyError = true;
NazaraErrorFmt("OpenAL error: {0}", TranslateOpenALError(lastError));
}
m_didCollectErrors = true;
m_hadAnyError = hasAnyError;
return hasAnyError;
}
/*!
* \brief Gets the speed of sound
* \return Speed of sound
@ -218,7 +387,7 @@ namespace Nz
{
MakeContextCurrent();
return m_library.alGetFloat(AL_SPEED_OF_SOUND);
return alGetFloat(AL_SPEED_OF_SOUND);
}
const void* OpenALDevice::GetSubSystemIdentifier() const
@ -249,7 +418,7 @@ namespace Nz
{
MakeContextCurrent();
m_library.alDopplerFactor(dopplerFactor);
alDopplerFactor(dopplerFactor);
}
/*!
@ -261,7 +430,7 @@ namespace Nz
{
MakeContextCurrent();
m_library.alListenerf(AL_GAIN, volume);
alListenerf(AL_GAIN, volume);
}
/*!
@ -282,7 +451,7 @@ namespace Nz
up.x, up.y, up.z
};
m_library.alListenerfv(AL_ORIENTATION, orientation);
alListenerfv(AL_ORIENTATION, orientation);
}
/*!
@ -296,7 +465,7 @@ namespace Nz
{
MakeContextCurrent();
m_library.alListenerfv(AL_POSITION, &position.x);
alListenerfv(AL_POSITION, &position.x);
}
/*!
@ -310,7 +479,7 @@ namespace Nz
{
MakeContextCurrent();
m_library.alListenerfv(AL_VELOCITY, &velocity.x);
alListenerfv(AL_VELOCITY, &velocity.x);
}
/*!
@ -322,6 +491,6 @@ namespace Nz
{
MakeContextCurrent();
m_library.alSpeedOfSound(speed);
alSpeedOfSound(speed);
}
}

View File

@ -23,13 +23,6 @@ namespace Nz
auto PostLoad = [&]
{
// Load ext
#define NAZARA_AUDIO_AL_ALC_FUNCTION(name)
#define NAZARA_AUDIO_AL_EXT_BEGIN(ext) if (alIsExtensionPresent(#ext)) {
#define NAZARA_AUDIO_AL_EXT_END() }
#define NAZARA_AUDIO_AL_EXT_FUNCTION(name) name = reinterpret_cast<decltype(&::name)>(alGetProcAddress(#name));
#include <Nazara/Audio/OpenALFunctions.hpp>
m_hasCaptureSupport = alcIsExtensionPresent(nullptr, "ALC_EXT_CAPTURE");
m_isLoaded = true;
@ -79,8 +72,6 @@ namespace Nz
try
{
#define NAZARA_AUDIO_AL_ALC_FUNCTION(name) name = reinterpret_cast<decltype(&::name)>(LoadSymbol(#name, false));
#define NAZARA_AUDIO_AL_EXT_BEGIN(name)
#define NAZARA_AUDIO_AL_EXT_END(name)
#define NAZARA_AUDIO_AL_EXT_FUNCTION(name)
#include <Nazara/Audio/OpenALFunctions.hpp>
}
@ -104,8 +95,6 @@ namespace Nz
// Load core
#define NAZARA_AUDIO_AL_ALC_FUNCTION(name) name = &::name;
#define NAZARA_AUDIO_AL_EXT_BEGIN(name)
#define NAZARA_AUDIO_AL_EXT_END(name)
#define NAZARA_AUDIO_AL_EXT_FUNCTION(name)
#include <Nazara/Audio/OpenALFunctions.hpp>
@ -142,6 +131,7 @@ namespace Nz
return;
#define NAZARA_AUDIO_AL_ALC_FUNCTION(name) name = nullptr;
#define NAZARA_AUDIO_AL_EXT_FUNCTION(name)
#include <Nazara/Audio/OpenALFunctions.hpp>
m_library.Unload();

View File

@ -15,63 +15,72 @@ namespace Nz
{
OpenALSource::~OpenALSource()
{
GetDevice().MakeContextCurrent();
OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
m_library.alDeleteSources(1, &m_sourceId);
device.alDeleteSources(1, &m_sourceId);
}
void OpenALSource::EnableLooping(bool loop)
{
GetDevice().MakeContextCurrent();
m_library.alSourcei(m_sourceId, AL_LOOPING, loop);
OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
device.alSourcei(m_sourceId, AL_LOOPING, loop);
}
void OpenALSource::EnableSpatialization(bool spatialization)
{
GetDevice().MakeContextCurrent();
m_library.alSourcei(m_sourceId, AL_SOURCE_RELATIVE, !spatialization);
OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
device.alSourcei(m_sourceId, AL_SOURCE_RELATIVE, !spatialization);
}
float OpenALSource::GetAttenuation() const
{
GetDevice().MakeContextCurrent();
const OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
ALfloat attenuation;
m_library.alGetSourcefv(m_sourceId, AL_ROLLOFF_FACTOR, &attenuation);
device.alGetSourcefv(m_sourceId, AL_ROLLOFF_FACTOR, &attenuation);
return attenuation;
}
float OpenALSource::GetMinDistance() const
{
GetDevice().MakeContextCurrent();
const OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
ALfloat minDistance;
m_library.alGetSourcefv(m_sourceId, AL_REFERENCE_DISTANCE, &minDistance);
device.alGetSourcefv(m_sourceId, AL_REFERENCE_DISTANCE, &minDistance);
return minDistance;
}
float OpenALSource::GetPitch() const
{
GetDevice().MakeContextCurrent();
const OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
ALfloat pitch;
m_library.alGetSourcefv(m_sourceId, AL_PITCH, &pitch);
device.alGetSourcefv(m_sourceId, AL_PITCH, &pitch);
return pitch;
}
Time OpenALSource::GetPlayingOffset() const
{
GetDevice().MakeContextCurrent();
const OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
#ifdef AL_SOFT_source_latency
if (GetDevice().IsExtensionSupported(OpenALExtension::SourceLatency))
{
// alGetSourcedvSOFT has extra precision thanks to double
ALdouble playingOffset;
m_library.alGetSourcedvSOFT(m_sourceId, AL_SEC_OFFSET, &playingOffset);
device.alGetSourcedvSOFT(m_sourceId, AL_SEC_OFFSET, &playingOffset);
return Time::Seconds(playingOffset);
}
@ -79,7 +88,7 @@ namespace Nz
#endif
{
ALfloat playingOffset;
m_library.alGetSourcefv(m_sourceId, AL_SEC_OFFSET, &playingOffset);
device.alGetSourcefv(m_sourceId, AL_SEC_OFFSET, &playingOffset);
return Time::Seconds(playingOffset);
}
@ -87,20 +96,23 @@ namespace Nz
Vector3f OpenALSource::GetPosition() const
{
GetDevice().MakeContextCurrent();
const OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
Vector3f position;
m_library.alGetSourcefv(m_sourceId, AL_POSITION, &position.x);
device.alGetSourcefv(m_sourceId, AL_POSITION, &position.x);
return position;
}
UInt32 OpenALSource::GetSampleOffset() const
{
GetDevice().MakeContextCurrent();
const OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
ALint samples = 0;
m_library.alGetSourcei(m_sourceId, AL_SAMPLE_OFFSET, &samples);
device.alGetSourcei(m_sourceId, AL_SAMPLE_OFFSET, &samples);
return SafeCast<UInt32>(samples);
}
@ -111,10 +123,11 @@ namespace Nz
#ifdef AL_SOFT_source_latency
if (GetDevice().IsExtensionSupported(OpenALExtension::SourceLatency))
{
GetDevice().MakeContextCurrent();
const OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
std::array<ALint64SOFT, 2> values;
m_library.alGetSourcei64vSOFT(m_sourceId, AL_SAMPLE_OFFSET_LATENCY_SOFT, values.data());
device.alGetSourcei64vSOFT(m_sourceId, AL_SAMPLE_OFFSET_LATENCY_SOFT, values.data());
offsetWithLatency.sampleOffset = ((values[0] & 0xFFFFFFFF00000000) >> 32) * 1'000;
offsetWithLatency.sourceLatency = Time::Nanoseconds(values[1] / 1'000);
@ -132,20 +145,22 @@ namespace Nz
Vector3f OpenALSource::GetVelocity() const
{
GetDevice().MakeContextCurrent();
const OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
Vector3f velocity;
m_library.alGetSourcefv(m_sourceId, AL_VELOCITY, &velocity.x);
device.alGetSourcefv(m_sourceId, AL_VELOCITY, &velocity.x);
return velocity;
}
SoundStatus OpenALSource::GetStatus() const
{
GetDevice().MakeContextCurrent();
const OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
ALint state;
m_library.alGetSourcei(m_sourceId, AL_SOURCE_STATE, &state);
device.alGetSourcei(m_sourceId, AL_SOURCE_STATE, &state);
switch (state)
{
@ -168,30 +183,33 @@ namespace Nz
float OpenALSource::GetVolume() const
{
GetDevice().MakeContextCurrent();
const OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
ALfloat volume;
m_library.alGetSourcefv(m_sourceId, AL_GAIN, &volume);
device.alGetSourcefv(m_sourceId, AL_GAIN, &volume);
return volume;
}
bool OpenALSource::IsLooping() const
{
GetDevice().MakeContextCurrent();
const OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
ALint looping;
m_library.alGetSourcei(m_sourceId, AL_LOOPING, &looping);
device.alGetSourcei(m_sourceId, AL_LOOPING, &looping);
return looping == AL_TRUE;
}
bool OpenALSource::IsSpatializationEnabled() const
{
GetDevice().MakeContextCurrent();
const OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
ALint relative;
m_library.alGetSourcei(m_sourceId, AL_SOURCE_RELATIVE, &relative);
device.alGetSourcei(m_sourceId, AL_SOURCE_RELATIVE, &relative);
return relative == AL_FALSE;
}
@ -203,33 +221,38 @@ namespace Nz
std::shared_ptr<OpenALBuffer> newBuffer = std::static_pointer_cast<OpenALBuffer>(std::move(audioBuffer));
GetDevice().MakeContextCurrent();
OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
ALuint bufferId = newBuffer->GetBufferId();
m_library.alSourceQueueBuffers(m_sourceId, 1, &bufferId);
device.alSourceQueueBuffers(m_sourceId, 1, &bufferId);
m_queuedBuffers.emplace_back(std::move(newBuffer));
}
void OpenALSource::Pause()
{
GetDevice().MakeContextCurrent();
OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
m_library.alSourcePause(m_sourceId);
device.alSourcePause(m_sourceId);
}
void OpenALSource::Play()
{
GetDevice().MakeContextCurrent();
OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
m_library.alSourcePlay(m_sourceId);
device.alSourcePlay(m_sourceId);
}
void OpenALSource::SetAttenuation(float attenuation)
{
GetDevice().MakeContextCurrent();
OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
m_library.alSourcef(m_sourceId, AL_ROLLOFF_FACTOR, attenuation);
device.alSourcef(m_sourceId, AL_ROLLOFF_FACTOR, attenuation);
}
void OpenALSource::SetBuffer(std::shared_ptr<AudioBuffer> audioBuffer)
@ -238,90 +261,100 @@ namespace Nz
std::shared_ptr<OpenALBuffer> newBuffer = std::static_pointer_cast<OpenALBuffer>(std::move(audioBuffer));
GetDevice().MakeContextCurrent();
OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
if (newBuffer)
m_library.alSourcei(m_sourceId, AL_BUFFER, newBuffer->GetBufferId());
device.alSourcei(m_sourceId, AL_BUFFER, newBuffer->GetBufferId());
else
m_library.alSourcei(m_sourceId, AL_BUFFER, AL_NONE);
device.alSourcei(m_sourceId, AL_BUFFER, AL_NONE);
m_currentBuffer = std::move(newBuffer);
}
void OpenALSource::SetMinDistance(float minDistance)
{
GetDevice().MakeContextCurrent();
OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
m_library.alSourcef(m_sourceId, AL_REFERENCE_DISTANCE, minDistance);
device.alSourcef(m_sourceId, AL_REFERENCE_DISTANCE, minDistance);
}
void OpenALSource::SetPitch(float pitch)
{
GetDevice().MakeContextCurrent();
OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
m_library.alSourcef(m_sourceId, AL_PITCH, pitch);
device.alSourcef(m_sourceId, AL_PITCH, pitch);
}
void OpenALSource::SetPlayingOffset(Time offset)
{
GetDevice().MakeContextCurrent();
OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
#ifdef AL_SOFT_source_latency
if (GetDevice().IsExtensionSupported(OpenALExtension::SourceLatency))
// alGetSourcedvSOFT has extra precision thanks to double
m_library.alSourcedSOFT(m_sourceId, AL_SEC_OFFSET, offset.AsSeconds<ALdouble>());
device.alSourcedSOFT(m_sourceId, AL_SEC_OFFSET, offset.AsSeconds<ALdouble>());
else
#endif
m_library.alSourcef(m_sourceId, AL_SEC_OFFSET, offset.AsSeconds<ALfloat>());
device.alSourcef(m_sourceId, AL_SEC_OFFSET, offset.AsSeconds<ALfloat>());
}
void OpenALSource::SetPosition(const Vector3f& position)
{
GetDevice().MakeContextCurrent();
OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
m_library.alSource3f(m_sourceId, AL_POSITION, position.x, position.y, position.z);
device.alSource3f(m_sourceId, AL_POSITION, position.x, position.y, position.z);
}
void OpenALSource::SetSampleOffset(UInt32 offset)
{
GetDevice().MakeContextCurrent();
OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
m_library.alSourcei(m_sourceId, AL_SAMPLE_OFFSET, offset);
device.alSourcei(m_sourceId, AL_SAMPLE_OFFSET, offset);
}
void OpenALSource::SetVelocity(const Vector3f& velocity)
{
GetDevice().MakeContextCurrent();
OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
m_library.alSource3f(m_sourceId, AL_VELOCITY, velocity.x, velocity.y, velocity.z);
device.alSource3f(m_sourceId, AL_VELOCITY, velocity.x, velocity.y, velocity.z);
}
void OpenALSource::SetVolume(float volume)
{
GetDevice().MakeContextCurrent();
OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
m_library.alSourcef(m_sourceId, AL_GAIN, volume);
device.alSourcef(m_sourceId, AL_GAIN, volume);
}
void OpenALSource::Stop()
{
GetDevice().MakeContextCurrent();
OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
m_library.alSourceStop(m_sourceId);
device.alSourceStop(m_sourceId);
}
std::shared_ptr<AudioBuffer> OpenALSource::TryUnqueueProcessedBuffer()
{
GetDevice().MakeContextCurrent();
OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
ALint processedCount = 0;
m_library.alGetSourcei(m_sourceId, AL_BUFFERS_PROCESSED, &processedCount);
device.alGetSourcei(m_sourceId, AL_BUFFERS_PROCESSED, &processedCount);
if (processedCount == 0)
return {};
ALuint bufferId;
m_library.alSourceUnqueueBuffers(m_sourceId, 1, &bufferId);
device.alSourceUnqueueBuffers(m_sourceId, 1, &bufferId);
auto it = std::find_if(m_queuedBuffers.begin(), m_queuedBuffers.end(), [=](const std::shared_ptr<OpenALBuffer>& alBuffer)
{
@ -337,13 +370,14 @@ namespace Nz
void OpenALSource::UnqueueAllBuffers()
{
GetDevice().MakeContextCurrent();
OpenALDevice& device = GetDevice();
device.MakeContextCurrent();
ALint queuedBufferCount = 0;
m_library.alGetSourcei(m_sourceId, AL_BUFFERS_QUEUED, &queuedBufferCount);
device.alGetSourcei(m_sourceId, AL_BUFFERS_QUEUED, &queuedBufferCount);
StackArray<ALuint> buffers = NazaraStackArrayNoInit(ALuint, queuedBufferCount);
m_library.alSourceUnqueueBuffers(m_sourceId, queuedBufferCount, buffers.data());
device.alSourceUnqueueBuffers(m_sourceId, queuedBufferCount, buffers.data());
m_queuedBuffers.clear();
}