diff options
author | Boris I. Bendovsky <[email protected]> | 2022-01-30 14:47:32 +0200 |
---|---|---|
committer | GitHub <[email protected]> | 2022-01-30 04:47:32 -0800 |
commit | 19ed994dc30ed84ea7cbbb5152577669fc25caf6 (patch) | |
tree | f68933bf8f778806618bd6c0b1bf9ced1b0ccf08 /al/effects/chorus.cpp | |
parent | 619249371a40f03cf988d1f5750d643df797c485 (diff) |
Add EAX extensions (EAX 2.0-5.0, X-RAM) (#632)
* Add EAX extensions (EAX 2.0-5.0, X-RAM)
* Comment out C++17 leftovers
* Remove everything related to patching
* Update alsoftrc.sample
* Rewrite integration
* Fix GCC compilation under Linux
* Always reset EAX effect properties when loading it into FX slot
Diffstat (limited to 'al/effects/chorus.cpp')
-rw-r--r-- | al/effects/chorus.cpp | 1240 |
1 files changed, 1240 insertions, 0 deletions
diff --git a/al/effects/chorus.cpp b/al/effects/chorus.cpp index 2466d97e..ed994fbb 100644 --- a/al/effects/chorus.cpp +++ b/al/effects/chorus.cpp @@ -11,6 +11,15 @@ #include "core/logging.h" #include "effects.h" +#if ALSOFT_EAX +#include <cassert> + +#include "alnumeric.h" + +#include "al/eax_exception.h" +#include "al/eax_utils.h" +#endif // ALSOFT_EAX + namespace { @@ -279,3 +288,1234 @@ const EffectProps ChorusEffectProps{genDefaultChorusProps()}; DEFINE_ALEFFECT_VTABLE(Flanger); const EffectProps FlangerEffectProps{genDefaultFlangerProps()}; + + +#if ALSOFT_EAX +namespace +{ + + +void eax_set_efx_waveform( + ALenum waveform, + EffectProps& al_effect_props) +{ + const auto efx_waveform = WaveformFromEnum(waveform); + assert(efx_waveform.has_value()); + al_effect_props.Chorus.Waveform = *efx_waveform; +} + +void eax_set_efx_phase( + ALint phase, + EffectProps& al_effect_props) +{ + al_effect_props.Chorus.Phase = phase; +} + +void eax_set_efx_rate( + ALfloat rate, + EffectProps& al_effect_props) +{ + al_effect_props.Chorus.Rate = rate; +} + +void eax_set_efx_depth( + ALfloat depth, + EffectProps& al_effect_props) +{ + al_effect_props.Chorus.Depth = depth; +} + +void eax_set_efx_feedback( + ALfloat feedback, + EffectProps& al_effect_props) +{ + al_effect_props.Chorus.Feedback = feedback; +} + +void eax_set_efx_delay( + ALfloat delay, + EffectProps& al_effect_props) +{ + al_effect_props.Chorus.Delay = delay; +} + + +using EaxChorusEffectDirtyFlagsValue = std::uint_least8_t; + +struct EaxChorusEffectDirtyFlags +{ + using EaxIsBitFieldStruct = bool; + + EaxChorusEffectDirtyFlagsValue ulWaveform : 1; + EaxChorusEffectDirtyFlagsValue lPhase : 1; + EaxChorusEffectDirtyFlagsValue flRate : 1; + EaxChorusEffectDirtyFlagsValue flDepth : 1; + EaxChorusEffectDirtyFlagsValue flFeedback : 1; + EaxChorusEffectDirtyFlagsValue flDelay : 1; +}; // EaxChorusEffectDirtyFlags + + +class EaxChorusEffect final : + public EaxEffect +{ +public: + EaxChorusEffect( + EffectProps& al_effect_props); + + + // [[nodiscard]] + bool dispatch( + const EaxEaxCall& eax_call) override; + + +private: + EffectProps& al_effect_props_; + EAXCHORUSPROPERTIES eax_{}; + EAXCHORUSPROPERTIES eax_d_{}; + EaxChorusEffectDirtyFlags eax_dirty_flags_{}; + + + void set_eax_defaults() noexcept; + + + void set_efx_waveform(); + + void set_efx_phase(); + + void set_efx_rate(); + + void set_efx_depth(); + + void set_efx_feedback(); + + void set_efx_delay(); + + void set_efx_defaults(); + + + // [[nodiscard]] + bool get( + const EaxEaxCall& eax_call); + + + void validate_waveform( + unsigned long ulWaveform); + + void validate_phase( + long lPhase); + + void validate_rate( + float flRate); + + void validate_depth( + float flDepth); + + void validate_feedback( + float flFeedback); + + void validate_delay( + float flDelay); + + void validate_all( + const EAXCHORUSPROPERTIES& eax_all); + + + void defer_waveform( + unsigned long ulWaveform); + + void defer_phase( + long lPhase); + + void defer_rate( + float flRate); + + void defer_depth( + float flDepth); + + void defer_feedback( + float flFeedback); + + void defer_delay( + float flDelay); + + void defer_all( + const EAXCHORUSPROPERTIES& eax_all); + + + void defer_waveform( + const EaxEaxCall& eax_call); + + void defer_phase( + const EaxEaxCall& eax_call); + + void defer_rate( + const EaxEaxCall& eax_call); + + void defer_depth( + const EaxEaxCall& eax_call); + + void defer_feedback( + const EaxEaxCall& eax_call); + + void defer_delay( + const EaxEaxCall& eax_call); + + void defer_all( + const EaxEaxCall& eax_call); + + + // [[nodiscard]] + bool apply_deferred(); + + // [[nodiscard]] + bool set( + const EaxEaxCall& eax_call); +}; // EaxChorusEffect + + +class EaxChorusEffectException : + public EaxException +{ +public: + explicit EaxChorusEffectException( + const char* message) + : + EaxException{"EAX_CHORUS_EFFECT", message} + { + } +}; // EaxChorusEffectException + + +EaxChorusEffect::EaxChorusEffect( + EffectProps& al_effect_props) + : + al_effect_props_{al_effect_props} +{ + set_eax_defaults(); + set_efx_defaults(); +} + +// [[nodiscard]] +bool EaxChorusEffect::dispatch( + const EaxEaxCall& eax_call) +{ + return eax_call.is_get() ? get(eax_call) : set(eax_call); +} + +void EaxChorusEffect::set_eax_defaults() noexcept +{ + eax_.ulWaveform = EAXCHORUS_DEFAULTWAVEFORM; + eax_.lPhase = EAXCHORUS_DEFAULTPHASE; + eax_.flRate = EAXCHORUS_DEFAULTRATE; + eax_.flDepth = EAXCHORUS_DEFAULTDEPTH; + eax_.flFeedback = EAXCHORUS_DEFAULTFEEDBACK; + eax_.flDelay = EAXCHORUS_DEFAULTDELAY; + + eax_d_ = eax_; +} + +void EaxChorusEffect::set_efx_waveform() +{ + const auto waveform = clamp( + static_cast<ALint>(eax_.ulWaveform), + AL_CHORUS_MIN_WAVEFORM, + AL_CHORUS_MAX_WAVEFORM); + + eax_set_efx_waveform(waveform, al_effect_props_); +} + +void EaxChorusEffect::set_efx_phase() +{ + const auto phase = clamp( + static_cast<ALint>(eax_.lPhase), + AL_CHORUS_MIN_PHASE, + AL_CHORUS_MAX_PHASE); + + eax_set_efx_phase(phase, al_effect_props_); +} + +void EaxChorusEffect::set_efx_rate() +{ + const auto rate = clamp( + eax_.flRate, + AL_CHORUS_MIN_RATE, + AL_CHORUS_MAX_RATE); + + eax_set_efx_rate(rate, al_effect_props_); +} + +void EaxChorusEffect::set_efx_depth() +{ + const auto depth = clamp( + eax_.flDepth, + AL_CHORUS_MIN_DEPTH, + AL_CHORUS_MAX_DEPTH); + + eax_set_efx_depth(depth, al_effect_props_); +} + +void EaxChorusEffect::set_efx_feedback() +{ + const auto feedback = clamp( + eax_.flFeedback, + AL_CHORUS_MIN_FEEDBACK, + AL_CHORUS_MAX_FEEDBACK); + + eax_set_efx_feedback(feedback, al_effect_props_); +} + +void EaxChorusEffect::set_efx_delay() +{ + const auto delay = clamp( + eax_.flDelay, + AL_CHORUS_MIN_DELAY, + AL_CHORUS_MAX_DELAY); + + eax_set_efx_delay(delay, al_effect_props_); +} + +void EaxChorusEffect::set_efx_defaults() +{ + set_efx_waveform(); + set_efx_phase(); + set_efx_rate(); + set_efx_depth(); + set_efx_feedback(); + set_efx_delay(); +} + +bool EaxChorusEffect::get( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXCHORUS_NONE: + break; + + case EAXCHORUS_ALLPARAMETERS: + eax_call.set_value<EaxChorusEffectException>(eax_); + break; + + case EAXCHORUS_WAVEFORM: + eax_call.set_value<EaxChorusEffectException>(eax_.ulWaveform); + break; + + case EAXCHORUS_PHASE: + eax_call.set_value<EaxChorusEffectException>(eax_.lPhase); + break; + + case EAXCHORUS_RATE: + eax_call.set_value<EaxChorusEffectException>(eax_.flRate); + break; + + case EAXCHORUS_DEPTH: + eax_call.set_value<EaxChorusEffectException>(eax_.flDepth); + break; + + case EAXCHORUS_FEEDBACK: + eax_call.set_value<EaxChorusEffectException>(eax_.flFeedback); + break; + + case EAXCHORUS_DELAY: + eax_call.set_value<EaxChorusEffectException>(eax_.flDelay); + break; + + default: + throw EaxChorusEffectException{"Unsupported property id."}; + } + + return false; +} + +void EaxChorusEffect::validate_waveform( + unsigned long ulWaveform) +{ + eax_validate_range<EaxChorusEffectException>( + "Waveform", + ulWaveform, + EAXCHORUS_MINWAVEFORM, + EAXCHORUS_MAXWAVEFORM); +} + +void EaxChorusEffect::validate_phase( + long lPhase) +{ + eax_validate_range<EaxChorusEffectException>( + "Phase", + lPhase, + EAXCHORUS_MINPHASE, + EAXCHORUS_MAXPHASE); +} + +void EaxChorusEffect::validate_rate( + float flRate) +{ + eax_validate_range<EaxChorusEffectException>( + "Rate", + flRate, + EAXCHORUS_MINRATE, + EAXCHORUS_MAXRATE); +} + +void EaxChorusEffect::validate_depth( + float flDepth) +{ + eax_validate_range<EaxChorusEffectException>( + "Depth", + flDepth, + EAXCHORUS_MINDEPTH, + EAXCHORUS_MAXDEPTH); +} + +void EaxChorusEffect::validate_feedback( + float flFeedback) +{ + eax_validate_range<EaxChorusEffectException>( + "Feedback", + flFeedback, + EAXCHORUS_MINFEEDBACK, + EAXCHORUS_MAXFEEDBACK); +} + +void EaxChorusEffect::validate_delay( + float flDelay) +{ + eax_validate_range<EaxChorusEffectException>( + "Delay", + flDelay, + EAXCHORUS_MINDELAY, + EAXCHORUS_MAXDELAY); +} + +void EaxChorusEffect::validate_all( + const EAXCHORUSPROPERTIES& eax_all) +{ + validate_waveform(eax_all.ulWaveform); + validate_phase(eax_all.lPhase); + validate_rate(eax_all.flRate); + validate_depth(eax_all.flDepth); + validate_feedback(eax_all.flFeedback); + validate_delay(eax_all.flDelay); +} + +void EaxChorusEffect::defer_waveform( + unsigned long ulWaveform) +{ + eax_d_.ulWaveform = ulWaveform; + eax_dirty_flags_.ulWaveform = (eax_.ulWaveform != eax_d_.ulWaveform); +} + +void EaxChorusEffect::defer_phase( + long lPhase) +{ + eax_d_.lPhase = lPhase; + eax_dirty_flags_.lPhase = (eax_.lPhase != eax_d_.lPhase); +} + +void EaxChorusEffect::defer_rate( + float flRate) +{ + eax_d_.flRate = flRate; + eax_dirty_flags_.flRate = (eax_.flRate != eax_d_.flRate); +} + +void EaxChorusEffect::defer_depth( + float flDepth) +{ + eax_d_.flDepth = flDepth; + eax_dirty_flags_.flDepth = (eax_.flDepth != eax_d_.flDepth); +} + +void EaxChorusEffect::defer_feedback( + float flFeedback) +{ + eax_d_.flFeedback = flFeedback; + eax_dirty_flags_.flFeedback = (eax_.flFeedback != eax_d_.flFeedback); +} + +void EaxChorusEffect::defer_delay( + float flDelay) +{ + eax_d_.flDelay = flDelay; + eax_dirty_flags_.flDelay = (eax_.flDelay != eax_d_.flDelay); +} + +void EaxChorusEffect::defer_all( + const EAXCHORUSPROPERTIES& eax_all) +{ + defer_waveform(eax_all.ulWaveform); + defer_phase(eax_all.lPhase); + defer_rate(eax_all.flRate); + defer_depth(eax_all.flDepth); + defer_feedback(eax_all.flFeedback); + defer_delay(eax_all.flDelay); +} + +void EaxChorusEffect::defer_waveform( + const EaxEaxCall& eax_call) +{ + const auto& waveform = + eax_call.get_value<EaxChorusEffectException, const decltype(EAXCHORUSPROPERTIES::ulWaveform)>(); + + validate_waveform(waveform); + defer_waveform(waveform); +} + +void EaxChorusEffect::defer_phase( + const EaxEaxCall& eax_call) +{ + const auto& phase = + eax_call.get_value<EaxChorusEffectException, const decltype(EAXCHORUSPROPERTIES::lPhase)>(); + + validate_phase(phase); + defer_phase(phase); +} + +void EaxChorusEffect::defer_rate( + const EaxEaxCall& eax_call) +{ + const auto& rate = + eax_call.get_value<EaxChorusEffectException, const decltype(EAXCHORUSPROPERTIES::flRate)>(); + + validate_rate(rate); + defer_rate(rate); +} + +void EaxChorusEffect::defer_depth( + const EaxEaxCall& eax_call) +{ + const auto& depth = + eax_call.get_value<EaxChorusEffectException, const decltype(EAXCHORUSPROPERTIES::flDepth)>(); + + validate_depth(depth); + defer_depth(depth); +} + +void EaxChorusEffect::defer_feedback( + const EaxEaxCall& eax_call) +{ + const auto& feedback = + eax_call.get_value<EaxChorusEffectException, const decltype(EAXCHORUSPROPERTIES::flFeedback)>(); + + validate_feedback(feedback); + defer_feedback(feedback); +} + +void EaxChorusEffect::defer_delay( + const EaxEaxCall& eax_call) +{ + const auto& delay = + eax_call.get_value<EaxChorusEffectException, const decltype(EAXCHORUSPROPERTIES::flDelay)>(); + + validate_delay(delay); + defer_delay(delay); +} + +void EaxChorusEffect::defer_all( + const EaxEaxCall& eax_call) +{ + const auto& all = + eax_call.get_value<EaxChorusEffectException, const EAXCHORUSPROPERTIES>(); + + validate_all(all); + defer_all(all); +} + +// [[nodiscard]] +bool EaxChorusEffect::apply_deferred() +{ + if (eax_dirty_flags_ == EaxChorusEffectDirtyFlags{}) + { + return false; + } + + eax_ = eax_d_; + + if (eax_dirty_flags_.ulWaveform) + { + set_efx_waveform(); + } + + if (eax_dirty_flags_.lPhase) + { + set_efx_phase(); + } + + if (eax_dirty_flags_.flRate) + { + set_efx_rate(); + } + + if (eax_dirty_flags_.flDepth) + { + set_efx_depth(); + } + + if (eax_dirty_flags_.flFeedback) + { + set_efx_feedback(); + } + + if (eax_dirty_flags_.flDelay) + { + set_efx_delay(); + } + + eax_dirty_flags_ = EaxChorusEffectDirtyFlags{}; + + return true; +} + +// [[nodiscard]] +bool EaxChorusEffect::set( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXCHORUS_NONE: + break; + + case EAXCHORUS_ALLPARAMETERS: + defer_all(eax_call); + break; + + case EAXCHORUS_WAVEFORM: + defer_waveform(eax_call); + break; + + case EAXCHORUS_PHASE: + defer_phase(eax_call); + break; + + case EAXCHORUS_RATE: + defer_rate(eax_call); + break; + + case EAXCHORUS_DEPTH: + defer_depth(eax_call); + break; + + case EAXCHORUS_FEEDBACK: + defer_feedback(eax_call); + break; + + case EAXCHORUS_DELAY: + defer_delay(eax_call); + break; + + default: + throw EaxChorusEffectException{"Unsupported property id."}; + } + + if (!eax_call.is_deferred()) + { + return apply_deferred(); + } + + return false; +} + + +} // namespace + + +EaxEffectUPtr eax_create_eax_chorus_effect( + EffectProps& al_effect_props) +{ + return std::make_unique<::EaxChorusEffect>(al_effect_props); +} + + +namespace +{ + + +using EaxFlangerEffectDirtyFlagsValue = std::uint_least8_t; + +struct EaxFlangerEffectDirtyFlags +{ + using EaxIsBitFieldStruct = bool; + + EaxFlangerEffectDirtyFlagsValue ulWaveform : 1; + EaxFlangerEffectDirtyFlagsValue lPhase : 1; + EaxFlangerEffectDirtyFlagsValue flRate : 1; + EaxFlangerEffectDirtyFlagsValue flDepth : 1; + EaxFlangerEffectDirtyFlagsValue flFeedback : 1; + EaxFlangerEffectDirtyFlagsValue flDelay : 1; +}; // EaxFlangerEffectDirtyFlags + + +class EaxFlangerEffect final : + public EaxEffect +{ +public: + EaxFlangerEffect( + EffectProps& al_effect_props); + + + // [[nodiscard]] + bool dispatch( + const EaxEaxCall& eax_call) override; + + +private: + EffectProps& al_effect_props_; + + EAXFLANGERPROPERTIES eax_{}; + EAXFLANGERPROPERTIES eax_d_{}; + EaxFlangerEffectDirtyFlags eax_dirty_flags_{}; + + + void set_eax_defaults(); + + + void set_efx_waveform(); + + void set_efx_phase(); + + void set_efx_rate(); + + void set_efx_depth(); + + void set_efx_feedback(); + + void set_efx_delay(); + + void set_efx_defaults(); + + + // [[nodiscard]] + bool get( + const EaxEaxCall& eax_call); + + + void validate_waveform( + unsigned long ulWaveform); + + void validate_phase( + long lPhase); + + void validate_rate( + float flRate); + + void validate_depth( + float flDepth); + + void validate_feedback( + float flFeedback); + + void validate_delay( + float flDelay); + + void validate_all( + const EAXFLANGERPROPERTIES& all); + + + void defer_waveform( + unsigned long ulWaveform); + + void defer_phase( + long lPhase); + + void defer_rate( + float flRate); + + void defer_depth( + float flDepth); + + void defer_feedback( + float flFeedback); + + void defer_delay( + float flDelay); + + void defer_all( + const EAXFLANGERPROPERTIES& all); + + + void defer_waveform( + const EaxEaxCall& eax_call); + + void defer_phase( + const EaxEaxCall& eax_call); + + void defer_rate( + const EaxEaxCall& eax_call); + + void defer_depth( + const EaxEaxCall& eax_call); + + void defer_feedback( + const EaxEaxCall& eax_call); + + void defer_delay( + const EaxEaxCall& eax_call); + + void defer_all( + const EaxEaxCall& eax_call); + + + // [[nodiscard]] + bool apply_deferred(); + + // [[nodiscard]] + bool set( + const EaxEaxCall& eax_call); +}; // EaxFlangerEffect + + +class EaxFlangerEffectException : + public EaxException +{ +public: + explicit EaxFlangerEffectException( + const char* message) + : + EaxException{"EAX_FLANGER_EFFECT", message} + { + } +}; // EaxFlangerEffectException + + +EaxFlangerEffect::EaxFlangerEffect( + EffectProps& al_effect_props) + : + al_effect_props_{al_effect_props} +{ + set_eax_defaults(); + set_efx_defaults(); +} + +// [[nodiscard]] +bool EaxFlangerEffect::dispatch( + const EaxEaxCall& eax_call) +{ + return eax_call.is_get() ? get(eax_call) : set(eax_call); +} + +void EaxFlangerEffect::set_eax_defaults() +{ + eax_.ulWaveform = EAXFLANGER_DEFAULTWAVEFORM; + eax_.lPhase = EAXFLANGER_DEFAULTPHASE; + eax_.flRate = EAXFLANGER_DEFAULTRATE; + eax_.flDepth = EAXFLANGER_DEFAULTDEPTH; + eax_.flFeedback = EAXFLANGER_DEFAULTFEEDBACK; + eax_.flDelay = EAXFLANGER_DEFAULTDELAY; + + eax_d_ = eax_; +} + +void EaxFlangerEffect::set_efx_waveform() +{ + const auto waveform = clamp( + static_cast<ALint>(eax_.ulWaveform), + AL_FLANGER_MIN_WAVEFORM, + AL_FLANGER_MAX_WAVEFORM); + + eax_set_efx_waveform(waveform, al_effect_props_); +} + +void EaxFlangerEffect::set_efx_phase() +{ + const auto phase = clamp( + static_cast<ALint>(eax_.lPhase), + AL_FLANGER_MIN_PHASE, + AL_FLANGER_MAX_PHASE); + + eax_set_efx_phase(phase, al_effect_props_); +} + +void EaxFlangerEffect::set_efx_rate() +{ + const auto rate = clamp( + eax_.flRate, + AL_FLANGER_MIN_RATE, + AL_FLANGER_MAX_RATE); + + eax_set_efx_rate(rate, al_effect_props_); +} + +void EaxFlangerEffect::set_efx_depth() +{ + const auto depth = clamp( + eax_.flDepth, + AL_FLANGER_MIN_DEPTH, + AL_FLANGER_MAX_DEPTH); + + eax_set_efx_depth(depth, al_effect_props_); +} + +void EaxFlangerEffect::set_efx_feedback() +{ + const auto feedback = clamp( + eax_.flFeedback, + AL_FLANGER_MIN_FEEDBACK, + AL_FLANGER_MAX_FEEDBACK); + + eax_set_efx_feedback(feedback, al_effect_props_); +} + +void EaxFlangerEffect::set_efx_delay() +{ + const auto delay = clamp( + eax_.flDelay, + AL_FLANGER_MIN_DELAY, + AL_FLANGER_MAX_DELAY); + + eax_set_efx_delay(delay, al_effect_props_); +} + +void EaxFlangerEffect::set_efx_defaults() +{ + set_efx_waveform(); + set_efx_phase(); + set_efx_rate(); + set_efx_depth(); + set_efx_feedback(); + set_efx_delay(); +} + +// [[nodiscard]] +bool EaxFlangerEffect::get( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXFLANGER_NONE: + break; + + case EAXFLANGER_ALLPARAMETERS: + eax_call.set_value<EaxFlangerEffectException>(eax_); + break; + + case EAXFLANGER_WAVEFORM: + eax_call.set_value<EaxFlangerEffectException>(eax_.ulWaveform); + break; + + case EAXFLANGER_PHASE: + eax_call.set_value<EaxFlangerEffectException>(eax_.lPhase); + break; + + case EAXFLANGER_RATE: + eax_call.set_value<EaxFlangerEffectException>(eax_.flRate); + break; + + case EAXFLANGER_DEPTH: + eax_call.set_value<EaxFlangerEffectException>(eax_.flDepth); + break; + + case EAXFLANGER_FEEDBACK: + eax_call.set_value<EaxFlangerEffectException>(eax_.flFeedback); + break; + + case EAXFLANGER_DELAY: + eax_call.set_value<EaxFlangerEffectException>(eax_.flDelay); + break; + + default: + throw EaxFlangerEffectException{"Unsupported property id."}; + } + + return false; +} + +void EaxFlangerEffect::validate_waveform( + unsigned long ulWaveform) +{ + eax_validate_range<EaxFlangerEffectException>( + "Waveform", + ulWaveform, + EAXFLANGER_MINWAVEFORM, + EAXFLANGER_MAXWAVEFORM); +} + +void EaxFlangerEffect::validate_phase( + long lPhase) +{ + eax_validate_range<EaxFlangerEffectException>( + "Phase", + lPhase, + EAXFLANGER_MINPHASE, + EAXFLANGER_MAXPHASE); +} + +void EaxFlangerEffect::validate_rate( + float flRate) +{ + eax_validate_range<EaxFlangerEffectException>( + "Rate", + flRate, + EAXFLANGER_MINRATE, + EAXFLANGER_MAXRATE); +} + +void EaxFlangerEffect::validate_depth( + float flDepth) +{ + eax_validate_range<EaxFlangerEffectException>( + "Depth", + flDepth, + EAXFLANGER_MINDEPTH, + EAXFLANGER_MAXDEPTH); +} + +void EaxFlangerEffect::validate_feedback( + float flFeedback) +{ + eax_validate_range<EaxFlangerEffectException>( + "Feedback", + flFeedback, + EAXFLANGER_MINFEEDBACK, + EAXFLANGER_MAXFEEDBACK); +} + +void EaxFlangerEffect::validate_delay( + float flDelay) +{ + eax_validate_range<EaxFlangerEffectException>( + "Delay", + flDelay, + EAXFLANGER_MINDELAY, + EAXFLANGER_MAXDELAY); +} + +void EaxFlangerEffect::validate_all( + const EAXFLANGERPROPERTIES& all) +{ + validate_waveform(all.ulWaveform); + validate_phase(all.lPhase); + validate_rate(all.flRate); + validate_depth(all.flDepth); + validate_feedback(all.flDelay); + validate_delay(all.flDelay); +} + +void EaxFlangerEffect::defer_waveform( + unsigned long ulWaveform) +{ + eax_d_.ulWaveform = ulWaveform; + eax_dirty_flags_.ulWaveform = (eax_.ulWaveform != eax_d_.ulWaveform); +} + +void EaxFlangerEffect::defer_phase( + long lPhase) +{ + eax_d_.lPhase = lPhase; + eax_dirty_flags_.lPhase = (eax_.lPhase != eax_d_.lPhase); +} + +void EaxFlangerEffect::defer_rate( + float flRate) +{ + eax_d_.flRate = flRate; + eax_dirty_flags_.flRate = (eax_.flRate != eax_d_.flRate); +} + +void EaxFlangerEffect::defer_depth( + float flDepth) +{ + eax_d_.flDepth = flDepth; + eax_dirty_flags_.flDepth = (eax_.flDepth != eax_d_.flDepth); +} + +void EaxFlangerEffect::defer_feedback( + float flFeedback) +{ + eax_d_.flFeedback = flFeedback; + eax_dirty_flags_.flFeedback = (eax_.flFeedback != eax_d_.flFeedback); +} + +void EaxFlangerEffect::defer_delay( + float flDelay) +{ + eax_d_.flDelay = flDelay; + eax_dirty_flags_.flDelay = (eax_.flDelay != eax_d_.flDelay); +} + +void EaxFlangerEffect::defer_all( + const EAXFLANGERPROPERTIES& all) +{ + defer_waveform(all.ulWaveform); + defer_phase(all.lPhase); + defer_rate(all.flRate); + defer_depth(all.flDepth); + defer_feedback(all.flDelay); + defer_delay(all.flDelay); +} + +void EaxFlangerEffect::defer_waveform( + const EaxEaxCall& eax_call) +{ + const auto& waveform = + eax_call.get_value<EaxFlangerEffectException, const decltype(EAXFLANGERPROPERTIES::ulWaveform)>(); + + validate_waveform(waveform); + defer_waveform(waveform); +} + +void EaxFlangerEffect::defer_phase( + const EaxEaxCall& eax_call) +{ + const auto& phase = + eax_call.get_value<EaxFlangerEffectException, const decltype(EAXFLANGERPROPERTIES::lPhase)>(); + + validate_phase(phase); + defer_phase(phase); +} + +void EaxFlangerEffect::defer_rate( + const EaxEaxCall& eax_call) +{ + const auto& rate = + eax_call.get_value<EaxFlangerEffectException, const decltype(EAXFLANGERPROPERTIES::flRate)>(); + + validate_rate(rate); + defer_rate(rate); +} + +void EaxFlangerEffect::defer_depth( + const EaxEaxCall& eax_call) +{ + const auto& depth = + eax_call.get_value<EaxFlangerEffectException, const decltype(EAXFLANGERPROPERTIES::flDepth)>(); + + validate_depth(depth); + defer_depth(depth); +} + +void EaxFlangerEffect::defer_feedback( + const EaxEaxCall& eax_call) +{ + const auto& feedback = + eax_call.get_value<EaxFlangerEffectException, const decltype(EAXFLANGERPROPERTIES::flFeedback)>(); + + validate_feedback(feedback); + defer_feedback(feedback); +} + +void EaxFlangerEffect::defer_delay( + const EaxEaxCall& eax_call) +{ + const auto& delay = + eax_call.get_value<EaxFlangerEffectException, const decltype(EAXFLANGERPROPERTIES::flDelay)>(); + + validate_delay(delay); + defer_delay(delay); +} + +void EaxFlangerEffect::defer_all( + const EaxEaxCall& eax_call) +{ + const auto& all = + eax_call.get_value<EaxFlangerEffectException, const EAXFLANGERPROPERTIES>(); + + validate_all(all); + defer_all(all); +} + +// [[nodiscard]] +bool EaxFlangerEffect::apply_deferred() +{ + if (eax_dirty_flags_ == EaxFlangerEffectDirtyFlags{}) + { + return false; + } + + eax_ = eax_d_; + + if (eax_dirty_flags_.ulWaveform) + { + set_efx_waveform(); + } + + if (eax_dirty_flags_.lPhase) + { + set_efx_phase(); + } + + if (eax_dirty_flags_.flRate) + { + set_efx_rate(); + } + + if (eax_dirty_flags_.flDepth) + { + set_efx_depth(); + } + + if (eax_dirty_flags_.flFeedback) + { + set_efx_feedback(); + } + + if (eax_dirty_flags_.flDelay) + { + set_efx_delay(); + } + + eax_dirty_flags_ = EaxFlangerEffectDirtyFlags{}; + + return true; +} + +// [[nodiscard]] +bool EaxFlangerEffect::set( + const EaxEaxCall& eax_call) +{ + switch (eax_call.get_property_id()) + { + case EAXFLANGER_NONE: + break; + + case EAXFLANGER_ALLPARAMETERS: + defer_all(eax_call); + break; + + case EAXFLANGER_WAVEFORM: + defer_waveform(eax_call); + break; + + case EAXFLANGER_PHASE: + defer_phase(eax_call); + break; + + case EAXFLANGER_RATE: + defer_rate(eax_call); + break; + + case EAXFLANGER_DEPTH: + defer_depth(eax_call); + break; + + case EAXFLANGER_FEEDBACK: + defer_feedback(eax_call); + break; + + case EAXFLANGER_DELAY: + defer_delay(eax_call); + break; + + default: + throw EaxFlangerEffectException{"Unsupported property id."}; + } + + if (!eax_call.is_deferred()) + { + return apply_deferred(); + } + + return false; +} + + +} // namespace + + +EaxEffectUPtr eax_create_eax_flanger_effect( + EffectProps& al_effect_props) +{ + return std::make_unique<EaxFlangerEffect>(al_effect_props); +} + + +#endif // ALSOFT_EAX |