#ifndef EAX_EFFECT_INCLUDED #define EAX_EFFECT_INCLUDED #include #include #include #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; template struct overloaded : Ts... { using Ts::operator()...; }; template overloaded(Ts...) -> overloaded; 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 static void defer(const EaxCall& call, TProperty& property) { const auto& value = call.get_value(); TValidator{}(value); property = value; } template static void defer(const EaxCall& call, TProperties& properties, TProperty&) { const auto& value = call.get_value(); TValidator{}(value); TDeferrer{}(properties, value); } template static void defer3(const EaxCall& call, EAXREVERBPROPERTIES& properties, TProperty& property) { const auto& value = call.get_value(); 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); 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 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 translate(const EAX_REVERBPROPERTIES& src, EAXREVERBPROPERTIES& dst) noexcept; static void translate(const EAX20LISTENERPROPERTIES& src, EAXREVERBPROPERTIES& dst) noexcept; }; template struct EaxCommitter { struct Exception; EaxCommitter(EaxEffectProps &eaxprops, EffectProps &alprops) : mEaxProps{eaxprops}, mAlProps{alprops} { } EaxEffectProps &mEaxProps; EffectProps &mAlProps; template static void defer(const EaxCall& call, TProperty& property) { const auto& value = call.get_value(); TValidator{}(value); property = value; } [[noreturn]] static void fail(const char *message); [[noreturn]] static void fail_unknown_property_id() { fail(EaxEffectErrorMessages::unknown_property_id()); } }; struct EaxAutowahCommitter : public EaxCommitter { using EaxCommitter::EaxCommitter; bool commit(const EAXAUTOWAHPROPERTIES &props); static void SetDefaults(EaxEffectProps &props); static void Get(const EaxCall &call, const EAXAUTOWAHPROPERTIES &props); static void Set(const EaxCall &call, EAXAUTOWAHPROPERTIES &props); }; struct EaxChorusCommitter : public EaxCommitter { using EaxCommitter::EaxCommitter; bool commit(const EAXCHORUSPROPERTIES &props); static void SetDefaults(EaxEffectProps &props); static void Get(const EaxCall &call, const EAXCHORUSPROPERTIES &props); static void Set(const EaxCall &call, EAXCHORUSPROPERTIES &props); }; struct EaxCompressorCommitter : public EaxCommitter { using EaxCommitter::EaxCommitter; bool commit(const EAXAGCCOMPRESSORPROPERTIES &props); static void SetDefaults(EaxEffectProps &props); static void Get(const EaxCall &call, const EAXAGCCOMPRESSORPROPERTIES &props); static void Set(const EaxCall &call, EAXAGCCOMPRESSORPROPERTIES &props); }; struct EaxDistortionCommitter : public EaxCommitter { using EaxCommitter::EaxCommitter; bool commit(const EAXDISTORTIONPROPERTIES &props); static void SetDefaults(EaxEffectProps &props); static void Get(const EaxCall &call, const EAXDISTORTIONPROPERTIES &props); static void Set(const EaxCall &call, EAXDISTORTIONPROPERTIES &props); }; struct EaxEchoCommitter : public EaxCommitter { using EaxCommitter::EaxCommitter; bool commit(const EAXECHOPROPERTIES &props); static void SetDefaults(EaxEffectProps &props); static void Get(const EaxCall &call, const EAXECHOPROPERTIES &props); static void Set(const EaxCall &call, EAXECHOPROPERTIES &props); }; struct EaxEqualizerCommitter : public EaxCommitter { using EaxCommitter::EaxCommitter; bool commit(const EAXEQUALIZERPROPERTIES &props); static void SetDefaults(EaxEffectProps &props); static void Get(const EaxCall &call, const EAXEQUALIZERPROPERTIES &props); static void Set(const EaxCall &call, EAXEQUALIZERPROPERTIES &props); }; struct EaxFlangerCommitter : public EaxCommitter { using EaxCommitter::EaxCommitter; bool commit(const EAXFLANGERPROPERTIES &props); static void SetDefaults(EaxEffectProps &props); static void Get(const EaxCall &call, const EAXFLANGERPROPERTIES &props); static void Set(const EaxCall &call, EAXFLANGERPROPERTIES &props); }; struct EaxFrequencyShifterCommitter : public EaxCommitter { using EaxCommitter::EaxCommitter; bool commit(const EAXFREQUENCYSHIFTERPROPERTIES &props); static void SetDefaults(EaxEffectProps &props); static void Get(const EaxCall &call, const EAXFREQUENCYSHIFTERPROPERTIES &props); static void Set(const EaxCall &call, EAXFREQUENCYSHIFTERPROPERTIES &props); }; struct EaxModulatorCommitter : public EaxCommitter { using EaxCommitter::EaxCommitter; bool commit(const EAXRINGMODULATORPROPERTIES &props); static void SetDefaults(EaxEffectProps &props); static void Get(const EaxCall &call, const EAXRINGMODULATORPROPERTIES &props); static void Set(const EaxCall &call, EAXRINGMODULATORPROPERTIES &props); }; struct EaxPitchShifterCommitter : public EaxCommitter { using EaxCommitter::EaxCommitter; bool commit(const EAXPITCHSHIFTERPROPERTIES &props); static void SetDefaults(EaxEffectProps &props); static void Get(const EaxCall &call, const EAXPITCHSHIFTERPROPERTIES &props); static void Set(const EaxCall &call, EAXPITCHSHIFTERPROPERTIES &props); }; struct EaxVocalMorpherCommitter : public EaxCommitter { using EaxCommitter::EaxCommitter; bool commit(const EAXVOCALMORPHERPROPERTIES &props); static void SetDefaults(EaxEffectProps &props); static void Get(const EaxCall &call, const EAXVOCALMORPHERPROPERTIES &props); static void Set(const EaxCall &call, EAXVOCALMORPHERPROPERTIES &props); }; struct EaxNullCommitter : public EaxCommitter { using EaxCommitter::EaxCommitter; bool commit(const std::monostate &props); static void SetDefaults(EaxEffectProps &props); static void Get(const EaxCall &call, const std::monostate &props); static void Set(const EaxCall &call, std::monostate &props); }; template struct CommitterFromProps { }; template<> struct CommitterFromProps { using type = EaxNullCommitter; }; template<> struct CommitterFromProps { using type = EaxReverbCommitter; }; template<> struct CommitterFromProps { using type = EaxChorusCommitter; }; template<> struct CommitterFromProps { using type = EaxCompressorCommitter; }; template<> struct CommitterFromProps { using type = EaxAutowahCommitter; }; template<> struct CommitterFromProps { using type = EaxDistortionCommitter; }; template<> struct CommitterFromProps { using type = EaxEchoCommitter; }; template<> struct CommitterFromProps { using type = EaxEqualizerCommitter; }; template<> struct CommitterFromProps { using type = EaxFlangerCommitter; }; template<> struct CommitterFromProps { using type = EaxFrequencyShifterCommitter; }; template<> struct CommitterFromProps { using type = EaxModulatorCommitter; }; template<> struct CommitterFromProps { using type = EaxPitchShifterCommitter; }; template<> struct CommitterFromProps { using type = EaxVocalMorpherCommitter; }; template using CommitterFor = typename CommitterFromProps>>::type; 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_{}; void call_set_defaults(const ALenum altype, EaxEffectProps &props) { switch(altype) { case AL_EFFECT_EAXREVERB: return EaxReverbCommitter::SetDefaults(props); case AL_EFFECT_CHORUS: return EaxChorusCommitter::SetDefaults(props); case AL_EFFECT_AUTOWAH: return EaxAutowahCommitter::SetDefaults(props); case AL_EFFECT_COMPRESSOR: return EaxCompressorCommitter::SetDefaults(props); case AL_EFFECT_DISTORTION: return EaxDistortionCommitter::SetDefaults(props); case AL_EFFECT_ECHO: return EaxEchoCommitter::SetDefaults(props); case AL_EFFECT_EQUALIZER: return EaxEqualizerCommitter::SetDefaults(props); case AL_EFFECT_FLANGER: return EaxFlangerCommitter::SetDefaults(props); case AL_EFFECT_FREQUENCY_SHIFTER: return EaxFrequencyShifterCommitter::SetDefaults(props); case AL_EFFECT_RING_MODULATOR: return EaxModulatorCommitter::SetDefaults(props); case AL_EFFECT_PITCH_SHIFTER: return EaxPitchShifterCommitter::SetDefaults(props); case AL_EFFECT_VOCAL_MORPHER: return EaxVocalMorpherCommitter::SetDefaults(props); case AL_EFFECT_NULL: break; } return EaxNullCommitter::SetDefaults(props); } template void init() { EaxReverbCommitter::SetDefaults(state1_.d); state1_.i = state1_.d; EaxReverbCommitter::SetDefaults(state2_.d); state2_.i = state2_.d; EaxReverbCommitter::SetDefaults(state3_.d); state3_.i = state3_.d; T::SetDefaults(state4_.d); state4_.i = state4_.d; T::SetDefaults(state5_.d); state5_.i = state5_.d; } void set_defaults(int eax_version, ALenum altype) { switch(eax_version) { case 1: EaxReverbCommitter::SetDefaults(state1_.d); break; case 2: EaxReverbCommitter::SetDefaults(state2_.d); break; case 3: EaxReverbCommitter::SetDefaults(state3_.d); break; case 4: call_set_defaults(altype, state4_.d); break; case 5: call_set_defaults(altype, state5_.d); break; } changed_ = true; } static void call_set(const EaxCall &call, EaxEffectProps &props) { return std::visit([&](auto &arg) { return CommitterFor::Set(call, arg); }, props); } void set(const EaxCall &call) { switch(call.get_version()) { case 1: EaxReverbCommitter::Set(call, state1_.d); break; case 2: EaxReverbCommitter::Set(call, state2_.d); break; case 3: EaxReverbCommitter::Set(call, state3_.d); break; case 4: call_set(call, state4_.d); break; case 5: call_set(call, state5_.d); break; } changed_ = true; } static void call_get(const EaxCall &call, const EaxEffectProps &props) { return std::visit([&](auto &arg) { return CommitterFor::Get(call, arg); }, props); } void get(const EaxCall &call) { switch(call.get_version()) { case 1: EaxReverbCommitter::Get(call, state1_.d); break; case 2: EaxReverbCommitter::Get(call, state2_.d); break; case 3: EaxReverbCommitter::Get(call, state3_.d); break; case 4: call_get(call, state4_.d); break; case 5: call_get(call, state5_.d); break; } } bool call_commit(const EaxEffectProps &props) { return std::visit([&](auto &arg) { return CommitterFor{props_, al_effect_props_}.commit(arg); }, 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 |= EaxReverbCommitter{props_, al_effect_props_}.commit(state1_.d); break; case 2: state2_.i = state2_.d; ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state2_.d); break; case 3: state3_.i = state3_.d; ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(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; #endif // !EAX_EFFECT_INCLUDED