axmol/thirdparty/openal/al/eax/effect.h

414 lines
15 KiB
C++

#ifndef EAX_EFFECT_INCLUDED
#define EAX_EFFECT_INCLUDED
#include <cassert>
#include <memory>
#include <variant>
#include "alnumeric.h"
#include "AL/al.h"
#include "core/effects/base.h"
#include "call.h"
struct EaxEffectErrorMessages {
static constexpr auto unknown_property_id() noexcept { return "Unknown property id."; }
static constexpr auto unknown_version() noexcept { return "Unknown version."; }
}; // EaxEffectErrorMessages
using EaxEffectProps = std::variant<std::monostate,
EAXREVERBPROPERTIES,
EAXCHORUSPROPERTIES,
EAXAUTOWAHPROPERTIES,
EAXAGCCOMPRESSORPROPERTIES,
EAXDISTORTIONPROPERTIES,
EAXECHOPROPERTIES,
EAXEQUALIZERPROPERTIES,
EAXFLANGERPROPERTIES,
EAXFREQUENCYSHIFTERPROPERTIES,
EAXRINGMODULATORPROPERTIES,
EAXPITCHSHIFTERPROPERTIES,
EAXVOCALMORPHERPROPERTIES>;
template<typename... Ts>
struct overloaded : Ts... { using Ts::operator()...; };
template<typename... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
constexpr ALenum EnumFromEaxEffectType(const EaxEffectProps &props) noexcept
{
return std::visit(overloaded{
[](const std::monostate&) noexcept { return AL_EFFECT_NULL; },
[](const EAXREVERBPROPERTIES&) noexcept { return AL_EFFECT_EAXREVERB; },
[](const EAXCHORUSPROPERTIES&) noexcept { return AL_EFFECT_CHORUS; },
[](const EAXAUTOWAHPROPERTIES&) noexcept { return AL_EFFECT_AUTOWAH; },
[](const EAXAGCCOMPRESSORPROPERTIES&) noexcept { return AL_EFFECT_COMPRESSOR; },
[](const EAXDISTORTIONPROPERTIES&) noexcept { return AL_EFFECT_DISTORTION; },
[](const EAXECHOPROPERTIES&) noexcept { return AL_EFFECT_ECHO; },
[](const EAXEQUALIZERPROPERTIES&) noexcept { return AL_EFFECT_EQUALIZER; },
[](const EAXFLANGERPROPERTIES&) noexcept { return AL_EFFECT_FLANGER; },
[](const EAXFREQUENCYSHIFTERPROPERTIES&) noexcept { return AL_EFFECT_FREQUENCY_SHIFTER; },
[](const EAXRINGMODULATORPROPERTIES&) noexcept { return AL_EFFECT_RING_MODULATOR; },
[](const EAXPITCHSHIFTERPROPERTIES&) noexcept { return AL_EFFECT_PITCH_SHIFTER; },
[](const EAXVOCALMORPHERPROPERTIES&) noexcept { return AL_EFFECT_VOCAL_MORPHER; }
}, props);
}
struct EaxReverbCommitter {
struct Exception;
EaxReverbCommitter(EaxEffectProps &eaxprops, EffectProps &alprops)
: mEaxProps{eaxprops}, mAlProps{alprops}
{ }
EaxEffectProps &mEaxProps;
EffectProps &mAlProps;
[[noreturn]] static void fail(const char* message);
[[noreturn]] static void fail_unknown_property_id()
{ fail(EaxEffectErrorMessages::unknown_property_id()); }
template<typename TValidator, typename TProperty>
static void defer(const EaxCall& call, TProperty& property)
{
const auto& value = call.get_value<Exception, const TProperty>();
TValidator{}(value);
property = value;
}
template<typename TValidator, typename TDeferrer, typename TProperties, typename TProperty>
static void defer(const EaxCall& call, TProperties& properties, TProperty&)
{
const auto& value = call.get_value<Exception, const TProperty>();
TValidator{}(value);
TDeferrer{}(properties, value);
}
template<typename TValidator, typename TProperty>
static void defer3(const EaxCall& call, EAXREVERBPROPERTIES& properties, TProperty& property)
{
const auto& value = call.get_value<Exception, const TProperty>();
TValidator{}(value);
if (value == property)
return;
property = value;
properties.ulEnvironment = EAX_ENVIRONMENT_UNDEFINED;
}
bool commit(const EAX_REVERBPROPERTIES &props);
bool commit(const EAX20LISTENERPROPERTIES &props);
bool commit(const EAXREVERBPROPERTIES &props);
bool commit(const EaxEffectProps &props);
static void SetDefaults(EAX_REVERBPROPERTIES &props);
static void SetDefaults(EAX20LISTENERPROPERTIES &props);
static void SetDefaults(EAXREVERBPROPERTIES &props);
static void SetDefaults(EaxEffectProps &props);
static void Get(const EaxCall &call, const EAX_REVERBPROPERTIES &props);
static void Get(const EaxCall &call, const EAX20LISTENERPROPERTIES &props);
static void Get(const EaxCall &call, const EAXREVERBPROPERTIES &props);
static void Get(const EaxCall &call, const EaxEffectProps &props);
static void Set(const EaxCall &call, EAX_REVERBPROPERTIES &props);
static void Set(const EaxCall &call, EAX20LISTENERPROPERTIES &props);
static void Set(const EaxCall &call, EAXREVERBPROPERTIES &props);
static void Set(const EaxCall &call, EaxEffectProps &props);
static void translate(const EAX_REVERBPROPERTIES& src, EaxEffectProps& dst) noexcept;
static void translate(const EAX20LISTENERPROPERTIES& src, EaxEffectProps& dst) noexcept;
static void translate(const EAXREVERBPROPERTIES& src, EaxEffectProps& dst) noexcept;
};
template<typename T>
struct EaxCommitter {
struct Exception;
EaxCommitter(EaxEffectProps &eaxprops, EffectProps &alprops)
: mEaxProps{eaxprops}, mAlProps{alprops}
{ }
EaxEffectProps &mEaxProps;
EffectProps &mAlProps;
template<typename TValidator, typename TProperty>
static void defer(const EaxCall& call, TProperty& property)
{
const auto& value = call.get_value<Exception, const TProperty>();
TValidator{}(value);
property = value;
}
[[noreturn]] static void fail(const char *message);
[[noreturn]] static void fail_unknown_property_id()
{ fail(EaxEffectErrorMessages::unknown_property_id()); }
bool commit(const EaxEffectProps &props);
static void SetDefaults(EaxEffectProps &props);
static void Get(const EaxCall &call, const EaxEffectProps &props);
static void Set(const EaxCall &call, EaxEffectProps &props);
};
struct EaxAutowahCommitter : public EaxCommitter<EaxAutowahCommitter> {
using EaxCommitter<EaxAutowahCommitter>::EaxCommitter;
};
struct EaxChorusCommitter : public EaxCommitter<EaxChorusCommitter> {
using EaxCommitter<EaxChorusCommitter>::EaxCommitter;
};
struct EaxCompressorCommitter : public EaxCommitter<EaxCompressorCommitter> {
using EaxCommitter<EaxCompressorCommitter>::EaxCommitter;
};
struct EaxDistortionCommitter : public EaxCommitter<EaxDistortionCommitter> {
using EaxCommitter<EaxDistortionCommitter>::EaxCommitter;
};
struct EaxEchoCommitter : public EaxCommitter<EaxEchoCommitter> {
using EaxCommitter<EaxEchoCommitter>::EaxCommitter;
};
struct EaxEqualizerCommitter : public EaxCommitter<EaxEqualizerCommitter> {
using EaxCommitter<EaxEqualizerCommitter>::EaxCommitter;
};
struct EaxFlangerCommitter : public EaxCommitter<EaxFlangerCommitter> {
using EaxCommitter<EaxFlangerCommitter>::EaxCommitter;
};
struct EaxFrequencyShifterCommitter : public EaxCommitter<EaxFrequencyShifterCommitter> {
using EaxCommitter<EaxFrequencyShifterCommitter>::EaxCommitter;
};
struct EaxModulatorCommitter : public EaxCommitter<EaxModulatorCommitter> {
using EaxCommitter<EaxModulatorCommitter>::EaxCommitter;
};
struct EaxPitchShifterCommitter : public EaxCommitter<EaxPitchShifterCommitter> {
using EaxCommitter<EaxPitchShifterCommitter>::EaxCommitter;
};
struct EaxVocalMorpherCommitter : public EaxCommitter<EaxVocalMorpherCommitter> {
using EaxCommitter<EaxVocalMorpherCommitter>::EaxCommitter;
};
struct EaxNullCommitter : public EaxCommitter<EaxNullCommitter> {
using EaxCommitter<EaxNullCommitter>::EaxCommitter;
};
class EaxEffect {
public:
EaxEffect() noexcept = default;
~EaxEffect() = default;
ALenum al_effect_type_{AL_EFFECT_NULL};
EffectProps al_effect_props_{};
using Props1 = EAX_REVERBPROPERTIES;
using Props2 = EAX20LISTENERPROPERTIES;
using Props3 = EAXREVERBPROPERTIES;
using Props4 = EaxEffectProps;
struct State1 {
Props1 i; // Immediate.
Props1 d; // Deferred.
};
struct State2 {
Props2 i; // Immediate.
Props2 d; // Deferred.
};
struct State3 {
Props3 i; // Immediate.
Props3 d; // Deferred.
};
struct State4 {
Props4 i; // Immediate.
Props4 d; // Deferred.
};
int version_{};
bool changed_{};
Props4 props_{};
State1 state1_{};
State2 state2_{};
State3 state3_{};
State4 state4_{};
State4 state5_{};
template<typename T, typename ...Args>
void call_set_defaults(Args&& ...args)
{ return T::SetDefaults(std::forward<Args>(args)...); }
void call_set_defaults(const ALenum altype, EaxEffectProps &props)
{
if(altype == AL_EFFECT_EAXREVERB)
return call_set_defaults<EaxReverbCommitter>(props);
if(altype == AL_EFFECT_CHORUS)
return call_set_defaults<EaxChorusCommitter>(props);
if(altype == AL_EFFECT_AUTOWAH)
return call_set_defaults<EaxAutowahCommitter>(props);
if(altype == AL_EFFECT_COMPRESSOR)
return call_set_defaults<EaxCompressorCommitter>(props);
if(altype == AL_EFFECT_DISTORTION)
return call_set_defaults<EaxDistortionCommitter>(props);
if(altype == AL_EFFECT_ECHO)
return call_set_defaults<EaxEchoCommitter>(props);
if(altype == AL_EFFECT_EQUALIZER)
return call_set_defaults<EaxEqualizerCommitter>(props);
if(altype == AL_EFFECT_FLANGER)
return call_set_defaults<EaxFlangerCommitter>(props);
if(altype == AL_EFFECT_FREQUENCY_SHIFTER)
return call_set_defaults<EaxFrequencyShifterCommitter>(props);
if(altype == AL_EFFECT_RING_MODULATOR)
return call_set_defaults<EaxModulatorCommitter>(props);
if(altype == AL_EFFECT_PITCH_SHIFTER)
return call_set_defaults<EaxPitchShifterCommitter>(props);
if(altype == AL_EFFECT_VOCAL_MORPHER)
return call_set_defaults<EaxVocalMorpherCommitter>(props);
return call_set_defaults<EaxNullCommitter>(props);
}
template<typename T>
void init()
{
call_set_defaults<EaxReverbCommitter>(state1_.d);
state1_.i = state1_.d;
call_set_defaults<EaxReverbCommitter>(state2_.d);
state2_.i = state2_.d;
call_set_defaults<EaxReverbCommitter>(state3_.d);
state3_.i = state3_.d;
call_set_defaults<T>(state4_.d);
state4_.i = state4_.d;
call_set_defaults<T>(state5_.d);
state5_.i = state5_.d;
}
void set_defaults(int eax_version, ALenum altype)
{
switch(eax_version)
{
case 1: call_set_defaults<EaxReverbCommitter>(state1_.d); break;
case 2: call_set_defaults<EaxReverbCommitter>(state2_.d); break;
case 3: call_set_defaults<EaxReverbCommitter>(state3_.d); break;
case 4: call_set_defaults(altype, state4_.d); break;
case 5: call_set_defaults(altype, state5_.d); break;
}
changed_ = true;
}
#define EAXCALL(Props, Callable, ...) \
if(std::holds_alternative<EAXREVERBPROPERTIES>(Props)) \
return Callable<EaxReverbCommitter>(__VA_ARGS__); \
if(std::holds_alternative<EAXCHORUSPROPERTIES>(Props)) \
return Callable<EaxChorusCommitter>(__VA_ARGS__); \
if(std::holds_alternative<EAXAUTOWAHPROPERTIES>(Props)) \
return Callable<EaxAutowahCommitter>(__VA_ARGS__); \
if(std::holds_alternative<EAXAGCCOMPRESSORPROPERTIES>(Props)) \
return Callable<EaxCompressorCommitter>(__VA_ARGS__); \
if(std::holds_alternative<EAXDISTORTIONPROPERTIES>(Props)) \
return Callable<EaxDistortionCommitter>(__VA_ARGS__); \
if(std::holds_alternative<EAXECHOPROPERTIES>(Props)) \
return Callable<EaxEchoCommitter>(__VA_ARGS__); \
if(std::holds_alternative<EAXEQUALIZERPROPERTIES>(Props)) \
return Callable<EaxEqualizerCommitter>(__VA_ARGS__); \
if(std::holds_alternative<EAXFLANGERPROPERTIES>(Props)) \
return Callable<EaxFlangerCommitter>(__VA_ARGS__); \
if(std::holds_alternative<EAXFREQUENCYSHIFTERPROPERTIES>(Props)) \
return Callable<EaxFrequencyShifterCommitter>(__VA_ARGS__); \
if(std::holds_alternative<EAXRINGMODULATORPROPERTIES>(Props)) \
return Callable<EaxModulatorCommitter>(__VA_ARGS__); \
if(std::holds_alternative<EAXPITCHSHIFTERPROPERTIES>(Props)) \
return Callable<EaxPitchShifterCommitter>(__VA_ARGS__); \
if(std::holds_alternative<EAXVOCALMORPHERPROPERTIES>(Props)) \
return Callable<EaxVocalMorpherCommitter>(__VA_ARGS__); \
return Callable<EaxNullCommitter>(__VA_ARGS__)
template<typename T, typename ...Args>
static void call_set(Args&& ...args)
{ return T::Set(std::forward<Args>(args)...); }
static void call_set(const EaxCall &call, EaxEffectProps &props)
{ EAXCALL(props, call_set, call, props); }
void set(const EaxCall &call)
{
switch(call.get_version())
{
case 1: call_set<EaxReverbCommitter>(call, state1_.d); break;
case 2: call_set<EaxReverbCommitter>(call, state2_.d); break;
case 3: call_set<EaxReverbCommitter>(call, state3_.d); break;
case 4: call_set(call, state4_.d); break;
case 5: call_set(call, state5_.d); break;
}
changed_ = true;
}
template<typename T, typename ...Args>
static void call_get(Args&& ...args)
{ return T::Get(std::forward<Args>(args)...); }
static void call_get(const EaxCall &call, const EaxEffectProps &props)
{ EAXCALL(props, call_get, call, props); }
void get(const EaxCall &call)
{
switch(call.get_version())
{
case 1: call_get<EaxReverbCommitter>(call, state1_.d); break;
case 2: call_get<EaxReverbCommitter>(call, state2_.d); break;
case 3: call_get<EaxReverbCommitter>(call, state3_.d); break;
case 4: call_get(call, state4_.d); break;
case 5: call_get(call, state5_.d); break;
}
}
template<typename T, typename ...Args>
bool call_commit(Args&& ...args)
{ return T{props_, al_effect_props_}.commit(std::forward<Args>(args)...); }
bool call_commit(const EaxEffectProps &props)
{ EAXCALL(props, call_commit, props); }
bool commit(int eax_version)
{
changed_ |= version_ != eax_version;
if(!changed_) return false;
bool ret{version_ != eax_version};
version_ = eax_version;
changed_ = false;
switch(eax_version)
{
case 1:
state1_.i = state1_.d;
ret |= call_commit<EaxReverbCommitter>(state1_.d);
break;
case 2:
state2_.i = state2_.d;
ret |= call_commit<EaxReverbCommitter>(state2_.d);
break;
case 3:
state3_.i = state3_.d;
ret |= call_commit<EaxReverbCommitter>(state3_.d);
break;
case 4:
state4_.i = state4_.d;
ret |= call_commit(state4_.d);
break;
case 5:
state5_.i = state5_.d;
ret |= call_commit(state5_.d);
break;
}
al_effect_type_ = EnumFromEaxEffectType(props_);
return ret;
}
#undef EAXCALL
}; // EaxEffect
using EaxEffectUPtr = std::unique_ptr<EaxEffect>;
#endif // !EAX_EFFECT_INCLUDED