From 93d96ced9cef7200b04dbfec51325a3c9c55af19 Mon Sep 17 00:00:00 2001
From: Chris Robinson <chris.kcat@gmail.com>
Date: Sat, 17 Nov 2018 06:53:20 -0800
Subject: Convert the dedicated, distortion, echo, and equalizer to C++

---
 Alc/effects/dedicated.c    | 184 -----------------------
 Alc/effects/dedicated.cpp  | 187 +++++++++++++++++++++++
 Alc/effects/distortion.c   | 286 ------------------------------------
 Alc/effects/distortion.cpp | 289 ++++++++++++++++++++++++++++++++++++
 Alc/effects/echo.c         | 310 --------------------------------------
 Alc/effects/echo.cpp       | 314 +++++++++++++++++++++++++++++++++++++++
 Alc/effects/equalizer.c    | 355 --------------------------------------------
 Alc/effects/equalizer.cpp  | 359 +++++++++++++++++++++++++++++++++++++++++++++
 CMakeLists.txt             |   8 +-
 9 files changed, 1153 insertions(+), 1139 deletions(-)
 delete mode 100644 Alc/effects/dedicated.c
 create mode 100644 Alc/effects/dedicated.cpp
 delete mode 100644 Alc/effects/distortion.c
 create mode 100644 Alc/effects/distortion.cpp
 delete mode 100644 Alc/effects/echo.c
 create mode 100644 Alc/effects/echo.cpp
 delete mode 100644 Alc/effects/equalizer.c
 create mode 100644 Alc/effects/equalizer.cpp

