OpenAL: load DLL dynamically

This commit is contained in:
Michael Maltese 2017-06-25 23:47:15 -07:00
parent 18e70cdf91
commit c882d52f4a
2 changed files with 120 additions and 48 deletions

View File

@ -4,6 +4,7 @@
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h>
#include <climits> #include <climits>
#include <cstring> #include <cstring>
#include <thread> #include <thread>
@ -14,45 +15,115 @@
#include "Common/Thread.h" #include "Common/Thread.h"
#include "Core/ConfigManager.h" #include "Core/ConfigManager.h"
#ifdef _WIN32 static HMODULE s_openal_dll = nullptr;
#pragma comment(lib, "openal32.lib")
#endif #define OPENAL_API_VISIT(X) \
X(alBufferData) \
X(alcCloseDevice) \
X(alcCreateContext) \
X(alcDestroyContext) \
X(alcGetContextsDevice) \
X(alcGetCurrentContext) \
X(alcGetString) \
X(alcIsExtensionPresent) \
X(alcMakeContextCurrent) \
X(alcOpenDevice) \
X(alDeleteBuffers) \
X(alDeleteSources) \
X(alGenBuffers) \
X(alGenSources) \
X(alGetError) \
X(alGetSourcei) \
X(alGetString) \
X(alIsExtensionPresent) \
X(alSourcef) \
X(alSourcei) \
X(alSourcePlay) \
X(alSourceQueueBuffers) \
X(alSourceStop) \
X(alSourceUnqueueBuffers)
// Create func_t function pointer type and declare a nullptr-initialized static variable of that
// type named "pfunc".
#define DYN_FUNC_DECLARE(func) \
typedef decltype(&func) func##_t; \
static func##_t p##func = nullptr;
// Attempt to load the function from the given module handle.
#define OPENAL_FUNC_LOAD(func) \
p##func = (func##_t)::GetProcAddress(s_openal_dll, #func); \
if (!p##func) \
{ \
return false; \
}
OPENAL_API_VISIT(DYN_FUNC_DECLARE);
static bool InitFunctions()
{
OPENAL_API_VISIT(OPENAL_FUNC_LOAD);
return true;
}
static bool InitLibrary()
{
if (s_openal_dll)
return true;
s_openal_dll = ::LoadLibrary(TEXT("openal32.dll"));
if (!s_openal_dll)
return false;
if (!InitFunctions())
{
::FreeLibrary(s_openal_dll);
s_openal_dll = nullptr;
return false;
}
return true;
}
bool OpenALStream::isValid()
{
return InitLibrary();
}
// //
// AyuanX: Spec says OpenAL1.1 is thread safe already // AyuanX: Spec says OpenAL1.1 is thread safe already
// //
bool OpenALStream::Start() bool OpenALStream::Start()
{ {
if (!alcIsExtensionPresent(nullptr, "ALC_ENUMERATION_EXT")) if (!palcIsExtensionPresent(nullptr, "ALC_ENUMERATION_EXT"))
{ {
PanicAlertT("OpenAL: can't find sound devices"); PanicAlertT("OpenAL: can't find sound devices");
return false; return false;
} }
const char* defaultDeviceName = alcGetString(nullptr, ALC_DEFAULT_DEVICE_SPECIFIER); const char* defaultDeviceName = palcGetString(nullptr, ALC_DEFAULT_DEVICE_SPECIFIER);
INFO_LOG(AUDIO, "Found OpenAL device %s", defaultDeviceName); INFO_LOG(AUDIO, "Found OpenAL device %s", defaultDeviceName);
ALCdevice* pDevice = alcOpenDevice(defaultDeviceName); ALCdevice* pDevice = palcOpenDevice(defaultDeviceName);
if (!pDevice) if (!pDevice)
{ {
PanicAlertT("OpenAL: can't open device %s", defaultDeviceName); PanicAlertT("OpenAL: can't open device %s", defaultDeviceName);
return false; return false;
} }
ALCcontext* pContext = alcCreateContext(pDevice, nullptr); ALCcontext* pContext = palcCreateContext(pDevice, nullptr);
if (!pContext) if (!pContext)
{ {
alcCloseDevice(pDevice); palcCloseDevice(pDevice);
PanicAlertT("OpenAL: can't create context for device %s", defaultDeviceName); PanicAlertT("OpenAL: can't create context for device %s", defaultDeviceName);
return false; return false;
} }
// Used to determine an appropriate period size (2x period = total buffer size) // Used to determine an appropriate period size (2x period = total buffer size)
// ALCint refresh; // ALCint refresh;
// alcGetIntegerv(pDevice, ALC_REFRESH, 1, &refresh); // palcGetIntegerv(pDevice, ALC_REFRESH, 1, &refresh);
// period_size_in_millisec = 1000 / refresh; // period_size_in_millisec = 1000 / refresh;
alcMakeContextCurrent(pContext); palcMakeContextCurrent(pContext);
m_run_thread.Set(); m_run_thread.Set();
thread = std::thread(&OpenALStream::SoundLoop, this); thread = std::thread(&OpenALStream::SoundLoop, this);
return true; return true;
@ -66,20 +137,20 @@ void OpenALStream::Stop()
thread.join(); thread.join();
alSourceStop(uiSource); palSourceStop(uiSource);
alSourcei(uiSource, AL_BUFFER, 0); palSourcei(uiSource, AL_BUFFER, 0);
// Clean up buffers and sources // Clean up buffers and sources
alDeleteSources(1, &uiSource); palDeleteSources(1, &uiSource);
uiSource = 0; uiSource = 0;
alDeleteBuffers(numBuffers, uiBuffers); palDeleteBuffers(numBuffers, uiBuffers);
ALCcontext* pContext = alcGetCurrentContext(); ALCcontext* pContext = palcGetCurrentContext();
ALCdevice* pDevice = alcGetContextsDevice(pContext); ALCdevice* pDevice = palcGetContextsDevice(pContext);
alcMakeContextCurrent(nullptr); palcMakeContextCurrent(nullptr);
alcDestroyContext(pContext); palcDestroyContext(pContext);
alcCloseDevice(pDevice); palcCloseDevice(pDevice);
} }
void OpenALStream::SetVolume(int volume) void OpenALStream::SetVolume(int volume)
@ -87,7 +158,7 @@ void OpenALStream::SetVolume(int volume)
fVolume = (float)volume / 100.0f; fVolume = (float)volume / 100.0f;
if (uiSource) if (uiSource)
alSourcef(uiSource, AL_GAIN, fVolume); palSourcef(uiSource, AL_GAIN, fVolume);
} }
void OpenALStream::Update() void OpenALStream::Update()
@ -101,17 +172,17 @@ void OpenALStream::Clear(bool mute)
if (m_muted) if (m_muted)
{ {
alSourceStop(uiSource); palSourceStop(uiSource);
} }
else else
{ {
alSourcePlay(uiSource); palSourcePlay(uiSource);
} }
} }
static ALenum CheckALError(const char* desc) static ALenum CheckALError(const char* desc)
{ {
ALenum err = alGetError(); ALenum err = palGetError();
if (err != AL_NO_ERROR) if (err != AL_NO_ERROR)
{ {
@ -147,15 +218,15 @@ static ALenum CheckALError(const char* desc)
static bool IsCreativeXFi() static bool IsCreativeXFi()
{ {
return strstr(alGetString(AL_RENDERER), "X-Fi") != nullptr; return strstr(palGetString(AL_RENDERER), "X-Fi") != nullptr;
} }
void OpenALStream::SoundLoop() void OpenALStream::SoundLoop()
{ {
Common::SetCurrentThreadName("Audio thread - openal"); Common::SetCurrentThreadName("Audio thread - openal");
bool float32_capable = alIsExtensionPresent("AL_EXT_float32") != 0; bool float32_capable = palIsExtensionPresent("AL_EXT_float32") != 0;
bool surround_capable = alIsExtensionPresent("AL_EXT_MCFORMATS") || IsCreativeXFi(); bool surround_capable = palIsExtensionPresent("AL_EXT_MCFORMATS") || IsCreativeXFi();
bool use_surround = SConfig::GetInstance().bDPL2Decoder && surround_capable; bool use_surround = SConfig::GetInstance().bDPL2Decoder && surround_capable;
// As there is no extension to check for 32-bit fixed point support // As there is no extension to check for 32-bit fixed point support
@ -170,18 +241,18 @@ void OpenALStream::SoundLoop()
uiSource = 0; uiSource = 0;
// Clear error state before querying or else we get false positives. // Clear error state before querying or else we get false positives.
ALenum err = alGetError(); ALenum err = palGetError();
// Generate some AL Buffers for streaming // Generate some AL Buffers for streaming
alGenBuffers(numBuffers, (ALuint*)uiBuffers); palGenBuffers(numBuffers, (ALuint*)uiBuffers);
err = CheckALError("generating buffers"); err = CheckALError("generating buffers");
// Generate a Source to playback the Buffers // Generate a Source to playback the Buffers
alGenSources(1, &uiSource); palGenSources(1, &uiSource);
err = CheckALError("generating sources"); err = CheckALError("generating sources");
// Set the default sound volume as saved in the config file. // Set the default sound volume as saved in the config file.
alSourcef(uiSource, AL_GAIN, fVolume); palSourcef(uiSource, AL_GAIN, fVolume);
// TODO: Error handling // TODO: Error handling
// ALenum err = alGetError(); // ALenum err = alGetError();
@ -194,7 +265,7 @@ void OpenALStream::SoundLoop()
{ {
// Block until we have a free buffer // Block until we have a free buffer
int numBuffersProcessed; int numBuffersProcessed;
alGetSourcei(uiSource, AL_BUFFERS_PROCESSED, &numBuffersProcessed); palGetSourcei(uiSource, AL_BUFFERS_PROCESSED, &numBuffersProcessed);
if (numBuffers == numBuffersQueued && !numBuffersProcessed) if (numBuffers == numBuffersQueued && !numBuffersProcessed)
{ {
soundSyncEvent.Wait(); soundSyncEvent.Wait();
@ -205,7 +276,7 @@ void OpenALStream::SoundLoop()
if (numBuffersProcessed) if (numBuffersProcessed)
{ {
ALuint unqueuedBufferIds[OAL_MAX_BUFFERS]; ALuint unqueuedBufferIds[OAL_MAX_BUFFERS];
alSourceUnqueueBuffers(uiSource, numBuffersProcessed, unqueuedBufferIds); palSourceUnqueueBuffers(uiSource, numBuffersProcessed, unqueuedBufferIds);
err = CheckALError("unqueuing buffers"); err = CheckALError("unqueuing buffers");
numBuffersQueued -= numBuffersProcessed; numBuffersQueued -= numBuffersProcessed;
@ -235,8 +306,8 @@ void OpenALStream::SoundLoop()
if (float32_capable) if (float32_capable)
{ {
alBufferData(uiBuffers[nextBuffer], AL_FORMAT_51CHN32, dpl2, palBufferData(uiBuffers[nextBuffer], AL_FORMAT_51CHN32, dpl2,
numSamples * FRAME_SURROUND_FLOAT, ulFrequency); numSamples * FRAME_SURROUND_FLOAT, ulFrequency);
} }
else if (fixed32_capable) else if (fixed32_capable)
{ {
@ -256,8 +327,8 @@ void OpenALStream::SoundLoop()
surround_int32[i] = (int)dpl2[i]; surround_int32[i] = (int)dpl2[i];
} }
alBufferData(uiBuffers[nextBuffer], AL_FORMAT_51CHN32, surround_int32, palBufferData(uiBuffers[nextBuffer], AL_FORMAT_51CHN32, surround_int32,
numSamples * FRAME_SURROUND_INT32, ulFrequency); numSamples * FRAME_SURROUND_INT32, ulFrequency);
} }
else else
{ {
@ -274,8 +345,8 @@ void OpenALStream::SoundLoop()
surround_short[i] = (int)dpl2[i]; surround_short[i] = (int)dpl2[i];
} }
alBufferData(uiBuffers[nextBuffer], AL_FORMAT_51CHN16, surround_short, palBufferData(uiBuffers[nextBuffer], AL_FORMAT_51CHN16, surround_short,
numSamples * FRAME_SURROUND_SHORT, ulFrequency); numSamples * FRAME_SURROUND_SHORT, ulFrequency);
} }
err = CheckALError("buffering data"); err = CheckALError("buffering data");
@ -300,8 +371,8 @@ void OpenALStream::SoundLoop()
if (float32_capable) if (float32_capable)
{ {
alBufferData(uiBuffers[nextBuffer], AL_FORMAT_STEREO_FLOAT32, sampleBuffer, palBufferData(uiBuffers[nextBuffer], AL_FORMAT_STEREO_FLOAT32, sampleBuffer,
numSamples * FRAME_STEREO_FLOAT, ulFrequency); numSamples * FRAME_STEREO_FLOAT, ulFrequency);
err = CheckALError("buffering float32 data"); err = CheckALError("buffering float32 data");
if (err == AL_INVALID_ENUM) if (err == AL_INVALID_ENUM)
@ -316,8 +387,8 @@ void OpenALStream::SoundLoop()
for (u32 i = 0; i < numSamples * STEREO_CHANNELS; ++i) for (u32 i = 0; i < numSamples * STEREO_CHANNELS; ++i)
stereo_int32[i] = (int)((float)sampleBuffer[i] * (INT64_C(1) << 31)); stereo_int32[i] = (int)((float)sampleBuffer[i] * (INT64_C(1) << 31));
alBufferData(uiBuffers[nextBuffer], AL_FORMAT_STEREO32, stereo_int32, palBufferData(uiBuffers[nextBuffer], AL_FORMAT_STEREO32, stereo_int32,
numSamples * FRAME_STEREO_INT32, ulFrequency); numSamples * FRAME_STEREO_INT32, ulFrequency);
} }
else else
{ {
@ -326,22 +397,22 @@ void OpenALStream::SoundLoop()
for (u32 i = 0; i < numSamples * STEREO_CHANNELS; ++i) for (u32 i = 0; i < numSamples * STEREO_CHANNELS; ++i)
stereo[i] = (short)((float)sampleBuffer[i] * (1 << 15)); stereo[i] = (short)((float)sampleBuffer[i] * (1 << 15));
alBufferData(uiBuffers[nextBuffer], AL_FORMAT_STEREO16, stereo, palBufferData(uiBuffers[nextBuffer], AL_FORMAT_STEREO16, stereo,
numSamples * FRAME_STEREO_SHORT, ulFrequency); numSamples * FRAME_STEREO_SHORT, ulFrequency);
} }
} }
alSourceQueueBuffers(uiSource, 1, &uiBuffers[nextBuffer]); palSourceQueueBuffers(uiSource, 1, &uiBuffers[nextBuffer]);
err = CheckALError("queuing buffers"); err = CheckALError("queuing buffers");
numBuffersQueued++; numBuffersQueued++;
nextBuffer = (nextBuffer + 1) % numBuffers; nextBuffer = (nextBuffer + 1) % numBuffers;
alGetSourcei(uiSource, AL_SOURCE_STATE, &iState); palGetSourcei(uiSource, AL_SOURCE_STATE, &iState);
if (iState != AL_PLAYING) if (iState != AL_PLAYING)
{ {
// Buffer underrun occurred, resume playback // Buffer underrun occurred, resume playback
alSourcePlay(uiSource); palSourcePlay(uiSource);
err = CheckALError("occurred resuming playback"); err = CheckALError("occurred resuming playback");
} }
} }

View File

@ -64,7 +64,8 @@ public:
void Clear(bool mute) override; void Clear(bool mute) override;
void Update() override; void Update() override;
static bool isValid() { return true; } static bool isValid();
private: private:
std::thread thread; std::thread thread;
Common::Flag m_run_thread; Common::Flag m_run_thread;