aboutsummaryrefslogtreecommitdiffstats
path: root/al/effects
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2023-05-03 16:17:49 +0200
committerSven Gothel <[email protected]>2023-05-03 16:17:49 +0200
commitec167fd05661a5b02dd406c87081f84a0f8dd77d (patch)
tree9c4669e471c9969bda59265381b18d2d416db060 /al/effects
parent0d14d30808cfe7b9e3413353e3eef8a0f201399a (diff)
parentd3875f333fb6abe2f39d82caca329414871ae53b (diff)
Merge branch 'v1.23.1'
Resolved Conflicts: CMakeLists.txt
Diffstat (limited to 'al/effects')
-rw-r--r--al/effects/autowah.cpp252
-rw-r--r--al/effects/chorus.cpp724
-rw-r--r--al/effects/compressor.cpp162
-rw-r--r--al/effects/convolution.cpp93
-rw-r--r--al/effects/dedicated.cpp72
-rw-r--r--al/effects/distortion.cpp271
-rw-r--r--al/effects/echo.cpp268
-rw-r--r--al/effects/effects.cpp9
-rw-r--r--al/effects/effects.h88
-rw-r--r--al/effects/equalizer.cpp411
-rw-r--r--al/effects/fshifter.cpp264
-rw-r--r--al/effects/modulator.cpp272
-rw-r--r--al/effects/null.cpp149
-rw-r--r--al/effects/pshifter.cpp191
-rw-r--r--al/effects/reverb.cpp1499
-rw-r--r--al/effects/vmorpher.cpp520
16 files changed, 5245 insertions, 0 deletions
diff --git a/al/effects/autowah.cpp b/al/effects/autowah.cpp
new file mode 100644
index 00000000..129318f4
--- /dev/null
+++ b/al/effects/autowah.cpp
@@ -0,0 +1,252 @@
+
+#include "config.h"
+
+#include <cmath>
+#include <cstdlib>
+
+#include <algorithm>
+
+#include "AL/efx.h"
+
+#include "alc/effects/base.h"
+#include "effects.h"
+
+#ifdef 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)
+{
+ switch(param)
+ {
+ case AL_AUTOWAH_ATTACK_TIME:
+ if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME))
+ throw effect_exception{AL_INVALID_VALUE, "Autowah attack time out of range"};
+ props->Autowah.AttackTime = val;
+ break;
+
+ case AL_AUTOWAH_RELEASE_TIME:
+ if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME))
+ throw effect_exception{AL_INVALID_VALUE, "Autowah release time out of range"};
+ props->Autowah.ReleaseTime = val;
+ break;
+
+ case AL_AUTOWAH_RESONANCE:
+ if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
+ throw effect_exception{AL_INVALID_VALUE, "Autowah resonance out of range"};
+ props->Autowah.Resonance = val;
+ break;
+
+ case AL_AUTOWAH_PEAK_GAIN:
+ if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN))
+ throw effect_exception{AL_INVALID_VALUE, "Autowah peak gain out of range"};
+ props->Autowah.PeakGain = val;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param};
+ }
+}
+void Autowah_setParamfv(EffectProps *props, ALenum param, const float *vals)
+{ Autowah_setParamf(props, param, vals[0]); }
+
+void Autowah_setParami(EffectProps*, ALenum param, int)
+{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
+void Autowah_setParamiv(EffectProps*, ALenum param, const int*)
+{
+ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x",
+ param};
+}
+
+void Autowah_getParamf(const EffectProps *props, ALenum param, float *val)
+{
+ switch(param)
+ {
+ case AL_AUTOWAH_ATTACK_TIME:
+ *val = props->Autowah.AttackTime;
+ break;
+
+ case AL_AUTOWAH_RELEASE_TIME:
+ *val = props->Autowah.ReleaseTime;
+ break;
+
+ case AL_AUTOWAH_RESONANCE:
+ *val = props->Autowah.Resonance;
+ break;
+
+ case AL_AUTOWAH_PEAK_GAIN:
+ *val = props->Autowah.PeakGain;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param};
+ }
+
+}
+void Autowah_getParamfv(const EffectProps *props, ALenum param, float *vals)
+{ Autowah_getParamf(props, param, vals); }
+
+void Autowah_getParami(const EffectProps*, ALenum param, int*)
+{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
+void Autowah_getParamiv(const EffectProps*, ALenum param, int*)
+{
+ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x",
+ param};
+}
+
+EffectProps genDefaultProps() noexcept
+{
+ EffectProps props{};
+ props.Autowah.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME;
+ props.Autowah.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME;
+ props.Autowah.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE;
+ props.Autowah.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN;
+ return props;
+}
+
+} // namespace
+
+DEFINE_ALEFFECT_VTABLE(Autowah);
+
+const EffectProps AutowahEffectProps{genDefaultProps()};
+
+#ifdef ALSOFT_EAX
+namespace {
+
+using AutowahCommitter = EaxCommitter<EaxAutowahCommitter>;
+
+struct AttackTimeValidator {
+ void operator()(float flAttackTime) const
+ {
+ eax_validate_range<AutowahCommitter::Exception>(
+ "Attack Time",
+ flAttackTime,
+ EAXAUTOWAH_MINATTACKTIME,
+ EAXAUTOWAH_MAXATTACKTIME);
+ }
+}; // AttackTimeValidator
+
+struct ReleaseTimeValidator {
+ void operator()(float flReleaseTime) const
+ {
+ eax_validate_range<AutowahCommitter::Exception>(
+ "Release Time",
+ flReleaseTime,
+ EAXAUTOWAH_MINRELEASETIME,
+ EAXAUTOWAH_MAXRELEASETIME);
+ }
+}; // ReleaseTimeValidator
+
+struct ResonanceValidator {
+ void operator()(long lResonance) const
+ {
+ eax_validate_range<AutowahCommitter::Exception>(
+ "Resonance",
+ lResonance,
+ EAXAUTOWAH_MINRESONANCE,
+ EAXAUTOWAH_MAXRESONANCE);
+ }
+}; // ResonanceValidator
+
+struct PeakLevelValidator {
+ void operator()(long lPeakLevel) const
+ {
+ eax_validate_range<AutowahCommitter::Exception>(
+ "Peak Level",
+ lPeakLevel,
+ EAXAUTOWAH_MINPEAKLEVEL,
+ EAXAUTOWAH_MAXPEAKLEVEL);
+ }
+}; // PeakLevelValidator
+
+struct AllValidator {
+ void operator()(const EAXAUTOWAHPROPERTIES& all) const
+ {
+ AttackTimeValidator{}(all.flAttackTime);
+ ReleaseTimeValidator{}(all.flReleaseTime);
+ ResonanceValidator{}(all.lResonance);
+ PeakLevelValidator{}(all.lPeakLevel);
+ }
+}; // AllValidator
+
+} // namespace
+
+template<>
+struct AutowahCommitter::Exception : public EaxException
+{
+ explicit Exception(const char *message) : EaxException{"EAX_AUTOWAH_EFFECT", message}
+ { }
+};
+
+template<>
+[[noreturn]] void AutowahCommitter::fail(const char *message)
+{
+ throw Exception{message};
+}
+
+template<>
+bool AutowahCommitter::commit(const EaxEffectProps &props)
+{
+ if(props.mType == mEaxProps.mType
+ && mEaxProps.mAutowah.flAttackTime == props.mAutowah.flAttackTime
+ && mEaxProps.mAutowah.flReleaseTime == props.mAutowah.flReleaseTime
+ && mEaxProps.mAutowah.lResonance == props.mAutowah.lResonance
+ && mEaxProps.mAutowah.lPeakLevel == props.mAutowah.lPeakLevel)
+ return false;
+
+ mEaxProps = props;
+
+ mAlProps.Autowah.AttackTime = props.mAutowah.flAttackTime;
+ mAlProps.Autowah.ReleaseTime = props.mAutowah.flReleaseTime;
+ mAlProps.Autowah.Resonance = level_mb_to_gain(static_cast<float>(props.mAutowah.lResonance));
+ mAlProps.Autowah.PeakGain = level_mb_to_gain(static_cast<float>(props.mAutowah.lPeakLevel));
+
+ return true;
+}
+
+template<>
+void AutowahCommitter::SetDefaults(EaxEffectProps &props)
+{
+ props.mType = EaxEffectType::Autowah;
+ props.mAutowah.flAttackTime = EAXAUTOWAH_DEFAULTATTACKTIME;
+ props.mAutowah.flReleaseTime = EAXAUTOWAH_DEFAULTRELEASETIME;
+ props.mAutowah.lResonance = EAXAUTOWAH_DEFAULTRESONANCE;
+ props.mAutowah.lPeakLevel = EAXAUTOWAH_DEFAULTPEAKLEVEL;
+}
+
+template<>
+void AutowahCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+{
+ switch(call.get_property_id())
+ {
+ case EAXAUTOWAH_NONE: break;
+ case EAXAUTOWAH_ALLPARAMETERS: call.set_value<Exception>(props.mAutowah); break;
+ case EAXAUTOWAH_ATTACKTIME: call.set_value<Exception>(props.mAutowah.flAttackTime); break;
+ case EAXAUTOWAH_RELEASETIME: call.set_value<Exception>(props.mAutowah.flReleaseTime); break;
+ case EAXAUTOWAH_RESONANCE: call.set_value<Exception>(props.mAutowah.lResonance); break;
+ case EAXAUTOWAH_PEAKLEVEL: call.set_value<Exception>(props.mAutowah.lPeakLevel); break;
+ default: fail_unknown_property_id();
+ }
+}
+
+template<>
+void AutowahCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+{
+ switch(call.get_property_id())
+ {
+ case EAXAUTOWAH_NONE: break;
+ case EAXAUTOWAH_ALLPARAMETERS: defer<AllValidator>(call, props.mAutowah); break;
+ case EAXAUTOWAH_ATTACKTIME: defer<AttackTimeValidator>(call, props.mAutowah.flAttackTime); break;
+ case EAXAUTOWAH_RELEASETIME: defer<ReleaseTimeValidator>(call, props.mAutowah.flReleaseTime); break;
+ case EAXAUTOWAH_RESONANCE: defer<ResonanceValidator>(call, props.mAutowah.lResonance); break;
+ case EAXAUTOWAH_PEAKLEVEL: defer<PeakLevelValidator>(call, props.mAutowah.lPeakLevel); break;
+ default: fail_unknown_property_id();
+ }
+}
+
+#endif // ALSOFT_EAX
diff --git a/al/effects/chorus.cpp b/al/effects/chorus.cpp
new file mode 100644
index 00000000..305259a4
--- /dev/null
+++ b/al/effects/chorus.cpp
@@ -0,0 +1,724 @@
+
+#include "config.h"
+
+#include <stdexcept>
+
+#include "AL/al.h"
+#include "AL/efx.h"
+
+#include "alc/effects/base.h"
+#include "aloptional.h"
+#include "core/logging.h"
+#include "effects.h"
+
+#ifdef ALSOFT_EAX
+#include <cassert>
+#include "alnumeric.h"
+#include "al/eax/exception.h"
+#include "al/eax/utils.h"
+#endif // ALSOFT_EAX
+
+
+namespace {
+
+static_assert(ChorusMaxDelay >= AL_CHORUS_MAX_DELAY, "Chorus max delay too small");
+static_assert(FlangerMaxDelay >= AL_FLANGER_MAX_DELAY, "Flanger max delay too small");
+
+static_assert(AL_CHORUS_WAVEFORM_SINUSOID == AL_FLANGER_WAVEFORM_SINUSOID, "Chorus/Flanger waveform value mismatch");
+static_assert(AL_CHORUS_WAVEFORM_TRIANGLE == AL_FLANGER_WAVEFORM_TRIANGLE, "Chorus/Flanger waveform value mismatch");
+
+inline al::optional<ChorusWaveform> WaveformFromEnum(ALenum type)
+{
+ switch(type)
+ {
+ case AL_CHORUS_WAVEFORM_SINUSOID: return ChorusWaveform::Sinusoid;
+ case AL_CHORUS_WAVEFORM_TRIANGLE: return ChorusWaveform::Triangle;
+ }
+ return al::nullopt;
+}
+inline ALenum EnumFromWaveform(ChorusWaveform type)
+{
+ switch(type)
+ {
+ case ChorusWaveform::Sinusoid: return AL_CHORUS_WAVEFORM_SINUSOID;
+ case ChorusWaveform::Triangle: return AL_CHORUS_WAVEFORM_TRIANGLE;
+ }
+ throw std::runtime_error{"Invalid chorus waveform: "+std::to_string(static_cast<int>(type))};
+}
+
+void Chorus_setParami(EffectProps *props, ALenum param, int val)
+{
+ switch(param)
+ {
+ case AL_CHORUS_WAVEFORM:
+ if(auto formopt = WaveformFromEnum(val))
+ props->Chorus.Waveform = *formopt;
+ else
+ throw effect_exception{AL_INVALID_VALUE, "Invalid chorus waveform: 0x%04x", val};
+ break;
+
+ case AL_CHORUS_PHASE:
+ if(!(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_PHASE))
+ throw effect_exception{AL_INVALID_VALUE, "Chorus phase out of range: %d", val};
+ props->Chorus.Phase = val;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param};
+ }
+}
+void Chorus_setParamiv(EffectProps *props, ALenum param, const int *vals)
+{ Chorus_setParami(props, param, vals[0]); }
+void Chorus_setParamf(EffectProps *props, ALenum param, float val)
+{
+ switch(param)
+ {
+ case AL_CHORUS_RATE:
+ if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE))
+ throw effect_exception{AL_INVALID_VALUE, "Chorus rate out of range: %f", val};
+ props->Chorus.Rate = val;
+ break;
+
+ case AL_CHORUS_DEPTH:
+ if(!(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_DEPTH))
+ throw effect_exception{AL_INVALID_VALUE, "Chorus depth out of range: %f", val};
+ props->Chorus.Depth = val;
+ break;
+
+ case AL_CHORUS_FEEDBACK:
+ if(!(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_FEEDBACK))
+ throw effect_exception{AL_INVALID_VALUE, "Chorus feedback out of range: %f", val};
+ props->Chorus.Feedback = val;
+ break;
+
+ case AL_CHORUS_DELAY:
+ if(!(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_DELAY))
+ throw effect_exception{AL_INVALID_VALUE, "Chorus delay out of range: %f", val};
+ props->Chorus.Delay = val;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param};
+ }
+}
+void Chorus_setParamfv(EffectProps *props, ALenum param, const float *vals)
+{ Chorus_setParamf(props, param, vals[0]); }
+
+void Chorus_getParami(const EffectProps *props, ALenum param, int *val)
+{
+ switch(param)
+ {
+ case AL_CHORUS_WAVEFORM:
+ *val = EnumFromWaveform(props->Chorus.Waveform);
+ break;
+
+ case AL_CHORUS_PHASE:
+ *val = props->Chorus.Phase;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param};
+ }
+}
+void Chorus_getParamiv(const EffectProps *props, ALenum param, int *vals)
+{ Chorus_getParami(props, param, vals); }
+void Chorus_getParamf(const EffectProps *props, ALenum param, float *val)
+{
+ switch(param)
+ {
+ case AL_CHORUS_RATE:
+ *val = props->Chorus.Rate;
+ break;
+
+ case AL_CHORUS_DEPTH:
+ *val = props->Chorus.Depth;
+ break;
+
+ case AL_CHORUS_FEEDBACK:
+ *val = props->Chorus.Feedback;
+ break;
+
+ case AL_CHORUS_DELAY:
+ *val = props->Chorus.Delay;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param};
+ }
+}
+void Chorus_getParamfv(const EffectProps *props, ALenum param, float *vals)
+{ Chorus_getParamf(props, param, vals); }
+
+EffectProps genDefaultChorusProps() noexcept
+{
+ EffectProps props{};
+ props.Chorus.Waveform = *WaveformFromEnum(AL_CHORUS_DEFAULT_WAVEFORM);
+ props.Chorus.Phase = AL_CHORUS_DEFAULT_PHASE;
+ props.Chorus.Rate = AL_CHORUS_DEFAULT_RATE;
+ props.Chorus.Depth = AL_CHORUS_DEFAULT_DEPTH;
+ props.Chorus.Feedback = AL_CHORUS_DEFAULT_FEEDBACK;
+ props.Chorus.Delay = AL_CHORUS_DEFAULT_DELAY;
+ return props;
+}
+
+
+void Flanger_setParami(EffectProps *props, ALenum param, int val)
+{
+ switch(param)
+ {
+ case AL_FLANGER_WAVEFORM:
+ if(auto formopt = WaveformFromEnum(val))
+ props->Chorus.Waveform = *formopt;
+ else
+ throw effect_exception{AL_INVALID_VALUE, "Invalid flanger waveform: 0x%04x", val};
+ break;
+
+ case AL_FLANGER_PHASE:
+ if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE))
+ throw effect_exception{AL_INVALID_VALUE, "Flanger phase out of range: %d", val};
+ props->Chorus.Phase = val;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param};
+ }
+}
+void Flanger_setParamiv(EffectProps *props, ALenum param, const int *vals)
+{ Flanger_setParami(props, param, vals[0]); }
+void Flanger_setParamf(EffectProps *props, ALenum param, float val)
+{
+ switch(param)
+ {
+ case AL_FLANGER_RATE:
+ if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE))
+ throw effect_exception{AL_INVALID_VALUE, "Flanger rate out of range: %f", val};
+ props->Chorus.Rate = val;
+ break;
+
+ case AL_FLANGER_DEPTH:
+ if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH))
+ throw effect_exception{AL_INVALID_VALUE, "Flanger depth out of range: %f", val};
+ props->Chorus.Depth = val;
+ break;
+
+ case AL_FLANGER_FEEDBACK:
+ if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK))
+ throw effect_exception{AL_INVALID_VALUE, "Flanger feedback out of range: %f", val};
+ props->Chorus.Feedback = val;
+ break;
+
+ case AL_FLANGER_DELAY:
+ if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY))
+ throw effect_exception{AL_INVALID_VALUE, "Flanger delay out of range: %f", val};
+ props->Chorus.Delay = val;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param};
+ }
+}
+void Flanger_setParamfv(EffectProps *props, ALenum param, const float *vals)
+{ Flanger_setParamf(props, param, vals[0]); }
+
+void Flanger_getParami(const EffectProps *props, ALenum param, int *val)
+{
+ switch(param)
+ {
+ case AL_FLANGER_WAVEFORM:
+ *val = EnumFromWaveform(props->Chorus.Waveform);
+ break;
+
+ case AL_FLANGER_PHASE:
+ *val = props->Chorus.Phase;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param};
+ }
+}
+void Flanger_getParamiv(const EffectProps *props, ALenum param, int *vals)
+{ Flanger_getParami(props, param, vals); }
+void Flanger_getParamf(const EffectProps *props, ALenum param, float *val)
+{
+ switch(param)
+ {
+ case AL_FLANGER_RATE:
+ *val = props->Chorus.Rate;
+ break;
+
+ case AL_FLANGER_DEPTH:
+ *val = props->Chorus.Depth;
+ break;
+
+ case AL_FLANGER_FEEDBACK:
+ *val = props->Chorus.Feedback;
+ break;
+
+ case AL_FLANGER_DELAY:
+ *val = props->Chorus.Delay;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param};
+ }
+}
+void Flanger_getParamfv(const EffectProps *props, ALenum param, float *vals)
+{ Flanger_getParamf(props, param, vals); }
+
+EffectProps genDefaultFlangerProps() noexcept
+{
+ EffectProps props{};
+ props.Chorus.Waveform = *WaveformFromEnum(AL_FLANGER_DEFAULT_WAVEFORM);
+ props.Chorus.Phase = AL_FLANGER_DEFAULT_PHASE;
+ props.Chorus.Rate = AL_FLANGER_DEFAULT_RATE;
+ props.Chorus.Depth = AL_FLANGER_DEFAULT_DEPTH;
+ props.Chorus.Feedback = AL_FLANGER_DEFAULT_FEEDBACK;
+ props.Chorus.Delay = AL_FLANGER_DEFAULT_DELAY;
+ return props;
+}
+
+} // namespace
+
+DEFINE_ALEFFECT_VTABLE(Chorus);
+
+const EffectProps ChorusEffectProps{genDefaultChorusProps()};
+
+DEFINE_ALEFFECT_VTABLE(Flanger);
+
+const EffectProps FlangerEffectProps{genDefaultFlangerProps()};
+
+
+#ifdef ALSOFT_EAX
+namespace {
+
+struct EaxChorusTraits {
+ using Props = EAXCHORUSPROPERTIES;
+ using Committer = EaxChorusCommitter;
+ static constexpr auto Field = &EaxEffectProps::mChorus;
+
+ static constexpr auto eax_effect_type() { return EaxEffectType::Chorus; }
+ static constexpr auto efx_effect() { return AL_EFFECT_CHORUS; }
+
+ static constexpr auto eax_none_param_id() { return EAXCHORUS_NONE; }
+ static constexpr auto eax_allparameters_param_id() { return EAXCHORUS_ALLPARAMETERS; }
+ static constexpr auto eax_waveform_param_id() { return EAXCHORUS_WAVEFORM; }
+ static constexpr auto eax_phase_param_id() { return EAXCHORUS_PHASE; }
+ static constexpr auto eax_rate_param_id() { return EAXCHORUS_RATE; }
+ static constexpr auto eax_depth_param_id() { return EAXCHORUS_DEPTH; }
+ static constexpr auto eax_feedback_param_id() { return EAXCHORUS_FEEDBACK; }
+ static constexpr auto eax_delay_param_id() { return EAXCHORUS_DELAY; }
+
+ static constexpr auto eax_min_waveform() { return EAXCHORUS_MINWAVEFORM; }
+ static constexpr auto eax_min_phase() { return EAXCHORUS_MINPHASE; }
+ static constexpr auto eax_min_rate() { return EAXCHORUS_MINRATE; }
+ static constexpr auto eax_min_depth() { return EAXCHORUS_MINDEPTH; }
+ static constexpr auto eax_min_feedback() { return EAXCHORUS_MINFEEDBACK; }
+ static constexpr auto eax_min_delay() { return EAXCHORUS_MINDELAY; }
+
+ static constexpr auto eax_max_waveform() { return EAXCHORUS_MAXWAVEFORM; }
+ static constexpr auto eax_max_phase() { return EAXCHORUS_MAXPHASE; }
+ static constexpr auto eax_max_rate() { return EAXCHORUS_MAXRATE; }
+ static constexpr auto eax_max_depth() { return EAXCHORUS_MAXDEPTH; }
+ static constexpr auto eax_max_feedback() { return EAXCHORUS_MAXFEEDBACK; }
+ static constexpr auto eax_max_delay() { return EAXCHORUS_MAXDELAY; }
+
+ static constexpr auto eax_default_waveform() { return EAXCHORUS_DEFAULTWAVEFORM; }
+ static constexpr auto eax_default_phase() { return EAXCHORUS_DEFAULTPHASE; }
+ static constexpr auto eax_default_rate() { return EAXCHORUS_DEFAULTRATE; }
+ static constexpr auto eax_default_depth() { return EAXCHORUS_DEFAULTDEPTH; }
+ static constexpr auto eax_default_feedback() { return EAXCHORUS_DEFAULTFEEDBACK; }
+ static constexpr auto eax_default_delay() { return EAXCHORUS_DEFAULTDELAY; }
+
+ static constexpr auto efx_min_waveform() { return AL_CHORUS_MIN_WAVEFORM; }
+ static constexpr auto efx_min_phase() { return AL_CHORUS_MIN_PHASE; }
+ static constexpr auto efx_min_rate() { return AL_CHORUS_MIN_RATE; }
+ static constexpr auto efx_min_depth() { return AL_CHORUS_MIN_DEPTH; }
+ static constexpr auto efx_min_feedback() { return AL_CHORUS_MIN_FEEDBACK; }
+ static constexpr auto efx_min_delay() { return AL_CHORUS_MIN_DELAY; }
+
+ static constexpr auto efx_max_waveform() { return AL_CHORUS_MAX_WAVEFORM; }
+ static constexpr auto efx_max_phase() { return AL_CHORUS_MAX_PHASE; }
+ static constexpr auto efx_max_rate() { return AL_CHORUS_MAX_RATE; }
+ static constexpr auto efx_max_depth() { return AL_CHORUS_MAX_DEPTH; }
+ static constexpr auto efx_max_feedback() { return AL_CHORUS_MAX_FEEDBACK; }
+ static constexpr auto efx_max_delay() { return AL_CHORUS_MAX_DELAY; }
+
+ static constexpr auto efx_default_waveform() { return AL_CHORUS_DEFAULT_WAVEFORM; }
+ static constexpr auto efx_default_phase() { return AL_CHORUS_DEFAULT_PHASE; }
+ static constexpr auto efx_default_rate() { return AL_CHORUS_DEFAULT_RATE; }
+ static constexpr auto efx_default_depth() { return AL_CHORUS_DEFAULT_DEPTH; }
+ static constexpr auto efx_default_feedback() { return AL_CHORUS_DEFAULT_FEEDBACK; }
+ static constexpr auto efx_default_delay() { return AL_CHORUS_DEFAULT_DELAY; }
+
+ static ChorusWaveform eax_waveform(unsigned long type)
+ {
+ if(type == EAX_CHORUS_SINUSOID) return ChorusWaveform::Sinusoid;
+ if(type == EAX_CHORUS_TRIANGLE) return ChorusWaveform::Triangle;
+ return ChorusWaveform::Sinusoid;
+ }
+}; // EaxChorusTraits
+
+struct EaxFlangerTraits {
+ using Props = EAXFLANGERPROPERTIES;
+ using Committer = EaxFlangerCommitter;
+ static constexpr auto Field = &EaxEffectProps::mFlanger;
+
+ static constexpr auto eax_effect_type() { return EaxEffectType::Flanger; }
+ static constexpr auto efx_effect() { return AL_EFFECT_FLANGER; }
+
+ static constexpr auto eax_none_param_id() { return EAXFLANGER_NONE; }
+ static constexpr auto eax_allparameters_param_id() { return EAXFLANGER_ALLPARAMETERS; }
+ static constexpr auto eax_waveform_param_id() { return EAXFLANGER_WAVEFORM; }
+ static constexpr auto eax_phase_param_id() { return EAXFLANGER_PHASE; }
+ static constexpr auto eax_rate_param_id() { return EAXFLANGER_RATE; }
+ static constexpr auto eax_depth_param_id() { return EAXFLANGER_DEPTH; }
+ static constexpr auto eax_feedback_param_id() { return EAXFLANGER_FEEDBACK; }
+ static constexpr auto eax_delay_param_id() { return EAXFLANGER_DELAY; }
+
+ static constexpr auto eax_min_waveform() { return EAXFLANGER_MINWAVEFORM; }
+ static constexpr auto eax_min_phase() { return EAXFLANGER_MINPHASE; }
+ static constexpr auto eax_min_rate() { return EAXFLANGER_MINRATE; }
+ static constexpr auto eax_min_depth() { return EAXFLANGER_MINDEPTH; }
+ static constexpr auto eax_min_feedback() { return EAXFLANGER_MINFEEDBACK; }
+ static constexpr auto eax_min_delay() { return EAXFLANGER_MINDELAY; }
+
+ static constexpr auto eax_max_waveform() { return EAXFLANGER_MAXWAVEFORM; }
+ static constexpr auto eax_max_phase() { return EAXFLANGER_MAXPHASE; }
+ static constexpr auto eax_max_rate() { return EAXFLANGER_MAXRATE; }
+ static constexpr auto eax_max_depth() { return EAXFLANGER_MAXDEPTH; }
+ static constexpr auto eax_max_feedback() { return EAXFLANGER_MAXFEEDBACK; }
+ static constexpr auto eax_max_delay() { return EAXFLANGER_MAXDELAY; }
+
+ static constexpr auto eax_default_waveform() { return EAXFLANGER_DEFAULTWAVEFORM; }
+ static constexpr auto eax_default_phase() { return EAXFLANGER_DEFAULTPHASE; }
+ static constexpr auto eax_default_rate() { return EAXFLANGER_DEFAULTRATE; }
+ static constexpr auto eax_default_depth() { return EAXFLANGER_DEFAULTDEPTH; }
+ static constexpr auto eax_default_feedback() { return EAXFLANGER_DEFAULTFEEDBACK; }
+ static constexpr auto eax_default_delay() { return EAXFLANGER_DEFAULTDELAY; }
+
+ static constexpr auto efx_min_waveform() { return AL_FLANGER_MIN_WAVEFORM; }
+ static constexpr auto efx_min_phase() { return AL_FLANGER_MIN_PHASE; }
+ static constexpr auto efx_min_rate() { return AL_FLANGER_MIN_RATE; }
+ static constexpr auto efx_min_depth() { return AL_FLANGER_MIN_DEPTH; }
+ static constexpr auto efx_min_feedback() { return AL_FLANGER_MIN_FEEDBACK; }
+ static constexpr auto efx_min_delay() { return AL_FLANGER_MIN_DELAY; }
+
+ static constexpr auto efx_max_waveform() { return AL_FLANGER_MAX_WAVEFORM; }
+ static constexpr auto efx_max_phase() { return AL_FLANGER_MAX_PHASE; }
+ static constexpr auto efx_max_rate() { return AL_FLANGER_MAX_RATE; }
+ static constexpr auto efx_max_depth() { return AL_FLANGER_MAX_DEPTH; }
+ static constexpr auto efx_max_feedback() { return AL_FLANGER_MAX_FEEDBACK; }
+ static constexpr auto efx_max_delay() { return AL_FLANGER_MAX_DELAY; }
+
+ static constexpr auto efx_default_waveform() { return AL_FLANGER_DEFAULT_WAVEFORM; }
+ static constexpr auto efx_default_phase() { return AL_FLANGER_DEFAULT_PHASE; }
+ static constexpr auto efx_default_rate() { return AL_FLANGER_DEFAULT_RATE; }
+ static constexpr auto efx_default_depth() { return AL_FLANGER_DEFAULT_DEPTH; }
+ static constexpr auto efx_default_feedback() { return AL_FLANGER_DEFAULT_FEEDBACK; }
+ static constexpr auto efx_default_delay() { return AL_FLANGER_DEFAULT_DELAY; }
+
+ static ChorusWaveform eax_waveform(unsigned long type)
+ {
+ if(type == EAX_FLANGER_SINUSOID) return ChorusWaveform::Sinusoid;
+ if(type == EAX_FLANGER_TRIANGLE) return ChorusWaveform::Triangle;
+ return ChorusWaveform::Sinusoid;
+ }
+}; // EaxFlangerTraits
+
+template<typename TTraits>
+struct ChorusFlangerEffect {
+ using Traits = TTraits;
+ using Committer = typename Traits::Committer;
+ using Exception = typename Committer::Exception;
+
+ static constexpr auto Field = Traits::Field;
+
+ struct WaveformValidator {
+ void operator()(unsigned long ulWaveform) const
+ {
+ eax_validate_range<Exception>(
+ "Waveform",
+ ulWaveform,
+ Traits::eax_min_waveform(),
+ Traits::eax_max_waveform());
+ }
+ }; // WaveformValidator
+
+ struct PhaseValidator {
+ void operator()(long lPhase) const
+ {
+ eax_validate_range<Exception>(
+ "Phase",
+ lPhase,
+ Traits::eax_min_phase(),
+ Traits::eax_max_phase());
+ }
+ }; // PhaseValidator
+
+ struct RateValidator {
+ void operator()(float flRate) const
+ {
+ eax_validate_range<Exception>(
+ "Rate",
+ flRate,
+ Traits::eax_min_rate(),
+ Traits::eax_max_rate());
+ }
+ }; // RateValidator
+
+ struct DepthValidator {
+ void operator()(float flDepth) const
+ {
+ eax_validate_range<Exception>(
+ "Depth",
+ flDepth,
+ Traits::eax_min_depth(),
+ Traits::eax_max_depth());
+ }
+ }; // DepthValidator
+
+ struct FeedbackValidator {
+ void operator()(float flFeedback) const
+ {
+ eax_validate_range<Exception>(
+ "Feedback",
+ flFeedback,
+ Traits::eax_min_feedback(),
+ Traits::eax_max_feedback());
+ }
+ }; // FeedbackValidator
+
+ struct DelayValidator {
+ void operator()(float flDelay) const
+ {
+ eax_validate_range<Exception>(
+ "Delay",
+ flDelay,
+ Traits::eax_min_delay(),
+ Traits::eax_max_delay());
+ }
+ }; // DelayValidator
+
+ struct AllValidator {
+ void operator()(const typename Traits::Props& all) const
+ {
+ WaveformValidator{}(all.ulWaveform);
+ PhaseValidator{}(all.lPhase);
+ RateValidator{}(all.flRate);
+ DepthValidator{}(all.flDepth);
+ FeedbackValidator{}(all.flFeedback);
+ DelayValidator{}(all.flDelay);
+ }
+ }; // AllValidator
+
+public:
+ static void SetDefaults(EaxEffectProps &props)
+ {
+ auto&& all = props.*Field;
+ props.mType = Traits::eax_effect_type();
+ all.ulWaveform = Traits::eax_default_waveform();
+ all.lPhase = Traits::eax_default_phase();
+ all.flRate = Traits::eax_default_rate();
+ all.flDepth = Traits::eax_default_depth();
+ all.flFeedback = Traits::eax_default_feedback();
+ all.flDelay = Traits::eax_default_delay();
+ }
+
+
+ static void Get(const EaxCall &call, const EaxEffectProps &props)
+ {
+ auto&& all = props.*Field;
+ switch(call.get_property_id())
+ {
+ case Traits::eax_none_param_id():
+ break;
+
+ case Traits::eax_allparameters_param_id():
+ call.template set_value<Exception>(all);
+ break;
+
+ case Traits::eax_waveform_param_id():
+ call.template set_value<Exception>(all.ulWaveform);
+ break;
+
+ case Traits::eax_phase_param_id():
+ call.template set_value<Exception>(all.lPhase);
+ break;
+
+ case Traits::eax_rate_param_id():
+ call.template set_value<Exception>(all.flRate);
+ break;
+
+ case Traits::eax_depth_param_id():
+ call.template set_value<Exception>(all.flDepth);
+ break;
+
+ case Traits::eax_feedback_param_id():
+ call.template set_value<Exception>(all.flFeedback);
+ break;
+
+ case Traits::eax_delay_param_id():
+ call.template set_value<Exception>(all.flDelay);
+ break;
+
+ default:
+ Committer::fail_unknown_property_id();
+ }
+ }
+
+ static void Set(const EaxCall &call, EaxEffectProps &props)
+ {
+ auto&& all = props.*Field;
+ switch(call.get_property_id())
+ {
+ case Traits::eax_none_param_id():
+ break;
+
+ case Traits::eax_allparameters_param_id():
+ Committer::template defer<AllValidator>(call, all);
+ break;
+
+ case Traits::eax_waveform_param_id():
+ Committer::template defer<WaveformValidator>(call, all.ulWaveform);
+ break;
+
+ case Traits::eax_phase_param_id():
+ Committer::template defer<PhaseValidator>(call, all.lPhase);
+ break;
+
+ case Traits::eax_rate_param_id():
+ Committer::template defer<RateValidator>(call, all.flRate);
+ break;
+
+ case Traits::eax_depth_param_id():
+ Committer::template defer<DepthValidator>(call, all.flDepth);
+ break;
+
+ case Traits::eax_feedback_param_id():
+ Committer::template defer<FeedbackValidator>(call, all.flFeedback);
+ break;
+
+ case Traits::eax_delay_param_id():
+ Committer::template defer<DelayValidator>(call, all.flDelay);
+ break;
+
+ default:
+ Committer::fail_unknown_property_id();
+ }
+ }
+
+ static bool Commit(const EaxEffectProps &props, EaxEffectProps &props_, EffectProps &al_props_)
+ {
+ if(props.mType == props_.mType)
+ {
+ auto&& src = props_.*Field;
+ auto&& dst = props.*Field;
+ if(dst.ulWaveform == src.ulWaveform && dst.lPhase == src.lPhase
+ && dst.flRate == src.flRate && dst.flDepth == src.flDepth
+ && dst.flFeedback == src.flFeedback && dst.flDelay == src.flDelay)
+ return false;
+ }
+
+ props_ = props;
+ auto&& dst = props.*Field;
+
+ al_props_.Chorus.Waveform = Traits::eax_waveform(dst.ulWaveform);
+ al_props_.Chorus.Phase = static_cast<int>(dst.lPhase);
+ al_props_.Chorus.Rate = dst.flRate;
+ al_props_.Chorus.Depth = dst.flDepth;
+ al_props_.Chorus.Feedback = dst.flFeedback;
+ al_props_.Chorus.Delay = dst.flDelay;
+
+ return true;
+ }
+}; // EaxChorusFlangerEffect
+
+
+using ChorusCommitter = EaxCommitter<EaxChorusCommitter>;
+using FlangerCommitter = EaxCommitter<EaxFlangerCommitter>;
+
+} // namespace
+
+template<>
+struct ChorusCommitter::Exception : public EaxException
+{
+ explicit Exception(const char *message) : EaxException{"EAX_CHORUS_EFFECT", message}
+ { }
+};
+
+template<>
+[[noreturn]] void ChorusCommitter::fail(const char *message)
+{
+ throw Exception{message};
+}
+
+template<>
+bool ChorusCommitter::commit(const EaxEffectProps &props)
+{
+ using Committer = ChorusFlangerEffect<EaxChorusTraits>;
+ return Committer::Commit(props, mEaxProps, mAlProps);
+}
+
+template<>
+void ChorusCommitter::SetDefaults(EaxEffectProps &props)
+{
+ using Committer = ChorusFlangerEffect<EaxChorusTraits>;
+ Committer::SetDefaults(props);
+}
+
+template<>
+void ChorusCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+{
+ using Committer = ChorusFlangerEffect<EaxChorusTraits>;
+ Committer::Get(call, props);
+}
+
+template<>
+void ChorusCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+{
+ using Committer = ChorusFlangerEffect<EaxChorusTraits>;
+ Committer::Set(call, props);
+}
+
+template<>
+struct FlangerCommitter::Exception : public EaxException
+{
+ explicit Exception(const char *message) : EaxException{"EAX_FLANGER_EFFECT", message}
+ { }
+};
+
+template<>
+[[noreturn]] void FlangerCommitter::fail(const char *message)
+{
+ throw Exception{message};
+}
+
+template<>
+bool FlangerCommitter::commit(const EaxEffectProps &props)
+{
+ using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
+ return Committer::Commit(props, mEaxProps, mAlProps);
+}
+
+template<>
+void FlangerCommitter::SetDefaults(EaxEffectProps &props)
+{
+ using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
+ Committer::SetDefaults(props);
+}
+
+template<>
+void FlangerCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+{
+ using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
+ Committer::Get(call, props);
+}
+
+template<>
+void FlangerCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+{
+ using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
+ Committer::Set(call, props);
+}
+
+#endif // ALSOFT_EAX
diff --git a/al/effects/compressor.cpp b/al/effects/compressor.cpp
new file mode 100644
index 00000000..a4aa8e77
--- /dev/null
+++ b/al/effects/compressor.cpp
@@ -0,0 +1,162 @@
+
+#include "config.h"
+
+#include "AL/al.h"
+#include "AL/efx.h"
+
+#include "alc/effects/base.h"
+#include "effects.h"
+
+#ifdef ALSOFT_EAX
+#include "alnumeric.h"
+#include "al/eax/exception.h"
+#include "al/eax/utils.h"
+#endif // ALSOFT_EAX
+
+
+namespace {
+
+void Compressor_setParami(EffectProps *props, ALenum param, int val)
+{
+ switch(param)
+ {
+ case AL_COMPRESSOR_ONOFF:
+ if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF))
+ throw effect_exception{AL_INVALID_VALUE, "Compressor state out of range"};
+ props->Compressor.OnOff = (val != AL_FALSE);
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
+ param};
+ }
+}
+void Compressor_setParamiv(EffectProps *props, ALenum param, const int *vals)
+{ Compressor_setParami(props, param, vals[0]); }
+void Compressor_setParamf(EffectProps*, ALenum param, float)
+{ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; }
+void Compressor_setParamfv(EffectProps*, ALenum param, const float*)
+{
+ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x",
+ param};
+}
+
+void Compressor_getParami(const EffectProps *props, ALenum param, int *val)
+{
+ switch(param)
+ {
+ case AL_COMPRESSOR_ONOFF:
+ *val = props->Compressor.OnOff;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
+ param};
+ }
+}
+void Compressor_getParamiv(const EffectProps *props, ALenum param, int *vals)
+{ Compressor_getParami(props, param, vals); }
+void Compressor_getParamf(const EffectProps*, ALenum param, float*)
+{ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; }
+void Compressor_getParamfv(const EffectProps*, ALenum param, float*)
+{
+ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x",
+ param};
+}
+
+EffectProps genDefaultProps() noexcept
+{
+ EffectProps props{};
+ props.Compressor.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF;
+ return props;
+}
+
+} // namespace
+
+DEFINE_ALEFFECT_VTABLE(Compressor);
+
+const EffectProps CompressorEffectProps{genDefaultProps()};
+
+#ifdef ALSOFT_EAX
+namespace {
+
+using CompressorCommitter = EaxCommitter<EaxCompressorCommitter>;
+
+struct OnOffValidator {
+ void operator()(unsigned long ulOnOff) const
+ {
+ eax_validate_range<CompressorCommitter::Exception>(
+ "On-Off",
+ ulOnOff,
+ EAXAGCCOMPRESSOR_MINONOFF,
+ EAXAGCCOMPRESSOR_MAXONOFF);
+ }
+}; // OnOffValidator
+
+struct AllValidator {
+ void operator()(const EAXAGCCOMPRESSORPROPERTIES& all) const
+ {
+ OnOffValidator{}(all.ulOnOff);
+ }
+}; // AllValidator
+
+} // namespace
+
+template<>
+struct CompressorCommitter::Exception : public EaxException
+{
+ explicit Exception(const char *message) : EaxException{"EAX_CHORUS_EFFECT", message}
+ { }
+};
+
+template<>
+[[noreturn]] void CompressorCommitter::fail(const char *message)
+{
+ throw Exception{message};
+}
+
+template<>
+bool CompressorCommitter::commit(const EaxEffectProps &props)
+{
+ if(props.mType == mEaxProps.mType
+ && props.mCompressor.ulOnOff == mEaxProps.mCompressor.ulOnOff)
+ return false;
+
+ mEaxProps = props;
+
+ mAlProps.Compressor.OnOff = (props.mCompressor.ulOnOff != 0);
+ return true;
+}
+
+template<>
+void CompressorCommitter::SetDefaults(EaxEffectProps &props)
+{
+ props.mType = EaxEffectType::Compressor;
+ props.mCompressor.ulOnOff = EAXAGCCOMPRESSOR_DEFAULTONOFF;
+}
+
+template<>
+void CompressorCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+{
+ switch(call.get_property_id())
+ {
+ case EAXAGCCOMPRESSOR_NONE: break;
+ case EAXAGCCOMPRESSOR_ALLPARAMETERS: call.set_value<Exception>(props.mCompressor); break;
+ case EAXAGCCOMPRESSOR_ONOFF: call.set_value<Exception>(props.mCompressor.ulOnOff); break;
+ default: fail_unknown_property_id();
+ }
+}
+
+template<>
+void CompressorCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+{
+ switch(call.get_property_id())
+ {
+ case EAXAGCCOMPRESSOR_NONE: break;
+ case EAXAGCCOMPRESSOR_ALLPARAMETERS: defer<AllValidator>(call, props.mCompressor); break;
+ case EAXAGCCOMPRESSOR_ONOFF: defer<OnOffValidator>(call, props.mCompressor.ulOnOff); break;
+ default: fail_unknown_property_id();
+ }
+}
+
+#endif // ALSOFT_EAX
diff --git a/al/effects/convolution.cpp b/al/effects/convolution.cpp
new file mode 100644
index 00000000..8e850fd3
--- /dev/null
+++ b/al/effects/convolution.cpp
@@ -0,0 +1,93 @@
+
+#include "config.h"
+
+#include "AL/al.h"
+#include "alc/inprogext.h"
+
+#include "alc/effects/base.h"
+#include "effects.h"
+
+
+namespace {
+
+void Convolution_setParami(EffectProps* /*props*/, ALenum param, int /*val*/)
+{
+ switch(param)
+ {
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
+ param};
+ }
+}
+void Convolution_setParamiv(EffectProps *props, ALenum param, const int *vals)
+{
+ switch(param)
+ {
+ default:
+ Convolution_setParami(props, param, vals[0]);
+ }
+}
+void Convolution_setParamf(EffectProps* /*props*/, ALenum param, float /*val*/)
+{
+ switch(param)
+ {
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
+ param};
+ }
+}
+void Convolution_setParamfv(EffectProps *props, ALenum param, const float *vals)
+{
+ switch(param)
+ {
+ default:
+ Convolution_setParamf(props, param, vals[0]);
+ }
+}
+
+void Convolution_getParami(const EffectProps* /*props*/, ALenum param, int* /*val*/)
+{
+ switch(param)
+ {
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
+ param};
+ }
+}
+void Convolution_getParamiv(const EffectProps *props, ALenum param, int *vals)
+{
+ switch(param)
+ {
+ default:
+ Convolution_getParami(props, param, vals);
+ }
+}
+void Convolution_getParamf(const EffectProps* /*props*/, ALenum param, float* /*val*/)
+{
+ switch(param)
+ {
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
+ param};
+ }
+}
+void Convolution_getParamfv(const EffectProps *props, ALenum param, float *vals)
+{
+ switch(param)
+ {
+ default:
+ Convolution_getParamf(props, param, vals);
+ }
+}
+
+EffectProps genDefaultProps() noexcept
+{
+ EffectProps props{};
+ return props;
+}
+
+} // namespace
+
+DEFINE_ALEFFECT_VTABLE(Convolution);
+
+const EffectProps ConvolutionEffectProps{genDefaultProps()};
diff --git a/al/effects/dedicated.cpp b/al/effects/dedicated.cpp
new file mode 100644
index 00000000..db57003c
--- /dev/null
+++ b/al/effects/dedicated.cpp
@@ -0,0 +1,72 @@
+
+#include "config.h"
+
+#include <cmath>
+
+#include "AL/al.h"
+#include "AL/alext.h"
+
+#include "alc/effects/base.h"
+#include "effects.h"
+
+
+namespace {
+
+void Dedicated_setParami(EffectProps*, ALenum param, int)
+{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
+void Dedicated_setParamiv(EffectProps*, ALenum param, const int*)
+{
+ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
+ param};
+}
+void Dedicated_setParamf(EffectProps *props, ALenum param, float val)
+{
+ switch(param)
+ {
+ case AL_DEDICATED_GAIN:
+ if(!(val >= 0.0f && std::isfinite(val)))
+ throw effect_exception{AL_INVALID_VALUE, "Dedicated gain out of range"};
+ props->Dedicated.Gain = val;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
+ }
+}
+void Dedicated_setParamfv(EffectProps *props, ALenum param, const float *vals)
+{ Dedicated_setParamf(props, param, vals[0]); }
+
+void Dedicated_getParami(const EffectProps*, ALenum param, int*)
+{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
+void Dedicated_getParamiv(const EffectProps*, ALenum param, int*)
+{
+ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
+ param};
+}
+void Dedicated_getParamf(const EffectProps *props, ALenum param, float *val)
+{
+ switch(param)
+ {
+ case AL_DEDICATED_GAIN:
+ *val = props->Dedicated.Gain;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
+ }
+}
+void Dedicated_getParamfv(const EffectProps *props, ALenum param, float *vals)
+{ Dedicated_getParamf(props, param, vals); }
+
+EffectProps genDefaultProps() noexcept
+{
+ EffectProps props{};
+ props.Dedicated.Gain = 1.0f;
+ return props;
+}
+
+} // namespace
+
+DEFINE_ALEFFECT_VTABLE(Dedicated);
+
+const EffectProps DedicatedEffectProps{genDefaultProps()};
diff --git a/al/effects/distortion.cpp b/al/effects/distortion.cpp
new file mode 100644
index 00000000..ee298ddf
--- /dev/null
+++ b/al/effects/distortion.cpp
@@ -0,0 +1,271 @@
+
+#include "config.h"
+
+#include "AL/al.h"
+#include "AL/efx.h"
+
+#include "alc/effects/base.h"
+#include "effects.h"
+
+#ifdef ALSOFT_EAX
+#include "alnumeric.h"
+#include "al/eax/exception.h"
+#include "al/eax/utils.h"
+#endif // ALSOFT_EAX
+
+
+namespace {
+
+void Distortion_setParami(EffectProps*, ALenum param, int)
+{ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; }
+void Distortion_setParamiv(EffectProps*, ALenum param, const int*)
+{
+ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x",
+ param};
+}
+void Distortion_setParamf(EffectProps *props, ALenum param, float val)
+{
+ switch(param)
+ {
+ case AL_DISTORTION_EDGE:
+ if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE))
+ throw effect_exception{AL_INVALID_VALUE, "Distortion edge out of range"};
+ props->Distortion.Edge = val;
+ break;
+
+ case AL_DISTORTION_GAIN:
+ if(!(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN))
+ throw effect_exception{AL_INVALID_VALUE, "Distortion gain out of range"};
+ props->Distortion.Gain = val;
+ break;
+
+ case AL_DISTORTION_LOWPASS_CUTOFF:
+ if(!(val >= AL_DISTORTION_MIN_LOWPASS_CUTOFF && val <= AL_DISTORTION_MAX_LOWPASS_CUTOFF))
+ throw effect_exception{AL_INVALID_VALUE, "Distortion low-pass cutoff out of range"};
+ props->Distortion.LowpassCutoff = val;
+ break;
+
+ case AL_DISTORTION_EQCENTER:
+ if(!(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER))
+ throw effect_exception{AL_INVALID_VALUE, "Distortion EQ center out of range"};
+ props->Distortion.EQCenter = val;
+ break;
+
+ case AL_DISTORTION_EQBANDWIDTH:
+ if(!(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH))
+ throw effect_exception{AL_INVALID_VALUE, "Distortion EQ bandwidth out of range"};
+ props->Distortion.EQBandwidth = val;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param};
+ }
+}
+void Distortion_setParamfv(EffectProps *props, ALenum param, const float *vals)
+{ Distortion_setParamf(props, param, vals[0]); }
+
+void Distortion_getParami(const EffectProps*, ALenum param, int*)
+{ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; }
+void Distortion_getParamiv(const EffectProps*, ALenum param, int*)
+{
+ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x",
+ param};
+}
+void Distortion_getParamf(const EffectProps *props, ALenum param, float *val)
+{
+ switch(param)
+ {
+ case AL_DISTORTION_EDGE:
+ *val = props->Distortion.Edge;
+ break;
+
+ case AL_DISTORTION_GAIN:
+ *val = props->Distortion.Gain;
+ break;
+
+ case AL_DISTORTION_LOWPASS_CUTOFF:
+ *val = props->Distortion.LowpassCutoff;
+ break;
+
+ case AL_DISTORTION_EQCENTER:
+ *val = props->Distortion.EQCenter;
+ break;
+
+ case AL_DISTORTION_EQBANDWIDTH:
+ *val = props->Distortion.EQBandwidth;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param};
+ }
+}
+void Distortion_getParamfv(const EffectProps *props, ALenum param, float *vals)
+{ Distortion_getParamf(props, param, vals); }
+
+EffectProps genDefaultProps() noexcept
+{
+ EffectProps props{};
+ props.Distortion.Edge = AL_DISTORTION_DEFAULT_EDGE;
+ props.Distortion.Gain = AL_DISTORTION_DEFAULT_GAIN;
+ props.Distortion.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF;
+ props.Distortion.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER;
+ props.Distortion.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH;
+ return props;
+}
+
+} // namespace
+
+DEFINE_ALEFFECT_VTABLE(Distortion);
+
+const EffectProps DistortionEffectProps{genDefaultProps()};
+
+#ifdef ALSOFT_EAX
+namespace {
+
+using DistortionCommitter = EaxCommitter<EaxDistortionCommitter>;
+
+struct EdgeValidator {
+ void operator()(float flEdge) const
+ {
+ eax_validate_range<DistortionCommitter::Exception>(
+ "Edge",
+ flEdge,
+ EAXDISTORTION_MINEDGE,
+ EAXDISTORTION_MAXEDGE);
+ }
+}; // EdgeValidator
+
+struct GainValidator {
+ void operator()(long lGain) const
+ {
+ eax_validate_range<DistortionCommitter::Exception>(
+ "Gain",
+ lGain,
+ EAXDISTORTION_MINGAIN,
+ EAXDISTORTION_MAXGAIN);
+ }
+}; // GainValidator
+
+struct LowPassCutOffValidator {
+ void operator()(float flLowPassCutOff) const
+ {
+ eax_validate_range<DistortionCommitter::Exception>(
+ "Low-pass Cut-off",
+ flLowPassCutOff,
+ EAXDISTORTION_MINLOWPASSCUTOFF,
+ EAXDISTORTION_MAXLOWPASSCUTOFF);
+ }
+}; // LowPassCutOffValidator
+
+struct EqCenterValidator {
+ void operator()(float flEQCenter) const
+ {
+ eax_validate_range<DistortionCommitter::Exception>(
+ "EQ Center",
+ flEQCenter,
+ EAXDISTORTION_MINEQCENTER,
+ EAXDISTORTION_MAXEQCENTER);
+ }
+}; // EqCenterValidator
+
+struct EqBandwidthValidator {
+ void operator()(float flEQBandwidth) const
+ {
+ eax_validate_range<DistortionCommitter::Exception>(
+ "EQ Bandwidth",
+ flEQBandwidth,
+ EAXDISTORTION_MINEQBANDWIDTH,
+ EAXDISTORTION_MAXEQBANDWIDTH);
+ }
+}; // EqBandwidthValidator
+
+struct AllValidator {
+ void operator()(const EAXDISTORTIONPROPERTIES& all) const
+ {
+ EdgeValidator{}(all.flEdge);
+ GainValidator{}(all.lGain);
+ LowPassCutOffValidator{}(all.flLowPassCutOff);
+ EqCenterValidator{}(all.flEQCenter);
+ EqBandwidthValidator{}(all.flEQBandwidth);
+ }
+}; // AllValidator
+
+} // namespace
+
+template<>
+struct DistortionCommitter::Exception : public EaxException {
+ explicit Exception(const char *message) : EaxException{"EAX_DISTORTION_EFFECT", message}
+ { }
+};
+
+template<>
+[[noreturn]] void DistortionCommitter::fail(const char *message)
+{
+ throw Exception{message};
+}
+
+template<>
+bool DistortionCommitter::commit(const EaxEffectProps &props)
+{
+ if(props.mType == mEaxProps.mType && mEaxProps.mDistortion.flEdge == props.mDistortion.flEdge
+ && mEaxProps.mDistortion.lGain == props.mDistortion.lGain
+ && mEaxProps.mDistortion.flLowPassCutOff == props.mDistortion.flLowPassCutOff
+ && mEaxProps.mDistortion.flEQCenter == props.mDistortion.flEQCenter
+ && mEaxProps.mDistortion.flEQBandwidth == props.mDistortion.flEQBandwidth)
+ return false;
+
+ mEaxProps = props;
+
+ mAlProps.Distortion.Edge = props.mDistortion.flEdge;
+ mAlProps.Distortion.Gain = level_mb_to_gain(static_cast<float>(props.mDistortion.lGain));
+ mAlProps.Distortion.LowpassCutoff = props.mDistortion.flLowPassCutOff;
+ mAlProps.Distortion.EQCenter = props.mDistortion.flEQCenter;
+ mAlProps.Distortion.EQBandwidth = props.mDistortion.flEdge;
+
+ return true;
+}
+
+template<>
+void DistortionCommitter::SetDefaults(EaxEffectProps &props)
+{
+ props.mType = EaxEffectType::Distortion;
+ props.mDistortion.flEdge = EAXDISTORTION_DEFAULTEDGE;
+ props.mDistortion.lGain = EAXDISTORTION_DEFAULTGAIN;
+ props.mDistortion.flLowPassCutOff = EAXDISTORTION_DEFAULTLOWPASSCUTOFF;
+ props.mDistortion.flEQCenter = EAXDISTORTION_DEFAULTEQCENTER;
+ props.mDistortion.flEQBandwidth = EAXDISTORTION_DEFAULTEQBANDWIDTH;
+}
+
+template<>
+void DistortionCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+{
+ switch(call.get_property_id())
+ {
+ case EAXDISTORTION_NONE: break;
+ case EAXDISTORTION_ALLPARAMETERS: call.set_value<Exception>(props.mDistortion); break;
+ case EAXDISTORTION_EDGE: call.set_value<Exception>(props.mDistortion.flEdge); break;
+ case EAXDISTORTION_GAIN: call.set_value<Exception>(props.mDistortion.lGain); break;
+ case EAXDISTORTION_LOWPASSCUTOFF: call.set_value<Exception>(props.mDistortion.flLowPassCutOff); break;
+ case EAXDISTORTION_EQCENTER: call.set_value<Exception>(props.mDistortion.flEQCenter); break;
+ case EAXDISTORTION_EQBANDWIDTH: call.set_value<Exception>(props.mDistortion.flEQBandwidth); break;
+ default: fail_unknown_property_id();
+ }
+}
+
+template<>
+void DistortionCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+{
+ switch(call.get_property_id())
+ {
+ case EAXDISTORTION_NONE: break;
+ case EAXDISTORTION_ALLPARAMETERS: defer<AllValidator>(call, props.mDistortion); break;
+ case EAXDISTORTION_EDGE: defer<EdgeValidator>(call, props.mDistortion.flEdge); break;
+ case EAXDISTORTION_GAIN: defer<GainValidator>(call, props.mDistortion.lGain); break;
+ case EAXDISTORTION_LOWPASSCUTOFF: defer<LowPassCutOffValidator>(call, props.mDistortion.flLowPassCutOff); break;
+ case EAXDISTORTION_EQCENTER: defer<EqCenterValidator>(call, props.mDistortion.flEQCenter); break;
+ case EAXDISTORTION_EQBANDWIDTH: defer<EqBandwidthValidator>(call, props.mDistortion.flEQBandwidth); break;
+ default: fail_unknown_property_id();
+ }
+}
+
+#endif // ALSOFT_EAX
diff --git a/al/effects/echo.cpp b/al/effects/echo.cpp
new file mode 100644
index 00000000..2eb37603
--- /dev/null
+++ b/al/effects/echo.cpp
@@ -0,0 +1,268 @@
+
+#include "config.h"
+
+#include "AL/al.h"
+#include "AL/efx.h"
+
+#include "alc/effects/base.h"
+#include "effects.h"
+
+#ifdef ALSOFT_EAX
+#include "alnumeric.h"
+#include "al/eax/exception.h"
+#include "al/eax/utils.h"
+#endif // ALSOFT_EAX
+
+
+namespace {
+
+static_assert(EchoMaxDelay >= AL_ECHO_MAX_DELAY, "Echo max delay too short");
+static_assert(EchoMaxLRDelay >= AL_ECHO_MAX_LRDELAY, "Echo max left-right delay too short");
+
+void Echo_setParami(EffectProps*, ALenum param, int)
+{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; }
+void Echo_setParamiv(EffectProps*, ALenum param, const int*)
+{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; }
+void Echo_setParamf(EffectProps *props, ALenum param, float val)
+{
+ switch(param)
+ {
+ case AL_ECHO_DELAY:
+ if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY))
+ throw effect_exception{AL_INVALID_VALUE, "Echo delay out of range"};
+ props->Echo.Delay = val;
+ break;
+
+ case AL_ECHO_LRDELAY:
+ if(!(val >= AL_ECHO_MIN_LRDELAY && val <= AL_ECHO_MAX_LRDELAY))
+ throw effect_exception{AL_INVALID_VALUE, "Echo LR delay out of range"};
+ props->Echo.LRDelay = val;
+ break;
+
+ case AL_ECHO_DAMPING:
+ if(!(val >= AL_ECHO_MIN_DAMPING && val <= AL_ECHO_MAX_DAMPING))
+ throw effect_exception{AL_INVALID_VALUE, "Echo damping out of range"};
+ props->Echo.Damping = val;
+ break;
+
+ case AL_ECHO_FEEDBACK:
+ if(!(val >= AL_ECHO_MIN_FEEDBACK && val <= AL_ECHO_MAX_FEEDBACK))
+ throw effect_exception{AL_INVALID_VALUE, "Echo feedback out of range"};
+ props->Echo.Feedback = val;
+ break;
+
+ case AL_ECHO_SPREAD:
+ if(!(val >= AL_ECHO_MIN_SPREAD && val <= AL_ECHO_MAX_SPREAD))
+ throw effect_exception{AL_INVALID_VALUE, "Echo spread out of range"};
+ props->Echo.Spread = val;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param};
+ }
+}
+void Echo_setParamfv(EffectProps *props, ALenum param, const float *vals)
+{ Echo_setParamf(props, param, vals[0]); }
+
+void Echo_getParami(const EffectProps*, ALenum param, int*)
+{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; }
+void Echo_getParamiv(const EffectProps*, ALenum param, int*)
+{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; }
+void Echo_getParamf(const EffectProps *props, ALenum param, float *val)
+{
+ switch(param)
+ {
+ case AL_ECHO_DELAY:
+ *val = props->Echo.Delay;
+ break;
+
+ case AL_ECHO_LRDELAY:
+ *val = props->Echo.LRDelay;
+ break;
+
+ case AL_ECHO_DAMPING:
+ *val = props->Echo.Damping;
+ break;
+
+ case AL_ECHO_FEEDBACK:
+ *val = props->Echo.Feedback;
+ break;
+
+ case AL_ECHO_SPREAD:
+ *val = props->Echo.Spread;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param};
+ }
+}
+void Echo_getParamfv(const EffectProps *props, ALenum param, float *vals)
+{ Echo_getParamf(props, param, vals); }
+
+EffectProps genDefaultProps() noexcept
+{
+ EffectProps props{};
+ props.Echo.Delay = AL_ECHO_DEFAULT_DELAY;
+ props.Echo.LRDelay = AL_ECHO_DEFAULT_LRDELAY;
+ props.Echo.Damping = AL_ECHO_DEFAULT_DAMPING;
+ props.Echo.Feedback = AL_ECHO_DEFAULT_FEEDBACK;
+ props.Echo.Spread = AL_ECHO_DEFAULT_SPREAD;
+ return props;
+}
+
+} // namespace
+
+DEFINE_ALEFFECT_VTABLE(Echo);
+
+const EffectProps EchoEffectProps{genDefaultProps()};
+
+#ifdef ALSOFT_EAX
+namespace {
+
+using EchoCommitter = EaxCommitter<EaxEchoCommitter>;
+
+struct DelayValidator {
+ void operator()(float flDelay) const
+ {
+ eax_validate_range<EchoCommitter::Exception>(
+ "Delay",
+ flDelay,
+ EAXECHO_MINDELAY,
+ EAXECHO_MAXDELAY);
+ }
+}; // DelayValidator
+
+struct LrDelayValidator {
+ void operator()(float flLRDelay) const
+ {
+ eax_validate_range<EchoCommitter::Exception>(
+ "LR Delay",
+ flLRDelay,
+ EAXECHO_MINLRDELAY,
+ EAXECHO_MAXLRDELAY);
+ }
+}; // LrDelayValidator
+
+struct DampingValidator {
+ void operator()(float flDamping) const
+ {
+ eax_validate_range<EchoCommitter::Exception>(
+ "Damping",
+ flDamping,
+ EAXECHO_MINDAMPING,
+ EAXECHO_MAXDAMPING);
+ }
+}; // DampingValidator
+
+struct FeedbackValidator {
+ void operator()(float flFeedback) const
+ {
+ eax_validate_range<EchoCommitter::Exception>(
+ "Feedback",
+ flFeedback,
+ EAXECHO_MINFEEDBACK,
+ EAXECHO_MAXFEEDBACK);
+ }
+}; // FeedbackValidator
+
+struct SpreadValidator {
+ void operator()(float flSpread) const
+ {
+ eax_validate_range<EchoCommitter::Exception>(
+ "Spread",
+ flSpread,
+ EAXECHO_MINSPREAD,
+ EAXECHO_MAXSPREAD);
+ }
+}; // SpreadValidator
+
+struct AllValidator {
+ void operator()(const EAXECHOPROPERTIES& all) const
+ {
+ DelayValidator{}(all.flDelay);
+ LrDelayValidator{}(all.flLRDelay);
+ DampingValidator{}(all.flDamping);
+ FeedbackValidator{}(all.flFeedback);
+ SpreadValidator{}(all.flSpread);
+ }
+}; // AllValidator
+
+} // namespace
+
+template<>
+struct EchoCommitter::Exception : public EaxException {
+ explicit Exception(const char* message) : EaxException{"EAX_ECHO_EFFECT", message}
+ { }
+};
+
+template<>
+[[noreturn]] void EchoCommitter::fail(const char *message)
+{
+ throw Exception{message};
+}
+
+template<>
+bool EchoCommitter::commit(const EaxEffectProps &props)
+{
+ if(props.mType == mEaxProps.mType && mEaxProps.mEcho.flDelay == props.mEcho.flDelay
+ && mEaxProps.mEcho.flLRDelay == props.mEcho.flLRDelay
+ && mEaxProps.mEcho.flDamping == props.mEcho.flDamping
+ && mEaxProps.mEcho.flFeedback == props.mEcho.flFeedback
+ && mEaxProps.mEcho.flSpread == props.mEcho.flSpread)
+ return false;
+
+ mEaxProps = props;
+
+ mAlProps.Echo.Delay = props.mEcho.flDelay;
+ mAlProps.Echo.LRDelay = props.mEcho.flLRDelay;
+ mAlProps.Echo.Damping = props.mEcho.flDamping;
+ mAlProps.Echo.Feedback = props.mEcho.flFeedback;
+ mAlProps.Echo.Spread = props.mEcho.flSpread;
+
+ return true;
+}
+
+template<>
+void EchoCommitter::SetDefaults(EaxEffectProps &props)
+{
+ props.mType = EaxEffectType::Echo;
+ props.mEcho.flDelay = EAXECHO_DEFAULTDELAY;
+ props.mEcho.flLRDelay = EAXECHO_DEFAULTLRDELAY;
+ props.mEcho.flDamping = EAXECHO_DEFAULTDAMPING;
+ props.mEcho.flFeedback = EAXECHO_DEFAULTFEEDBACK;
+ props.mEcho.flSpread = EAXECHO_DEFAULTSPREAD;
+}
+
+template<>
+void EchoCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+{
+ switch(call.get_property_id())
+ {
+ case EAXECHO_NONE: break;
+ case EAXECHO_ALLPARAMETERS: call.set_value<Exception>(props.mEcho); break;
+ case EAXECHO_DELAY: call.set_value<Exception>(props.mEcho.flDelay); break;
+ case EAXECHO_LRDELAY: call.set_value<Exception>(props.mEcho.flLRDelay); break;
+ case EAXECHO_DAMPING: call.set_value<Exception>(props.mEcho.flDamping); break;
+ case EAXECHO_FEEDBACK: call.set_value<Exception>(props.mEcho.flFeedback); break;
+ case EAXECHO_SPREAD: call.set_value<Exception>(props.mEcho.flSpread); break;
+ default: fail_unknown_property_id();
+ }
+}
+
+template<>
+void EchoCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+{
+ switch(call.get_property_id())
+ {
+ case EAXECHO_NONE: break;
+ case EAXECHO_ALLPARAMETERS: defer<AllValidator>(call, props.mEcho); break;
+ case EAXECHO_DELAY: defer<DelayValidator>(call, props.mEcho.flDelay); break;
+ case EAXECHO_LRDELAY: defer<LrDelayValidator>(call, props.mEcho.flLRDelay); break;
+ case EAXECHO_DAMPING: defer<DampingValidator>(call, props.mEcho.flDamping); break;
+ case EAXECHO_FEEDBACK: defer<FeedbackValidator>(call, props.mEcho.flFeedback); break;
+ case EAXECHO_SPREAD: defer<SpreadValidator>(call, props.mEcho.flSpread); break;
+ default: fail_unknown_property_id();
+ }
+}
+
+#endif // ALSOFT_EAX
diff --git a/al/effects/effects.cpp b/al/effects/effects.cpp
new file mode 100644
index 00000000..4a67b5ff
--- /dev/null
+++ b/al/effects/effects.cpp
@@ -0,0 +1,9 @@
+#include "config.h"
+
+#ifdef ALSOFT_EAX
+
+#include <cassert>
+#include "AL/efx.h"
+#include "effects.h"
+
+#endif // ALSOFT_EAX
diff --git a/al/effects/effects.h b/al/effects/effects.h
new file mode 100644
index 00000000..9d57dd82
--- /dev/null
+++ b/al/effects/effects.h
@@ -0,0 +1,88 @@
+#ifndef AL_EFFECTS_EFFECTS_H
+#define AL_EFFECTS_EFFECTS_H
+
+#include "AL/al.h"
+
+#include "core/except.h"
+
+#ifdef ALSOFT_EAX
+#include "al/eax/effect.h"
+#endif // ALSOFT_EAX
+
+union EffectProps;
+
+
+class effect_exception final : public al::base_exception {
+ ALenum mErrorCode;
+
+public:
+#ifdef __USE_MINGW_ANSI_STDIO
+ [[gnu::format(gnu_printf, 3, 4)]]
+#else
+ [[gnu::format(printf, 3, 4)]]
+#endif
+ effect_exception(ALenum code, const char *msg, ...);
+ ~effect_exception() override;
+
+ ALenum errorCode() const noexcept { return mErrorCode; }
+};
+
+
+struct EffectVtable {
+ void (*const setParami)(EffectProps *props, ALenum param, int val);
+ void (*const setParamiv)(EffectProps *props, ALenum param, const int *vals);
+ void (*const setParamf)(EffectProps *props, ALenum param, float val);
+ void (*const setParamfv)(EffectProps *props, ALenum param, const float *vals);
+
+ void (*const getParami)(const EffectProps *props, ALenum param, int *val);
+ void (*const getParamiv)(const EffectProps *props, ALenum param, int *vals);
+ void (*const getParamf)(const EffectProps *props, ALenum param, float *val);
+ void (*const getParamfv)(const EffectProps *props, ALenum param, float *vals);
+};
+
+#define DEFINE_ALEFFECT_VTABLE(T) \
+const EffectVtable T##EffectVtable = { \
+ T##_setParami, T##_setParamiv, \
+ T##_setParamf, T##_setParamfv, \
+ T##_getParami, T##_getParamiv, \
+ T##_getParamf, T##_getParamfv, \
+}
+
+
+/* Default properties for the given effect types. */
+extern const EffectProps NullEffectProps;
+extern const EffectProps ReverbEffectProps;
+extern const EffectProps StdReverbEffectProps;
+extern const EffectProps AutowahEffectProps;
+extern const EffectProps ChorusEffectProps;
+extern const EffectProps CompressorEffectProps;
+extern const EffectProps DistortionEffectProps;
+extern const EffectProps EchoEffectProps;
+extern const EffectProps EqualizerEffectProps;
+extern const EffectProps FlangerEffectProps;
+extern const EffectProps FshifterEffectProps;
+extern const EffectProps ModulatorEffectProps;
+extern const EffectProps PshifterEffectProps;
+extern const EffectProps VmorpherEffectProps;
+extern const EffectProps DedicatedEffectProps;
+extern const EffectProps ConvolutionEffectProps;
+
+/* Vtables to get/set properties for the given effect types. */
+extern const EffectVtable NullEffectVtable;
+extern const EffectVtable ReverbEffectVtable;
+extern const EffectVtable StdReverbEffectVtable;
+extern const EffectVtable AutowahEffectVtable;
+extern const EffectVtable ChorusEffectVtable;
+extern const EffectVtable CompressorEffectVtable;
+extern const EffectVtable DistortionEffectVtable;
+extern const EffectVtable EchoEffectVtable;
+extern const EffectVtable EqualizerEffectVtable;
+extern const EffectVtable FlangerEffectVtable;
+extern const EffectVtable FshifterEffectVtable;
+extern const EffectVtable ModulatorEffectVtable;
+extern const EffectVtable PshifterEffectVtable;
+extern const EffectVtable VmorpherEffectVtable;
+extern const EffectVtable DedicatedEffectVtable;
+extern const EffectVtable ConvolutionEffectVtable;
+
+#endif /* AL_EFFECTS_EFFECTS_H */
diff --git a/al/effects/equalizer.cpp b/al/effects/equalizer.cpp
new file mode 100644
index 00000000..7dc703db
--- /dev/null
+++ b/al/effects/equalizer.cpp
@@ -0,0 +1,411 @@
+
+#include "config.h"
+
+#include "AL/al.h"
+#include "AL/efx.h"
+
+#include "alc/effects/base.h"
+#include "effects.h"
+
+#ifdef ALSOFT_EAX
+#include "alnumeric.h"
+#include "al/eax/exception.h"
+#include "al/eax/utils.h"
+#endif // ALSOFT_EAX
+
+
+namespace {
+
+void Equalizer_setParami(EffectProps*, ALenum param, int)
+{ throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; }
+void Equalizer_setParamiv(EffectProps*, ALenum param, const int*)
+{
+ throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x",
+ param};
+}
+void Equalizer_setParamf(EffectProps *props, ALenum param, float val)
+{
+ switch(param)
+ {
+ case AL_EQUALIZER_LOW_GAIN:
+ if(!(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN))
+ throw effect_exception{AL_INVALID_VALUE, "Equalizer low-band gain out of range"};
+ props->Equalizer.LowGain = val;
+ break;
+
+ case AL_EQUALIZER_LOW_CUTOFF:
+ if(!(val >= AL_EQUALIZER_MIN_LOW_CUTOFF && val <= AL_EQUALIZER_MAX_LOW_CUTOFF))
+ throw effect_exception{AL_INVALID_VALUE, "Equalizer low-band cutoff out of range"};
+ props->Equalizer.LowCutoff = val;
+ break;
+
+ case AL_EQUALIZER_MID1_GAIN:
+ if(!(val >= AL_EQUALIZER_MIN_MID1_GAIN && val <= AL_EQUALIZER_MAX_MID1_GAIN))
+ throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band gain out of range"};
+ props->Equalizer.Mid1Gain = val;
+ break;
+
+ case AL_EQUALIZER_MID1_CENTER:
+ if(!(val >= AL_EQUALIZER_MIN_MID1_CENTER && val <= AL_EQUALIZER_MAX_MID1_CENTER))
+ throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band center out of range"};
+ props->Equalizer.Mid1Center = val;
+ break;
+
+ case AL_EQUALIZER_MID1_WIDTH:
+ if(!(val >= AL_EQUALIZER_MIN_MID1_WIDTH && val <= AL_EQUALIZER_MAX_MID1_WIDTH))
+ throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band width out of range"};
+ props->Equalizer.Mid1Width = val;
+ break;
+
+ case AL_EQUALIZER_MID2_GAIN:
+ if(!(val >= AL_EQUALIZER_MIN_MID2_GAIN && val <= AL_EQUALIZER_MAX_MID2_GAIN))
+ throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band gain out of range"};
+ props->Equalizer.Mid2Gain = val;
+ break;
+
+ case AL_EQUALIZER_MID2_CENTER:
+ if(!(val >= AL_EQUALIZER_MIN_MID2_CENTER && val <= AL_EQUALIZER_MAX_MID2_CENTER))
+ throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band center out of range"};
+ props->Equalizer.Mid2Center = val;
+ break;
+
+ case AL_EQUALIZER_MID2_WIDTH:
+ if(!(val >= AL_EQUALIZER_MIN_MID2_WIDTH && val <= AL_EQUALIZER_MAX_MID2_WIDTH))
+ throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band width out of range"};
+ props->Equalizer.Mid2Width = val;
+ break;
+
+ case AL_EQUALIZER_HIGH_GAIN:
+ if(!(val >= AL_EQUALIZER_MIN_HIGH_GAIN && val <= AL_EQUALIZER_MAX_HIGH_GAIN))
+ throw effect_exception{AL_INVALID_VALUE, "Equalizer high-band gain out of range"};
+ props->Equalizer.HighGain = val;
+ break;
+
+ case AL_EQUALIZER_HIGH_CUTOFF:
+ if(!(val >= AL_EQUALIZER_MIN_HIGH_CUTOFF && val <= AL_EQUALIZER_MAX_HIGH_CUTOFF))
+ throw effect_exception{AL_INVALID_VALUE, "Equalizer high-band cutoff out of range"};
+ props->Equalizer.HighCutoff = val;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param};
+ }
+}
+void Equalizer_setParamfv(EffectProps *props, ALenum param, const float *vals)
+{ Equalizer_setParamf(props, param, vals[0]); }
+
+void Equalizer_getParami(const EffectProps*, ALenum param, int*)
+{ throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; }
+void Equalizer_getParamiv(const EffectProps*, ALenum param, int*)
+{
+ throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x",
+ param};
+}
+void Equalizer_getParamf(const EffectProps *props, ALenum param, float *val)
+{
+ switch(param)
+ {
+ case AL_EQUALIZER_LOW_GAIN:
+ *val = props->Equalizer.LowGain;
+ break;
+
+ case AL_EQUALIZER_LOW_CUTOFF:
+ *val = props->Equalizer.LowCutoff;
+ break;
+
+ case AL_EQUALIZER_MID1_GAIN:
+ *val = props->Equalizer.Mid1Gain;
+ break;
+
+ case AL_EQUALIZER_MID1_CENTER:
+ *val = props->Equalizer.Mid1Center;
+ break;
+
+ case AL_EQUALIZER_MID1_WIDTH:
+ *val = props->Equalizer.Mid1Width;
+ break;
+
+ case AL_EQUALIZER_MID2_GAIN:
+ *val = props->Equalizer.Mid2Gain;
+ break;
+
+ case AL_EQUALIZER_MID2_CENTER:
+ *val = props->Equalizer.Mid2Center;
+ break;
+
+ case AL_EQUALIZER_MID2_WIDTH:
+ *val = props->Equalizer.Mid2Width;
+ break;
+
+ case AL_EQUALIZER_HIGH_GAIN:
+ *val = props->Equalizer.HighGain;
+ break;
+
+ case AL_EQUALIZER_HIGH_CUTOFF:
+ *val = props->Equalizer.HighCutoff;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param};
+ }
+}
+void Equalizer_getParamfv(const EffectProps *props, ALenum param, float *vals)
+{ Equalizer_getParamf(props, param, vals); }
+
+EffectProps genDefaultProps() noexcept
+{
+ EffectProps props{};
+ props.Equalizer.LowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF;
+ props.Equalizer.LowGain = AL_EQUALIZER_DEFAULT_LOW_GAIN;
+ props.Equalizer.Mid1Center = AL_EQUALIZER_DEFAULT_MID1_CENTER;
+ props.Equalizer.Mid1Gain = AL_EQUALIZER_DEFAULT_MID1_GAIN;
+ props.Equalizer.Mid1Width = AL_EQUALIZER_DEFAULT_MID1_WIDTH;
+ props.Equalizer.Mid2Center = AL_EQUALIZER_DEFAULT_MID2_CENTER;
+ props.Equalizer.Mid2Gain = AL_EQUALIZER_DEFAULT_MID2_GAIN;
+ props.Equalizer.Mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH;
+ props.Equalizer.HighCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF;
+ props.Equalizer.HighGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN;
+ return props;
+}
+
+} // namespace
+
+DEFINE_ALEFFECT_VTABLE(Equalizer);
+
+const EffectProps EqualizerEffectProps{genDefaultProps()};
+
+#ifdef ALSOFT_EAX
+namespace {
+
+using EqualizerCommitter = EaxCommitter<EaxEqualizerCommitter>;
+
+struct LowGainValidator {
+ void operator()(long lLowGain) const
+ {
+ eax_validate_range<EqualizerCommitter::Exception>(
+ "Low Gain",
+ lLowGain,
+ EAXEQUALIZER_MINLOWGAIN,
+ EAXEQUALIZER_MAXLOWGAIN);
+ }
+}; // LowGainValidator
+
+struct LowCutOffValidator {
+ void operator()(float flLowCutOff) const
+ {
+ eax_validate_range<EqualizerCommitter::Exception>(
+ "Low Cutoff",
+ flLowCutOff,
+ EAXEQUALIZER_MINLOWCUTOFF,
+ EAXEQUALIZER_MAXLOWCUTOFF);
+ }
+}; // LowCutOffValidator
+
+struct Mid1GainValidator {
+ void operator()(long lMid1Gain) const
+ {
+ eax_validate_range<EqualizerCommitter::Exception>(
+ "Mid1 Gain",
+ lMid1Gain,
+ EAXEQUALIZER_MINMID1GAIN,
+ EAXEQUALIZER_MAXMID1GAIN);
+ }
+}; // Mid1GainValidator
+
+struct Mid1CenterValidator {
+ void operator()(float flMid1Center) const
+ {
+ eax_validate_range<EqualizerCommitter::Exception>(
+ "Mid1 Center",
+ flMid1Center,
+ EAXEQUALIZER_MINMID1CENTER,
+ EAXEQUALIZER_MAXMID1CENTER);
+ }
+}; // Mid1CenterValidator
+
+struct Mid1WidthValidator {
+ void operator()(float flMid1Width) const
+ {
+ eax_validate_range<EqualizerCommitter::Exception>(
+ "Mid1 Width",
+ flMid1Width,
+ EAXEQUALIZER_MINMID1WIDTH,
+ EAXEQUALIZER_MAXMID1WIDTH);
+ }
+}; // Mid1WidthValidator
+
+struct Mid2GainValidator {
+ void operator()(long lMid2Gain) const
+ {
+ eax_validate_range<EqualizerCommitter::Exception>(
+ "Mid2 Gain",
+ lMid2Gain,
+ EAXEQUALIZER_MINMID2GAIN,
+ EAXEQUALIZER_MAXMID2GAIN);
+ }
+}; // Mid2GainValidator
+
+struct Mid2CenterValidator {
+ void operator()(float flMid2Center) const
+ {
+ eax_validate_range<EqualizerCommitter::Exception>(
+ "Mid2 Center",
+ flMid2Center,
+ EAXEQUALIZER_MINMID2CENTER,
+ EAXEQUALIZER_MAXMID2CENTER);
+ }
+}; // Mid2CenterValidator
+
+struct Mid2WidthValidator {
+ void operator()(float flMid2Width) const
+ {
+ eax_validate_range<EqualizerCommitter::Exception>(
+ "Mid2 Width",
+ flMid2Width,
+ EAXEQUALIZER_MINMID2WIDTH,
+ EAXEQUALIZER_MAXMID2WIDTH);
+ }
+}; // Mid2WidthValidator
+
+struct HighGainValidator {
+ void operator()(long lHighGain) const
+ {
+ eax_validate_range<EqualizerCommitter::Exception>(
+ "High Gain",
+ lHighGain,
+ EAXEQUALIZER_MINHIGHGAIN,
+ EAXEQUALIZER_MAXHIGHGAIN);
+ }
+}; // HighGainValidator
+
+struct HighCutOffValidator {
+ void operator()(float flHighCutOff) const
+ {
+ eax_validate_range<EqualizerCommitter::Exception>(
+ "High Cutoff",
+ flHighCutOff,
+ EAXEQUALIZER_MINHIGHCUTOFF,
+ EAXEQUALIZER_MAXHIGHCUTOFF);
+ }
+}; // HighCutOffValidator
+
+struct AllValidator {
+ void operator()(const EAXEQUALIZERPROPERTIES& all) const
+ {
+ LowGainValidator{}(all.lLowGain);
+ LowCutOffValidator{}(all.flLowCutOff);
+ Mid1GainValidator{}(all.lMid1Gain);
+ Mid1CenterValidator{}(all.flMid1Center);
+ Mid1WidthValidator{}(all.flMid1Width);
+ Mid2GainValidator{}(all.lMid2Gain);
+ Mid2CenterValidator{}(all.flMid2Center);
+ Mid2WidthValidator{}(all.flMid2Width);
+ HighGainValidator{}(all.lHighGain);
+ HighCutOffValidator{}(all.flHighCutOff);
+ }
+}; // AllValidator
+
+} // namespace
+
+template<>
+struct EqualizerCommitter::Exception : public EaxException {
+ explicit Exception(const char* message) : EaxException{"EAX_EQUALIZER_EFFECT", message}
+ { }
+};
+
+template<>
+[[noreturn]] void EqualizerCommitter::fail(const char *message)
+{
+ throw Exception{message};
+}
+
+template<>
+bool EqualizerCommitter::commit(const EaxEffectProps &props)
+{
+ if(props.mType == mEaxProps.mType && mEaxProps.mEqualizer.lLowGain == props.mEqualizer.lLowGain
+ && mEaxProps.mEqualizer.flLowCutOff == props.mEqualizer.flLowCutOff
+ && mEaxProps.mEqualizer.lMid1Gain == props.mEqualizer.lMid1Gain
+ && mEaxProps.mEqualizer.flMid1Center == props.mEqualizer.flMid1Center
+ && mEaxProps.mEqualizer.flMid1Width == props.mEqualizer.flMid1Width
+ && mEaxProps.mEqualizer.lMid2Gain == props.mEqualizer.lMid2Gain
+ && mEaxProps.mEqualizer.flMid2Center == props.mEqualizer.flMid2Center
+ && mEaxProps.mEqualizer.flMid2Width == props.mEqualizer.flMid2Width
+ && mEaxProps.mEqualizer.lHighGain == props.mEqualizer.lHighGain
+ && mEaxProps.mEqualizer.flHighCutOff == props.mEqualizer.flHighCutOff)
+ return false;
+
+ mEaxProps = props;
+
+ mAlProps.Equalizer.LowGain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lLowGain));
+ mAlProps.Equalizer.LowCutoff = props.mEqualizer.flLowCutOff;
+ mAlProps.Equalizer.Mid1Gain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lMid1Gain));
+ mAlProps.Equalizer.Mid1Center = props.mEqualizer.flMid1Center;
+ mAlProps.Equalizer.Mid1Width = props.mEqualizer.flMid1Width;
+ mAlProps.Equalizer.Mid2Gain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lMid2Gain));
+ mAlProps.Equalizer.Mid2Center = props.mEqualizer.flMid2Center;
+ mAlProps.Equalizer.Mid2Width = props.mEqualizer.flMid2Width;
+ mAlProps.Equalizer.HighGain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lHighGain));
+ mAlProps.Equalizer.HighCutoff = props.mEqualizer.flHighCutOff;
+
+ return true;
+}
+
+template<>
+void EqualizerCommitter::SetDefaults(EaxEffectProps &props)
+{
+ props.mType = EaxEffectType::Equalizer;
+ props.mEqualizer.lLowGain = EAXEQUALIZER_DEFAULTLOWGAIN;
+ props.mEqualizer.flLowCutOff = EAXEQUALIZER_DEFAULTLOWCUTOFF;
+ props.mEqualizer.lMid1Gain = EAXEQUALIZER_DEFAULTMID1GAIN;
+ props.mEqualizer.flMid1Center = EAXEQUALIZER_DEFAULTMID1CENTER;
+ props.mEqualizer.flMid1Width = EAXEQUALIZER_DEFAULTMID1WIDTH;
+ props.mEqualizer.lMid2Gain = EAXEQUALIZER_DEFAULTMID2GAIN;
+ props.mEqualizer.flMid2Center = EAXEQUALIZER_DEFAULTMID2CENTER;
+ props.mEqualizer.flMid2Width = EAXEQUALIZER_DEFAULTMID2WIDTH;
+ props.mEqualizer.lHighGain = EAXEQUALIZER_DEFAULTHIGHGAIN;
+ props.mEqualizer.flHighCutOff = EAXEQUALIZER_DEFAULTHIGHCUTOFF;
+}
+
+template<>
+void EqualizerCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+{
+ switch(call.get_property_id())
+ {
+ case EAXEQUALIZER_NONE: break;
+ case EAXEQUALIZER_ALLPARAMETERS: call.set_value<Exception>(props.mEqualizer); break;
+ case EAXEQUALIZER_LOWGAIN: call.set_value<Exception>(props.mEqualizer.lLowGain); break;
+ case EAXEQUALIZER_LOWCUTOFF: call.set_value<Exception>(props.mEqualizer.flLowCutOff); break;
+ case EAXEQUALIZER_MID1GAIN: call.set_value<Exception>(props.mEqualizer.lMid1Gain); break;
+ case EAXEQUALIZER_MID1CENTER: call.set_value<Exception>(props.mEqualizer.flMid1Center); break;
+ case EAXEQUALIZER_MID1WIDTH: call.set_value<Exception>(props.mEqualizer.flMid1Width); break;
+ case EAXEQUALIZER_MID2GAIN: call.set_value<Exception>(props.mEqualizer.lMid2Gain); break;
+ case EAXEQUALIZER_MID2CENTER: call.set_value<Exception>(props.mEqualizer.flMid2Center); break;
+ case EAXEQUALIZER_MID2WIDTH: call.set_value<Exception>(props.mEqualizer.flMid2Width); break;
+ case EAXEQUALIZER_HIGHGAIN: call.set_value<Exception>(props.mEqualizer.lHighGain); break;
+ case EAXEQUALIZER_HIGHCUTOFF: call.set_value<Exception>(props.mEqualizer.flHighCutOff); break;
+ default: fail_unknown_property_id();
+ }
+}
+
+template<>
+void EqualizerCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+{
+ switch(call.get_property_id())
+ {
+ case EAXEQUALIZER_NONE: break;
+ case EAXEQUALIZER_ALLPARAMETERS: defer<AllValidator>(call, props.mEqualizer); break;
+ case EAXEQUALIZER_LOWGAIN: defer<LowGainValidator>(call, props.mEqualizer.lLowGain); break;
+ case EAXEQUALIZER_LOWCUTOFF: defer<LowCutOffValidator>(call, props.mEqualizer.flLowCutOff); break;
+ case EAXEQUALIZER_MID1GAIN: defer<Mid1GainValidator>(call, props.mEqualizer.lMid1Gain); break;
+ case EAXEQUALIZER_MID1CENTER: defer<Mid1CenterValidator>(call, props.mEqualizer.flMid1Center); break;
+ case EAXEQUALIZER_MID1WIDTH: defer<Mid1WidthValidator>(call, props.mEqualizer.flMid1Width); break;
+ case EAXEQUALIZER_MID2GAIN: defer<Mid2GainValidator>(call, props.mEqualizer.lMid2Gain); break;
+ case EAXEQUALIZER_MID2CENTER: defer<Mid2CenterValidator>(call, props.mEqualizer.flMid2Center); break;
+ case EAXEQUALIZER_MID2WIDTH: defer<Mid2WidthValidator>(call, props.mEqualizer.flMid2Width); break;
+ case EAXEQUALIZER_HIGHGAIN: defer<HighGainValidator>(call, props.mEqualizer.lHighGain); break;
+ case EAXEQUALIZER_HIGHCUTOFF: defer<HighCutOffValidator>(call, props.mEqualizer.flHighCutOff); break;
+ default: fail_unknown_property_id();
+ }
+}
+
+#endif // ALSOFT_EAX
diff --git a/al/effects/fshifter.cpp b/al/effects/fshifter.cpp
new file mode 100644
index 00000000..949db203
--- /dev/null
+++ b/al/effects/fshifter.cpp
@@ -0,0 +1,264 @@
+
+#include "config.h"
+
+#include <stdexcept>
+
+#include "AL/al.h"
+#include "AL/efx.h"
+
+#include "alc/effects/base.h"
+#include "aloptional.h"
+#include "effects.h"
+
+#ifdef ALSOFT_EAX
+#include <cassert>
+#include "alnumeric.h"
+#include "al/eax/exception.h"
+#include "al/eax/utils.h"
+#endif // ALSOFT_EAX
+
+
+namespace {
+
+al::optional<FShifterDirection> DirectionFromEmum(ALenum value)
+{
+ switch(value)
+ {
+ case AL_FREQUENCY_SHIFTER_DIRECTION_DOWN: return FShifterDirection::Down;
+ case AL_FREQUENCY_SHIFTER_DIRECTION_UP: return FShifterDirection::Up;
+ case AL_FREQUENCY_SHIFTER_DIRECTION_OFF: return FShifterDirection::Off;
+ }
+ return al::nullopt;
+}
+ALenum EnumFromDirection(FShifterDirection dir)
+{
+ switch(dir)
+ {
+ case FShifterDirection::Down: return AL_FREQUENCY_SHIFTER_DIRECTION_DOWN;
+ case FShifterDirection::Up: return AL_FREQUENCY_SHIFTER_DIRECTION_UP;
+ case FShifterDirection::Off: return AL_FREQUENCY_SHIFTER_DIRECTION_OFF;
+ }
+ throw std::runtime_error{"Invalid direction: "+std::to_string(static_cast<int>(dir))};
+}
+
+void Fshifter_setParamf(EffectProps *props, ALenum param, float val)
+{
+ switch(param)
+ {
+ case AL_FREQUENCY_SHIFTER_FREQUENCY:
+ if(!(val >= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY))
+ throw effect_exception{AL_INVALID_VALUE, "Frequency shifter frequency out of range"};
+ props->Fshifter.Frequency = val;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x",
+ param};
+ }
+}
+void Fshifter_setParamfv(EffectProps *props, ALenum param, const float *vals)
+{ Fshifter_setParamf(props, param, vals[0]); }
+
+void Fshifter_setParami(EffectProps *props, ALenum param, int val)
+{
+ switch(param)
+ {
+ case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
+ if(auto diropt = DirectionFromEmum(val))
+ props->Fshifter.LeftDirection = *diropt;
+ else
+ throw effect_exception{AL_INVALID_VALUE,
+ "Unsupported frequency shifter left direction: 0x%04x", val};
+ break;
+
+ case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
+ if(auto diropt = DirectionFromEmum(val))
+ props->Fshifter.RightDirection = *diropt;
+ else
+ throw effect_exception{AL_INVALID_VALUE,
+ "Unsupported frequency shifter right direction: 0x%04x", val};
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM,
+ "Invalid frequency shifter integer property 0x%04x", param};
+ }
+}
+void Fshifter_setParamiv(EffectProps *props, ALenum param, const int *vals)
+{ Fshifter_setParami(props, param, vals[0]); }
+
+void Fshifter_getParami(const EffectProps *props, ALenum param, int *val)
+{
+ switch(param)
+ {
+ case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
+ *val = EnumFromDirection(props->Fshifter.LeftDirection);
+ break;
+ case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
+ *val = EnumFromDirection(props->Fshifter.RightDirection);
+ break;
+ default:
+ throw effect_exception{AL_INVALID_ENUM,
+ "Invalid frequency shifter integer property 0x%04x", param};
+ }
+}
+void Fshifter_getParamiv(const EffectProps *props, ALenum param, int *vals)
+{ Fshifter_getParami(props, param, vals); }
+
+void Fshifter_getParamf(const EffectProps *props, ALenum param, float *val)
+{
+ switch(param)
+ {
+ case AL_FREQUENCY_SHIFTER_FREQUENCY:
+ *val = props->Fshifter.Frequency;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x",
+ param};
+ }
+}
+void Fshifter_getParamfv(const EffectProps *props, ALenum param, float *vals)
+{ Fshifter_getParamf(props, param, vals); }
+
+EffectProps genDefaultProps() noexcept
+{
+ EffectProps props{};
+ props.Fshifter.Frequency = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY;
+ props.Fshifter.LeftDirection = *DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION);
+ props.Fshifter.RightDirection = *DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION);
+ return props;
+}
+
+} // namespace
+
+DEFINE_ALEFFECT_VTABLE(Fshifter);
+
+const EffectProps FshifterEffectProps{genDefaultProps()};
+
+#ifdef ALSOFT_EAX
+namespace {
+
+using FrequencyShifterCommitter = EaxCommitter<EaxFrequencyShifterCommitter>;
+
+struct FrequencyValidator {
+ void operator()(float flFrequency) const
+ {
+ eax_validate_range<FrequencyShifterCommitter::Exception>(
+ "Frequency",
+ flFrequency,
+ EAXFREQUENCYSHIFTER_MINFREQUENCY,
+ EAXFREQUENCYSHIFTER_MAXFREQUENCY);
+ }
+}; // FrequencyValidator
+
+struct LeftDirectionValidator {
+ void operator()(unsigned long ulLeftDirection) const
+ {
+ eax_validate_range<FrequencyShifterCommitter::Exception>(
+ "Left Direction",
+ ulLeftDirection,
+ EAXFREQUENCYSHIFTER_MINLEFTDIRECTION,
+ EAXFREQUENCYSHIFTER_MAXLEFTDIRECTION);
+ }
+}; // LeftDirectionValidator
+
+struct RightDirectionValidator {
+ void operator()(unsigned long ulRightDirection) const
+ {
+ eax_validate_range<FrequencyShifterCommitter::Exception>(
+ "Right Direction",
+ ulRightDirection,
+ EAXFREQUENCYSHIFTER_MINRIGHTDIRECTION,
+ EAXFREQUENCYSHIFTER_MAXRIGHTDIRECTION);
+ }
+}; // RightDirectionValidator
+
+struct AllValidator {
+ void operator()(const EAXFREQUENCYSHIFTERPROPERTIES& all) const
+ {
+ FrequencyValidator{}(all.flFrequency);
+ LeftDirectionValidator{}(all.ulLeftDirection);
+ RightDirectionValidator{}(all.ulRightDirection);
+ }
+}; // AllValidator
+
+} // namespace
+
+template<>
+struct FrequencyShifterCommitter::Exception : public EaxException {
+ explicit Exception(const char *message) : EaxException{"EAX_FREQUENCY_SHIFTER_EFFECT", message}
+ { }
+};
+
+template<>
+[[noreturn]] void FrequencyShifterCommitter::fail(const char *message)
+{
+ throw Exception{message};
+}
+
+template<>
+bool FrequencyShifterCommitter::commit(const EaxEffectProps &props)
+{
+ if(props.mType == mEaxProps.mType
+ && mEaxProps.mFrequencyShifter.flFrequency == props.mFrequencyShifter.flFrequency
+ && mEaxProps.mFrequencyShifter.ulLeftDirection == props.mFrequencyShifter.ulLeftDirection
+ && mEaxProps.mFrequencyShifter.ulRightDirection == props.mFrequencyShifter.ulRightDirection)
+ return false;
+
+ mEaxProps = props;
+
+ auto get_direction = [](unsigned long dir) noexcept
+ {
+ if(dir == EAX_FREQUENCYSHIFTER_DOWN)
+ return FShifterDirection::Down;
+ if(dir == EAX_FREQUENCYSHIFTER_UP)
+ return FShifterDirection::Up;
+ return FShifterDirection::Off;
+ };
+
+ mAlProps.Fshifter.Frequency = props.mFrequencyShifter.flFrequency;
+ mAlProps.Fshifter.LeftDirection = get_direction(props.mFrequencyShifter.ulLeftDirection);
+ mAlProps.Fshifter.RightDirection = get_direction(props.mFrequencyShifter.ulRightDirection);
+
+ return true;
+}
+
+template<>
+void FrequencyShifterCommitter::SetDefaults(EaxEffectProps &props)
+{
+ props.mType = EaxEffectType::FrequencyShifter;
+ props.mFrequencyShifter.flFrequency = EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY;
+ props.mFrequencyShifter.ulLeftDirection = EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION;
+ props.mFrequencyShifter.ulRightDirection = EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION;
+}
+
+template<>
+void FrequencyShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+{
+ switch(call.get_property_id())
+ {
+ case EAXFREQUENCYSHIFTER_NONE: break;
+ case EAXFREQUENCYSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props.mFrequencyShifter); break;
+ case EAXFREQUENCYSHIFTER_FREQUENCY: call.set_value<Exception>(props.mFrequencyShifter.flFrequency); break;
+ case EAXFREQUENCYSHIFTER_LEFTDIRECTION: call.set_value<Exception>(props.mFrequencyShifter.ulLeftDirection); break;
+ case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: call.set_value<Exception>(props.mFrequencyShifter.ulRightDirection); break;
+ default: fail_unknown_property_id();
+ }
+}
+
+template<>
+void FrequencyShifterCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+{
+ switch(call.get_property_id())
+ {
+ case EAXFREQUENCYSHIFTER_NONE: break;
+ case EAXFREQUENCYSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props.mFrequencyShifter); break;
+ case EAXFREQUENCYSHIFTER_FREQUENCY: defer<FrequencyValidator>(call, props.mFrequencyShifter.flFrequency); break;
+ case EAXFREQUENCYSHIFTER_LEFTDIRECTION: defer<LeftDirectionValidator>(call, props.mFrequencyShifter.ulLeftDirection); break;
+ case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: defer<RightDirectionValidator>(call, props.mFrequencyShifter.ulRightDirection); break;
+ default: fail_unknown_property_id();
+ }
+}
+
+#endif // ALSOFT_EAX
diff --git a/al/effects/modulator.cpp b/al/effects/modulator.cpp
new file mode 100644
index 00000000..5f37d08f
--- /dev/null
+++ b/al/effects/modulator.cpp
@@ -0,0 +1,272 @@
+
+#include "config.h"
+
+#include <stdexcept>
+
+#include "AL/al.h"
+#include "AL/efx.h"
+
+#include "alc/effects/base.h"
+#include "aloptional.h"
+#include "effects.h"
+
+#ifdef ALSOFT_EAX
+#include <cassert>
+#include "alnumeric.h"
+#include "al/eax/exception.h"
+#include "al/eax/utils.h"
+#endif // ALSOFT_EAX
+
+
+namespace {
+
+al::optional<ModulatorWaveform> WaveformFromEmum(ALenum value)
+{
+ switch(value)
+ {
+ case AL_RING_MODULATOR_SINUSOID: return ModulatorWaveform::Sinusoid;
+ case AL_RING_MODULATOR_SAWTOOTH: return ModulatorWaveform::Sawtooth;
+ case AL_RING_MODULATOR_SQUARE: return ModulatorWaveform::Square;
+ }
+ return al::nullopt;
+}
+ALenum EnumFromWaveform(ModulatorWaveform type)
+{
+ switch(type)
+ {
+ case ModulatorWaveform::Sinusoid: return AL_RING_MODULATOR_SINUSOID;
+ case ModulatorWaveform::Sawtooth: return AL_RING_MODULATOR_SAWTOOTH;
+ case ModulatorWaveform::Square: return AL_RING_MODULATOR_SQUARE;
+ }
+ throw std::runtime_error{"Invalid modulator waveform: " +
+ std::to_string(static_cast<int>(type))};
+}
+
+void Modulator_setParamf(EffectProps *props, ALenum param, float val)
+{
+ switch(param)
+ {
+ case AL_RING_MODULATOR_FREQUENCY:
+ if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY))
+ throw effect_exception{AL_INVALID_VALUE, "Modulator frequency out of range: %f", val};
+ props->Modulator.Frequency = val;
+ break;
+
+ case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
+ if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF))
+ throw effect_exception{AL_INVALID_VALUE, "Modulator high-pass cutoff out of range: %f", val};
+ props->Modulator.HighPassCutoff = val;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param};
+ }
+}
+void Modulator_setParamfv(EffectProps *props, ALenum param, const float *vals)
+{ Modulator_setParamf(props, param, vals[0]); }
+void Modulator_setParami(EffectProps *props, ALenum param, int val)
+{
+ switch(param)
+ {
+ case AL_RING_MODULATOR_FREQUENCY:
+ case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
+ Modulator_setParamf(props, param, static_cast<float>(val));
+ break;
+
+ case AL_RING_MODULATOR_WAVEFORM:
+ if(auto formopt = WaveformFromEmum(val))
+ props->Modulator.Waveform = *formopt;
+ else
+ throw effect_exception{AL_INVALID_VALUE, "Invalid modulator waveform: 0x%04x", val};
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x",
+ param};
+ }
+}
+void Modulator_setParamiv(EffectProps *props, ALenum param, const int *vals)
+{ Modulator_setParami(props, param, vals[0]); }
+
+void Modulator_getParami(const EffectProps *props, ALenum param, int *val)
+{
+ switch(param)
+ {
+ case AL_RING_MODULATOR_FREQUENCY:
+ *val = static_cast<int>(props->Modulator.Frequency);
+ break;
+ case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
+ *val = static_cast<int>(props->Modulator.HighPassCutoff);
+ break;
+ case AL_RING_MODULATOR_WAVEFORM:
+ *val = EnumFromWaveform(props->Modulator.Waveform);
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x",
+ param};
+ }
+}
+void Modulator_getParamiv(const EffectProps *props, ALenum param, int *vals)
+{ Modulator_getParami(props, param, vals); }
+void Modulator_getParamf(const EffectProps *props, ALenum param, float *val)
+{
+ switch(param)
+ {
+ case AL_RING_MODULATOR_FREQUENCY:
+ *val = props->Modulator.Frequency;
+ break;
+ case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
+ *val = props->Modulator.HighPassCutoff;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param};
+ }
+}
+void Modulator_getParamfv(const EffectProps *props, ALenum param, float *vals)
+{ Modulator_getParamf(props, param, vals); }
+
+EffectProps genDefaultProps() noexcept
+{
+ EffectProps props{};
+ props.Modulator.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY;
+ props.Modulator.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF;
+ props.Modulator.Waveform = *WaveformFromEmum(AL_RING_MODULATOR_DEFAULT_WAVEFORM);
+ return props;
+}
+
+} // namespace
+
+DEFINE_ALEFFECT_VTABLE(Modulator);
+
+const EffectProps ModulatorEffectProps{genDefaultProps()};
+
+#ifdef ALSOFT_EAX
+namespace {
+
+using ModulatorCommitter = EaxCommitter<EaxModulatorCommitter>;
+
+struct FrequencyValidator {
+ void operator()(float flFrequency) const
+ {
+ eax_validate_range<ModulatorCommitter::Exception>(
+ "Frequency",
+ flFrequency,
+ EAXRINGMODULATOR_MINFREQUENCY,
+ EAXRINGMODULATOR_MAXFREQUENCY);
+ }
+}; // FrequencyValidator
+
+struct HighPassCutOffValidator {
+ void operator()(float flHighPassCutOff) const
+ {
+ eax_validate_range<ModulatorCommitter::Exception>(
+ "High-Pass Cutoff",
+ flHighPassCutOff,
+ EAXRINGMODULATOR_MINHIGHPASSCUTOFF,
+ EAXRINGMODULATOR_MAXHIGHPASSCUTOFF);
+ }
+}; // HighPassCutOffValidator
+
+struct WaveformValidator {
+ void operator()(unsigned long ulWaveform) const
+ {
+ eax_validate_range<ModulatorCommitter::Exception>(
+ "Waveform",
+ ulWaveform,
+ EAXRINGMODULATOR_MINWAVEFORM,
+ EAXRINGMODULATOR_MAXWAVEFORM);
+ }
+}; // WaveformValidator
+
+struct AllValidator {
+ void operator()(const EAXRINGMODULATORPROPERTIES& all) const
+ {
+ FrequencyValidator{}(all.flFrequency);
+ HighPassCutOffValidator{}(all.flHighPassCutOff);
+ WaveformValidator{}(all.ulWaveform);
+ }
+}; // AllValidator
+
+} // namespace
+
+template<>
+struct ModulatorCommitter::Exception : public EaxException {
+ explicit Exception(const char *message) : EaxException{"EAX_RING_MODULATOR_EFFECT", message}
+ { }
+};
+
+template<>
+[[noreturn]] void ModulatorCommitter::fail(const char *message)
+{
+ throw Exception{message};
+}
+
+template<>
+bool ModulatorCommitter::commit(const EaxEffectProps &props)
+{
+ if(props.mType == mEaxProps.mType
+ && mEaxProps.mModulator.flFrequency == props.mModulator.flFrequency
+ && mEaxProps.mModulator.flHighPassCutOff == props.mModulator.flHighPassCutOff
+ && mEaxProps.mModulator.ulWaveform == props.mModulator.ulWaveform)
+ return false;
+
+ mEaxProps = props;
+
+ auto get_waveform = [](unsigned long form)
+ {
+ if(form == EAX_RINGMODULATOR_SINUSOID)
+ return ModulatorWaveform::Sinusoid;
+ if(form == EAX_RINGMODULATOR_SAWTOOTH)
+ return ModulatorWaveform::Sawtooth;
+ if(form == EAX_RINGMODULATOR_SQUARE)
+ return ModulatorWaveform::Square;
+ return ModulatorWaveform::Sinusoid;
+ };
+
+ mAlProps.Modulator.Frequency = props.mModulator.flFrequency;
+ mAlProps.Modulator.HighPassCutoff = props.mModulator.flHighPassCutOff;
+ mAlProps.Modulator.Waveform = get_waveform(props.mModulator.ulWaveform);
+
+ return true;
+}
+
+template<>
+void ModulatorCommitter::SetDefaults(EaxEffectProps &props)
+{
+ props.mType = EaxEffectType::Modulator;
+ props.mModulator.flFrequency = EAXRINGMODULATOR_DEFAULTFREQUENCY;
+ props.mModulator.flHighPassCutOff = EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF;
+ props.mModulator.ulWaveform = EAXRINGMODULATOR_DEFAULTWAVEFORM;
+}
+
+template<>
+void ModulatorCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+{
+ switch(call.get_property_id())
+ {
+ case EAXRINGMODULATOR_NONE: break;
+ case EAXRINGMODULATOR_ALLPARAMETERS: call.set_value<Exception>(props.mModulator); break;
+ case EAXRINGMODULATOR_FREQUENCY: call.set_value<Exception>(props.mModulator.flFrequency); break;
+ case EAXRINGMODULATOR_HIGHPASSCUTOFF: call.set_value<Exception>(props.mModulator.flHighPassCutOff); break;
+ case EAXRINGMODULATOR_WAVEFORM: call.set_value<Exception>(props.mModulator.ulWaveform); break;
+ default: fail_unknown_property_id();
+ }
+}
+
+template<>
+void ModulatorCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+{
+ switch (call.get_property_id())
+ {
+ case EAXRINGMODULATOR_NONE: break;
+ case EAXRINGMODULATOR_ALLPARAMETERS: defer<AllValidator>(call, props.mModulator); break;
+ case EAXRINGMODULATOR_FREQUENCY: defer<FrequencyValidator>(call, props.mModulator.flFrequency); break;
+ case EAXRINGMODULATOR_HIGHPASSCUTOFF: defer<HighPassCutOffValidator>(call, props.mModulator.flHighPassCutOff); break;
+ case EAXRINGMODULATOR_WAVEFORM: defer<WaveformValidator>(call, props.mModulator.ulWaveform); break;
+ default: fail_unknown_property_id();
+ }
+}
+
+#endif // ALSOFT_EAX
diff --git a/al/effects/null.cpp b/al/effects/null.cpp
new file mode 100644
index 00000000..0bbc183a
--- /dev/null
+++ b/al/effects/null.cpp
@@ -0,0 +1,149 @@
+
+#include "config.h"
+
+#include "AL/al.h"
+#include "AL/efx.h"
+
+#include "alc/effects/base.h"
+#include "effects.h"
+
+#ifdef ALSOFT_EAX
+#include "al/eax/exception.h"
+#endif // ALSOFT_EAX
+
+
+namespace {
+
+void Null_setParami(EffectProps* /*props*/, ALenum param, int /*val*/)
+{
+ switch(param)
+ {
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
+ param};
+ }
+}
+void Null_setParamiv(EffectProps *props, ALenum param, const int *vals)
+{
+ switch(param)
+ {
+ default:
+ Null_setParami(props, param, vals[0]);
+ }
+}
+void Null_setParamf(EffectProps* /*props*/, ALenum param, float /*val*/)
+{
+ switch(param)
+ {
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
+ param};
+ }
+}
+void Null_setParamfv(EffectProps *props, ALenum param, const float *vals)
+{
+ switch(param)
+ {
+ default:
+ Null_setParamf(props, param, vals[0]);
+ }
+}
+
+void Null_getParami(const EffectProps* /*props*/, ALenum param, int* /*val*/)
+{
+ switch(param)
+ {
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x",
+ param};
+ }
+}
+void Null_getParamiv(const EffectProps *props, ALenum param, int *vals)
+{
+ switch(param)
+ {
+ default:
+ Null_getParami(props, param, vals);
+ }
+}
+void Null_getParamf(const EffectProps* /*props*/, ALenum param, float* /*val*/)
+{
+ switch(param)
+ {
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x",
+ param};
+ }
+}
+void Null_getParamfv(const EffectProps *props, ALenum param, float *vals)
+{
+ switch(param)
+ {
+ default:
+ Null_getParamf(props, param, vals);
+ }
+}
+
+EffectProps genDefaultProps() noexcept
+{
+ EffectProps props{};
+ return props;
+}
+
+} // namespace
+
+DEFINE_ALEFFECT_VTABLE(Null);
+
+const EffectProps NullEffectProps{genDefaultProps()};
+
+
+#ifdef ALSOFT_EAX
+namespace {
+
+using NullCommitter = EaxCommitter<EaxNullCommitter>;
+
+} // namespace
+
+template<>
+struct NullCommitter::Exception : public EaxException
+{
+ explicit Exception(const char *message) : EaxException{"EAX_NULL_EFFECT", message}
+ { }
+};
+
+template<>
+[[noreturn]] void NullCommitter::fail(const char *message)
+{
+ throw Exception{message};
+}
+
+template<>
+bool NullCommitter::commit(const EaxEffectProps &props)
+{
+ const bool ret{props.mType != mEaxProps.mType};
+ mEaxProps = props;
+ return ret;
+}
+
+template<>
+void NullCommitter::SetDefaults(EaxEffectProps &props)
+{
+ props = EaxEffectProps{};
+ props.mType = EaxEffectType::None;
+}
+
+template<>
+void NullCommitter::Get(const EaxCall &call, const EaxEffectProps&)
+{
+ if(call.get_property_id() != 0)
+ fail_unknown_property_id();
+}
+
+template<>
+void NullCommitter::Set(const EaxCall &call, EaxEffectProps&)
+{
+ if(call.get_property_id() != 0)
+ fail_unknown_property_id();
+}
+
+#endif // ALSOFT_EAX
diff --git a/al/effects/pshifter.cpp b/al/effects/pshifter.cpp
new file mode 100644
index 00000000..634eb186
--- /dev/null
+++ b/al/effects/pshifter.cpp
@@ -0,0 +1,191 @@
+
+#include "config.h"
+
+#include "AL/al.h"
+#include "AL/efx.h"
+
+#include "alc/effects/base.h"
+#include "effects.h"
+
+#ifdef ALSOFT_EAX
+#include "alnumeric.h"
+#include "al/eax/exception.h"
+#include "al/eax/utils.h"
+#endif // ALSOFT_EAX
+
+
+namespace {
+
+void Pshifter_setParamf(EffectProps*, ALenum param, float)
+{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; }
+void Pshifter_setParamfv(EffectProps*, ALenum param, const float*)
+{
+ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x",
+ param};
+}
+
+void Pshifter_setParami(EffectProps *props, ALenum param, int val)
+{
+ switch(param)
+ {
+ case AL_PITCH_SHIFTER_COARSE_TUNE:
+ if(!(val >= AL_PITCH_SHIFTER_MIN_COARSE_TUNE && val <= AL_PITCH_SHIFTER_MAX_COARSE_TUNE))
+ throw effect_exception{AL_INVALID_VALUE, "Pitch shifter coarse tune out of range"};
+ props->Pshifter.CoarseTune = val;
+ break;
+
+ case AL_PITCH_SHIFTER_FINE_TUNE:
+ if(!(val >= AL_PITCH_SHIFTER_MIN_FINE_TUNE && val <= AL_PITCH_SHIFTER_MAX_FINE_TUNE))
+ throw effect_exception{AL_INVALID_VALUE, "Pitch shifter fine tune out of range"};
+ props->Pshifter.FineTune = val;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x",
+ param};
+ }
+}
+void Pshifter_setParamiv(EffectProps *props, ALenum param, const int *vals)
+{ Pshifter_setParami(props, param, vals[0]); }
+
+void Pshifter_getParami(const EffectProps *props, ALenum param, int *val)
+{
+ switch(param)
+ {
+ case AL_PITCH_SHIFTER_COARSE_TUNE:
+ *val = props->Pshifter.CoarseTune;
+ break;
+ case AL_PITCH_SHIFTER_FINE_TUNE:
+ *val = props->Pshifter.FineTune;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x",
+ param};
+ }
+}
+void Pshifter_getParamiv(const EffectProps *props, ALenum param, int *vals)
+{ Pshifter_getParami(props, param, vals); }
+
+void Pshifter_getParamf(const EffectProps*, ALenum param, float*)
+{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; }
+void Pshifter_getParamfv(const EffectProps*, ALenum param, float*)
+{
+ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float vector-property 0x%04x",
+ param};
+}
+
+EffectProps genDefaultProps() noexcept
+{
+ EffectProps props{};
+ props.Pshifter.CoarseTune = AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE;
+ props.Pshifter.FineTune = AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE;
+ return props;
+}
+
+} // namespace
+
+DEFINE_ALEFFECT_VTABLE(Pshifter);
+
+const EffectProps PshifterEffectProps{genDefaultProps()};
+
+#ifdef ALSOFT_EAX
+namespace {
+
+using PitchShifterCommitter = EaxCommitter<EaxPitchShifterCommitter>;
+
+struct CoarseTuneValidator {
+ void operator()(long lCoarseTune) const
+ {
+ eax_validate_range<PitchShifterCommitter::Exception>(
+ "Coarse Tune",
+ lCoarseTune,
+ EAXPITCHSHIFTER_MINCOARSETUNE,
+ EAXPITCHSHIFTER_MAXCOARSETUNE);
+ }
+}; // CoarseTuneValidator
+
+struct FineTuneValidator {
+ void operator()(long lFineTune) const
+ {
+ eax_validate_range<PitchShifterCommitter::Exception>(
+ "Fine Tune",
+ lFineTune,
+ EAXPITCHSHIFTER_MINFINETUNE,
+ EAXPITCHSHIFTER_MAXFINETUNE);
+ }
+}; // FineTuneValidator
+
+struct AllValidator {
+ void operator()(const EAXPITCHSHIFTERPROPERTIES& all) const
+ {
+ CoarseTuneValidator{}(all.lCoarseTune);
+ FineTuneValidator{}(all.lFineTune);
+ }
+}; // AllValidator
+
+} // namespace
+
+template<>
+struct PitchShifterCommitter::Exception : public EaxException {
+ explicit Exception(const char *message) : EaxException{"EAX_PITCH_SHIFTER_EFFECT", message}
+ { }
+};
+
+template<>
+[[noreturn]] void PitchShifterCommitter::fail(const char *message)
+{
+ throw Exception{message};
+}
+
+template<>
+bool PitchShifterCommitter::commit(const EaxEffectProps &props)
+{
+ if(props.mType == mEaxProps.mType
+ && mEaxProps.mPitchShifter.lCoarseTune == props.mPitchShifter.lCoarseTune
+ && mEaxProps.mPitchShifter.lFineTune == props.mPitchShifter.lFineTune)
+ return false;
+
+ mEaxProps = props;
+
+ mAlProps.Pshifter.CoarseTune = static_cast<int>(mEaxProps.mPitchShifter.lCoarseTune);
+ mAlProps.Pshifter.FineTune = static_cast<int>(mEaxProps.mPitchShifter.lFineTune);
+
+ return true;
+}
+
+template<>
+void PitchShifterCommitter::SetDefaults(EaxEffectProps &props)
+{
+ props.mType = EaxEffectType::PitchShifter;
+ props.mPitchShifter.lCoarseTune = EAXPITCHSHIFTER_DEFAULTCOARSETUNE;
+ props.mPitchShifter.lFineTune = EAXPITCHSHIFTER_DEFAULTFINETUNE;
+}
+
+template<>
+void PitchShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+{
+ switch(call.get_property_id())
+ {
+ case EAXPITCHSHIFTER_NONE: break;
+ case EAXPITCHSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props.mPitchShifter); break;
+ case EAXPITCHSHIFTER_COARSETUNE: call.set_value<Exception>(props.mPitchShifter.lCoarseTune); break;
+ case EAXPITCHSHIFTER_FINETUNE: call.set_value<Exception>(props.mPitchShifter.lFineTune); break;
+ default: fail_unknown_property_id();
+ }
+}
+
+template<>
+void PitchShifterCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+{
+ switch(call.get_property_id())
+ {
+ case EAXPITCHSHIFTER_NONE: break;
+ case EAXPITCHSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props.mPitchShifter); break;
+ case EAXPITCHSHIFTER_COARSETUNE: defer<CoarseTuneValidator>(call, props.mPitchShifter.lCoarseTune); break;
+ case EAXPITCHSHIFTER_FINETUNE: defer<FineTuneValidator>(call, props.mPitchShifter.lFineTune); break;
+ default: fail_unknown_property_id();
+ }
+}
+
+#endif // ALSOFT_EAX
diff --git a/al/effects/reverb.cpp b/al/effects/reverb.cpp
new file mode 100644
index 00000000..440d7b4e
--- /dev/null
+++ b/al/effects/reverb.cpp
@@ -0,0 +1,1499 @@
+
+#include "config.h"
+
+#include <cmath>
+
+#include "AL/al.h"
+#include "AL/efx.h"
+
+#include "alc/effects/base.h"
+#include "effects.h"
+
+#ifdef ALSOFT_EAX
+#include <cassert>
+#include "alnumeric.h"
+#include "AL/efx-presets.h"
+#include "al/eax/exception.h"
+#include "al/eax/utils.h"
+#endif // ALSOFT_EAX
+
+
+namespace {
+
+void Reverb_setParami(EffectProps *props, ALenum param, int val)
+{
+ switch(param)
+ {
+ case AL_EAXREVERB_DECAY_HFLIMIT:
+ if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT))
+ throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay hflimit out of range"};
+ props->Reverb.DecayHFLimit = val != AL_FALSE;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
+ param};
+ }
+}
+void Reverb_setParamiv(EffectProps *props, ALenum param, const int *vals)
+{ Reverb_setParami(props, param, vals[0]); }
+void Reverb_setParamf(EffectProps *props, ALenum param, float val)
+{
+ switch(param)
+ {
+ case AL_EAXREVERB_DENSITY:
+ if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY))
+ throw effect_exception{AL_INVALID_VALUE, "EAX Reverb density out of range"};
+ props->Reverb.Density = val;
+ break;
+
+ case AL_EAXREVERB_DIFFUSION:
+ if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION))
+ throw effect_exception{AL_INVALID_VALUE, "EAX Reverb diffusion out of range"};
+ props->Reverb.Diffusion = val;
+ break;
+
+ case AL_EAXREVERB_GAIN:
+ if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN))
+ throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gain out of range"};
+ props->Reverb.Gain = val;
+ break;
+
+ case AL_EAXREVERB_GAINHF:
+ if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF))
+ throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gainhf out of range"};
+ props->Reverb.GainHF = val;
+ break;
+
+ case AL_EAXREVERB_GAINLF:
+ if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF))
+ throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gainlf out of range"};
+ props->Reverb.GainLF = val;
+ break;
+
+ case AL_EAXREVERB_DECAY_TIME:
+ if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME))
+ throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay time out of range"};
+ props->Reverb.DecayTime = val;
+ break;
+
+ case AL_EAXREVERB_DECAY_HFRATIO:
+ if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO))
+ throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay hfratio out of range"};
+ props->Reverb.DecayHFRatio = val;
+ break;
+
+ case AL_EAXREVERB_DECAY_LFRATIO:
+ if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO))
+ throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay lfratio out of range"};
+ props->Reverb.DecayLFRatio = val;
+ break;
+
+ case AL_EAXREVERB_REFLECTIONS_GAIN:
+ if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN))
+ throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections gain out of range"};
+ props->Reverb.ReflectionsGain = val;
+ break;
+
+ case AL_EAXREVERB_REFLECTIONS_DELAY:
+ if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY))
+ throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections delay out of range"};
+ props->Reverb.ReflectionsDelay = val;
+ break;
+
+ case AL_EAXREVERB_LATE_REVERB_GAIN:
+ if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN))
+ throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb gain out of range"};
+ props->Reverb.LateReverbGain = val;
+ break;
+
+ case AL_EAXREVERB_LATE_REVERB_DELAY:
+ if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY))
+ throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb delay out of range"};
+ props->Reverb.LateReverbDelay = val;
+ break;
+
+ case AL_EAXREVERB_ECHO_TIME:
+ if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME))
+ throw effect_exception{AL_INVALID_VALUE, "EAX Reverb echo time out of range"};
+ props->Reverb.EchoTime = val;
+ break;
+
+ case AL_EAXREVERB_ECHO_DEPTH:
+ if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH))
+ throw effect_exception{AL_INVALID_VALUE, "EAX Reverb echo depth out of range"};
+ props->Reverb.EchoDepth = val;
+ break;
+
+ case AL_EAXREVERB_MODULATION_TIME:
+ if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME))
+ throw effect_exception{AL_INVALID_VALUE, "EAX Reverb modulation time out of range"};
+ props->Reverb.ModulationTime = val;
+ break;
+
+ case AL_EAXREVERB_MODULATION_DEPTH:
+ if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH))
+ throw effect_exception{AL_INVALID_VALUE, "EAX Reverb modulation depth out of range"};
+ props->Reverb.ModulationDepth = val;
+ break;
+
+ case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
+ if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF))
+ throw effect_exception{AL_INVALID_VALUE, "EAX Reverb air absorption gainhf out of range"};
+ props->Reverb.AirAbsorptionGainHF = val;
+ break;
+
+ case AL_EAXREVERB_HFREFERENCE:
+ if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE))
+ throw effect_exception{AL_INVALID_VALUE, "EAX Reverb hfreference out of range"};
+ props->Reverb.HFReference = val;
+ break;
+
+ case AL_EAXREVERB_LFREFERENCE:
+ if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE))
+ throw effect_exception{AL_INVALID_VALUE, "EAX Reverb lfreference out of range"};
+ props->Reverb.LFReference = val;
+ break;
+
+ case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
+ if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR))
+ throw effect_exception{AL_INVALID_VALUE, "EAX Reverb room rolloff factor out of range"};
+ props->Reverb.RoomRolloffFactor = val;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x", param};
+ }
+}
+void Reverb_setParamfv(EffectProps *props, ALenum param, const float *vals)
+{
+ switch(param)
+ {
+ case AL_EAXREVERB_REFLECTIONS_PAN:
+ if(!(std::isfinite(vals[0]) && std::isfinite(vals[1]) && std::isfinite(vals[2])))
+ throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections pan out of range"};
+ props->Reverb.ReflectionsPan[0] = vals[0];
+ props->Reverb.ReflectionsPan[1] = vals[1];
+ props->Reverb.ReflectionsPan[2] = vals[2];
+ break;
+ case AL_EAXREVERB_LATE_REVERB_PAN:
+ if(!(std::isfinite(vals[0]) && std::isfinite(vals[1]) && std::isfinite(vals[2])))
+ throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb pan out of range"};
+ props->Reverb.LateReverbPan[0] = vals[0];
+ props->Reverb.LateReverbPan[1] = vals[1];
+ props->Reverb.LateReverbPan[2] = vals[2];
+ break;
+
+ default:
+ Reverb_setParamf(props, param, vals[0]);
+ break;
+ }
+}
+
+void Reverb_getParami(const EffectProps *props, ALenum param, int *val)
+{
+ switch(param)
+ {
+ case AL_EAXREVERB_DECAY_HFLIMIT:
+ *val = props->Reverb.DecayHFLimit;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x",
+ param};
+ }
+}
+void Reverb_getParamiv(const EffectProps *props, ALenum param, int *vals)
+{ Reverb_getParami(props, param, vals); }
+void Reverb_getParamf(const EffectProps *props, ALenum param, float *val)
+{
+ switch(param)
+ {
+ case AL_EAXREVERB_DENSITY:
+ *val = props->Reverb.Density;
+ break;
+
+ case AL_EAXREVERB_DIFFUSION:
+ *val = props->Reverb.Diffusion;
+ break;
+
+ case AL_EAXREVERB_GAIN:
+ *val = props->Reverb.Gain;
+ break;
+
+ case AL_EAXREVERB_GAINHF:
+ *val = props->Reverb.GainHF;
+ break;
+
+ case AL_EAXREVERB_GAINLF:
+ *val = props->Reverb.GainLF;
+ break;
+
+ case AL_EAXREVERB_DECAY_TIME:
+ *val = props->Reverb.DecayTime;
+ break;
+
+ case AL_EAXREVERB_DECAY_HFRATIO:
+ *val = props->Reverb.DecayHFRatio;
+ break;
+
+ case AL_EAXREVERB_DECAY_LFRATIO:
+ *val = props->Reverb.DecayLFRatio;
+ break;
+
+ case AL_EAXREVERB_REFLECTIONS_GAIN:
+ *val = props->Reverb.ReflectionsGain;
+ break;
+
+ case AL_EAXREVERB_REFLECTIONS_DELAY:
+ *val = props->Reverb.ReflectionsDelay;
+ break;
+
+ case AL_EAXREVERB_LATE_REVERB_GAIN:
+ *val = props->Reverb.LateReverbGain;
+ break;
+
+ case AL_EAXREVERB_LATE_REVERB_DELAY:
+ *val = props->Reverb.LateReverbDelay;
+ break;
+
+ case AL_EAXREVERB_ECHO_TIME:
+ *val = props->Reverb.EchoTime;
+ break;
+
+ case AL_EAXREVERB_ECHO_DEPTH:
+ *val = props->Reverb.EchoDepth;
+ break;
+
+ case AL_EAXREVERB_MODULATION_TIME:
+ *val = props->Reverb.ModulationTime;
+ break;
+
+ case AL_EAXREVERB_MODULATION_DEPTH:
+ *val = props->Reverb.ModulationDepth;
+ break;
+
+ case AL_EAXREVERB_AIR_ABSORPTION_GAINHF:
+ *val = props->Reverb.AirAbsorptionGainHF;
+ break;
+
+ case AL_EAXREVERB_HFREFERENCE:
+ *val = props->Reverb.HFReference;
+ break;
+
+ case AL_EAXREVERB_LFREFERENCE:
+ *val = props->Reverb.LFReference;
+ break;
+
+ case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR:
+ *val = props->Reverb.RoomRolloffFactor;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x", param};
+ }
+}
+void Reverb_getParamfv(const EffectProps *props, ALenum param, float *vals)
+{
+ switch(param)
+ {
+ case AL_EAXREVERB_REFLECTIONS_PAN:
+ vals[0] = props->Reverb.ReflectionsPan[0];
+ vals[1] = props->Reverb.ReflectionsPan[1];
+ vals[2] = props->Reverb.ReflectionsPan[2];
+ break;
+ case AL_EAXREVERB_LATE_REVERB_PAN:
+ vals[0] = props->Reverb.LateReverbPan[0];
+ vals[1] = props->Reverb.LateReverbPan[1];
+ vals[2] = props->Reverb.LateReverbPan[2];
+ break;
+
+ default:
+ Reverb_getParamf(props, param, vals);
+ break;
+ }
+}
+
+EffectProps genDefaultProps() noexcept
+{
+ EffectProps props{};
+ props.Reverb.Density = AL_EAXREVERB_DEFAULT_DENSITY;
+ props.Reverb.Diffusion = AL_EAXREVERB_DEFAULT_DIFFUSION;
+ props.Reverb.Gain = AL_EAXREVERB_DEFAULT_GAIN;
+ props.Reverb.GainHF = AL_EAXREVERB_DEFAULT_GAINHF;
+ props.Reverb.GainLF = AL_EAXREVERB_DEFAULT_GAINLF;
+ props.Reverb.DecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME;
+ props.Reverb.DecayHFRatio = AL_EAXREVERB_DEFAULT_DECAY_HFRATIO;
+ props.Reverb.DecayLFRatio = AL_EAXREVERB_DEFAULT_DECAY_LFRATIO;
+ props.Reverb.ReflectionsGain = AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN;
+ props.Reverb.ReflectionsDelay = AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY;
+ props.Reverb.ReflectionsPan[0] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ;
+ props.Reverb.ReflectionsPan[1] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ;
+ props.Reverb.ReflectionsPan[2] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ;
+ props.Reverb.LateReverbGain = AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN;
+ props.Reverb.LateReverbDelay = AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY;
+ props.Reverb.LateReverbPan[0] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ;
+ props.Reverb.LateReverbPan[1] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ;
+ props.Reverb.LateReverbPan[2] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ;
+ props.Reverb.EchoTime = AL_EAXREVERB_DEFAULT_ECHO_TIME;
+ props.Reverb.EchoDepth = AL_EAXREVERB_DEFAULT_ECHO_DEPTH;
+ props.Reverb.ModulationTime = AL_EAXREVERB_DEFAULT_MODULATION_TIME;
+ props.Reverb.ModulationDepth = AL_EAXREVERB_DEFAULT_MODULATION_DEPTH;
+ props.Reverb.AirAbsorptionGainHF = AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF;
+ props.Reverb.HFReference = AL_EAXREVERB_DEFAULT_HFREFERENCE;
+ props.Reverb.LFReference = AL_EAXREVERB_DEFAULT_LFREFERENCE;
+ props.Reverb.RoomRolloffFactor = AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR;
+ props.Reverb.DecayHFLimit = AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT;
+ return props;
+}
+
+
+void StdReverb_setParami(EffectProps *props, ALenum param, int val)
+{
+ switch(param)
+ {
+ case AL_REVERB_DECAY_HFLIMIT:
+ if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT))
+ throw effect_exception{AL_INVALID_VALUE, "Reverb decay hflimit out of range"};
+ props->Reverb.DecayHFLimit = val != AL_FALSE;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param};
+ }
+}
+void StdReverb_setParamiv(EffectProps *props, ALenum param, const int *vals)
+{ StdReverb_setParami(props, param, vals[0]); }
+void StdReverb_setParamf(EffectProps *props, ALenum param, float val)
+{
+ switch(param)
+ {
+ case AL_REVERB_DENSITY:
+ if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY))
+ throw effect_exception{AL_INVALID_VALUE, "Reverb density out of range"};
+ props->Reverb.Density = val;
+ break;
+
+ case AL_REVERB_DIFFUSION:
+ if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION))
+ throw effect_exception{AL_INVALID_VALUE, "Reverb diffusion out of range"};
+ props->Reverb.Diffusion = val;
+ break;
+
+ case AL_REVERB_GAIN:
+ if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN))
+ throw effect_exception{AL_INVALID_VALUE, "Reverb gain out of range"};
+ props->Reverb.Gain = val;
+ break;
+
+ case AL_REVERB_GAINHF:
+ if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF))
+ throw effect_exception{AL_INVALID_VALUE, "Reverb gainhf out of range"};
+ props->Reverb.GainHF = val;
+ break;
+
+ case AL_REVERB_DECAY_TIME:
+ if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME))
+ throw effect_exception{AL_INVALID_VALUE, "Reverb decay time out of range"};
+ props->Reverb.DecayTime = val;
+ break;
+
+ case AL_REVERB_DECAY_HFRATIO:
+ if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO))
+ throw effect_exception{AL_INVALID_VALUE, "Reverb decay hfratio out of range"};
+ props->Reverb.DecayHFRatio = val;
+ break;
+
+ case AL_REVERB_REFLECTIONS_GAIN:
+ if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN))
+ throw effect_exception{AL_INVALID_VALUE, "Reverb reflections gain out of range"};
+ props->Reverb.ReflectionsGain = val;
+ break;
+
+ case AL_REVERB_REFLECTIONS_DELAY:
+ if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY))
+ throw effect_exception{AL_INVALID_VALUE, "Reverb reflections delay out of range"};
+ props->Reverb.ReflectionsDelay = val;
+ break;
+
+ case AL_REVERB_LATE_REVERB_GAIN:
+ if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN))
+ throw effect_exception{AL_INVALID_VALUE, "Reverb late reverb gain out of range"};
+ props->Reverb.LateReverbGain = val;
+ break;
+
+ case AL_REVERB_LATE_REVERB_DELAY:
+ if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY))
+ throw effect_exception{AL_INVALID_VALUE, "Reverb late reverb delay out of range"};
+ props->Reverb.LateReverbDelay = val;
+ break;
+
+ case AL_REVERB_AIR_ABSORPTION_GAINHF:
+ if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF))
+ throw effect_exception{AL_INVALID_VALUE, "Reverb air absorption gainhf out of range"};
+ props->Reverb.AirAbsorptionGainHF = val;
+ break;
+
+ case AL_REVERB_ROOM_ROLLOFF_FACTOR:
+ if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR))
+ throw effect_exception{AL_INVALID_VALUE, "Reverb room rolloff factor out of range"};
+ props->Reverb.RoomRolloffFactor = val;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param};
+ }
+}
+void StdReverb_setParamfv(EffectProps *props, ALenum param, const float *vals)
+{ StdReverb_setParamf(props, param, vals[0]); }
+
+void StdReverb_getParami(const EffectProps *props, ALenum param, int *val)
+{
+ switch(param)
+ {
+ case AL_REVERB_DECAY_HFLIMIT:
+ *val = props->Reverb.DecayHFLimit;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param};
+ }
+}
+void StdReverb_getParamiv(const EffectProps *props, ALenum param, int *vals)
+{ StdReverb_getParami(props, param, vals); }
+void StdReverb_getParamf(const EffectProps *props, ALenum param, float *val)
+{
+ switch(param)
+ {
+ case AL_REVERB_DENSITY:
+ *val = props->Reverb.Density;
+ break;
+
+ case AL_REVERB_DIFFUSION:
+ *val = props->Reverb.Diffusion;
+ break;
+
+ case AL_REVERB_GAIN:
+ *val = props->Reverb.Gain;
+ break;
+
+ case AL_REVERB_GAINHF:
+ *val = props->Reverb.GainHF;
+ break;
+
+ case AL_REVERB_DECAY_TIME:
+ *val = props->Reverb.DecayTime;
+ break;
+
+ case AL_REVERB_DECAY_HFRATIO:
+ *val = props->Reverb.DecayHFRatio;
+ break;
+
+ case AL_REVERB_REFLECTIONS_GAIN:
+ *val = props->Reverb.ReflectionsGain;
+ break;
+
+ case AL_REVERB_REFLECTIONS_DELAY:
+ *val = props->Reverb.ReflectionsDelay;
+ break;
+
+ case AL_REVERB_LATE_REVERB_GAIN:
+ *val = props->Reverb.LateReverbGain;
+ break;
+
+ case AL_REVERB_LATE_REVERB_DELAY:
+ *val = props->Reverb.LateReverbDelay;
+ break;
+
+ case AL_REVERB_AIR_ABSORPTION_GAINHF:
+ *val = props->Reverb.AirAbsorptionGainHF;
+ break;
+
+ case AL_REVERB_ROOM_ROLLOFF_FACTOR:
+ *val = props->Reverb.RoomRolloffFactor;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param};
+ }
+}
+void StdReverb_getParamfv(const EffectProps *props, ALenum param, float *vals)
+{ StdReverb_getParamf(props, param, vals); }
+
+EffectProps genDefaultStdProps() noexcept
+{
+ EffectProps props{};
+ props.Reverb.Density = AL_REVERB_DEFAULT_DENSITY;
+ props.Reverb.Diffusion = AL_REVERB_DEFAULT_DIFFUSION;
+ props.Reverb.Gain = AL_REVERB_DEFAULT_GAIN;
+ props.Reverb.GainHF = AL_REVERB_DEFAULT_GAINHF;
+ props.Reverb.GainLF = 1.0f;
+ props.Reverb.DecayTime = AL_REVERB_DEFAULT_DECAY_TIME;
+ props.Reverb.DecayHFRatio = AL_REVERB_DEFAULT_DECAY_HFRATIO;
+ props.Reverb.DecayLFRatio = 1.0f;
+ props.Reverb.ReflectionsGain = AL_REVERB_DEFAULT_REFLECTIONS_GAIN;
+ props.Reverb.ReflectionsDelay = AL_REVERB_DEFAULT_REFLECTIONS_DELAY;
+ props.Reverb.ReflectionsPan[0] = 0.0f;
+ props.Reverb.ReflectionsPan[1] = 0.0f;
+ props.Reverb.ReflectionsPan[2] = 0.0f;
+ props.Reverb.LateReverbGain = AL_REVERB_DEFAULT_LATE_REVERB_GAIN;
+ props.Reverb.LateReverbDelay = AL_REVERB_DEFAULT_LATE_REVERB_DELAY;
+ props.Reverb.LateReverbPan[0] = 0.0f;
+ props.Reverb.LateReverbPan[1] = 0.0f;
+ props.Reverb.LateReverbPan[2] = 0.0f;
+ props.Reverb.EchoTime = 0.25f;
+ props.Reverb.EchoDepth = 0.0f;
+ props.Reverb.ModulationTime = 0.25f;
+ props.Reverb.ModulationDepth = 0.0f;
+ props.Reverb.AirAbsorptionGainHF = AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF;
+ props.Reverb.HFReference = 5000.0f;
+ props.Reverb.LFReference = 250.0f;
+ props.Reverb.RoomRolloffFactor = AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR;
+ props.Reverb.DecayHFLimit = AL_REVERB_DEFAULT_DECAY_HFLIMIT;
+ return props;
+}
+
+} // namespace
+
+DEFINE_ALEFFECT_VTABLE(Reverb);
+
+const EffectProps ReverbEffectProps{genDefaultProps()};
+
+DEFINE_ALEFFECT_VTABLE(StdReverb);
+
+const EffectProps StdReverbEffectProps{genDefaultStdProps()};
+
+#ifdef ALSOFT_EAX
+namespace {
+
+class EaxReverbEffectException : public EaxException
+{
+public:
+ explicit EaxReverbEffectException(const char* message)
+ : EaxException{"EAX_REVERB_EFFECT", message}
+ {}
+}; // EaxReverbEffectException
+
+struct EnvironmentValidator1 {
+ void operator()(unsigned long ulEnvironment) const
+ {
+ eax_validate_range<EaxReverbEffectException>(
+ "Environment",
+ ulEnvironment,
+ EAXREVERB_MINENVIRONMENT,
+ EAX1REVERB_MAXENVIRONMENT);
+ }
+}; // EnvironmentValidator1
+
+struct VolumeValidator {
+ void operator()(float volume) const
+ {
+ eax_validate_range<EaxReverbEffectException>(
+ "Volume",
+ volume,
+ EAX1REVERB_MINVOLUME,
+ EAX1REVERB_MAXVOLUME);
+ }
+}; // VolumeValidator
+
+struct DecayTimeValidator {
+ void operator()(float flDecayTime) const
+ {
+ eax_validate_range<EaxReverbEffectException>(
+ "Decay Time",
+ flDecayTime,
+ EAXREVERB_MINDECAYTIME,
+ EAXREVERB_MAXDECAYTIME);
+ }
+}; // DecayTimeValidator
+
+struct DampingValidator {
+ void operator()(float damping) const
+ {
+ eax_validate_range<EaxReverbEffectException>(
+ "Damping",
+ damping,
+ EAX1REVERB_MINDAMPING,
+ EAX1REVERB_MAXDAMPING);
+ }
+}; // DampingValidator
+
+struct AllValidator1 {
+ void operator()(const EAX_REVERBPROPERTIES& all) const
+ {
+ EnvironmentValidator1{}(all.environment);
+ VolumeValidator{}(all.fVolume);
+ DecayTimeValidator{}(all.fDecayTime_sec);
+ DampingValidator{}(all.fDamping);
+ }
+}; // AllValidator1
+
+struct RoomValidator {
+ void operator()(long lRoom) const
+ {
+ eax_validate_range<EaxReverbEffectException>(
+ "Room",
+ lRoom,
+ EAXREVERB_MINROOM,
+ EAXREVERB_MAXROOM);
+ }
+}; // RoomValidator
+
+struct RoomHFValidator {
+ void operator()(long lRoomHF) const
+ {
+ eax_validate_range<EaxReverbEffectException>(
+ "Room HF",
+ lRoomHF,
+ EAXREVERB_MINROOMHF,
+ EAXREVERB_MAXROOMHF);
+ }
+}; // RoomHFValidator
+
+struct RoomRolloffFactorValidator {
+ void operator()(float flRoomRolloffFactor) const
+ {
+ eax_validate_range<EaxReverbEffectException>(
+ "Room Rolloff Factor",
+ flRoomRolloffFactor,
+ EAXREVERB_MINROOMROLLOFFFACTOR,
+ EAXREVERB_MAXROOMROLLOFFFACTOR);
+ }
+}; // RoomRolloffFactorValidator
+
+struct DecayHFRatioValidator {
+ void operator()(float flDecayHFRatio) const
+ {
+ eax_validate_range<EaxReverbEffectException>(
+ "Decay HF Ratio",
+ flDecayHFRatio,
+ EAXREVERB_MINDECAYHFRATIO,
+ EAXREVERB_MAXDECAYHFRATIO);
+ }
+}; // DecayHFRatioValidator
+
+struct ReflectionsValidator {
+ void operator()(long lReflections) const
+ {
+ eax_validate_range<EaxReverbEffectException>(
+ "Reflections",
+ lReflections,
+ EAXREVERB_MINREFLECTIONS,
+ EAXREVERB_MAXREFLECTIONS);
+ }
+}; // ReflectionsValidator
+
+struct ReflectionsDelayValidator {
+ void operator()(float flReflectionsDelay) const
+ {
+ eax_validate_range<EaxReverbEffectException>(
+ "Reflections Delay",
+ flReflectionsDelay,
+ EAXREVERB_MINREFLECTIONSDELAY,
+ EAXREVERB_MAXREFLECTIONSDELAY);
+ }
+}; // ReflectionsDelayValidator
+
+struct ReverbValidator {
+ void operator()(long lReverb) const
+ {
+ eax_validate_range<EaxReverbEffectException>(
+ "Reverb",
+ lReverb,
+ EAXREVERB_MINREVERB,
+ EAXREVERB_MAXREVERB);
+ }
+}; // ReverbValidator
+
+struct ReverbDelayValidator {
+ void operator()(float flReverbDelay) const
+ {
+ eax_validate_range<EaxReverbEffectException>(
+ "Reverb Delay",
+ flReverbDelay,
+ EAXREVERB_MINREVERBDELAY,
+ EAXREVERB_MAXREVERBDELAY);
+ }
+}; // ReverbDelayValidator
+
+struct EnvironmentSizeValidator {
+ void operator()(float flEnvironmentSize) const
+ {
+ eax_validate_range<EaxReverbEffectException>(
+ "Environment Size",
+ flEnvironmentSize,
+ EAXREVERB_MINENVIRONMENTSIZE,
+ EAXREVERB_MAXENVIRONMENTSIZE);
+ }
+}; // EnvironmentSizeValidator
+
+struct EnvironmentDiffusionValidator {
+ void operator()(float flEnvironmentDiffusion) const
+ {
+ eax_validate_range<EaxReverbEffectException>(
+ "Environment Diffusion",
+ flEnvironmentDiffusion,
+ EAXREVERB_MINENVIRONMENTDIFFUSION,
+ EAXREVERB_MAXENVIRONMENTDIFFUSION);
+ }
+}; // EnvironmentDiffusionValidator
+
+struct AirAbsorptionHFValidator {
+ void operator()(float flAirAbsorptionHF) const
+ {
+ eax_validate_range<EaxReverbEffectException>(
+ "Air Absorbtion HF",
+ flAirAbsorptionHF,
+ EAXREVERB_MINAIRABSORPTIONHF,
+ EAXREVERB_MAXAIRABSORPTIONHF);
+ }
+}; // AirAbsorptionHFValidator
+
+struct FlagsValidator2 {
+ void operator()(unsigned long ulFlags) const
+ {
+ eax_validate_range<EaxReverbEffectException>(
+ "Flags",
+ ulFlags,
+ 0UL,
+ ~EAX2LISTENERFLAGS_RESERVED);
+ }
+}; // FlagsValidator2
+
+struct AllValidator2 {
+ void operator()(const EAX20LISTENERPROPERTIES& all) const
+ {
+ RoomValidator{}(all.lRoom);
+ RoomHFValidator{}(all.lRoomHF);
+ RoomRolloffFactorValidator{}(all.flRoomRolloffFactor);
+ DecayTimeValidator{}(all.flDecayTime);
+ DecayHFRatioValidator{}(all.flDecayHFRatio);
+ ReflectionsValidator{}(all.lReflections);
+ ReflectionsDelayValidator{}(all.flReflectionsDelay);
+ ReverbValidator{}(all.lReverb);
+ ReverbDelayValidator{}(all.flReverbDelay);
+ EnvironmentValidator1{}(all.dwEnvironment);
+ EnvironmentSizeValidator{}(all.flEnvironmentSize);
+ EnvironmentDiffusionValidator{}(all.flEnvironmentDiffusion);
+ AirAbsorptionHFValidator{}(all.flAirAbsorptionHF);
+ FlagsValidator2{}(all.dwFlags);
+ }
+}; // AllValidator2
+
+struct EnvironmentValidator3 {
+ void operator()(unsigned long ulEnvironment) const
+ {
+ eax_validate_range<EaxReverbEffectException>(
+ "Environment",
+ ulEnvironment,
+ EAXREVERB_MINENVIRONMENT,
+ EAX30REVERB_MAXENVIRONMENT);
+ }
+}; // EnvironmentValidator1
+
+struct RoomLFValidator {
+ void operator()(long lRoomLF) const
+ {
+ eax_validate_range<EaxReverbEffectException>(
+ "Room LF",
+ lRoomLF,
+ EAXREVERB_MINROOMLF,
+ EAXREVERB_MAXROOMLF);
+ }
+}; // RoomLFValidator
+
+struct DecayLFRatioValidator {
+ void operator()(float flDecayLFRatio) const
+ {
+ eax_validate_range<EaxReverbEffectException>(
+ "Decay LF Ratio",
+ flDecayLFRatio,
+ EAXREVERB_MINDECAYLFRATIO,
+ EAXREVERB_MAXDECAYLFRATIO);
+ }
+}; // DecayLFRatioValidator
+
+struct VectorValidator {
+ void operator()(const EAXVECTOR&) const
+ {}
+}; // VectorValidator
+
+struct EchoTimeValidator {
+ void operator()(float flEchoTime) const
+ {
+ eax_validate_range<EaxReverbEffectException>(
+ "Echo Time",
+ flEchoTime,
+ EAXREVERB_MINECHOTIME,
+ EAXREVERB_MAXECHOTIME);
+ }
+}; // EchoTimeValidator
+
+struct EchoDepthValidator {
+ void operator()(float flEchoDepth) const
+ {
+ eax_validate_range<EaxReverbEffectException>(
+ "Echo Depth",
+ flEchoDepth,
+ EAXREVERB_MINECHODEPTH,
+ EAXREVERB_MAXECHODEPTH);
+ }
+}; // EchoDepthValidator
+
+struct ModulationTimeValidator {
+ void operator()(float flModulationTime) const
+ {
+ eax_validate_range<EaxReverbEffectException>(
+ "Modulation Time",
+ flModulationTime,
+ EAXREVERB_MINMODULATIONTIME,
+ EAXREVERB_MAXMODULATIONTIME);
+ }
+}; // ModulationTimeValidator
+
+struct ModulationDepthValidator {
+ void operator()(float flModulationDepth) const
+ {
+ eax_validate_range<EaxReverbEffectException>(
+ "Modulation Depth",
+ flModulationDepth,
+ EAXREVERB_MINMODULATIONDEPTH,
+ EAXREVERB_MAXMODULATIONDEPTH);
+ }
+}; // ModulationDepthValidator
+
+struct HFReferenceValidator {
+ void operator()(float flHFReference) const
+ {
+ eax_validate_range<EaxReverbEffectException>(
+ "HF Reference",
+ flHFReference,
+ EAXREVERB_MINHFREFERENCE,
+ EAXREVERB_MAXHFREFERENCE);
+ }
+}; // HFReferenceValidator
+
+struct LFReferenceValidator {
+ void operator()(float flLFReference) const
+ {
+ eax_validate_range<EaxReverbEffectException>(
+ "LF Reference",
+ flLFReference,
+ EAXREVERB_MINLFREFERENCE,
+ EAXREVERB_MAXLFREFERENCE);
+ }
+}; // LFReferenceValidator
+
+struct FlagsValidator3 {
+ void operator()(unsigned long ulFlags) const
+ {
+ eax_validate_range<EaxReverbEffectException>(
+ "Flags",
+ ulFlags,
+ 0UL,
+ ~EAXREVERBFLAGS_RESERVED);
+ }
+}; // FlagsValidator3
+
+struct AllValidator3 {
+ void operator()(const EAXREVERBPROPERTIES& all) const
+ {
+ EnvironmentValidator3{}(all.ulEnvironment);
+ EnvironmentSizeValidator{}(all.flEnvironmentSize);
+ EnvironmentDiffusionValidator{}(all.flEnvironmentDiffusion);
+ RoomValidator{}(all.lRoom);
+ RoomHFValidator{}(all.lRoomHF);
+ RoomLFValidator{}(all.lRoomLF);
+ DecayTimeValidator{}(all.flDecayTime);
+ DecayHFRatioValidator{}(all.flDecayHFRatio);
+ DecayLFRatioValidator{}(all.flDecayLFRatio);
+ ReflectionsValidator{}(all.lReflections);
+ ReflectionsDelayValidator{}(all.flReflectionsDelay);
+ VectorValidator{}(all.vReflectionsPan);
+ ReverbValidator{}(all.lReverb);
+ ReverbDelayValidator{}(all.flReverbDelay);
+ VectorValidator{}(all.vReverbPan);
+ EchoTimeValidator{}(all.flEchoTime);
+ EchoDepthValidator{}(all.flEchoDepth);
+ ModulationTimeValidator{}(all.flModulationTime);
+ ModulationDepthValidator{}(all.flModulationDepth);
+ AirAbsorptionHFValidator{}(all.flAirAbsorptionHF);
+ HFReferenceValidator{}(all.flHFReference);
+ LFReferenceValidator{}(all.flLFReference);
+ RoomRolloffFactorValidator{}(all.flRoomRolloffFactor);
+ FlagsValidator3{}(all.ulFlags);
+ }
+}; // AllValidator3
+
+struct EnvironmentDeferrer2 {
+ void operator()(EAX20LISTENERPROPERTIES& props, unsigned long dwEnvironment) const
+ {
+ props = EAX2REVERB_PRESETS[dwEnvironment];
+ }
+}; // EnvironmentDeferrer2
+
+struct EnvironmentSizeDeferrer2 {
+ void operator()(EAX20LISTENERPROPERTIES& props, float flEnvironmentSize) const
+ {
+ if (props.flEnvironmentSize == flEnvironmentSize)
+ {
+ return;
+ }
+
+ const auto scale = flEnvironmentSize / props.flEnvironmentSize;
+ props.flEnvironmentSize = flEnvironmentSize;
+
+ if ((props.dwFlags & EAX2LISTENERFLAGS_DECAYTIMESCALE) != 0)
+ {
+ props.flDecayTime = clamp(
+ props.flDecayTime * scale,
+ EAXREVERB_MINDECAYTIME,
+ EAXREVERB_MAXDECAYTIME);
+ }
+
+ if ((props.dwFlags & EAX2LISTENERFLAGS_REFLECTIONSSCALE) != 0 &&
+ (props.dwFlags & EAX2LISTENERFLAGS_REFLECTIONSDELAYSCALE) != 0)
+ {
+ props.lReflections = clamp(
+ props.lReflections - static_cast<long>(gain_to_level_mb(scale)),
+ EAXREVERB_MINREFLECTIONS,
+ EAXREVERB_MAXREFLECTIONS);
+ }
+
+ if ((props.dwFlags & EAX2LISTENERFLAGS_REFLECTIONSDELAYSCALE) != 0)
+ {
+ props.flReflectionsDelay = clamp(
+ props.flReflectionsDelay * scale,
+ EAXREVERB_MINREFLECTIONSDELAY,
+ EAXREVERB_MAXREFLECTIONSDELAY);
+ }
+
+ if ((props.dwFlags & EAX2LISTENERFLAGS_REVERBSCALE) != 0)
+ {
+ const auto log_scalar = ((props.dwFlags & EAXREVERBFLAGS_DECAYTIMESCALE) != 0) ? 2'000.0F : 3'000.0F;
+
+ props.lReverb = clamp(
+ props.lReverb - static_cast<long>(std::log10(scale) * log_scalar),
+ EAXREVERB_MINREVERB,
+ EAXREVERB_MAXREVERB);
+ }
+
+ if ((props.dwFlags & EAX2LISTENERFLAGS_REVERBDELAYSCALE) != 0)
+ {
+ props.flReverbDelay = clamp(
+ props.flReverbDelay * scale,
+ EAXREVERB_MINREVERBDELAY,
+ EAXREVERB_MAXREVERBDELAY);
+ }
+ }
+}; // EnvironmentSizeDeferrer2
+
+struct EnvironmentDeferrer3 {
+ void operator()(EAXREVERBPROPERTIES& props, unsigned long ulEnvironment) const
+ {
+ if (ulEnvironment == EAX_ENVIRONMENT_UNDEFINED)
+ {
+ props.ulEnvironment = EAX_ENVIRONMENT_UNDEFINED;
+ return;
+ }
+
+ props = EAXREVERB_PRESETS[ulEnvironment];
+ }
+}; // EnvironmentDeferrer3
+
+struct EnvironmentSizeDeferrer3 {
+ void operator()(EAXREVERBPROPERTIES& props, float flEnvironmentSize) const
+ {
+ if (props.flEnvironmentSize == flEnvironmentSize)
+ {
+ return;
+ }
+
+ const auto scale = flEnvironmentSize / props.flEnvironmentSize;
+ props.ulEnvironment = EAX_ENVIRONMENT_UNDEFINED;
+ props.flEnvironmentSize = flEnvironmentSize;
+
+ if ((props.ulFlags & EAXREVERBFLAGS_DECAYTIMESCALE) != 0)
+ {
+ props.flDecayTime = clamp(
+ props.flDecayTime * scale,
+ EAXREVERB_MINDECAYTIME,
+ EAXREVERB_MAXDECAYTIME);
+ }
+
+ if ((props.ulFlags & EAXREVERBFLAGS_REFLECTIONSSCALE) != 0 &&
+ (props.ulFlags & EAXREVERBFLAGS_REFLECTIONSDELAYSCALE) != 0)
+ {
+ props.lReflections = clamp(
+ props.lReflections - static_cast<long>(gain_to_level_mb(scale)),
+ EAXREVERB_MINREFLECTIONS,
+ EAXREVERB_MAXREFLECTIONS);
+ }
+
+ if ((props.ulFlags & EAXREVERBFLAGS_REFLECTIONSDELAYSCALE) != 0)
+ {
+ props.flReflectionsDelay = clamp(
+ props.flReflectionsDelay * scale,
+ EAXREVERB_MINREFLECTIONSDELAY,
+ EAXREVERB_MAXREFLECTIONSDELAY);
+ }
+
+ if ((props.ulFlags & EAXREVERBFLAGS_REVERBSCALE) != 0)
+ {
+ const auto log_scalar = ((props.ulFlags & EAXREVERBFLAGS_DECAYTIMESCALE) != 0) ? 2'000.0F : 3'000.0F;
+ props.lReverb = clamp(
+ props.lReverb - static_cast<long>(std::log10(scale) * log_scalar),
+ EAXREVERB_MINREVERB,
+ EAXREVERB_MAXREVERB);
+ }
+
+ if ((props.ulFlags & EAXREVERBFLAGS_REVERBDELAYSCALE) != 0)
+ {
+ props.flReverbDelay = clamp(
+ props.flReverbDelay * scale,
+ EAXREVERB_MINREVERBDELAY,
+ EAXREVERB_MAXREVERBDELAY);
+ }
+
+ if ((props.ulFlags & EAXREVERBFLAGS_ECHOTIMESCALE) != 0)
+ {
+ props.flEchoTime = clamp(
+ props.flEchoTime * scale,
+ EAXREVERB_MINECHOTIME,
+ EAXREVERB_MAXECHOTIME);
+ }
+
+ if ((props.ulFlags & EAXREVERBFLAGS_MODULATIONTIMESCALE) != 0)
+ {
+ props.flModulationTime = clamp(
+ props.flModulationTime * scale,
+ EAXREVERB_MINMODULATIONTIME,
+ EAXREVERB_MAXMODULATIONTIME);
+ }
+ }
+}; // EnvironmentSizeDeferrer3
+
+} // namespace
+
+
+struct EaxReverbCommitter::Exception : public EaxReverbEffectException
+{
+ using EaxReverbEffectException::EaxReverbEffectException;
+};
+
+[[noreturn]] void EaxReverbCommitter::fail(const char* message)
+{
+ throw Exception{message};
+}
+
+void EaxReverbCommitter::translate(const EAX_REVERBPROPERTIES& src, EaxEffectProps& dst) noexcept
+{
+ assert(src.environment <= EAX1REVERB_MAXENVIRONMENT);
+ dst.mType = EaxEffectType::Reverb;
+ dst.mReverb = EAXREVERB_PRESETS[src.environment];
+ dst.mReverb.flDecayTime = src.fDecayTime_sec;
+ dst.mReverb.flDecayHFRatio = src.fDamping;
+ dst.mReverb.lReverb = mini(static_cast<int>(gain_to_level_mb(src.fVolume)), 0);
+}
+
+void EaxReverbCommitter::translate(const EAX20LISTENERPROPERTIES& src, EaxEffectProps& dst) noexcept
+{
+ assert(src.dwEnvironment <= EAX1REVERB_MAXENVIRONMENT);
+ const auto& env = EAXREVERB_PRESETS[src.dwEnvironment];
+ dst.mType = EaxEffectType::Reverb;
+ dst.mReverb.ulEnvironment = src.dwEnvironment;
+ dst.mReverb.flEnvironmentSize = src.flEnvironmentSize;
+ dst.mReverb.flEnvironmentDiffusion = src.flEnvironmentDiffusion;
+ dst.mReverb.lRoom = src.lRoom;
+ dst.mReverb.lRoomHF = src.lRoomHF;
+ dst.mReverb.lRoomLF = env.lRoomLF;
+ dst.mReverb.flDecayTime = src.flDecayTime;
+ dst.mReverb.flDecayHFRatio = src.flDecayHFRatio;
+ dst.mReverb.flDecayLFRatio = env.flDecayLFRatio;
+ dst.mReverb.lReflections = src.lReflections;
+ dst.mReverb.flReflectionsDelay = src.flReflectionsDelay;
+ dst.mReverb.vReflectionsPan = env.vReflectionsPan;
+ dst.mReverb.lReverb = src.lReverb;
+ dst.mReverb.flReverbDelay = src.flReverbDelay;
+ dst.mReverb.vReverbPan = env.vReverbPan;
+ dst.mReverb.flEchoTime = env.flEchoTime;
+ dst.mReverb.flEchoDepth = env.flEchoDepth;
+ dst.mReverb.flModulationTime = env.flModulationTime;
+ dst.mReverb.flModulationDepth = env.flModulationDepth;
+ dst.mReverb.flAirAbsorptionHF = src.flAirAbsorptionHF;
+ dst.mReverb.flHFReference = env.flHFReference;
+ dst.mReverb.flLFReference = env.flLFReference;
+ dst.mReverb.flRoomRolloffFactor = src.flRoomRolloffFactor;
+ dst.mReverb.ulFlags = src.dwFlags;
+}
+
+void EaxReverbCommitter::translate(const EAXREVERBPROPERTIES& src, EaxEffectProps& dst) noexcept
+{
+ dst.mType = EaxEffectType::Reverb;
+ dst.mReverb = src;
+}
+
+bool EaxReverbCommitter::commit(const EAX_REVERBPROPERTIES &props)
+{
+ EaxEffectProps dst{};
+ translate(props, dst);
+ return commit(dst);
+}
+
+bool EaxReverbCommitter::commit(const EAX20LISTENERPROPERTIES &props)
+{
+ EaxEffectProps dst{};
+ translate(props, dst);
+ return commit(dst);
+}
+
+bool EaxReverbCommitter::commit(const EAXREVERBPROPERTIES &props)
+{
+ EaxEffectProps dst{};
+ translate(props, dst);
+ return commit(dst);
+}
+
+bool EaxReverbCommitter::commit(const EaxEffectProps &props)
+{
+ if(props.mType == mEaxProps.mType
+ && memcmp(&props.mReverb, &mEaxProps.mReverb, sizeof(mEaxProps.mReverb)) == 0)
+ return false;
+
+ mEaxProps = props;
+
+ const auto size = props.mReverb.flEnvironmentSize;
+ const auto density = (size * size * size) / 16.0F;
+ mAlProps.Reverb.Density = std::min(density, AL_EAXREVERB_MAX_DENSITY);
+ mAlProps.Reverb.Diffusion = props.mReverb.flEnvironmentDiffusion;
+ mAlProps.Reverb.Gain = level_mb_to_gain(static_cast<float>(props.mReverb.lRoom));
+ mAlProps.Reverb.GainHF = level_mb_to_gain(static_cast<float>(props.mReverb.lRoomHF));
+ mAlProps.Reverb.GainLF = level_mb_to_gain(static_cast<float>(props.mReverb.lRoomLF));
+ mAlProps.Reverb.DecayTime = props.mReverb.flDecayTime;
+ mAlProps.Reverb.DecayHFRatio = props.mReverb.flDecayHFRatio;
+ mAlProps.Reverb.DecayLFRatio = mEaxProps.mReverb.flDecayLFRatio;
+ mAlProps.Reverb.ReflectionsGain = level_mb_to_gain(static_cast<float>(props.mReverb.lReflections));
+ mAlProps.Reverb.ReflectionsDelay = props.mReverb.flReflectionsDelay;
+ mAlProps.Reverb.ReflectionsPan[0] = props.mReverb.vReflectionsPan.x;
+ mAlProps.Reverb.ReflectionsPan[1] = props.mReverb.vReflectionsPan.y;
+ mAlProps.Reverb.ReflectionsPan[2] = props.mReverb.vReflectionsPan.z;
+ mAlProps.Reverb.LateReverbGain = level_mb_to_gain(static_cast<float>(props.mReverb.lReverb));
+ mAlProps.Reverb.LateReverbDelay = props.mReverb.flReverbDelay;
+ mAlProps.Reverb.LateReverbPan[0] = props.mReverb.vReverbPan.x;
+ mAlProps.Reverb.LateReverbPan[1] = props.mReverb.vReverbPan.y;
+ mAlProps.Reverb.LateReverbPan[2] = props.mReverb.vReverbPan.z;
+ mAlProps.Reverb.EchoTime = props.mReverb.flEchoTime;
+ mAlProps.Reverb.EchoDepth = props.mReverb.flEchoDepth;
+ mAlProps.Reverb.ModulationTime = props.mReverb.flModulationTime;
+ mAlProps.Reverb.ModulationDepth = props.mReverb.flModulationDepth;
+ mAlProps.Reverb.AirAbsorptionGainHF = level_mb_to_gain(props.mReverb.flAirAbsorptionHF);
+ mAlProps.Reverb.HFReference = props.mReverb.flHFReference;
+ mAlProps.Reverb.LFReference = props.mReverb.flLFReference;
+ mAlProps.Reverb.RoomRolloffFactor = props.mReverb.flRoomRolloffFactor;
+ mAlProps.Reverb.DecayHFLimit = ((props.mReverb.ulFlags & EAXREVERBFLAGS_DECAYHFLIMIT) != 0);
+ return true;
+}
+
+void EaxReverbCommitter::SetDefaults(EAX_REVERBPROPERTIES &props)
+{
+ props = EAX1REVERB_PRESETS[EAX_ENVIRONMENT_GENERIC];
+}
+
+void EaxReverbCommitter::SetDefaults(EAX20LISTENERPROPERTIES &props)
+{
+ props = EAX2REVERB_PRESETS[EAX2_ENVIRONMENT_GENERIC];
+ props.lRoom = -10'000L;
+}
+
+void EaxReverbCommitter::SetDefaults(EAXREVERBPROPERTIES &props)
+{
+ props = EAXREVERB_PRESETS[EAX_ENVIRONMENT_GENERIC];
+}
+
+void EaxReverbCommitter::SetDefaults(EaxEffectProps &props)
+{
+ props.mType = EaxEffectType::Reverb;
+ SetDefaults(props.mReverb);
+}
+
+
+void EaxReverbCommitter::Get(const EaxCall &call, const EAX_REVERBPROPERTIES &props)
+{
+ switch(call.get_property_id())
+ {
+ case DSPROPERTY_EAX_ALL: call.set_value<Exception>(props); break;
+ case DSPROPERTY_EAX_ENVIRONMENT: call.set_value<Exception>(props.environment); break;
+ case DSPROPERTY_EAX_VOLUME: call.set_value<Exception>(props.fVolume); break;
+ case DSPROPERTY_EAX_DECAYTIME: call.set_value<Exception>(props.fDecayTime_sec); break;
+ case DSPROPERTY_EAX_DAMPING: call.set_value<Exception>(props.fDamping); break;
+ default: fail_unknown_property_id();
+ }
+}
+
+void EaxReverbCommitter::Get(const EaxCall &call, const EAX20LISTENERPROPERTIES &props)
+{
+ switch(call.get_property_id())
+ {
+ case DSPROPERTY_EAX20LISTENER_NONE: break;
+ case DSPROPERTY_EAX20LISTENER_ALLPARAMETERS: call.set_value<Exception>(props); break;
+ case DSPROPERTY_EAX20LISTENER_ROOM: call.set_value<Exception>(props.lRoom); break;
+ case DSPROPERTY_EAX20LISTENER_ROOMHF: call.set_value<Exception>(props.lRoomHF); break;
+ case DSPROPERTY_EAX20LISTENER_ROOMROLLOFFFACTOR: call.set_value<Exception>(props.flRoomRolloffFactor); break;
+ case DSPROPERTY_EAX20LISTENER_DECAYTIME: call.set_value<Exception>(props.flDecayTime); break;
+ case DSPROPERTY_EAX20LISTENER_DECAYHFRATIO: call.set_value<Exception>(props.flDecayHFRatio); break;
+ case DSPROPERTY_EAX20LISTENER_REFLECTIONS: call.set_value<Exception>(props.lReflections); break;
+ case DSPROPERTY_EAX20LISTENER_REFLECTIONSDELAY: call.set_value<Exception>(props.flReflectionsDelay); break;
+ case DSPROPERTY_EAX20LISTENER_REVERB: call.set_value<Exception>(props.lReverb); break;
+ case DSPROPERTY_EAX20LISTENER_REVERBDELAY: call.set_value<Exception>(props.flReverbDelay); break;
+ case DSPROPERTY_EAX20LISTENER_ENVIRONMENT: call.set_value<Exception>(props.dwEnvironment); break;
+ case DSPROPERTY_EAX20LISTENER_ENVIRONMENTSIZE: call.set_value<Exception>(props.flEnvironmentSize); break;
+ case DSPROPERTY_EAX20LISTENER_ENVIRONMENTDIFFUSION: call.set_value<Exception>(props.flEnvironmentDiffusion); break;
+ case DSPROPERTY_EAX20LISTENER_AIRABSORPTIONHF: call.set_value<Exception>(props.flAirAbsorptionHF); break;
+ case DSPROPERTY_EAX20LISTENER_FLAGS: call.set_value<Exception>(props.dwFlags); break;
+ default: fail_unknown_property_id();
+ }
+}
+
+void EaxReverbCommitter::Get(const EaxCall &call, const EAXREVERBPROPERTIES &props)
+{
+ switch(call.get_property_id())
+ {
+ case EAXREVERB_NONE: break;
+ case EAXREVERB_ALLPARAMETERS: call.set_value<Exception>(props); break;
+ case EAXREVERB_ENVIRONMENT: call.set_value<Exception>(props.ulEnvironment); break;
+ case EAXREVERB_ENVIRONMENTSIZE: call.set_value<Exception>(props.flEnvironmentSize); break;
+ case EAXREVERB_ENVIRONMENTDIFFUSION: call.set_value<Exception>(props.flEnvironmentDiffusion); break;
+ case EAXREVERB_ROOM: call.set_value<Exception>(props.lRoom); break;
+ case EAXREVERB_ROOMHF: call.set_value<Exception>(props.lRoomHF); break;
+ case EAXREVERB_ROOMLF: call.set_value<Exception>(props.lRoomLF); break;
+ case EAXREVERB_DECAYTIME: call.set_value<Exception>(props.flDecayTime); break;
+ case EAXREVERB_DECAYHFRATIO: call.set_value<Exception>(props.flDecayHFRatio); break;
+ case EAXREVERB_DECAYLFRATIO: call.set_value<Exception>(props.flDecayLFRatio); break;
+ case EAXREVERB_REFLECTIONS: call.set_value<Exception>(props.lReflections); break;
+ case EAXREVERB_REFLECTIONSDELAY: call.set_value<Exception>(props.flReflectionsDelay); break;
+ case EAXREVERB_REFLECTIONSPAN: call.set_value<Exception>(props.vReflectionsPan); break;
+ case EAXREVERB_REVERB: call.set_value<Exception>(props.lReverb); break;
+ case EAXREVERB_REVERBDELAY: call.set_value<Exception>(props.flReverbDelay); break;
+ case EAXREVERB_REVERBPAN: call.set_value<Exception>(props.vReverbPan); break;
+ case EAXREVERB_ECHOTIME: call.set_value<Exception>(props.flEchoTime); break;
+ case EAXREVERB_ECHODEPTH: call.set_value<Exception>(props.flEchoDepth); break;
+ case EAXREVERB_MODULATIONTIME: call.set_value<Exception>(props.flModulationTime); break;
+ case EAXREVERB_MODULATIONDEPTH: call.set_value<Exception>(props.flModulationDepth); break;
+ case EAXREVERB_AIRABSORPTIONHF: call.set_value<Exception>(props.flAirAbsorptionHF); break;
+ case EAXREVERB_HFREFERENCE: call.set_value<Exception>(props.flHFReference); break;
+ case EAXREVERB_LFREFERENCE: call.set_value<Exception>(props.flLFReference); break;
+ case EAXREVERB_ROOMROLLOFFFACTOR: call.set_value<Exception>(props.flRoomRolloffFactor); break;
+ case EAXREVERB_FLAGS: call.set_value<Exception>(props.ulFlags); break;
+ default: fail_unknown_property_id();
+ }
+}
+
+void EaxReverbCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+{
+ Get(call, props.mReverb);
+}
+
+
+void EaxReverbCommitter::Set(const EaxCall &call, EAX_REVERBPROPERTIES &props)
+{
+ switch(call.get_property_id())
+ {
+ case DSPROPERTY_EAX_ALL: defer<AllValidator1>(call, props); break;
+ case DSPROPERTY_EAX_ENVIRONMENT: defer<EnvironmentValidator1>(call, props.environment); break;
+ case DSPROPERTY_EAX_VOLUME: defer<VolumeValidator>(call, props.fVolume); break;
+ case DSPROPERTY_EAX_DECAYTIME: defer<DecayTimeValidator>(call, props.fDecayTime_sec); break;
+ case DSPROPERTY_EAX_DAMPING: defer<DampingValidator>(call, props.fDamping); break;
+ default: fail_unknown_property_id();
+ }
+}
+
+void EaxReverbCommitter::Set(const EaxCall &call, EAX20LISTENERPROPERTIES &props)
+{
+ switch(call.get_property_id())
+ {
+ case DSPROPERTY_EAX20LISTENER_NONE:
+ break;
+
+ case DSPROPERTY_EAX20LISTENER_ALLPARAMETERS:
+ defer<AllValidator2>(call, props);
+ break;
+
+ case DSPROPERTY_EAX20LISTENER_ROOM:
+ defer<RoomValidator>(call, props.lRoom);
+ break;
+
+ case DSPROPERTY_EAX20LISTENER_ROOMHF:
+ defer<RoomHFValidator>(call, props.lRoomHF);
+ break;
+
+ case DSPROPERTY_EAX20LISTENER_ROOMROLLOFFFACTOR:
+ defer<RoomRolloffFactorValidator>(call, props.flRoomRolloffFactor);
+ break;
+
+ case DSPROPERTY_EAX20LISTENER_DECAYTIME:
+ defer<DecayTimeValidator>(call, props.flDecayTime);
+ break;
+
+ case DSPROPERTY_EAX20LISTENER_DECAYHFRATIO:
+ defer<DecayHFRatioValidator>(call, props.flDecayHFRatio);
+ break;
+
+ case DSPROPERTY_EAX20LISTENER_REFLECTIONS:
+ defer<ReflectionsValidator>(call, props.lReflections);
+ break;
+
+ case DSPROPERTY_EAX20LISTENER_REFLECTIONSDELAY:
+ defer<ReflectionsDelayValidator>(call, props.flReverbDelay);
+ break;
+
+ case DSPROPERTY_EAX20LISTENER_REVERB:
+ defer<ReverbValidator>(call, props.lReverb);
+ break;
+
+ case DSPROPERTY_EAX20LISTENER_REVERBDELAY:
+ defer<ReverbDelayValidator>(call, props.flReverbDelay);
+ break;
+
+ case DSPROPERTY_EAX20LISTENER_ENVIRONMENT:
+ defer<EnvironmentValidator1, EnvironmentDeferrer2>(call, props, props.dwEnvironment);
+ break;
+
+ case DSPROPERTY_EAX20LISTENER_ENVIRONMENTSIZE:
+ defer<EnvironmentSizeValidator, EnvironmentSizeDeferrer2>(call, props, props.flEnvironmentSize);
+ break;
+
+ case DSPROPERTY_EAX20LISTENER_ENVIRONMENTDIFFUSION:
+ defer<EnvironmentDiffusionValidator>(call, props.flEnvironmentDiffusion);
+ break;
+
+ case DSPROPERTY_EAX20LISTENER_AIRABSORPTIONHF:
+ defer<AirAbsorptionHFValidator>(call, props.flAirAbsorptionHF);
+ break;
+
+ case DSPROPERTY_EAX20LISTENER_FLAGS:
+ defer<FlagsValidator2>(call, props.dwFlags);
+ break;
+
+ default:
+ fail_unknown_property_id();
+ }
+}
+
+void EaxReverbCommitter::Set(const EaxCall &call, EAXREVERBPROPERTIES &props)
+{
+ switch(call.get_property_id())
+ {
+ case EAXREVERB_NONE:
+ break;
+
+ case EAXREVERB_ALLPARAMETERS:
+ defer<AllValidator3>(call, props);
+ break;
+
+ case EAXREVERB_ENVIRONMENT:
+ defer<EnvironmentValidator3, EnvironmentDeferrer3>(call, props, props.ulEnvironment);
+ break;
+
+ case EAXREVERB_ENVIRONMENTSIZE:
+ defer<EnvironmentSizeValidator, EnvironmentSizeDeferrer3>(call, props, props.flEnvironmentSize);
+ break;
+
+ case EAXREVERB_ENVIRONMENTDIFFUSION:
+ defer3<EnvironmentDiffusionValidator>(call, props, props.flEnvironmentDiffusion);
+ break;
+
+ case EAXREVERB_ROOM:
+ defer3<RoomValidator>(call, props, props.lRoom);
+ break;
+
+ case EAXREVERB_ROOMHF:
+ defer3<RoomHFValidator>(call, props, props.lRoomHF);
+ break;
+
+ case EAXREVERB_ROOMLF:
+ defer3<RoomLFValidator>(call, props, props.lRoomLF);
+ break;
+
+ case EAXREVERB_DECAYTIME:
+ defer3<DecayTimeValidator>(call, props, props.flDecayTime);
+ break;
+
+ case EAXREVERB_DECAYHFRATIO:
+ defer3<DecayHFRatioValidator>(call, props, props.flDecayHFRatio);
+ break;
+
+ case EAXREVERB_DECAYLFRATIO:
+ defer3<DecayLFRatioValidator>(call, props, props.flDecayLFRatio);
+ break;
+
+ case EAXREVERB_REFLECTIONS:
+ defer3<ReflectionsValidator>(call, props, props.lReflections);
+ break;
+
+ case EAXREVERB_REFLECTIONSDELAY:
+ defer3<ReflectionsDelayValidator>(call, props, props.flReflectionsDelay);
+ break;
+
+ case EAXREVERB_REFLECTIONSPAN:
+ defer3<VectorValidator>(call, props, props.vReflectionsPan);
+ break;
+
+ case EAXREVERB_REVERB:
+ defer3<ReverbValidator>(call, props, props.lReverb);
+ break;
+
+ case EAXREVERB_REVERBDELAY:
+ defer3<ReverbDelayValidator>(call, props, props.flReverbDelay);
+ break;
+
+ case EAXREVERB_REVERBPAN:
+ defer3<VectorValidator>(call, props, props.vReverbPan);
+ break;
+
+ case EAXREVERB_ECHOTIME:
+ defer3<EchoTimeValidator>(call, props, props.flEchoTime);
+ break;
+
+ case EAXREVERB_ECHODEPTH:
+ defer3<EchoDepthValidator>(call, props, props.flEchoDepth);
+ break;
+
+ case EAXREVERB_MODULATIONTIME:
+ defer3<ModulationTimeValidator>(call, props, props.flModulationTime);
+ break;
+
+ case EAXREVERB_MODULATIONDEPTH:
+ defer3<ModulationDepthValidator>(call, props, props.flModulationDepth);
+ break;
+
+ case EAXREVERB_AIRABSORPTIONHF:
+ defer3<AirAbsorptionHFValidator>(call, props, props.flAirAbsorptionHF);
+ break;
+
+ case EAXREVERB_HFREFERENCE:
+ defer3<HFReferenceValidator>(call, props, props.flHFReference);
+ break;
+
+ case EAXREVERB_LFREFERENCE:
+ defer3<LFReferenceValidator>(call, props, props.flLFReference);
+ break;
+
+ case EAXREVERB_ROOMROLLOFFFACTOR:
+ defer3<RoomRolloffFactorValidator>(call, props, props.flRoomRolloffFactor);
+ break;
+
+ case EAXREVERB_FLAGS:
+ defer3<FlagsValidator3>(call, props, props.ulFlags);
+ break;
+
+ default:
+ fail_unknown_property_id();
+ }
+}
+
+void EaxReverbCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+{
+ Set(call, props.mReverb);
+}
+
+#endif // ALSOFT_EAX
diff --git a/al/effects/vmorpher.cpp b/al/effects/vmorpher.cpp
new file mode 100644
index 00000000..21ea3680
--- /dev/null
+++ b/al/effects/vmorpher.cpp
@@ -0,0 +1,520 @@
+
+#include "config.h"
+
+#include <stdexcept>
+
+#include "AL/al.h"
+#include "AL/efx.h"
+
+#include "alc/effects/base.h"
+#include "aloptional.h"
+#include "effects.h"
+
+#ifdef ALSOFT_EAX
+#include <cassert>
+#include "alnumeric.h"
+#include "al/eax/exception.h"
+#include "al/eax/utils.h"
+#endif // ALSOFT_EAX
+
+
+namespace {
+
+al::optional<VMorpherPhenome> PhenomeFromEnum(ALenum val)
+{
+#define HANDLE_PHENOME(x) case AL_VOCAL_MORPHER_PHONEME_ ## x: \
+ return VMorpherPhenome::x
+ switch(val)
+ {
+ HANDLE_PHENOME(A);
+ HANDLE_PHENOME(E);
+ HANDLE_PHENOME(I);
+ HANDLE_PHENOME(O);
+ HANDLE_PHENOME(U);
+ HANDLE_PHENOME(AA);
+ HANDLE_PHENOME(AE);
+ HANDLE_PHENOME(AH);
+ HANDLE_PHENOME(AO);
+ HANDLE_PHENOME(EH);
+ HANDLE_PHENOME(ER);
+ HANDLE_PHENOME(IH);
+ HANDLE_PHENOME(IY);
+ HANDLE_PHENOME(UH);
+ HANDLE_PHENOME(UW);
+ HANDLE_PHENOME(B);
+ HANDLE_PHENOME(D);
+ HANDLE_PHENOME(F);
+ HANDLE_PHENOME(G);
+ HANDLE_PHENOME(J);
+ HANDLE_PHENOME(K);
+ HANDLE_PHENOME(L);
+ HANDLE_PHENOME(M);
+ HANDLE_PHENOME(N);
+ HANDLE_PHENOME(P);
+ HANDLE_PHENOME(R);
+ HANDLE_PHENOME(S);
+ HANDLE_PHENOME(T);
+ HANDLE_PHENOME(V);
+ HANDLE_PHENOME(Z);
+ }
+ return al::nullopt;
+#undef HANDLE_PHENOME
+}
+ALenum EnumFromPhenome(VMorpherPhenome phenome)
+{
+#define HANDLE_PHENOME(x) case VMorpherPhenome::x: return AL_VOCAL_MORPHER_PHONEME_ ## x
+ switch(phenome)
+ {
+ HANDLE_PHENOME(A);
+ HANDLE_PHENOME(E);
+ HANDLE_PHENOME(I);
+ HANDLE_PHENOME(O);
+ HANDLE_PHENOME(U);
+ HANDLE_PHENOME(AA);
+ HANDLE_PHENOME(AE);
+ HANDLE_PHENOME(AH);
+ HANDLE_PHENOME(AO);
+ HANDLE_PHENOME(EH);
+ HANDLE_PHENOME(ER);
+ HANDLE_PHENOME(IH);
+ HANDLE_PHENOME(IY);
+ HANDLE_PHENOME(UH);
+ HANDLE_PHENOME(UW);
+ HANDLE_PHENOME(B);
+ HANDLE_PHENOME(D);
+ HANDLE_PHENOME(F);
+ HANDLE_PHENOME(G);
+ HANDLE_PHENOME(J);
+ HANDLE_PHENOME(K);
+ HANDLE_PHENOME(L);
+ HANDLE_PHENOME(M);
+ HANDLE_PHENOME(N);
+ HANDLE_PHENOME(P);
+ HANDLE_PHENOME(R);
+ HANDLE_PHENOME(S);
+ HANDLE_PHENOME(T);
+ HANDLE_PHENOME(V);
+ HANDLE_PHENOME(Z);
+ }
+ throw std::runtime_error{"Invalid phenome: "+std::to_string(static_cast<int>(phenome))};
+#undef HANDLE_PHENOME
+}
+
+al::optional<VMorpherWaveform> WaveformFromEmum(ALenum value)
+{
+ switch(value)
+ {
+ case AL_VOCAL_MORPHER_WAVEFORM_SINUSOID: return VMorpherWaveform::Sinusoid;
+ case AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE: return VMorpherWaveform::Triangle;
+ case AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH: return VMorpherWaveform::Sawtooth;
+ }
+ return al::nullopt;
+}
+ALenum EnumFromWaveform(VMorpherWaveform type)
+{
+ switch(type)
+ {
+ case VMorpherWaveform::Sinusoid: return AL_VOCAL_MORPHER_WAVEFORM_SINUSOID;
+ case VMorpherWaveform::Triangle: return AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE;
+ case VMorpherWaveform::Sawtooth: return AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH;
+ }
+ throw std::runtime_error{"Invalid vocal morpher waveform: " +
+ std::to_string(static_cast<int>(type))};
+}
+
+void Vmorpher_setParami(EffectProps *props, ALenum param, int val)
+{
+ switch(param)
+ {
+ case AL_VOCAL_MORPHER_PHONEMEA:
+ if(auto phenomeopt = PhenomeFromEnum(val))
+ props->Vmorpher.PhonemeA = *phenomeopt;
+ else
+ throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-a out of range: 0x%04x", val};
+ break;
+
+ case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING:
+ if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING && val <= AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING))
+ throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-a coarse tuning out of range"};
+ props->Vmorpher.PhonemeACoarseTuning = val;
+ break;
+
+ case AL_VOCAL_MORPHER_PHONEMEB:
+ if(auto phenomeopt = PhenomeFromEnum(val))
+ props->Vmorpher.PhonemeB = *phenomeopt;
+ else
+ throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-b out of range: 0x%04x", val};
+ break;
+
+ case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING:
+ if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING && val <= AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING))
+ throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-b coarse tuning out of range"};
+ props->Vmorpher.PhonemeBCoarseTuning = val;
+ break;
+
+ case AL_VOCAL_MORPHER_WAVEFORM:
+ if(auto formopt = WaveformFromEmum(val))
+ props->Vmorpher.Waveform = *formopt;
+ else
+ throw effect_exception{AL_INVALID_VALUE, "Vocal morpher waveform out of range: 0x%04x", val};
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer property 0x%04x",
+ param};
+ }
+}
+void Vmorpher_setParamiv(EffectProps*, ALenum param, const int*)
+{
+ throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x",
+ param};
+}
+void Vmorpher_setParamf(EffectProps *props, ALenum param, float val)
+{
+ switch(param)
+ {
+ case AL_VOCAL_MORPHER_RATE:
+ if(!(val >= AL_VOCAL_MORPHER_MIN_RATE && val <= AL_VOCAL_MORPHER_MAX_RATE))
+ throw effect_exception{AL_INVALID_VALUE, "Vocal morpher rate out of range"};
+ props->Vmorpher.Rate = val;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher float property 0x%04x",
+ param};
+ }
+}
+void Vmorpher_setParamfv(EffectProps *props, ALenum param, const float *vals)
+{ Vmorpher_setParamf(props, param, vals[0]); }
+
+void Vmorpher_getParami(const EffectProps *props, ALenum param, int* val)
+{
+ switch(param)
+ {
+ case AL_VOCAL_MORPHER_PHONEMEA:
+ *val = EnumFromPhenome(props->Vmorpher.PhonemeA);
+ break;
+
+ case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING:
+ *val = props->Vmorpher.PhonemeACoarseTuning;
+ break;
+
+ case AL_VOCAL_MORPHER_PHONEMEB:
+ *val = EnumFromPhenome(props->Vmorpher.PhonemeB);
+ break;
+
+ case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING:
+ *val = props->Vmorpher.PhonemeBCoarseTuning;
+ break;
+
+ case AL_VOCAL_MORPHER_WAVEFORM:
+ *val = EnumFromWaveform(props->Vmorpher.Waveform);
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer property 0x%04x",
+ param};
+ }
+}
+void Vmorpher_getParamiv(const EffectProps*, ALenum param, int*)
+{
+ throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x",
+ param};
+}
+void Vmorpher_getParamf(const EffectProps *props, ALenum param, float *val)
+{
+ switch(param)
+ {
+ case AL_VOCAL_MORPHER_RATE:
+ *val = props->Vmorpher.Rate;
+ break;
+
+ default:
+ throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher float property 0x%04x",
+ param};
+ }
+}
+void Vmorpher_getParamfv(const EffectProps *props, ALenum param, float *vals)
+{ Vmorpher_getParamf(props, param, vals); }
+
+EffectProps genDefaultProps() noexcept
+{
+ EffectProps props{};
+ props.Vmorpher.Rate = AL_VOCAL_MORPHER_DEFAULT_RATE;
+ props.Vmorpher.PhonemeA = *PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEA);
+ props.Vmorpher.PhonemeB = *PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEB);
+ props.Vmorpher.PhonemeACoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING;
+ props.Vmorpher.PhonemeBCoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING;
+ props.Vmorpher.Waveform = *WaveformFromEmum(AL_VOCAL_MORPHER_DEFAULT_WAVEFORM);
+ return props;
+}
+
+} // namespace
+
+DEFINE_ALEFFECT_VTABLE(Vmorpher);
+
+const EffectProps VmorpherEffectProps{genDefaultProps()};
+
+#ifdef ALSOFT_EAX
+namespace {
+
+using VocalMorpherCommitter = EaxCommitter<EaxVocalMorpherCommitter>;
+
+struct PhonemeAValidator {
+ void operator()(unsigned long ulPhonemeA) const
+ {
+ eax_validate_range<VocalMorpherCommitter::Exception>(
+ "Phoneme A",
+ ulPhonemeA,
+ EAXVOCALMORPHER_MINPHONEMEA,
+ EAXVOCALMORPHER_MAXPHONEMEA);
+ }
+}; // PhonemeAValidator
+
+struct PhonemeACoarseTuningValidator {
+ void operator()(long lPhonemeACoarseTuning) const
+ {
+ eax_validate_range<VocalMorpherCommitter::Exception>(
+ "Phoneme A Coarse Tuning",
+ lPhonemeACoarseTuning,
+ EAXVOCALMORPHER_MINPHONEMEACOARSETUNING,
+ EAXVOCALMORPHER_MAXPHONEMEACOARSETUNING);
+ }
+}; // PhonemeACoarseTuningValidator
+
+struct PhonemeBValidator {
+ void operator()(unsigned long ulPhonemeB) const
+ {
+ eax_validate_range<VocalMorpherCommitter::Exception>(
+ "Phoneme B",
+ ulPhonemeB,
+ EAXVOCALMORPHER_MINPHONEMEB,
+ EAXVOCALMORPHER_MAXPHONEMEB);
+ }
+}; // PhonemeBValidator
+
+struct PhonemeBCoarseTuningValidator {
+ void operator()(long lPhonemeBCoarseTuning) const
+ {
+ eax_validate_range<VocalMorpherCommitter::Exception>(
+ "Phoneme B Coarse Tuning",
+ lPhonemeBCoarseTuning,
+ EAXVOCALMORPHER_MINPHONEMEBCOARSETUNING,
+ EAXVOCALMORPHER_MAXPHONEMEBCOARSETUNING);
+ }
+}; // PhonemeBCoarseTuningValidator
+
+struct WaveformValidator {
+ void operator()(unsigned long ulWaveform) const
+ {
+ eax_validate_range<VocalMorpherCommitter::Exception>(
+ "Waveform",
+ ulWaveform,
+ EAXVOCALMORPHER_MINWAVEFORM,
+ EAXVOCALMORPHER_MAXWAVEFORM);
+ }
+}; // WaveformValidator
+
+struct RateValidator {
+ void operator()(float flRate) const
+ {
+ eax_validate_range<VocalMorpherCommitter::Exception>(
+ "Rate",
+ flRate,
+ EAXVOCALMORPHER_MINRATE,
+ EAXVOCALMORPHER_MAXRATE);
+ }
+}; // RateValidator
+
+struct AllValidator {
+ void operator()(const EAXVOCALMORPHERPROPERTIES& all) const
+ {
+ PhonemeAValidator{}(all.ulPhonemeA);
+ PhonemeACoarseTuningValidator{}(all.lPhonemeACoarseTuning);
+ PhonemeBValidator{}(all.ulPhonemeB);
+ PhonemeBCoarseTuningValidator{}(all.lPhonemeBCoarseTuning);
+ WaveformValidator{}(all.ulWaveform);
+ RateValidator{}(all.flRate);
+ }
+}; // AllValidator
+
+} // namespace
+
+template<>
+struct VocalMorpherCommitter::Exception : public EaxException {
+ explicit Exception(const char *message) : EaxException{"EAX_VOCAL_MORPHER_EFFECT", message}
+ { }
+};
+
+template<>
+[[noreturn]] void VocalMorpherCommitter::fail(const char *message)
+{
+ throw Exception{message};
+}
+
+template<>
+bool VocalMorpherCommitter::commit(const EaxEffectProps &props)
+{
+ if(props.mType == mEaxProps.mType
+ && mEaxProps.mVocalMorpher.ulPhonemeA == props.mVocalMorpher.ulPhonemeA
+ && mEaxProps.mVocalMorpher.lPhonemeACoarseTuning == props.mVocalMorpher.lPhonemeACoarseTuning
+ && mEaxProps.mVocalMorpher.ulPhonemeB == props.mVocalMorpher.ulPhonemeB
+ && mEaxProps.mVocalMorpher.lPhonemeBCoarseTuning == props.mVocalMorpher.lPhonemeBCoarseTuning
+ && mEaxProps.mVocalMorpher.ulWaveform == props.mVocalMorpher.ulWaveform
+ && mEaxProps.mVocalMorpher.flRate == props.mVocalMorpher.flRate)
+ return false;
+
+ mEaxProps = props;
+
+ auto get_phoneme = [](unsigned long phoneme) noexcept
+ {
+#define HANDLE_PHENOME(x) case x: return VMorpherPhenome::x
+ switch(phoneme)
+ {
+ HANDLE_PHENOME(A);
+ HANDLE_PHENOME(E);
+ HANDLE_PHENOME(I);
+ HANDLE_PHENOME(O);
+ HANDLE_PHENOME(U);
+ HANDLE_PHENOME(AA);
+ HANDLE_PHENOME(AE);
+ HANDLE_PHENOME(AH);
+ HANDLE_PHENOME(AO);
+ HANDLE_PHENOME(EH);
+ HANDLE_PHENOME(ER);
+ HANDLE_PHENOME(IH);
+ HANDLE_PHENOME(IY);
+ HANDLE_PHENOME(UH);
+ HANDLE_PHENOME(UW);
+ HANDLE_PHENOME(B);
+ HANDLE_PHENOME(D);
+ HANDLE_PHENOME(F);
+ HANDLE_PHENOME(G);
+ HANDLE_PHENOME(J);
+ HANDLE_PHENOME(K);
+ HANDLE_PHENOME(L);
+ HANDLE_PHENOME(M);
+ HANDLE_PHENOME(N);
+ HANDLE_PHENOME(P);
+ HANDLE_PHENOME(R);
+ HANDLE_PHENOME(S);
+ HANDLE_PHENOME(T);
+ HANDLE_PHENOME(V);
+ HANDLE_PHENOME(Z);
+ }
+ return VMorpherPhenome::A;
+#undef HANDLE_PHENOME
+ };
+ auto get_waveform = [](unsigned long form) noexcept
+ {
+ if(form == EAX_VOCALMORPHER_SINUSOID) return VMorpherWaveform::Sinusoid;
+ if(form == EAX_VOCALMORPHER_TRIANGLE) return VMorpherWaveform::Triangle;
+ if(form == EAX_VOCALMORPHER_SAWTOOTH) return VMorpherWaveform::Sawtooth;
+ return VMorpherWaveform::Sinusoid;
+ };
+
+ mAlProps.Vmorpher.PhonemeA = get_phoneme(props.mVocalMorpher.ulPhonemeA);
+ mAlProps.Vmorpher.PhonemeACoarseTuning = static_cast<int>(props.mVocalMorpher.lPhonemeACoarseTuning);
+ mAlProps.Vmorpher.PhonemeB = get_phoneme(props.mVocalMorpher.ulPhonemeB);
+ mAlProps.Vmorpher.PhonemeBCoarseTuning = static_cast<int>(props.mVocalMorpher.lPhonemeBCoarseTuning);
+ mAlProps.Vmorpher.Waveform = get_waveform(props.mVocalMorpher.ulWaveform);
+ mAlProps.Vmorpher.Rate = props.mVocalMorpher.flRate;
+
+ return true;
+}
+
+template<>
+void VocalMorpherCommitter::SetDefaults(EaxEffectProps &props)
+{
+ props.mType = EaxEffectType::VocalMorpher;
+ props.mVocalMorpher.ulPhonemeA = EAXVOCALMORPHER_DEFAULTPHONEMEA;
+ props.mVocalMorpher.lPhonemeACoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING;
+ props.mVocalMorpher.ulPhonemeB = EAXVOCALMORPHER_DEFAULTPHONEMEB;
+ props.mVocalMorpher.lPhonemeBCoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING;
+ props.mVocalMorpher.ulWaveform = EAXVOCALMORPHER_DEFAULTWAVEFORM;
+ props.mVocalMorpher.flRate = EAXVOCALMORPHER_DEFAULTRATE;
+}
+
+template<>
+void VocalMorpherCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
+{
+ switch(call.get_property_id())
+ {
+ case EAXVOCALMORPHER_NONE:
+ break;
+
+ case EAXVOCALMORPHER_ALLPARAMETERS:
+ call.set_value<Exception>(props.mVocalMorpher);
+ break;
+
+ case EAXVOCALMORPHER_PHONEMEA:
+ call.set_value<Exception>(props.mVocalMorpher.ulPhonemeA);
+ break;
+
+ case EAXVOCALMORPHER_PHONEMEACOARSETUNING:
+ call.set_value<Exception>(props.mVocalMorpher.lPhonemeACoarseTuning);
+ break;
+
+ case EAXVOCALMORPHER_PHONEMEB:
+ call.set_value<Exception>(props.mVocalMorpher.ulPhonemeB);
+ break;
+
+ case EAXVOCALMORPHER_PHONEMEBCOARSETUNING:
+ call.set_value<Exception>(props.mVocalMorpher.lPhonemeBCoarseTuning);
+ break;
+
+ case EAXVOCALMORPHER_WAVEFORM:
+ call.set_value<Exception>(props.mVocalMorpher.ulWaveform);
+ break;
+
+ case EAXVOCALMORPHER_RATE:
+ call.set_value<Exception>(props.mVocalMorpher.flRate);
+ break;
+
+ default:
+ fail_unknown_property_id();
+ }
+}
+
+template<>
+void VocalMorpherCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+{
+ switch(call.get_property_id())
+ {
+ case EAXVOCALMORPHER_NONE:
+ break;
+
+ case EAXVOCALMORPHER_ALLPARAMETERS:
+ defer<AllValidator>(call, props.mVocalMorpher);
+ break;
+
+ case EAXVOCALMORPHER_PHONEMEA:
+ defer<PhonemeAValidator>(call, props.mVocalMorpher.ulPhonemeA);
+ break;
+
+ case EAXVOCALMORPHER_PHONEMEACOARSETUNING:
+ defer<PhonemeACoarseTuningValidator>(call, props.mVocalMorpher.lPhonemeACoarseTuning);
+ break;
+
+ case EAXVOCALMORPHER_PHONEMEB:
+ defer<PhonemeBValidator>(call, props.mVocalMorpher.ulPhonemeB);
+ break;
+
+ case EAXVOCALMORPHER_PHONEMEBCOARSETUNING:
+ defer<PhonemeBCoarseTuningValidator>(call, props.mVocalMorpher.lPhonemeBCoarseTuning);
+ break;
+
+ case EAXVOCALMORPHER_WAVEFORM:
+ defer<WaveformValidator>(call, props.mVocalMorpher.ulWaveform);
+ break;
+
+ case EAXVOCALMORPHER_RATE:
+ defer<RateValidator>(call, props.mVocalMorpher.flRate);
+ break;
+
+ default:
+ fail_unknown_property_id();
+ }
+}
+
+#endif // ALSOFT_EAX