diff --git a/Alc/effects/dedicated.c b/Alc/effects/dedicated.c
deleted file mode 100644
index 59c13b77..00000000
--- a/Alc/effects/dedicated.c
+++ /dev/null
@@ -1,184 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2011 by Chris Robinson.
- * This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Library General Public
- *  License as published by the Free Software Foundation; either
- *  version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- *  License along with this library; if not, write to the
- *  Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <stdlib.h>
-
-#include "alMain.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alu.h"
-#include "filters/defs.h"
-
-
-typedef struct ALdedicatedState {
-    DERIVE_FROM_TYPE(ALeffectState);
-
-    ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
-    ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
-} ALdedicatedState;
-
-static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state);
-static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *state, ALCdevice *device);
-static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
-static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
-DECLARE_DEFAULT_ALLOCATORS(ALdedicatedState)
-
-DEFINE_ALEFFECTSTATE_VTABLE(ALdedicatedState);
-
-
-static void ALdedicatedState_Construct(ALdedicatedState *state)
-{
-    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
-    SET_VTABLE2(ALdedicatedState, ALeffectState, state);
-}
-
-static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state)
-{
-    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
-}
-
-static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *state, ALCdevice *UNUSED(device))
-{
-    ALsizei i;
-    for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
-        state->CurrentGains[i] = 0.0f;
-    return AL_TRUE;
-}
-
-static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
-{
-    const ALCdevice *device = context->Device;
-    ALfloat Gain;
-    ALsizei i;
-
-    for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
-        state->TargetGains[i] = 0.0f;
-
-    Gain = slot->Params.Gain * props->Dedicated.Gain;
-    if(slot->Params.EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
-    {
-        int idx;
-        if((idx=GetChannelIdxByName(&device->RealOut, LFE)) != -1)
-        {
-            STATIC_CAST(ALeffectState,state)->OutBuffer = device->RealOut.Buffer;
-            STATIC_CAST(ALeffectState,state)->OutChannels = device->RealOut.NumChannels;
-            state->TargetGains[idx] = Gain;
-        }
-    }
-    else if(slot->Params.EffectType == AL_EFFECT_DEDICATED_DIALOGUE)
-    {
-        int idx;
-        /* Dialog goes to the front-center speaker if it exists, otherwise it
-         * plays from the front-center location. */
-        if((idx=GetChannelIdxByName(&device->RealOut, FrontCenter)) != -1)
-        {
-            STATIC_CAST(ALeffectState,state)->OutBuffer = device->RealOut.Buffer;
-            STATIC_CAST(ALeffectState,state)->OutChannels = device->RealOut.NumChannels;
-            state->TargetGains[idx] = Gain;
-        }
-        else
-        {
-            ALfloat coeffs[MAX_AMBI_COEFFS];
-            CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
-
-            STATIC_CAST(ALeffectState,state)->OutBuffer = device->Dry.Buffer;
-            STATIC_CAST(ALeffectState,state)->OutChannels = device->Dry.NumChannels;
-            ComputePanGains(&device->Dry, coeffs, Gain, state->TargetGains);
-        }
-    }
-}
-
-static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
-{
-    MixSamples(SamplesIn[0], NumChannels, SamplesOut, state->CurrentGains,
-               state->TargetGains, SamplesToDo, 0, SamplesToDo);
-}
-
-
-typedef struct DedicatedStateFactory {
-    DERIVE_FROM_TYPE(EffectStateFactory);
-} DedicatedStateFactory;
-
-ALeffectState *DedicatedStateFactory_create(DedicatedStateFactory *UNUSED(factory))
-{
-    ALdedicatedState *state;
-
-    NEW_OBJ0(state, ALdedicatedState)();
-    if(!state) return NULL;
-
-    return STATIC_CAST(ALeffectState, state);
-}
-
-DEFINE_EFFECTSTATEFACTORY_VTABLE(DedicatedStateFactory);
-
-
-EffectStateFactory *DedicatedStateFactory_getFactory(void)
-{
-    static DedicatedStateFactory DedicatedFactory = { { GET_VTABLE2(DedicatedStateFactory, EffectStateFactory) } };
-
-    return STATIC_CAST(EffectStateFactory, &DedicatedFactory);
-}
-
-
-void ALdedicated_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
-{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param); }
-void ALdedicated_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
-{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", param); }
-void ALdedicated_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
-{
-    ALeffectProps *props = &effect->Props;
-    switch(param)
-    {
-        case AL_DEDICATED_GAIN:
-            if(!(val >= 0.0f && isfinite(val)))
-                SETERR_RETURN(context, AL_INVALID_VALUE,, "Dedicated gain out of range");
-            props->Dedicated.Gain = val;
-            break;
-
-        default:
-            alSetError(context, AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param);
-    }
-}
-void ALdedicated_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ ALdedicated_setParamf(effect, context, param, vals[0]); }
-
-void ALdedicated_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
-{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param); }
-void ALdedicated_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
-{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", param); }
-void ALdedicated_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
-{
-    const ALeffectProps *props = &effect->Props;
-    switch(param)
-    {
-        case AL_DEDICATED_GAIN:
-            *val = props->Dedicated.Gain;
-            break;
-
-        default:
-            alSetError(context, AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param);
-    }
-}
-void ALdedicated_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
-{ ALdedicated_getParamf(effect, context, param, vals); }
-
-DEFINE_ALEFFECT_VTABLE(ALdedicated);
diff --git a/Alc/effects/dedicated.cpp b/Alc/effects/dedicated.cpp
new file mode 100644
index 00000000..dd250e64
--- /dev/null
+++ b/Alc/effects/dedicated.cpp
@@ -0,0 +1,187 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2011 by Chris Robinson.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+#include "alu.h"
+#include "filters/defs.h"
+
+
+struct ALdedicatedState final : public ALeffectState {
+    ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
+    ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
+};
+
+static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state);
+static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *state, ALCdevice *device);
+static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
+static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALdedicatedState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALdedicatedState);
+
+
+static void ALdedicatedState_Construct(ALdedicatedState *state)
+{
+    new (state) ALdedicatedState{};
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALdedicatedState, ALeffectState, state);
+}
+
+static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state)
+{
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
+    state->~ALdedicatedState();
+}
+
+static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *state, ALCdevice *UNUSED(device))
+{
+    ALsizei i;
+    for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+        state->CurrentGains[i] = 0.0f;
+    return AL_TRUE;
+}
+
+static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
+{
+    const ALCdevice *device = context->Device;
+    ALfloat Gain;
+    ALsizei i;
+
+    for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
+        state->TargetGains[i] = 0.0f;
+
+    Gain = slot->Params.Gain * props->Dedicated.Gain;
+    if(slot->Params.EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
+    {
+        int idx;
+        if((idx=GetChannelIdxByName(&device->RealOut, LFE)) != -1)
+        {
+            STATIC_CAST(ALeffectState,state)->OutBuffer = device->RealOut.Buffer;
+            STATIC_CAST(ALeffectState,state)->OutChannels = device->RealOut.NumChannels;
+            state->TargetGains[idx] = Gain;
+        }
+    }
+    else if(slot->Params.EffectType == AL_EFFECT_DEDICATED_DIALOGUE)
+    {
+        int idx;
+        /* Dialog goes to the front-center speaker if it exists, otherwise it
+         * plays from the front-center location. */
+        if((idx=GetChannelIdxByName(&device->RealOut, FrontCenter)) != -1)
+        {
+            STATIC_CAST(ALeffectState,state)->OutBuffer = device->RealOut.Buffer;
+            STATIC_CAST(ALeffectState,state)->OutChannels = device->RealOut.NumChannels;
+            state->TargetGains[idx] = Gain;
+        }
+        else
+        {
+            ALfloat coeffs[MAX_AMBI_COEFFS];
+            CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
+
+            STATIC_CAST(ALeffectState,state)->OutBuffer = device->Dry.Buffer;
+            STATIC_CAST(ALeffectState,state)->OutChannels = device->Dry.NumChannels;
+            ComputePanGains(&device->Dry, coeffs, Gain, state->TargetGains);
+        }
+    }
+}
+
+static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
+{
+    MixSamples(SamplesIn[0], NumChannels, SamplesOut, state->CurrentGains,
+               state->TargetGains, SamplesToDo, 0, SamplesToDo);
+}
+
+
+struct DedicatedStateFactory final : public EffectStateFactory {
+    DedicatedStateFactory() noexcept;
+};
+
+ALeffectState *DedicatedStateFactory_create(DedicatedStateFactory *UNUSED(factory))
+{
+    ALdedicatedState *state;
+
+    NEW_OBJ0(state, ALdedicatedState)();
+    if(!state) return NULL;
+
+    return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_EFFECTSTATEFACTORY_VTABLE(DedicatedStateFactory);
+
+DedicatedStateFactory::DedicatedStateFactory() noexcept
+  : EffectStateFactory{GET_VTABLE2(DedicatedStateFactory, EffectStateFactory)}
+{
+}
+
+EffectStateFactory *DedicatedStateFactory_getFactory(void)
+{
+    static DedicatedStateFactory DedicatedFactory{};
+    return STATIC_CAST(EffectStateFactory, &DedicatedFactory);
+}
+
+
+void ALdedicated_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param); }
+void ALdedicated_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", param); }
+void ALdedicated_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_DEDICATED_GAIN:
+            if(!(val >= 0.0f && isfinite(val)))
+                SETERR_RETURN(context, AL_INVALID_VALUE,, "Dedicated gain out of range");
+            props->Dedicated.Gain = val;
+            break;
+
+        default:
+            alSetError(context, AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param);
+    }
+}
+void ALdedicated_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{ ALdedicated_setParamf(effect, context, param, vals[0]); }
+
+void ALdedicated_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param); }
+void ALdedicated_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", param); }
+void ALdedicated_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+    const ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_DEDICATED_GAIN:
+            *val = props->Dedicated.Gain;
+            break;
+
+        default:
+            alSetError(context, AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param);
+    }
+}
+void ALdedicated_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{ ALdedicated_getParamf(effect, context, param, vals); }
+
+DEFINE_ALEFFECT_VTABLE(ALdedicated);
diff --git a/Alc/effects/distortion.c b/Alc/effects/distortion.c
deleted file mode 100644
index f2a70bff..00000000
--- a/Alc/effects/distortion.c
+++ /dev/null
@@ -1,286 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2013 by Mike Gorchak
- * This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Library General Public
- *  License as published by the Free Software Foundation; either
- *  version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- *  License along with this library; if not, write to the
- *  Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <math.h>
-#include <stdlib.h>
-
-#include "alMain.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alu.h"
-#include "filters/defs.h"
-
-
-typedef struct ALdistortionState {
-    DERIVE_FROM_TYPE(ALeffectState);
-
-    /* Effect gains for each channel */
-    ALfloat Gain[MAX_OUTPUT_CHANNELS];
-
-    /* Effect parameters */
-    BiquadFilter lowpass;
-    BiquadFilter bandpass;
-    ALfloat attenuation;
-    ALfloat edge_coeff;
-
-    ALfloat Buffer[2][BUFFERSIZE];
-} ALdistortionState;
-
-static ALvoid ALdistortionState_Destruct(ALdistortionState *state);
-static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *state, ALCdevice *device);
-static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
-static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
-DECLARE_DEFAULT_ALLOCATORS(ALdistortionState)
-
-DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState);
-
-
-static void ALdistortionState_Construct(ALdistortionState *state)
-{
-    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
-    SET_VTABLE2(ALdistortionState, ALeffectState, state);
-}
-
-static ALvoid ALdistortionState_Destruct(ALdistortionState *state)
-{
-    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
-}
-
-static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *state, ALCdevice *UNUSED(device))
-{
-    BiquadFilter_clear(&state->lowpass);
-    BiquadFilter_clear(&state->bandpass);
-    return AL_TRUE;
-}
-
-static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
-{
-    const ALCdevice *device = context->Device;
-    ALfloat frequency = (ALfloat)device->Frequency;
-    ALfloat coeffs[MAX_AMBI_COEFFS];
-    ALfloat bandwidth;
-    ALfloat cutoff;
-    ALfloat edge;
-
-    /* Store waveshaper edge settings. */
-    edge = sinf(props->Distortion.Edge * (F_PI_2));
-    edge = minf(edge, 0.99f);
-    state->edge_coeff = 2.0f * edge / (1.0f-edge);
-
-    cutoff = props->Distortion.LowpassCutoff;
-    /* Bandwidth value is constant in octaves. */
-    bandwidth = (cutoff / 2.0f) / (cutoff * 0.67f);
-    /* Multiply sampling frequency by the amount of oversampling done during
-     * processing.
-     */
-    BiquadFilter_setParams(&state->lowpass, BiquadType_LowPass, 1.0f,
-        cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
-    );
-
-    cutoff = props->Distortion.EQCenter;
-    /* Convert bandwidth in Hz to octaves. */
-    bandwidth = props->Distortion.EQBandwidth / (cutoff * 0.67f);
-    BiquadFilter_setParams(&state->bandpass, BiquadType_BandPass, 1.0f,
-        cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
-    );
-
-    CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
-    ComputePanGains(&device->Dry, coeffs, slot->Params.Gain*props->Distortion.Gain, state->Gain);
-}
-
-static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
-{
-    ALfloat (*RESTRICT buffer)[BUFFERSIZE] = state->Buffer;
-    const ALfloat fc = state->edge_coeff;
-    ALsizei base;
-    ALsizei i, k;
-
-    for(base = 0;base < SamplesToDo;)
-    {
-        /* Perform 4x oversampling to avoid aliasing. Oversampling greatly
-         * improves distortion quality and allows to implement lowpass and
-         * bandpass filters using high frequencies, at which classic IIR
-         * filters became unstable.
-         */
-        ALsizei todo = mini(BUFFERSIZE, (SamplesToDo-base) * 4);
-
-        /* Fill oversample buffer using zero stuffing. Multiply the sample by
-         * the amount of oversampling to maintain the signal's power.
-         */
-        for(i = 0;i < todo;i++)
-            buffer[0][i] = !(i&3) ? SamplesIn[0][(i>>2)+base] * 4.0f : 0.0f;
-
-        /* First step, do lowpass filtering of original signal. Additionally
-         * perform buffer interpolation and lowpass cutoff for oversampling
-         * (which is fortunately first step of distortion). So combine three
-         * operations into the one.
-         */
-        BiquadFilter_process(&state->lowpass, buffer[1], buffer[0], todo);
-
-        /* Second step, do distortion using waveshaper function to emulate
-         * signal processing during tube overdriving. Three steps of
-         * waveshaping are intended to modify waveform without boost/clipping/
-         * attenuation process.
-         */
-        for(i = 0;i < todo;i++)
-        {
-            ALfloat smp = buffer[1][i];
-
-            smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
-            smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp)) * -1.0f;
-            smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
-
-            buffer[0][i] = smp;
-        }
-
-        /* Third step, do bandpass filtering of distorted signal. */
-        BiquadFilter_process(&state->bandpass, buffer[1], buffer[0], todo);
-
-        todo >>= 2;
-        for(k = 0;k < NumChannels;k++)
-        {
-            /* Fourth step, final, do attenuation and perform decimation,
-             * storing only one sample out of four.
-             */
-            ALfloat gain = state->Gain[k];
-            if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
-                continue;
-
-            for(i = 0;i < todo;i++)
-                SamplesOut[k][base+i] += gain * buffer[1][i*4];
-        }
-
-        base += todo;
-    }
-}
-
-
-typedef struct DistortionStateFactory {
-    DERIVE_FROM_TYPE(EffectStateFactory);
-} DistortionStateFactory;
-
-static ALeffectState *DistortionStateFactory_create(DistortionStateFactory *UNUSED(factory))
-{
-    ALdistortionState *state;
-
-    NEW_OBJ0(state, ALdistortionState)();
-    if(!state) return NULL;
-
-    return STATIC_CAST(ALeffectState, state);
-}
-
-DEFINE_EFFECTSTATEFACTORY_VTABLE(DistortionStateFactory);
-
-
-EffectStateFactory *DistortionStateFactory_getFactory(void)
-{
-    static DistortionStateFactory DistortionFactory = { { GET_VTABLE2(DistortionStateFactory, EffectStateFactory) } };
-
-    return STATIC_CAST(EffectStateFactory, &DistortionFactory);
-}
-
-
-void ALdistortion_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
-{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param); }
-void ALdistortion_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
-{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param); }
-void ALdistortion_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
-{
-    ALeffectProps *props = &effect->Props;
-    switch(param)
-    {
-        case AL_DISTORTION_EDGE:
-            if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE))
-                SETERR_RETURN(context, 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))
-                SETERR_RETURN(context, 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))
-                SETERR_RETURN(context, 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))
-                SETERR_RETURN(context, 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))
-                SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion EQ bandwidth out of range");
-            props->Distortion.EQBandwidth = val;
-            break;
-
-        default:
-            alSetError(context, AL_INVALID_ENUM, "Invalid distortion float property 0x%04x",
-                       param);
-    }
-}
-void ALdistortion_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ ALdistortion_setParamf(effect, context, param, vals[0]); }
-
-void ALdistortion_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
-{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param); }
-void ALdistortion_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
-{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param); }
-void ALdistortion_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
-{
-    const ALeffectProps *props = &effect->Props;
-    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:
-            alSetError(context, AL_INVALID_ENUM, "Invalid distortion float property 0x%04x",
-                       param);
-    }
-}
-void ALdistortion_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
-{ ALdistortion_getParamf(effect, context, param, vals); }
-
-DEFINE_ALEFFECT_VTABLE(ALdistortion);
diff --git a/Alc/effects/distortion.cpp b/Alc/effects/distortion.cpp
new file mode 100644
index 00000000..75629d9c
--- /dev/null
+++ b/Alc/effects/distortion.cpp
@@ -0,0 +1,289 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2013 by Mike Gorchak
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+#include "alu.h"
+#include "filters/defs.h"
+
+
+struct ALdistortionState final : public ALeffectState {
+    /* Effect gains for each channel */
+    ALfloat Gain[MAX_OUTPUT_CHANNELS];
+
+    /* Effect parameters */
+    BiquadFilter lowpass;
+    BiquadFilter bandpass;
+    ALfloat attenuation;
+    ALfloat edge_coeff;
+
+    ALfloat Buffer[2][BUFFERSIZE];
+};
+
+static ALvoid ALdistortionState_Destruct(ALdistortionState *state);
+static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *state, ALCdevice *device);
+static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
+static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALdistortionState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState);
+
+
+static void ALdistortionState_Construct(ALdistortionState *state)
+{
+    new (state) ALdistortionState{};
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALdistortionState, ALeffectState, state);
+}
+
+static ALvoid ALdistortionState_Destruct(ALdistortionState *state)
+{
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
+    state->~ALdistortionState();
+}
+
+static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *state, ALCdevice *UNUSED(device))
+{
+    BiquadFilter_clear(&state->lowpass);
+    BiquadFilter_clear(&state->bandpass);
+    return AL_TRUE;
+}
+
+static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
+{
+    const ALCdevice *device = context->Device;
+    ALfloat frequency = (ALfloat)device->Frequency;
+    ALfloat coeffs[MAX_AMBI_COEFFS];
+    ALfloat bandwidth;
+    ALfloat cutoff;
+    ALfloat edge;
+
+    /* Store waveshaper edge settings. */
+    edge = sinf(props->Distortion.Edge * (F_PI_2));
+    edge = minf(edge, 0.99f);
+    state->edge_coeff = 2.0f * edge / (1.0f-edge);
+
+    cutoff = props->Distortion.LowpassCutoff;
+    /* Bandwidth value is constant in octaves. */
+    bandwidth = (cutoff / 2.0f) / (cutoff * 0.67f);
+    /* Multiply sampling frequency by the amount of oversampling done during
+     * processing.
+     */
+    BiquadFilter_setParams(&state->lowpass, BiquadType_LowPass, 1.0f,
+        cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
+    );
+
+    cutoff = props->Distortion.EQCenter;
+    /* Convert bandwidth in Hz to octaves. */
+    bandwidth = props->Distortion.EQBandwidth / (cutoff * 0.67f);
+    BiquadFilter_setParams(&state->bandpass, BiquadType_BandPass, 1.0f,
+        cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
+    );
+
+    CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
+    ComputePanGains(&device->Dry, coeffs, slot->Params.Gain*props->Distortion.Gain, state->Gain);
+}
+
+static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
+{
+    ALfloat (*RESTRICT buffer)[BUFFERSIZE] = state->Buffer;
+    const ALfloat fc = state->edge_coeff;
+    ALsizei base;
+    ALsizei i, k;
+
+    for(base = 0;base < SamplesToDo;)
+    {
+        /* Perform 4x oversampling to avoid aliasing. Oversampling greatly
+         * improves distortion quality and allows to implement lowpass and
+         * bandpass filters using high frequencies, at which classic IIR
+         * filters became unstable.
+         */
+        ALsizei todo = mini(BUFFERSIZE, (SamplesToDo-base) * 4);
+
+        /* Fill oversample buffer using zero stuffing. Multiply the sample by
+         * the amount of oversampling to maintain the signal's power.
+         */
+        for(i = 0;i < todo;i++)
+            buffer[0][i] = !(i&3) ? SamplesIn[0][(i>>2)+base] * 4.0f : 0.0f;
+
+        /* First step, do lowpass filtering of original signal. Additionally
+         * perform buffer interpolation and lowpass cutoff for oversampling
+         * (which is fortunately first step of distortion). So combine three
+         * operations into the one.
+         */
+        BiquadFilter_process(&state->lowpass, buffer[1], buffer[0], todo);
+
+        /* Second step, do distortion using waveshaper function to emulate
+         * signal processing during tube overdriving. Three steps of
+         * waveshaping are intended to modify waveform without boost/clipping/
+         * attenuation process.
+         */
+        for(i = 0;i < todo;i++)
+        {
+            ALfloat smp = buffer[1][i];
+
+            smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
+            smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp)) * -1.0f;
+            smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
+
+            buffer[0][i] = smp;
+        }
+
+        /* Third step, do bandpass filtering of distorted signal. */
+        BiquadFilter_process(&state->bandpass, buffer[1], buffer[0], todo);
+
+        todo >>= 2;
+        for(k = 0;k < NumChannels;k++)
+        {
+            /* Fourth step, final, do attenuation and perform decimation,
+             * storing only one sample out of four.
+             */
+            ALfloat gain = state->Gain[k];
+            if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
+                continue;
+
+            for(i = 0;i < todo;i++)
+                SamplesOut[k][base+i] += gain * buffer[1][i*4];
+        }
+
+        base += todo;
+    }
+}
+
+
+struct DistortionStateFactory final : public EffectStateFactory {
+    DistortionStateFactory() noexcept;
+};
+
+static ALeffectState *DistortionStateFactory_create(DistortionStateFactory *UNUSED(factory))
+{
+    ALdistortionState *state;
+
+    NEW_OBJ0(state, ALdistortionState)();
+    if(!state) return NULL;
+
+    return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_EFFECTSTATEFACTORY_VTABLE(DistortionStateFactory);
+
+DistortionStateFactory::DistortionStateFactory() noexcept
+  : EffectStateFactory{GET_VTABLE2(DistortionStateFactory, EffectStateFactory)}
+{
+}
+
+EffectStateFactory *DistortionStateFactory_getFactory(void)
+{
+    static DistortionStateFactory DistortionFactory{};
+    return STATIC_CAST(EffectStateFactory, &DistortionFactory);
+}
+
+
+void ALdistortion_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param); }
+void ALdistortion_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param); }
+void ALdistortion_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_DISTORTION_EDGE:
+            if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE))
+                SETERR_RETURN(context, 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))
+                SETERR_RETURN(context, 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))
+                SETERR_RETURN(context, 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))
+                SETERR_RETURN(context, 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))
+                SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion EQ bandwidth out of range");
+            props->Distortion.EQBandwidth = val;
+            break;
+
+        default:
+            alSetError(context, AL_INVALID_ENUM, "Invalid distortion float property 0x%04x",
+                       param);
+    }
+}
+void ALdistortion_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{ ALdistortion_setParamf(effect, context, param, vals[0]); }
+
+void ALdistortion_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param); }
+void ALdistortion_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param); }
+void ALdistortion_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+    const ALeffectProps *props = &effect->Props;
+    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:
+            alSetError(context, AL_INVALID_ENUM, "Invalid distortion float property 0x%04x",
+                       param);
+    }
+}
+void ALdistortion_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{ ALdistortion_getParamf(effect, context, param, vals); }
+
+DEFINE_ALEFFECT_VTABLE(ALdistortion);
diff --git a/Alc/effects/echo.c b/Alc/effects/echo.c
deleted file mode 100644
index 5c323986..00000000
--- a/Alc/effects/echo.c
+++ /dev/null
@@ -1,310 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2009 by Chris Robinson.
- * This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Library General Public
- *  License as published by the Free Software Foundation; either
- *  version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- *  License along with this library; if not, write to the
- *  Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <math.h>
-#include <stdlib.h>
-
-#include "alMain.h"
-#include "alFilter.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alu.h"
-#include "filters/defs.h"
-
-
-typedef struct ALechoState {
-    DERIVE_FROM_TYPE(ALeffectState);
-
-    ALfloat *SampleBuffer;
-    ALsizei BufferLength;
-
-    // The echo is two tap. The delay is the number of samples from before the
-    // current offset
-    struct {
-        ALsizei delay;
-    } Tap[2];
-    ALsizei Offset;
-
-    /* The panning gains for the two taps */
-    struct {
-        ALfloat Current[MAX_OUTPUT_CHANNELS];
-        ALfloat Target[MAX_OUTPUT_CHANNELS];
-    } Gains[2];
-
-    ALfloat FeedGain;
-
-    BiquadFilter Filter;
-} ALechoState;
-
-static ALvoid ALechoState_Destruct(ALechoState *state);
-static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device);
-static ALvoid ALechoState_update(ALechoState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
-static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
-DECLARE_DEFAULT_ALLOCATORS(ALechoState)
-
-DEFINE_ALEFFECTSTATE_VTABLE(ALechoState);
-
-
-static void ALechoState_Construct(ALechoState *state)
-{
-    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
-    SET_VTABLE2(ALechoState, ALeffectState, state);
-
-    state->BufferLength = 0;
-    state->SampleBuffer = NULL;
-
-    state->Tap[0].delay = 0;
-    state->Tap[1].delay = 0;
-    state->Offset = 0;
-
-    BiquadFilter_clear(&state->Filter);
-}
-
-static ALvoid ALechoState_Destruct(ALechoState *state)
-{
-    al_free(state->SampleBuffer);
-    state->SampleBuffer = NULL;
-    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
-}
-
-static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device)
-{
-    ALsizei maxlen;
-
-    // Use the next power of 2 for the buffer length, so the tap offsets can be
-    // wrapped using a mask instead of a modulo
-    maxlen = float2int(AL_ECHO_MAX_DELAY*Device->Frequency + 0.5f) +
-             float2int(AL_ECHO_MAX_LRDELAY*Device->Frequency + 0.5f);
-    maxlen = NextPowerOf2(maxlen);
-    if(maxlen <= 0) return AL_FALSE;
-
-    if(maxlen != state->BufferLength)
-    {
-        void *temp = al_calloc(16, maxlen * sizeof(ALfloat));
-        if(!temp) return AL_FALSE;
-
-        al_free(state->SampleBuffer);
-        state->SampleBuffer = temp;
-        state->BufferLength = maxlen;
-    }
-
-    memset(state->SampleBuffer, 0, state->BufferLength*sizeof(ALfloat));
-    memset(state->Gains, 0, sizeof(state->Gains));
-
-    return AL_TRUE;
-}
-
-static ALvoid ALechoState_update(ALechoState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
-{
-    const ALCdevice *device = context->Device;
-    ALuint frequency = device->Frequency;
-    ALfloat coeffs[MAX_AMBI_COEFFS];
-    ALfloat gainhf, lrpan, spread;
-
-    state->Tap[0].delay = maxi(float2int(props->Echo.Delay*frequency + 0.5f), 1);
-    state->Tap[1].delay = float2int(props->Echo.LRDelay*frequency + 0.5f);
-    state->Tap[1].delay += state->Tap[0].delay;
-
-    spread = props->Echo.Spread;
-    if(spread < 0.0f) lrpan = -1.0f;
-    else lrpan = 1.0f;
-    /* Convert echo spread (where 0 = omni, +/-1 = directional) to coverage
-     * spread (where 0 = point, tau = omni).
-     */
-    spread = asinf(1.0f - fabsf(spread))*4.0f;
-
-    state->FeedGain = props->Echo.Feedback;
-
-    gainhf = maxf(1.0f - props->Echo.Damping, 0.0625f); /* Limit -24dB */
-    BiquadFilter_setParams(&state->Filter, BiquadType_HighShelf,
-        gainhf, LOWPASSFREQREF/frequency, calc_rcpQ_from_slope(gainhf, 1.0f)
-    );
-
-    /* First tap panning */
-    CalcAngleCoeffs(-F_PI_2*lrpan, 0.0f, spread, coeffs);
-    ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[0].Target);
-
-    /* Second tap panning */
-    CalcAngleCoeffs( F_PI_2*lrpan, 0.0f, spread, coeffs);
-    ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[1].Target);
-}
-
-static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
-{
-    const ALsizei mask = state->BufferLength-1;
-    const ALsizei tap1 = state->Tap[0].delay;
-    const ALsizei tap2 = state->Tap[1].delay;
-    ALfloat *RESTRICT delaybuf = state->SampleBuffer;
-    ALsizei offset = state->Offset;
-    ALfloat z1, z2, in, out;
-    ALsizei base;
-    ALsizei c, i;
-
-    z1 = state->Filter.z1;
-    z2 = state->Filter.z2;
-    for(base = 0;base < SamplesToDo;)
-    {
-        alignas(16) ALfloat temps[2][128];
-        ALsizei td = mini(128, SamplesToDo-base);
-
-        for(i = 0;i < td;i++)
-        {
-            /* Feed the delay buffer's input first. */
-            delaybuf[offset&mask] = SamplesIn[0][i+base];
-
-            /* First tap */
-            temps[0][i] = delaybuf[(offset-tap1) & mask];
-            /* Second tap */
-            temps[1][i] = delaybuf[(offset-tap2) & mask];
-
-            /* Apply damping to the second tap, then add it to the buffer with
-             * feedback attenuation.
-             */
-            in = temps[1][i];
-            out = in*state->Filter.b0 + z1;
-            z1 = in*state->Filter.b1 - out*state->Filter.a1 + z2;
-            z2 = in*state->Filter.b2 - out*state->Filter.a2;
-
-            delaybuf[offset&mask] += out * state->FeedGain;
-            offset++;
-        }
-
-        for(c = 0;c < 2;c++)
-            MixSamples(temps[c], NumChannels, SamplesOut, state->Gains[c].Current,
-                       state->Gains[c].Target, SamplesToDo-base, base, td);
-
-        base += td;
-    }
-    state->Filter.z1 = z1;
-    state->Filter.z2 = z2;
-
-    state->Offset = offset;
-}
-
-
-typedef struct EchoStateFactory {
-    DERIVE_FROM_TYPE(EffectStateFactory);
-} EchoStateFactory;
-
-ALeffectState *EchoStateFactory_create(EchoStateFactory *UNUSED(factory))
-{
-    ALechoState *state;
-
-    NEW_OBJ0(state, ALechoState)();
-    if(!state) return NULL;
-
-    return STATIC_CAST(ALeffectState, state);
-}
-
-DEFINE_EFFECTSTATEFACTORY_VTABLE(EchoStateFactory);
-
-EffectStateFactory *EchoStateFactory_getFactory(void)
-{
-    static EchoStateFactory EchoFactory = { { GET_VTABLE2(EchoStateFactory, EffectStateFactory) } };
-
-    return STATIC_CAST(EffectStateFactory, &EchoFactory);
-}
-
-
-void ALecho_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
-{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param); }
-void ALecho_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
-{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param); }
-void ALecho_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
-{
-    ALeffectProps *props = &effect->Props;
-    switch(param)
-    {
-        case AL_ECHO_DELAY:
-            if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY))
-                SETERR_RETURN(context, 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))
-                SETERR_RETURN(context, 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))
-                SETERR_RETURN(context, 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))
-                SETERR_RETURN(context, 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))
-                SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo spread out of range");
-            props->Echo.Spread = val;
-            break;
-
-        default:
-            alSetError(context, AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param);
-    }
-}
-void ALecho_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ ALecho_setParamf(effect, context, param, vals[0]); }
-
-void ALecho_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
-{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param); }
-void ALecho_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
-{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param); }
-void ALecho_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
-{
-    const ALeffectProps *props = &effect->Props;
-    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:
-            alSetError(context, AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param);
-    }
-}
-void ALecho_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
-{ ALecho_getParamf(effect, context, param, vals); }
-
-DEFINE_ALEFFECT_VTABLE(ALecho);
diff --git a/Alc/effects/echo.cpp b/Alc/effects/echo.cpp
new file mode 100644
index 00000000..f987e582
--- /dev/null
+++ b/Alc/effects/echo.cpp
@@ -0,0 +1,314 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2009 by Chris Robinson.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alFilter.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+#include "alu.h"
+#include "filters/defs.h"
+
+
+struct ALechoState final : public ALeffectState {
+    ALfloat *SampleBuffer;
+    ALsizei BufferLength;
+
+    // The echo is two tap. The delay is the number of samples from before the
+    // current offset
+    struct {
+        ALsizei delay;
+    } Tap[2];
+    ALsizei Offset;
+
+    /* The panning gains for the two taps */
+    struct {
+        ALfloat Current[MAX_OUTPUT_CHANNELS];
+        ALfloat Target[MAX_OUTPUT_CHANNELS];
+    } Gains[2];
+
+    ALfloat FeedGain;
+
+    BiquadFilter Filter;
+};
+
+static ALvoid ALechoState_Destruct(ALechoState *state);
+static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device);
+static ALvoid ALechoState_update(ALechoState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
+static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALechoState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALechoState);
+
+
+static void ALechoState_Construct(ALechoState *state)
+{
+    new (state) ALechoState{};
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALechoState, ALeffectState, state);
+
+    state->BufferLength = 0;
+    state->SampleBuffer = NULL;
+
+    state->Tap[0].delay = 0;
+    state->Tap[1].delay = 0;
+    state->Offset = 0;
+
+    BiquadFilter_clear(&state->Filter);
+}
+
+static ALvoid ALechoState_Destruct(ALechoState *state)
+{
+    al_free(state->SampleBuffer);
+    state->SampleBuffer = NULL;
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
+    state->~ALechoState();
+}
+
+static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device)
+{
+    ALsizei maxlen;
+
+    // Use the next power of 2 for the buffer length, so the tap offsets can be
+    // wrapped using a mask instead of a modulo
+    maxlen = float2int(AL_ECHO_MAX_DELAY*Device->Frequency + 0.5f) +
+             float2int(AL_ECHO_MAX_LRDELAY*Device->Frequency + 0.5f);
+    maxlen = NextPowerOf2(maxlen);
+    if(maxlen <= 0) return AL_FALSE;
+
+    if(maxlen != state->BufferLength)
+    {
+        void *temp = al_calloc(16, maxlen * sizeof(ALfloat));
+        if(!temp) return AL_FALSE;
+
+        al_free(state->SampleBuffer);
+        state->SampleBuffer = static_cast<float*>(temp);
+        state->BufferLength = maxlen;
+    }
+
+    memset(state->SampleBuffer, 0, state->BufferLength*sizeof(ALfloat));
+    memset(state->Gains, 0, sizeof(state->Gains));
+
+    return AL_TRUE;
+}
+
+static ALvoid ALechoState_update(ALechoState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
+{
+    const ALCdevice *device = context->Device;
+    ALuint frequency = device->Frequency;
+    ALfloat coeffs[MAX_AMBI_COEFFS];
+    ALfloat gainhf, lrpan, spread;
+
+    state->Tap[0].delay = maxi(float2int(props->Echo.Delay*frequency + 0.5f), 1);
+    state->Tap[1].delay = float2int(props->Echo.LRDelay*frequency + 0.5f);
+    state->Tap[1].delay += state->Tap[0].delay;
+
+    spread = props->Echo.Spread;
+    if(spread < 0.0f) lrpan = -1.0f;
+    else lrpan = 1.0f;
+    /* Convert echo spread (where 0 = omni, +/-1 = directional) to coverage
+     * spread (where 0 = point, tau = omni).
+     */
+    spread = asinf(1.0f - fabsf(spread))*4.0f;
+
+    state->FeedGain = props->Echo.Feedback;
+
+    gainhf = maxf(1.0f - props->Echo.Damping, 0.0625f); /* Limit -24dB */
+    BiquadFilter_setParams(&state->Filter, BiquadType_HighShelf,
+        gainhf, LOWPASSFREQREF/frequency, calc_rcpQ_from_slope(gainhf, 1.0f)
+    );
+
+    /* First tap panning */
+    CalcAngleCoeffs(-F_PI_2*lrpan, 0.0f, spread, coeffs);
+    ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[0].Target);
+
+    /* Second tap panning */
+    CalcAngleCoeffs( F_PI_2*lrpan, 0.0f, spread, coeffs);
+    ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[1].Target);
+}
+
+static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
+{
+    const ALsizei mask = state->BufferLength-1;
+    const ALsizei tap1 = state->Tap[0].delay;
+    const ALsizei tap2 = state->Tap[1].delay;
+    ALfloat *RESTRICT delaybuf = state->SampleBuffer;
+    ALsizei offset = state->Offset;
+    ALfloat z1, z2, in, out;
+    ALsizei base;
+    ALsizei c, i;
+
+    z1 = state->Filter.z1;
+    z2 = state->Filter.z2;
+    for(base = 0;base < SamplesToDo;)
+    {
+        alignas(16) ALfloat temps[2][128];
+        ALsizei td = mini(128, SamplesToDo-base);
+
+        for(i = 0;i < td;i++)
+        {
+            /* Feed the delay buffer's input first. */
+            delaybuf[offset&mask] = SamplesIn[0][i+base];
+
+            /* First tap */
+            temps[0][i] = delaybuf[(offset-tap1) & mask];
+            /* Second tap */
+            temps[1][i] = delaybuf[(offset-tap2) & mask];
+
+            /* Apply damping to the second tap, then add it to the buffer with
+             * feedback attenuation.
+             */
+            in = temps[1][i];
+            out = in*state->Filter.b0 + z1;
+            z1 = in*state->Filter.b1 - out*state->Filter.a1 + z2;
+            z2 = in*state->Filter.b2 - out*state->Filter.a2;
+
+            delaybuf[offset&mask] += out * state->FeedGain;
+            offset++;
+        }
+
+        for(c = 0;c < 2;c++)
+            MixSamples(temps[c], NumChannels, SamplesOut, state->Gains[c].Current,
+                       state->Gains[c].Target, SamplesToDo-base, base, td);
+
+        base += td;
+    }
+    state->Filter.z1 = z1;
+    state->Filter.z2 = z2;
+
+    state->Offset = offset;
+}
+
+
+struct EchoStateFactory final : public EffectStateFactory {
+    EchoStateFactory() noexcept;
+};
+
+ALeffectState *EchoStateFactory_create(EchoStateFactory *UNUSED(factory))
+{
+    ALechoState *state;
+
+    NEW_OBJ0(state, ALechoState)();
+    if(!state) return NULL;
+
+    return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_EFFECTSTATEFACTORY_VTABLE(EchoStateFactory);
+
+EchoStateFactory::EchoStateFactory() noexcept
+  : EffectStateFactory{GET_VTABLE2(EchoStateFactory, EffectStateFactory)}
+{
+}
+
+EffectStateFactory *EchoStateFactory_getFactory(void)
+{
+    static EchoStateFactory EchoFactory{};
+    return STATIC_CAST(EffectStateFactory, &EchoFactory);
+}
+
+
+void ALecho_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param); }
+void ALecho_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param); }
+void ALecho_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_ECHO_DELAY:
+            if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY))
+                SETERR_RETURN(context, 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))
+                SETERR_RETURN(context, 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))
+                SETERR_RETURN(context, 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))
+                SETERR_RETURN(context, 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))
+                SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo spread out of range");
+            props->Echo.Spread = val;
+            break;
+
+        default:
+            alSetError(context, AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param);
+    }
+}
+void ALecho_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{ ALecho_setParamf(effect, context, param, vals[0]); }
+
+void ALecho_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param); }
+void ALecho_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param); }
+void ALecho_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+    const ALeffectProps *props = &effect->Props;
+    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:
+            alSetError(context, AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param);
+    }
+}
+void ALecho_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{ ALecho_getParamf(effect, context, param, vals); }
+
+DEFINE_ALEFFECT_VTABLE(ALecho);
diff --git a/Alc/effects/equalizer.c b/Alc/effects/equalizer.c
deleted file mode 100644
index c62f8e80..00000000
--- a/Alc/effects/equalizer.c
+++ /dev/null
@@ -1,355 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2013 by Mike Gorchak
- * This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Library General Public
- *  License as published by the Free Software Foundation; either
- *  version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- *  License along with this library; if not, write to the
- *  Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * Or go to http://www.gnu.org/copyleft/lgpl.html
- */
-
-#include "config.h"
-
-#include <math.h>
-#include <stdlib.h>
-
-#include "alMain.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alu.h"
-#include "filters/defs.h"
-
-
-/*  The document  "Effects Extension Guide.pdf"  says that low and high  *
- *  frequencies are cutoff frequencies. This is not fully correct, they  *
- *  are corner frequencies for low and high shelf filters. If they were  *
- *  just cutoff frequencies, there would be no need in cutoff frequency  *
- *  gains, which are present.  Documentation for  "Creative Proteus X2"  *
- *  software describes  4-band equalizer functionality in a much better  *
- *  way.  This equalizer seems  to be a predecessor  of  OpenAL  4-band  *
- *  equalizer.  With low and high  shelf filters  we are able to cutoff  *
- *  frequencies below and/or above corner frequencies using attenuation  *
- *  gains (below 1.0) and amplify all low and/or high frequencies using  *
- *  gains above 1.0.                                                     *
- *                                                                       *
- *     Low-shelf       Low Mid Band      High Mid Band     High-shelf    *
- *      corner            center             center          corner      *
- *     frequency        frequency          frequency       frequency     *
- *    50Hz..800Hz     200Hz..3000Hz      1000Hz..8000Hz  4000Hz..16000Hz *
- *                                                                       *
- *          |               |                  |               |         *
- *          |               |                  |               |         *
- *   B -----+            /--+--\            /--+--\            +-----    *
- *   O      |\          |   |   |          |   |   |          /|         *
- *   O      | \        -    |    -        -    |    -        / |         *
- *   S +    |  \      |     |     |      |     |     |      /  |         *
- *   T      |   |    |      |      |    |      |      |    |   |         *
- * ---------+---------------+------------------+---------------+-------- *
- *   C      |   |    |      |      |    |      |      |    |   |         *
- *   U -    |  /      |     |     |      |     |     |      \  |         *
- *   T      | /        -    |    -        -    |    -        \ |         *
- *   O      |/          |   |   |          |   |   |          \|         *
- *   F -----+            \--+--/            \--+--/            +-----    *
- *   F      |               |                  |               |         *
- *          |               |                  |               |         *
- *                                                                       *
- * Gains vary from 0.126 up to 7.943, which means from -18dB attenuation *
- * up to +18dB amplification. Band width varies from 0.01 up to 1.0 in   *
- * octaves for two mid bands.                                            *
- *                                                                       *
- * Implementation is based on the "Cookbook formulae for audio EQ biquad *
- * filter coefficients" by Robert Bristow-Johnson                        *
- * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt                   */
-
-
-typedef struct ALequalizerState {
-    DERIVE_FROM_TYPE(ALeffectState);
-
-    struct {
-        /* Effect parameters */
-        BiquadFilter filter[4];
-
-        /* Effect gains for each channel */
-        ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
-        ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
-    } Chans[MAX_EFFECT_CHANNELS];
-
-    ALfloat SampleBuffer[MAX_EFFECT_CHANNELS][BUFFERSIZE];
-} ALequalizerState;
-
-static ALvoid ALequalizerState_Destruct(ALequalizerState *state);
-static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *state, ALCdevice *device);
-static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
-static ALvoid ALequalizerState_process(ALequalizerState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
-DECLARE_DEFAULT_ALLOCATORS(ALequalizerState)
-
-DEFINE_ALEFFECTSTATE_VTABLE(ALequalizerState);
-
-
-static void ALequalizerState_Construct(ALequalizerState *state)
-{
-    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
-    SET_VTABLE2(ALequalizerState, ALeffectState, state);
-}
-
-static ALvoid ALequalizerState_Destruct(ALequalizerState *state)
-{
-    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
-}
-
-static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *state, ALCdevice *UNUSED(device))
-{
-    ALsizei i, j;
-
-    for(i = 0; i < MAX_EFFECT_CHANNELS;i++)
-    {
-        for(j = 0;j < 4;j++)
-            BiquadFilter_clear(&state->Chans[i].filter[j]);
-        for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
-            state->Chans[i].CurrentGains[j] = 0.0f;
-    }
-    return AL_TRUE;
-}
-
-static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
-{
-    const ALCdevice *device = context->Device;
-    ALfloat frequency = (ALfloat)device->Frequency;
-    ALfloat gain, f0norm;
-    ALuint i;
-
-    /* Calculate coefficients for the each type of filter. Note that the shelf
-     * filters' gain is for the reference frequency, which is the centerpoint
-     * of the transition band.
-     */
-    gain = maxf(sqrtf(props->Equalizer.LowGain), 0.0625f); /* Limit -24dB */
-    f0norm = props->Equalizer.LowCutoff/frequency;
-    BiquadFilter_setParams(&state->Chans[0].filter[0], BiquadType_LowShelf,
-        gain, f0norm, calc_rcpQ_from_slope(gain, 0.75f)
-    );
-
-    gain = maxf(props->Equalizer.Mid1Gain, 0.0625f);
-    f0norm = props->Equalizer.Mid1Center/frequency;
-    BiquadFilter_setParams(&state->Chans[0].filter[1], BiquadType_Peaking,
-        gain, f0norm, calc_rcpQ_from_bandwidth(
-            f0norm, props->Equalizer.Mid1Width
-        )
-    );
-
-    gain = maxf(props->Equalizer.Mid2Gain, 0.0625f);
-    f0norm = props->Equalizer.Mid2Center/frequency;
-    BiquadFilter_setParams(&state->Chans[0].filter[2], BiquadType_Peaking,
-        gain, f0norm, calc_rcpQ_from_bandwidth(
-            f0norm, props->Equalizer.Mid2Width
-        )
-    );
-
-    gain = maxf(sqrtf(props->Equalizer.HighGain), 0.0625f);
-    f0norm = props->Equalizer.HighCutoff/frequency;
-    BiquadFilter_setParams(&state->Chans[0].filter[3], BiquadType_HighShelf,
-        gain, f0norm, calc_rcpQ_from_slope(gain, 0.75f)
-    );
-
-    /* Copy the filter coefficients for the other input channels. */
-    for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
-    {
-        BiquadFilter_copyParams(&state->Chans[i].filter[0], &state->Chans[0].filter[0]);
-        BiquadFilter_copyParams(&state->Chans[i].filter[1], &state->Chans[0].filter[1]);
-        BiquadFilter_copyParams(&state->Chans[i].filter[2], &state->Chans[0].filter[2]);
-        BiquadFilter_copyParams(&state->Chans[i].filter[3], &state->Chans[0].filter[3]);
-    }
-
-    STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
-    STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
-    for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
-        ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain,
-                        state->Chans[i].TargetGains);
-}
-
-static ALvoid ALequalizerState_process(ALequalizerState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
-{
-    ALfloat (*RESTRICT temps)[BUFFERSIZE] = state->SampleBuffer;
-    ALsizei c;
-
-    for(c = 0;c < MAX_EFFECT_CHANNELS;c++)
-    {
-        BiquadFilter_process(&state->Chans[c].filter[0], temps[0], SamplesIn[c], SamplesToDo);
-        BiquadFilter_process(&state->Chans[c].filter[1], temps[1], temps[0], SamplesToDo);
-        BiquadFilter_process(&state->Chans[c].filter[2], temps[2], temps[1], SamplesToDo);
-        BiquadFilter_process(&state->Chans[c].filter[3], temps[3], temps[2], SamplesToDo);
-
-        MixSamples(temps[3], NumChannels, SamplesOut,
-            state->Chans[c].CurrentGains, state->Chans[c].TargetGains,
-            SamplesToDo, 0, SamplesToDo
-        );
-    }
-}
-
-
-typedef struct EqualizerStateFactory {
-    DERIVE_FROM_TYPE(EffectStateFactory);
-} EqualizerStateFactory;
-
-ALeffectState *EqualizerStateFactory_create(EqualizerStateFactory *UNUSED(factory))
-{
-    ALequalizerState *state;
-
-    NEW_OBJ0(state, ALequalizerState)();
-    if(!state) return NULL;
-
-    return STATIC_CAST(ALeffectState, state);
-}
-
-DEFINE_EFFECTSTATEFACTORY_VTABLE(EqualizerStateFactory);
-
-EffectStateFactory *EqualizerStateFactory_getFactory(void)
-{
-    static EqualizerStateFactory EqualizerFactory = { { GET_VTABLE2(EqualizerStateFactory, EffectStateFactory) } };
-
-    return STATIC_CAST(EffectStateFactory, &EqualizerFactory);
-}
-
-
-void ALequalizer_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
-{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param); }
-void ALequalizer_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
-{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", param); }
-void ALequalizer_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
-{
-    ALeffectProps *props = &effect->Props;
-    switch(param)
-    {
-        case AL_EQUALIZER_LOW_GAIN:
-            if(!(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN))
-                SETERR_RETURN(context, 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))
-                SETERR_RETURN(context, 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))
-                SETERR_RETURN(context, 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))
-                SETERR_RETURN(context, 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))
-                SETERR_RETURN(context, 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))
-                SETERR_RETURN(context, 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))
-                SETERR_RETURN(context, 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))
-                SETERR_RETURN(context, 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))
-                SETERR_RETURN(context, 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))
-                SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer high-band cutoff out of range");
-            props->Equalizer.HighCutoff = val;
-            break;
-
-        default:
-            alSetError(context, AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param);
-    }
-}
-void ALequalizer_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ ALequalizer_setParamf(effect, context, param, vals[0]); }
-
-void ALequalizer_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
-{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param); }
-void ALequalizer_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
-{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", param); }
-void ALequalizer_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
-{
-    const ALeffectProps *props = &effect->Props;
-    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:
-            alSetError(context, AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param);
-    }
-}
-void ALequalizer_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
-{ ALequalizer_getParamf(effect, context, param, vals); }
-
-DEFINE_ALEFFECT_VTABLE(ALequalizer);
diff --git a/Alc/effects/equalizer.cpp b/Alc/effects/equalizer.cpp
new file mode 100644
index 00000000..8388a123
--- /dev/null
+++ b/Alc/effects/equalizer.cpp
@@ -0,0 +1,359 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2013 by Mike Gorchak
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+#include "alu.h"
+#include "filters/defs.h"
+
+
+/*  The document  "Effects Extension Guide.pdf"  says that low and high  *
+ *  frequencies are cutoff frequencies. This is not fully correct, they  *
+ *  are corner frequencies for low and high shelf filters. If they were  *
+ *  just cutoff frequencies, there would be no need in cutoff frequency  *
+ *  gains, which are present.  Documentation for  "Creative Proteus X2"  *
+ *  software describes  4-band equalizer functionality in a much better  *
+ *  way.  This equalizer seems  to be a predecessor  of  OpenAL  4-band  *
+ *  equalizer.  With low and high  shelf filters  we are able to cutoff  *
+ *  frequencies below and/or above corner frequencies using attenuation  *
+ *  gains (below 1.0) and amplify all low and/or high frequencies using  *
+ *  gains above 1.0.                                                     *
+ *                                                                       *
+ *     Low-shelf       Low Mid Band      High Mid Band     High-shelf    *
+ *      corner            center             center          corner      *
+ *     frequency        frequency          frequency       frequency     *
+ *    50Hz..800Hz     200Hz..3000Hz      1000Hz..8000Hz  4000Hz..16000Hz *
+ *                                                                       *
+ *          |               |                  |               |         *
+ *          |               |                  |               |         *
+ *   B -----+            /--+--\            /--+--\            +-----    *
+ *   O      |\          |   |   |          |   |   |          /|         *
+ *   O      | \        -    |    -        -    |    -        / |         *
+ *   S +    |  \      |     |     |      |     |     |      /  |         *
+ *   T      |   |    |      |      |    |      |      |    |   |         *
+ * ---------+---------------+------------------+---------------+-------- *
+ *   C      |   |    |      |      |    |      |      |    |   |         *
+ *   U -    |  /      |     |     |      |     |     |      \  |         *
+ *   T      | /        -    |    -        -    |    -        \ |         *
+ *   O      |/          |   |   |          |   |   |          \|         *
+ *   F -----+            \--+--/            \--+--/            +-----    *
+ *   F      |               |                  |               |         *
+ *          |               |                  |               |         *
+ *                                                                       *
+ * Gains vary from 0.126 up to 7.943, which means from -18dB attenuation *
+ * up to +18dB amplification. Band width varies from 0.01 up to 1.0 in   *
+ * octaves for two mid bands.                                            *
+ *                                                                       *
+ * Implementation is based on the "Cookbook formulae for audio EQ biquad *
+ * filter coefficients" by Robert Bristow-Johnson                        *
+ * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt                   */
+
+
+struct ALequalizerState final : public ALeffectState {
+    struct {
+        /* Effect parameters */
+        BiquadFilter filter[4];
+
+        /* Effect gains for each channel */
+        ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
+        ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
+    } Chans[MAX_EFFECT_CHANNELS];
+
+    ALfloat SampleBuffer[MAX_EFFECT_CHANNELS][BUFFERSIZE];
+};
+
+static ALvoid ALequalizerState_Destruct(ALequalizerState *state);
+static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *state, ALCdevice *device);
+static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
+static ALvoid ALequalizerState_process(ALequalizerState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALequalizerState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALequalizerState);
+
+
+static void ALequalizerState_Construct(ALequalizerState *state)
+{
+    new (state) ALequalizerState{};
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALequalizerState, ALeffectState, state);
+}
+
+static ALvoid ALequalizerState_Destruct(ALequalizerState *state)
+{
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
+    state->~ALequalizerState();
+}
+
+static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *state, ALCdevice *UNUSED(device))
+{
+    ALsizei i, j;
+
+    for(i = 0; i < MAX_EFFECT_CHANNELS;i++)
+    {
+        for(j = 0;j < 4;j++)
+            BiquadFilter_clear(&state->Chans[i].filter[j]);
+        for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
+            state->Chans[i].CurrentGains[j] = 0.0f;
+    }
+    return AL_TRUE;
+}
+
+static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
+{
+    const ALCdevice *device = context->Device;
+    ALfloat frequency = (ALfloat)device->Frequency;
+    ALfloat gain, f0norm;
+    ALuint i;
+
+    /* Calculate coefficients for the each type of filter. Note that the shelf
+     * filters' gain is for the reference frequency, which is the centerpoint
+     * of the transition band.
+     */
+    gain = maxf(sqrtf(props->Equalizer.LowGain), 0.0625f); /* Limit -24dB */
+    f0norm = props->Equalizer.LowCutoff/frequency;
+    BiquadFilter_setParams(&state->Chans[0].filter[0], BiquadType_LowShelf,
+        gain, f0norm, calc_rcpQ_from_slope(gain, 0.75f)
+    );
+
+    gain = maxf(props->Equalizer.Mid1Gain, 0.0625f);
+    f0norm = props->Equalizer.Mid1Center/frequency;
+    BiquadFilter_setParams(&state->Chans[0].filter[1], BiquadType_Peaking,
+        gain, f0norm, calc_rcpQ_from_bandwidth(
+            f0norm, props->Equalizer.Mid1Width
+        )
+    );
+
+    gain = maxf(props->Equalizer.Mid2Gain, 0.0625f);
+    f0norm = props->Equalizer.Mid2Center/frequency;
+    BiquadFilter_setParams(&state->Chans[0].filter[2], BiquadType_Peaking,
+        gain, f0norm, calc_rcpQ_from_bandwidth(
+            f0norm, props->Equalizer.Mid2Width
+        )
+    );
+
+    gain = maxf(sqrtf(props->Equalizer.HighGain), 0.0625f);
+    f0norm = props->Equalizer.HighCutoff/frequency;
+    BiquadFilter_setParams(&state->Chans[0].filter[3], BiquadType_HighShelf,
+        gain, f0norm, calc_rcpQ_from_slope(gain, 0.75f)
+    );
+
+    /* Copy the filter coefficients for the other input channels. */
+    for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
+    {
+        BiquadFilter_copyParams(&state->Chans[i].filter[0], &state->Chans[0].filter[0]);
+        BiquadFilter_copyParams(&state->Chans[i].filter[1], &state->Chans[0].filter[1]);
+        BiquadFilter_copyParams(&state->Chans[i].filter[2], &state->Chans[0].filter[2]);
+        BiquadFilter_copyParams(&state->Chans[i].filter[3], &state->Chans[0].filter[3]);
+    }
+
+    STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
+    STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
+    for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
+        ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain,
+                        state->Chans[i].TargetGains);
+}
+
+static ALvoid ALequalizerState_process(ALequalizerState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
+{
+    ALfloat (*RESTRICT temps)[BUFFERSIZE] = state->SampleBuffer;
+    ALsizei c;
+
+    for(c = 0;c < MAX_EFFECT_CHANNELS;c++)
+    {
+        BiquadFilter_process(&state->Chans[c].filter[0], temps[0], SamplesIn[c], SamplesToDo);
+        BiquadFilter_process(&state->Chans[c].filter[1], temps[1], temps[0], SamplesToDo);
+        BiquadFilter_process(&state->Chans[c].filter[2], temps[2], temps[1], SamplesToDo);
+        BiquadFilter_process(&state->Chans[c].filter[3], temps[3], temps[2], SamplesToDo);
+
+        MixSamples(temps[3], NumChannels, SamplesOut,
+            state->Chans[c].CurrentGains, state->Chans[c].TargetGains,
+            SamplesToDo, 0, SamplesToDo
+        );
+    }
+}
+
+
+struct EqualizerStateFactory final : public EffectStateFactory {
+    EqualizerStateFactory() noexcept;
+};
+
+ALeffectState *EqualizerStateFactory_create(EqualizerStateFactory *UNUSED(factory))
+{
+    ALequalizerState *state;
+
+    NEW_OBJ0(state, ALequalizerState)();
+    if(!state) return NULL;
+
+    return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_EFFECTSTATEFACTORY_VTABLE(EqualizerStateFactory);
+
+EqualizerStateFactory::EqualizerStateFactory() noexcept
+  : EffectStateFactory{GET_VTABLE2(EqualizerStateFactory, EffectStateFactory)}
+{
+}
+
+EffectStateFactory *EqualizerStateFactory_getFactory(void)
+{
+    static EqualizerStateFactory EqualizerFactory{};
+    return STATIC_CAST(EffectStateFactory, &EqualizerFactory);
+}
+
+
+void ALequalizer_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param); }
+void ALequalizer_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", param); }
+void ALequalizer_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_EQUALIZER_LOW_GAIN:
+            if(!(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN))
+                SETERR_RETURN(context, 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))
+                SETERR_RETURN(context, 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))
+                SETERR_RETURN(context, 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))
+                SETERR_RETURN(context, 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))
+                SETERR_RETURN(context, 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))
+                SETERR_RETURN(context, 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))
+                SETERR_RETURN(context, 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))
+                SETERR_RETURN(context, 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))
+                SETERR_RETURN(context, 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))
+                SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer high-band cutoff out of range");
+            props->Equalizer.HighCutoff = val;
+            break;
+
+        default:
+            alSetError(context, AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param);
+    }
+}
+void ALequalizer_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{ ALequalizer_setParamf(effect, context, param, vals[0]); }
+
+void ALequalizer_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param); }
+void ALequalizer_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", param); }
+void ALequalizer_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+    const ALeffectProps *props = &effect->Props;
+    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:
+            alSetError(context, AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param);
+    }
+}
+void ALequalizer_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{ ALequalizer_getParamf(effect, context, param, vals); }
+
+DEFINE_ALEFFECT_VTABLE(ALequalizer);
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 79530117..79d28e23 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -811,10 +811,10 @@ SET(ALC_OBJS
     Alc/effects/autowah.c
     Alc/effects/chorus.c
     Alc/effects/compressor.c
-    Alc/effects/dedicated.c
-    Alc/effects/distortion.c
-    Alc/effects/echo.c
-    Alc/effects/equalizer.c
+    Alc/effects/dedicated.cpp
+    Alc/effects/distortion.cpp
+    Alc/effects/echo.cpp
+    Alc/effects/equalizer.cpp
     Alc/effects/fshifter.cpp
     Alc/effects/modulator.cpp
     Alc/effects/null.cpp
-- 
cgit v1.2.3