mirror of
https://github.com/love2d/love.git
synced 2026-08-12 08:30:56 +02:00
Add support for toggling audio output spatialization (HRTF). Disabled by default.
Add love.audio.setOutputSpatialization(enable [, filtername]). Add enabled, filtername = love.audio.getOutputSpatialization(). Add filterlist = love.audio.getOutputSpatializationFilters(). Additional filters (HRTFs) can currently be added via the data files and search paths OpenAL Soft uses. Resolves #1699.
This commit is contained in:
@@ -282,6 +282,10 @@ public:
|
||||
*/
|
||||
virtual bool isEFXsupported() const = 0;
|
||||
|
||||
virtual bool setOutputSpatialization(bool enable, const char *filter = nullptr) = 0;
|
||||
virtual bool getOutputSpatialization(const char *&filter) const = 0;
|
||||
virtual void getOutputSpatializationFilters(std::vector<std::string> &list) const = 0;
|
||||
|
||||
/**
|
||||
* Sets whether audio from other apps mixes with love.audio or is muted,
|
||||
* on supported platforms.
|
||||
|
||||
@@ -199,6 +199,21 @@ bool Audio::isEFXsupported() const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Audio::setOutputSpatialization(bool, const char *)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Audio::getOutputSpatialization(const char *&filter) const
|
||||
{
|
||||
filter = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
void Audio::getOutputSpatializationFilters(std::vector<std::string> &) const
|
||||
{
|
||||
}
|
||||
|
||||
void Audio::pauseContext()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -83,6 +83,10 @@ public:
|
||||
int getMaxSourceEffects() const;
|
||||
bool isEFXsupported() const;
|
||||
|
||||
bool setOutputSpatialization(bool enable, const char *filter = nullptr) override;
|
||||
bool getOutputSpatialization(const char *&filter) const override;
|
||||
void getOutputSpatializationFilters(std::vector<std::string> &list) const override;
|
||||
|
||||
void pauseContext();
|
||||
void resumeContext();
|
||||
|
||||
|
||||
@@ -113,9 +113,6 @@ Audio::Audio()
|
||||
, poolThread(nullptr)
|
||||
, distanceModel(DISTANCE_INVERSE_CLAMPED)
|
||||
{
|
||||
attribs.push_back(0);
|
||||
attribs.push_back(0);
|
||||
|
||||
// Before opening new device, check if recording
|
||||
// is requested.
|
||||
if (getRequestRecordingPermission())
|
||||
@@ -137,10 +134,9 @@ Audio::Audio()
|
||||
if (device == nullptr)
|
||||
throw love::Exception("Could not open device.");
|
||||
|
||||
#ifdef ALC_EXT_EFX
|
||||
attribs.insert(attribs.begin(), ALC_MAX_AUXILIARY_SENDS);
|
||||
attribs.insert(attribs.begin() + 1, MAX_SOURCE_EFFECTS);
|
||||
#endif
|
||||
hasHRTFExtension = alcIsExtensionPresent(device, "ALC_SOFT_HRTF") == AL_TRUE;
|
||||
|
||||
std::vector<ALint> attribs = computeContextAttribs();
|
||||
|
||||
context = alcCreateContext(device, attribs.data());
|
||||
|
||||
@@ -257,6 +253,44 @@ Audio::~Audio()
|
||||
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);
|
||||
@@ -386,7 +420,7 @@ void Audio::getPlaybackDevices(std::vector<std::string> &list)
|
||||
}
|
||||
}
|
||||
|
||||
void Audio::setPlaybackDevice(const char* name)
|
||||
void Audio::setPlaybackDevice(const char *name)
|
||||
{
|
||||
#ifndef ALC_SOFT_reopen_device
|
||||
typedef ALCboolean (ALC_APIENTRY*LPALCREOPENDEVICESOFT)(ALCdevice *device,
|
||||
@@ -404,6 +438,8 @@ void Audio::setPlaybackDevice(const char* 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)));
|
||||
}
|
||||
@@ -707,6 +743,71 @@ bool Audio::isEFXsupported() const
|
||||
#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);
|
||||
|
||||
@@ -122,6 +122,10 @@ public:
|
||||
int getMaxSourceEffects() const;
|
||||
bool isEFXsupported() const;
|
||||
|
||||
bool setOutputSpatialization(bool enable, const char *filter = nullptr) override;
|
||||
bool getOutputSpatialization(const char *&filter) const override;
|
||||
void getOutputSpatializationFilters(std::vector<std::string> &list) const override;
|
||||
|
||||
bool getEffectID(const char *name, ALuint &id);
|
||||
|
||||
std::string getPlaybackDevice();
|
||||
@@ -129,7 +133,10 @@ public:
|
||||
void setPlaybackDevice(const char *name);
|
||||
|
||||
private:
|
||||
|
||||
std::vector<ALint> computeContextAttribs();
|
||||
void initializeEFX();
|
||||
|
||||
// The OpenAL device.
|
||||
ALCdevice *device;
|
||||
|
||||
@@ -138,7 +145,6 @@ private:
|
||||
|
||||
// The OpenAL context.
|
||||
ALCcontext *context;
|
||||
std::vector<ALCint> attribs;
|
||||
|
||||
// The OpenAL effects
|
||||
struct EffectMapStorage
|
||||
@@ -149,7 +155,12 @@ private:
|
||||
std::map<std::string, struct EffectMapStorage> effectmap;
|
||||
std::stack<ALuint> slotlist;
|
||||
int MAX_SCENE_EFFECTS = 64;
|
||||
int MAX_SOURCE_EFFECTS = 64;
|
||||
int MAX_REQUESTED_SOURCE_EFFECTS = 64;
|
||||
int MAX_SOURCE_EFFECTS = 0;
|
||||
|
||||
// Disable HRTF output by default.
|
||||
bool requestEnableHRTF = false;
|
||||
std::string requestedHRTFFilter;
|
||||
|
||||
// The Pool.
|
||||
Pool *pool;
|
||||
@@ -179,6 +190,8 @@ private:
|
||||
DistanceModel distanceModel;
|
||||
//float metersPerUnit = 1.0;
|
||||
|
||||
bool hasHRTFExtension = false;
|
||||
|
||||
#ifdef LOVE_ANDROID
|
||||
# ifndef ALC_SOFT_pause_device
|
||||
typedef void (ALC_APIENTRY*LPALCDEVICEPAUSESOFT)(ALCdevice *device);
|
||||
|
||||
@@ -540,6 +540,41 @@ int w_isEffectsSupported(lua_State *L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_setOutputSpatialization(lua_State *L)
|
||||
{
|
||||
bool enable = luax_checkboolean(L, 1);
|
||||
const char *filter = luaL_optstring(L, 2, nullptr);
|
||||
bool success = instance()->setOutputSpatialization(enable, filter);
|
||||
luax_pushboolean(L, success);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_getOutputSpatialization(lua_State *L)
|
||||
{
|
||||
const char *filter = nullptr;
|
||||
bool enabled = instance()->getOutputSpatialization(filter);
|
||||
luax_pushboolean(L, enabled);
|
||||
if (filter != nullptr)
|
||||
lua_pushstring(L, filter);
|
||||
else
|
||||
lua_pushnil(L);
|
||||
return 2;
|
||||
}
|
||||
|
||||
int w_getOutputSpatializationFilters(lua_State *L)
|
||||
{
|
||||
std::vector<std::string> filters;
|
||||
instance()->getOutputSpatializationFilters(filters);
|
||||
|
||||
lua_createtable(L, (int)filters.size(), 0);
|
||||
for (int i = 0; i < (int)filters.size(); i++)
|
||||
{
|
||||
luax_pushstring(L, filters[i]);
|
||||
lua_rawseti(L, -2, i + 1);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int w_setMixWithSystem(lua_State *L)
|
||||
{
|
||||
luax_pushboolean(L, Audio::setMixWithSystem(luax_checkboolean(L, 1)));
|
||||
@@ -622,6 +657,9 @@ static const luaL_Reg functions[] =
|
||||
{ "getMaxSceneEffects", w_getMaxSceneEffects },
|
||||
{ "getMaxSourceEffects", w_getMaxSourceEffects },
|
||||
{ "isEffectsSupported", w_isEffectsSupported },
|
||||
{ "setOutputSpatialization", w_setOutputSpatialization },
|
||||
{ "getOutputSpatialization", w_getOutputSpatialization },
|
||||
{ "getOutputSpatializationFilters", w_getOutputSpatializationFilters },
|
||||
{ "setMixWithSystem", w_setMixWithSystem },
|
||||
{ "getPlaybackDevice", w_getPlaybackDevice },
|
||||
{ "getPlaybackDevices", w_getPlaybackDevices },
|
||||
|
||||
Reference in New Issue
Block a user