123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926 | /**
* Copyright (c) 2006-2026 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "Audio.h"
#include "common/delay.h"
#include "RecordingDevice.h"
#include "sound/Decoder.h"
#include <cstdlib>
#include <iostream>
#ifdef LOVE_IOS
#include "common/ios.h"
#endif
namespace love
{
namespace audio
{
namespace openal
{
Audio::PoolThread::PoolThread(Pool *pool)
: pool(pool)
, finish(false)
{
threadName = "AudioPool";
}
Audio::PoolThread::~PoolThread()
{
}
void Audio::PoolThread::threadFunction()
{
while (true)
{
{
thread::Lock lock(mutex);
if (finish)
{
return;
}
}
pool->update();
sleep(5);
}
}
void Audio::PoolThread::setFinish()
{
thread::Lock lock(mutex);
finish = true;
}
ALenum Audio::getFormat(int bitDepth, int channels)
{
if (bitDepth != 8 && bitDepth != 16)
return AL_NONE;
if (channels == 1)
return bitDepth == 8 ? AL_FORMAT_MONO8 : AL_FORMAT_MONO16;
else if (channels == 2)
return bitDepth == 8 ? AL_FORMAT_STEREO8 : AL_FORMAT_STEREO16;
#ifdef AL_EXT_MCFORMATS
else if (alIsExtensionPresent("AL_EXT_MCFORMATS"))
{
if (channels == 6)
return bitDepth == 8 ? AL_FORMAT_51CHN8 : AL_FORMAT_51CHN16;
else if (channels == 8)
return bitDepth == 8 ? AL_FORMAT_71CHN8 : AL_FORMAT_71CHN16;
}
#endif
return AL_NONE;
}
static const char *getDeviceSpecifier(ALCdevice *device)
{
#ifndef ALC_ALL_DEVICES_SPECIFIER
constexpr ALCenum ALC_ALL_DEVICES_SPECIFIER = 0x1013;
#endif
static ALCenum deviceEnum = alcIsExtensionPresent(nullptr, "ALC_ENUMERATE_ALL_EXT") == ALC_TRUE
? ALC_ALL_DEVICES_SPECIFIER
: ALC_DEVICE_SPECIFIER;
return alcGetString(device, deviceEnum);
}
Audio::Audio()
: love::audio::Audio("love.audio.openal")
, device(nullptr)
, context(nullptr)
, pool(nullptr)
, poolThread(nullptr)
, distanceModel(DISTANCE_INVERSE_CLAMPED)
{
// Before opening new device, check if recording
// is requested.
if (getRequestRecordingPermission())
{
if (!hasRecordingPermission())
// Request recording permission on some OSes.
requestRecordingPermission();
}
{
#if defined(LOVE_LINUX)
// Temporarly block signals, as the thread inherits this mask
love::thread::ScopedDisableSignals disableSignals;
#endif
// Passing null for default device.
device = alcOpenDevice(nullptr);
if (device == nullptr)
throw love::Exception("Could not open device.");
hasHRTFExtension = alcIsExtensionPresent(device, "ALC_SOFT_HRTF") == AL_TRUE;
std::vector<ALint> attribs = computeContextAttribs();
context = alcCreateContext(device, attribs.data());
if (context == nullptr)
throw love::Exception("Could not create context: %s", alcGetString(device, alcGetError(device)));
if (!alcMakeContextCurrent(context))
throw love::Exception("Could not make context current: %s", alcGetString(device, alcGetError(device)));
}
#ifdef ALC_EXT_EFX
initializeEFX();
alcGetIntegerv(device, ALC_MAX_AUXILIARY_SENDS, 1, &MAX_SOURCE_EFFECTS);
alGetError();
if (alGenAuxiliaryEffectSlots)
{
for (int i = 0; i < MAX_SCENE_EFFECTS; i++)
{
ALuint slot;
alGenAuxiliaryEffectSlots(1, &slot);
if (alGetError() == AL_NO_ERROR)
slotlist.push(slot);
else
{
MAX_SCENE_EFFECTS = i;
break;
}
}
}
else
MAX_SCENE_EFFECTS = MAX_SOURCE_EFFECTS = 0;
#else
MAX_SCENE_EFFECTS = MAX_SOURCE_EFFECTS = 0;
#endif
try
{
pool = new Pool(device);
}
catch (love::Exception &)
{
for (auto c : capture)
delete c;
#ifdef ALC_EXT_EFX
if (alDeleteAuxiliaryEffectSlots)
{
while (!slotlist.empty())
{
alDeleteAuxiliaryEffectSlots(1, &slotlist.top());
slotlist.pop();
}
}
#endif
alcMakeContextCurrent(nullptr);
alcDestroyContext(context);
alcCloseDevice(device);
throw;
}
poolThread = new PoolThread(pool);
poolThread->start();
#ifdef LOVE_IOS
love::ios::initAudioSessionInterruptionHandler();
#endif
#ifdef LOVE_ANDROID
bool hasPauseDeviceExt = alcIsExtensionPresent(device, "ALC_SOFT_pause_device") == ALC_TRUE;
alcDevicePauseSOFT = hasPauseDeviceExt
? (LPALCDEVICEPAUSESOFT) alcGetProcAddress(device, "alcDevicePauseSOFT")
: nullptr;
alcDeviceResumeSOFT = hasPauseDeviceExt
? (LPALCDEVICERESUMESOFT) alcGetProcAddress(device, "alcDeviceResumeSOFT")
: nullptr;
#endif
}
Audio::~Audio()
{
#ifdef LOVE_IOS
love::ios::destroyAudioSessionInterruptionHandler();
#endif
poolThread->setFinish();
poolThread->wait();
delete poolThread;
delete pool;
for (auto c : capture)
delete c;
#ifdef ALC_EXT_EFX
for (auto e : effectmap)
{
delete e.second.effect;
slotlist.push(e.second.slot);
}
if (alDeleteAuxiliaryEffectSlots)
{
while (!slotlist.empty())
{
alDeleteAuxiliaryEffectSlots(1, &slotlist.top());
slotlist.pop();
}
}
#endif
alcMakeContextCurrent(nullptr);
alcDestroyContext(context);
alcCloseDevice(device);
}
std::vector<ALint> Audio::computeContextAttribs()
{
std::vector<ALint> attribs;
#ifdef ALC_EXT_EFX
attribs.push_back(ALC_MAX_AUXILIARY_SENDS);
attribs.push_back(MAX_REQUESTED_SOURCE_EFFECTS);
#endif
#ifdef ALC_SOFT_HRTF
if (hasHRTFExtension)
{
attribs.push_back(ALC_HRTF_SOFT);
attribs.push_back(requestEnableHRTF ? AL_TRUE : AL_FALSE);
if (!requestedHRTFFilter.empty())
{
std::vector<std::string> filters;
getOutputSpatializationFilters(filters);
for (size_t i = 0; i < filters.size(); i++)
{
if (filters[i] == requestedHRTFFilter)
{
attribs.push_back(ALC_HRTF_ID_SOFT);
attribs.push_back((int)i);
break;
}
}
}
}
#endif
attribs.push_back(0);
attribs.push_back(0);
return attribs;
}
love::audio::Source *Audio::newSource(love::sound::Decoder *decoder)
{
return new Source(pool, decoder);
}
love::audio::Source *Audio::newSource(love::sound::SoundData *soundData)
{
return new Source(pool, soundData);
}
love::audio::Source *Audio::newSource(int sampleRate, int bitDepth, int channels, int buffers)
{
return new Source(pool, sampleRate, bitDepth, channels, buffers);
}
int Audio::getActiveSourceCount() const
{
return pool->getActiveSourceCount();
}
int Audio::getMaxSources() const
{
return pool->getMaxSources();
}
bool Audio::play(love::audio::Source *source)
{
return source->play();
}
bool Audio::play(const std::vector<love::audio::Source*> &sources)
{
return Source::play(sources);
}
void Audio::stop(love::audio::Source *source)
{
source->stop();
}
void Audio::stop(const std::vector<love::audio::Source*> &sources)
{
return Source::stop(sources);
}
void Audio::stop()
{
return Source::stop(pool);
}
void Audio::pause(love::audio::Source *source)
{
source->pause();
}
void Audio::pause(const std::vector<love::audio::Source*> &sources)
{
return Source::pause(sources);
}
std::vector<love::audio::Source*> Audio::pause()
{
return Source::pause(pool);
}
void Audio::pauseContext()
{
#ifdef LOVE_ANDROID
if (alcDevicePauseSOFT)
alcDevicePauseSOFT(device);
else
{
// This is extremely rare case since we're using OpenAL-soft
// in Android and the ALC_SOFT_pause_device has been supported
// since 1.16
for (auto &src: pausedSources)
src->release();
pausedSources = pause();
for (auto &src: pausedSources)
src->retain();
}
#else
alcMakeContextCurrent(nullptr);
#endif
}
void Audio::resumeContext()
{
#ifdef LOVE_ANDROID
if (alcDeviceResumeSOFT)
alcDeviceResumeSOFT(device);
else
{
// Again, this is rare case
play(pausedSources);
for (auto &src: pausedSources)
src->release();
pausedSources.resize(0);
}
#else
if (context && alcGetCurrentContext() != context)
alcMakeContextCurrent(context);
#endif
}
std::string Audio::getPlaybackDevice()
{
const char *dev = getDeviceSpecifier(device);
if (dev == nullptr)
throw Exception("Failed to get current device: %s", alcGetString(device, alcGetError(device)));
return dev;
}
void Audio::getPlaybackDevices(std::vector<std::string> &list)
{
const char *devices = getDeviceSpecifier(nullptr);
if (devices == nullptr)
throw Exception("Failed to enumerate devices: %s", alcGetString(nullptr, alcGetError(nullptr)));
for (const char *device = devices; *device; device++)
{
list.emplace_back(device);
device += list.back().length();
}
}
void Audio::setPlaybackDevice(const char *name)
{
#ifndef ALC_SOFT_reopen_device
typedef ALCboolean (ALC_APIENTRY*LPALCREOPENDEVICESOFT)(ALCdevice *device,
const ALCchar *deviceName, const ALCint *attribs);
#endif
static LPALCREOPENDEVICESOFT alcReopenDeviceSOFT = alcIsExtensionPresent(device, "ALC_SOFT_reopen_device") == ALC_TRUE
? (LPALCREOPENDEVICESOFT) alcGetProcAddress(device, "alcReopenDeviceSOFT")
: nullptr;
if (alcReopenDeviceSOFT == nullptr)
{
// Default implementation throws exception. To make
// error message consistent, call the base class.
love::audio::Audio::setPlaybackDevice(name);
return;
}
std::vector<ALint> attribs = computeContextAttribs();
if (alcReopenDeviceSOFT(device, (const ALCchar *) name, attribs.data()) == ALC_FALSE)
throw love::Exception("Cannot set output device: %s", alcGetString(device, alcGetError(device)));
}
void Audio::setVolume(float volume)
{
alListenerf(AL_GAIN, volume);
}
float Audio::getVolume() const
{
ALfloat volume;
alGetListenerf(AL_GAIN, &volume);
return volume;
}
void Audio::getPosition(float *v) const
{
alGetListenerfv(AL_POSITION, v);
}
void Audio::setPosition(float *v)
{
alListenerfv(AL_POSITION, v);
}
void Audio::getOrientation(float *v) const
{
alGetListenerfv(AL_ORIENTATION, v);
}
void Audio::setOrientation(float *v)
{
alListenerfv(AL_ORIENTATION, v);
}
void Audio::getVelocity(float *v) const
{
alGetListenerfv(AL_VELOCITY, v);
}
void Audio::setVelocity(float *v)
{
alListenerfv(AL_VELOCITY, v);
}
void Audio::setDopplerScale(float scale)
{
if (scale >= 0.0f)
alDopplerFactor(scale);
}
float Audio::getDopplerScale() const
{
return alGetFloat(AL_DOPPLER_FACTOR);
}
/*
void Audio::setMeter(float scale)
{
if (scale >= 0.0f)
{
metersPerUnit = scale;
#ifdef ALC_EXT_EFX
alListenerf(AL_METERS_PER_UNIT, scale);
#endif
}
}
float Audio::getMeter() const
{
return metersPerUnit;
}
*/
Audio::DistanceModel Audio::getDistanceModel() const
{
return distanceModel;
}
void Audio::setDistanceModel(DistanceModel distanceModel)
{
this->distanceModel = distanceModel;
switch (distanceModel)
{
case DISTANCE_NONE:
alDistanceModel(AL_NONE);
break;
case DISTANCE_INVERSE:
alDistanceModel(AL_INVERSE_DISTANCE);
break;
case DISTANCE_INVERSE_CLAMPED:
alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
break;
case DISTANCE_LINEAR:
alDistanceModel(AL_LINEAR_DISTANCE);
break;
case DISTANCE_LINEAR_CLAMPED:
alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
break;
case DISTANCE_EXPONENT:
alDistanceModel(AL_EXPONENT_DISTANCE);
break;
case DISTANCE_EXPONENT_CLAMPED:
alDistanceModel(AL_EXPONENT_DISTANCE_CLAMPED);
break;
default:
break;
}
}
const std::vector<love::audio::RecordingDevice*> &Audio::getRecordingDevices()
{
std::vector<std::string> devnames;
std::vector<love::audio::RecordingDevice*> devices;
// If recording permission is not granted, inform user about it
// and return empty list.
if (!hasRecordingPermission() && getRequestRecordingPermission())
{
showRecordingPermissionMissingDialog();
capture.clear();
return capture;
}
std::string defaultname(alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER));
//no device name obtained from AL, fallback to reading from device
if (defaultname.length() == 0)
{
//use some safe basic parameters - 8 kHz, 8 bits, 1 channel
ALCdevice *defaultdevice = alcCaptureOpenDevice(NULL, 8000, AL_FORMAT_MONO8, 1024);
if (alGetError() == AL_NO_ERROR)
{
defaultname = alcGetString(defaultdevice, ALC_CAPTURE_DEVICE_SPECIFIER);
alcCaptureCloseDevice(defaultdevice);
}
else
{
//failed to open default recording device - bail, return empty list
capture.clear();
return capture;
}
}
devnames.reserve(capture.size());
devnames.push_back(defaultname);
//find devices name list
const ALCchar *devstr = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
size_t offset = 0;
while (true)
{
if (devstr[offset] == '\0')
break;
std::string str((ALCchar*)&devstr[offset]);
if (str != defaultname)
devnames.push_back(str);
offset += str.length() + 1;
}
devices.reserve(devnames.size());
//build ordered list of devices
for (int i = 0; i < (int) devnames.size(); i++)
{
devices.push_back(nullptr);
auto d = devices.end() - 1;
for (auto c : capture)
if (devnames[i] == c->getName())
*d = c;
if (*d == nullptr)
*d = new RecordingDevice(devnames[i].c_str());
else
(*d)->retain();
}
for (auto c : capture)
c->release();
capture.clear();
capture.reserve(devices.size());
//this needs to be executed in specific order
for (unsigned int i = 0; i < devnames.size(); i++)
capture.push_back(devices[i]);
return capture;
}
bool Audio::setEffect(const char *name, std::map<Effect::Parameter, float> ¶ms)
{
Effect *effect;
ALuint slot;
auto iter = effectmap.find(name);
if (iter == effectmap.end())
{
//new effect needed but no more slots
if (effectmap.size() >= (unsigned int)MAX_SCENE_EFFECTS)
return false;
effect = new Effect();
slot = slotlist.top();
slotlist.pop();
effectmap[name] = {effect, slot};
}
else
{
effect = iter->second.effect;
slot = iter->second.slot;
}
bool result = effect->setParams(params);
#ifdef ALC_EXT_EFX
if (alAuxiliaryEffectSloti)
{
if (result)
{
auto iter = params.find(Effect::EFFECT_VOLUME);
if (iter != params.end())
alAuxiliaryEffectSlotf(slot, AL_EFFECTSLOT_GAIN, iter->second);
alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_EFFECT, effect->getEffect());
}
else
alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_EFFECT, AL_EFFECT_NULL);
alGetError();
}
#endif
return result;
}
bool Audio::unsetEffect(const char *name)
{
auto iter = effectmap.find(name);
if (iter == effectmap.end())
return false;
Effect *effect = iter->second.effect;
ALuint slot = iter->second.slot;
#ifdef ALC_EXT_EFX
if (alAuxiliaryEffectSloti)
alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_EFFECT, AL_EFFECT_NULL);
#endif
delete effect;
effectmap.erase(iter);
slotlist.push(slot);
return true;
}
bool Audio::getEffect(const char *name, std::map<Effect::Parameter, float> ¶ms)
{
auto iter = effectmap.find(name);
if (iter == effectmap.end())
return false;
params = iter->second.effect->getParams();
return true;
}
bool Audio::getActiveEffects(std::vector<std::string> &list) const
{
if (effectmap.empty())
return false;
list.reserve(effectmap.size());
for (auto i : effectmap)
list.push_back(i.first);
return true;
}
int Audio::getMaxSceneEffects() const
{
return MAX_SCENE_EFFECTS;
}
int Audio::getMaxSourceEffects() const
{
return MAX_SOURCE_EFFECTS;
}
bool Audio::isEFXsupported() const
{
#ifdef ALC_EXT_EFX
return (alGenEffects != nullptr);
#else
return false;
#endif
}
bool Audio::setOutputSpatialization(bool enable, const char *filter)
{
requestEnableHRTF = enable;
if (filter != nullptr)
requestedHRTFFilter = filter;
else
requestedHRTFFilter.clear();
#ifdef ALC_SOFT_HRTF
if (hasHRTFExtension)
{
static auto alcResetDeviceSOFT = (LPALCRESETDEVICESOFT)alcGetProcAddress(device, "alcResetDeviceSOFT");
if (alcResetDeviceSOFT == nullptr)
return false;
std::vector<ALint> attribs = computeContextAttribs();
return alcResetDeviceSOFT(device, attribs.data()) != AL_FALSE;
}
#endif
return false;
}
bool Audio::getOutputSpatialization(const char *&filter) const
{
#ifdef ALC_SOFT_HRTF
if (hasHRTFExtension)
{
ALCint enabled = 0;
alcGetIntegerv(device, ALC_HRTF_SOFT, 1, &enabled);
if (enabled != 0)
filter = alcGetString(device, ALC_HRTF_SPECIFIER_SOFT);
else
filter = nullptr;
return enabled != 0;
}
#endif
filter = nullptr;
return false;
}
void Audio::getOutputSpatializationFilters(std::vector<std::string> &list) const
{
#ifdef ALC_SOFT_HRTF
if (!hasHRTFExtension)
return;
static auto alcGetStringiSOFT = (LPALCGETSTRINGISOFT)alcGetProcAddress(device, "alcGetStringiSOFT");
if (alcGetStringiSOFT == nullptr)
return;
ALCint count = 0;
alcGetIntegerv(device, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &count);
for (int i = 0; i < count; i++)
{
const char *specifier = alcGetStringiSOFT(device, ALC_HRTF_SPECIFIER_SOFT, i);
if (specifier != nullptr)
list.push_back(specifier);
}
#else
LOVE_UNUSED(list);
#endif
}
bool Audio::getEffectID(const char *name, ALuint &id)
{
auto iter = effectmap.find(name);
if (iter == effectmap.end())
return false;
id = iter->second.slot;
return true;
}
#ifdef ALC_EXT_EFX
LPALGENEFFECTS alGenEffects = nullptr;
LPALDELETEEFFECTS alDeleteEffects = nullptr;
LPALISEFFECT alIsEffect = nullptr;
LPALEFFECTI alEffecti = nullptr;
LPALEFFECTIV alEffectiv = nullptr;
LPALEFFECTF alEffectf = nullptr;
LPALEFFECTFV alEffectfv = nullptr;
LPALGETEFFECTI alGetEffecti = nullptr;
LPALGETEFFECTIV alGetEffectiv = nullptr;
LPALGETEFFECTF alGetEffectf = nullptr;
LPALGETEFFECTFV alGetEffectfv = nullptr;
LPALGENFILTERS alGenFilters = nullptr;
LPALDELETEFILTERS alDeleteFilters = nullptr;
LPALISFILTER alIsFilter = nullptr;
LPALFILTERI alFilteri = nullptr;
LPALFILTERIV alFilteriv = nullptr;
LPALFILTERF alFilterf = nullptr;
LPALFILTERFV alFilterfv = nullptr;
LPALGETFILTERI alGetFilteri = nullptr;
LPALGETFILTERIV alGetFilteriv = nullptr;
LPALGETFILTERF alGetFilterf = nullptr;
LPALGETFILTERFV alGetFilterfv = nullptr;
LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots = nullptr;
LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots = nullptr;
LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot = nullptr;
LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti = nullptr;
LPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv = nullptr;
LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf = nullptr;
LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv = nullptr;
LPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti = nullptr;
LPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv = nullptr;
LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf = nullptr;
LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv = nullptr;
#endif
void Audio::initializeEFX()
{
#ifdef ALC_EXT_EFX
if (alcIsExtensionPresent(device, "ALC_EXT_EFX") == AL_FALSE)
return;
alGenEffects = (LPALGENEFFECTS)alGetProcAddress("alGenEffects");
alDeleteEffects = (LPALDELETEEFFECTS)alGetProcAddress("alDeleteEffects");
alIsEffect = (LPALISEFFECT)alGetProcAddress("alIsEffect");
alEffecti = (LPALEFFECTI)alGetProcAddress("alEffecti");
alEffectiv = (LPALEFFECTIV)alGetProcAddress("alEffectiv");
alEffectf = (LPALEFFECTF)alGetProcAddress("alEffectf");
alEffectfv = (LPALEFFECTFV)alGetProcAddress("alEffectfv");
alGetEffecti = (LPALGETEFFECTI)alGetProcAddress("alGetEffecti");
alGetEffectiv = (LPALGETEFFECTIV)alGetProcAddress("alGetEffectiv");
alGetEffectf = (LPALGETEFFECTF)alGetProcAddress("alGetEffectf");
alGetEffectfv = (LPALGETEFFECTFV)alGetProcAddress("alGetEffectfv");
alGenFilters = (LPALGENFILTERS)alGetProcAddress("alGenFilters");
alDeleteFilters = (LPALDELETEFILTERS)alGetProcAddress("alDeleteFilters");
alIsFilter = (LPALISFILTER)alGetProcAddress("alIsFilter");
alFilteri = (LPALFILTERI)alGetProcAddress("alFilteri");
alFilteriv = (LPALFILTERIV)alGetProcAddress("alFilteriv");
alFilterf = (LPALFILTERF)alGetProcAddress("alFilterf");
alFilterfv = (LPALFILTERFV)alGetProcAddress("alFilterfv");
alGetFilteri = (LPALGETFILTERI)alGetProcAddress("alGetFilteri");
alGetFilteriv = (LPALGETFILTERIV)alGetProcAddress("alGetFilteriv");
alGetFilterf = (LPALGETFILTERF)alGetProcAddress("alGetFilterf");
alGetFilterfv = (LPALGETFILTERFV)alGetProcAddress("alGetFilterfv");
alGenAuxiliaryEffectSlots = (LPALGENAUXILIARYEFFECTSLOTS)alGetProcAddress("alGenAuxiliaryEffectSlots");
alDeleteAuxiliaryEffectSlots = (LPALDELETEAUXILIARYEFFECTSLOTS)alGetProcAddress("alDeleteAuxiliaryEffectSlots");
alIsAuxiliaryEffectSlot = (LPALISAUXILIARYEFFECTSLOT)alGetProcAddress("alIsAuxiliaryEffectSlot");
alAuxiliaryEffectSloti = (LPALAUXILIARYEFFECTSLOTI)alGetProcAddress("alAuxiliaryEffectSloti");
alAuxiliaryEffectSlotiv = (LPALAUXILIARYEFFECTSLOTIV)alGetProcAddress("alAuxiliaryEffectSlotiv");
alAuxiliaryEffectSlotf = (LPALAUXILIARYEFFECTSLOTF)alGetProcAddress("alAuxiliaryEffectSlotf");
alAuxiliaryEffectSlotfv = (LPALAUXILIARYEFFECTSLOTFV)alGetProcAddress("alAuxiliaryEffectSlotfv");
alGetAuxiliaryEffectSloti = (LPALGETAUXILIARYEFFECTSLOTI)alGetProcAddress("alGetAuxiliaryEffectSloti");
alGetAuxiliaryEffectSlotiv = (LPALGETAUXILIARYEFFECTSLOTIV)alGetProcAddress("alGetAuxiliaryEffectSlotiv");
alGetAuxiliaryEffectSlotf = (LPALGETAUXILIARYEFFECTSLOTF)alGetProcAddress("alGetAuxiliaryEffectSlotf");
alGetAuxiliaryEffectSlotfv = (LPALGETAUXILIARYEFFECTSLOTFV)alGetProcAddress("alGetAuxiliaryEffectSlotfv");
//failed to initialize functions, revert to nullptr
if (!alGenEffects || !alDeleteEffects || !alIsEffect ||
!alGenFilters || !alDeleteFilters || !alIsFilter ||
!alGenAuxiliaryEffectSlots || !alDeleteAuxiliaryEffectSlots || !alIsAuxiliaryEffectSlot ||
!alEffecti || !alEffectiv || !alEffectf || !alEffectfv ||
!alGetEffecti || !alGetEffectiv || !alGetEffectf || !alGetEffectfv ||
!alFilteri || !alFilteriv || !alFilterf || !alFilterfv ||
!alGetFilteri || !alGetFilteriv || !alGetFilterf || !alGetFilterfv ||
!alAuxiliaryEffectSloti || !alAuxiliaryEffectSlotiv || !alAuxiliaryEffectSlotf || !alAuxiliaryEffectSlotfv ||
!alGetAuxiliaryEffectSloti || !alGetAuxiliaryEffectSlotiv || !alGetAuxiliaryEffectSlotf || !alGetAuxiliaryEffectSlotfv)
{
alGenEffects = nullptr; alDeleteEffects = nullptr; alIsEffect = nullptr;
alEffecti = nullptr; alEffectiv = nullptr; alEffectf = nullptr; alEffectfv = nullptr;
alGetEffecti = nullptr; alGetEffectiv = nullptr; alGetEffectf = nullptr; alGetEffectfv = nullptr;
alGenFilters = nullptr; alDeleteFilters = nullptr; alIsFilter = nullptr;
alFilteri = nullptr; alFilteriv = nullptr; alFilterf = nullptr; alFilterfv = nullptr;
alGetFilteri = nullptr; alGetFilteriv = nullptr; alGetFilterf = nullptr; alGetFilterfv = nullptr;
alGenAuxiliaryEffectSlots = nullptr; alDeleteAuxiliaryEffectSlots = nullptr; alIsAuxiliaryEffectSlot = nullptr;
alAuxiliaryEffectSloti = nullptr; alAuxiliaryEffectSlotiv = nullptr;
alAuxiliaryEffectSlotf = nullptr; alAuxiliaryEffectSlotfv = nullptr;
alGetAuxiliaryEffectSloti = nullptr; alGetAuxiliaryEffectSlotiv = nullptr;
alGetAuxiliaryEffectSlotf = nullptr; alGetAuxiliaryEffectSlotfv = nullptr;
}
#endif
}
} // openal
} // audio
} // love
|