aboutsummaryrefslogtreecommitdiffstats
path: root/al/effects/chorus.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'al/effects/chorus.cpp')
-rw-r--r--al/effects/chorus.cpp1240
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