aboutsummaryrefslogtreecommitdiffstats
path: root/al/effects
diff options
context:
space:
mode:
authorBoris I. Bendovsky <[email protected]>2022-01-30 14:47:32 +0200
committerGitHub <[email protected]>2022-01-30 04:47:32 -0800
commit19ed994dc30ed84ea7cbbb5152577669fc25caf6 (patch)
treef68933bf8f778806618bd6c0b1bf9ced1b0ccf08 /al/effects
parent619249371a40f03cf988d1f5750d643df797c485 (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')
-rw-r--r--al/effects/autowah.cpp471
-rw-r--r--al/effects/chorus.cpp1240
-rw-r--r--al/effects/compressor.cpp272
-rw-r--r--al/effects/distortion.cpp536
-rw-r--r--al/effects/echo.cpp534
-rw-r--r--al/effects/effects.cpp106
-rw-r--r--al/effects/effects.h11
-rw-r--r--al/effects/equalizer.cpp866
-rw-r--r--al/effects/fshifter.cpp414
-rw-r--r--al/effects/modulator.cpp411
-rw-r--r--al/effects/null.cpp57
-rw-r--r--al/effects/pshifter.cpp338
-rw-r--r--al/effects/reverb.cpp1922
-rw-r--r--al/effects/vmorpher.cpp624
14 files changed, 7802 insertions, 0 deletions
diff --git a/al/effects/autowah.cpp b/al/effects/autowah.cpp
index 9abef1f7..bdd1bc09 100644
--- a/al/effects/autowah.cpp
+++ b/al/effects/autowah.cpp
@@ -11,6 +11,14 @@
#include "alc/effects/base.h"
#include "effects.h"
+#if ALSOFT_EAX
+#include "alnumeric.h"
+
+#include "al/eax_exception.h"
+#include "al/eax_utils.h"
+#endif // ALSOFT_EAX
+
+
namespace {
void Autowah_setParamf(EffectProps *props, ALenum param, float val)
@@ -107,3 +115,466 @@ EffectProps genDefaultProps() noexcept
DEFINE_ALEFFECT_VTABLE(Autowah);
const EffectProps AutowahEffectProps{genDefaultProps()};
+
+#if ALSOFT_EAX
+namespace
+{
+
+
+using EaxAutoWahEffectDirtyFlagsValue = std::uint_least8_t;
+
+struct EaxAutoWahEffectDirtyFlags
+{
+ using EaxIsBitFieldStruct = bool;
+
+ EaxAutoWahEffectDirtyFlagsValue flAttackTime : 1;
+ EaxAutoWahEffectDirtyFlagsValue flReleaseTime : 1;
+ EaxAutoWahEffectDirtyFlagsValue lResonance : 1;
+ EaxAutoWahEffectDirtyFlagsValue lPeakLevel : 1;
+}; // EaxAutoWahEffectDirtyFlags
+
+
+class EaxAutoWahEffect final :
+ public EaxEffect
+{
+public:
+ EaxAutoWahEffect(
+ EffectProps& al_effect_props);
+
+
+ // [[nodiscard]]
+ bool dispatch(
+ const EaxEaxCall& eax_call) override;
+
+
+private:
+ EffectProps& al_effect_props_;
+
+ EAXAUTOWAHPROPERTIES eax_{};
+ EAXAUTOWAHPROPERTIES eax_d_{};
+ EaxAutoWahEffectDirtyFlags eax_dirty_flags_{};
+
+
+ void set_eax_defaults();
+
+
+ void set_efx_attack_time();
+
+ void set_efx_release_time();
+
+ void set_efx_resonance();
+
+ void set_efx_peak_gain();
+
+ void set_efx_defaults();
+
+
+ // [[nodiscard]]
+ bool get(
+ const EaxEaxCall& eax_call);
+
+
+ void validate_attack_time(
+ float flAttackTime);
+
+ void validate_release_time(
+ float flReleaseTime);
+
+ void validate_resonance(
+ long lResonance);
+
+ void validate_peak_level(
+ long lPeakLevel);
+
+ void validate_all(
+ const EAXAUTOWAHPROPERTIES& eax_all);
+
+
+ void defer_attack_time(
+ float flAttackTime);
+
+ void defer_release_time(
+ float flReleaseTime);
+
+ void defer_resonance(
+ long lResonance);
+
+ void defer_peak_level(
+ long lPeakLevel);
+
+ void defer_all(
+ const EAXAUTOWAHPROPERTIES& eax_all);
+
+
+ void defer_attack_time(
+ const EaxEaxCall& eax_call);
+
+ void defer_release_time(
+ const EaxEaxCall& eax_call);
+
+ void defer_resonance(
+ const EaxEaxCall& eax_call);
+
+ void defer_peak_level(
+ const EaxEaxCall& eax_call);
+
+ void defer_all(
+ const EaxEaxCall& eax_call);
+
+ // [[nodiscard]]
+ bool apply_deferred();
+
+ // [[nodiscard]]
+ bool set(
+ const EaxEaxCall& eax_call);
+}; // EaxAutoWahEffect
+
+
+class EaxAutoWahEffectException :
+ public EaxException
+{
+public:
+ explicit EaxAutoWahEffectException(
+ const char* message)
+ :
+ EaxException{"EAX_AUTO_WAH_EFFECT", message}
+ {
+ }
+}; // EaxAutoWahEffectException
+
+
+EaxAutoWahEffect::EaxAutoWahEffect(
+ EffectProps& al_effect_props)
+ :
+ al_effect_props_{al_effect_props}
+{
+ set_eax_defaults();
+ set_efx_defaults();
+}
+
+// [[nodiscard]]
+bool EaxAutoWahEffect::dispatch(
+ const EaxEaxCall& eax_call)
+{
+ return eax_call.is_get() ? get(eax_call) : set(eax_call);
+}
+
+void EaxAutoWahEffect::set_eax_defaults()
+{
+ eax_.flAttackTime = EAXAUTOWAH_DEFAULTATTACKTIME;
+ eax_.flReleaseTime = EAXAUTOWAH_DEFAULTRELEASETIME;
+ eax_.lResonance = EAXAUTOWAH_DEFAULTRESONANCE;
+ eax_.lPeakLevel = EAXAUTOWAH_DEFAULTPEAKLEVEL;
+
+ eax_d_ = eax_;
+}
+
+void EaxAutoWahEffect::set_efx_attack_time()
+{
+ const auto attack_time = clamp(
+ eax_.flAttackTime,
+ AL_AUTOWAH_MIN_ATTACK_TIME,
+ AL_AUTOWAH_MAX_ATTACK_TIME);
+
+ al_effect_props_.Autowah.AttackTime = attack_time;
+}
+
+void EaxAutoWahEffect::set_efx_release_time()
+{
+ const auto release_time = clamp(
+ eax_.flReleaseTime,
+ AL_AUTOWAH_MIN_RELEASE_TIME,
+ AL_AUTOWAH_MAX_RELEASE_TIME);
+
+ al_effect_props_.Autowah.ReleaseTime = release_time;
+}
+
+void EaxAutoWahEffect::set_efx_resonance()
+{
+ const auto resonance = clamp(
+ level_mb_to_gain(static_cast<float>(eax_.lResonance)),
+ AL_AUTOWAH_MIN_RESONANCE,
+ AL_AUTOWAH_MAX_RESONANCE);
+
+ al_effect_props_.Autowah.Resonance = resonance;
+}
+
+void EaxAutoWahEffect::set_efx_peak_gain()
+{
+ const auto peak_gain = clamp(
+ level_mb_to_gain(static_cast<float>(eax_.lPeakLevel)),
+ AL_AUTOWAH_MIN_PEAK_GAIN,
+ AL_AUTOWAH_MAX_PEAK_GAIN);
+
+ al_effect_props_.Autowah.PeakGain = peak_gain;
+}
+
+void EaxAutoWahEffect::set_efx_defaults()
+{
+ set_efx_attack_time();
+ set_efx_release_time();
+ set_efx_resonance();
+ set_efx_peak_gain();
+}
+
+bool EaxAutoWahEffect::get(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXAUTOWAH_NONE:
+ break;
+
+ case EAXAUTOWAH_ALLPARAMETERS:
+ eax_call.set_value<EaxAutoWahEffectException>(eax_);
+ break;
+
+ case EAXAUTOWAH_ATTACKTIME:
+ eax_call.set_value<EaxAutoWahEffectException>(eax_.flAttackTime);
+ break;
+
+ case EAXAUTOWAH_RELEASETIME:
+ eax_call.set_value<EaxAutoWahEffectException>(eax_.flReleaseTime);
+ break;
+
+ case EAXAUTOWAH_RESONANCE:
+ eax_call.set_value<EaxAutoWahEffectException>(eax_.lResonance);
+ break;
+
+ case EAXAUTOWAH_PEAKLEVEL:
+ eax_call.set_value<EaxAutoWahEffectException>(eax_.lPeakLevel);
+ break;
+
+ default:
+ throw EaxAutoWahEffectException{"Unsupported property id."};
+ }
+
+ return false;
+}
+
+void EaxAutoWahEffect::validate_attack_time(
+ float flAttackTime)
+{
+ eax_validate_range<EaxAutoWahEffectException>(
+ "Attack Time",
+ flAttackTime,
+ EAXAUTOWAH_MINATTACKTIME,
+ EAXAUTOWAH_MAXATTACKTIME);
+}
+
+void EaxAutoWahEffect::validate_release_time(
+ float flReleaseTime)
+{
+ eax_validate_range<EaxAutoWahEffectException>(
+ "Release Time",
+ flReleaseTime,
+ EAXAUTOWAH_MINRELEASETIME,
+ EAXAUTOWAH_MAXRELEASETIME);
+}
+
+void EaxAutoWahEffect::validate_resonance(
+ long lResonance)
+{
+ eax_validate_range<EaxAutoWahEffectException>(
+ "Resonance",
+ lResonance,
+ EAXAUTOWAH_MINRESONANCE,
+ EAXAUTOWAH_MAXRESONANCE);
+}
+
+void EaxAutoWahEffect::validate_peak_level(
+ long lPeakLevel)
+{
+ eax_validate_range<EaxAutoWahEffectException>(
+ "Peak Level",
+ lPeakLevel,
+ EAXAUTOWAH_MINPEAKLEVEL,
+ EAXAUTOWAH_MAXPEAKLEVEL);
+}
+
+void EaxAutoWahEffect::validate_all(
+ const EAXAUTOWAHPROPERTIES& eax_all)
+{
+ validate_attack_time(eax_all.flAttackTime);
+ validate_release_time(eax_all.flReleaseTime);
+ validate_resonance(eax_all.lResonance);
+ validate_peak_level(eax_all.lPeakLevel);
+}
+
+void EaxAutoWahEffect::defer_attack_time(
+ float flAttackTime)
+{
+ eax_d_.flAttackTime = flAttackTime;
+ eax_dirty_flags_.flAttackTime = (eax_.flAttackTime != eax_d_.flAttackTime);
+}
+
+void EaxAutoWahEffect::defer_release_time(
+ float flReleaseTime)
+{
+ eax_d_.flReleaseTime = flReleaseTime;
+ eax_dirty_flags_.flReleaseTime = (eax_.flReleaseTime != eax_d_.flReleaseTime);
+}
+
+void EaxAutoWahEffect::defer_resonance(
+ long lResonance)
+{
+ eax_d_.lResonance = lResonance;
+ eax_dirty_flags_.lResonance = (eax_.lResonance != eax_d_.lResonance);
+}
+
+void EaxAutoWahEffect::defer_peak_level(
+ long lPeakLevel)
+{
+ eax_d_.lPeakLevel = lPeakLevel;
+ eax_dirty_flags_.lPeakLevel = (eax_.lPeakLevel != eax_d_.lPeakLevel);
+}
+
+void EaxAutoWahEffect::defer_all(
+ const EAXAUTOWAHPROPERTIES& eax_all)
+{
+ validate_all(eax_all);
+
+ defer_attack_time(eax_all.flAttackTime);
+ defer_release_time(eax_all.flReleaseTime);
+ defer_resonance(eax_all.lResonance);
+ defer_peak_level(eax_all.lPeakLevel);
+}
+
+void EaxAutoWahEffect::defer_attack_time(
+ const EaxEaxCall& eax_call)
+{
+ const auto& attack_time =
+ eax_call.get_value<EaxAutoWahEffectException, const decltype(EAXAUTOWAHPROPERTIES::flAttackTime)>();
+
+ validate_attack_time(attack_time);
+ defer_attack_time(attack_time);
+}
+
+void EaxAutoWahEffect::defer_release_time(
+ const EaxEaxCall& eax_call)
+{
+ const auto& release_time =
+ eax_call.get_value<EaxAutoWahEffectException, const decltype(EAXAUTOWAHPROPERTIES::flReleaseTime)>();
+
+ validate_release_time(release_time);
+ defer_release_time(release_time);
+}
+
+void EaxAutoWahEffect::defer_resonance(
+ const EaxEaxCall& eax_call)
+{
+ const auto& resonance =
+ eax_call.get_value<EaxAutoWahEffectException, const decltype(EAXAUTOWAHPROPERTIES::lResonance)>();
+
+ validate_resonance(resonance);
+ defer_resonance(resonance);
+}
+
+void EaxAutoWahEffect::defer_peak_level(
+ const EaxEaxCall& eax_call)
+{
+ const auto& peak_level =
+ eax_call.get_value<EaxAutoWahEffectException, const decltype(EAXAUTOWAHPROPERTIES::lPeakLevel)>();
+
+ validate_peak_level(peak_level);
+ defer_peak_level(peak_level);
+}
+
+void EaxAutoWahEffect::defer_all(
+ const EaxEaxCall& eax_call)
+{
+ const auto& all =
+ eax_call.get_value<EaxAutoWahEffectException, const EAXAUTOWAHPROPERTIES>();
+
+ validate_all(all);
+ defer_all(all);
+}
+
+// [[nodiscard]]
+bool EaxAutoWahEffect::apply_deferred()
+{
+ if (eax_dirty_flags_ == EaxAutoWahEffectDirtyFlags{})
+ {
+ return false;
+ }
+
+ eax_ = eax_d_;
+
+ if (eax_dirty_flags_.flAttackTime)
+ {
+ set_efx_attack_time();
+ }
+
+ if (eax_dirty_flags_.flReleaseTime)
+ {
+ set_efx_release_time();
+ }
+
+ if (eax_dirty_flags_.lResonance)
+ {
+ set_efx_resonance();
+ }
+
+ if (eax_dirty_flags_.lPeakLevel)
+ {
+ set_efx_peak_gain();
+ }
+
+ eax_dirty_flags_ = EaxAutoWahEffectDirtyFlags{};
+
+ return true;
+}
+
+// [[nodiscard]]
+bool EaxAutoWahEffect::set(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXAUTOWAH_NONE:
+ break;
+
+ case EAXAUTOWAH_ALLPARAMETERS:
+ defer_all(eax_call);
+ break;
+
+ case EAXAUTOWAH_ATTACKTIME:
+ defer_attack_time(eax_call);
+ break;
+
+ case EAXAUTOWAH_RELEASETIME:
+ defer_release_time(eax_call);
+ break;
+
+ case EAXAUTOWAH_RESONANCE:
+ defer_resonance(eax_call);
+ break;
+
+ case EAXAUTOWAH_PEAKLEVEL:
+ defer_peak_level(eax_call);
+ break;
+
+ default:
+ throw EaxAutoWahEffectException{"Unsupported property id."};
+ }
+
+ if (!eax_call.is_deferred())
+ {
+ return apply_deferred();
+ }
+
+ return false;
+}
+
+
+} // namespace
+
+
+EaxEffectUPtr eax_create_eax_auto_wah_effect(
+ EffectProps& al_effect_props)
+{
+ return std::make_unique<::EaxAutoWahEffect>(al_effect_props);
+}
+
+
+#endif // ALSOFT_EAX
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
diff --git a/al/effects/compressor.cpp b/al/effects/compressor.cpp
index f5db2a6f..868c5c1b 100644
--- a/al/effects/compressor.cpp
+++ b/al/effects/compressor.cpp
@@ -7,6 +7,13 @@
#include "alc/effects/base.h"
#include "effects.h"
+#if ALSOFT_EAX
+#include "alnumeric.h"
+
+#include "al/eax_exception.h"
+#include "al/eax_utils.h"
+#endif // ALSOFT_EAX
+
namespace {
@@ -70,3 +77,268 @@ EffectProps genDefaultProps() noexcept
DEFINE_ALEFFECT_VTABLE(Compressor);
const EffectProps CompressorEffectProps{genDefaultProps()};
+
+#if ALSOFT_EAX
+namespace
+{
+
+
+using EaxCompressorEffectDirtyFlagsValue = std::uint_least8_t;
+
+struct EaxCompressorEffectDirtyFlags
+{
+ using EaxIsBitFieldStruct = bool;
+
+ EaxCompressorEffectDirtyFlagsValue ulOnOff : 1;
+}; // EaxCompressorEffectDirtyFlags
+
+
+class EaxCompressorEffect final :
+ public EaxEffect
+{
+public:
+ EaxCompressorEffect(
+ EffectProps& al_effect_props);
+
+
+ // [[nodiscard]]
+ bool dispatch(
+ const EaxEaxCall& eax_call) override;
+
+
+private:
+ EffectProps& al_effect_props_;
+
+ EAXAGCCOMPRESSORPROPERTIES eax_{};
+ EAXAGCCOMPRESSORPROPERTIES eax_d_{};
+ EaxCompressorEffectDirtyFlags eax_dirty_flags_{};
+
+
+ void set_eax_defaults();
+
+
+ void set_efx_on_off();
+
+ void set_efx_defaults();
+
+
+ // [[nodiscard]]
+ bool get(
+ const EaxEaxCall& eax_call);
+
+
+ void validate_on_off(
+ unsigned long ulOnOff);
+
+ void validate_all(
+ const EAXAGCCOMPRESSORPROPERTIES& eax_all);
+
+
+ void defer_on_off(
+ unsigned long ulOnOff);
+
+ void defer_all(
+ const EAXAGCCOMPRESSORPROPERTIES& eax_all);
+
+
+ void defer_on_off(
+ const EaxEaxCall& eax_call);
+
+ void defer_all(
+ const EaxEaxCall& eax_call);
+
+
+ // [[nodiscard]]
+ bool apply_deferred();
+
+ // [[nodiscard]]
+ bool set(
+ const EaxEaxCall& eax_call);
+}; // EaxCompressorEffect
+
+
+class EaxCompressorEffectException :
+ public EaxException
+{
+public:
+ explicit EaxCompressorEffectException(
+ const char* message)
+ :
+ EaxException{"EAX_COMPRESSOR_EFFECT", message}
+ {
+ }
+}; // EaxCompressorEffectException
+
+
+EaxCompressorEffect::EaxCompressorEffect(
+ EffectProps& al_effect_props)
+ :
+ al_effect_props_{al_effect_props}
+{
+ set_eax_defaults();
+ set_efx_defaults();
+}
+
+// [[nodiscard]]
+bool EaxCompressorEffect::dispatch(
+ const EaxEaxCall& eax_call)
+{
+ return eax_call.is_get() ? get(eax_call) : set(eax_call);
+}
+
+void EaxCompressorEffect::set_eax_defaults()
+{
+ eax_.ulOnOff = EAXAGCCOMPRESSOR_DEFAULTONOFF;
+
+ eax_d_ = eax_;
+}
+
+void EaxCompressorEffect::set_efx_on_off()
+{
+ const auto on_off = clamp(
+ static_cast<ALint>(eax_.ulOnOff),
+ AL_COMPRESSOR_MIN_ONOFF,
+ AL_COMPRESSOR_MAX_ONOFF);
+
+ al_effect_props_.Compressor.OnOff = (on_off != AL_FALSE);
+}
+
+void EaxCompressorEffect::set_efx_defaults()
+{
+ set_efx_on_off();
+}
+
+// [[nodiscard]]
+bool EaxCompressorEffect::get(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXAGCCOMPRESSOR_NONE:
+ break;
+
+ case EAXAGCCOMPRESSOR_ALLPARAMETERS:
+ eax_call.set_value<EaxCompressorEffectException>(eax_);
+ break;
+
+ case EAXAGCCOMPRESSOR_ONOFF:
+ eax_call.set_value<EaxCompressorEffectException>(eax_.ulOnOff);
+ break;
+
+ default:
+ throw EaxCompressorEffectException{"Unsupported property id."};
+ }
+
+ return false;
+}
+
+void EaxCompressorEffect::validate_on_off(
+ unsigned long ulOnOff)
+{
+ eax_validate_range<EaxCompressorEffectException>(
+ "On-Off",
+ ulOnOff,
+ EAXAGCCOMPRESSOR_MINONOFF,
+ EAXAGCCOMPRESSOR_MAXONOFF);
+}
+
+void EaxCompressorEffect::validate_all(
+ const EAXAGCCOMPRESSORPROPERTIES& eax_all)
+{
+ validate_on_off(eax_all.ulOnOff);
+}
+
+void EaxCompressorEffect::defer_on_off(
+ unsigned long ulOnOff)
+{
+ eax_d_.ulOnOff = ulOnOff;
+ eax_dirty_flags_.ulOnOff = (eax_.ulOnOff != eax_d_.ulOnOff);
+}
+
+void EaxCompressorEffect::defer_all(
+ const EAXAGCCOMPRESSORPROPERTIES& eax_all)
+{
+ defer_on_off(eax_all.ulOnOff);
+}
+
+void EaxCompressorEffect::defer_on_off(
+ const EaxEaxCall& eax_call)
+{
+ const auto& on_off =
+ eax_call.get_value<EaxCompressorEffectException, const decltype(EAXAGCCOMPRESSORPROPERTIES::ulOnOff)>();
+
+ validate_on_off(on_off);
+ defer_on_off(on_off);
+}
+
+void EaxCompressorEffect::defer_all(
+ const EaxEaxCall& eax_call)
+{
+ const auto& all =
+ eax_call.get_value<EaxCompressorEffectException, const EAXAGCCOMPRESSORPROPERTIES>();
+
+ validate_all(all);
+ defer_all(all);
+}
+
+// [[nodiscard]]
+bool EaxCompressorEffect::apply_deferred()
+{
+ if (eax_dirty_flags_ == EaxCompressorEffectDirtyFlags{})
+ {
+ return false;
+ }
+
+ eax_ = eax_d_;
+
+ if (eax_dirty_flags_.ulOnOff)
+ {
+ set_efx_on_off();
+ }
+
+ eax_dirty_flags_ = EaxCompressorEffectDirtyFlags{};
+
+ return true;
+}
+
+// [[nodiscard]]
+bool EaxCompressorEffect::set(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXAGCCOMPRESSOR_NONE:
+ break;
+
+ case EAXAGCCOMPRESSOR_ALLPARAMETERS:
+ defer_all(eax_call);
+ break;
+
+ case EAXAGCCOMPRESSOR_ONOFF:
+ defer_on_off(eax_call);
+ break;
+
+ default:
+ throw EaxCompressorEffectException{"Unsupported property id."};
+ }
+
+ if (!eax_call.is_deferred())
+ {
+ return apply_deferred();
+ }
+
+ return false;
+}
+
+
+} // namespace
+
+
+EaxEffectUPtr eax_create_eax_compressor_effect(
+ EffectProps& al_effect_props)
+{
+ return std::make_unique<EaxCompressorEffect>(al_effect_props);
+}
+
+
+#endif // ALSOFT_EAX
diff --git a/al/effects/distortion.cpp b/al/effects/distortion.cpp
index f5d64a9a..062cdc54 100644
--- a/al/effects/distortion.cpp
+++ b/al/effects/distortion.cpp
@@ -7,6 +7,13 @@
#include "alc/effects/base.h"
#include "effects.h"
+#if ALSOFT_EAX
+#include "alnumeric.h"
+
+#include "al/eax_exception.h"
+#include "al/eax_utils.h"
+#endif // ALSOFT_EAX
+
namespace {
@@ -112,3 +119,532 @@ EffectProps genDefaultProps() noexcept
DEFINE_ALEFFECT_VTABLE(Distortion);
const EffectProps DistortionEffectProps{genDefaultProps()};
+
+#if ALSOFT_EAX
+namespace
+{
+
+
+using EaxDistortionEffectDirtyFlagsValue = std::uint_least8_t;
+
+struct EaxDistortionEffectDirtyFlags
+{
+ using EaxIsBitFieldStruct = bool;
+
+ EaxDistortionEffectDirtyFlagsValue flEdge : 1;
+ EaxDistortionEffectDirtyFlagsValue lGain : 1;
+ EaxDistortionEffectDirtyFlagsValue flLowPassCutOff : 1;
+ EaxDistortionEffectDirtyFlagsValue flEQCenter : 1;
+ EaxDistortionEffectDirtyFlagsValue flEQBandwidth : 1;
+}; // EaxDistortionEffectDirtyFlags
+
+
+class EaxDistortionEffect final :
+ public EaxEffect
+{
+public:
+ EaxDistortionEffect(
+ EffectProps& al_effect_props);
+
+
+ // [[nodiscard]]
+ bool dispatch(
+ const EaxEaxCall& eax_call) override;
+
+
+private:
+ EffectProps& al_effect_props_;
+
+ EAXDISTORTIONPROPERTIES eax_{};
+ EAXDISTORTIONPROPERTIES eax_d_{};
+ EaxDistortionEffectDirtyFlags eax_dirty_flags_{};
+
+
+ void set_eax_defaults();
+
+
+ void set_efx_edge();
+
+ void set_efx_gain();
+
+ void set_efx_lowpass_cutoff();
+
+ void set_efx_eq_center();
+
+ void set_efx_eq_bandwidth();
+
+ void set_efx_defaults();
+
+
+ // [[nodiscard]]
+ bool get(
+ const EaxEaxCall& eax_call);
+
+
+ void validate_edge(
+ float flEdge);
+
+ void validate_gain(
+ long lGain);
+
+ void validate_lowpass_cutoff(
+ float flLowPassCutOff);
+
+ void validate_eq_center(
+ float flEQCenter);
+
+ void validate_eq_bandwidth(
+ float flEQBandwidth);
+
+ void validate_all(
+ const EAXDISTORTIONPROPERTIES& eax_all);
+
+
+ void defer_edge(
+ float flEdge);
+
+ void defer_gain(
+ long lGain);
+
+ void defer_low_pass_cutoff(
+ float flLowPassCutOff);
+
+ void defer_eq_center(
+ float flEQCenter);
+
+ void defer_eq_bandwidth(
+ float flEQBandwidth);
+
+ void defer_all(
+ const EAXDISTORTIONPROPERTIES& eax_all);
+
+
+ void defer_edge(
+ const EaxEaxCall& eax_call);
+
+ void defer_gain(
+ const EaxEaxCall& eax_call);
+
+ void defer_low_pass_cutoff(
+ const EaxEaxCall& eax_call);
+
+ void defer_eq_center(
+ const EaxEaxCall& eax_call);
+
+ void defer_eq_bandwidth(
+ const EaxEaxCall& eax_call);
+
+ void defer_all(
+ const EaxEaxCall& eax_call);
+
+
+ // [[nodiscard]]
+ bool apply_deferred();
+
+ // [[nodiscard]]
+ bool set(
+ const EaxEaxCall& eax_call);
+}; // EaxDistortionEffect
+
+
+class EaxDistortionEffectException :
+ public EaxException
+{
+public:
+ explicit EaxDistortionEffectException(
+ const char* message)
+ :
+ EaxException{"EAX_DISTORTION_EFFECT", message}
+ {
+ }
+}; // EaxDistortionEffectException
+
+
+EaxDistortionEffect::EaxDistortionEffect(
+ EffectProps& al_effect_props)
+ :
+ al_effect_props_{al_effect_props}
+{
+ set_eax_defaults();
+ set_efx_defaults();
+}
+
+// [[nodiscard]]
+bool EaxDistortionEffect::dispatch(
+ const EaxEaxCall& eax_call)
+{
+ return eax_call.is_get() ? get(eax_call) : set(eax_call);
+}
+
+void EaxDistortionEffect::set_eax_defaults()
+{
+ eax_.flEdge = EAXDISTORTION_DEFAULTEDGE;
+ eax_.lGain = EAXDISTORTION_DEFAULTGAIN;
+ eax_.flLowPassCutOff = EAXDISTORTION_DEFAULTLOWPASSCUTOFF;
+ eax_.flEQCenter = EAXDISTORTION_DEFAULTEQCENTER;
+ eax_.flEQBandwidth = EAXDISTORTION_DEFAULTEQBANDWIDTH;
+
+ eax_d_ = eax_;
+}
+
+void EaxDistortionEffect::set_efx_edge()
+{
+ const auto edge = clamp(
+ eax_.flEdge,
+ AL_DISTORTION_MIN_EDGE,
+ AL_DISTORTION_MAX_EDGE);
+
+ al_effect_props_.Distortion.Edge = edge;
+}
+
+void EaxDistortionEffect::set_efx_gain()
+{
+ const auto gain = clamp(
+ level_mb_to_gain(static_cast<float>(eax_.lGain)),
+ AL_DISTORTION_MIN_GAIN,
+ AL_DISTORTION_MAX_GAIN);
+
+ al_effect_props_.Distortion.Gain = gain;
+}
+
+void EaxDistortionEffect::set_efx_lowpass_cutoff()
+{
+ const auto lowpass_cutoff = clamp(
+ eax_.flLowPassCutOff,
+ AL_DISTORTION_MIN_LOWPASS_CUTOFF,
+ AL_DISTORTION_MAX_LOWPASS_CUTOFF);
+
+ al_effect_props_.Distortion.LowpassCutoff = lowpass_cutoff;
+}
+
+void EaxDistortionEffect::set_efx_eq_center()
+{
+ const auto eq_center = clamp(
+ eax_.flEQCenter,
+ AL_DISTORTION_MIN_EQCENTER,
+ AL_DISTORTION_MAX_EQCENTER);
+
+ al_effect_props_.Distortion.EQCenter = eq_center;
+}
+
+void EaxDistortionEffect::set_efx_eq_bandwidth()
+{
+ const auto eq_bandwidth = clamp(
+ eax_.flEdge,
+ AL_DISTORTION_MIN_EQBANDWIDTH,
+ AL_DISTORTION_MAX_EQBANDWIDTH);
+
+ al_effect_props_.Distortion.EQBandwidth = eq_bandwidth;
+}
+
+void EaxDistortionEffect::set_efx_defaults()
+{
+ set_efx_edge();
+ set_efx_gain();
+ set_efx_lowpass_cutoff();
+ set_efx_eq_center();
+ set_efx_eq_bandwidth();
+}
+
+// [[nodiscard]]
+bool EaxDistortionEffect::get(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXDISTORTION_NONE:
+ break;
+
+ case EAXDISTORTION_ALLPARAMETERS:
+ eax_call.set_value<EaxDistortionEffectException>(eax_);
+ break;
+
+ case EAXDISTORTION_EDGE:
+ eax_call.set_value<EaxDistortionEffectException>(eax_.flEdge);
+ break;
+
+ case EAXDISTORTION_GAIN:
+ eax_call.set_value<EaxDistortionEffectException>(eax_.lGain);
+ break;
+
+ case EAXDISTORTION_LOWPASSCUTOFF:
+ eax_call.set_value<EaxDistortionEffectException>(eax_.flLowPassCutOff);
+ break;
+
+ case EAXDISTORTION_EQCENTER:
+ eax_call.set_value<EaxDistortionEffectException>(eax_.flEQCenter);
+ break;
+
+ case EAXDISTORTION_EQBANDWIDTH:
+ eax_call.set_value<EaxDistortionEffectException>(eax_.flEQBandwidth);
+ break;
+
+ default:
+ throw EaxDistortionEffectException{"Unsupported property id."};
+ }
+
+ return false;
+}
+
+void EaxDistortionEffect::validate_edge(
+ float flEdge)
+{
+ eax_validate_range<EaxDistortionEffectException>(
+ "Edge",
+ flEdge,
+ EAXDISTORTION_MINEDGE,
+ EAXDISTORTION_MAXEDGE);
+}
+
+void EaxDistortionEffect::validate_gain(
+ long lGain)
+{
+ eax_validate_range<EaxDistortionEffectException>(
+ "Gain",
+ lGain,
+ EAXDISTORTION_MINGAIN,
+ EAXDISTORTION_MAXGAIN);
+}
+
+void EaxDistortionEffect::validate_lowpass_cutoff(
+ float flLowPassCutOff)
+{
+ eax_validate_range<EaxDistortionEffectException>(
+ "Low-pass Cut-off",
+ flLowPassCutOff,
+ EAXDISTORTION_MINLOWPASSCUTOFF,
+ EAXDISTORTION_MAXLOWPASSCUTOFF);
+}
+
+void EaxDistortionEffect::validate_eq_center(
+ float flEQCenter)
+{
+ eax_validate_range<EaxDistortionEffectException>(
+ "EQ Center",
+ flEQCenter,
+ EAXDISTORTION_MINEQCENTER,
+ EAXDISTORTION_MAXEQCENTER);
+}
+
+void EaxDistortionEffect::validate_eq_bandwidth(
+ float flEQBandwidth)
+{
+ eax_validate_range<EaxDistortionEffectException>(
+ "EQ Bandwidth",
+ flEQBandwidth,
+ EAXDISTORTION_MINEQBANDWIDTH,
+ EAXDISTORTION_MAXEQBANDWIDTH);
+}
+
+void EaxDistortionEffect::validate_all(
+ const EAXDISTORTIONPROPERTIES& eax_all)
+{
+ validate_edge(eax_all.flEdge);
+ validate_gain(eax_all.lGain);
+ validate_lowpass_cutoff(eax_all.flLowPassCutOff);
+ validate_eq_center(eax_all.flEQCenter);
+ validate_eq_bandwidth(eax_all.flEQBandwidth);
+}
+
+void EaxDistortionEffect::defer_edge(
+ float flEdge)
+{
+ eax_d_.flEdge = flEdge;
+ eax_dirty_flags_.flEdge = (eax_.flEdge != eax_d_.flEdge);
+}
+
+void EaxDistortionEffect::defer_gain(
+ long lGain)
+{
+ eax_d_.lGain = lGain;
+ eax_dirty_flags_.lGain = (eax_.lGain != eax_d_.lGain);
+}
+
+void EaxDistortionEffect::defer_low_pass_cutoff(
+ float flLowPassCutOff)
+{
+ eax_d_.flLowPassCutOff = flLowPassCutOff;
+ eax_dirty_flags_.flLowPassCutOff = (eax_.flLowPassCutOff != eax_d_.flLowPassCutOff);
+}
+
+void EaxDistortionEffect::defer_eq_center(
+ float flEQCenter)
+{
+ eax_d_.flEQCenter = flEQCenter;
+ eax_dirty_flags_.flEQCenter = (eax_.flEQCenter != eax_d_.flEQCenter);
+}
+
+void EaxDistortionEffect::defer_eq_bandwidth(
+ float flEQBandwidth)
+{
+ eax_d_.flEQBandwidth = flEQBandwidth;
+ eax_dirty_flags_.flEQBandwidth = (eax_.flEQBandwidth != eax_d_.flEQBandwidth);
+}
+
+void EaxDistortionEffect::defer_all(
+ const EAXDISTORTIONPROPERTIES& eax_all)
+{
+ defer_edge(eax_all.flEdge);
+ defer_gain(eax_all.lGain);
+ defer_low_pass_cutoff(eax_all.flLowPassCutOff);
+ defer_eq_center(eax_all.flEQCenter);
+ defer_eq_bandwidth(eax_all.flEQBandwidth);
+}
+
+void EaxDistortionEffect::defer_edge(
+ const EaxEaxCall& eax_call)
+{
+ const auto& edge =
+ eax_call.get_value<EaxDistortionEffectException, const decltype(EAXDISTORTIONPROPERTIES::flEdge)>();
+
+ validate_edge(edge);
+ defer_edge(edge);
+}
+
+void EaxDistortionEffect::defer_gain(
+ const EaxEaxCall& eax_call)
+{
+ const auto& gain =
+ eax_call.get_value<EaxDistortionEffectException, const decltype(EAXDISTORTIONPROPERTIES::lGain)>();
+
+ validate_gain(gain);
+ defer_gain(gain);
+}
+
+void EaxDistortionEffect::defer_low_pass_cutoff(
+ const EaxEaxCall& eax_call)
+{
+ const auto& lowpass_cutoff =
+ eax_call.get_value<EaxDistortionEffectException, const decltype(EAXDISTORTIONPROPERTIES::flLowPassCutOff)>();
+
+ validate_lowpass_cutoff(lowpass_cutoff);
+ defer_low_pass_cutoff(lowpass_cutoff);
+}
+
+void EaxDistortionEffect::defer_eq_center(
+ const EaxEaxCall& eax_call)
+{
+ const auto& eq_center =
+ eax_call.get_value<EaxDistortionEffectException, const decltype(EAXDISTORTIONPROPERTIES::flEQCenter)>();
+
+ validate_eq_center(eq_center);
+ defer_eq_center(eq_center);
+}
+
+void EaxDistortionEffect::defer_eq_bandwidth(
+ const EaxEaxCall& eax_call)
+{
+ const auto& eq_bandwidth =
+ eax_call.get_value<EaxDistortionEffectException, const decltype(EAXDISTORTIONPROPERTIES::flEQBandwidth)>();
+
+ validate_eq_bandwidth(eq_bandwidth);
+ defer_eq_bandwidth(eq_bandwidth);
+}
+
+void EaxDistortionEffect::defer_all(
+ const EaxEaxCall& eax_call)
+{
+ const auto& all =
+ eax_call.get_value<EaxDistortionEffectException, const EAXDISTORTIONPROPERTIES>();
+
+ validate_all(all);
+ defer_all(all);
+}
+
+// [[nodiscard]]
+bool EaxDistortionEffect::apply_deferred()
+{
+ if (eax_dirty_flags_ == EaxDistortionEffectDirtyFlags{})
+ {
+ return false;
+ }
+
+ eax_ = eax_d_;
+
+ if (eax_dirty_flags_.flEdge)
+ {
+ set_efx_edge();
+ }
+
+ if (eax_dirty_flags_.lGain)
+ {
+ set_efx_gain();
+ }
+
+ if (eax_dirty_flags_.flLowPassCutOff)
+ {
+ set_efx_lowpass_cutoff();
+ }
+
+ if (eax_dirty_flags_.flEQCenter)
+ {
+ set_efx_eq_center();
+ }
+
+ if (eax_dirty_flags_.flEQBandwidth)
+ {
+ set_efx_eq_bandwidth();
+ }
+
+ eax_dirty_flags_ = EaxDistortionEffectDirtyFlags{};
+
+ return true;
+}
+
+// [[nodiscard]]
+bool EaxDistortionEffect::set(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXDISTORTION_NONE:
+ break;
+
+ case EAXDISTORTION_ALLPARAMETERS:
+ defer_all(eax_call);
+ break;
+
+ case EAXDISTORTION_EDGE:
+ defer_edge(eax_call);
+ break;
+
+ case EAXDISTORTION_GAIN:
+ defer_gain(eax_call);
+ break;
+
+ case EAXDISTORTION_LOWPASSCUTOFF:
+ defer_low_pass_cutoff(eax_call);
+ break;
+
+ case EAXDISTORTION_EQCENTER:
+ defer_eq_center(eax_call);
+ break;
+
+ case EAXDISTORTION_EQBANDWIDTH:
+ defer_eq_bandwidth(eax_call);
+ break;
+
+ default:
+ throw EaxDistortionEffectException{"Unsupported property id."};
+ }
+
+ if (!eax_call.is_deferred())
+ {
+ return apply_deferred();
+ }
+
+ return false;
+}
+
+
+} // namespace
+
+
+EaxEffectUPtr eax_create_eax_distortion_effect(
+ EffectProps& al_effect_props)
+{
+ return std::make_unique<EaxDistortionEffect>(al_effect_props);
+}
+
+
+#endif // ALSOFT_EAX
diff --git a/al/effects/echo.cpp b/al/effects/echo.cpp
index 65f691c6..5ceb161d 100644
--- a/al/effects/echo.cpp
+++ b/al/effects/echo.cpp
@@ -7,6 +7,13 @@
#include "alc/effects/base.h"
#include "effects.h"
+#if ALSOFT_EAX
+#include "alnumeric.h"
+
+#include "al/eax_exception.h"
+#include "al/eax_utils.h"
+#endif // ALSOFT_EAX
+
namespace {
@@ -109,3 +116,530 @@ EffectProps genDefaultProps() noexcept
DEFINE_ALEFFECT_VTABLE(Echo);
const EffectProps EchoEffectProps{genDefaultProps()};
+
+#if ALSOFT_EAX
+namespace
+{
+
+
+using EaxEchoEffectDirtyFlagsValue = std::uint_least8_t;
+
+struct EaxEchoEffectDirtyFlags
+{
+ using EaxIsBitFieldStruct = bool;
+
+ EaxEchoEffectDirtyFlagsValue flDelay : 1;
+ EaxEchoEffectDirtyFlagsValue flLRDelay : 1;
+ EaxEchoEffectDirtyFlagsValue flDamping : 1;
+ EaxEchoEffectDirtyFlagsValue flFeedback : 1;
+ EaxEchoEffectDirtyFlagsValue flSpread : 1;
+}; // EaxEchoEffectDirtyFlags
+
+
+class EaxEchoEffect final :
+ public EaxEffect
+{
+public:
+ EaxEchoEffect(
+ EffectProps& al_effect_props);
+
+
+ // [[nodiscard]]
+ bool dispatch(
+ const EaxEaxCall& eax_call) override;
+
+
+private:
+ EffectProps& al_effect_props_;
+
+ EAXECHOPROPERTIES eax_{};
+ EAXECHOPROPERTIES eax_d_{};
+ EaxEchoEffectDirtyFlags eax_dirty_flags_{};
+
+
+ void set_eax_defaults();
+
+
+ void set_efx_delay();
+
+ void set_efx_lr_delay();
+
+ void set_efx_damping();
+
+ void set_efx_feedback();
+
+ void set_efx_spread();
+
+ void set_efx_defaults();
+
+
+ // [[nodiscard]]
+ bool get(
+ const EaxEaxCall& eax_call);
+
+
+ void validate_delay(
+ float flDelay);
+
+ void validate_lr_delay(
+ float flLRDelay);
+
+ void validate_damping(
+ float flDamping);
+
+ void validate_feedback(
+ float flFeedback);
+
+ void validate_spread(
+ float flSpread);
+
+ void validate_all(
+ const EAXECHOPROPERTIES& all);
+
+
+ void defer_delay(
+ float flDelay);
+
+ void defer_lr_delay(
+ float flLRDelay);
+
+ void defer_damping(
+ float flDamping);
+
+ void defer_feedback(
+ float flFeedback);
+
+ void defer_spread(
+ float flSpread);
+
+ void defer_all(
+ const EAXECHOPROPERTIES& all);
+
+
+ void defer_delay(
+ const EaxEaxCall& eax_call);
+
+ void defer_lr_delay(
+ const EaxEaxCall& eax_call);
+
+ void defer_damping(
+ const EaxEaxCall& eax_call);
+
+ void defer_feedback(
+ const EaxEaxCall& eax_call);
+
+ void defer_spread(
+ const EaxEaxCall& eax_call);
+
+ void defer_all(
+ const EaxEaxCall& eax_call);
+
+
+ bool apply_deferred();
+
+ bool set(
+ const EaxEaxCall& eax_call);
+}; // EaxEchoEffect
+
+
+class EaxEchoEffectException :
+ public EaxException
+{
+public:
+ explicit EaxEchoEffectException(
+ const char* message)
+ :
+ EaxException{"EAX_ECHO_EFFECT", message}
+ {
+ }
+}; // EaxEchoEffectException
+
+
+EaxEchoEffect::EaxEchoEffect(
+ EffectProps& al_effect_props)
+ :
+ al_effect_props_{al_effect_props}
+{
+ set_eax_defaults();
+ set_efx_defaults();
+}
+
+// [[nodiscard]]
+bool EaxEchoEffect::dispatch(
+ const EaxEaxCall& eax_call)
+{
+ return eax_call.is_get() ? get(eax_call) : set(eax_call);
+}
+
+void EaxEchoEffect::set_eax_defaults()
+{
+ eax_.flDelay = EAXECHO_DEFAULTDELAY;
+ eax_.flLRDelay = EAXECHO_DEFAULTLRDELAY;
+ eax_.flDamping = EAXECHO_DEFAULTDAMPING;
+ eax_.flFeedback = EAXECHO_DEFAULTFEEDBACK;
+ eax_.flSpread = EAXECHO_DEFAULTSPREAD;
+
+ eax_d_ = eax_;
+}
+
+void EaxEchoEffect::set_efx_delay()
+{
+ const auto delay = clamp(
+ eax_.flDelay,
+ AL_ECHO_MIN_DELAY,
+ AL_ECHO_MAX_DELAY);
+
+ al_effect_props_.Echo.Delay = delay;
+}
+
+void EaxEchoEffect::set_efx_lr_delay()
+{
+ const auto lr_delay = clamp(
+ eax_.flLRDelay,
+ AL_ECHO_MIN_LRDELAY,
+ AL_ECHO_MAX_LRDELAY);
+
+ al_effect_props_.Echo.LRDelay = lr_delay;
+}
+
+void EaxEchoEffect::set_efx_damping()
+{
+ const auto damping = clamp(
+ eax_.flDamping,
+ AL_ECHO_MIN_DAMPING,
+ AL_ECHO_MAX_DAMPING);
+
+ al_effect_props_.Echo.Damping = damping;
+}
+
+void EaxEchoEffect::set_efx_feedback()
+{
+ const auto feedback = clamp(
+ eax_.flFeedback,
+ AL_ECHO_MIN_FEEDBACK,
+ AL_ECHO_MAX_FEEDBACK);
+
+ al_effect_props_.Echo.Feedback = feedback;
+}
+
+void EaxEchoEffect::set_efx_spread()
+{
+ const auto spread = clamp(
+ eax_.flSpread,
+ AL_ECHO_MIN_SPREAD,
+ AL_ECHO_MAX_SPREAD);
+
+ al_effect_props_.Echo.Spread = spread;
+}
+
+void EaxEchoEffect::set_efx_defaults()
+{
+ set_efx_delay();
+ set_efx_lr_delay();
+ set_efx_damping();
+ set_efx_feedback();
+ set_efx_spread();
+}
+
+// [[nodiscard]]
+bool EaxEchoEffect::get(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXECHO_NONE:
+ break;
+
+ case EAXECHO_ALLPARAMETERS:
+ eax_call.set_value<EaxEchoEffectException>(eax_);
+ break;
+
+ case EAXECHO_DELAY:
+ eax_call.set_value<EaxEchoEffectException>(eax_.flDelay);
+ break;
+
+ case EAXECHO_LRDELAY:
+ eax_call.set_value<EaxEchoEffectException>(eax_.flLRDelay);
+ break;
+
+ case EAXECHO_DAMPING:
+ eax_call.set_value<EaxEchoEffectException>(eax_.flDamping);
+ break;
+
+ case EAXECHO_FEEDBACK:
+ eax_call.set_value<EaxEchoEffectException>(eax_.flFeedback);
+ break;
+
+ case EAXECHO_SPREAD:
+ eax_call.set_value<EaxEchoEffectException>(eax_.flSpread);
+ break;
+
+ default:
+ throw EaxEchoEffectException{"Unsupported property id."};
+ }
+
+ return false;
+}
+
+void EaxEchoEffect::validate_delay(
+ float flDelay)
+{
+ eax_validate_range<EaxEchoEffectException>(
+ "Delay",
+ flDelay,
+ EAXECHO_MINDELAY,
+ EAXECHO_MAXDELAY);
+}
+
+void EaxEchoEffect::validate_lr_delay(
+ float flLRDelay)
+{
+ eax_validate_range<EaxEchoEffectException>(
+ "LR Delay",
+ flLRDelay,
+ EAXECHO_MINLRDELAY,
+ EAXECHO_MAXLRDELAY);
+}
+
+void EaxEchoEffect::validate_damping(
+ float flDamping)
+{
+ eax_validate_range<EaxEchoEffectException>(
+ "Damping",
+ flDamping,
+ EAXECHO_MINDAMPING,
+ EAXECHO_MAXDAMPING);
+}
+
+void EaxEchoEffect::validate_feedback(
+ float flFeedback)
+{
+ eax_validate_range<EaxEchoEffectException>(
+ "Feedback",
+ flFeedback,
+ EAXECHO_MINFEEDBACK,
+ EAXECHO_MAXFEEDBACK);
+}
+
+void EaxEchoEffect::validate_spread(
+ float flSpread)
+{
+ eax_validate_range<EaxEchoEffectException>(
+ "Spread",
+ flSpread,
+ EAXECHO_MINSPREAD,
+ EAXECHO_MAXSPREAD);
+}
+
+void EaxEchoEffect::validate_all(
+ const EAXECHOPROPERTIES& all)
+{
+ validate_delay(all.flDelay);
+ validate_lr_delay(all.flLRDelay);
+ validate_damping(all.flDamping);
+ validate_feedback(all.flFeedback);
+ validate_spread(all.flSpread);
+}
+
+void EaxEchoEffect::defer_delay(
+ float flDelay)
+{
+ eax_d_.flDelay = flDelay;
+ eax_dirty_flags_.flDelay = (eax_.flDelay != eax_d_.flDelay);
+}
+
+void EaxEchoEffect::defer_lr_delay(
+ float flLRDelay)
+{
+ eax_d_.flLRDelay = flLRDelay;
+ eax_dirty_flags_.flLRDelay = (eax_.flLRDelay != eax_d_.flLRDelay);
+}
+
+void EaxEchoEffect::defer_damping(
+ float flDamping)
+{
+ eax_d_.flDamping = flDamping;
+ eax_dirty_flags_.flDamping = (eax_.flDamping != eax_d_.flDamping);
+}
+
+void EaxEchoEffect::defer_feedback(
+ float flFeedback)
+{
+ eax_d_.flFeedback = flFeedback;
+ eax_dirty_flags_.flFeedback = (eax_.flFeedback != eax_d_.flFeedback);
+}
+
+void EaxEchoEffect::defer_spread(
+ float flSpread)
+{
+ eax_d_.flSpread = flSpread;
+ eax_dirty_flags_.flSpread = (eax_.flSpread != eax_d_.flSpread);
+}
+
+void EaxEchoEffect::defer_all(
+ const EAXECHOPROPERTIES& all)
+{
+ defer_delay(all.flDelay);
+ defer_lr_delay(all.flLRDelay);
+ defer_damping(all.flDamping);
+ defer_feedback(all.flFeedback);
+ defer_spread(all.flSpread);
+}
+
+void EaxEchoEffect::defer_delay(
+ const EaxEaxCall& eax_call)
+{
+ const auto& delay =
+ eax_call.get_value<EaxEchoEffectException, const decltype(EAXECHOPROPERTIES::flDelay)>();
+
+ validate_delay(delay);
+ defer_delay(delay);
+}
+
+void EaxEchoEffect::defer_lr_delay(
+ const EaxEaxCall& eax_call)
+{
+ const auto& lr_delay =
+ eax_call.get_value<EaxEchoEffectException, const decltype(EAXECHOPROPERTIES::flLRDelay)>();
+
+ validate_lr_delay(lr_delay);
+ defer_lr_delay(lr_delay);
+}
+
+void EaxEchoEffect::defer_damping(
+ const EaxEaxCall& eax_call)
+{
+ const auto& damping =
+ eax_call.get_value<EaxEchoEffectException, const decltype(EAXECHOPROPERTIES::flDamping)>();
+
+ validate_damping(damping);
+ defer_damping(damping);
+}
+
+void EaxEchoEffect::defer_feedback(
+ const EaxEaxCall& eax_call)
+{
+ const auto& feedback =
+ eax_call.get_value<EaxEchoEffectException, const decltype(EAXECHOPROPERTIES::flFeedback)>();
+
+ validate_feedback(feedback);
+ defer_feedback(feedback);
+}
+
+void EaxEchoEffect::defer_spread(
+ const EaxEaxCall& eax_call)
+{
+ const auto& spread =
+ eax_call.get_value<EaxEchoEffectException, const decltype(EAXECHOPROPERTIES::flSpread)>();
+
+ validate_spread(spread);
+ defer_spread(spread);
+}
+
+void EaxEchoEffect::defer_all(
+ const EaxEaxCall& eax_call)
+{
+ const auto& all =
+ eax_call.get_value<EaxEchoEffectException, const EAXECHOPROPERTIES>();
+
+ validate_all(all);
+ defer_all(all);
+}
+
+// [[nodiscard]]
+bool EaxEchoEffect::apply_deferred()
+{
+ if (eax_dirty_flags_ == EaxEchoEffectDirtyFlags{})
+ {
+ return false;
+ }
+
+ eax_ = eax_d_;
+
+ if (eax_dirty_flags_.flDelay)
+ {
+ set_efx_delay();
+ }
+
+ if (eax_dirty_flags_.flLRDelay)
+ {
+ set_efx_lr_delay();
+ }
+
+ if (eax_dirty_flags_.flDamping)
+ {
+ set_efx_damping();
+ }
+
+ if (eax_dirty_flags_.flFeedback)
+ {
+ set_efx_feedback();
+ }
+
+ if (eax_dirty_flags_.flSpread)
+ {
+ set_efx_spread();
+ }
+
+ eax_dirty_flags_ = EaxEchoEffectDirtyFlags{};
+
+ return true;
+}
+
+// [[nodiscard]]
+bool EaxEchoEffect::set(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXECHO_NONE:
+ break;
+
+ case EAXECHO_ALLPARAMETERS:
+ defer_all(eax_call);
+ break;
+
+ case EAXECHO_DELAY:
+ defer_delay(eax_call);
+ break;
+
+ case EAXECHO_LRDELAY:
+ defer_lr_delay(eax_call);
+ break;
+
+ case EAXECHO_DAMPING:
+ defer_damping(eax_call);
+ break;
+
+ case EAXECHO_FEEDBACK:
+ defer_feedback(eax_call);
+ break;
+
+ case EAXECHO_SPREAD:
+ defer_spread(eax_call);
+ break;
+
+ default:
+ throw EaxEchoEffectException{"Unsupported property id."};
+ }
+
+ if (!eax_call.is_deferred())
+ {
+ return apply_deferred();
+ }
+
+ return false;
+}
+
+
+} // namespace
+
+
+EaxEffectUPtr eax_create_eax_echo_effect(
+ EffectProps& al_effect_props)
+{
+ return std::make_unique<EaxEchoEffect>(al_effect_props);
+}
+
+
+#endif // ALSOFT_EAX
diff --git a/al/effects/effects.cpp b/al/effects/effects.cpp
new file mode 100644
index 00000000..55abfdc5
--- /dev/null
+++ b/al/effects/effects.cpp
@@ -0,0 +1,106 @@
+#if ALSOFT_EAX
+
+
+#include <cassert>
+
+#include "AL/efx.h"
+
+#include "effects.h"
+
+
+EaxEffectUPtr eax_create_eax_null_effect();
+
+EaxEffectUPtr eax_create_eax_chorus_effect(
+ EffectProps& al_effect_props);
+
+EaxEffectUPtr eax_create_eax_distortion_effect(
+ EffectProps& al_effect_props);
+
+EaxEffectUPtr eax_create_eax_echo_effect(
+ EffectProps& al_effect_props);
+
+EaxEffectUPtr eax_create_eax_flanger_effect(
+ EffectProps& al_effect_props);
+
+EaxEffectUPtr eax_create_eax_frequency_shifter_effect(
+ EffectProps& al_effect_props);
+
+EaxEffectUPtr eax_create_eax_vocal_morpher_effect(
+ EffectProps& al_effect_props);
+
+EaxEffectUPtr eax_create_eax_pitch_shifter_effect(
+ EffectProps& al_effect_props);
+
+EaxEffectUPtr eax_create_eax_ring_modulator_effect(
+ EffectProps& al_effect_props);
+
+EaxEffectUPtr eax_create_eax_auto_wah_effect(
+ EffectProps& al_effect_props);
+
+EaxEffectUPtr eax_create_eax_compressor_effect(
+ EffectProps& al_effect_props);
+
+EaxEffectUPtr eax_create_eax_equalizer_effect(
+ EffectProps& al_effect_props);
+
+EaxEffectUPtr eax_create_eax_reverb_effect(
+ EffectProps& al_effect_props);
+
+
+EaxEffectUPtr eax_create_eax_effect(
+ ALenum al_effect_type,
+ EffectProps& al_effect_props)
+{
+#define EAX_PREFIX "[EAX_MAKE_EAX_EFFECT] "
+
+ switch (al_effect_type)
+ {
+ case AL_EFFECT_NULL:
+ return eax_create_eax_null_effect();
+
+ case AL_EFFECT_CHORUS:
+ return eax_create_eax_chorus_effect(al_effect_props);
+
+ case AL_EFFECT_DISTORTION:
+ return eax_create_eax_distortion_effect(al_effect_props);
+
+ case AL_EFFECT_ECHO:
+ return eax_create_eax_echo_effect(al_effect_props);
+
+ case AL_EFFECT_FLANGER:
+ return eax_create_eax_flanger_effect(al_effect_props);
+
+ case AL_EFFECT_FREQUENCY_SHIFTER:
+ return eax_create_eax_frequency_shifter_effect(al_effect_props);
+
+ case AL_EFFECT_VOCAL_MORPHER:
+ return eax_create_eax_vocal_morpher_effect(al_effect_props);
+
+ case AL_EFFECT_PITCH_SHIFTER:
+ return eax_create_eax_pitch_shifter_effect(al_effect_props);
+
+ case AL_EFFECT_RING_MODULATOR:
+ return eax_create_eax_ring_modulator_effect(al_effect_props);
+
+ case AL_EFFECT_AUTOWAH:
+ return eax_create_eax_auto_wah_effect(al_effect_props);
+
+ case AL_EFFECT_COMPRESSOR:
+ return eax_create_eax_compressor_effect(al_effect_props);
+
+ case AL_EFFECT_EQUALIZER:
+ return eax_create_eax_equalizer_effect(al_effect_props);
+
+ case AL_EFFECT_EAXREVERB:
+ return eax_create_eax_reverb_effect(al_effect_props);
+
+ default:
+ assert(false && "Unsupported AL effect type.");
+ return nullptr;
+ }
+
+#undef EAX_PREFIX
+}
+
+
+#endif // ALSOFT_EAX
diff --git a/al/effects/effects.h b/al/effects/effects.h
index 30b4bd75..6813beaa 100644
--- a/al/effects/effects.h
+++ b/al/effects/effects.h
@@ -5,6 +5,10 @@
#include "core/except.h"
+#if ALSOFT_EAX
+#include "al/eax_effect.h"
+#endif // ALSOFT_EAX
+
union EffectProps;
@@ -80,4 +84,11 @@ extern const EffectVtable VmorpherEffectVtable;
extern const EffectVtable DedicatedEffectVtable;
extern const EffectVtable ConvolutionEffectVtable;
+
+#if ALSOFT_EAX
+EaxEffectUPtr eax_create_eax_effect(
+ ALenum al_effect_type,
+ EffectProps& al_effect_props);
+#endif // ALSOFT_EAX
+
#endif /* AL_EFFECTS_EFFECTS_H */
diff --git a/al/effects/equalizer.cpp b/al/effects/equalizer.cpp
index 3c039688..c052db3e 100644
--- a/al/effects/equalizer.cpp
+++ b/al/effects/equalizer.cpp
@@ -7,6 +7,13 @@
#include "alc/effects/base.h"
#include "effects.h"
+#if ALSOFT_EAX
+#include "alnumeric.h"
+
+#include "al/eax_exception.h"
+#include "al/eax_utils.h"
+#endif // ALSOFT_EAX
+
namespace {
@@ -167,3 +174,862 @@ EffectProps genDefaultProps() noexcept
DEFINE_ALEFFECT_VTABLE(Equalizer);
const EffectProps EqualizerEffectProps{genDefaultProps()};
+
+#if ALSOFT_EAX
+namespace
+{
+
+
+using EaxEqualizerEffectDirtyFlagsValue = std::uint_least16_t;
+
+struct EaxEqualizerEffectDirtyFlags
+{
+ using EaxIsBitFieldStruct = bool;
+
+ EaxEqualizerEffectDirtyFlagsValue lLowGain : 1;
+ EaxEqualizerEffectDirtyFlagsValue flLowCutOff : 1;
+ EaxEqualizerEffectDirtyFlagsValue lMid1Gain : 1;
+ EaxEqualizerEffectDirtyFlagsValue flMid1Center : 1;
+ EaxEqualizerEffectDirtyFlagsValue flMid1Width : 1;
+ EaxEqualizerEffectDirtyFlagsValue lMid2Gain : 1;
+ EaxEqualizerEffectDirtyFlagsValue flMid2Center : 1;
+ EaxEqualizerEffectDirtyFlagsValue flMid2Width : 1;
+ EaxEqualizerEffectDirtyFlagsValue lHighGain : 1;
+ EaxEqualizerEffectDirtyFlagsValue flHighCutOff : 1;
+}; // EaxEqualizerEffectDirtyFlags
+
+
+class EaxEqualizerEffect final :
+ public EaxEffect
+{
+public:
+ EaxEqualizerEffect(
+ EffectProps& al_effect_props);
+
+
+ // [[nodiscard]]
+ bool dispatch(
+ const EaxEaxCall& eax_call) override;
+
+
+private:
+ EffectProps& al_effect_props_;
+
+ EAXEQUALIZERPROPERTIES eax_{};
+ EAXEQUALIZERPROPERTIES eax_d_{};
+ EaxEqualizerEffectDirtyFlags eax_dirty_flags_{};
+
+
+ void set_eax_defaults();
+
+
+ void set_efx_low_gain();
+
+ void set_efx_low_cutoff();
+
+ void set_efx_mid1_gain();
+
+ void set_efx_mid1_center();
+
+ void set_efx_mid1_width();
+
+ void set_efx_mid2_gain();
+
+ void set_efx_mid2_center();
+
+ void set_efx_mid2_width();
+
+ void set_efx_high_gain();
+
+ void set_efx_high_cutoff();
+
+ void set_efx_defaults();
+
+
+ // [[nodiscard]]
+ bool get(
+ const EaxEaxCall& eax_call);
+
+
+ void validate_low_gain(
+ long lLowGain);
+
+ void validate_low_cutoff(
+ float flLowCutOff);
+
+ void validate_mid1_gain(
+ long lMid1Gain);
+
+ void validate_mid1_center(
+ float flMid1Center);
+
+ void validate_mid1_width(
+ float flMid1Width);
+
+ void validate_mid2_gain(
+ long lMid2Gain);
+
+ void validate_mid2_center(
+ float flMid2Center);
+
+ void validate_mid2_width(
+ float flMid2Width);
+
+ void validate_high_gain(
+ long lHighGain);
+
+ void validate_high_cutoff(
+ float flHighCutOff);
+
+ void validate_all(
+ const EAXEQUALIZERPROPERTIES& all);
+
+
+ void defer_low_gain(
+ long lLowGain);
+
+ void defer_low_cutoff(
+ float flLowCutOff);
+
+ void defer_mid1_gain(
+ long lMid1Gain);
+
+ void defer_mid1_center(
+ float flMid1Center);
+
+ void defer_mid1_width(
+ float flMid1Width);
+
+ void defer_mid2_gain(
+ long lMid2Gain);
+
+ void defer_mid2_center(
+ float flMid2Center);
+
+ void defer_mid2_width(
+ float flMid2Width);
+
+ void defer_high_gain(
+ long lHighGain);
+
+ void defer_high_cutoff(
+ float flHighCutOff);
+
+ void defer_all(
+ const EAXEQUALIZERPROPERTIES& all);
+
+
+ void defer_low_gain(
+ const EaxEaxCall& eax_call);
+
+ void defer_low_cutoff(
+ const EaxEaxCall& eax_call);
+
+ void defer_mid1_gain(
+ const EaxEaxCall& eax_call);
+
+ void defer_mid1_center(
+ const EaxEaxCall& eax_call);
+
+ void defer_mid1_width(
+ const EaxEaxCall& eax_call);
+
+ void defer_mid2_gain(
+ const EaxEaxCall& eax_call);
+
+ void defer_mid2_center(
+ const EaxEaxCall& eax_call);
+
+ void defer_mid2_width(
+ const EaxEaxCall& eax_call);
+
+ void defer_high_gain(
+ const EaxEaxCall& eax_call);
+
+ void defer_high_cutoff(
+ const EaxEaxCall& eax_call);
+
+ void defer_all(
+ const EaxEaxCall& eax_call);
+
+
+ // [[nodiscard]]
+ bool apply_deferred();
+
+ // [[nodiscard]]
+ bool set(
+ const EaxEaxCall& eax_call);
+}; // EaxEqualizerEffect
+
+
+class EaxEqualizerEffectException :
+ public EaxException
+{
+public:
+ explicit EaxEqualizerEffectException(
+ const char* message)
+ :
+ EaxException{"EAX_EQUALIZER_EFFECT", message}
+ {
+ }
+}; // EaxEqualizerEffectException
+
+
+EaxEqualizerEffect::EaxEqualizerEffect(
+ EffectProps& al_effect_props)
+ :
+ al_effect_props_{al_effect_props}
+{
+ set_eax_defaults();
+ set_efx_defaults();
+}
+
+// [[nodiscard]]
+bool EaxEqualizerEffect::dispatch(
+ const EaxEaxCall& eax_call)
+{
+ return eax_call.is_get() ? get(eax_call) : set(eax_call);
+}
+
+void EaxEqualizerEffect::set_eax_defaults()
+{
+ eax_.lLowGain = EAXEQUALIZER_DEFAULTLOWGAIN;
+ eax_.flLowCutOff = EAXEQUALIZER_DEFAULTLOWCUTOFF;
+ eax_.lMid1Gain = EAXEQUALIZER_DEFAULTMID1GAIN;
+ eax_.flMid1Center = EAXEQUALIZER_DEFAULTMID1CENTER;
+ eax_.flMid1Width = EAXEQUALIZER_DEFAULTMID1WIDTH;
+ eax_.lMid2Gain = EAXEQUALIZER_DEFAULTMID2GAIN;
+ eax_.flMid2Center = EAXEQUALIZER_DEFAULTMID2CENTER;
+ eax_.flMid2Width = EAXEQUALIZER_DEFAULTMID2WIDTH;
+ eax_.lHighGain = EAXEQUALIZER_DEFAULTHIGHGAIN;
+ eax_.flHighCutOff = EAXEQUALIZER_DEFAULTHIGHCUTOFF;
+
+ eax_d_ = eax_;
+}
+
+void EaxEqualizerEffect::set_efx_low_gain()
+{
+ const auto low_gain = clamp(
+ level_mb_to_gain(static_cast<float>(eax_.lLowGain)),
+ AL_EQUALIZER_MIN_LOW_GAIN,
+ AL_EQUALIZER_MAX_LOW_GAIN);
+
+ al_effect_props_.Equalizer.LowGain = low_gain;
+}
+
+void EaxEqualizerEffect::set_efx_low_cutoff()
+{
+ const auto low_cutoff = clamp(
+ eax_.flLowCutOff,
+ AL_EQUALIZER_MIN_LOW_CUTOFF,
+ AL_EQUALIZER_MAX_LOW_CUTOFF);
+
+ al_effect_props_.Equalizer.LowCutoff = low_cutoff;
+}
+
+void EaxEqualizerEffect::set_efx_mid1_gain()
+{
+ const auto mid1_gain = clamp(
+ level_mb_to_gain(static_cast<float>(eax_.lMid1Gain)),
+ AL_EQUALIZER_MIN_MID1_GAIN,
+ AL_EQUALIZER_MAX_MID1_GAIN);
+
+ al_effect_props_.Equalizer.Mid1Gain = mid1_gain;
+}
+
+void EaxEqualizerEffect::set_efx_mid1_center()
+{
+ const auto mid1_center = clamp(
+ eax_.flMid1Center,
+ AL_EQUALIZER_MIN_MID1_CENTER,
+ AL_EQUALIZER_MAX_MID1_CENTER);
+
+ al_effect_props_.Equalizer.Mid1Center = mid1_center;
+}
+
+void EaxEqualizerEffect::set_efx_mid1_width()
+{
+ const auto mid1_width = clamp(
+ eax_.flMid1Width,
+ AL_EQUALIZER_MIN_MID1_WIDTH,
+ AL_EQUALIZER_MAX_MID1_WIDTH);
+
+ al_effect_props_.Equalizer.Mid1Width = mid1_width;
+}
+
+void EaxEqualizerEffect::set_efx_mid2_gain()
+{
+ const auto mid2_gain = clamp(
+ level_mb_to_gain(static_cast<float>(eax_.lMid2Gain)),
+ AL_EQUALIZER_MIN_MID2_GAIN,
+ AL_EQUALIZER_MAX_MID2_GAIN);
+
+ al_effect_props_.Equalizer.Mid2Gain = mid2_gain;
+}
+
+void EaxEqualizerEffect::set_efx_mid2_center()
+{
+ const auto mid2_center = clamp(
+ eax_.flMid2Center,
+ AL_EQUALIZER_MIN_MID2_CENTER,
+ AL_EQUALIZER_MAX_MID2_CENTER);
+
+ al_effect_props_.Equalizer.Mid2Center = mid2_center;
+}
+
+void EaxEqualizerEffect::set_efx_mid2_width()
+{
+ const auto mid2_width = clamp(
+ eax_.flMid2Width,
+ AL_EQUALIZER_MIN_MID2_WIDTH,
+ AL_EQUALIZER_MAX_MID2_WIDTH);
+
+ al_effect_props_.Equalizer.Mid2Width = mid2_width;
+}
+
+void EaxEqualizerEffect::set_efx_high_gain()
+{
+ const auto high_gain = clamp(
+ level_mb_to_gain(static_cast<float>(eax_.lHighGain)),
+ AL_EQUALIZER_MIN_HIGH_GAIN,
+ AL_EQUALIZER_MAX_HIGH_GAIN);
+
+ al_effect_props_.Equalizer.HighGain = high_gain;
+}
+
+void EaxEqualizerEffect::set_efx_high_cutoff()
+{
+ const auto high_cutoff = clamp(
+ eax_.flHighCutOff,
+ AL_EQUALIZER_MIN_HIGH_CUTOFF,
+ AL_EQUALIZER_MAX_HIGH_CUTOFF);
+
+ al_effect_props_.Equalizer.HighCutoff = high_cutoff;
+}
+
+void EaxEqualizerEffect::set_efx_defaults()
+{
+ set_efx_low_gain();
+ set_efx_low_cutoff();
+ set_efx_mid1_gain();
+ set_efx_mid1_center();
+ set_efx_mid1_width();
+ set_efx_mid2_gain();
+ set_efx_mid2_center();
+ set_efx_mid2_width();
+ set_efx_high_gain();
+ set_efx_high_cutoff();
+}
+
+// [[nodiscard]]
+bool EaxEqualizerEffect::get(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXEQUALIZER_NONE:
+ break;
+
+ case EAXEQUALIZER_ALLPARAMETERS:
+ eax_call.set_value<EaxEqualizerEffectException>(eax_);
+ break;
+
+ case EAXEQUALIZER_LOWGAIN:
+ eax_call.set_value<EaxEqualizerEffectException>(eax_.lLowGain);
+ break;
+
+ case EAXEQUALIZER_LOWCUTOFF:
+ eax_call.set_value<EaxEqualizerEffectException>(eax_.flLowCutOff);
+ break;
+
+ case EAXEQUALIZER_MID1GAIN:
+ eax_call.set_value<EaxEqualizerEffectException>(eax_.lMid1Gain);
+ break;
+
+ case EAXEQUALIZER_MID1CENTER:
+ eax_call.set_value<EaxEqualizerEffectException>(eax_.flMid1Center);
+ break;
+
+ case EAXEQUALIZER_MID1WIDTH:
+ eax_call.set_value<EaxEqualizerEffectException>(eax_.flMid1Width);
+ break;
+
+ case EAXEQUALIZER_MID2GAIN:
+ eax_call.set_value<EaxEqualizerEffectException>(eax_.lMid2Gain);
+ break;
+
+ case EAXEQUALIZER_MID2CENTER:
+ eax_call.set_value<EaxEqualizerEffectException>(eax_.flMid2Center);
+ break;
+
+ case EAXEQUALIZER_MID2WIDTH:
+ eax_call.set_value<EaxEqualizerEffectException>(eax_.flMid2Width);
+ break;
+
+ case EAXEQUALIZER_HIGHGAIN:
+ eax_call.set_value<EaxEqualizerEffectException>(eax_.lHighGain);
+ break;
+
+ case EAXEQUALIZER_HIGHCUTOFF:
+ eax_call.set_value<EaxEqualizerEffectException>(eax_.flHighCutOff);
+ break;
+
+ default:
+ throw EaxEqualizerEffectException{"Unsupported property id."};
+ }
+
+ return false;
+}
+
+void EaxEqualizerEffect::validate_low_gain(
+ long lLowGain)
+{
+ eax_validate_range<EaxEqualizerEffectException>(
+ "Low Gain",
+ lLowGain,
+ EAXEQUALIZER_MINLOWGAIN,
+ EAXEQUALIZER_MAXLOWGAIN);
+}
+
+void EaxEqualizerEffect::validate_low_cutoff(
+ float flLowCutOff)
+{
+ eax_validate_range<EaxEqualizerEffectException>(
+ "Low Cutoff",
+ flLowCutOff,
+ EAXEQUALIZER_MINLOWCUTOFF,
+ EAXEQUALIZER_MAXLOWCUTOFF);
+}
+
+void EaxEqualizerEffect::validate_mid1_gain(
+ long lMid1Gain)
+{
+ eax_validate_range<EaxEqualizerEffectException>(
+ "Mid1 Gain",
+ lMid1Gain,
+ EAXEQUALIZER_MINMID1GAIN,
+ EAXEQUALIZER_MAXMID1GAIN);
+}
+
+void EaxEqualizerEffect::validate_mid1_center(
+ float flMid1Center)
+{
+ eax_validate_range<EaxEqualizerEffectException>(
+ "Mid1 Center",
+ flMid1Center,
+ EAXEQUALIZER_MINMID1CENTER,
+ EAXEQUALIZER_MAXMID1CENTER);
+}
+
+void EaxEqualizerEffect::validate_mid1_width(
+ float flMid1Width)
+{
+ eax_validate_range<EaxEqualizerEffectException>(
+ "Mid1 Width",
+ flMid1Width,
+ EAXEQUALIZER_MINMID1WIDTH,
+ EAXEQUALIZER_MAXMID1WIDTH);
+}
+
+void EaxEqualizerEffect::validate_mid2_gain(
+ long lMid2Gain)
+{
+ eax_validate_range<EaxEqualizerEffectException>(
+ "Mid2 Gain",
+ lMid2Gain,
+ EAXEQUALIZER_MINMID2GAIN,
+ EAXEQUALIZER_MAXMID2GAIN);
+}
+
+void EaxEqualizerEffect::validate_mid2_center(
+ float flMid2Center)
+{
+ eax_validate_range<EaxEqualizerEffectException>(
+ "Mid2 Center",
+ flMid2Center,
+ EAXEQUALIZER_MINMID2CENTER,
+ EAXEQUALIZER_MAXMID2CENTER);
+}
+
+void EaxEqualizerEffect::validate_mid2_width(
+ float flMid2Width)
+{
+ eax_validate_range<EaxEqualizerEffectException>(
+ "Mid2 Width",
+ flMid2Width,
+ EAXEQUALIZER_MINMID2WIDTH,
+ EAXEQUALIZER_MAXMID2WIDTH);
+}
+
+void EaxEqualizerEffect::validate_high_gain(
+ long lHighGain)
+{
+ eax_validate_range<EaxEqualizerEffectException>(
+ "High Gain",
+ lHighGain,
+ EAXEQUALIZER_MINHIGHGAIN,
+ EAXEQUALIZER_MAXHIGHGAIN);
+}
+
+void EaxEqualizerEffect::validate_high_cutoff(
+ float flHighCutOff)
+{
+ eax_validate_range<EaxEqualizerEffectException>(
+ "High Cutoff",
+ flHighCutOff,
+ EAXEQUALIZER_MINHIGHCUTOFF,
+ EAXEQUALIZER_MAXHIGHCUTOFF);
+}
+
+void EaxEqualizerEffect::validate_all(
+ const EAXEQUALIZERPROPERTIES& all)
+{
+ validate_low_gain(all.lLowGain);
+ validate_low_cutoff(all.flLowCutOff);
+ validate_mid1_gain(all.lMid1Gain);
+ validate_mid1_center(all.flMid1Center);
+ validate_mid1_width(all.flMid1Width);
+ validate_mid2_gain(all.lMid2Gain);
+ validate_mid2_center(all.flMid2Center);
+ validate_mid2_width(all.flMid2Width);
+ validate_high_gain(all.lHighGain);
+ validate_high_cutoff(all.flHighCutOff);
+}
+
+void EaxEqualizerEffect::defer_low_gain(
+ long lLowGain)
+{
+ eax_d_.lLowGain = lLowGain;
+ eax_dirty_flags_.lLowGain = (eax_.lLowGain != eax_d_.lLowGain);
+}
+
+void EaxEqualizerEffect::defer_low_cutoff(
+ float flLowCutOff)
+{
+ eax_d_.flLowCutOff = flLowCutOff;
+ eax_dirty_flags_.flLowCutOff = (eax_.flLowCutOff != eax_d_.flLowCutOff);
+}
+
+void EaxEqualizerEffect::defer_mid1_gain(
+ long lMid1Gain)
+{
+ eax_d_.lMid1Gain = lMid1Gain;
+ eax_dirty_flags_.lMid1Gain = (eax_.lMid1Gain != eax_d_.lMid1Gain);
+}
+
+void EaxEqualizerEffect::defer_mid1_center(
+ float flMid1Center)
+{
+ eax_d_.flMid1Center = flMid1Center;
+ eax_dirty_flags_.flMid1Center = (eax_.flMid1Center != eax_d_.flMid1Center);
+}
+
+void EaxEqualizerEffect::defer_mid1_width(
+ float flMid1Width)
+{
+ eax_d_.flMid1Width = flMid1Width;
+ eax_dirty_flags_.flMid1Width = (eax_.flMid1Width != eax_d_.flMid1Width);
+}
+
+void EaxEqualizerEffect::defer_mid2_gain(
+ long lMid2Gain)
+{
+ eax_d_.lMid2Gain = lMid2Gain;
+ eax_dirty_flags_.lMid2Gain = (eax_.lMid2Gain != eax_d_.lMid2Gain);
+}
+
+void EaxEqualizerEffect::defer_mid2_center(
+ float flMid2Center)
+{
+ eax_d_.flMid2Center = flMid2Center;
+ eax_dirty_flags_.flMid2Center = (eax_.flMid2Center != eax_d_.flMid2Center);
+}
+
+void EaxEqualizerEffect::defer_mid2_width(
+ float flMid2Width)
+{
+ eax_d_.flMid2Width = flMid2Width;
+ eax_dirty_flags_.flMid2Width = (eax_.flMid2Width != eax_d_.flMid2Width);
+}
+
+void EaxEqualizerEffect::defer_high_gain(
+ long lHighGain)
+{
+ eax_d_.lHighGain = lHighGain;
+ eax_dirty_flags_.lHighGain = (eax_.lHighGain != eax_d_.lHighGain);
+}
+
+void EaxEqualizerEffect::defer_high_cutoff(
+ float flHighCutOff)
+{
+ eax_d_.flHighCutOff = flHighCutOff;
+ eax_dirty_flags_.flHighCutOff = (eax_.flHighCutOff != eax_d_.flHighCutOff);
+}
+
+void EaxEqualizerEffect::defer_all(
+ const EAXEQUALIZERPROPERTIES& all)
+{
+ defer_low_gain(all.lLowGain);
+ defer_low_cutoff(all.flLowCutOff);
+ defer_mid1_gain(all.lMid1Gain);
+ defer_mid1_center(all.flMid1Center);
+ defer_mid1_width(all.flMid1Width);
+ defer_mid2_gain(all.lMid2Gain);
+ defer_mid2_center(all.flMid2Center);
+ defer_mid2_width(all.flMid2Width);
+ defer_high_gain(all.lHighGain);
+ defer_high_cutoff(all.flHighCutOff);
+}
+
+void EaxEqualizerEffect::defer_low_gain(
+ const EaxEaxCall& eax_call)
+{
+ const auto& low_gain =
+ eax_call.get_value<EaxEqualizerEffectException, const decltype(EAXEQUALIZERPROPERTIES::lLowGain)>();
+
+ validate_low_gain(low_gain);
+ defer_low_gain(low_gain);
+}
+
+void EaxEqualizerEffect::defer_low_cutoff(
+ const EaxEaxCall& eax_call)
+{
+ const auto& low_cutoff =
+ eax_call.get_value<EaxEqualizerEffectException, const decltype(EAXEQUALIZERPROPERTIES::flLowCutOff)>();
+
+ validate_low_cutoff(low_cutoff);
+ defer_low_cutoff(low_cutoff);
+}
+
+void EaxEqualizerEffect::defer_mid1_gain(
+ const EaxEaxCall& eax_call)
+{
+ const auto& mid1_gain =
+ eax_call.get_value<EaxEqualizerEffectException, const decltype(EAXEQUALIZERPROPERTIES::lMid1Gain)>();
+
+ validate_mid1_gain(mid1_gain);
+ defer_mid1_gain(mid1_gain);
+}
+
+void EaxEqualizerEffect::defer_mid1_center(
+ const EaxEaxCall& eax_call)
+{
+ const auto& mid1_center =
+ eax_call.get_value<EaxEqualizerEffectException, const decltype(EAXEQUALIZERPROPERTIES::flMid1Center)>();
+
+ validate_mid1_center(mid1_center);
+ defer_mid1_center(mid1_center);
+}
+
+void EaxEqualizerEffect::defer_mid1_width(
+ const EaxEaxCall& eax_call)
+{
+ const auto& mid1_width =
+ eax_call.get_value<EaxEqualizerEffectException, const decltype(EAXEQUALIZERPROPERTIES::flMid1Width)>();
+
+ validate_mid1_width(mid1_width);
+ defer_mid1_width(mid1_width);
+}
+
+void EaxEqualizerEffect::defer_mid2_gain(
+ const EaxEaxCall& eax_call)
+{
+ const auto& mid2_gain =
+ eax_call.get_value<EaxEqualizerEffectException, const decltype(EAXEQUALIZERPROPERTIES::lMid2Gain)>();
+
+ validate_mid2_gain(mid2_gain);
+ defer_mid2_gain(mid2_gain);
+}
+
+void EaxEqualizerEffect::defer_mid2_center(
+ const EaxEaxCall& eax_call)
+{
+ const auto& mid2_center =
+ eax_call.get_value<EaxEqualizerEffectException, const decltype(EAXEQUALIZERPROPERTIES::flMid2Center)>();
+
+ validate_mid2_center(mid2_center);
+ defer_mid2_center(mid2_center);
+}
+
+void EaxEqualizerEffect::defer_mid2_width(
+ const EaxEaxCall& eax_call)
+{
+ const auto& mid2_width =
+ eax_call.get_value<EaxEqualizerEffectException, const decltype(EAXEQUALIZERPROPERTIES::flMid2Width)>();
+
+ validate_mid2_width(mid2_width);
+ defer_mid2_width(mid2_width);
+}
+
+void EaxEqualizerEffect::defer_high_gain(
+ const EaxEaxCall& eax_call)
+{
+ const auto& high_gain =
+ eax_call.get_value<EaxEqualizerEffectException, const decltype(EAXEQUALIZERPROPERTIES::lHighGain)>();
+
+ validate_high_gain(high_gain);
+ defer_high_gain(high_gain);
+}
+
+void EaxEqualizerEffect::defer_high_cutoff(
+ const EaxEaxCall& eax_call)
+{
+ const auto& high_cutoff =
+ eax_call.get_value<EaxEqualizerEffectException, const decltype(EAXEQUALIZERPROPERTIES::flHighCutOff)>();
+
+ validate_high_cutoff(high_cutoff);
+ defer_high_cutoff(high_cutoff);
+}
+
+void EaxEqualizerEffect::defer_all(
+ const EaxEaxCall& eax_call)
+{
+ const auto& all =
+ eax_call.get_value<EaxEqualizerEffectException, const EAXEQUALIZERPROPERTIES>();
+
+ validate_all(all);
+ defer_all(all);
+}
+
+// [[nodiscard]]
+bool EaxEqualizerEffect::apply_deferred()
+{
+ if (eax_dirty_flags_ == EaxEqualizerEffectDirtyFlags{})
+ {
+ return false;
+ }
+
+ eax_ = eax_d_;
+
+ if (eax_dirty_flags_.lLowGain)
+ {
+ set_efx_low_gain();
+ }
+
+ if (eax_dirty_flags_.flLowCutOff)
+ {
+ set_efx_low_cutoff();
+ }
+
+ if (eax_dirty_flags_.lMid1Gain)
+ {
+ set_efx_mid1_gain();
+ }
+
+ if (eax_dirty_flags_.flMid1Center)
+ {
+ set_efx_mid1_center();
+ }
+
+ if (eax_dirty_flags_.flMid1Width)
+ {
+ set_efx_mid1_width();
+ }
+
+ if (eax_dirty_flags_.lMid2Gain)
+ {
+ set_efx_mid2_gain();
+ }
+
+ if (eax_dirty_flags_.flMid2Center)
+ {
+ set_efx_mid2_center();
+ }
+
+ if (eax_dirty_flags_.flMid2Width)
+ {
+ set_efx_mid2_width();
+ }
+
+ if (eax_dirty_flags_.lHighGain)
+ {
+ set_efx_high_gain();
+ }
+
+ if (eax_dirty_flags_.flHighCutOff)
+ {
+ set_efx_high_cutoff();
+ }
+
+ eax_dirty_flags_ = EaxEqualizerEffectDirtyFlags{};
+
+ return true;
+}
+
+// [[nodiscard]]
+bool EaxEqualizerEffect::set(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXEQUALIZER_NONE:
+ break;
+
+ case EAXEQUALIZER_ALLPARAMETERS:
+ defer_all(eax_call);
+ break;
+
+ case EAXEQUALIZER_LOWGAIN:
+ defer_low_gain(eax_call);
+ break;
+
+ case EAXEQUALIZER_LOWCUTOFF:
+ defer_low_cutoff(eax_call);
+ break;
+
+ case EAXEQUALIZER_MID1GAIN:
+ defer_mid1_gain(eax_call);
+ break;
+
+ case EAXEQUALIZER_MID1CENTER:
+ defer_mid1_center(eax_call);
+ break;
+
+ case EAXEQUALIZER_MID1WIDTH:
+ defer_mid1_width(eax_call);
+ break;
+
+ case EAXEQUALIZER_MID2GAIN:
+ defer_mid2_gain(eax_call);
+ break;
+
+ case EAXEQUALIZER_MID2CENTER:
+ defer_mid2_center(eax_call);
+ break;
+
+ case EAXEQUALIZER_MID2WIDTH:
+ defer_mid2_width(eax_call);
+ break;
+
+ case EAXEQUALIZER_HIGHGAIN:
+ defer_high_gain(eax_call);
+ break;
+
+ case EAXEQUALIZER_HIGHCUTOFF:
+ defer_high_cutoff(eax_call);
+ break;
+
+ default:
+ throw EaxEqualizerEffectException{"Unsupported property id."};
+ }
+
+ if (!eax_call.is_deferred())
+ {
+ return apply_deferred();
+ }
+
+ return false;
+}
+
+
+} // namespace
+
+
+EaxEffectUPtr eax_create_eax_equalizer_effect(
+ EffectProps& al_effect_props)
+{
+ return std::make_unique<EaxEqualizerEffect>(al_effect_props);
+}
+
+
+#endif // ALSOFT_EAX
diff --git a/al/effects/fshifter.cpp b/al/effects/fshifter.cpp
index 4aa860a2..aa4ddadb 100644
--- a/al/effects/fshifter.cpp
+++ b/al/effects/fshifter.cpp
@@ -10,6 +10,15 @@
#include "aloptional.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 {
@@ -128,3 +137,408 @@ EffectProps genDefaultProps() noexcept
DEFINE_ALEFFECT_VTABLE(Fshifter);
const EffectProps FshifterEffectProps{genDefaultProps()};
+
+#if ALSOFT_EAX
+namespace
+{
+
+
+using EaxFrequencyShifterEffectDirtyFlagsValue = std::uint_least8_t;
+
+struct EaxFrequencyShifterEffectDirtyFlags
+{
+ using EaxIsBitFieldStruct = bool;
+
+ EaxFrequencyShifterEffectDirtyFlagsValue flFrequency : 1;
+ EaxFrequencyShifterEffectDirtyFlagsValue ulLeftDirection : 1;
+ EaxFrequencyShifterEffectDirtyFlagsValue ulRightDirection : 1;
+}; // EaxFrequencyShifterEffectDirtyFlags
+
+
+class EaxFrequencyShifterEffect final :
+ public EaxEffect
+{
+public:
+ EaxFrequencyShifterEffect(
+ EffectProps& al_effect_props);
+
+
+ // [[nodiscard]]
+ bool dispatch(
+ const EaxEaxCall& eax_call) override;
+
+
+private:
+ EffectProps& al_effect_props_;
+
+ EAXFREQUENCYSHIFTERPROPERTIES eax_{};
+ EAXFREQUENCYSHIFTERPROPERTIES eax_d_{};
+ EaxFrequencyShifterEffectDirtyFlags eax_dirty_flags_{};
+
+
+ void set_eax_defaults();
+
+
+ void set_efx_frequency();
+
+ void set_efx_left_direction();
+
+ void set_efx_right_direction();
+
+ void set_efx_defaults();
+
+
+ // [[nodiscard]]
+ bool get(
+ const EaxEaxCall& eax_call);
+
+
+ void validate_frequency(
+ float flFrequency);
+
+ void validate_left_direction(
+ unsigned long ulLeftDirection);
+
+ void validate_right_direction(
+ unsigned long ulRightDirection);
+
+ void validate_all(
+ const EAXFREQUENCYSHIFTERPROPERTIES& all);
+
+
+ void defer_frequency(
+ float flFrequency);
+
+ void defer_left_direction(
+ unsigned long ulLeftDirection);
+
+ void defer_right_direction(
+ unsigned long ulRightDirection);
+
+ void defer_all(
+ const EAXFREQUENCYSHIFTERPROPERTIES& all);
+
+
+ void defer_frequency(
+ const EaxEaxCall& eax_call);
+
+ void defer_left_direction(
+ const EaxEaxCall& eax_call);
+
+ void defer_right_direction(
+ const EaxEaxCall& eax_call);
+
+ void defer_all(
+ const EaxEaxCall& eax_call);
+
+
+ // [[nodiscard]]
+ bool apply_deferred();
+
+ // [[nodiscard]]
+ bool set(
+ const EaxEaxCall& eax_call);
+}; // EaxFrequencyShifterEffect
+
+
+class EaxFrequencyShifterEffectException :
+ public EaxException
+{
+public:
+ explicit EaxFrequencyShifterEffectException(
+ const char* message)
+ :
+ EaxException{"EAX_FREQUENCY_SHIFTER_EFFECT", message}
+ {
+ }
+}; // EaxFrequencyShifterEffectException
+
+
+EaxFrequencyShifterEffect::EaxFrequencyShifterEffect(
+ EffectProps& al_effect_props)
+ :
+ al_effect_props_{al_effect_props}
+{
+ set_eax_defaults();
+ set_efx_defaults();
+}
+
+// [[nodiscard]]
+bool EaxFrequencyShifterEffect::dispatch(
+ const EaxEaxCall& eax_call)
+{
+ return eax_call.is_get() ? get(eax_call) : set(eax_call);
+}
+
+void EaxFrequencyShifterEffect::set_eax_defaults()
+{
+ eax_.flFrequency = EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY;
+ eax_.ulLeftDirection = EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION;
+ eax_.ulRightDirection = EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION;
+
+ eax_d_ = eax_;
+}
+
+void EaxFrequencyShifterEffect::set_efx_frequency()
+{
+ const auto frequency = clamp(
+ eax_.flFrequency,
+ AL_FREQUENCY_SHIFTER_MIN_FREQUENCY,
+ AL_FREQUENCY_SHIFTER_MAX_FREQUENCY);
+
+ al_effect_props_.Fshifter.Frequency = frequency;
+}
+
+void EaxFrequencyShifterEffect::set_efx_left_direction()
+{
+ const auto left_direction = clamp(
+ static_cast<ALint>(eax_.ulLeftDirection),
+ AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION,
+ AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION);
+
+ const auto efx_left_direction = DirectionFromEmum(left_direction);
+ assert(efx_left_direction.has_value());
+ al_effect_props_.Fshifter.LeftDirection = *efx_left_direction;
+}
+
+void EaxFrequencyShifterEffect::set_efx_right_direction()
+{
+ const auto right_direction = clamp(
+ static_cast<ALint>(eax_.ulRightDirection),
+ AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION,
+ AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION);
+
+ const auto efx_right_direction = DirectionFromEmum(right_direction);
+ assert(efx_right_direction.has_value());
+ al_effect_props_.Fshifter.RightDirection = *efx_right_direction;
+}
+
+void EaxFrequencyShifterEffect::set_efx_defaults()
+{
+ set_efx_frequency();
+ set_efx_left_direction();
+ set_efx_right_direction();
+}
+
+// [[nodiscard]]
+bool EaxFrequencyShifterEffect::get(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXFREQUENCYSHIFTER_NONE:
+ break;
+
+ case EAXFREQUENCYSHIFTER_ALLPARAMETERS:
+ eax_call.set_value<EaxFrequencyShifterEffectException>(eax_);
+ break;
+
+ case EAXFREQUENCYSHIFTER_FREQUENCY:
+ eax_call.set_value<EaxFrequencyShifterEffectException>(eax_.flFrequency);
+ break;
+
+ case EAXFREQUENCYSHIFTER_LEFTDIRECTION:
+ eax_call.set_value<EaxFrequencyShifterEffectException>(eax_.ulLeftDirection);
+ break;
+
+ case EAXFREQUENCYSHIFTER_RIGHTDIRECTION:
+ eax_call.set_value<EaxFrequencyShifterEffectException>(eax_.ulRightDirection);
+ break;
+
+ default:
+ throw EaxFrequencyShifterEffectException{"Unsupported property id."};
+ }
+
+ return false;
+}
+
+void EaxFrequencyShifterEffect::validate_frequency(
+ float flFrequency)
+{
+ eax_validate_range<EaxFrequencyShifterEffectException>(
+ "Frequency",
+ flFrequency,
+ EAXFREQUENCYSHIFTER_MINFREQUENCY,
+ EAXFREQUENCYSHIFTER_MAXFREQUENCY);
+}
+
+void EaxFrequencyShifterEffect::validate_left_direction(
+ unsigned long ulLeftDirection)
+{
+ eax_validate_range<EaxFrequencyShifterEffectException>(
+ "Left Direction",
+ ulLeftDirection,
+ EAXFREQUENCYSHIFTER_MINLEFTDIRECTION,
+ EAXFREQUENCYSHIFTER_MAXLEFTDIRECTION);
+}
+
+void EaxFrequencyShifterEffect::validate_right_direction(
+ unsigned long ulRightDirection)
+{
+ eax_validate_range<EaxFrequencyShifterEffectException>(
+ "Right Direction",
+ ulRightDirection,
+ EAXFREQUENCYSHIFTER_MINRIGHTDIRECTION,
+ EAXFREQUENCYSHIFTER_MAXRIGHTDIRECTION);
+}
+
+void EaxFrequencyShifterEffect::validate_all(
+ const EAXFREQUENCYSHIFTERPROPERTIES& all)
+{
+ validate_frequency(all.flFrequency);
+ validate_left_direction(all.ulLeftDirection);
+ validate_right_direction(all.ulRightDirection);
+}
+
+void EaxFrequencyShifterEffect::defer_frequency(
+ float flFrequency)
+{
+ eax_d_.flFrequency = flFrequency;
+ eax_dirty_flags_.flFrequency = (eax_.flFrequency != eax_d_.flFrequency);
+}
+
+void EaxFrequencyShifterEffect::defer_left_direction(
+ unsigned long ulLeftDirection)
+{
+ eax_d_.ulLeftDirection = ulLeftDirection;
+ eax_dirty_flags_.ulLeftDirection = (eax_.ulLeftDirection != eax_d_.ulLeftDirection);
+}
+
+void EaxFrequencyShifterEffect::defer_right_direction(
+ unsigned long ulRightDirection)
+{
+ eax_d_.ulRightDirection = ulRightDirection;
+ eax_dirty_flags_.ulRightDirection = (eax_.ulRightDirection != eax_d_.ulRightDirection);
+}
+
+void EaxFrequencyShifterEffect::defer_all(
+ const EAXFREQUENCYSHIFTERPROPERTIES& all)
+{
+ defer_frequency(all.flFrequency);
+ defer_left_direction(all.ulLeftDirection);
+ defer_right_direction(all.ulRightDirection);
+}
+
+void EaxFrequencyShifterEffect::defer_frequency(
+ const EaxEaxCall& eax_call)
+{
+ const auto& frequency =
+ eax_call.get_value<
+ EaxFrequencyShifterEffectException, const decltype(EAXFREQUENCYSHIFTERPROPERTIES::flFrequency)>();
+
+ validate_frequency(frequency);
+ defer_frequency(frequency);
+}
+
+void EaxFrequencyShifterEffect::defer_left_direction(
+ const EaxEaxCall& eax_call)
+{
+ const auto& left_direction =
+ eax_call.get_value<
+ EaxFrequencyShifterEffectException, const decltype(EAXFREQUENCYSHIFTERPROPERTIES::ulLeftDirection)>();
+
+ validate_left_direction(left_direction);
+ defer_left_direction(left_direction);
+}
+
+void EaxFrequencyShifterEffect::defer_right_direction(
+ const EaxEaxCall& eax_call)
+{
+ const auto& right_direction =
+ eax_call.get_value<
+ EaxFrequencyShifterEffectException, const decltype(EAXFREQUENCYSHIFTERPROPERTIES::ulRightDirection)>();
+
+ validate_right_direction(right_direction);
+ defer_right_direction(right_direction);
+}
+
+void EaxFrequencyShifterEffect::defer_all(
+ const EaxEaxCall& eax_call)
+{
+ const auto& all =
+ eax_call.get_value<
+ EaxFrequencyShifterEffectException, const EAXFREQUENCYSHIFTERPROPERTIES>();
+
+ validate_all(all);
+ defer_all(all);
+}
+
+// [[nodiscard]]
+bool EaxFrequencyShifterEffect::apply_deferred()
+{
+ if (eax_dirty_flags_ == EaxFrequencyShifterEffectDirtyFlags{})
+ {
+ return false;
+ }
+
+ eax_ = eax_d_;
+
+ if (eax_dirty_flags_.flFrequency)
+ {
+ set_efx_frequency();
+ }
+
+ if (eax_dirty_flags_.ulLeftDirection)
+ {
+ set_efx_left_direction();
+ }
+
+ if (eax_dirty_flags_.ulRightDirection)
+ {
+ set_efx_right_direction();
+ }
+
+ eax_dirty_flags_ = EaxFrequencyShifterEffectDirtyFlags{};
+
+ return true;
+}
+
+// [[nodiscard]]
+bool EaxFrequencyShifterEffect::set(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXFREQUENCYSHIFTER_NONE:
+ break;
+
+ case EAXFREQUENCYSHIFTER_ALLPARAMETERS:
+ defer_all(eax_call);
+ break;
+
+ case EAXFREQUENCYSHIFTER_FREQUENCY:
+ defer_frequency(eax_call);
+ break;
+
+ case EAXFREQUENCYSHIFTER_LEFTDIRECTION:
+ defer_left_direction(eax_call);
+ break;
+
+ case EAXFREQUENCYSHIFTER_RIGHTDIRECTION:
+ defer_right_direction(eax_call);
+ break;
+
+ default:
+ throw EaxFrequencyShifterEffectException{"Unsupported property id."};
+ }
+
+ if (!eax_call.is_deferred())
+ {
+ return apply_deferred();
+ }
+
+ return false;
+}
+
+
+} // namespace
+
+
+EaxEffectUPtr eax_create_eax_frequency_shifter_effect(
+ EffectProps& al_effect_props)
+{
+ return std::make_unique<EaxFrequencyShifterEffect>(al_effect_props);
+}
+
+
+#endif // ALSOFT_EAX
diff --git a/al/effects/modulator.cpp b/al/effects/modulator.cpp
index d8a1bed6..6a30dc09 100644
--- a/al/effects/modulator.cpp
+++ b/al/effects/modulator.cpp
@@ -10,6 +10,15 @@
#include "aloptional.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 {
@@ -134,3 +143,405 @@ EffectProps genDefaultProps() noexcept
DEFINE_ALEFFECT_VTABLE(Modulator);
const EffectProps ModulatorEffectProps{genDefaultProps()};
+
+#if ALSOFT_EAX
+namespace
+{
+
+
+using EaxRingModulatorEffectDirtyFlagsValue = std::uint_least8_t;
+
+struct EaxRingModulatorEffectDirtyFlags
+{
+ using EaxIsBitFieldStruct = bool;
+
+ EaxRingModulatorEffectDirtyFlagsValue flFrequency : 1;
+ EaxRingModulatorEffectDirtyFlagsValue flHighPassCutOff : 1;
+ EaxRingModulatorEffectDirtyFlagsValue ulWaveform : 1;
+}; // EaxPitchShifterEffectDirtyFlags
+
+
+class EaxRingModulatorEffect final :
+ public EaxEffect
+{
+public:
+ EaxRingModulatorEffect(
+ EffectProps& al_effect_props);
+
+
+ // [[nodiscard]]
+ bool dispatch(
+ const EaxEaxCall& eax_call) override;
+
+
+private:
+ EffectProps& al_effect_props_;
+
+ EAXRINGMODULATORPROPERTIES eax_{};
+ EAXRINGMODULATORPROPERTIES eax_d_{};
+ EaxRingModulatorEffectDirtyFlags eax_dirty_flags_{};
+
+
+ void set_eax_defaults();
+
+
+ void set_efx_frequency();
+
+ void set_efx_high_pass_cutoff();
+
+ void set_efx_waveform();
+
+ void set_efx_defaults();
+
+
+ // [[nodiscard]]
+ bool get(
+ const EaxEaxCall& eax_call);
+
+
+ void validate_frequency(
+ float flFrequency);
+
+ void validate_high_pass_cutoff(
+ float flHighPassCutOff);
+
+ void validate_waveform(
+ unsigned long ulWaveform);
+
+ void validate_all(
+ const EAXRINGMODULATORPROPERTIES& all);
+
+
+ void defer_frequency(
+ float flFrequency);
+
+ void defer_high_pass_cutoff(
+ float flHighPassCutOff);
+
+ void defer_waveform(
+ unsigned long ulWaveform);
+
+ void defer_all(
+ const EAXRINGMODULATORPROPERTIES& all);
+
+
+ void defer_frequency(
+ const EaxEaxCall& eax_call);
+
+ void defer_high_pass_cutoff(
+ const EaxEaxCall& eax_call);
+
+ void defer_waveform(
+ const EaxEaxCall& eax_call);
+
+ void defer_all(
+ const EaxEaxCall& eax_call);
+
+
+ // [[nodiscard]]
+ bool apply_deferred();
+
+ // [[nodiscard]]
+ bool set(
+ const EaxEaxCall& eax_call);
+}; // EaxRingModulatorEffect
+
+
+class EaxRingModulatorEffectException :
+ public EaxException
+{
+public:
+ explicit EaxRingModulatorEffectException(
+ const char* message)
+ :
+ EaxException{"EAX_RING_MODULATOR_EFFECT", message}
+ {
+ }
+}; // EaxRingModulatorEffectException
+
+
+EaxRingModulatorEffect::EaxRingModulatorEffect(
+ EffectProps& al_effect_props)
+ :
+ al_effect_props_{al_effect_props}
+{
+ set_eax_defaults();
+ set_efx_defaults();
+}
+
+// [[nodiscard]]
+bool EaxRingModulatorEffect::dispatch(
+ const EaxEaxCall& eax_call)
+{
+ return eax_call.is_get() ? get(eax_call) : set(eax_call);
+}
+
+void EaxRingModulatorEffect::set_eax_defaults()
+{
+ eax_.flFrequency = EAXRINGMODULATOR_DEFAULTFREQUENCY;
+ eax_.flHighPassCutOff = EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF;
+ eax_.ulWaveform = EAXRINGMODULATOR_DEFAULTWAVEFORM;
+
+ eax_d_ = eax_;
+}
+
+void EaxRingModulatorEffect::set_efx_frequency()
+{
+ const auto frequency = clamp(
+ eax_.flFrequency,
+ AL_RING_MODULATOR_MIN_FREQUENCY,
+ AL_RING_MODULATOR_MAX_FREQUENCY);
+
+ al_effect_props_.Modulator.Frequency = frequency;
+}
+
+void EaxRingModulatorEffect::set_efx_high_pass_cutoff()
+{
+ const auto high_pass_cutoff = clamp(
+ eax_.flHighPassCutOff,
+ AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF,
+ AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF);
+
+ al_effect_props_.Modulator.HighPassCutoff = high_pass_cutoff;
+}
+
+void EaxRingModulatorEffect::set_efx_waveform()
+{
+ const auto waveform = clamp(
+ static_cast<ALint>(eax_.ulWaveform),
+ AL_RING_MODULATOR_MIN_WAVEFORM,
+ AL_RING_MODULATOR_MAX_WAVEFORM);
+
+ const auto efx_waveform = WaveformFromEmum(waveform);
+ assert(efx_waveform.has_value());
+ al_effect_props_.Modulator.Waveform = *efx_waveform;
+}
+
+void EaxRingModulatorEffect::set_efx_defaults()
+{
+ set_efx_frequency();
+ set_efx_high_pass_cutoff();
+ set_efx_waveform();
+}
+
+// [[nodiscard]]
+bool EaxRingModulatorEffect::get(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXRINGMODULATOR_NONE:
+ break;
+
+ case EAXRINGMODULATOR_ALLPARAMETERS:
+ eax_call.set_value<EaxRingModulatorEffectException>(eax_);
+ break;
+
+ case EAXRINGMODULATOR_FREQUENCY:
+ eax_call.set_value<EaxRingModulatorEffectException>(eax_.flFrequency);
+ break;
+
+ case EAXRINGMODULATOR_HIGHPASSCUTOFF:
+ eax_call.set_value<EaxRingModulatorEffectException>(eax_.flHighPassCutOff);
+ break;
+
+ case EAXRINGMODULATOR_WAVEFORM:
+ eax_call.set_value<EaxRingModulatorEffectException>(eax_.ulWaveform);
+ break;
+
+ default:
+ throw EaxRingModulatorEffectException{"Unsupported property id."};
+ }
+
+ return false;
+}
+
+void EaxRingModulatorEffect::validate_frequency(
+ float flFrequency)
+{
+ eax_validate_range<EaxRingModulatorEffectException>(
+ "Frequency",
+ flFrequency,
+ EAXRINGMODULATOR_MINFREQUENCY,
+ EAXRINGMODULATOR_MAXFREQUENCY);
+}
+
+void EaxRingModulatorEffect::validate_high_pass_cutoff(
+ float flHighPassCutOff)
+{
+ eax_validate_range<EaxRingModulatorEffectException>(
+ "High-Pass Cutoff",
+ flHighPassCutOff,
+ EAXRINGMODULATOR_MINHIGHPASSCUTOFF,
+ EAXRINGMODULATOR_MAXHIGHPASSCUTOFF);
+}
+
+void EaxRingModulatorEffect::validate_waveform(
+ unsigned long ulWaveform)
+{
+ eax_validate_range<EaxRingModulatorEffectException>(
+ "Waveform",
+ ulWaveform,
+ EAXRINGMODULATOR_MINWAVEFORM,
+ EAXRINGMODULATOR_MAXWAVEFORM);
+}
+
+void EaxRingModulatorEffect::validate_all(
+ const EAXRINGMODULATORPROPERTIES& all)
+{
+ validate_frequency(all.flFrequency);
+ validate_high_pass_cutoff(all.flHighPassCutOff);
+ validate_waveform(all.ulWaveform);
+}
+
+void EaxRingModulatorEffect::defer_frequency(
+ float flFrequency)
+{
+ eax_d_.flFrequency = flFrequency;
+ eax_dirty_flags_.flFrequency = (eax_.flFrequency != eax_d_.flFrequency);
+}
+
+void EaxRingModulatorEffect::defer_high_pass_cutoff(
+ float flHighPassCutOff)
+{
+ eax_d_.flHighPassCutOff = flHighPassCutOff;
+ eax_dirty_flags_.flHighPassCutOff = (eax_.flHighPassCutOff != eax_d_.flHighPassCutOff);
+}
+
+void EaxRingModulatorEffect::defer_waveform(
+ unsigned long ulWaveform)
+{
+ eax_d_.ulWaveform = ulWaveform;
+ eax_dirty_flags_.ulWaveform = (eax_.ulWaveform != eax_d_.ulWaveform);
+}
+
+void EaxRingModulatorEffect::defer_all(
+ const EAXRINGMODULATORPROPERTIES& all)
+{
+ defer_frequency(all.flFrequency);
+ defer_high_pass_cutoff(all.flHighPassCutOff);
+ defer_waveform(all.ulWaveform);
+}
+
+void EaxRingModulatorEffect::defer_frequency(
+ const EaxEaxCall& eax_call)
+{
+ const auto& frequency =
+ eax_call.get_value<
+ EaxRingModulatorEffectException, const decltype(EAXRINGMODULATORPROPERTIES::flFrequency)>();
+
+ validate_frequency(frequency);
+ defer_frequency(frequency);
+}
+
+void EaxRingModulatorEffect::defer_high_pass_cutoff(
+ const EaxEaxCall& eax_call)
+{
+ const auto& high_pass_cutoff =
+ eax_call.get_value<
+ EaxRingModulatorEffectException, const decltype(EAXRINGMODULATORPROPERTIES::flHighPassCutOff)>();
+
+ validate_high_pass_cutoff(high_pass_cutoff);
+ defer_high_pass_cutoff(high_pass_cutoff);
+}
+
+void EaxRingModulatorEffect::defer_waveform(
+ const EaxEaxCall& eax_call)
+{
+ const auto& waveform =
+ eax_call.get_value<
+ EaxRingModulatorEffectException, const decltype(EAXRINGMODULATORPROPERTIES::ulWaveform)>();
+
+ validate_waveform(waveform);
+ defer_waveform(waveform);
+}
+
+void EaxRingModulatorEffect::defer_all(
+ const EaxEaxCall& eax_call)
+{
+ const auto& all =
+ eax_call.get_value<EaxRingModulatorEffectException, const EAXRINGMODULATORPROPERTIES>();
+
+ validate_all(all);
+ defer_all(all);
+}
+
+// [[nodiscard]]
+bool EaxRingModulatorEffect::apply_deferred()
+{
+ if (eax_dirty_flags_ == EaxRingModulatorEffectDirtyFlags{})
+ {
+ return false;
+ }
+
+ eax_ = eax_d_;
+
+ if (eax_dirty_flags_.flFrequency)
+ {
+ set_efx_frequency();
+ }
+
+ if (eax_dirty_flags_.flHighPassCutOff)
+ {
+ set_efx_high_pass_cutoff();
+ }
+
+ if (eax_dirty_flags_.ulWaveform)
+ {
+ set_efx_waveform();
+ }
+
+ eax_dirty_flags_ = EaxRingModulatorEffectDirtyFlags{};
+
+ return true;
+}
+
+// [[nodiscard]]
+bool EaxRingModulatorEffect::set(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXRINGMODULATOR_NONE:
+ break;
+
+ case EAXRINGMODULATOR_ALLPARAMETERS:
+ defer_all(eax_call);
+ break;
+
+ case EAXRINGMODULATOR_FREQUENCY:
+ defer_frequency(eax_call);
+ break;
+
+ case EAXRINGMODULATOR_HIGHPASSCUTOFF:
+ defer_high_pass_cutoff(eax_call);
+ break;
+
+ case EAXRINGMODULATOR_WAVEFORM:
+ defer_waveform(eax_call);
+ break;
+
+ default:
+ throw EaxRingModulatorEffectException{"Unsupported property id."};
+ }
+
+ if (!eax_call.is_deferred())
+ {
+ return apply_deferred();
+ }
+
+ return false;
+}
+
+
+} // namespace
+
+
+EaxEffectUPtr eax_create_eax_ring_modulator_effect(
+ EffectProps& al_effect_props)
+{
+ return std::make_unique<EaxRingModulatorEffect>(al_effect_props);
+}
+
+
+#endif // ALSOFT_EAX
diff --git a/al/effects/null.cpp b/al/effects/null.cpp
index 516446db..44595208 100644
--- a/al/effects/null.cpp
+++ b/al/effects/null.cpp
@@ -7,6 +7,10 @@
#include "alc/effects/base.h"
#include "effects.h"
+#if ALSOFT_EAX
+#include "al/eax_exception.h"
+#endif // ALSOFT_EAX
+
namespace {
@@ -91,3 +95,56 @@ EffectProps genDefaultProps() noexcept
DEFINE_ALEFFECT_VTABLE(Null);
const EffectProps NullEffectProps{genDefaultProps()};
+
+
+#if ALSOFT_EAX
+namespace
+{
+
+
+class EaxNullEffect final :
+ public EaxEffect
+{
+public:
+ // [[nodiscard]]
+ bool dispatch(
+ const EaxEaxCall& eax_call) override;
+}; // EaxNullEffect
+
+
+class EaxNullEffectException :
+ public EaxException
+{
+public:
+ explicit EaxNullEffectException(
+ const char* message)
+ :
+ EaxException{"EAX_NULL_EFFECT", message}
+ {
+ }
+}; // EaxNullEffectException
+
+
+// [[nodiscard]]
+bool EaxNullEffect::dispatch(
+ const EaxEaxCall& eax_call)
+{
+ if (eax_call.get_property_id() != 0)
+ {
+ throw EaxNullEffectException{"Unsupported property id."};
+ }
+
+ return false;
+}
+
+
+} // namespace
+
+
+EaxEffectUPtr eax_create_eax_null_effect()
+{
+ return std::make_unique<EaxNullEffect>();
+}
+
+
+#endif // ALSOFT_EAX
diff --git a/al/effects/pshifter.cpp b/al/effects/pshifter.cpp
index 56059a3c..03f9a139 100644
--- a/al/effects/pshifter.cpp
+++ b/al/effects/pshifter.cpp
@@ -7,6 +7,13 @@
#include "alc/effects/base.h"
#include "effects.h"
+#if ALSOFT_EAX
+#include "alnumeric.h"
+
+#include "al/eax_exception.h"
+#include "al/eax_utils.h"
+#endif // ALSOFT_EAX
+
namespace {
@@ -82,3 +89,334 @@ EffectProps genDefaultProps() noexcept
DEFINE_ALEFFECT_VTABLE(Pshifter);
const EffectProps PshifterEffectProps{genDefaultProps()};
+
+#if ALSOFT_EAX
+namespace
+{
+
+
+using EaxPitchShifterEffectDirtyFlagsValue = std::uint_least8_t;
+
+struct EaxPitchShifterEffectDirtyFlags
+{
+ using EaxIsBitFieldStruct = bool;
+
+ EaxPitchShifterEffectDirtyFlagsValue lCoarseTune : 1;
+ EaxPitchShifterEffectDirtyFlagsValue lFineTune : 1;
+}; // EaxPitchShifterEffectDirtyFlags
+
+
+class EaxPitchShifterEffect final :
+ public EaxEffect
+{
+public:
+ EaxPitchShifterEffect(
+ EffectProps& al_effect_props);
+
+
+ // [[nodiscard]]
+ bool dispatch(
+ const EaxEaxCall& eax_call) override;
+
+
+private:
+ EffectProps& al_effect_props_;
+
+ EAXPITCHSHIFTERPROPERTIES eax_{};
+ EAXPITCHSHIFTERPROPERTIES eax_d_{};
+ EaxPitchShifterEffectDirtyFlags eax_dirty_flags_{};
+
+
+ void set_eax_defaults();
+
+
+ void set_efx_coarse_tune();
+
+ void set_efx_fine_tune();
+
+ void set_efx_defaults();
+
+
+ // [[nodiscard]]
+ bool get(
+ const EaxEaxCall& eax_call);
+
+
+ void validate_coarse_tune(
+ long lCoarseTune);
+
+ void validate_fine_tune(
+ long lFineTune);
+
+ void validate_all(
+ const EAXPITCHSHIFTERPROPERTIES& all);
+
+
+ void defer_coarse_tune(
+ long lCoarseTune);
+
+ void defer_fine_tune(
+ long lFineTune);
+
+ void defer_all(
+ const EAXPITCHSHIFTERPROPERTIES& all);
+
+
+ void defer_coarse_tune(
+ const EaxEaxCall& eax_call);
+
+ void defer_fine_tune(
+ const EaxEaxCall& eax_call);
+
+ void defer_all(
+ const EaxEaxCall& eax_call);
+
+
+ // [[nodiscard]]
+ bool apply_deferred();
+
+ // [[nodiscard]]
+ bool set(
+ const EaxEaxCall& eax_call);
+}; // EaxPitchShifterEffect
+
+
+class EaxPitchShifterEffectException :
+ public EaxException
+{
+public:
+ explicit EaxPitchShifterEffectException(
+ const char* message)
+ :
+ EaxException{"EAX_PITCH_SHIFTER_EFFECT", message}
+ {
+ }
+}; // EaxPitchShifterEffectException
+
+
+EaxPitchShifterEffect::EaxPitchShifterEffect(
+ EffectProps& al_effect_props)
+ :
+ al_effect_props_{al_effect_props}
+{
+ set_eax_defaults();
+ set_efx_defaults();
+}
+
+// [[nodiscard]]
+bool EaxPitchShifterEffect::dispatch(
+ const EaxEaxCall& eax_call)
+{
+ return eax_call.is_get() ? get(eax_call) : set(eax_call);
+}
+
+void EaxPitchShifterEffect::set_eax_defaults()
+{
+ eax_.lCoarseTune = EAXPITCHSHIFTER_DEFAULTCOARSETUNE;
+ eax_.lFineTune = EAXPITCHSHIFTER_DEFAULTFINETUNE;
+
+ eax_d_ = eax_;
+}
+
+void EaxPitchShifterEffect::set_efx_coarse_tune()
+{
+ const auto coarse_tune = clamp(
+ static_cast<ALint>(eax_.lCoarseTune),
+ AL_PITCH_SHIFTER_MIN_COARSE_TUNE,
+ AL_PITCH_SHIFTER_MAX_COARSE_TUNE);
+
+ al_effect_props_.Pshifter.CoarseTune = coarse_tune;
+}
+
+void EaxPitchShifterEffect::set_efx_fine_tune()
+{
+ const auto fine_tune = clamp(
+ static_cast<ALint>(eax_.lFineTune),
+ AL_PITCH_SHIFTER_MIN_FINE_TUNE,
+ AL_PITCH_SHIFTER_MAX_FINE_TUNE);
+
+ al_effect_props_.Pshifter.FineTune = fine_tune;
+}
+
+void EaxPitchShifterEffect::set_efx_defaults()
+{
+ set_efx_coarse_tune();
+ set_efx_fine_tune();
+}
+
+// [[nodiscard]]
+bool EaxPitchShifterEffect::get(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXPITCHSHIFTER_NONE:
+ break;
+
+ case EAXPITCHSHIFTER_ALLPARAMETERS:
+ eax_call.set_value<EaxPitchShifterEffectException>(eax_);
+ break;
+
+ case EAXPITCHSHIFTER_COARSETUNE:
+ eax_call.set_value<EaxPitchShifterEffectException>(eax_.lCoarseTune);
+ break;
+
+ case EAXPITCHSHIFTER_FINETUNE:
+ eax_call.set_value<EaxPitchShifterEffectException>(eax_.lFineTune);
+ break;
+
+ default:
+ throw EaxPitchShifterEffectException{"Unsupported property id."};
+ }
+
+ return false;
+}
+
+void EaxPitchShifterEffect::validate_coarse_tune(
+ long lCoarseTune)
+{
+ eax_validate_range<EaxPitchShifterEffectException>(
+ "Coarse Tune",
+ lCoarseTune,
+ EAXPITCHSHIFTER_MINCOARSETUNE,
+ EAXPITCHSHIFTER_MAXCOARSETUNE);
+}
+
+void EaxPitchShifterEffect::validate_fine_tune(
+ long lFineTune)
+{
+ eax_validate_range<EaxPitchShifterEffectException>(
+ "Fine Tune",
+ lFineTune,
+ EAXPITCHSHIFTER_MINFINETUNE,
+ EAXPITCHSHIFTER_MAXFINETUNE);
+}
+
+void EaxPitchShifterEffect::validate_all(
+ const EAXPITCHSHIFTERPROPERTIES& all)
+{
+ validate_coarse_tune(all.lCoarseTune);
+ validate_fine_tune(all.lFineTune);
+}
+
+void EaxPitchShifterEffect::defer_coarse_tune(
+ long lCoarseTune)
+{
+ eax_d_.lCoarseTune = lCoarseTune;
+ eax_dirty_flags_.lCoarseTune = (eax_.lCoarseTune != eax_d_.lCoarseTune);
+}
+
+void EaxPitchShifterEffect::defer_fine_tune(
+ long lFineTune)
+{
+ eax_d_.lFineTune = lFineTune;
+ eax_dirty_flags_.lFineTune = (eax_.lFineTune != eax_d_.lFineTune);
+}
+
+void EaxPitchShifterEffect::defer_all(
+ const EAXPITCHSHIFTERPROPERTIES& all)
+{
+ defer_coarse_tune(all.lCoarseTune);
+ defer_fine_tune(all.lFineTune);
+}
+
+void EaxPitchShifterEffect::defer_coarse_tune(
+ const EaxEaxCall& eax_call)
+{
+ const auto& coarse_tune =
+ eax_call.get_value<EaxPitchShifterEffectException, const decltype(EAXPITCHSHIFTERPROPERTIES::lCoarseTune)>();
+
+ validate_coarse_tune(coarse_tune);
+ defer_coarse_tune(coarse_tune);
+}
+
+void EaxPitchShifterEffect::defer_fine_tune(
+ const EaxEaxCall& eax_call)
+{
+ const auto& fine_tune =
+ eax_call.get_value<EaxPitchShifterEffectException, const decltype(EAXPITCHSHIFTERPROPERTIES::lFineTune)>();
+
+ validate_fine_tune(fine_tune);
+ defer_fine_tune(fine_tune);
+}
+
+void EaxPitchShifterEffect::defer_all(
+ const EaxEaxCall& eax_call)
+{
+ const auto& all =
+ eax_call.get_value<EaxPitchShifterEffectException, const EAXPITCHSHIFTERPROPERTIES>();
+
+ validate_all(all);
+ defer_all(all);
+}
+
+// [[nodiscard]]
+bool EaxPitchShifterEffect::apply_deferred()
+{
+ if (eax_dirty_flags_ == EaxPitchShifterEffectDirtyFlags{})
+ {
+ return false;
+ }
+
+ eax_ = eax_d_;
+
+ if (eax_dirty_flags_.lCoarseTune)
+ {
+ set_efx_coarse_tune();
+ }
+
+ if (eax_dirty_flags_.lFineTune)
+ {
+ set_efx_fine_tune();
+ }
+
+ eax_dirty_flags_ = EaxPitchShifterEffectDirtyFlags{};
+
+ return true;
+}
+
+// [[nodiscard]]
+bool EaxPitchShifterEffect::set(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXPITCHSHIFTER_NONE:
+ break;
+
+ case EAXPITCHSHIFTER_ALLPARAMETERS:
+ defer_all(eax_call);
+ break;
+
+ case EAXPITCHSHIFTER_COARSETUNE:
+ defer_coarse_tune(eax_call);
+ break;
+
+ case EAXPITCHSHIFTER_FINETUNE:
+ defer_fine_tune(eax_call);
+ break;
+
+ default:
+ throw EaxPitchShifterEffectException{"Unsupported property id."};
+ }
+
+ if (!eax_call.is_deferred())
+ {
+ return apply_deferred();
+ }
+
+ return false;
+}
+
+
+} // namespace
+
+
+EaxEffectUPtr eax_create_eax_pitch_shifter_effect(
+ EffectProps& al_effect_props)
+{
+ return std::make_unique<EaxPitchShifterEffect>(al_effect_props);
+}
+
+
+#endif // ALSOFT_EAX
diff --git a/al/effects/reverb.cpp b/al/effects/reverb.cpp
index 3f234b93..8012450d 100644
--- a/al/effects/reverb.cpp
+++ b/al/effects/reverb.cpp
@@ -9,6 +9,15 @@
#include "alc/effects/base.h"
#include "effects.h"
+#if ALSOFT_EAX
+#include <tuple>
+
+#include "alnumeric.h"
+
+#include "al/eax_exception.h"
+#include "al/eax_utils.h"
+#endif // ALSOFT_EAX
+
namespace {
@@ -554,3 +563,1916 @@ const EffectProps ReverbEffectProps{genDefaultProps()};
DEFINE_ALEFFECT_VTABLE(StdReverb);
const EffectProps StdReverbEffectProps{genDefaultStdProps()};
+
+#if ALSOFT_EAX
+namespace
+{
+
+
+using EaxReverbEffectDirtyFlagsValue = std::uint_least32_t;
+
+struct EaxReverbEffectDirtyFlags
+{
+ using EaxIsBitFieldStruct = bool;
+
+ EaxReverbEffectDirtyFlagsValue ulEnvironment : 1;
+ EaxReverbEffectDirtyFlagsValue flEnvironmentSize : 1;
+ EaxReverbEffectDirtyFlagsValue flEnvironmentDiffusion : 1;
+ EaxReverbEffectDirtyFlagsValue lRoom : 1;
+ EaxReverbEffectDirtyFlagsValue lRoomHF : 1;
+ EaxReverbEffectDirtyFlagsValue lRoomLF : 1;
+ EaxReverbEffectDirtyFlagsValue flDecayTime : 1;
+ EaxReverbEffectDirtyFlagsValue flDecayHFRatio : 1;
+ EaxReverbEffectDirtyFlagsValue flDecayLFRatio : 1;
+ EaxReverbEffectDirtyFlagsValue lReflections : 1;
+ EaxReverbEffectDirtyFlagsValue flReflectionsDelay : 1;
+ EaxReverbEffectDirtyFlagsValue vReflectionsPan : 1;
+ EaxReverbEffectDirtyFlagsValue lReverb : 1;
+ EaxReverbEffectDirtyFlagsValue flReverbDelay : 1;
+ EaxReverbEffectDirtyFlagsValue vReverbPan : 1;
+ EaxReverbEffectDirtyFlagsValue flEchoTime : 1;
+ EaxReverbEffectDirtyFlagsValue flEchoDepth : 1;
+ EaxReverbEffectDirtyFlagsValue flModulationTime : 1;
+ EaxReverbEffectDirtyFlagsValue flModulationDepth : 1;
+ EaxReverbEffectDirtyFlagsValue flAirAbsorptionHF : 1;
+ EaxReverbEffectDirtyFlagsValue flHFReference : 1;
+ EaxReverbEffectDirtyFlagsValue flLFReference : 1;
+ EaxReverbEffectDirtyFlagsValue flRoomRolloffFactor : 1;
+ EaxReverbEffectDirtyFlagsValue ulFlags : 1;
+}; // EaxReverbEffectDirtyFlags
+
+
+class EaxReverbEffect final :
+ public EaxEffect
+{
+public:
+ EaxReverbEffect(
+ EffectProps& al_effect_props);
+
+
+ // [[nodiscard]]
+ bool dispatch(
+ const EaxEaxCall& eax_call) override;
+
+
+private:
+ EffectProps& al_effect_props_;
+
+ EAXREVERBPROPERTIES eax_{};
+ EAXREVERBPROPERTIES eax_d_{};
+ EaxReverbEffectDirtyFlags eax_dirty_flags_{};
+
+
+ void set_eax_defaults();
+
+
+ void set_efx_density();
+
+ void set_efx_diffusion();
+
+ void set_efx_gain();
+
+ void set_efx_gain_hf();
+
+ void set_efx_gain_lf();
+
+ void set_efx_decay_time();
+
+ void set_efx_decay_hf_ratio();
+
+ void set_efx_decay_lf_ratio();
+
+ void set_efx_reflections_gain();
+
+ void set_efx_reflections_delay();
+
+ void set_efx_reflections_pan();
+
+ void set_efx_late_reverb_gain();
+
+ void set_efx_late_reverb_delay();
+
+ void set_efx_late_reverb_pan();
+
+ void set_efx_echo_time();
+
+ void set_efx_echo_depth();
+
+ void set_efx_modulation_time();
+
+ void set_efx_modulation_depth();
+
+ void set_efx_air_absorption_gain_hf();
+
+ void set_efx_hf_reference();
+
+ void set_efx_lf_reference();
+
+ void set_efx_room_rolloff_factor();
+
+ void set_efx_flags();
+
+ void set_efx_defaults();
+
+
+ void get_all(
+ const EaxEaxCall& eax_call) const;
+
+ // [[nodiscard]]
+ bool get(
+ const EaxEaxCall& eax_call) const;
+
+
+ void validate_environment(
+ unsigned long ulEnvironment,
+ int version,
+ bool is_standalone);
+
+ void validate_environment_size(
+ float flEnvironmentSize);
+
+ void validate_environment_diffusion(
+ float flEnvironmentDiffusion);
+
+ void validate_room(
+ long lRoom);
+
+ void validate_room_hf(
+ long lRoomHF);
+
+ void validate_room_lf(
+ long lRoomLF);
+
+ void validate_decay_time(
+ float flDecayTime);
+
+ void validate_decay_hf_ratio(
+ float flDecayHFRatio);
+
+ void validate_decay_lf_ratio(
+ float flDecayLFRatio);
+
+ void validate_reflections(
+ long lReflections);
+
+ void validate_reflections_delay(
+ float flReflectionsDelay);
+
+ void validate_reflections_pan(
+ const EAXVECTOR& vReflectionsPan);
+
+ void validate_reverb(
+ long lReverb);
+
+ void validate_reverb_delay(
+ float flReverbDelay);
+
+ void validate_reverb_pan(
+ const EAXVECTOR& vReverbPan);
+
+ void validate_echo_time(
+ float flEchoTime);
+
+ void validate_echo_depth(
+ float flEchoDepth);
+
+ void validate_modulation_time(
+ float flModulationTime);
+
+ void validate_modulation_depth(
+ float flModulationDepth);
+
+ void validate_air_absorbtion_hf(
+ float air_absorbtion_hf);
+
+ void validate_hf_reference(
+ float flHFReference);
+
+ void validate_lf_reference(
+ float flLFReference);
+
+ void validate_room_rolloff_factor(
+ float flRoomRolloffFactor);
+
+ void validate_flags(
+ unsigned long ulFlags);
+
+ void validate_all(
+ const EAX20LISTENERPROPERTIES& all,
+ int version);
+
+ void validate_all(
+ const EAXREVERBPROPERTIES& all,
+ int version);
+
+
+ void defer_environment(
+ unsigned long ulEnvironment);
+
+ void defer_environment_size(
+ float flEnvironmentSize);
+
+ void defer_environment_diffusion(
+ float flEnvironmentDiffusion);
+
+ void defer_room(
+ long lRoom);
+
+ void defer_room_hf(
+ long lRoomHF);
+
+ void defer_room_lf(
+ long lRoomLF);
+
+ void defer_decay_time(
+ float flDecayTime);
+
+ void defer_decay_hf_ratio(
+ float flDecayHFRatio);
+
+ void defer_decay_lf_ratio(
+ float flDecayLFRatio);
+
+ void defer_reflections(
+ long lReflections);
+
+ void defer_reflections_delay(
+ float flReflectionsDelay);
+
+ void defer_reflections_pan(
+ const EAXVECTOR& vReflectionsPan);
+
+ void defer_reverb(
+ long lReverb);
+
+ void defer_reverb_delay(
+ float flReverbDelay);
+
+ void defer_reverb_pan(
+ const EAXVECTOR& vReverbPan);
+
+ void defer_echo_time(
+ float flEchoTime);
+
+ void defer_echo_depth(
+ float flEchoDepth);
+
+ void defer_modulation_time(
+ float flModulationTime);
+
+ void defer_modulation_depth(
+ float flModulationDepth);
+
+ void defer_air_absorbtion_hf(
+ float flAirAbsorptionHF);
+
+ void defer_hf_reference(
+ float flHFReference);
+
+ void defer_lf_reference(
+ float flLFReference);
+
+ void defer_room_rolloff_factor(
+ float flRoomRolloffFactor);
+
+ void defer_flags(
+ unsigned long ulFlags);
+
+ void defer_all(
+ const EAX20LISTENERPROPERTIES& all);
+
+ void defer_all(
+ const EAXREVERBPROPERTIES& all);
+
+
+ void defer_environment(
+ const EaxEaxCall& eax_call);
+
+ void defer_environment_size(
+ const EaxEaxCall& eax_call);
+
+ void defer_environment_diffusion(
+ const EaxEaxCall& eax_call);
+
+ void defer_room(
+ const EaxEaxCall& eax_call);
+
+ void defer_room_hf(
+ const EaxEaxCall& eax_call);
+
+ void defer_room_lf(
+ const EaxEaxCall& eax_call);
+
+ void defer_decay_time(
+ const EaxEaxCall& eax_call);
+
+ void defer_decay_hf_ratio(
+ const EaxEaxCall& eax_call);
+
+ void defer_decay_lf_ratio(
+ const EaxEaxCall& eax_call);
+
+ void defer_reflections(
+ const EaxEaxCall& eax_call);
+
+ void defer_reflections_delay(
+ const EaxEaxCall& eax_call);
+
+ void defer_reflections_pan(
+ const EaxEaxCall& eax_call);
+
+ void defer_reverb(
+ const EaxEaxCall& eax_call);
+
+ void defer_reverb_delay(
+ const EaxEaxCall& eax_call);
+
+ void defer_reverb_pan(
+ const EaxEaxCall& eax_call);
+
+ void defer_echo_time(
+ const EaxEaxCall& eax_call);
+
+ void defer_echo_depth(
+ const EaxEaxCall& eax_call);
+
+ void defer_modulation_time(
+ const EaxEaxCall& eax_call);
+
+ void defer_modulation_depth(
+ const EaxEaxCall& eax_call);
+
+ void defer_air_absorbtion_hf(
+ const EaxEaxCall& eax_call);
+
+ void defer_hf_reference(
+ const EaxEaxCall& eax_call);
+
+ void defer_lf_reference(
+ const EaxEaxCall& eax_call);
+
+ void defer_room_rolloff_factor(
+ const EaxEaxCall& eax_call);
+
+ void defer_flags(
+ const EaxEaxCall& eax_call);
+
+ void defer_all(
+ const EaxEaxCall& eax_call);
+
+
+ // [[nodiscard]]
+ bool apply_deferred();
+
+ // [[nodiscard]]
+ bool set(
+ const EaxEaxCall& eax_call);
+}; // EaxReverbEffect
+
+
+class EaxReverbEffectException :
+ public EaxException
+{
+public:
+ explicit EaxReverbEffectException(
+ const char* message)
+ :
+ EaxException{"EAX_REVERB_EFFECT", message}
+ {
+ }
+}; // EaxReverbEffectException
+
+
+EaxReverbEffect::EaxReverbEffect(
+ EffectProps& al_effect_props)
+ :
+ al_effect_props_{al_effect_props}
+{
+ set_eax_defaults();
+ set_efx_defaults();
+}
+
+// [[nodiscard]]
+bool EaxReverbEffect::dispatch(
+ const EaxEaxCall& eax_call)
+{
+ return eax_call.is_get() ? get(eax_call) : set(eax_call);
+}
+
+void EaxReverbEffect::set_eax_defaults()
+{
+ eax_ = EAXREVERB_PRESETS[EAX_ENVIRONMENT_GENERIC];
+
+ eax_d_ = eax_;
+}
+
+void EaxReverbEffect::set_efx_density()
+{
+ const auto eax_environment_size = eax_.flEnvironmentSize;
+
+ const auto efx_density = clamp(
+ (eax_environment_size * eax_environment_size * eax_environment_size) / 16.0F,
+ AL_EAXREVERB_MIN_DENSITY,
+ AL_EAXREVERB_MAX_DENSITY);
+
+ al_effect_props_.Reverb.Density = efx_density;
+}
+
+void EaxReverbEffect::set_efx_diffusion()
+{
+ const auto efx_diffusion = clamp(
+ eax_.flEnvironmentDiffusion,
+ AL_EAXREVERB_MIN_DIFFUSION,
+ AL_EAXREVERB_MAX_DIFFUSION);
+
+ al_effect_props_.Reverb.Diffusion = efx_diffusion;
+}
+
+void EaxReverbEffect::set_efx_gain()
+{
+ const auto efx_gain = clamp(
+ level_mb_to_gain(static_cast<float>(eax_.lRoom)),
+ AL_EAXREVERB_MIN_GAIN,
+ AL_EAXREVERB_MAX_GAIN);
+
+ al_effect_props_.Reverb.Gain = efx_gain;
+}
+
+void EaxReverbEffect::set_efx_gain_hf()
+{
+ const auto efx_gain_hf = clamp(
+ level_mb_to_gain(static_cast<float>(eax_.lRoomHF)),
+ AL_EAXREVERB_MIN_GAINHF,
+ AL_EAXREVERB_MAX_GAINHF);
+
+ al_effect_props_.Reverb.GainHF = efx_gain_hf;
+}
+
+void EaxReverbEffect::set_efx_gain_lf()
+{
+ const auto efx_gain_lf = clamp(
+ level_mb_to_gain(static_cast<float>(eax_.lRoomLF)),
+ AL_EAXREVERB_MIN_GAINLF,
+ AL_EAXREVERB_MAX_GAINLF);
+
+ al_effect_props_.Reverb.GainLF = efx_gain_lf;
+}
+
+void EaxReverbEffect::set_efx_decay_time()
+{
+ const auto efx_decay_time = clamp(
+ eax_.flDecayTime,
+ AL_EAXREVERB_MIN_DECAY_TIME,
+ AL_EAXREVERB_MAX_DECAY_TIME);
+
+ al_effect_props_.Reverb.DecayTime = efx_decay_time;
+}
+
+void EaxReverbEffect::set_efx_decay_hf_ratio()
+{
+ const auto efx_decay_hf_ratio = clamp(
+ eax_.flDecayHFRatio,
+ AL_EAXREVERB_MIN_DECAY_HFRATIO,
+ AL_EAXREVERB_MAX_DECAY_HFRATIO);
+
+ al_effect_props_.Reverb.DecayHFRatio = efx_decay_hf_ratio;
+}
+
+void EaxReverbEffect::set_efx_decay_lf_ratio()
+{
+ const auto efx_decay_lf_ratio = clamp(
+ eax_.flDecayLFRatio,
+ AL_EAXREVERB_MIN_DECAY_LFRATIO,
+ AL_EAXREVERB_MAX_DECAY_LFRATIO);
+
+ al_effect_props_.Reverb.DecayLFRatio = efx_decay_lf_ratio;
+}
+
+void EaxReverbEffect::set_efx_reflections_gain()
+{
+ const auto efx_reflections_gain = clamp(
+ level_mb_to_gain(static_cast<float>(eax_.lReflections)),
+ AL_EAXREVERB_MIN_REFLECTIONS_GAIN,
+ AL_EAXREVERB_MAX_REFLECTIONS_GAIN);
+
+ al_effect_props_.Reverb.ReflectionsGain = efx_reflections_gain;
+}
+
+void EaxReverbEffect::set_efx_reflections_delay()
+{
+ const auto efx_reflections_delay = clamp(
+ eax_.flReflectionsDelay,
+ AL_EAXREVERB_MIN_REFLECTIONS_DELAY,
+ AL_EAXREVERB_MAX_REFLECTIONS_DELAY);
+
+ al_effect_props_.Reverb.ReflectionsDelay = efx_reflections_delay;
+}
+
+void EaxReverbEffect::set_efx_reflections_pan()
+{
+ al_effect_props_.Reverb.ReflectionsPan[0] = eax_.vReflectionsPan.x;
+ al_effect_props_.Reverb.ReflectionsPan[1] = eax_.vReflectionsPan.y;
+ al_effect_props_.Reverb.ReflectionsPan[2] = eax_.vReflectionsPan.z;
+}
+
+void EaxReverbEffect::set_efx_late_reverb_gain()
+{
+ const auto efx_late_reverb_gain = clamp(
+ level_mb_to_gain(static_cast<float>(eax_.lReverb)),
+ AL_EAXREVERB_MIN_LATE_REVERB_GAIN,
+ AL_EAXREVERB_MAX_LATE_REVERB_GAIN);
+
+ al_effect_props_.Reverb.LateReverbGain = efx_late_reverb_gain;
+}
+
+void EaxReverbEffect::set_efx_late_reverb_delay()
+{
+ const auto efx_late_reverb_delay = clamp(
+ eax_.flReverbDelay,
+ AL_EAXREVERB_MIN_LATE_REVERB_DELAY,
+ AL_EAXREVERB_MAX_LATE_REVERB_DELAY);
+
+ al_effect_props_.Reverb.LateReverbDelay = efx_late_reverb_delay;
+}
+
+void EaxReverbEffect::set_efx_late_reverb_pan()
+{
+ al_effect_props_.Reverb.LateReverbPan[0] = eax_.vReverbPan.x;
+ al_effect_props_.Reverb.LateReverbPan[1] = eax_.vReverbPan.y;
+ al_effect_props_.Reverb.LateReverbPan[2] = eax_.vReverbPan.z;
+}
+
+void EaxReverbEffect::set_efx_echo_time()
+{
+ const auto efx_echo_time = clamp(
+ eax_.flEchoTime,
+ AL_EAXREVERB_MIN_ECHO_TIME,
+ AL_EAXREVERB_MAX_ECHO_TIME);
+
+ al_effect_props_.Reverb.EchoTime = efx_echo_time;
+}
+
+void EaxReverbEffect::set_efx_echo_depth()
+{
+ const auto efx_echo_depth = clamp(
+ eax_.flEchoDepth,
+ AL_EAXREVERB_MIN_ECHO_DEPTH,
+ AL_EAXREVERB_MAX_ECHO_DEPTH);
+
+ al_effect_props_.Reverb.EchoDepth = efx_echo_depth;
+}
+
+void EaxReverbEffect::set_efx_modulation_time()
+{
+ const auto efx_modulation_time = clamp(
+ eax_.flModulationTime,
+ AL_EAXREVERB_MIN_MODULATION_TIME,
+ AL_EAXREVERB_MAX_MODULATION_TIME);
+
+ al_effect_props_.Reverb.ModulationTime = efx_modulation_time;
+}
+
+void EaxReverbEffect::set_efx_modulation_depth()
+{
+ const auto efx_modulation_depth = clamp(
+ eax_.flModulationDepth,
+ AL_EAXREVERB_MIN_MODULATION_DEPTH,
+ AL_EAXREVERB_MAX_MODULATION_DEPTH);
+
+ al_effect_props_.Reverb.ModulationDepth = efx_modulation_depth;
+}
+
+void EaxReverbEffect::set_efx_air_absorption_gain_hf()
+{
+ const auto efx_air_absorption_hf = clamp(
+ level_mb_to_gain(eax_.flAirAbsorptionHF),
+ AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF,
+ AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF);
+
+ al_effect_props_.Reverb.AirAbsorptionGainHF = efx_air_absorption_hf;
+}
+
+void EaxReverbEffect::set_efx_hf_reference()
+{
+ const auto efx_hf_reference = clamp(
+ eax_.flHFReference,
+ AL_EAXREVERB_MIN_HFREFERENCE,
+ AL_EAXREVERB_MAX_HFREFERENCE);
+
+ al_effect_props_.Reverb.HFReference = efx_hf_reference;
+}
+
+void EaxReverbEffect::set_efx_lf_reference()
+{
+ const auto efx_lf_reference = clamp(
+ eax_.flLFReference,
+ AL_EAXREVERB_MIN_LFREFERENCE,
+ AL_EAXREVERB_MAX_LFREFERENCE);
+
+ al_effect_props_.Reverb.LFReference = efx_lf_reference;
+}
+
+void EaxReverbEffect::set_efx_room_rolloff_factor()
+{
+ const auto efx_room_rolloff_factor = clamp(
+ eax_.flRoomRolloffFactor,
+ AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR,
+ AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR);
+
+ al_effect_props_.Reverb.RoomRolloffFactor = efx_room_rolloff_factor;
+}
+
+void EaxReverbEffect::set_efx_flags()
+{
+ al_effect_props_.Reverb.DecayHFLimit = ((eax_.ulFlags & EAXREVERBFLAGS_DECAYHFLIMIT) != 0);
+}
+
+void EaxReverbEffect::set_efx_defaults()
+{
+ set_efx_density();
+ set_efx_diffusion();
+ set_efx_gain();
+ set_efx_gain_hf();
+ set_efx_gain_lf();
+ set_efx_decay_time();
+ set_efx_decay_hf_ratio();
+ set_efx_decay_lf_ratio();
+ set_efx_reflections_gain();
+ set_efx_reflections_delay();
+ set_efx_reflections_pan();
+ set_efx_late_reverb_gain();
+ set_efx_late_reverb_delay();
+ set_efx_late_reverb_pan();
+ set_efx_echo_time();
+ set_efx_echo_depth();
+ set_efx_modulation_time();
+ set_efx_modulation_depth();
+ set_efx_air_absorption_gain_hf();
+ set_efx_hf_reference();
+ set_efx_lf_reference();
+ set_efx_room_rolloff_factor();
+ set_efx_flags();
+}
+
+void EaxReverbEffect::get_all(
+ const EaxEaxCall& eax_call) const
+{
+ if (eax_call.get_version() == 2)
+ {
+ auto& eax_reverb = eax_call.get_value<EaxReverbEffectException, EAX20LISTENERPROPERTIES>();
+ eax_reverb.lRoom = eax_.lRoom;
+ eax_reverb.lRoomHF = eax_.lRoomHF;
+ eax_reverb.flRoomRolloffFactor = eax_.flRoomRolloffFactor;
+ eax_reverb.flDecayTime = eax_.flDecayTime;
+ eax_reverb.flDecayHFRatio = eax_.flDecayHFRatio;
+ eax_reverb.lReflections = eax_.lReflections;
+ eax_reverb.flReflectionsDelay = eax_.flReflectionsDelay;
+ eax_reverb.lReverb = eax_.lReverb;
+ eax_reverb.flReverbDelay = eax_.flReverbDelay;
+ eax_reverb.dwEnvironment = eax_.ulEnvironment;
+ eax_reverb.flEnvironmentSize = eax_.flEnvironmentSize;
+ eax_reverb.flEnvironmentDiffusion = eax_.flEnvironmentDiffusion;
+ eax_reverb.flAirAbsorptionHF = eax_.flAirAbsorptionHF;
+ eax_reverb.dwFlags = eax_.ulFlags;
+ }
+ else
+ {
+ eax_call.set_value<EaxReverbEffectException>(eax_);
+ }
+}
+
+// [[nodiscard]]
+bool EaxReverbEffect::get(
+ const EaxEaxCall& eax_call) const
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXREVERB_NONE:
+ break;
+
+ case EAXREVERB_ALLPARAMETERS:
+ get_all(eax_call);
+ break;
+
+ case EAXREVERB_ENVIRONMENT:
+ eax_call.set_value<EaxReverbEffectException>(eax_.ulEnvironment);
+ break;
+
+ case EAXREVERB_ENVIRONMENTSIZE:
+ eax_call.set_value<EaxReverbEffectException>(eax_.flEnvironmentSize);
+ break;
+
+ case EAXREVERB_ENVIRONMENTDIFFUSION:
+ eax_call.set_value<EaxReverbEffectException>(eax_.flEnvironmentDiffusion);
+ break;
+
+ case EAXREVERB_ROOM:
+ eax_call.set_value<EaxReverbEffectException>(eax_.lRoom);
+ break;
+
+ case EAXREVERB_ROOMHF:
+ eax_call.set_value<EaxReverbEffectException>(eax_.lRoomHF);
+ break;
+
+ case EAXREVERB_ROOMLF:
+ eax_call.set_value<EaxReverbEffectException>(eax_.lRoomLF);
+ break;
+
+ case EAXREVERB_DECAYTIME:
+ eax_call.set_value<EaxReverbEffectException>(eax_.flDecayTime);
+ break;
+
+ case EAXREVERB_DECAYHFRATIO:
+ eax_call.set_value<EaxReverbEffectException>(eax_.flDecayHFRatio);
+ break;
+
+ case EAXREVERB_DECAYLFRATIO:
+ eax_call.set_value<EaxReverbEffectException>(eax_.flDecayLFRatio);
+ break;
+
+ case EAXREVERB_REFLECTIONS:
+ eax_call.set_value<EaxReverbEffectException>(eax_.lReflections);
+ break;
+
+ case EAXREVERB_REFLECTIONSDELAY:
+ eax_call.set_value<EaxReverbEffectException>(eax_.flReflectionsDelay);
+ break;
+
+ case EAXREVERB_REFLECTIONSPAN:
+ eax_call.set_value<EaxReverbEffectException>(eax_.vReflectionsPan);
+ break;
+
+ case EAXREVERB_REVERB:
+ eax_call.set_value<EaxReverbEffectException>(eax_.lReverb);
+ break;
+
+ case EAXREVERB_REVERBDELAY:
+ eax_call.set_value<EaxReverbEffectException>(eax_.flReverbDelay);
+ break;
+
+ case EAXREVERB_REVERBPAN:
+ eax_call.set_value<EaxReverbEffectException>(eax_.vReverbPan);
+ break;
+
+ case EAXREVERB_ECHOTIME:
+ eax_call.set_value<EaxReverbEffectException>(eax_.flEchoTime);
+ break;
+
+ case EAXREVERB_ECHODEPTH:
+ eax_call.set_value<EaxReverbEffectException>(eax_.flEchoDepth);
+ break;
+
+ case EAXREVERB_MODULATIONTIME:
+ eax_call.set_value<EaxReverbEffectException>(eax_.flModulationTime);
+ break;
+
+ case EAXREVERB_MODULATIONDEPTH:
+ eax_call.set_value<EaxReverbEffectException>(eax_.flModulationDepth);
+ break;
+
+ case EAXREVERB_AIRABSORPTIONHF:
+ eax_call.set_value<EaxReverbEffectException>(eax_.flAirAbsorptionHF);
+ break;
+
+ case EAXREVERB_HFREFERENCE:
+ eax_call.set_value<EaxReverbEffectException>(eax_.flHFReference);
+ break;
+
+ case EAXREVERB_LFREFERENCE:
+ eax_call.set_value<EaxReverbEffectException>(eax_.flLFReference);
+ break;
+
+ case EAXREVERB_ROOMROLLOFFFACTOR:
+ eax_call.set_value<EaxReverbEffectException>(eax_.flRoomRolloffFactor);
+ break;
+
+ case EAXREVERB_FLAGS:
+ eax_call.set_value<EaxReverbEffectException>(eax_.ulFlags);
+ break;
+
+ default:
+ throw EaxReverbEffectException{"Unsupported property id."};
+ }
+
+ return false;
+}
+
+void EaxReverbEffect::validate_environment(
+ unsigned long ulEnvironment,
+ int version,
+ bool is_standalone)
+{
+ eax_validate_range<EaxReverbEffectException>(
+ "Environment",
+ ulEnvironment,
+ EAXREVERB_MINENVIRONMENT,
+ (version == 2 || is_standalone) ? EAX20REVERB_MAXENVIRONMENT : EAX30REVERB_MAXENVIRONMENT);
+}
+
+void EaxReverbEffect::validate_environment_size(
+ float flEnvironmentSize)
+{
+ eax_validate_range<EaxReverbEffectException>(
+ "Environment Size",
+ flEnvironmentSize,
+ EAXREVERB_MINENVIRONMENTSIZE,
+ EAXREVERB_MAXENVIRONMENTSIZE);
+}
+
+void EaxReverbEffect::validate_environment_diffusion(
+ float flEnvironmentDiffusion)
+{
+ eax_validate_range<EaxReverbEffectException>(
+ "Environment Diffusion",
+ flEnvironmentDiffusion,
+ EAXREVERB_MINENVIRONMENTDIFFUSION,
+ EAXREVERB_MAXENVIRONMENTDIFFUSION);
+}
+
+void EaxReverbEffect::validate_room(
+ long lRoom)
+{
+ eax_validate_range<EaxReverbEffectException>(
+ "Room",
+ lRoom,
+ EAXREVERB_MINROOM,
+ EAXREVERB_MAXROOM);
+}
+
+void EaxReverbEffect::validate_room_hf(
+ long lRoomHF)
+{
+ eax_validate_range<EaxReverbEffectException>(
+ "Room HF",
+ lRoomHF,
+ EAXREVERB_MINROOMHF,
+ EAXREVERB_MAXROOMHF);
+}
+
+void EaxReverbEffect::validate_room_lf(
+ long lRoomLF)
+{
+ eax_validate_range<EaxReverbEffectException>(
+ "Room LF",
+ lRoomLF,
+ EAXREVERB_MINROOMLF,
+ EAXREVERB_MAXROOMLF);
+}
+
+void EaxReverbEffect::validate_decay_time(
+ float flDecayTime)
+{
+ eax_validate_range<EaxReverbEffectException>(
+ "Decay Time",
+ flDecayTime,
+ EAXREVERB_MINDECAYTIME,
+ EAXREVERB_MAXDECAYTIME);
+}
+
+void EaxReverbEffect::validate_decay_hf_ratio(
+ float flDecayHFRatio)
+{
+ eax_validate_range<EaxReverbEffectException>(
+ "Decay HF Ratio",
+ flDecayHFRatio,
+ EAXREVERB_MINDECAYHFRATIO,
+ EAXREVERB_MAXDECAYHFRATIO);
+}
+
+void EaxReverbEffect::validate_decay_lf_ratio(
+ float flDecayLFRatio)
+{
+ eax_validate_range<EaxReverbEffectException>(
+ "Decay LF Ratio",
+ flDecayLFRatio,
+ EAXREVERB_MINDECAYLFRATIO,
+ EAXREVERB_MAXDECAYLFRATIO);
+}
+
+void EaxReverbEffect::validate_reflections(
+ long lReflections)
+{
+ eax_validate_range<EaxReverbEffectException>(
+ "Reflections",
+ lReflections,
+ EAXREVERB_MINREFLECTIONS,
+ EAXREVERB_MAXREFLECTIONS);
+}
+
+void EaxReverbEffect::validate_reflections_delay(
+ float flReflectionsDelay)
+{
+ eax_validate_range<EaxReverbEffectException>(
+ "Reflections Delay",
+ flReflectionsDelay,
+ EAXREVERB_MINREFLECTIONSDELAY,
+ EAXREVERB_MAXREFLECTIONSDELAY);
+}
+
+void EaxReverbEffect::validate_reflections_pan(
+ const EAXVECTOR& vReflectionsPan)
+{
+ std::ignore = vReflectionsPan;
+}
+
+void EaxReverbEffect::validate_reverb(
+ long lReverb)
+{
+ eax_validate_range<EaxReverbEffectException>(
+ "Reverb",
+ lReverb,
+ EAXREVERB_MINREVERB,
+ EAXREVERB_MAXREVERB);
+}
+
+void EaxReverbEffect::validate_reverb_delay(
+ float flReverbDelay)
+{
+ eax_validate_range<EaxReverbEffectException>(
+ "Reverb Delay",
+ flReverbDelay,
+ EAXREVERB_MINREVERBDELAY,
+ EAXREVERB_MAXREVERBDELAY);
+}
+
+void EaxReverbEffect::validate_reverb_pan(
+ const EAXVECTOR& vReverbPan)
+{
+ std::ignore = vReverbPan;
+}
+
+void EaxReverbEffect::validate_echo_time(
+ float flEchoTime)
+{
+ eax_validate_range<EaxReverbEffectException>(
+ "Echo Time",
+ flEchoTime,
+ EAXREVERB_MINECHOTIME,
+ EAXREVERB_MAXECHOTIME);
+}
+
+void EaxReverbEffect::validate_echo_depth(
+ float flEchoDepth)
+{
+ eax_validate_range<EaxReverbEffectException>(
+ "Echo Depth",
+ flEchoDepth,
+ EAXREVERB_MINECHODEPTH,
+ EAXREVERB_MAXECHODEPTH);
+}
+
+void EaxReverbEffect::validate_modulation_time(
+ float flModulationTime)
+{
+ eax_validate_range<EaxReverbEffectException>(
+ "Modulation Time",
+ flModulationTime,
+ EAXREVERB_MINMODULATIONTIME,
+ EAXREVERB_MAXMODULATIONTIME);
+}
+
+void EaxReverbEffect::validate_modulation_depth(
+ float flModulationDepth)
+{
+ eax_validate_range<EaxReverbEffectException>(
+ "Modulation Depth",
+ flModulationDepth,
+ EAXREVERB_MINMODULATIONDEPTH,
+ EAXREVERB_MAXMODULATIONDEPTH);
+}
+
+void EaxReverbEffect::validate_air_absorbtion_hf(
+ float air_absorbtion_hf)
+{
+ eax_validate_range<EaxReverbEffectException>(
+ "Air Absorbtion HF",
+ air_absorbtion_hf,
+ EAXREVERB_MINAIRABSORPTIONHF,
+ EAXREVERB_MAXAIRABSORPTIONHF);
+}
+
+void EaxReverbEffect::validate_hf_reference(
+ float flHFReference)
+{
+ eax_validate_range<EaxReverbEffectException>(
+ "HF Reference",
+ flHFReference,
+ EAXREVERB_MINHFREFERENCE,
+ EAXREVERB_MAXHFREFERENCE);
+}
+
+void EaxReverbEffect::validate_lf_reference(
+ float flLFReference)
+{
+ eax_validate_range<EaxReverbEffectException>(
+ "LF Reference",
+ flLFReference,
+ EAXREVERB_MINLFREFERENCE,
+ EAXREVERB_MAXLFREFERENCE);
+}
+
+void EaxReverbEffect::validate_room_rolloff_factor(
+ float flRoomRolloffFactor)
+{
+ eax_validate_range<EaxReverbEffectException>(
+ "Room Rolloff Factor",
+ flRoomRolloffFactor,
+ EAXREVERB_MINROOMROLLOFFFACTOR,
+ EAXREVERB_MAXROOMROLLOFFFACTOR);
+}
+
+void EaxReverbEffect::validate_flags(
+ unsigned long ulFlags)
+{
+ eax_validate_range<EaxReverbEffectException>(
+ "Flags",
+ ulFlags,
+ 0UL,
+ ~EAXREVERBFLAGS_RESERVED);
+}
+
+void EaxReverbEffect::validate_all(
+ const EAX20LISTENERPROPERTIES& listener,
+ int version)
+{
+ validate_room(listener.lRoom);
+ validate_room_hf(listener.lRoomHF);
+ validate_room_rolloff_factor(listener.flRoomRolloffFactor);
+ validate_decay_time(listener.flDecayTime);
+ validate_decay_hf_ratio(listener.flDecayHFRatio);
+ validate_reflections(listener.lReflections);
+ validate_reflections_delay(listener.flReflectionsDelay);
+ validate_reverb(listener.lReverb);
+ validate_reverb_delay(listener.flReverbDelay);
+ validate_environment(listener.dwEnvironment, version, false);
+ validate_environment_size(listener.flEnvironmentSize);
+ validate_environment_diffusion(listener.flEnvironmentDiffusion);
+ validate_air_absorbtion_hf(listener.flAirAbsorptionHF);
+ validate_flags(listener.dwFlags);
+}
+
+void EaxReverbEffect::validate_all(
+ const EAXREVERBPROPERTIES& lReverb,
+ int version)
+{
+ validate_environment(lReverb.ulEnvironment, version, false);
+ validate_environment_size(lReverb.flEnvironmentSize);
+ validate_environment_diffusion(lReverb.flEnvironmentDiffusion);
+ validate_room(lReverb.lRoom);
+ validate_room_hf(lReverb.lRoomHF);
+ validate_room_lf(lReverb.lRoomLF);
+ validate_decay_time(lReverb.flDecayTime);
+ validate_decay_hf_ratio(lReverb.flDecayHFRatio);
+ validate_decay_lf_ratio(lReverb.flDecayLFRatio);
+ validate_reflections(lReverb.lReflections);
+ validate_reflections_delay(lReverb.flReflectionsDelay);
+ validate_reverb(lReverb.lReverb);
+ validate_reverb_delay(lReverb.flReverbDelay);
+ validate_echo_time(lReverb.flEchoTime);
+ validate_echo_depth(lReverb.flEchoDepth);
+ validate_modulation_time(lReverb.flModulationTime);
+ validate_modulation_depth(lReverb.flModulationDepth);
+ validate_air_absorbtion_hf(lReverb.flAirAbsorptionHF);
+ validate_hf_reference(lReverb.flHFReference);
+ validate_lf_reference(lReverb.flLFReference);
+ validate_room_rolloff_factor(lReverb.flRoomRolloffFactor);
+ validate_flags(lReverb.ulFlags);
+}
+
+void EaxReverbEffect::defer_environment(
+ unsigned long ulEnvironment)
+{
+ eax_d_.ulEnvironment = ulEnvironment;
+ eax_dirty_flags_.ulEnvironment = (eax_.ulEnvironment != eax_d_.ulEnvironment);
+}
+
+void EaxReverbEffect::defer_environment_size(
+ float flEnvironmentSize)
+{
+ eax_d_.flEnvironmentSize = flEnvironmentSize;
+ eax_dirty_flags_.flEnvironmentSize = (eax_.flEnvironmentSize != eax_d_.flEnvironmentSize);
+}
+
+void EaxReverbEffect::defer_environment_diffusion(
+ float flEnvironmentDiffusion)
+{
+ eax_d_.flEnvironmentDiffusion = flEnvironmentDiffusion;
+ eax_dirty_flags_.flEnvironmentDiffusion = (eax_.flEnvironmentDiffusion != eax_d_.flEnvironmentDiffusion);
+}
+
+void EaxReverbEffect::defer_room(
+ long lRoom)
+{
+ eax_d_.lRoom = lRoom;
+ eax_dirty_flags_.lRoom = (eax_.lRoom != eax_d_.lRoom);
+}
+
+void EaxReverbEffect::defer_room_hf(
+ long lRoomHF)
+{
+ eax_d_.lRoomHF = lRoomHF;
+ eax_dirty_flags_.lRoomHF = (eax_.lRoomHF != eax_d_.lRoomHF);
+}
+
+void EaxReverbEffect::defer_room_lf(
+ long lRoomLF)
+{
+ eax_d_.lRoomLF = lRoomLF;
+ eax_dirty_flags_.lRoomLF = (eax_.lRoomLF != eax_d_.lRoomLF);
+}
+
+void EaxReverbEffect::defer_decay_time(
+ float flDecayTime)
+{
+ eax_d_.flDecayTime = flDecayTime;
+ eax_dirty_flags_.flDecayTime = (eax_.flDecayTime != eax_d_.flDecayTime);
+}
+
+void EaxReverbEffect::defer_decay_hf_ratio(
+ float flDecayHFRatio)
+{
+ eax_d_.flDecayHFRatio = flDecayHFRatio;
+ eax_dirty_flags_.flDecayHFRatio = (eax_.flDecayHFRatio != eax_d_.flDecayHFRatio);
+}
+
+void EaxReverbEffect::defer_decay_lf_ratio(
+ float flDecayLFRatio)
+{
+ eax_d_.flDecayLFRatio = flDecayLFRatio;
+ eax_dirty_flags_.flDecayLFRatio = (eax_.flDecayLFRatio != eax_d_.flDecayLFRatio);
+}
+
+void EaxReverbEffect::defer_reflections(
+ long lReflections)
+{
+ eax_d_.lReflections = lReflections;
+ eax_dirty_flags_.lReflections = (eax_.lReflections != eax_d_.lReflections);
+}
+
+void EaxReverbEffect::defer_reflections_delay(
+ float flReflectionsDelay)
+{
+ eax_d_.flReflectionsDelay = flReflectionsDelay;
+ eax_dirty_flags_.flReflectionsDelay = (eax_.flReflectionsDelay != eax_d_.flReflectionsDelay);
+}
+
+void EaxReverbEffect::defer_reflections_pan(
+ const EAXVECTOR& vReflectionsPan)
+{
+ eax_d_.vReflectionsPan = vReflectionsPan;
+ eax_dirty_flags_.vReflectionsPan = (eax_.vReflectionsPan != eax_d_.vReflectionsPan);
+}
+
+void EaxReverbEffect::defer_reverb(
+ long lReverb)
+{
+ eax_d_.lReverb = lReverb;
+ eax_dirty_flags_.lReverb = (eax_.lReverb != eax_d_.lReverb);
+}
+
+void EaxReverbEffect::defer_reverb_delay(
+ float flReverbDelay)
+{
+ eax_d_.flReverbDelay = flReverbDelay;
+ eax_dirty_flags_.flReverbDelay = (eax_.flReverbDelay != eax_d_.flReverbDelay);
+}
+
+void EaxReverbEffect::defer_reverb_pan(
+ const EAXVECTOR& vReverbPan)
+{
+ eax_d_.vReverbPan = vReverbPan;
+ eax_dirty_flags_.vReverbPan = (eax_.vReverbPan != eax_d_.vReverbPan);
+}
+
+void EaxReverbEffect::defer_echo_time(
+ float flEchoTime)
+{
+ eax_d_.flEchoTime = flEchoTime;
+ eax_dirty_flags_.flEchoTime = (eax_.flEchoTime != eax_d_.flEchoTime);
+}
+
+void EaxReverbEffect::defer_echo_depth(
+ float flEchoDepth)
+{
+ eax_d_.flEchoDepth = flEchoDepth;
+ eax_dirty_flags_.flEchoDepth = (eax_.flEchoDepth != eax_d_.flEchoDepth);
+}
+
+void EaxReverbEffect::defer_modulation_time(
+ float flModulationTime)
+{
+ eax_d_.flModulationTime = flModulationTime;
+ eax_dirty_flags_.flModulationTime = (eax_.flModulationTime != eax_d_.flModulationTime);
+}
+
+void EaxReverbEffect::defer_modulation_depth(
+ float flModulationDepth)
+{
+ eax_d_.flModulationDepth = flModulationDepth;
+ eax_dirty_flags_.flModulationDepth = (eax_.flModulationDepth != eax_d_.flModulationDepth);
+}
+
+void EaxReverbEffect::defer_air_absorbtion_hf(
+ float flAirAbsorptionHF)
+{
+ eax_d_.flAirAbsorptionHF = flAirAbsorptionHF;
+ eax_dirty_flags_.flAirAbsorptionHF = (eax_.flAirAbsorptionHF != eax_d_.flAirAbsorptionHF);
+}
+
+void EaxReverbEffect::defer_hf_reference(
+ float flHFReference)
+{
+ eax_d_.flHFReference = flHFReference;
+ eax_dirty_flags_.flHFReference = (eax_.flHFReference != eax_d_.flHFReference);
+}
+
+void EaxReverbEffect::defer_lf_reference(
+ float flLFReference)
+{
+ eax_d_.flLFReference = flLFReference;
+ eax_dirty_flags_.flLFReference = (eax_.flLFReference != eax_d_.flLFReference);
+}
+
+void EaxReverbEffect::defer_room_rolloff_factor(
+ float flRoomRolloffFactor)
+{
+ eax_d_.flRoomRolloffFactor = flRoomRolloffFactor;
+ eax_dirty_flags_.flRoomRolloffFactor = (eax_.flRoomRolloffFactor != eax_d_.flRoomRolloffFactor);
+}
+
+void EaxReverbEffect::defer_flags(
+ unsigned long ulFlags)
+{
+ eax_d_.ulFlags = ulFlags;
+ eax_dirty_flags_.ulFlags = (eax_.ulFlags != eax_d_.ulFlags);
+}
+
+void EaxReverbEffect::defer_all(
+ const EAX20LISTENERPROPERTIES& listener)
+{
+ defer_room(listener.lRoom);
+ defer_room_hf(listener.lRoomHF);
+ defer_room_rolloff_factor(listener.flRoomRolloffFactor);
+ defer_decay_time(listener.flDecayTime);
+ defer_decay_hf_ratio(listener.flDecayHFRatio);
+ defer_reflections(listener.lReflections);
+ defer_reflections_delay(listener.flReflectionsDelay);
+ defer_reverb(listener.lReverb);
+ defer_reverb_delay(listener.flReverbDelay);
+ defer_environment(listener.dwEnvironment);
+ defer_environment_size(listener.flEnvironmentSize);
+ defer_environment_diffusion(listener.flEnvironmentDiffusion);
+ defer_air_absorbtion_hf(listener.flAirAbsorptionHF);
+ defer_flags(listener.dwFlags);
+}
+
+void EaxReverbEffect::defer_all(
+ const EAXREVERBPROPERTIES& lReverb)
+{
+ defer_environment(lReverb.ulEnvironment);
+ defer_environment_size(lReverb.flEnvironmentSize);
+ defer_environment_diffusion(lReverb.flEnvironmentDiffusion);
+ defer_room(lReverb.lRoom);
+ defer_room_hf(lReverb.lRoomHF);
+ defer_room_lf(lReverb.lRoomLF);
+ defer_decay_time(lReverb.flDecayTime);
+ defer_decay_hf_ratio(lReverb.flDecayHFRatio);
+ defer_decay_lf_ratio(lReverb.flDecayLFRatio);
+ defer_reflections(lReverb.lReflections);
+ defer_reflections_delay(lReverb.flReflectionsDelay);
+ defer_reflections_pan(lReverb.vReflectionsPan);
+ defer_reverb(lReverb.lReverb);
+ defer_reverb_delay(lReverb.flReverbDelay);
+ defer_reverb_pan(lReverb.vReverbPan);
+ defer_echo_time(lReverb.flEchoTime);
+ defer_echo_depth(lReverb.flEchoDepth);
+ defer_modulation_time(lReverb.flModulationTime);
+ defer_modulation_depth(lReverb.flModulationDepth);
+ defer_air_absorbtion_hf(lReverb.flAirAbsorptionHF);
+ defer_hf_reference(lReverb.flHFReference);
+ defer_lf_reference(lReverb.flLFReference);
+ defer_room_rolloff_factor(lReverb.flRoomRolloffFactor);
+ defer_flags(lReverb.ulFlags);
+}
+
+void EaxReverbEffect::defer_environment(
+ const EaxEaxCall& eax_call)
+{
+ const auto& ulEnvironment =
+ eax_call.get_value<EaxReverbEffectException, const decltype(EAXREVERBPROPERTIES::ulEnvironment)>();
+
+ validate_environment(ulEnvironment, eax_call.get_version(), true);
+
+ if (eax_d_.ulEnvironment == ulEnvironment)
+ {
+ return;
+ }
+
+ const auto& reverb_preset = EAXREVERB_PRESETS[ulEnvironment];
+
+ defer_all(reverb_preset);
+}
+
+void EaxReverbEffect::defer_environment_size(
+ const EaxEaxCall& eax_call)
+{
+ const auto& flEnvironmentSize =
+ eax_call.get_value<EaxReverbEffectException, const decltype(EAXREVERBPROPERTIES::flEnvironmentSize)>();
+
+ validate_environment_size(flEnvironmentSize);
+
+ if (eax_d_.flEnvironmentSize == flEnvironmentSize)
+ {
+ return;
+ }
+
+ const auto scale = flEnvironmentSize / eax_d_.flEnvironmentSize;
+
+ defer_environment_size(flEnvironmentSize);
+
+ if ((eax_d_.ulFlags & EAXREVERBFLAGS_DECAYTIMESCALE) != 0)
+ {
+ const auto flDecayTime = clamp(
+ scale * eax_d_.flDecayTime,
+ EAXREVERB_MINDECAYTIME,
+ EAXREVERB_MAXDECAYTIME);
+
+ defer_decay_time(flDecayTime);
+ }
+
+ if ((eax_d_.ulFlags & EAXREVERBFLAGS_REFLECTIONSSCALE) != 0)
+ {
+ if ((eax_d_.ulFlags & EAXREVERBFLAGS_REFLECTIONSDELAYSCALE) != 0)
+ {
+ const auto lReflections = clamp(
+ eax_d_.lReflections - static_cast<long>(gain_to_level_mb(scale)),
+ EAXREVERB_MINREFLECTIONS,
+ EAXREVERB_MAXREFLECTIONS);
+
+ defer_reflections(lReflections);
+ }
+ }
+
+ if ((eax_d_.ulFlags & EAXREVERBFLAGS_REFLECTIONSDELAYSCALE) != 0)
+ {
+ const auto flReflectionsDelay = clamp(
+ eax_d_.flReflectionsDelay * scale,
+ EAXREVERB_MINREFLECTIONSDELAY,
+ EAXREVERB_MAXREFLECTIONSDELAY);
+
+ defer_reflections_delay(flReflectionsDelay);
+ }
+
+ if ((eax_d_.ulFlags & EAXREVERBFLAGS_REVERBSCALE) != 0)
+ {
+ const auto log_scalar = ((eax_d_.ulFlags & EAXREVERBFLAGS_DECAYTIMESCALE) != 0) ? 2'000.0F : 3'000.0F;
+
+ const auto lReverb = clamp(
+ eax_d_.lReverb - static_cast<long>(std::log10(scale) * log_scalar),
+ EAXREVERB_MINREVERB,
+ EAXREVERB_MAXREVERB);
+
+ defer_reverb(lReverb);
+ }
+
+ if ((eax_d_.ulFlags & EAXREVERBFLAGS_REVERBDELAYSCALE) != 0)
+ {
+ const auto flReverbDelay = clamp(
+ scale * eax_d_.flReverbDelay,
+ EAXREVERB_MINREVERBDELAY,
+ EAXREVERB_MAXREVERBDELAY);
+
+ defer_reverb_delay(flReverbDelay);
+ }
+
+ if ((eax_d_.ulFlags & EAXREVERBFLAGS_ECHOTIMESCALE) != 0)
+ {
+ const auto flEchoTime = clamp(
+ eax_d_.flEchoTime * scale,
+ EAXREVERB_MINECHOTIME,
+ EAXREVERB_MAXECHOTIME);
+
+ defer_echo_time(flEchoTime);
+ }
+
+ if ((eax_d_.ulFlags & EAXREVERBFLAGS_MODULATIONTIMESCALE) != 0)
+ {
+ const auto flModulationTime = clamp(
+ scale * eax_d_.flModulationTime,
+ EAXREVERB_MINMODULATIONTIME,
+ EAXREVERB_MAXMODULATIONTIME);
+
+ defer_modulation_time(flModulationTime);
+ }
+}
+
+void EaxReverbEffect::defer_environment_diffusion(
+ const EaxEaxCall& eax_call)
+{
+ const auto& flEnvironmentDiffusion =
+ eax_call.get_value<EaxReverbEffectException, const decltype(EAXREVERBPROPERTIES::flEnvironmentDiffusion)>();
+
+ validate_environment_diffusion(flEnvironmentDiffusion);
+ defer_environment_diffusion(flEnvironmentDiffusion);
+}
+
+void EaxReverbEffect::defer_room(
+ const EaxEaxCall& eax_call)
+{
+ const auto& lRoom =
+ eax_call.get_value<EaxReverbEffectException, const decltype(EAXREVERBPROPERTIES::lRoom)>();
+
+ validate_room(lRoom);
+ defer_room(lRoom);
+}
+
+void EaxReverbEffect::defer_room_hf(
+ const EaxEaxCall& eax_call)
+{
+ const auto& lRoomHF =
+ eax_call.get_value<EaxReverbEffectException, const decltype(EAXREVERBPROPERTIES::lRoomHF)>();
+
+ validate_room_hf(lRoomHF);
+ defer_room_hf(lRoomHF);
+}
+
+void EaxReverbEffect::defer_room_lf(
+ const EaxEaxCall& eax_call)
+{
+ const auto& lRoomLF =
+ eax_call.get_value<EaxReverbEffectException, const decltype(EAXREVERBPROPERTIES::lRoomLF)>();
+
+ validate_room_lf(lRoomLF);
+ defer_room_lf(lRoomLF);
+}
+
+void EaxReverbEffect::defer_decay_time(
+ const EaxEaxCall& eax_call)
+{
+ const auto& flDecayTime =
+ eax_call.get_value<EaxReverbEffectException, const decltype(EAXREVERBPROPERTIES::flDecayTime)>();
+
+ validate_decay_time(flDecayTime);
+ defer_decay_time(flDecayTime);
+}
+
+void EaxReverbEffect::defer_decay_hf_ratio(
+ const EaxEaxCall& eax_call)
+{
+ const auto& flDecayHFRatio =
+ eax_call.get_value<EaxReverbEffectException, const decltype(EAXREVERBPROPERTIES::flDecayHFRatio)>();
+
+ validate_decay_hf_ratio(flDecayHFRatio);
+ defer_decay_hf_ratio(flDecayHFRatio);
+}
+
+void EaxReverbEffect::defer_decay_lf_ratio(
+ const EaxEaxCall& eax_call)
+{
+ const auto& flDecayLFRatio =
+ eax_call.get_value<EaxReverbEffectException, const decltype(EAXREVERBPROPERTIES::flDecayLFRatio)>();
+
+ validate_decay_lf_ratio(flDecayLFRatio);
+ defer_decay_lf_ratio(flDecayLFRatio);
+}
+
+void EaxReverbEffect::defer_reflections(
+ const EaxEaxCall& eax_call)
+{
+ const auto& lReflections =
+ eax_call.get_value<EaxReverbEffectException, const decltype(EAXREVERBPROPERTIES::lReflections)>();
+
+ validate_reflections(lReflections);
+ defer_reflections(lReflections);
+}
+
+void EaxReverbEffect::defer_reflections_delay(
+ const EaxEaxCall& eax_call)
+{
+ const auto& flReflectionsDelay =
+ eax_call.get_value<EaxReverbEffectException, const decltype(EAXREVERBPROPERTIES::flReflectionsDelay)>();
+
+ validate_reflections_delay(flReflectionsDelay);
+ defer_reflections_delay(flReflectionsDelay);
+}
+
+void EaxReverbEffect::defer_reflections_pan(
+ const EaxEaxCall& eax_call)
+{
+ const auto& vReflectionsPan =
+ eax_call.get_value<EaxReverbEffectException, const decltype(EAXREVERBPROPERTIES::vReflectionsPan)>();
+
+ validate_reflections_pan(vReflectionsPan);
+ defer_reflections_pan(vReflectionsPan);
+}
+
+void EaxReverbEffect::defer_reverb(
+ const EaxEaxCall& eax_call)
+{
+ const auto& lReverb =
+ eax_call.get_value<EaxReverbEffectException, const decltype(EAXREVERBPROPERTIES::lReverb)>();
+
+ validate_reverb(lReverb);
+ defer_reverb(lReverb);
+}
+
+void EaxReverbEffect::defer_reverb_delay(
+ const EaxEaxCall& eax_call)
+{
+ const auto& flReverbDelay =
+ eax_call.get_value<EaxReverbEffectException, const decltype(EAXREVERBPROPERTIES::flReverbDelay)>();
+
+ validate_reverb_delay(flReverbDelay);
+ defer_reverb_delay(flReverbDelay);
+}
+
+void EaxReverbEffect::defer_reverb_pan(
+ const EaxEaxCall& eax_call)
+{
+ const auto& vReverbPan =
+ eax_call.get_value<EaxReverbEffectException, const decltype(EAXREVERBPROPERTIES::vReverbPan)>();
+
+ validate_reverb_pan(vReverbPan);
+ defer_reverb_pan(vReverbPan);
+}
+
+void EaxReverbEffect::defer_echo_time(
+ const EaxEaxCall& eax_call)
+{
+ const auto& flEchoTime =
+ eax_call.get_value<EaxReverbEffectException, const decltype(EAXREVERBPROPERTIES::flEchoTime)>();
+
+ validate_echo_time(flEchoTime);
+ defer_echo_time(flEchoTime);
+}
+
+void EaxReverbEffect::defer_echo_depth(
+ const EaxEaxCall& eax_call)
+{
+ const auto& flEchoDepth =
+ eax_call.get_value<EaxReverbEffectException, const decltype(EAXREVERBPROPERTIES::flEchoDepth)>();
+
+ validate_echo_depth(flEchoDepth);
+ defer_echo_depth(flEchoDepth);
+}
+
+void EaxReverbEffect::defer_modulation_time(
+ const EaxEaxCall& eax_call)
+{
+ const auto& flModulationTime =
+ eax_call.get_value<EaxReverbEffectException, const decltype(EAXREVERBPROPERTIES::flModulationTime)>();
+
+ validate_modulation_time(flModulationTime);
+ defer_modulation_time(flModulationTime);
+}
+
+void EaxReverbEffect::defer_modulation_depth(
+ const EaxEaxCall& eax_call)
+{
+ const auto& flModulationDepth =
+ eax_call.get_value<EaxReverbEffectException, const decltype(EAXREVERBPROPERTIES::flModulationDepth)>();
+
+ validate_modulation_depth(flModulationDepth);
+ defer_modulation_depth(flModulationDepth);
+}
+
+void EaxReverbEffect::defer_air_absorbtion_hf(
+ const EaxEaxCall& eax_call)
+{
+ const auto& air_absorbtion_hf =
+ eax_call.get_value<EaxReverbEffectException, const decltype(EAXREVERBPROPERTIES::flAirAbsorptionHF)>();
+
+ validate_air_absorbtion_hf(air_absorbtion_hf);
+ defer_air_absorbtion_hf(air_absorbtion_hf);
+}
+
+void EaxReverbEffect::defer_hf_reference(
+ const EaxEaxCall& eax_call)
+{
+ const auto& flHFReference =
+ eax_call.get_value<EaxReverbEffectException, const decltype(EAXREVERBPROPERTIES::flHFReference)>();
+
+ validate_hf_reference(flHFReference);
+ defer_hf_reference(flHFReference);
+}
+
+void EaxReverbEffect::defer_lf_reference(
+ const EaxEaxCall& eax_call)
+{
+ const auto& flLFReference =
+ eax_call.get_value<EaxReverbEffectException, const decltype(EAXREVERBPROPERTIES::flLFReference)>();
+
+ validate_lf_reference(flLFReference);
+ defer_lf_reference(flLFReference);
+}
+
+void EaxReverbEffect::defer_room_rolloff_factor(
+ const EaxEaxCall& eax_call)
+{
+ const auto& flRoomRolloffFactor =
+ eax_call.get_value<EaxReverbEffectException, const decltype(EAXREVERBPROPERTIES::flRoomRolloffFactor)>();
+
+ validate_room_rolloff_factor(flRoomRolloffFactor);
+ defer_room_rolloff_factor(flRoomRolloffFactor);
+}
+
+void EaxReverbEffect::defer_flags(
+ const EaxEaxCall& eax_call)
+{
+ const auto& ulFlags =
+ eax_call.get_value<EaxReverbEffectException, const decltype(EAXREVERBPROPERTIES::ulFlags)>();
+
+ validate_flags(ulFlags);
+ defer_flags(ulFlags);
+}
+
+void EaxReverbEffect::defer_all(
+ const EaxEaxCall& eax_call)
+{
+ const auto eax_version = eax_call.get_version();
+
+ if (eax_version == 2)
+ {
+ const auto& listener =
+ eax_call.get_value<EaxReverbEffectException, const EAX20LISTENERPROPERTIES>();
+
+ validate_all(listener, eax_version);
+ defer_all(listener);
+ }
+ else
+ {
+ const auto& reverb_all =
+ eax_call.get_value<EaxReverbEffectException, const EAXREVERBPROPERTIES>();
+
+ validate_all(reverb_all, eax_version);
+ defer_all(reverb_all);
+ }
+}
+
+// [[nodiscard]]
+bool EaxReverbEffect::apply_deferred()
+{
+ if (eax_dirty_flags_ == EaxReverbEffectDirtyFlags{})
+ {
+ return false;
+ }
+
+ eax_ = eax_d_;
+
+ if (eax_dirty_flags_.ulEnvironment)
+ {
+ }
+
+ if (eax_dirty_flags_.flEnvironmentSize)
+ {
+ set_efx_density();
+ }
+
+ if (eax_dirty_flags_.flEnvironmentDiffusion)
+ {
+ set_efx_diffusion();
+ }
+
+ if (eax_dirty_flags_.lRoom)
+ {
+ set_efx_gain();
+ }
+
+ if (eax_dirty_flags_.lRoomHF)
+ {
+ set_efx_gain_hf();
+ }
+
+ if (eax_dirty_flags_.lRoomLF)
+ {
+ set_efx_gain_lf();
+ }
+
+ if (eax_dirty_flags_.flDecayTime)
+ {
+ set_efx_decay_time();
+ }
+
+ if (eax_dirty_flags_.flDecayHFRatio)
+ {
+ set_efx_decay_hf_ratio();
+ }
+
+ if (eax_dirty_flags_.flDecayLFRatio)
+ {
+ set_efx_decay_lf_ratio();
+ }
+
+ if (eax_dirty_flags_.lReflections)
+ {
+ set_efx_reflections_gain();
+ }
+
+ if (eax_dirty_flags_.flReflectionsDelay)
+ {
+ set_efx_reflections_delay();
+ }
+
+ if (eax_dirty_flags_.vReflectionsPan)
+ {
+ set_efx_reflections_pan();
+ }
+
+ if (eax_dirty_flags_.lReverb)
+ {
+ set_efx_late_reverb_gain();
+ }
+
+ if (eax_dirty_flags_.flReverbDelay)
+ {
+ set_efx_late_reverb_delay();
+ }
+
+ if (eax_dirty_flags_.vReverbPan)
+ {
+ set_efx_late_reverb_pan();
+ }
+
+ if (eax_dirty_flags_.flEchoTime)
+ {
+ set_efx_echo_time();
+ }
+
+ if (eax_dirty_flags_.flEchoDepth)
+ {
+ set_efx_echo_depth();
+ }
+
+ if (eax_dirty_flags_.flModulationTime)
+ {
+ set_efx_modulation_time();
+ }
+
+ if (eax_dirty_flags_.flModulationDepth)
+ {
+ set_efx_modulation_depth();
+ }
+
+ if (eax_dirty_flags_.flAirAbsorptionHF)
+ {
+ set_efx_air_absorption_gain_hf();
+ }
+
+ if (eax_dirty_flags_.flHFReference)
+ {
+ set_efx_hf_reference();
+ }
+
+ if (eax_dirty_flags_.flLFReference)
+ {
+ set_efx_lf_reference();
+ }
+
+ if (eax_dirty_flags_.flRoomRolloffFactor)
+ {
+ set_efx_room_rolloff_factor();
+ }
+
+ if (eax_dirty_flags_.ulFlags)
+ {
+ set_efx_flags();
+ }
+
+ eax_dirty_flags_ = EaxReverbEffectDirtyFlags{};
+
+ return true;
+}
+
+// [[nodiscard]]
+bool EaxReverbEffect::set(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXREVERB_NONE:
+ break;
+
+ case EAXREVERB_ALLPARAMETERS:
+ defer_all(eax_call);
+ break;
+
+ case EAXREVERB_ENVIRONMENT:
+ defer_environment(eax_call);
+ break;
+
+ case EAXREVERB_ENVIRONMENTSIZE:
+ defer_environment_size(eax_call);
+ break;
+
+ case EAXREVERB_ENVIRONMENTDIFFUSION:
+ defer_environment_diffusion(eax_call);
+ break;
+
+ case EAXREVERB_ROOM:
+ defer_room(eax_call);
+ break;
+
+ case EAXREVERB_ROOMHF:
+ defer_room_hf(eax_call);
+ break;
+
+ case EAXREVERB_ROOMLF:
+ defer_room_lf(eax_call);
+ break;
+
+ case EAXREVERB_DECAYTIME:
+ defer_decay_time(eax_call);
+ break;
+
+ case EAXREVERB_DECAYHFRATIO:
+ defer_decay_hf_ratio(eax_call);
+ break;
+
+ case EAXREVERB_DECAYLFRATIO:
+ defer_decay_lf_ratio(eax_call);
+ break;
+
+ case EAXREVERB_REFLECTIONS:
+ defer_reflections(eax_call);
+ break;
+
+ case EAXREVERB_REFLECTIONSDELAY:
+ defer_reflections_delay(eax_call);
+ break;
+
+ case EAXREVERB_REFLECTIONSPAN:
+ defer_reflections_pan(eax_call);
+ break;
+
+ case EAXREVERB_REVERB:
+ defer_reverb(eax_call);
+ break;
+
+ case EAXREVERB_REVERBDELAY:
+ defer_reverb_delay(eax_call);
+ break;
+
+ case EAXREVERB_REVERBPAN:
+ defer_reverb_pan(eax_call);
+ break;
+
+ case EAXREVERB_ECHOTIME:
+ defer_echo_time(eax_call);
+ break;
+
+ case EAXREVERB_ECHODEPTH:
+ defer_echo_depth(eax_call);
+ break;
+
+ case EAXREVERB_MODULATIONTIME:
+ defer_modulation_time(eax_call);
+ break;
+
+ case EAXREVERB_MODULATIONDEPTH:
+ defer_modulation_depth(eax_call);
+ break;
+
+ case EAXREVERB_AIRABSORPTIONHF:
+ defer_air_absorbtion_hf(eax_call);
+ break;
+
+ case EAXREVERB_HFREFERENCE:
+ defer_hf_reference(eax_call);
+ break;
+
+ case EAXREVERB_LFREFERENCE:
+ defer_lf_reference(eax_call);
+ break;
+
+ case EAXREVERB_ROOMROLLOFFFACTOR:
+ defer_room_rolloff_factor(eax_call);
+ break;
+
+ case EAXREVERB_FLAGS:
+ defer_flags(eax_call);
+ break;
+
+ default:
+ throw EaxReverbEffectException{"Unsupported property id."};
+ }
+
+ if (!eax_call.is_deferred())
+ {
+ return apply_deferred();
+ }
+
+ return false;
+}
+
+
+} // namespace
+
+
+EaxEffectUPtr eax_create_eax_reverb_effect(
+ EffectProps& al_effect_props)
+{
+ return std::make_unique<EaxReverbEffect>(al_effect_props);
+}
+
+
+#endif // ALSOFT_EAX
diff --git a/al/effects/vmorpher.cpp b/al/effects/vmorpher.cpp
index 1b4710ff..2a9e0702 100644
--- a/al/effects/vmorpher.cpp
+++ b/al/effects/vmorpher.cpp
@@ -10,6 +10,15 @@
#include "aloptional.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 {
@@ -247,3 +256,618 @@ EffectProps genDefaultProps() noexcept
DEFINE_ALEFFECT_VTABLE(Vmorpher);
const EffectProps VmorpherEffectProps{genDefaultProps()};
+
+#if ALSOFT_EAX
+namespace
+{
+
+
+using EaxVocalMorpherEffectDirtyFlagsValue = std::uint_least8_t;
+
+struct EaxVocalMorpherEffectDirtyFlags
+{
+ using EaxIsBitFieldStruct = bool;
+
+ EaxVocalMorpherEffectDirtyFlagsValue ulPhonemeA : 1;
+ EaxVocalMorpherEffectDirtyFlagsValue lPhonemeACoarseTuning : 1;
+ EaxVocalMorpherEffectDirtyFlagsValue ulPhonemeB : 1;
+ EaxVocalMorpherEffectDirtyFlagsValue lPhonemeBCoarseTuning : 1;
+ EaxVocalMorpherEffectDirtyFlagsValue ulWaveform : 1;
+ EaxVocalMorpherEffectDirtyFlagsValue flRate : 1;
+}; // EaxPitchShifterEffectDirtyFlags
+
+
+class EaxVocalMorpherEffect final :
+ public EaxEffect
+{
+public:
+ EaxVocalMorpherEffect(
+ EffectProps& al_effect_props);
+
+
+ // [[nodiscard]]
+ bool dispatch(
+ const EaxEaxCall& eax_call) override;
+
+
+private:
+ EffectProps& al_effect_props_;
+
+ EAXVOCALMORPHERPROPERTIES eax_{};
+ EAXVOCALMORPHERPROPERTIES eax_d_{};
+ EaxVocalMorpherEffectDirtyFlags eax_dirty_flags_{};
+
+
+ void set_eax_defaults();
+
+
+ void set_efx_phoneme_a();
+
+ void set_efx_phoneme_a_coarse_tuning();
+
+ void set_efx_phoneme_b();
+
+ void set_efx_phoneme_b_coarse_tuning();
+
+ void set_efx_waveform();
+
+ void set_efx_rate();
+
+ void set_efx_defaults();
+
+
+ // [[nodiscard]]
+ bool get(
+ const EaxEaxCall& eax_call);
+
+
+ void validate_phoneme_a(
+ unsigned long ulPhonemeA);
+
+ void validate_phoneme_a_coarse_tuning(
+ long lPhonemeACoarseTuning);
+
+ void validate_phoneme_b(
+ unsigned long ulPhonemeB);
+
+ void validate_phoneme_b_coarse_tuning(
+ long lPhonemeBCoarseTuning);
+
+ void validate_waveform(
+ unsigned long ulWaveform);
+
+ void validate_rate(
+ float flRate);
+
+ void validate_all(
+ const EAXVOCALMORPHERPROPERTIES& all);
+
+
+ void defer_phoneme_a(
+ unsigned long ulPhonemeA);
+
+ void defer_phoneme_a_coarse_tuning(
+ long lPhonemeACoarseTuning);
+
+ void defer_phoneme_b(
+ unsigned long ulPhonemeB);
+
+ void defer_phoneme_b_coarse_tuning(
+ long lPhonemeBCoarseTuning);
+
+ void defer_waveform(
+ unsigned long ulWaveform);
+
+ void defer_rate(
+ float flRate);
+
+ void defer_all(
+ const EAXVOCALMORPHERPROPERTIES& all);
+
+
+ void defer_phoneme_a(
+ const EaxEaxCall& eax_call);
+
+ void defer_phoneme_a_coarse_tuning(
+ const EaxEaxCall& eax_call);
+
+ void defer_phoneme_b(
+ const EaxEaxCall& eax_call);
+
+ void defer_phoneme_b_coarse_tuning(
+ const EaxEaxCall& eax_call);
+
+ void defer_waveform(
+ const EaxEaxCall& eax_call);
+
+ void defer_rate(
+ const EaxEaxCall& eax_call);
+
+ void defer_all(
+ const EaxEaxCall& eax_call);
+
+
+ // [[nodiscard]]
+ bool apply_deferred();
+
+ // [[nodiscard]]
+ bool set(
+ const EaxEaxCall& eax_call);
+}; // EaxVocalMorpherEffect
+
+
+class EaxVocalMorpherEffectException :
+ public EaxException
+{
+public:
+ explicit EaxVocalMorpherEffectException(
+ const char* message)
+ :
+ EaxException{"EAX_VOCAL_MORPHER_EFFECT", message}
+ {
+ }
+}; // EaxVocalMorpherEffectException
+
+
+EaxVocalMorpherEffect::EaxVocalMorpherEffect(
+ EffectProps& al_effect_props)
+ :
+ al_effect_props_{al_effect_props}
+{
+ set_eax_defaults();
+ set_efx_defaults();
+}
+
+// [[nodiscard]]
+bool EaxVocalMorpherEffect::dispatch(
+ const EaxEaxCall& eax_call)
+{
+ return eax_call.is_get() ? get(eax_call) : set(eax_call);
+}
+
+void EaxVocalMorpherEffect::set_eax_defaults()
+{
+ eax_.ulPhonemeA = EAXVOCALMORPHER_DEFAULTPHONEMEA;
+ eax_.lPhonemeACoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING;
+ eax_.ulPhonemeB = EAXVOCALMORPHER_DEFAULTPHONEMEB;
+ eax_.lPhonemeBCoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING;
+ eax_.ulWaveform = EAXVOCALMORPHER_DEFAULTWAVEFORM;
+ eax_.flRate = EAXVOCALMORPHER_DEFAULTRATE;
+
+ eax_d_ = eax_;
+}
+
+void EaxVocalMorpherEffect::set_efx_phoneme_a()
+{
+ const auto phoneme_a = clamp(
+ static_cast<ALint>(eax_.ulPhonemeA),
+ AL_VOCAL_MORPHER_MIN_PHONEMEA,
+ AL_VOCAL_MORPHER_MAX_PHONEMEA);
+
+ const auto efx_phoneme_a = PhenomeFromEnum(phoneme_a);
+ assert(efx_phoneme_a.has_value());
+ al_effect_props_.Vmorpher.PhonemeA = *efx_phoneme_a;
+}
+
+void EaxVocalMorpherEffect::set_efx_phoneme_a_coarse_tuning()
+{
+ const auto phoneme_a_coarse_tuning = clamp(
+ static_cast<ALint>(eax_.lPhonemeACoarseTuning),
+ AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING,
+ AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING);
+
+ al_effect_props_.Vmorpher.PhonemeACoarseTuning = phoneme_a_coarse_tuning;
+}
+
+void EaxVocalMorpherEffect::set_efx_phoneme_b()
+{
+ const auto phoneme_b = clamp(
+ static_cast<ALint>(eax_.ulPhonemeB),
+ AL_VOCAL_MORPHER_MIN_PHONEMEB,
+ AL_VOCAL_MORPHER_MAX_PHONEMEB);
+
+ const auto efx_phoneme_b = PhenomeFromEnum(phoneme_b);
+ assert(efx_phoneme_b.has_value());
+ al_effect_props_.Vmorpher.PhonemeB = *efx_phoneme_b;
+}
+
+void EaxVocalMorpherEffect::set_efx_phoneme_b_coarse_tuning()
+{
+ const auto phoneme_b_coarse_tuning = clamp(
+ static_cast<ALint>(eax_.lPhonemeBCoarseTuning),
+ AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING,
+ AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING);
+
+ al_effect_props_.Vmorpher.PhonemeBCoarseTuning = phoneme_b_coarse_tuning;
+}
+
+void EaxVocalMorpherEffect::set_efx_waveform()
+{
+ const auto waveform = clamp(
+ static_cast<ALint>(eax_.ulWaveform),
+ AL_VOCAL_MORPHER_MIN_WAVEFORM,
+ AL_VOCAL_MORPHER_MAX_WAVEFORM);
+
+ const auto wfx_waveform = WaveformFromEmum(waveform);
+ assert(wfx_waveform.has_value());
+ al_effect_props_.Vmorpher.Waveform = *wfx_waveform;
+}
+
+void EaxVocalMorpherEffect::set_efx_rate()
+{
+ const auto rate = clamp(
+ eax_.flRate,
+ AL_VOCAL_MORPHER_MIN_RATE,
+ AL_VOCAL_MORPHER_MAX_RATE);
+
+ al_effect_props_.Vmorpher.Rate = rate;
+}
+
+void EaxVocalMorpherEffect::set_efx_defaults()
+{
+ set_efx_phoneme_a();
+ set_efx_phoneme_a_coarse_tuning();
+ set_efx_phoneme_b();
+ set_efx_phoneme_b_coarse_tuning();
+ set_efx_waveform();
+ set_efx_rate();
+}
+
+// [[nodiscard]]
+bool EaxVocalMorpherEffect::get(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXVOCALMORPHER_NONE:
+ break;
+
+ case EAXVOCALMORPHER_ALLPARAMETERS:
+ eax_call.set_value<EaxVocalMorpherEffectException>(eax_);
+ break;
+
+ case EAXVOCALMORPHER_PHONEMEA:
+ eax_call.set_value<EaxVocalMorpherEffectException>(eax_.ulPhonemeA);
+ break;
+
+ case EAXVOCALMORPHER_PHONEMEACOARSETUNING:
+ eax_call.set_value<EaxVocalMorpherEffectException>(eax_.lPhonemeACoarseTuning);
+ break;
+
+ case EAXVOCALMORPHER_PHONEMEB:
+ eax_call.set_value<EaxVocalMorpherEffectException>(eax_.ulPhonemeB);
+ break;
+
+ case EAXVOCALMORPHER_PHONEMEBCOARSETUNING:
+ eax_call.set_value<EaxVocalMorpherEffectException>(eax_.lPhonemeBCoarseTuning);
+ break;
+
+ case EAXVOCALMORPHER_WAVEFORM:
+ eax_call.set_value<EaxVocalMorpherEffectException>(eax_.ulWaveform);
+ break;
+
+ case EAXVOCALMORPHER_RATE:
+ eax_call.set_value<EaxVocalMorpherEffectException>(eax_.flRate);
+ break;
+
+ default:
+ throw EaxVocalMorpherEffectException{"Unsupported property id."};
+ }
+
+ return false;
+}
+
+void EaxVocalMorpherEffect::validate_phoneme_a(
+ unsigned long ulPhonemeA)
+{
+ eax_validate_range<EaxVocalMorpherEffectException>(
+ "Phoneme A",
+ ulPhonemeA,
+ EAXVOCALMORPHER_MINPHONEMEA,
+ EAXVOCALMORPHER_MAXPHONEMEA);
+}
+
+void EaxVocalMorpherEffect::validate_phoneme_a_coarse_tuning(
+ long lPhonemeACoarseTuning)
+{
+ eax_validate_range<EaxVocalMorpherEffectException>(
+ "Phoneme A Coarse Tuning",
+ lPhonemeACoarseTuning,
+ EAXVOCALMORPHER_MINPHONEMEACOARSETUNING,
+ EAXVOCALMORPHER_MAXPHONEMEACOARSETUNING);
+}
+
+void EaxVocalMorpherEffect::validate_phoneme_b(
+ unsigned long ulPhonemeB)
+{
+ eax_validate_range<EaxVocalMorpherEffectException>(
+ "Phoneme B",
+ ulPhonemeB,
+ EAXVOCALMORPHER_MINPHONEMEB,
+ EAXVOCALMORPHER_MAXPHONEMEB);
+}
+
+void EaxVocalMorpherEffect::validate_phoneme_b_coarse_tuning(
+ long lPhonemeBCoarseTuning)
+{
+ eax_validate_range<EaxVocalMorpherEffectException>(
+ "Phoneme B Coarse Tuning",
+ lPhonemeBCoarseTuning,
+ EAXVOCALMORPHER_MINPHONEMEBCOARSETUNING,
+ EAXVOCALMORPHER_MAXPHONEMEBCOARSETUNING);
+}
+
+void EaxVocalMorpherEffect::validate_waveform(
+ unsigned long ulWaveform)
+{
+ eax_validate_range<EaxVocalMorpherEffectException>(
+ "Waveform",
+ ulWaveform,
+ EAXVOCALMORPHER_MINWAVEFORM,
+ EAXVOCALMORPHER_MAXWAVEFORM);
+}
+
+void EaxVocalMorpherEffect::validate_rate(
+ float flRate)
+{
+ eax_validate_range<EaxVocalMorpherEffectException>(
+ "Rate",
+ flRate,
+ EAXVOCALMORPHER_MINRATE,
+ EAXVOCALMORPHER_MAXRATE);
+}
+
+void EaxVocalMorpherEffect::validate_all(
+ const EAXVOCALMORPHERPROPERTIES& all)
+{
+ validate_phoneme_a(all.ulPhonemeA);
+ validate_phoneme_a_coarse_tuning(all.lPhonemeACoarseTuning);
+ validate_phoneme_b(all.ulPhonemeB);
+ validate_phoneme_b_coarse_tuning(all.lPhonemeBCoarseTuning);
+ validate_waveform(all.ulWaveform);
+ validate_rate(all.flRate);
+}
+
+void EaxVocalMorpherEffect::defer_phoneme_a(
+ unsigned long ulPhonemeA)
+{
+ eax_d_.ulPhonemeA = ulPhonemeA;
+ eax_dirty_flags_.ulPhonemeA = (eax_.ulPhonemeA != eax_d_.ulPhonemeA);
+}
+
+void EaxVocalMorpherEffect::defer_phoneme_a_coarse_tuning(
+ long lPhonemeACoarseTuning)
+{
+ eax_d_.lPhonemeACoarseTuning = lPhonemeACoarseTuning;
+ eax_dirty_flags_.lPhonemeACoarseTuning = (eax_.lPhonemeACoarseTuning != eax_d_.lPhonemeACoarseTuning);
+}
+
+void EaxVocalMorpherEffect::defer_phoneme_b(
+ unsigned long ulPhonemeB)
+{
+ eax_d_.ulPhonemeB = ulPhonemeB;
+ eax_dirty_flags_.ulPhonemeB = (eax_.ulPhonemeB != eax_d_.ulPhonemeB);
+}
+
+void EaxVocalMorpherEffect::defer_phoneme_b_coarse_tuning(
+ long lPhonemeBCoarseTuning)
+{
+ eax_d_.lPhonemeBCoarseTuning = lPhonemeBCoarseTuning;
+ eax_dirty_flags_.lPhonemeBCoarseTuning = (eax_.lPhonemeBCoarseTuning != eax_d_.lPhonemeBCoarseTuning);
+}
+
+void EaxVocalMorpherEffect::defer_waveform(
+ unsigned long ulWaveform)
+{
+ eax_d_.ulWaveform = ulWaveform;
+ eax_dirty_flags_.ulWaveform = (eax_.ulWaveform != eax_d_.ulWaveform);
+}
+
+void EaxVocalMorpherEffect::defer_rate(
+ float flRate)
+{
+ eax_d_.flRate = flRate;
+ eax_dirty_flags_.flRate = (eax_.flRate != eax_d_.flRate);
+}
+
+void EaxVocalMorpherEffect::defer_all(
+ const EAXVOCALMORPHERPROPERTIES& all)
+{
+ defer_phoneme_a(all.ulPhonemeA);
+ defer_phoneme_a_coarse_tuning(all.lPhonemeACoarseTuning);
+ defer_phoneme_b(all.ulPhonemeB);
+ defer_phoneme_b_coarse_tuning(all.lPhonemeBCoarseTuning);
+ defer_waveform(all.ulWaveform);
+ defer_rate(all.flRate);
+}
+
+void EaxVocalMorpherEffect::defer_phoneme_a(
+ const EaxEaxCall& eax_call)
+{
+ const auto& phoneme_a = eax_call.get_value<
+ EaxVocalMorpherEffectException,
+ const decltype(EAXVOCALMORPHERPROPERTIES::ulPhonemeA)
+ >();
+
+ validate_phoneme_a(phoneme_a);
+ defer_phoneme_a(phoneme_a);
+}
+
+void EaxVocalMorpherEffect::defer_phoneme_a_coarse_tuning(
+ const EaxEaxCall& eax_call)
+{
+ const auto& phoneme_a_coarse_tuning = eax_call.get_value<
+ EaxVocalMorpherEffectException,
+ const decltype(EAXVOCALMORPHERPROPERTIES::lPhonemeACoarseTuning)
+ >();
+
+ validate_phoneme_a_coarse_tuning(phoneme_a_coarse_tuning);
+ defer_phoneme_a_coarse_tuning(phoneme_a_coarse_tuning);
+}
+
+void EaxVocalMorpherEffect::defer_phoneme_b(
+ const EaxEaxCall& eax_call)
+{
+ const auto& phoneme_b = eax_call.get_value<
+ EaxVocalMorpherEffectException,
+ const decltype(EAXVOCALMORPHERPROPERTIES::ulPhonemeB)
+ >();
+
+ validate_phoneme_b(phoneme_b);
+ defer_phoneme_b(phoneme_b);
+}
+
+void EaxVocalMorpherEffect::defer_phoneme_b_coarse_tuning(
+ const EaxEaxCall& eax_call)
+{
+ const auto& phoneme_b_coarse_tuning = eax_call.get_value<
+ EaxVocalMorpherEffectException,
+ const decltype(EAXVOCALMORPHERPROPERTIES::lPhonemeBCoarseTuning)
+ >();
+
+ validate_phoneme_b_coarse_tuning(phoneme_b_coarse_tuning);
+ defer_phoneme_b_coarse_tuning(phoneme_b_coarse_tuning);
+}
+
+void EaxVocalMorpherEffect::defer_waveform(
+ const EaxEaxCall& eax_call)
+{
+ const auto& waveform = eax_call.get_value<
+ EaxVocalMorpherEffectException,
+ const decltype(EAXVOCALMORPHERPROPERTIES::ulWaveform)
+ >();
+
+ validate_waveform(waveform);
+ defer_waveform(waveform);
+}
+
+void EaxVocalMorpherEffect::defer_rate(
+ const EaxEaxCall& eax_call)
+{
+ const auto& rate = eax_call.get_value<
+ EaxVocalMorpherEffectException,
+ const decltype(EAXVOCALMORPHERPROPERTIES::flRate)
+ >();
+
+ validate_rate(rate);
+ defer_rate(rate);
+}
+
+void EaxVocalMorpherEffect::defer_all(
+ const EaxEaxCall& eax_call)
+{
+ const auto& all = eax_call.get_value<
+ EaxVocalMorpherEffectException,
+ const EAXVOCALMORPHERPROPERTIES
+ >();
+
+ validate_all(all);
+ defer_all(all);
+}
+
+// [[nodiscard]]
+bool EaxVocalMorpherEffect::apply_deferred()
+{
+ if (eax_dirty_flags_ == EaxVocalMorpherEffectDirtyFlags{})
+ {
+ return false;
+ }
+
+ eax_ = eax_d_;
+
+ if (eax_dirty_flags_.ulPhonemeA)
+ {
+ set_efx_phoneme_a();
+ }
+
+ if (eax_dirty_flags_.lPhonemeACoarseTuning)
+ {
+ set_efx_phoneme_a_coarse_tuning();
+ }
+
+ if (eax_dirty_flags_.ulPhonemeB)
+ {
+ set_efx_phoneme_b();
+ }
+
+ if (eax_dirty_flags_.lPhonemeBCoarseTuning)
+ {
+ set_efx_phoneme_b_coarse_tuning();
+ }
+
+ if (eax_dirty_flags_.ulWaveform)
+ {
+ set_efx_waveform();
+ }
+
+ if (eax_dirty_flags_.flRate)
+ {
+ set_efx_rate();
+ }
+
+ eax_dirty_flags_ = EaxVocalMorpherEffectDirtyFlags{};
+
+ return true;
+}
+
+// [[nodiscard]]
+bool EaxVocalMorpherEffect::set(
+ const EaxEaxCall& eax_call)
+{
+ switch (eax_call.get_property_id())
+ {
+ case EAXVOCALMORPHER_NONE:
+ break;
+
+ case EAXVOCALMORPHER_ALLPARAMETERS:
+ defer_all(eax_call);
+ break;
+
+ case EAXVOCALMORPHER_PHONEMEA:
+ defer_phoneme_a(eax_call);
+ break;
+
+ case EAXVOCALMORPHER_PHONEMEACOARSETUNING:
+ defer_phoneme_a_coarse_tuning(eax_call);
+ break;
+
+ case EAXVOCALMORPHER_PHONEMEB:
+ defer_phoneme_b(eax_call);
+ break;
+
+ case EAXVOCALMORPHER_PHONEMEBCOARSETUNING:
+ defer_phoneme_b_coarse_tuning(eax_call);
+ break;
+
+ case EAXVOCALMORPHER_WAVEFORM:
+ defer_waveform(eax_call);
+ break;
+
+ case EAXVOCALMORPHER_RATE:
+ defer_rate(eax_call);
+ break;
+
+ default:
+ throw EaxVocalMorpherEffectException{"Unsupported property id."};
+ }
+
+ if (!eax_call.is_deferred())
+ {
+ return apply_deferred();
+ }
+
+ return false;
+}
+
+
+} // namespace
+
+
+EaxEffectUPtr eax_create_eax_vocal_morpher_effect(
+ EffectProps& al_effect_props)
+{
+ return std::make_unique<EaxVocalMorpherEffect>(al_effect_props);
+}
+
+
+#endif // ALSOFT_EAX