mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-19 20:24:49 +00:00
keeping the alt 87514151c4 (diff-73a8dc1ce58605f6c5ea53548454c3bae516ec5132a29c9d7ff7edf9730c75be)
759 lines
25 KiB
C++
759 lines
25 KiB
C++
/**
|
|
* OpenAL cross platform audio library
|
|
* Copyright (C) 1999-2007 by authors.
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "effect.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <iterator>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <numeric>
|
|
#include <string>
|
|
#include <type_traits>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
#include <variant>
|
|
#include <vector>
|
|
|
|
#include "AL/al.h"
|
|
#include "AL/alc.h"
|
|
#include "AL/alext.h"
|
|
#include "AL/efx-presets.h"
|
|
#include "AL/efx.h"
|
|
|
|
#include "al/effects/effects.h"
|
|
#include "albit.h"
|
|
#include "alc/context.h"
|
|
#include "alc/device.h"
|
|
#include "alc/inprogext.h"
|
|
#include "almalloc.h"
|
|
#include "alnumeric.h"
|
|
#include "alspan.h"
|
|
#include "alstring.h"
|
|
#include "core/except.h"
|
|
#include "core/logging.h"
|
|
#include "direct_defs.h"
|
|
#include "intrusive_ptr.h"
|
|
#include "opthelpers.h"
|
|
|
|
|
|
const std::array<EffectList,16> gEffectList{{
|
|
{ "eaxreverb", EAXREVERB_EFFECT, AL_EFFECT_EAXREVERB },
|
|
{ "reverb", REVERB_EFFECT, AL_EFFECT_REVERB },
|
|
{ "autowah", AUTOWAH_EFFECT, AL_EFFECT_AUTOWAH },
|
|
{ "chorus", CHORUS_EFFECT, AL_EFFECT_CHORUS },
|
|
{ "compressor", COMPRESSOR_EFFECT, AL_EFFECT_COMPRESSOR },
|
|
{ "distortion", DISTORTION_EFFECT, AL_EFFECT_DISTORTION },
|
|
{ "echo", ECHO_EFFECT, AL_EFFECT_ECHO },
|
|
{ "equalizer", EQUALIZER_EFFECT, AL_EFFECT_EQUALIZER },
|
|
{ "flanger", FLANGER_EFFECT, AL_EFFECT_FLANGER },
|
|
{ "fshifter", FSHIFTER_EFFECT, AL_EFFECT_FREQUENCY_SHIFTER },
|
|
{ "modulator", MODULATOR_EFFECT, AL_EFFECT_RING_MODULATOR },
|
|
{ "pshifter", PSHIFTER_EFFECT, AL_EFFECT_PITCH_SHIFTER },
|
|
{ "vmorpher", VMORPHER_EFFECT, AL_EFFECT_VOCAL_MORPHER },
|
|
{ "dedicated", DEDICATED_EFFECT, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT },
|
|
{ "dedicated", DEDICATED_EFFECT, AL_EFFECT_DEDICATED_DIALOGUE },
|
|
{ "convolution", CONVOLUTION_EFFECT, AL_EFFECT_CONVOLUTION_SOFT },
|
|
}};
|
|
|
|
|
|
namespace {
|
|
|
|
using SubListAllocator = al::allocator<std::array<ALeffect,64>>;
|
|
|
|
constexpr auto GetDefaultProps(ALenum type) noexcept -> const EffectProps&
|
|
{
|
|
switch(type)
|
|
{
|
|
case AL_EFFECT_NULL: return NullEffectProps;
|
|
case AL_EFFECT_EAXREVERB: return ReverbEffectProps;
|
|
case AL_EFFECT_REVERB: return StdReverbEffectProps;
|
|
case AL_EFFECT_AUTOWAH: return AutowahEffectProps;
|
|
case AL_EFFECT_CHORUS: return ChorusEffectProps;
|
|
case AL_EFFECT_COMPRESSOR: return CompressorEffectProps;
|
|
case AL_EFFECT_DISTORTION: return DistortionEffectProps;
|
|
case AL_EFFECT_ECHO: return EchoEffectProps;
|
|
case AL_EFFECT_EQUALIZER: return EqualizerEffectProps;
|
|
case AL_EFFECT_FLANGER: return FlangerEffectProps;
|
|
case AL_EFFECT_FREQUENCY_SHIFTER: return FshifterEffectProps;
|
|
case AL_EFFECT_RING_MODULATOR: return ModulatorEffectProps;
|
|
case AL_EFFECT_PITCH_SHIFTER: return PshifterEffectProps;
|
|
case AL_EFFECT_VOCAL_MORPHER: return VmorpherEffectProps;
|
|
case AL_EFFECT_DEDICATED_DIALOGUE: return DedicatedDialogEffectProps;
|
|
case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: return DedicatedLfeEffectProps;
|
|
case AL_EFFECT_CONVOLUTION_SOFT: return ConvolutionEffectProps;
|
|
}
|
|
return NullEffectProps;
|
|
}
|
|
|
|
void InitEffectParams(ALeffect *effect, ALenum type) noexcept
|
|
{
|
|
switch(type)
|
|
{
|
|
case AL_EFFECT_NULL: effect->PropsVariant.emplace<NullEffectHandler>(); break;
|
|
case AL_EFFECT_EAXREVERB: effect->PropsVariant.emplace<ReverbEffectHandler>(); break;
|
|
case AL_EFFECT_REVERB: effect->PropsVariant.emplace<StdReverbEffectHandler>(); break;
|
|
case AL_EFFECT_AUTOWAH: effect->PropsVariant.emplace<AutowahEffectHandler>(); break;
|
|
case AL_EFFECT_CHORUS: effect->PropsVariant.emplace<ChorusEffectHandler>(); break;
|
|
case AL_EFFECT_COMPRESSOR: effect->PropsVariant.emplace<CompressorEffectHandler>(); break;
|
|
case AL_EFFECT_DISTORTION: effect->PropsVariant.emplace<DistortionEffectHandler>(); break;
|
|
case AL_EFFECT_ECHO: effect->PropsVariant.emplace<EchoEffectHandler>(); break;
|
|
case AL_EFFECT_EQUALIZER: effect->PropsVariant.emplace<EqualizerEffectHandler>(); break;
|
|
case AL_EFFECT_FLANGER: effect->PropsVariant.emplace<ChorusEffectHandler>(); break;
|
|
case AL_EFFECT_FREQUENCY_SHIFTER: effect->PropsVariant.emplace<FshifterEffectHandler>(); break;
|
|
case AL_EFFECT_RING_MODULATOR: effect->PropsVariant.emplace<ModulatorEffectHandler>(); break;
|
|
case AL_EFFECT_PITCH_SHIFTER: effect->PropsVariant.emplace<PshifterEffectHandler>(); break;
|
|
case AL_EFFECT_VOCAL_MORPHER: effect->PropsVariant.emplace<VmorpherEffectHandler>(); break;
|
|
case AL_EFFECT_DEDICATED_DIALOGUE:
|
|
effect->PropsVariant.emplace<DedicatedDialogEffectHandler>();
|
|
break;
|
|
case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT:
|
|
effect->PropsVariant.emplace<DedicatedLfeEffectHandler>();
|
|
break;
|
|
case AL_EFFECT_CONVOLUTION_SOFT:
|
|
effect->PropsVariant.emplace<ConvolutionEffectHandler>();
|
|
break;
|
|
}
|
|
effect->Props = GetDefaultProps(type);
|
|
effect->type = type;
|
|
}
|
|
|
|
[[nodiscard]]
|
|
auto EnsureEffects(al::Device *device, size_t needed) noexcept -> bool
|
|
try {
|
|
size_t count{std::accumulate(device->EffectList.cbegin(), device->EffectList.cend(), 0_uz,
|
|
[](size_t cur, const EffectSubList &sublist) noexcept -> size_t
|
|
{ return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
|
|
|
|
while(needed > count)
|
|
{
|
|
if(device->EffectList.size() >= 1<<25) UNLIKELY
|
|
return false;
|
|
|
|
EffectSubList sublist{};
|
|
sublist.FreeMask = ~0_u64;
|
|
sublist.Effects = SubListAllocator{}.allocate(1);
|
|
device->EffectList.emplace_back(std::move(sublist));
|
|
count += std::tuple_size_v<SubListAllocator::value_type>;
|
|
}
|
|
return true;
|
|
}
|
|
catch(...) {
|
|
return false;
|
|
}
|
|
|
|
[[nodiscard]]
|
|
auto AllocEffect(al::Device *device) noexcept -> ALeffect*
|
|
{
|
|
auto sublist = std::find_if(device->EffectList.begin(), device->EffectList.end(),
|
|
[](const EffectSubList &entry) noexcept -> bool
|
|
{ return entry.FreeMask != 0; });
|
|
auto lidx = static_cast<ALuint>(std::distance(device->EffectList.begin(), sublist));
|
|
auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
|
|
ASSUME(slidx < 64);
|
|
|
|
ALeffect *effect{al::construct_at(al::to_address(sublist->Effects->begin() + slidx))};
|
|
InitEffectParams(effect, AL_EFFECT_NULL);
|
|
|
|
/* Add 1 to avoid effect ID 0. */
|
|
effect->id = ((lidx<<6) | slidx) + 1;
|
|
|
|
sublist->FreeMask &= ~(1_u64 << slidx);
|
|
|
|
return effect;
|
|
}
|
|
|
|
void FreeEffect(al::Device *device, ALeffect *effect)
|
|
{
|
|
device->mEffectNames.erase(effect->id);
|
|
|
|
const ALuint id{effect->id - 1};
|
|
const size_t lidx{id >> 6};
|
|
const ALuint slidx{id & 0x3f};
|
|
|
|
std::destroy_at(effect);
|
|
|
|
device->EffectList[lidx].FreeMask |= 1_u64 << slidx;
|
|
}
|
|
|
|
[[nodiscard]] inline
|
|
auto LookupEffect(al::Device *device, ALuint id) noexcept -> ALeffect*
|
|
{
|
|
const size_t lidx{(id-1) >> 6};
|
|
const ALuint slidx{(id-1) & 0x3f};
|
|
|
|
if(lidx >= device->EffectList.size()) UNLIKELY
|
|
return nullptr;
|
|
EffectSubList &sublist = device->EffectList[lidx];
|
|
if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
|
|
return nullptr;
|
|
return al::to_address(sublist.Effects->begin() + slidx);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
AL_API DECL_FUNC2(void, alGenEffects, ALsizei,n, ALuint*,effects)
|
|
FORCE_ALIGN void AL_APIENTRY alGenEffectsDirect(ALCcontext *context, ALsizei n, ALuint *effects) noexcept
|
|
try {
|
|
if(n < 0)
|
|
context->throw_error(AL_INVALID_VALUE, "Generating {} effects", n);
|
|
if(n <= 0) UNLIKELY return;
|
|
|
|
auto *device = context->mALDevice.get();
|
|
auto effectlock = std::lock_guard{device->EffectLock};
|
|
|
|
const al::span eids{effects, static_cast<ALuint>(n)};
|
|
if(!EnsureEffects(device, eids.size()))
|
|
context->throw_error(AL_OUT_OF_MEMORY, "Failed to allocate {} effect{}", n,
|
|
(n==1) ? "" : "s");
|
|
|
|
std::generate(eids.begin(), eids.end(), [device]{ return AllocEffect(device)->id; });
|
|
}
|
|
catch(al::base_exception&) {
|
|
}
|
|
catch(std::exception &e) {
|
|
ERR("Caught exception: {}", e.what());
|
|
}
|
|
|
|
AL_API DECL_FUNC2(void, alDeleteEffects, ALsizei,n, const ALuint*,effects)
|
|
FORCE_ALIGN void AL_APIENTRY alDeleteEffectsDirect(ALCcontext *context, ALsizei n,
|
|
const ALuint *effects) noexcept
|
|
try {
|
|
if(n < 0)
|
|
context->throw_error(AL_INVALID_VALUE, "Deleting {} effects", n);
|
|
if(n <= 0) UNLIKELY return;
|
|
|
|
auto *device = context->mALDevice.get();
|
|
auto effectlock = std::lock_guard{device->EffectLock};
|
|
|
|
/* First try to find any effects that are invalid. */
|
|
auto validate_effect = [device](const ALuint eid) -> bool
|
|
{ return !eid || LookupEffect(device, eid) != nullptr; };
|
|
|
|
const al::span eids{effects, static_cast<ALuint>(n)};
|
|
auto inveffect = std::find_if_not(eids.begin(), eids.end(), validate_effect);
|
|
if(inveffect != eids.end())
|
|
context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", *inveffect);
|
|
|
|
/* All good. Delete non-0 effect IDs. */
|
|
auto delete_effect = [device](ALuint eid) -> void
|
|
{
|
|
if(ALeffect *effect{eid ? LookupEffect(device, eid) : nullptr})
|
|
FreeEffect(device, effect);
|
|
};
|
|
std::for_each(eids.begin(), eids.end(), delete_effect);
|
|
}
|
|
catch(al::base_exception&) {
|
|
}
|
|
catch(std::exception &e) {
|
|
ERR("Caught exception: {}", e.what());
|
|
}
|
|
|
|
AL_API DECL_FUNC1(ALboolean, alIsEffect, ALuint,effect)
|
|
FORCE_ALIGN ALboolean AL_APIENTRY alIsEffectDirect(ALCcontext *context, ALuint effect) noexcept
|
|
{
|
|
auto *device = context->mALDevice.get();
|
|
auto effectlock = std::lock_guard{device->EffectLock};
|
|
if(!effect || LookupEffect(device, effect))
|
|
return AL_TRUE;
|
|
return AL_FALSE;
|
|
}
|
|
|
|
AL_API DECL_FUNC3(void, alEffecti, ALuint,effect, ALenum,param, ALint,value)
|
|
FORCE_ALIGN void AL_APIENTRY alEffectiDirect(ALCcontext *context, ALuint effect, ALenum param,
|
|
ALint value) noexcept
|
|
try {
|
|
auto *device = context->mALDevice.get();
|
|
auto effectlock = std::lock_guard{device->EffectLock};
|
|
|
|
ALeffect *aleffect{LookupEffect(device, effect)};
|
|
if(!aleffect)
|
|
context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", effect);
|
|
|
|
switch(param)
|
|
{
|
|
case AL_EFFECT_TYPE:
|
|
if(value != AL_EFFECT_NULL)
|
|
{
|
|
auto check_effect = [value](const EffectList &item) -> bool
|
|
{ return value == item.val && !DisabledEffects.test(item.type); };
|
|
if(!std::any_of(gEffectList.cbegin(), gEffectList.cend(), check_effect))
|
|
context->throw_error(AL_INVALID_VALUE, "Effect type {:#04x} not supported",
|
|
as_unsigned(value));
|
|
}
|
|
|
|
InitEffectParams(aleffect, value);
|
|
return;
|
|
}
|
|
|
|
/* Call the appropriate handler */
|
|
std::visit([context,aleffect,param,value](auto &arg)
|
|
{
|
|
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
|
using PropType = typename Type::prop_type;
|
|
return arg.SetParami(context, std::get<PropType>(aleffect->Props), param, value);
|
|
}, aleffect->PropsVariant);
|
|
}
|
|
catch(al::base_exception&) {
|
|
}
|
|
catch(std::exception &e) {
|
|
ERR("Caught exception: {}", e.what());
|
|
}
|
|
|
|
AL_API DECL_FUNC3(void, alEffectiv, ALuint,effect, ALenum,param, const ALint*,values)
|
|
FORCE_ALIGN void AL_APIENTRY alEffectivDirect(ALCcontext *context, ALuint effect, ALenum param,
|
|
const ALint *values) noexcept
|
|
try {
|
|
switch(param)
|
|
{
|
|
case AL_EFFECT_TYPE:
|
|
alEffectiDirect(context, effect, param, *values);
|
|
return;
|
|
}
|
|
|
|
auto *device = context->mALDevice.get();
|
|
auto effectlock = std::lock_guard{device->EffectLock};
|
|
|
|
ALeffect *aleffect{LookupEffect(device, effect)};
|
|
if(!aleffect)
|
|
context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", effect);
|
|
|
|
/* Call the appropriate handler */
|
|
std::visit([context,aleffect,param,values](auto &arg)
|
|
{
|
|
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
|
using PropType = typename Type::prop_type;
|
|
return arg.SetParamiv(context, std::get<PropType>(aleffect->Props), param, values);
|
|
}, aleffect->PropsVariant);
|
|
}
|
|
catch(al::base_exception&) {
|
|
}
|
|
catch(std::exception &e) {
|
|
ERR("Caught exception: {}", e.what());
|
|
}
|
|
|
|
AL_API DECL_FUNC3(void, alEffectf, ALuint,effect, ALenum,param, ALfloat,value)
|
|
FORCE_ALIGN void AL_APIENTRY alEffectfDirect(ALCcontext *context, ALuint effect, ALenum param,
|
|
ALfloat value) noexcept
|
|
try {
|
|
auto *device = context->mALDevice.get();
|
|
auto effectlock = std::lock_guard{device->EffectLock};
|
|
|
|
ALeffect *aleffect{LookupEffect(device, effect)};
|
|
if(!aleffect)
|
|
context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", effect);
|
|
|
|
/* Call the appropriate handler */
|
|
std::visit([context,aleffect,param,value](auto &arg)
|
|
{
|
|
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
|
using PropType = typename Type::prop_type;
|
|
return arg.SetParamf(context, std::get<PropType>(aleffect->Props), param, value);
|
|
}, aleffect->PropsVariant);
|
|
}
|
|
catch(al::base_exception&) {
|
|
}
|
|
catch(std::exception &e) {
|
|
ERR("Caught exception: {}", e.what());
|
|
}
|
|
|
|
AL_API DECL_FUNC3(void, alEffectfv, ALuint,effect, ALenum,param, const ALfloat*,values)
|
|
FORCE_ALIGN void AL_APIENTRY alEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param,
|
|
const ALfloat *values) noexcept
|
|
try {
|
|
auto *device = context->mALDevice.get();
|
|
auto effectlock = std::lock_guard{device->EffectLock};
|
|
|
|
ALeffect *aleffect{LookupEffect(device, effect)};
|
|
if(!aleffect)
|
|
context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", effect);
|
|
|
|
/* Call the appropriate handler */
|
|
std::visit([context,aleffect,param,values](auto &arg)
|
|
{
|
|
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
|
using PropType = typename Type::prop_type;
|
|
return arg.SetParamfv(context, std::get<PropType>(aleffect->Props), param, values);
|
|
}, aleffect->PropsVariant);
|
|
}
|
|
catch(al::base_exception&) {
|
|
}
|
|
catch(std::exception &e) {
|
|
ERR("Caught exception: {}", e.what());
|
|
}
|
|
|
|
AL_API DECL_FUNC3(void, alGetEffecti, ALuint,effect, ALenum,param, ALint*,value)
|
|
FORCE_ALIGN void AL_APIENTRY alGetEffectiDirect(ALCcontext *context, ALuint effect, ALenum param,
|
|
ALint *value) noexcept
|
|
try {
|
|
auto *device = context->mALDevice.get();
|
|
auto effectlock = std::lock_guard{device->EffectLock};
|
|
|
|
const ALeffect *aleffect{LookupEffect(device, effect)};
|
|
if(!aleffect)
|
|
context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", effect);
|
|
|
|
switch(param)
|
|
{
|
|
case AL_EFFECT_TYPE:
|
|
*value = aleffect->type;
|
|
return;
|
|
}
|
|
|
|
/* Call the appropriate handler */
|
|
std::visit([context,aleffect,param,value](auto &arg)
|
|
{
|
|
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
|
using PropType = typename Type::prop_type;
|
|
return arg.GetParami(context, std::get<PropType>(aleffect->Props), param, value);
|
|
}, aleffect->PropsVariant);
|
|
}
|
|
catch(al::base_exception&) {
|
|
}
|
|
catch(std::exception &e) {
|
|
ERR("Caught exception: {}", e.what());
|
|
}
|
|
|
|
AL_API DECL_FUNC3(void, alGetEffectiv, ALuint,effect, ALenum,param, ALint*,values)
|
|
FORCE_ALIGN void AL_APIENTRY alGetEffectivDirect(ALCcontext *context, ALuint effect, ALenum param,
|
|
ALint *values) noexcept
|
|
try {
|
|
switch(param)
|
|
{
|
|
case AL_EFFECT_TYPE:
|
|
alGetEffectiDirect(context, effect, param, values);
|
|
return;
|
|
}
|
|
|
|
auto *device = context->mALDevice.get();
|
|
auto effectlock = std::lock_guard{device->EffectLock};
|
|
|
|
const ALeffect *aleffect{LookupEffect(device, effect)};
|
|
if(!aleffect)
|
|
context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", effect);
|
|
|
|
/* Call the appropriate handler */
|
|
std::visit([context,aleffect,param,values](auto &arg)
|
|
{
|
|
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
|
using PropType = typename Type::prop_type;
|
|
return arg.GetParamiv(context, std::get<PropType>(aleffect->Props), param, values);
|
|
}, aleffect->PropsVariant);
|
|
}
|
|
catch(al::base_exception&) {
|
|
}
|
|
catch(std::exception &e) {
|
|
ERR("Caught exception: {}", e.what());
|
|
}
|
|
|
|
AL_API DECL_FUNC3(void, alGetEffectf, ALuint,effect, ALenum,param, ALfloat*,value)
|
|
FORCE_ALIGN void AL_APIENTRY alGetEffectfDirect(ALCcontext *context, ALuint effect, ALenum param,
|
|
ALfloat *value) noexcept
|
|
try {
|
|
auto *device = context->mALDevice.get();
|
|
auto effectlock = std::lock_guard{device->EffectLock};
|
|
|
|
const ALeffect *aleffect{LookupEffect(device, effect)};
|
|
if(!aleffect)
|
|
context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", effect);
|
|
|
|
/* Call the appropriate handler */
|
|
std::visit([context,aleffect,param,value](auto &arg)
|
|
{
|
|
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
|
using PropType = typename Type::prop_type;
|
|
return arg.GetParamf(context, std::get<PropType>(aleffect->Props), param, value);
|
|
}, aleffect->PropsVariant);
|
|
}
|
|
catch(al::base_exception&) {
|
|
}
|
|
catch(std::exception &e) {
|
|
ERR("Caught exception: {}", e.what());
|
|
}
|
|
|
|
AL_API DECL_FUNC3(void, alGetEffectfv, ALuint,effect, ALenum,param, ALfloat*,values)
|
|
FORCE_ALIGN void AL_APIENTRY alGetEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param,
|
|
ALfloat *values) noexcept
|
|
try {
|
|
auto *device = context->mALDevice.get();
|
|
auto effectlock = std::lock_guard{device->EffectLock};
|
|
|
|
const ALeffect *aleffect{LookupEffect(device, effect)};
|
|
if(!aleffect)
|
|
context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", effect);
|
|
|
|
/* Call the appropriate handler */
|
|
std::visit([context,aleffect,param,values](auto &arg)
|
|
{
|
|
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
|
using PropType = typename Type::prop_type;
|
|
return arg.GetParamfv(context, std::get<PropType>(aleffect->Props), param, values);
|
|
}, aleffect->PropsVariant);
|
|
}
|
|
catch(al::base_exception&) {
|
|
}
|
|
catch(std::exception &e) {
|
|
ERR("Caught exception: {}", e.what());
|
|
}
|
|
|
|
|
|
void InitEffect(ALeffect *effect)
|
|
{
|
|
InitEffectParams(effect, AL_EFFECT_NULL);
|
|
}
|
|
|
|
void ALeffect::SetName(ALCcontext* context, ALuint id, std::string_view name)
|
|
{
|
|
auto *device = context->mALDevice.get();
|
|
auto effectlock = std::lock_guard{device->EffectLock};
|
|
|
|
auto effect = LookupEffect(device, id);
|
|
if(!effect)
|
|
context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", id);
|
|
|
|
device->mEffectNames.insert_or_assign(id, name);
|
|
}
|
|
|
|
|
|
EffectSubList::~EffectSubList()
|
|
{
|
|
if(!Effects)
|
|
return;
|
|
|
|
uint64_t usemask{~FreeMask};
|
|
while(usemask)
|
|
{
|
|
const int idx{al::countr_zero(usemask)};
|
|
std::destroy_at(al::to_address(Effects->begin()+idx));
|
|
usemask &= ~(1_u64 << idx);
|
|
}
|
|
FreeMask = ~usemask;
|
|
SubListAllocator{}.deallocate(Effects, 1);
|
|
Effects = nullptr;
|
|
}
|
|
|
|
|
|
struct EffectPreset {
|
|
const char name[32]; /* NOLINT(*-avoid-c-arrays) */
|
|
EFXEAXREVERBPROPERTIES props;
|
|
};
|
|
#define DECL(x) EffectPreset{#x, EFX_REVERB_PRESET_##x}
|
|
static constexpr std::array reverblist{
|
|
DECL(GENERIC),
|
|
DECL(PADDEDCELL),
|
|
DECL(ROOM),
|
|
DECL(BATHROOM),
|
|
DECL(LIVINGROOM),
|
|
DECL(STONEROOM),
|
|
DECL(AUDITORIUM),
|
|
DECL(CONCERTHALL),
|
|
DECL(CAVE),
|
|
DECL(ARENA),
|
|
DECL(HANGAR),
|
|
DECL(CARPETEDHALLWAY),
|
|
DECL(HALLWAY),
|
|
DECL(STONECORRIDOR),
|
|
DECL(ALLEY),
|
|
DECL(FOREST),
|
|
DECL(CITY),
|
|
DECL(MOUNTAINS),
|
|
DECL(QUARRY),
|
|
DECL(PLAIN),
|
|
DECL(PARKINGLOT),
|
|
DECL(SEWERPIPE),
|
|
DECL(UNDERWATER),
|
|
DECL(DRUGGED),
|
|
DECL(DIZZY),
|
|
DECL(PSYCHOTIC),
|
|
|
|
DECL(CASTLE_SMALLROOM),
|
|
DECL(CASTLE_SHORTPASSAGE),
|
|
DECL(CASTLE_MEDIUMROOM),
|
|
DECL(CASTLE_LARGEROOM),
|
|
DECL(CASTLE_LONGPASSAGE),
|
|
DECL(CASTLE_HALL),
|
|
DECL(CASTLE_CUPBOARD),
|
|
DECL(CASTLE_COURTYARD),
|
|
DECL(CASTLE_ALCOVE),
|
|
|
|
DECL(FACTORY_SMALLROOM),
|
|
DECL(FACTORY_SHORTPASSAGE),
|
|
DECL(FACTORY_MEDIUMROOM),
|
|
DECL(FACTORY_LARGEROOM),
|
|
DECL(FACTORY_LONGPASSAGE),
|
|
DECL(FACTORY_HALL),
|
|
DECL(FACTORY_CUPBOARD),
|
|
DECL(FACTORY_COURTYARD),
|
|
DECL(FACTORY_ALCOVE),
|
|
|
|
DECL(ICEPALACE_SMALLROOM),
|
|
DECL(ICEPALACE_SHORTPASSAGE),
|
|
DECL(ICEPALACE_MEDIUMROOM),
|
|
DECL(ICEPALACE_LARGEROOM),
|
|
DECL(ICEPALACE_LONGPASSAGE),
|
|
DECL(ICEPALACE_HALL),
|
|
DECL(ICEPALACE_CUPBOARD),
|
|
DECL(ICEPALACE_COURTYARD),
|
|
DECL(ICEPALACE_ALCOVE),
|
|
|
|
DECL(SPACESTATION_SMALLROOM),
|
|
DECL(SPACESTATION_SHORTPASSAGE),
|
|
DECL(SPACESTATION_MEDIUMROOM),
|
|
DECL(SPACESTATION_LARGEROOM),
|
|
DECL(SPACESTATION_LONGPASSAGE),
|
|
DECL(SPACESTATION_HALL),
|
|
DECL(SPACESTATION_CUPBOARD),
|
|
DECL(SPACESTATION_ALCOVE),
|
|
|
|
DECL(WOODEN_SMALLROOM),
|
|
DECL(WOODEN_SHORTPASSAGE),
|
|
DECL(WOODEN_MEDIUMROOM),
|
|
DECL(WOODEN_LARGEROOM),
|
|
DECL(WOODEN_LONGPASSAGE),
|
|
DECL(WOODEN_HALL),
|
|
DECL(WOODEN_CUPBOARD),
|
|
DECL(WOODEN_COURTYARD),
|
|
DECL(WOODEN_ALCOVE),
|
|
|
|
DECL(SPORT_EMPTYSTADIUM),
|
|
DECL(SPORT_SQUASHCOURT),
|
|
DECL(SPORT_SMALLSWIMMINGPOOL),
|
|
DECL(SPORT_LARGESWIMMINGPOOL),
|
|
DECL(SPORT_GYMNASIUM),
|
|
DECL(SPORT_FULLSTADIUM),
|
|
DECL(SPORT_STADIUMTANNOY),
|
|
|
|
DECL(PREFAB_WORKSHOP),
|
|
DECL(PREFAB_SCHOOLROOM),
|
|
DECL(PREFAB_PRACTISEROOM),
|
|
DECL(PREFAB_OUTHOUSE),
|
|
DECL(PREFAB_CARAVAN),
|
|
|
|
DECL(DOME_TOMB),
|
|
DECL(PIPE_SMALL),
|
|
DECL(DOME_SAINTPAULS),
|
|
DECL(PIPE_LONGTHIN),
|
|
DECL(PIPE_LARGE),
|
|
DECL(PIPE_RESONANT),
|
|
|
|
DECL(OUTDOORS_BACKYARD),
|
|
DECL(OUTDOORS_ROLLINGPLAINS),
|
|
DECL(OUTDOORS_DEEPCANYON),
|
|
DECL(OUTDOORS_CREEK),
|
|
DECL(OUTDOORS_VALLEY),
|
|
|
|
DECL(MOOD_HEAVEN),
|
|
DECL(MOOD_HELL),
|
|
DECL(MOOD_MEMORY),
|
|
|
|
DECL(DRIVING_COMMENTATOR),
|
|
DECL(DRIVING_PITGARAGE),
|
|
DECL(DRIVING_INCAR_RACER),
|
|
DECL(DRIVING_INCAR_SPORTS),
|
|
DECL(DRIVING_INCAR_LUXURY),
|
|
DECL(DRIVING_FULLGRANDSTAND),
|
|
DECL(DRIVING_EMPTYGRANDSTAND),
|
|
DECL(DRIVING_TUNNEL),
|
|
|
|
DECL(CITY_STREETS),
|
|
DECL(CITY_SUBWAY),
|
|
DECL(CITY_MUSEUM),
|
|
DECL(CITY_LIBRARY),
|
|
DECL(CITY_UNDERPASS),
|
|
DECL(CITY_ABANDONED),
|
|
|
|
DECL(DUSTYROOM),
|
|
DECL(CHAPEL),
|
|
DECL(SMALLWATERROOM),
|
|
};
|
|
#undef DECL
|
|
|
|
void LoadReverbPreset(const std::string_view name, ALeffect *effect)
|
|
{
|
|
using namespace std::string_view_literals;
|
|
|
|
if(al::case_compare(name, "NONE"sv) == 0)
|
|
{
|
|
InitEffectParams(effect, AL_EFFECT_NULL);
|
|
TRACE("Loading reverb '{}'", "NONE");
|
|
return;
|
|
}
|
|
|
|
if(!DisabledEffects.test(EAXREVERB_EFFECT))
|
|
InitEffectParams(effect, AL_EFFECT_EAXREVERB);
|
|
else if(!DisabledEffects.test(REVERB_EFFECT))
|
|
InitEffectParams(effect, AL_EFFECT_REVERB);
|
|
else
|
|
InitEffectParams(effect, AL_EFFECT_NULL);
|
|
for(const auto &reverbitem : reverblist)
|
|
{
|
|
if(al::case_compare(name, std::data(reverbitem.name)) != 0)
|
|
continue;
|
|
|
|
TRACE("Loading reverb '{}'", std::data(reverbitem.name));
|
|
const auto &props = reverbitem.props;
|
|
auto &dst = std::get<ReverbProps>(effect->Props);
|
|
dst.Density = props.flDensity;
|
|
dst.Diffusion = props.flDiffusion;
|
|
dst.Gain = props.flGain;
|
|
dst.GainHF = props.flGainHF;
|
|
dst.GainLF = props.flGainLF;
|
|
dst.DecayTime = props.flDecayTime;
|
|
dst.DecayHFRatio = props.flDecayHFRatio;
|
|
dst.DecayLFRatio = props.flDecayLFRatio;
|
|
dst.ReflectionsGain = props.flReflectionsGain;
|
|
dst.ReflectionsDelay = props.flReflectionsDelay;
|
|
dst.ReflectionsPan[0] = props.flReflectionsPan[0];
|
|
dst.ReflectionsPan[1] = props.flReflectionsPan[1];
|
|
dst.ReflectionsPan[2] = props.flReflectionsPan[2];
|
|
dst.LateReverbGain = props.flLateReverbGain;
|
|
dst.LateReverbDelay = props.flLateReverbDelay;
|
|
dst.LateReverbPan[0] = props.flLateReverbPan[0];
|
|
dst.LateReverbPan[1] = props.flLateReverbPan[1];
|
|
dst.LateReverbPan[2] = props.flLateReverbPan[2];
|
|
dst.EchoTime = props.flEchoTime;
|
|
dst.EchoDepth = props.flEchoDepth;
|
|
dst.ModulationTime = props.flModulationTime;
|
|
dst.ModulationDepth = props.flModulationDepth;
|
|
dst.AirAbsorptionGainHF = props.flAirAbsorptionGainHF;
|
|
dst.HFReference = props.flHFReference;
|
|
dst.LFReference = props.flLFReference;
|
|
dst.RoomRolloffFactor = props.flRoomRolloffFactor;
|
|
dst.DecayHFLimit = props.iDecayHFLimit ? AL_TRUE : AL_FALSE;
|
|
return;
|
|
}
|
|
|
|
WARN("Reverb preset '{}' not found", name);
|
|
}
|
|
|
|
bool IsValidEffectType(ALenum type) noexcept
|
|
{
|
|
if(type == AL_EFFECT_NULL)
|
|
return true;
|
|
|
|
auto check_effect = [type](const EffectList &item) noexcept -> bool
|
|
{ return type == item.val && !DisabledEffects.test(item.type); };
|
|
return std::any_of(gEffectList.cbegin(), gEffectList.cend(), check_effect);
|
|
}
|