aboutsummaryrefslogtreecommitdiffstats
path: root/al/effects
diff options
context:
space:
mode:
Diffstat (limited to 'al/effects')
-rw-r--r--al/effects/autowah.cpp109
-rw-r--r--al/effects/chorus.cpp250
-rw-r--r--al/effects/compressor.cpp72
-rw-r--r--al/effects/convolution.cpp93
-rw-r--r--al/effects/dedicated.cpp72
-rw-r--r--al/effects/distortion.cpp114
-rw-r--r--al/effects/echo.cpp108
-rw-r--r--al/effects/effects.h75
-rw-r--r--al/effects/equalizer.cpp169
-rw-r--r--al/effects/fshifter.cpp104
-rw-r--r--al/effects/modulator.cpp110
-rw-r--r--al/effects/null.cpp93
-rw-r--r--al/effects/pshifter.cpp84
-rw-r--r--al/effects/reverb.cpp556
-rw-r--r--al/effects/vmorpher.cpp141
15 files changed, 2150 insertions, 0 deletions
diff --git a/al/effects/autowah.cpp b/al/effects/autowah.cpp
new file mode 100644
index 00000000..65d4b702
--- /dev/null
+++ b/al/effects/autowah.cpp
@@ -0,0 +1,109 @@
+
+#include "config.h"
+
+#include <cmath>
+#include <cstdlib>
+
+#include <algorithm>
+
+#include "AL/efx.h"
+
+#include "effects/base.h"
+#include "effects.h"
+
+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()};
diff --git a/al/effects/chorus.cpp b/al/effects/chorus.cpp
new file mode 100644
index 00000000..2d983885
--- /dev/null
+++ b/al/effects/chorus.cpp
@@ -0,0 +1,250 @@
+
+#include "config.h"
+
+#include "AL/al.h"
+#include "AL/efx.h"
+
+#include "effects.h"
+#include "effects/base.h"
+
+
+namespace {
+
+void Chorus_setParami(EffectProps *props, ALenum param, int val)
+{
+ switch(param)
+ {
+ case AL_CHORUS_WAVEFORM:
+ if(!(val >= AL_CHORUS_MIN_WAVEFORM && val <= AL_CHORUS_MAX_WAVEFORM))
+ throw effect_exception{AL_INVALID_VALUE, "Invalid chorus waveform"};
+ props->Chorus.Waveform = 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"};
+ 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"};
+ 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"};
+ 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"};
+ 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"};
+ 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 = 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); }
+
+const EffectProps genDefaultChorusProps() noexcept
+{
+ EffectProps props{};
+ props.Chorus.Waveform = 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(!(val >= AL_FLANGER_MIN_WAVEFORM && val <= AL_FLANGER_MAX_WAVEFORM))
+ throw effect_exception{AL_INVALID_VALUE, "Invalid flanger waveform"};
+ props->Chorus.Waveform = 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"};
+ 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"};
+ 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"};
+ 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"};
+ 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"};
+ 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 = 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 = 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()};
diff --git a/al/effects/compressor.cpp b/al/effects/compressor.cpp
new file mode 100644
index 00000000..94e07431
--- /dev/null
+++ b/al/effects/compressor.cpp
@@ -0,0 +1,72 @@
+
+#include "config.h"
+
+#include "AL/al.h"
+#include "AL/efx.h"
+
+#include "effects.h"
+#include "effects/base.h"
+
+
+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()};
diff --git a/al/effects/convolution.cpp b/al/effects/convolution.cpp
new file mode 100644
index 00000000..4d87b1c4
--- /dev/null
+++ b/al/effects/convolution.cpp
@@ -0,0 +1,93 @@
+
+#include "config.h"
+
+#include "AL/al.h"
+#include "inprogext.h"
+
+#include "effects.h"
+#include "effects/base.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..334d9e56
--- /dev/null
+++ b/al/effects/dedicated.cpp
@@ -0,0 +1,72 @@
+
+#include "config.h"
+
+#include <cmath>
+
+#include "AL/al.h"
+#include "AL/efx.h"
+
+#include "effects.h"
+#include "effects/base.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..8961a4d9
--- /dev/null
+++ b/al/effects/distortion.cpp
@@ -0,0 +1,114 @@
+
+#include "config.h"
+
+#include "AL/al.h"
+#include "AL/efx.h"
+
+#include "effects.h"
+#include "effects/base.h"
+
+
+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()};
diff --git a/al/effects/echo.cpp b/al/effects/echo.cpp
new file mode 100644
index 00000000..b242a9cd
--- /dev/null
+++ b/al/effects/echo.cpp
@@ -0,0 +1,108 @@
+
+#include "config.h"
+
+#include "AL/al.h"
+#include "AL/efx.h"
+
+#include "effects.h"
+#include "effects/base.h"
+
+
+namespace {
+
+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()};
diff --git a/al/effects/effects.h b/al/effects/effects.h
new file mode 100644
index 00000000..1850271b
--- /dev/null
+++ b/al/effects/effects.h
@@ -0,0 +1,75 @@
+#ifndef AL_EFFECTS_EFFECTS_H
+#define AL_EFFECTS_EFFECTS_H
+
+#include "AL/al.h"
+
+#include "alexcpt.h"
+
+union EffectProps;
+
+
+class effect_exception final : public al::base_exception {
+public:
+ [[gnu::format(printf, 3, 4)]]
+ effect_exception(ALenum code, const char *msg, ...);
+};
+
+
+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..3a7c0a8f
--- /dev/null
+++ b/al/effects/equalizer.cpp
@@ -0,0 +1,169 @@
+
+#include "config.h"
+
+#include "AL/al.h"
+#include "AL/efx.h"
+
+#include "effects.h"
+#include "effects/base.h"
+
+
+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()};
diff --git a/al/effects/fshifter.cpp b/al/effects/fshifter.cpp
new file mode 100644
index 00000000..31138fe6
--- /dev/null
+++ b/al/effects/fshifter.cpp
@@ -0,0 +1,104 @@
+
+#include "config.h"
+
+#include "AL/al.h"
+#include "AL/efx.h"
+
+#include "effects.h"
+#include "effects/base.h"
+
+
+namespace {
+
+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(!(val >= AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION && val <= AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION))
+ throw effect_exception{AL_INVALID_VALUE,
+ "Frequency shifter left direction out of range"};
+ props->Fshifter.LeftDirection = val;
+ break;
+
+ case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
+ if(!(val >= AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION && val <= AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION))
+ throw effect_exception{AL_INVALID_VALUE,
+ "Frequency shifter right direction out of range"};
+ props->Fshifter.RightDirection = 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 = props->Fshifter.LeftDirection;
+ break;
+ case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
+ *val = 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 = AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION;
+ props.Fshifter.RightDirection = AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION;
+ return props;
+}
+
+} // namespace
+
+DEFINE_ALEFFECT_VTABLE(Fshifter);
+
+const EffectProps FshifterEffectProps{genDefaultProps()};
diff --git a/al/effects/modulator.cpp b/al/effects/modulator.cpp
new file mode 100644
index 00000000..95f379e2
--- /dev/null
+++ b/al/effects/modulator.cpp
@@ -0,0 +1,110 @@
+
+#include "config.h"
+
+#include "AL/al.h"
+#include "AL/efx.h"
+
+#include "effects.h"
+#include "effects/base.h"
+
+
+namespace {
+
+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"};
+ 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"};
+ 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(!(val >= AL_RING_MODULATOR_MIN_WAVEFORM && val <= AL_RING_MODULATOR_MAX_WAVEFORM))
+ throw effect_exception{AL_INVALID_VALUE, "Invalid modulator waveform"};
+ props->Modulator.Waveform = 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 = 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 = AL_RING_MODULATOR_DEFAULT_WAVEFORM;
+ return props;
+}
+
+} // namespace
+
+DEFINE_ALEFFECT_VTABLE(Modulator);
+
+const EffectProps ModulatorEffectProps{genDefaultProps()};
diff --git a/al/effects/null.cpp b/al/effects/null.cpp
new file mode 100644
index 00000000..0ac5278f
--- /dev/null
+++ b/al/effects/null.cpp
@@ -0,0 +1,93 @@
+
+#include "config.h"
+
+#include "AL/al.h"
+#include "AL/efx.h"
+
+#include "effects.h"
+#include "effects/base.h"
+
+
+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()};
diff --git a/al/effects/pshifter.cpp b/al/effects/pshifter.cpp
new file mode 100644
index 00000000..e6b0b3b0
--- /dev/null
+++ b/al/effects/pshifter.cpp
@@ -0,0 +1,84 @@
+
+#include "config.h"
+
+#include "AL/al.h"
+#include "AL/efx.h"
+
+#include "effects.h"
+#include "effects/base.h"
+
+
+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()};
diff --git a/al/effects/reverb.cpp b/al/effects/reverb.cpp
new file mode 100644
index 00000000..caa0c81e
--- /dev/null
+++ b/al/effects/reverb.cpp
@@ -0,0 +1,556 @@
+
+#include "config.h"
+
+#include <cmath>
+
+#include "AL/al.h"
+#include "AL/efx.h"
+
+#include "effects.h"
+#include "effects/base.h"
+
+
+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_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_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_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_AIR_ABSORPTION_GAINHF:
+ *val = props->Reverb.AirAbsorptionGainHF;
+ 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_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()};
diff --git a/al/effects/vmorpher.cpp b/al/effects/vmorpher.cpp
new file mode 100644
index 00000000..f6b73705
--- /dev/null
+++ b/al/effects/vmorpher.cpp
@@ -0,0 +1,141 @@
+
+#include "config.h"
+
+#include "AL/al.h"
+#include "AL/efx.h"
+
+#include "effects.h"
+#include "effects/base.h"
+
+
+namespace {
+
+void Vmorpher_setParami(EffectProps *props, ALenum param, int val)
+{
+ switch(param)
+ {
+ case AL_VOCAL_MORPHER_WAVEFORM:
+ if(!(val >= AL_VOCAL_MORPHER_MIN_WAVEFORM && val <= AL_VOCAL_MORPHER_MAX_WAVEFORM))
+ throw effect_exception{AL_INVALID_VALUE, "Vocal morpher waveform out of range"};
+ props->Vmorpher.Waveform = val;
+ break;
+
+ case AL_VOCAL_MORPHER_PHONEMEA:
+ if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEA && val <= AL_VOCAL_MORPHER_MAX_PHONEMEA))
+ throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-a out of range"};
+ props->Vmorpher.PhonemeA = val;
+ break;
+
+ case AL_VOCAL_MORPHER_PHONEMEB:
+ if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEB && val <= AL_VOCAL_MORPHER_MAX_PHONEMEB))
+ throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-b out of range"};
+ props->Vmorpher.PhonemeB = 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_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;
+
+ 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 = props->Vmorpher.PhonemeA;
+ break;
+
+ case AL_VOCAL_MORPHER_PHONEMEB:
+ *val = props->Vmorpher.PhonemeB;
+ break;
+
+ case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING:
+ *val = props->Vmorpher.PhonemeACoarseTuning;
+ break;
+
+ case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING:
+ *val = props->Vmorpher.PhonemeBCoarseTuning;
+ break;
+
+ case AL_VOCAL_MORPHER_WAVEFORM:
+ *val = 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 = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA;
+ props.Vmorpher.PhonemeB = 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 = AL_VOCAL_MORPHER_DEFAULT_WAVEFORM;
+ return props;
+}
+
+} // namespace
+
+DEFINE_ALEFFECT_VTABLE(Vmorpher);
+
+const EffectProps VmorpherEffectProps{genDefaultProps()};