From 13478126cb7d41e8f6efebf020fb5a387c303f2d Mon Sep 17 00:00:00 2001
From: Chris Robinson <chris.kcat@gmail.com>
Date: Sat, 17 Nov 2018 07:08:41 -0800
Subject: Convert the remaining effects to C++

---
 Alc/effects/autowah.c      | 321 --------------------------
 Alc/effects/autowah.cpp    | 326 ++++++++++++++++++++++++++
 Alc/effects/chorus.c       | 555 --------------------------------------------
 Alc/effects/chorus.cpp     | 561 +++++++++++++++++++++++++++++++++++++++++++++
 Alc/effects/compressor.c   | 243 --------------------
 Alc/effects/compressor.cpp | 247 ++++++++++++++++++++
 CMakeLists.txt             |   6 +-
 7 files changed, 1137 insertions(+), 1122 deletions(-)
 delete mode 100644 Alc/effects/autowah.c
 create mode 100644 Alc/effects/autowah.cpp
 delete mode 100644 Alc/effects/chorus.c
 create mode 100644 Alc/effects/chorus.cpp
 delete mode 100644 Alc/effects/compressor.c
 create mode 100644 Alc/effects/compressor.cpp

diff --git a/Alc/effects/autowah.c b/Alc/effects/autowah.c
deleted file mode 100644
index f65f1be6..00000000
--- a/Alc/effects/autowah.c
+++ /dev/null
@@ -1,321 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2018 by Raul Herraiz.
- * 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"
-
-#define MIN_FREQ 20.0f
-#define MAX_FREQ 2500.0f
-#define Q_FACTOR 5.0f
-
-typedef struct ALautowahState {
-    DERIVE_FROM_TYPE(ALeffectState);
-
-    /* Effect parameters */
-    ALfloat AttackRate;
-    ALfloat ReleaseRate;
-    ALfloat ResonanceGain;
-    ALfloat PeakGain;
-    ALfloat FreqMinNorm;
-    ALfloat BandwidthNorm;
-    ALfloat env_delay;
-
-    /* Filter components derived from the envelope. */
-    struct {
-        ALfloat cos_w0;
-        ALfloat alpha;
-    } Env[BUFFERSIZE];
-
-    struct {
-        /* Effect filters' history. */
-        struct {
-            ALfloat z1, z2;
-        } Filter;
-
-        /* Effect gains for each output channel */
-        ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
-        ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
-    } Chans[MAX_EFFECT_CHANNELS];
-
-    /* Effects buffers */
-    alignas(16) ALfloat BufferOut[BUFFERSIZE];
-} ALautowahState;
-
-static ALvoid ALautowahState_Destruct(ALautowahState *state);
-static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *device);
-static ALvoid ALautowahState_update(ALautowahState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
-static ALvoid ALautowahState_process(ALautowahState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
-DECLARE_DEFAULT_ALLOCATORS(ALautowahState)
-
-DEFINE_ALEFFECTSTATE_VTABLE(ALautowahState);
-
-static void ALautowahState_Construct(ALautowahState *state)
-{
-    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
-    SET_VTABLE2(ALautowahState, ALeffectState, state);
-}
-
-static ALvoid ALautowahState_Destruct(ALautowahState *state)
-{
-    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
-}
-
-static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *UNUSED(device))
-{
-    /* (Re-)initializing parameters and clear the buffers. */
-    ALsizei i, j;
-
-    state->AttackRate    = 1.0f;
-    state->ReleaseRate   = 1.0f;
-    state->ResonanceGain = 10.0f;
-    state->PeakGain      = 4.5f;
-    state->FreqMinNorm   = 4.5e-4f;
-    state->BandwidthNorm = 0.05f;
-    state->env_delay     = 0.0f;
-
-    memset(state->Env, 0, sizeof(state->Env));
-
-    for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
-    {
-        for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
-            state->Chans[i].CurrentGains[j] = 0.0f;
-        state->Chans[i].Filter.z1 = 0.0f;
-        state->Chans[i].Filter.z2 = 0.0f;
-    }
-
-    return AL_TRUE;
-}
-
-static ALvoid ALautowahState_update(ALautowahState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
-{
-    const ALCdevice *device = context->Device;
-    ALfloat ReleaseTime;
-    ALsizei i;
-
-    ReleaseTime = clampf(props->Autowah.ReleaseTime, 0.001f, 1.0f);
-
-    state->AttackRate    = expf(-1.0f / (props->Autowah.AttackTime*device->Frequency));
-    state->ReleaseRate   = expf(-1.0f / (ReleaseTime*device->Frequency));
-    /* 0-20dB Resonance Peak gain */
-    state->ResonanceGain = sqrtf(log10f(props->Autowah.Resonance)*10.0f / 3.0f);
-    state->PeakGain      = 1.0f - log10f(props->Autowah.PeakGain/AL_AUTOWAH_MAX_PEAK_GAIN);
-    state->FreqMinNorm   = MIN_FREQ / device->Frequency;
-    state->BandwidthNorm = (MAX_FREQ-MIN_FREQ) / device->Frequency;
-
-    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 ALautowahState_process(ALautowahState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
-{
-    const ALfloat attack_rate = state->AttackRate;
-    const ALfloat release_rate = state->ReleaseRate;
-    const ALfloat res_gain = state->ResonanceGain;
-    const ALfloat peak_gain = state->PeakGain;
-    const ALfloat freq_min = state->FreqMinNorm;
-    const ALfloat bandwidth = state->BandwidthNorm;
-    ALfloat env_delay;
-    ALsizei c, i;
-
-    env_delay = state->env_delay;
-    for(i = 0;i < SamplesToDo;i++)
-    {
-        ALfloat w0, sample, a;
-
-        /* Envelope follower described on the book: Audio Effects, Theory,
-         * Implementation and Application.
-         */
-        sample = peak_gain * fabsf(SamplesIn[0][i]);
-        a = (sample > env_delay) ? attack_rate : release_rate;
-        env_delay = lerp(sample, env_delay, a);
-
-        /* Calculate the cos and alpha components for this sample's filter. */
-        w0 = minf((bandwidth*env_delay + freq_min), 0.46f) * F_TAU;
-        state->Env[i].cos_w0 = cosf(w0);
-        state->Env[i].alpha = sinf(w0)/(2.0f * Q_FACTOR);
-    }
-    state->env_delay = env_delay;
-
-    for(c = 0;c < MAX_EFFECT_CHANNELS; c++)
-    {
-        /* This effectively inlines BiquadFilter_setParams for a peaking
-         * filter and BiquadFilter_processC. The alpha and cosine components
-         * for the filter coefficients were previously calculated with the
-         * envelope. Because the filter changes for each sample, the
-         * coefficients are transient and don't need to be held.
-         */
-        ALfloat z1 = state->Chans[c].Filter.z1;
-        ALfloat z2 = state->Chans[c].Filter.z2;
-
-        for(i = 0;i < SamplesToDo;i++)
-        {
-            const ALfloat alpha = state->Env[i].alpha;
-            const ALfloat cos_w0 = state->Env[i].cos_w0;
-            ALfloat input, output;
-            ALfloat a[3], b[3];
-
-            b[0] =  1.0f + alpha*res_gain;
-            b[1] = -2.0f * cos_w0;
-            b[2] =  1.0f - alpha*res_gain;
-            a[0] =  1.0f + alpha/res_gain;
-            a[1] = -2.0f * cos_w0;
-            a[2] =  1.0f - alpha/res_gain;
-
-            input = SamplesIn[c][i];
-            output = input*(b[0]/a[0]) + z1;
-            z1 = input*(b[1]/a[0]) - output*(a[1]/a[0]) + z2;
-            z2 = input*(b[2]/a[0]) - output*(a[2]/a[0]);
-            state->BufferOut[i] = output;
-        }
-        state->Chans[c].Filter.z1 = z1;
-        state->Chans[c].Filter.z2 = z2;
-
-        /* Now, mix the processed sound data to the output. */
-        MixSamples(state->BufferOut, NumChannels, SamplesOut, state->Chans[c].CurrentGains,
-                   state->Chans[c].TargetGains, SamplesToDo, 0, SamplesToDo);
-    }
-}
-
-typedef struct AutowahStateFactory {
-    DERIVE_FROM_TYPE(EffectStateFactory);
-} AutowahStateFactory;
-
-static ALeffectState *AutowahStateFactory_create(AutowahStateFactory *UNUSED(factory))
-{
-    ALautowahState *state;
-
-    NEW_OBJ0(state, ALautowahState)();
-    if(!state) return NULL;
-
-    return STATIC_CAST(ALeffectState, state);
-}
-
-DEFINE_EFFECTSTATEFACTORY_VTABLE(AutowahStateFactory);
-
-EffectStateFactory *AutowahStateFactory_getFactory(void)
-{
-    static AutowahStateFactory AutowahFactory = { { GET_VTABLE2(AutowahStateFactory, EffectStateFactory) } };
-
-    return STATIC_CAST(EffectStateFactory, &AutowahFactory);
-}
-
-void ALautowah_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
-{
-    ALeffectProps *props = &effect->Props;
-    switch(param)
-    {
-        case AL_AUTOWAH_ATTACK_TIME:
-            if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME))
-                SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah attack time out of range");
-            props->Autowah.AttackTime = val;
-            break;
-
-        case AL_AUTOWAH_RELEASE_TIME:
-            if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME))
-                SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah release time out of range");
-            props->Autowah.ReleaseTime = val;
-            break;
-
-        case AL_AUTOWAH_RESONANCE:
-            if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
-                SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah resonance out of range");
-            props->Autowah.Resonance = val;
-            break;
-
-        case AL_AUTOWAH_PEAK_GAIN:
-            if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN))
-                SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah peak gain out of range");
-            props->Autowah.PeakGain = val;
-            break;
-
-        default:
-            alSetError(context, AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param);
-    }
-}
-
-void ALautowah_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
-{
-    ALautowah_setParamf(effect, context, param, vals[0]);
-}
-
-void ALautowah_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
-{
-    alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param);
-}
-
-void ALautowah_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
-{
-    alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", param);
-}
-
-void ALautowah_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
-{
-    alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param);
-}
-void ALautowah_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
-{
-    alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", param);
-}
-
-void ALautowah_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
-{
-
-    const ALeffectProps *props = &effect->Props;
-    switch(param)
-    {
-        case AL_AUTOWAH_ATTACK_TIME:
-            *val = props->Autowah.AttackTime;
-            break;
-
-        case AL_AUTOWAH_RELEASE_TIME:
-            *val = props->Autowah.ReleaseTime;
-            break;
-
-        case AL_AUTOWAH_RESONANCE:
-            *val = props->Autowah.Resonance;
-            break;
-
-        case AL_AUTOWAH_PEAK_GAIN:
-            *val = props->Autowah.PeakGain;
-            break;
-
-        default:
-            alSetError(context, AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param);
-    }
-
-}
-
-void ALautowah_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
-{
-    ALautowah_getParamf(effect, context, param, vals);
-}
-
-DEFINE_ALEFFECT_VTABLE(ALautowah);
diff --git a/Alc/effects/autowah.cpp b/Alc/effects/autowah.cpp
new file mode 100644
index 00000000..b4ef8f3c
--- /dev/null
+++ b/Alc/effects/autowah.cpp
@@ -0,0 +1,326 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2018 by Raul Herraiz.
+ * 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"
+
+#define MIN_FREQ 20.0f
+#define MAX_FREQ 2500.0f
+#define Q_FACTOR 5.0f
+
+struct ALautowahState final : public ALeffectState {
+    /* Effect parameters */
+    ALfloat AttackRate;
+    ALfloat ReleaseRate;
+    ALfloat ResonanceGain;
+    ALfloat PeakGain;
+    ALfloat FreqMinNorm;
+    ALfloat BandwidthNorm;
+    ALfloat env_delay;
+
+    /* Filter components derived from the envelope. */
+    struct {
+        ALfloat cos_w0;
+        ALfloat alpha;
+    } Env[BUFFERSIZE];
+
+    struct {
+        /* Effect filters' history. */
+        struct {
+            ALfloat z1, z2;
+        } Filter;
+
+        /* Effect gains for each output channel */
+        ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
+        ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
+    } Chans[MAX_EFFECT_CHANNELS];
+
+    /* Effects buffers */
+    alignas(16) ALfloat BufferOut[BUFFERSIZE];
+};
+
+static ALvoid ALautowahState_Destruct(ALautowahState *state);
+static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *device);
+static ALvoid ALautowahState_update(ALautowahState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
+static ALvoid ALautowahState_process(ALautowahState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALautowahState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALautowahState);
+
+static void ALautowahState_Construct(ALautowahState *state)
+{
+    new (state) ALautowahState{};
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALautowahState, ALeffectState, state);
+}
+
+static ALvoid ALautowahState_Destruct(ALautowahState *state)
+{
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
+    state->~ALautowahState();
+}
+
+static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *UNUSED(device))
+{
+    /* (Re-)initializing parameters and clear the buffers. */
+    ALsizei i, j;
+
+    state->AttackRate    = 1.0f;
+    state->ReleaseRate   = 1.0f;
+    state->ResonanceGain = 10.0f;
+    state->PeakGain      = 4.5f;
+    state->FreqMinNorm   = 4.5e-4f;
+    state->BandwidthNorm = 0.05f;
+    state->env_delay     = 0.0f;
+
+    memset(state->Env, 0, sizeof(state->Env));
+
+    for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
+    {
+        for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
+            state->Chans[i].CurrentGains[j] = 0.0f;
+        state->Chans[i].Filter.z1 = 0.0f;
+        state->Chans[i].Filter.z2 = 0.0f;
+    }
+
+    return AL_TRUE;
+}
+
+static ALvoid ALautowahState_update(ALautowahState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
+{
+    const ALCdevice *device = context->Device;
+    ALfloat ReleaseTime;
+    ALsizei i;
+
+    ReleaseTime = clampf(props->Autowah.ReleaseTime, 0.001f, 1.0f);
+
+    state->AttackRate    = expf(-1.0f / (props->Autowah.AttackTime*device->Frequency));
+    state->ReleaseRate   = expf(-1.0f / (ReleaseTime*device->Frequency));
+    /* 0-20dB Resonance Peak gain */
+    state->ResonanceGain = sqrtf(log10f(props->Autowah.Resonance)*10.0f / 3.0f);
+    state->PeakGain      = 1.0f - log10f(props->Autowah.PeakGain/AL_AUTOWAH_MAX_PEAK_GAIN);
+    state->FreqMinNorm   = MIN_FREQ / device->Frequency;
+    state->BandwidthNorm = (MAX_FREQ-MIN_FREQ) / device->Frequency;
+
+    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 ALautowahState_process(ALautowahState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
+{
+    const ALfloat attack_rate = state->AttackRate;
+    const ALfloat release_rate = state->ReleaseRate;
+    const ALfloat res_gain = state->ResonanceGain;
+    const ALfloat peak_gain = state->PeakGain;
+    const ALfloat freq_min = state->FreqMinNorm;
+    const ALfloat bandwidth = state->BandwidthNorm;
+    ALfloat env_delay;
+    ALsizei c, i;
+
+    env_delay = state->env_delay;
+    for(i = 0;i < SamplesToDo;i++)
+    {
+        ALfloat w0, sample, a;
+
+        /* Envelope follower described on the book: Audio Effects, Theory,
+         * Implementation and Application.
+         */
+        sample = peak_gain * fabsf(SamplesIn[0][i]);
+        a = (sample > env_delay) ? attack_rate : release_rate;
+        env_delay = lerp(sample, env_delay, a);
+
+        /* Calculate the cos and alpha components for this sample's filter. */
+        w0 = minf((bandwidth*env_delay + freq_min), 0.46f) * F_TAU;
+        state->Env[i].cos_w0 = cosf(w0);
+        state->Env[i].alpha = sinf(w0)/(2.0f * Q_FACTOR);
+    }
+    state->env_delay = env_delay;
+
+    for(c = 0;c < MAX_EFFECT_CHANNELS; c++)
+    {
+        /* This effectively inlines BiquadFilter_setParams for a peaking
+         * filter and BiquadFilter_processC. The alpha and cosine components
+         * for the filter coefficients were previously calculated with the
+         * envelope. Because the filter changes for each sample, the
+         * coefficients are transient and don't need to be held.
+         */
+        ALfloat z1 = state->Chans[c].Filter.z1;
+        ALfloat z2 = state->Chans[c].Filter.z2;
+
+        for(i = 0;i < SamplesToDo;i++)
+        {
+            const ALfloat alpha = state->Env[i].alpha;
+            const ALfloat cos_w0 = state->Env[i].cos_w0;
+            ALfloat input, output;
+            ALfloat a[3], b[3];
+
+            b[0] =  1.0f + alpha*res_gain;
+            b[1] = -2.0f * cos_w0;
+            b[2] =  1.0f - alpha*res_gain;
+            a[0] =  1.0f + alpha/res_gain;
+            a[1] = -2.0f * cos_w0;
+            a[2] =  1.0f - alpha/res_gain;
+
+            input = SamplesIn[c][i];
+            output = input*(b[0]/a[0]) + z1;
+            z1 = input*(b[1]/a[0]) - output*(a[1]/a[0]) + z2;
+            z2 = input*(b[2]/a[0]) - output*(a[2]/a[0]);
+            state->BufferOut[i] = output;
+        }
+        state->Chans[c].Filter.z1 = z1;
+        state->Chans[c].Filter.z2 = z2;
+
+        /* Now, mix the processed sound data to the output. */
+        MixSamples(state->BufferOut, NumChannels, SamplesOut, state->Chans[c].CurrentGains,
+                   state->Chans[c].TargetGains, SamplesToDo, 0, SamplesToDo);
+    }
+}
+
+struct AutowahStateFactory final : public EffectStateFactory {
+    AutowahStateFactory() noexcept;
+};
+
+static ALeffectState *AutowahStateFactory_create(AutowahStateFactory *UNUSED(factory))
+{
+    ALautowahState *state;
+
+    NEW_OBJ0(state, ALautowahState)();
+    if(!state) return NULL;
+
+    return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_EFFECTSTATEFACTORY_VTABLE(AutowahStateFactory);
+
+AutowahStateFactory::AutowahStateFactory() noexcept
+  : EffectStateFactory{GET_VTABLE2(AutowahStateFactory, EffectStateFactory)}
+{
+}
+
+EffectStateFactory *AutowahStateFactory_getFactory(void)
+{
+    static AutowahStateFactory AutowahFactory{};
+
+    return STATIC_CAST(EffectStateFactory, &AutowahFactory);
+}
+
+void ALautowah_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_AUTOWAH_ATTACK_TIME:
+            if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME))
+                SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah attack time out of range");
+            props->Autowah.AttackTime = val;
+            break;
+
+        case AL_AUTOWAH_RELEASE_TIME:
+            if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME))
+                SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah release time out of range");
+            props->Autowah.ReleaseTime = val;
+            break;
+
+        case AL_AUTOWAH_RESONANCE:
+            if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
+                SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah resonance out of range");
+            props->Autowah.Resonance = val;
+            break;
+
+        case AL_AUTOWAH_PEAK_GAIN:
+            if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN))
+                SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah peak gain out of range");
+            props->Autowah.PeakGain = val;
+            break;
+
+        default:
+            alSetError(context, AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param);
+    }
+}
+
+void ALautowah_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+    ALautowah_setParamf(effect, context, param, vals[0]);
+}
+
+void ALautowah_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
+{
+    alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param);
+}
+
+void ALautowah_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
+{
+    alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", param);
+}
+
+void ALautowah_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
+{
+    alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param);
+}
+void ALautowah_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
+{
+    alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", param);
+}
+
+void ALautowah_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+
+    const ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_AUTOWAH_ATTACK_TIME:
+            *val = props->Autowah.AttackTime;
+            break;
+
+        case AL_AUTOWAH_RELEASE_TIME:
+            *val = props->Autowah.ReleaseTime;
+            break;
+
+        case AL_AUTOWAH_RESONANCE:
+            *val = props->Autowah.Resonance;
+            break;
+
+        case AL_AUTOWAH_PEAK_GAIN:
+            *val = props->Autowah.PeakGain;
+            break;
+
+        default:
+            alSetError(context, AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param);
+    }
+
+}
+
+void ALautowah_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+    ALautowah_getParamf(effect, context, param, vals);
+}
+
+DEFINE_ALEFFECT_VTABLE(ALautowah);
diff --git a/Alc/effects/chorus.c b/Alc/effects/chorus.c
deleted file mode 100644
index 725189b3..00000000
--- a/Alc/effects/chorus.c
+++ /dev/null
@@ -1,555 +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"
-
-
-static_assert(AL_CHORUS_WAVEFORM_SINUSOID == AL_FLANGER_WAVEFORM_SINUSOID, "Chorus/Flanger waveform value mismatch");
-static_assert(AL_CHORUS_WAVEFORM_TRIANGLE == AL_FLANGER_WAVEFORM_TRIANGLE, "Chorus/Flanger waveform value mismatch");
-
-enum WaveForm {
-    WF_Sinusoid,
-    WF_Triangle
-};
-
-typedef struct ALchorusState {
-    DERIVE_FROM_TYPE(ALeffectState);
-
-    ALfloat *SampleBuffer;
-    ALsizei BufferLength;
-    ALsizei offset;
-
-    ALsizei lfo_offset;
-    ALsizei lfo_range;
-    ALfloat lfo_scale;
-    ALint lfo_disp;
-
-    /* Gains for left and right sides */
-    struct {
-        ALfloat Current[MAX_OUTPUT_CHANNELS];
-        ALfloat Target[MAX_OUTPUT_CHANNELS];
-    } Gains[2];
-
-    /* effect parameters */
-    enum WaveForm waveform;
-    ALint delay;
-    ALfloat depth;
-    ALfloat feedback;
-} ALchorusState;
-
-static ALvoid ALchorusState_Destruct(ALchorusState *state);
-static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device);
-static ALvoid ALchorusState_update(ALchorusState *state, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props);
-static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
-DECLARE_DEFAULT_ALLOCATORS(ALchorusState)
-
-DEFINE_ALEFFECTSTATE_VTABLE(ALchorusState);
-
-
-static void ALchorusState_Construct(ALchorusState *state)
-{
-    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
-    SET_VTABLE2(ALchorusState, ALeffectState, state);
-
-    state->BufferLength = 0;
-    state->SampleBuffer = NULL;
-    state->offset = 0;
-    state->lfo_offset = 0;
-    state->lfo_range = 1;
-    state->waveform = WF_Triangle;
-}
-
-static ALvoid ALchorusState_Destruct(ALchorusState *state)
-{
-    al_free(state->SampleBuffer);
-    state->SampleBuffer = NULL;
-
-    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
-}
-
-static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device)
-{
-    const ALfloat max_delay = maxf(AL_CHORUS_MAX_DELAY, AL_FLANGER_MAX_DELAY);
-    ALsizei maxlen;
-
-    maxlen = NextPowerOf2(float2int(max_delay*2.0f*Device->Frequency) + 1u);
-    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 ALchorusState_update(ALchorusState *state, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props)
-{
-    const ALsizei mindelay = MAX_RESAMPLE_PADDING << FRACTIONBITS;
-    const ALCdevice *device = Context->Device;
-    ALfloat frequency = (ALfloat)device->Frequency;
-    ALfloat coeffs[MAX_AMBI_COEFFS];
-    ALfloat rate;
-    ALint phase;
-
-    switch(props->Chorus.Waveform)
-    {
-        case AL_CHORUS_WAVEFORM_TRIANGLE:
-            state->waveform = WF_Triangle;
-            break;
-        case AL_CHORUS_WAVEFORM_SINUSOID:
-            state->waveform = WF_Sinusoid;
-            break;
-    }
-
-    /* The LFO depth is scaled to be relative to the sample delay. Clamp the
-     * delay and depth to allow enough padding for resampling.
-     */
-    state->delay = maxi(float2int(props->Chorus.Delay*frequency*FRACTIONONE + 0.5f),
-                        mindelay);
-    state->depth = minf(props->Chorus.Depth * state->delay,
-                        (ALfloat)(state->delay - mindelay));
-
-    state->feedback = props->Chorus.Feedback;
-
-    /* Gains for left and right sides */
-    CalcAngleCoeffs(-F_PI_2, 0.0f, 0.0f, coeffs);
-    ComputePanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[0].Target);
-    CalcAngleCoeffs( F_PI_2, 0.0f, 0.0f, coeffs);
-    ComputePanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[1].Target);
-
-    phase = props->Chorus.Phase;
-    rate = props->Chorus.Rate;
-    if(!(rate > 0.0f))
-    {
-        state->lfo_offset = 0;
-        state->lfo_range = 1;
-        state->lfo_scale = 0.0f;
-        state->lfo_disp = 0;
-    }
-    else
-    {
-        /* Calculate LFO coefficient (number of samples per cycle). Limit the
-         * max range to avoid overflow when calculating the displacement.
-         */
-        ALsizei lfo_range = float2int(minf(frequency/rate + 0.5f, (ALfloat)(INT_MAX/360 - 180)));
-
-        state->lfo_offset = float2int((ALfloat)state->lfo_offset/state->lfo_range*
-                                      lfo_range + 0.5f) % lfo_range;
-        state->lfo_range = lfo_range;
-        switch(state->waveform)
-        {
-            case WF_Triangle:
-                state->lfo_scale = 4.0f / state->lfo_range;
-                break;
-            case WF_Sinusoid:
-                state->lfo_scale = F_TAU / state->lfo_range;
-                break;
-        }
-
-        /* Calculate lfo phase displacement */
-        if(phase < 0) phase = 360 + phase;
-        state->lfo_disp = (state->lfo_range*phase + 180) / 360;
-    }
-}
-
-static void GetTriangleDelays(ALint *RESTRICT delays, ALsizei offset, const ALsizei lfo_range,
-                              const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
-                              const ALsizei todo)
-{
-    ALsizei i;
-    for(i = 0;i < todo;i++)
-    {
-        delays[i] = fastf2i((1.0f - fabsf(2.0f - lfo_scale*offset)) * depth) + delay;
-        offset = (offset+1)%lfo_range;
-    }
-}
-
-static void GetSinusoidDelays(ALint *RESTRICT delays, ALsizei offset, const ALsizei lfo_range,
-                              const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
-                              const ALsizei todo)
-{
-    ALsizei i;
-    for(i = 0;i < todo;i++)
-    {
-        delays[i] = fastf2i(sinf(lfo_scale*offset) * depth) + delay;
-        offset = (offset+1)%lfo_range;
-    }
-}
-
-
-static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
-{
-    const ALsizei bufmask = state->BufferLength-1;
-    const ALfloat feedback = state->feedback;
-    const ALsizei avgdelay = (state->delay + (FRACTIONONE>>1)) >> FRACTIONBITS;
-    ALfloat *RESTRICT delaybuf = state->SampleBuffer;
-    ALsizei offset = state->offset;
-    ALsizei i, c;
-    ALsizei base;
-
-    for(base = 0;base < SamplesToDo;)
-    {
-        const ALsizei todo = mini(256, SamplesToDo-base);
-        ALint moddelays[2][256];
-        alignas(16) ALfloat temps[2][256];
-
-        if(state->waveform == WF_Sinusoid)
-        {
-            GetSinusoidDelays(moddelays[0], state->lfo_offset, state->lfo_range, state->lfo_scale,
-                              state->depth, state->delay, todo);
-            GetSinusoidDelays(moddelays[1], (state->lfo_offset+state->lfo_disp)%state->lfo_range,
-                              state->lfo_range, state->lfo_scale, state->depth, state->delay,
-                              todo);
-        }
-        else /*if(state->waveform == WF_Triangle)*/
-        {
-            GetTriangleDelays(moddelays[0], state->lfo_offset, state->lfo_range, state->lfo_scale,
-                              state->depth, state->delay, todo);
-            GetTriangleDelays(moddelays[1], (state->lfo_offset+state->lfo_disp)%state->lfo_range,
-                              state->lfo_range, state->lfo_scale, state->depth, state->delay,
-                              todo);
-        }
-        state->lfo_offset = (state->lfo_offset+todo) % state->lfo_range;
-
-        for(i = 0;i < todo;i++)
-        {
-            ALint delay;
-            ALfloat mu;
-
-            // Feed the buffer's input first (necessary for delays < 1).
-            delaybuf[offset&bufmask] = SamplesIn[0][base+i];
-
-            // Tap for the left output.
-            delay = offset - (moddelays[0][i]>>FRACTIONBITS);
-            mu = (moddelays[0][i]&FRACTIONMASK) * (1.0f/FRACTIONONE);
-            temps[0][i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay  ) & bufmask],
-                                delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask],
-                                mu);
-
-            // Tap for the right output.
-            delay = offset - (moddelays[1][i]>>FRACTIONBITS);
-            mu = (moddelays[1][i]&FRACTIONMASK) * (1.0f/FRACTIONONE);
-            temps[1][i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay  ) & bufmask],
-                                delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask],
-                                mu);
-
-            // Accumulate feedback from the average delay of the taps.
-            delaybuf[offset&bufmask] += delaybuf[(offset-avgdelay) & bufmask] * feedback;
-            offset++;
-        }
-
-        for(c = 0;c < 2;c++)
-            MixSamples(temps[c], NumChannels, SamplesOut, state->Gains[c].Current,
-                       state->Gains[c].Target, SamplesToDo-base, base, todo);
-
-        base += todo;
-    }
-
-    state->offset = offset;
-}
-
-
-typedef struct ChorusStateFactory {
-    DERIVE_FROM_TYPE(EffectStateFactory);
-} ChorusStateFactory;
-
-static ALeffectState *ChorusStateFactory_create(ChorusStateFactory *UNUSED(factory))
-{
-    ALchorusState *state;
-
-    NEW_OBJ0(state, ALchorusState)();
-    if(!state) return NULL;
-
-    return STATIC_CAST(ALeffectState, state);
-}
-
-DEFINE_EFFECTSTATEFACTORY_VTABLE(ChorusStateFactory);
-
-
-EffectStateFactory *ChorusStateFactory_getFactory(void)
-{
-    static ChorusStateFactory ChorusFactory = { { GET_VTABLE2(ChorusStateFactory, EffectStateFactory) } };
-
-    return STATIC_CAST(EffectStateFactory, &ChorusFactory);
-}
-
-
-void ALchorus_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
-{
-    ALeffectProps *props = &effect->Props;
-    switch(param)
-    {
-        case AL_CHORUS_WAVEFORM:
-            if(!(val >= AL_CHORUS_MIN_WAVEFORM && val <= AL_CHORUS_MAX_WAVEFORM))
-                SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid chorus waveform");
-            props->Chorus.Waveform = val;
-            break;
-
-        case AL_CHORUS_PHASE:
-            if(!(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_PHASE))
-                SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus phase out of range");
-            props->Chorus.Phase = val;
-            break;
-
-        default:
-            alSetError(context, AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param);
-    }
-}
-void ALchorus_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
-{ ALchorus_setParami(effect, context, param, vals[0]); }
-void ALchorus_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
-{
-    ALeffectProps *props = &effect->Props;
-    switch(param)
-    {
-        case AL_CHORUS_RATE:
-            if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE))
-                SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus rate out of range");
-            props->Chorus.Rate = val;
-            break;
-
-        case AL_CHORUS_DEPTH:
-            if(!(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_DEPTH))
-                SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus depth out of range");
-            props->Chorus.Depth = val;
-            break;
-
-        case AL_CHORUS_FEEDBACK:
-            if(!(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_FEEDBACK))
-                SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus feedback out of range");
-            props->Chorus.Feedback = val;
-            break;
-
-        case AL_CHORUS_DELAY:
-            if(!(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_DELAY))
-                SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus delay out of range");
-            props->Chorus.Delay = val;
-            break;
-
-        default:
-            alSetError(context, AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param);
-    }
-}
-void ALchorus_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ ALchorus_setParamf(effect, context, param, vals[0]); }
-
-void ALchorus_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
-{
-    const ALeffectProps *props = &effect->Props;
-    switch(param)
-    {
-        case AL_CHORUS_WAVEFORM:
-            *val = props->Chorus.Waveform;
-            break;
-
-        case AL_CHORUS_PHASE:
-            *val = props->Chorus.Phase;
-            break;
-
-        default:
-            alSetError(context, AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param);
-    }
-}
-void ALchorus_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
-{ ALchorus_getParami(effect, context, param, vals); }
-void ALchorus_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
-{
-    const ALeffectProps *props = &effect->Props;
-    switch(param)
-    {
-        case AL_CHORUS_RATE:
-            *val = props->Chorus.Rate;
-            break;
-
-        case AL_CHORUS_DEPTH:
-            *val = props->Chorus.Depth;
-            break;
-
-        case AL_CHORUS_FEEDBACK:
-            *val = props->Chorus.Feedback;
-            break;
-
-        case AL_CHORUS_DELAY:
-            *val = props->Chorus.Delay;
-            break;
-
-        default:
-            alSetError(context, AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param);
-    }
-}
-void ALchorus_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
-{ ALchorus_getParamf(effect, context, param, vals); }
-
-DEFINE_ALEFFECT_VTABLE(ALchorus);
-
-
-/* Flanger is basically a chorus with a really short delay. They can both use
- * the same processing functions, so piggyback flanger on the chorus functions.
- */
-typedef struct FlangerStateFactory {
-    DERIVE_FROM_TYPE(EffectStateFactory);
-} FlangerStateFactory;
-
-ALeffectState *FlangerStateFactory_create(FlangerStateFactory *UNUSED(factory))
-{
-    ALchorusState *state;
-
-    NEW_OBJ0(state, ALchorusState)();
-    if(!state) return NULL;
-
-    return STATIC_CAST(ALeffectState, state);
-}
-
-DEFINE_EFFECTSTATEFACTORY_VTABLE(FlangerStateFactory);
-
-EffectStateFactory *FlangerStateFactory_getFactory(void)
-{
-    static FlangerStateFactory FlangerFactory = { { GET_VTABLE2(FlangerStateFactory, EffectStateFactory) } };
-
-    return STATIC_CAST(EffectStateFactory, &FlangerFactory);
-}
-
-
-void ALflanger_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
-{
-    ALeffectProps *props = &effect->Props;
-    switch(param)
-    {
-        case AL_FLANGER_WAVEFORM:
-            if(!(val >= AL_FLANGER_MIN_WAVEFORM && val <= AL_FLANGER_MAX_WAVEFORM))
-                SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid flanger waveform");
-            props->Chorus.Waveform = val;
-            break;
-
-        case AL_FLANGER_PHASE:
-            if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE))
-                SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger phase out of range");
-            props->Chorus.Phase = val;
-            break;
-
-        default:
-            alSetError(context, AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param);
-    }
-}
-void ALflanger_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
-{ ALflanger_setParami(effect, context, param, vals[0]); }
-void ALflanger_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
-{
-    ALeffectProps *props = &effect->Props;
-    switch(param)
-    {
-        case AL_FLANGER_RATE:
-            if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE))
-                SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger rate out of range");
-            props->Chorus.Rate = val;
-            break;
-
-        case AL_FLANGER_DEPTH:
-            if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH))
-                SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger depth out of range");
-            props->Chorus.Depth = val;
-            break;
-
-        case AL_FLANGER_FEEDBACK:
-            if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK))
-                SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger feedback out of range");
-            props->Chorus.Feedback = val;
-            break;
-
-        case AL_FLANGER_DELAY:
-            if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY))
-                SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger delay out of range");
-            props->Chorus.Delay = val;
-            break;
-
-        default:
-            alSetError(context, AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param);
-    }
-}
-void ALflanger_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
-{ ALflanger_setParamf(effect, context, param, vals[0]); }
-
-void ALflanger_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
-{
-    const ALeffectProps *props = &effect->Props;
-    switch(param)
-    {
-        case AL_FLANGER_WAVEFORM:
-            *val = props->Chorus.Waveform;
-            break;
-
-        case AL_FLANGER_PHASE:
-            *val = props->Chorus.Phase;
-            break;
-
-        default:
-            alSetError(context, AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param);
-    }
-}
-void ALflanger_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
-{ ALflanger_getParami(effect, context, param, vals); }
-void ALflanger_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
-{
-    const ALeffectProps *props = &effect->Props;
-    switch(param)
-    {
-        case AL_FLANGER_RATE:
-            *val = props->Chorus.Rate;
-            break;
-
-        case AL_FLANGER_DEPTH:
-            *val = props->Chorus.Depth;
-            break;
-
-        case AL_FLANGER_FEEDBACK:
-            *val = props->Chorus.Feedback;
-            break;
-
-        case AL_FLANGER_DELAY:
-            *val = props->Chorus.Delay;
-            break;
-
-        default:
-            alSetError(context, AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param);
-    }
-}
-void ALflanger_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
-{ ALflanger_getParamf(effect, context, param, vals); }
-
-DEFINE_ALEFFECT_VTABLE(ALflanger);
diff --git a/Alc/effects/chorus.cpp b/Alc/effects/chorus.cpp
new file mode 100644
index 00000000..b658098e
--- /dev/null
+++ b/Alc/effects/chorus.cpp
@@ -0,0 +1,561 @@
+/**
+ * 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"
+
+
+static_assert(AL_CHORUS_WAVEFORM_SINUSOID == AL_FLANGER_WAVEFORM_SINUSOID, "Chorus/Flanger waveform value mismatch");
+static_assert(AL_CHORUS_WAVEFORM_TRIANGLE == AL_FLANGER_WAVEFORM_TRIANGLE, "Chorus/Flanger waveform value mismatch");
+
+enum WaveForm {
+    WF_Sinusoid,
+    WF_Triangle
+};
+
+struct ALchorusState final : public ALeffectState {
+    ALfloat *SampleBuffer;
+    ALsizei BufferLength;
+    ALsizei offset;
+
+    ALsizei lfo_offset;
+    ALsizei lfo_range;
+    ALfloat lfo_scale;
+    ALint lfo_disp;
+
+    /* Gains for left and right sides */
+    struct {
+        ALfloat Current[MAX_OUTPUT_CHANNELS];
+        ALfloat Target[MAX_OUTPUT_CHANNELS];
+    } Gains[2];
+
+    /* effect parameters */
+    enum WaveForm waveform;
+    ALint delay;
+    ALfloat depth;
+    ALfloat feedback;
+};
+
+static ALvoid ALchorusState_Destruct(ALchorusState *state);
+static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device);
+static ALvoid ALchorusState_update(ALchorusState *state, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props);
+static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALchorusState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALchorusState);
+
+
+static void ALchorusState_Construct(ALchorusState *state)
+{
+    new (state) ALchorusState{};
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALchorusState, ALeffectState, state);
+
+    state->BufferLength = 0;
+    state->SampleBuffer = NULL;
+    state->offset = 0;
+    state->lfo_offset = 0;
+    state->lfo_range = 1;
+    state->waveform = WF_Triangle;
+}
+
+static ALvoid ALchorusState_Destruct(ALchorusState *state)
+{
+    al_free(state->SampleBuffer);
+    state->SampleBuffer = NULL;
+
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
+    state->~ALchorusState();
+}
+
+static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device)
+{
+    const ALfloat max_delay = maxf(AL_CHORUS_MAX_DELAY, AL_FLANGER_MAX_DELAY);
+    ALsizei maxlen;
+
+    maxlen = NextPowerOf2(float2int(max_delay*2.0f*Device->Frequency) + 1u);
+    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 ALchorusState_update(ALchorusState *state, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props)
+{
+    const ALsizei mindelay = MAX_RESAMPLE_PADDING << FRACTIONBITS;
+    const ALCdevice *device = Context->Device;
+    ALfloat frequency = (ALfloat)device->Frequency;
+    ALfloat coeffs[MAX_AMBI_COEFFS];
+    ALfloat rate;
+    ALint phase;
+
+    switch(props->Chorus.Waveform)
+    {
+        case AL_CHORUS_WAVEFORM_TRIANGLE:
+            state->waveform = WF_Triangle;
+            break;
+        case AL_CHORUS_WAVEFORM_SINUSOID:
+            state->waveform = WF_Sinusoid;
+            break;
+    }
+
+    /* The LFO depth is scaled to be relative to the sample delay. Clamp the
+     * delay and depth to allow enough padding for resampling.
+     */
+    state->delay = maxi(float2int(props->Chorus.Delay*frequency*FRACTIONONE + 0.5f),
+                        mindelay);
+    state->depth = minf(props->Chorus.Depth * state->delay,
+                        (ALfloat)(state->delay - mindelay));
+
+    state->feedback = props->Chorus.Feedback;
+
+    /* Gains for left and right sides */
+    CalcAngleCoeffs(-F_PI_2, 0.0f, 0.0f, coeffs);
+    ComputePanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[0].Target);
+    CalcAngleCoeffs( F_PI_2, 0.0f, 0.0f, coeffs);
+    ComputePanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[1].Target);
+
+    phase = props->Chorus.Phase;
+    rate = props->Chorus.Rate;
+    if(!(rate > 0.0f))
+    {
+        state->lfo_offset = 0;
+        state->lfo_range = 1;
+        state->lfo_scale = 0.0f;
+        state->lfo_disp = 0;
+    }
+    else
+    {
+        /* Calculate LFO coefficient (number of samples per cycle). Limit the
+         * max range to avoid overflow when calculating the displacement.
+         */
+        ALsizei lfo_range = float2int(minf(frequency/rate + 0.5f, (ALfloat)(INT_MAX/360 - 180)));
+
+        state->lfo_offset = float2int((ALfloat)state->lfo_offset/state->lfo_range*
+                                      lfo_range + 0.5f) % lfo_range;
+        state->lfo_range = lfo_range;
+        switch(state->waveform)
+        {
+            case WF_Triangle:
+                state->lfo_scale = 4.0f / state->lfo_range;
+                break;
+            case WF_Sinusoid:
+                state->lfo_scale = F_TAU / state->lfo_range;
+                break;
+        }
+
+        /* Calculate lfo phase displacement */
+        if(phase < 0) phase = 360 + phase;
+        state->lfo_disp = (state->lfo_range*phase + 180) / 360;
+    }
+}
+
+static void GetTriangleDelays(ALint *RESTRICT delays, ALsizei offset, const ALsizei lfo_range,
+                              const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
+                              const ALsizei todo)
+{
+    ALsizei i;
+    for(i = 0;i < todo;i++)
+    {
+        delays[i] = fastf2i((1.0f - fabsf(2.0f - lfo_scale*offset)) * depth) + delay;
+        offset = (offset+1)%lfo_range;
+    }
+}
+
+static void GetSinusoidDelays(ALint *RESTRICT delays, ALsizei offset, const ALsizei lfo_range,
+                              const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
+                              const ALsizei todo)
+{
+    ALsizei i;
+    for(i = 0;i < todo;i++)
+    {
+        delays[i] = fastf2i(sinf(lfo_scale*offset) * depth) + delay;
+        offset = (offset+1)%lfo_range;
+    }
+}
+
+
+static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
+{
+    const ALsizei bufmask = state->BufferLength-1;
+    const ALfloat feedback = state->feedback;
+    const ALsizei avgdelay = (state->delay + (FRACTIONONE>>1)) >> FRACTIONBITS;
+    ALfloat *RESTRICT delaybuf = state->SampleBuffer;
+    ALsizei offset = state->offset;
+    ALsizei i, c;
+    ALsizei base;
+
+    for(base = 0;base < SamplesToDo;)
+    {
+        const ALsizei todo = mini(256, SamplesToDo-base);
+        ALint moddelays[2][256];
+        alignas(16) ALfloat temps[2][256];
+
+        if(state->waveform == WF_Sinusoid)
+        {
+            GetSinusoidDelays(moddelays[0], state->lfo_offset, state->lfo_range, state->lfo_scale,
+                              state->depth, state->delay, todo);
+            GetSinusoidDelays(moddelays[1], (state->lfo_offset+state->lfo_disp)%state->lfo_range,
+                              state->lfo_range, state->lfo_scale, state->depth, state->delay,
+                              todo);
+        }
+        else /*if(state->waveform == WF_Triangle)*/
+        {
+            GetTriangleDelays(moddelays[0], state->lfo_offset, state->lfo_range, state->lfo_scale,
+                              state->depth, state->delay, todo);
+            GetTriangleDelays(moddelays[1], (state->lfo_offset+state->lfo_disp)%state->lfo_range,
+                              state->lfo_range, state->lfo_scale, state->depth, state->delay,
+                              todo);
+        }
+        state->lfo_offset = (state->lfo_offset+todo) % state->lfo_range;
+
+        for(i = 0;i < todo;i++)
+        {
+            ALint delay;
+            ALfloat mu;
+
+            // Feed the buffer's input first (necessary for delays < 1).
+            delaybuf[offset&bufmask] = SamplesIn[0][base+i];
+
+            // Tap for the left output.
+            delay = offset - (moddelays[0][i]>>FRACTIONBITS);
+            mu = (moddelays[0][i]&FRACTIONMASK) * (1.0f/FRACTIONONE);
+            temps[0][i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay  ) & bufmask],
+                                delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask],
+                                mu);
+
+            // Tap for the right output.
+            delay = offset - (moddelays[1][i]>>FRACTIONBITS);
+            mu = (moddelays[1][i]&FRACTIONMASK) * (1.0f/FRACTIONONE);
+            temps[1][i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay  ) & bufmask],
+                                delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask],
+                                mu);
+
+            // Accumulate feedback from the average delay of the taps.
+            delaybuf[offset&bufmask] += delaybuf[(offset-avgdelay) & bufmask] * feedback;
+            offset++;
+        }
+
+        for(c = 0;c < 2;c++)
+            MixSamples(temps[c], NumChannels, SamplesOut, state->Gains[c].Current,
+                       state->Gains[c].Target, SamplesToDo-base, base, todo);
+
+        base += todo;
+    }
+
+    state->offset = offset;
+}
+
+
+struct ChorusStateFactory final : public EffectStateFactory {
+    ChorusStateFactory() noexcept;
+};
+
+static ALeffectState *ChorusStateFactory_create(ChorusStateFactory *UNUSED(factory))
+{
+    ALchorusState *state;
+
+    NEW_OBJ0(state, ALchorusState)();
+    if(!state) return NULL;
+
+    return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_EFFECTSTATEFACTORY_VTABLE(ChorusStateFactory);
+
+ChorusStateFactory::ChorusStateFactory() noexcept
+  : EffectStateFactory{GET_VTABLE2(ChorusStateFactory, EffectStateFactory)}
+{
+}
+
+EffectStateFactory *ChorusStateFactory_getFactory(void)
+{
+    static ChorusStateFactory ChorusFactory{};
+    return STATIC_CAST(EffectStateFactory, &ChorusFactory);
+}
+
+
+void ALchorus_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_CHORUS_WAVEFORM:
+            if(!(val >= AL_CHORUS_MIN_WAVEFORM && val <= AL_CHORUS_MAX_WAVEFORM))
+                SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid chorus waveform");
+            props->Chorus.Waveform = val;
+            break;
+
+        case AL_CHORUS_PHASE:
+            if(!(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_PHASE))
+                SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus phase out of range");
+            props->Chorus.Phase = val;
+            break;
+
+        default:
+            alSetError(context, AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param);
+    }
+}
+void ALchorus_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{ ALchorus_setParami(effect, context, param, vals[0]); }
+void ALchorus_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_CHORUS_RATE:
+            if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE))
+                SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus rate out of range");
+            props->Chorus.Rate = val;
+            break;
+
+        case AL_CHORUS_DEPTH:
+            if(!(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_DEPTH))
+                SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus depth out of range");
+            props->Chorus.Depth = val;
+            break;
+
+        case AL_CHORUS_FEEDBACK:
+            if(!(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_FEEDBACK))
+                SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus feedback out of range");
+            props->Chorus.Feedback = val;
+            break;
+
+        case AL_CHORUS_DELAY:
+            if(!(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_DELAY))
+                SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus delay out of range");
+            props->Chorus.Delay = val;
+            break;
+
+        default:
+            alSetError(context, AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param);
+    }
+}
+void ALchorus_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{ ALchorus_setParamf(effect, context, param, vals[0]); }
+
+void ALchorus_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{
+    const ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_CHORUS_WAVEFORM:
+            *val = props->Chorus.Waveform;
+            break;
+
+        case AL_CHORUS_PHASE:
+            *val = props->Chorus.Phase;
+            break;
+
+        default:
+            alSetError(context, AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param);
+    }
+}
+void ALchorus_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{ ALchorus_getParami(effect, context, param, vals); }
+void ALchorus_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+    const ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_CHORUS_RATE:
+            *val = props->Chorus.Rate;
+            break;
+
+        case AL_CHORUS_DEPTH:
+            *val = props->Chorus.Depth;
+            break;
+
+        case AL_CHORUS_FEEDBACK:
+            *val = props->Chorus.Feedback;
+            break;
+
+        case AL_CHORUS_DELAY:
+            *val = props->Chorus.Delay;
+            break;
+
+        default:
+            alSetError(context, AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param);
+    }
+}
+void ALchorus_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{ ALchorus_getParamf(effect, context, param, vals); }
+
+DEFINE_ALEFFECT_VTABLE(ALchorus);
+
+
+/* Flanger is basically a chorus with a really short delay. They can both use
+ * the same processing functions, so piggyback flanger on the chorus functions.
+ */
+struct FlangerStateFactory final : public EffectStateFactory {
+    FlangerStateFactory() noexcept;
+};
+
+ALeffectState *FlangerStateFactory_create(FlangerStateFactory *UNUSED(factory))
+{
+    ALchorusState *state;
+
+    NEW_OBJ0(state, ALchorusState)();
+    if(!state) return NULL;
+
+    return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_EFFECTSTATEFACTORY_VTABLE(FlangerStateFactory);
+
+FlangerStateFactory::FlangerStateFactory() noexcept
+  : EffectStateFactory{GET_VTABLE2(FlangerStateFactory, EffectStateFactory)}
+{
+}
+
+EffectStateFactory *FlangerStateFactory_getFactory(void)
+{
+    static FlangerStateFactory FlangerFactory{};
+    return STATIC_CAST(EffectStateFactory, &FlangerFactory);
+}
+
+
+void ALflanger_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_FLANGER_WAVEFORM:
+            if(!(val >= AL_FLANGER_MIN_WAVEFORM && val <= AL_FLANGER_MAX_WAVEFORM))
+                SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid flanger waveform");
+            props->Chorus.Waveform = val;
+            break;
+
+        case AL_FLANGER_PHASE:
+            if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE))
+                SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger phase out of range");
+            props->Chorus.Phase = val;
+            break;
+
+        default:
+            alSetError(context, AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param);
+    }
+}
+void ALflanger_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{ ALflanger_setParami(effect, context, param, vals[0]); }
+void ALflanger_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_FLANGER_RATE:
+            if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE))
+                SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger rate out of range");
+            props->Chorus.Rate = val;
+            break;
+
+        case AL_FLANGER_DEPTH:
+            if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH))
+                SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger depth out of range");
+            props->Chorus.Depth = val;
+            break;
+
+        case AL_FLANGER_FEEDBACK:
+            if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK))
+                SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger feedback out of range");
+            props->Chorus.Feedback = val;
+            break;
+
+        case AL_FLANGER_DELAY:
+            if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY))
+                SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger delay out of range");
+            props->Chorus.Delay = val;
+            break;
+
+        default:
+            alSetError(context, AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param);
+    }
+}
+void ALflanger_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{ ALflanger_setParamf(effect, context, param, vals[0]); }
+
+void ALflanger_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{
+    const ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_FLANGER_WAVEFORM:
+            *val = props->Chorus.Waveform;
+            break;
+
+        case AL_FLANGER_PHASE:
+            *val = props->Chorus.Phase;
+            break;
+
+        default:
+            alSetError(context, AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param);
+    }
+}
+void ALflanger_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{ ALflanger_getParami(effect, context, param, vals); }
+void ALflanger_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+    const ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_FLANGER_RATE:
+            *val = props->Chorus.Rate;
+            break;
+
+        case AL_FLANGER_DEPTH:
+            *val = props->Chorus.Depth;
+            break;
+
+        case AL_FLANGER_FEEDBACK:
+            *val = props->Chorus.Feedback;
+            break;
+
+        case AL_FLANGER_DELAY:
+            *val = props->Chorus.Delay;
+            break;
+
+        default:
+            alSetError(context, AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param);
+    }
+}
+void ALflanger_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{ ALflanger_getParamf(effect, context, param, vals); }
+
+DEFINE_ALEFFECT_VTABLE(ALflanger);
diff --git a/Alc/effects/compressor.c b/Alc/effects/compressor.c
deleted file mode 100644
index d9b9f1e0..00000000
--- a/Alc/effects/compressor.c
+++ /dev/null
@@ -1,243 +0,0 @@
-/**
- * OpenAL cross platform audio library
- * Copyright (C) 2013 by Anis A. Hireche
- * 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 <stdlib.h>
-
-#include "config.h"
-#include "alError.h"
-#include "alMain.h"
-#include "alAuxEffectSlot.h"
-#include "alu.h"
-
-
-#define AMP_ENVELOPE_MIN  0.5f
-#define AMP_ENVELOPE_MAX  2.0f
-
-#define ATTACK_TIME  0.1f /* 100ms to rise from min to max */
-#define RELEASE_TIME 0.2f /* 200ms to drop from max to min */
-
-
-typedef struct ALcompressorState {
-    DERIVE_FROM_TYPE(ALeffectState);
-
-    /* Effect gains for each channel */
-    ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS];
-
-    /* Effect parameters */
-    ALboolean Enabled;
-    ALfloat AttackMult;
-    ALfloat ReleaseMult;
-    ALfloat EnvFollower;
-} ALcompressorState;
-
-static ALvoid ALcompressorState_Destruct(ALcompressorState *state);
-static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device);
-static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
-static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
-DECLARE_DEFAULT_ALLOCATORS(ALcompressorState)
-
-DEFINE_ALEFFECTSTATE_VTABLE(ALcompressorState);
-
-
-static void ALcompressorState_Construct(ALcompressorState *state)
-{
-    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
-    SET_VTABLE2(ALcompressorState, ALeffectState, state);
-
-    state->Enabled = AL_TRUE;
-    state->AttackMult = 1.0f;
-    state->ReleaseMult = 1.0f;
-    state->EnvFollower = 1.0f;
-}
-
-static ALvoid ALcompressorState_Destruct(ALcompressorState *state)
-{
-    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
-}
-
-static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device)
-{
-    /* Number of samples to do a full attack and release (non-integer sample
-     * counts are okay).
-     */
-    const ALfloat attackCount  = (ALfloat)device->Frequency * ATTACK_TIME;
-    const ALfloat releaseCount = (ALfloat)device->Frequency * RELEASE_TIME;
-
-    /* Calculate per-sample multipliers to attack and release at the desired
-     * rates.
-     */
-    state->AttackMult  = powf(AMP_ENVELOPE_MAX/AMP_ENVELOPE_MIN, 1.0f/attackCount);
-    state->ReleaseMult = powf(AMP_ENVELOPE_MIN/AMP_ENVELOPE_MAX, 1.0f/releaseCount);
-
-    return AL_TRUE;
-}
-
-static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
-{
-    const ALCdevice *device = context->Device;
-    ALuint i;
-
-    state->Enabled = props->Compressor.OnOff;
-
-    STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
-    STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
-    for(i = 0;i < 4;i++)
-        ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain, state->Gain[i]);
-}
-
-static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
-{
-    ALsizei i, j, k;
-    ALsizei base;
-
-    for(base = 0;base < SamplesToDo;)
-    {
-        ALfloat gains[256];
-        ALsizei td = mini(256, SamplesToDo-base);
-        ALfloat env = state->EnvFollower;
-
-        /* Generate the per-sample gains from the signal envelope. */
-        if(state->Enabled)
-        {
-            for(i = 0;i < td;++i)
-            {
-                /* Clamp the absolute amplitude to the defined envelope limits,
-                 * then attack or release the envelope to reach it.
-                 */
-                ALfloat amplitude = clampf(fabsf(SamplesIn[0][base+i]),
-                                           AMP_ENVELOPE_MIN, AMP_ENVELOPE_MAX);
-                if(amplitude > env)
-                    env = minf(env*state->AttackMult, amplitude);
-                else if(amplitude < env)
-                    env = maxf(env*state->ReleaseMult, amplitude);
-
-                /* Apply the reciprocal of the envelope to normalize the volume
-                 * (compress the dynamic range).
-                 */
-                gains[i] = 1.0f / env;
-            }
-        }
-        else
-        {
-            /* Same as above, except the amplitude is forced to 1. This helps
-             * ensure smooth gain changes when the compressor is turned on and
-             * off.
-             */
-            for(i = 0;i < td;++i)
-            {
-                ALfloat amplitude = 1.0f;
-                if(amplitude > env)
-                    env = minf(env*state->AttackMult, amplitude);
-                else if(amplitude < env)
-                    env = maxf(env*state->ReleaseMult, amplitude);
-
-                gains[i] = 1.0f / env;
-            }
-        }
-        state->EnvFollower = env;
-
-        /* Now compress the signal amplitude to output. */
-        for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
-        {
-            for(k = 0;k < NumChannels;k++)
-            {
-                ALfloat gain = state->Gain[j][k];
-                if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
-                    continue;
-
-                for(i = 0;i < td;i++)
-                    SamplesOut[k][base+i] += SamplesIn[j][base+i] * gains[i] * gain;
-            }
-        }
-
-        base += td;
-    }
-}
-
-
-typedef struct CompressorStateFactory {
-    DERIVE_FROM_TYPE(EffectStateFactory);
-} CompressorStateFactory;
-
-static ALeffectState *CompressorStateFactory_create(CompressorStateFactory *UNUSED(factory))
-{
-    ALcompressorState *state;
-
-    NEW_OBJ0(state, ALcompressorState)();
-    if(!state) return NULL;
-
-    return STATIC_CAST(ALeffectState, state);
-}
-
-DEFINE_EFFECTSTATEFACTORY_VTABLE(CompressorStateFactory);
-
-EffectStateFactory *CompressorStateFactory_getFactory(void)
-{
-    static CompressorStateFactory CompressorFactory = { { GET_VTABLE2(CompressorStateFactory, EffectStateFactory) } };
-
-    return STATIC_CAST(EffectStateFactory, &CompressorFactory);
-}
-
-
-void ALcompressor_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
-{
-    ALeffectProps *props = &effect->Props;
-    switch(param)
-    {
-        case AL_COMPRESSOR_ONOFF:
-            if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF))
-                SETERR_RETURN(context, AL_INVALID_VALUE,, "Compressor state out of range");
-            props->Compressor.OnOff = val;
-            break;
-
-        default:
-            alSetError(context, AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
-                       param);
-    }
-}
-void ALcompressor_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
-{ ALcompressor_setParami(effect, context, param, vals[0]); }
-void ALcompressor_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val))
-{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param); }
-void ALcompressor_setParamfv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat *UNUSED(vals))
-{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", param); }
-
-void ALcompressor_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
-{ 
-    const ALeffectProps *props = &effect->Props;
-    switch(param)
-    {
-        case AL_COMPRESSOR_ONOFF:
-            *val = props->Compressor.OnOff;
-            break;
-
-        default:
-            alSetError(context, AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
-                       param);
-    }
-}
-void ALcompressor_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
-{ ALcompressor_getParami(effect, context, param, vals); }
-void ALcompressor_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat *UNUSED(val))
-{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param); }
-void ALcompressor_getParamfv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat *UNUSED(vals))
-{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", param); }
-
-DEFINE_ALEFFECT_VTABLE(ALcompressor);
diff --git a/Alc/effects/compressor.cpp b/Alc/effects/compressor.cpp
new file mode 100644
index 00000000..0a307a5f
--- /dev/null
+++ b/Alc/effects/compressor.cpp
@@ -0,0 +1,247 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2013 by Anis A. Hireche
+ * 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 <stdlib.h>
+
+#include "config.h"
+#include "alError.h"
+#include "alMain.h"
+#include "alAuxEffectSlot.h"
+#include "alu.h"
+
+
+#define AMP_ENVELOPE_MIN  0.5f
+#define AMP_ENVELOPE_MAX  2.0f
+
+#define ATTACK_TIME  0.1f /* 100ms to rise from min to max */
+#define RELEASE_TIME 0.2f /* 200ms to drop from max to min */
+
+
+struct ALcompressorState final : public ALeffectState {
+    /* Effect gains for each channel */
+    ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS];
+
+    /* Effect parameters */
+    ALboolean Enabled;
+    ALfloat AttackMult;
+    ALfloat ReleaseMult;
+    ALfloat EnvFollower;
+};
+
+static ALvoid ALcompressorState_Destruct(ALcompressorState *state);
+static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device);
+static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
+static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
+DECLARE_DEFAULT_ALLOCATORS(ALcompressorState)
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALcompressorState);
+
+
+static void ALcompressorState_Construct(ALcompressorState *state)
+{
+    new (state) ALcompressorState{};
+    ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
+    SET_VTABLE2(ALcompressorState, ALeffectState, state);
+
+    state->Enabled = AL_TRUE;
+    state->AttackMult = 1.0f;
+    state->ReleaseMult = 1.0f;
+    state->EnvFollower = 1.0f;
+}
+
+static ALvoid ALcompressorState_Destruct(ALcompressorState *state)
+{
+    ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
+    state->~ALcompressorState();
+}
+
+static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device)
+{
+    /* Number of samples to do a full attack and release (non-integer sample
+     * counts are okay).
+     */
+    const ALfloat attackCount  = (ALfloat)device->Frequency * ATTACK_TIME;
+    const ALfloat releaseCount = (ALfloat)device->Frequency * RELEASE_TIME;
+
+    /* Calculate per-sample multipliers to attack and release at the desired
+     * rates.
+     */
+    state->AttackMult  = powf(AMP_ENVELOPE_MAX/AMP_ENVELOPE_MIN, 1.0f/attackCount);
+    state->ReleaseMult = powf(AMP_ENVELOPE_MIN/AMP_ENVELOPE_MAX, 1.0f/releaseCount);
+
+    return AL_TRUE;
+}
+
+static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
+{
+    const ALCdevice *device = context->Device;
+    ALuint i;
+
+    state->Enabled = props->Compressor.OnOff;
+
+    STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
+    STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
+    for(i = 0;i < 4;i++)
+        ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain, state->Gain[i]);
+}
+
+static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
+{
+    ALsizei i, j, k;
+    ALsizei base;
+
+    for(base = 0;base < SamplesToDo;)
+    {
+        ALfloat gains[256];
+        ALsizei td = mini(256, SamplesToDo-base);
+        ALfloat env = state->EnvFollower;
+
+        /* Generate the per-sample gains from the signal envelope. */
+        if(state->Enabled)
+        {
+            for(i = 0;i < td;++i)
+            {
+                /* Clamp the absolute amplitude to the defined envelope limits,
+                 * then attack or release the envelope to reach it.
+                 */
+                ALfloat amplitude = clampf(fabsf(SamplesIn[0][base+i]),
+                                           AMP_ENVELOPE_MIN, AMP_ENVELOPE_MAX);
+                if(amplitude > env)
+                    env = minf(env*state->AttackMult, amplitude);
+                else if(amplitude < env)
+                    env = maxf(env*state->ReleaseMult, amplitude);
+
+                /* Apply the reciprocal of the envelope to normalize the volume
+                 * (compress the dynamic range).
+                 */
+                gains[i] = 1.0f / env;
+            }
+        }
+        else
+        {
+            /* Same as above, except the amplitude is forced to 1. This helps
+             * ensure smooth gain changes when the compressor is turned on and
+             * off.
+             */
+            for(i = 0;i < td;++i)
+            {
+                ALfloat amplitude = 1.0f;
+                if(amplitude > env)
+                    env = minf(env*state->AttackMult, amplitude);
+                else if(amplitude < env)
+                    env = maxf(env*state->ReleaseMult, amplitude);
+
+                gains[i] = 1.0f / env;
+            }
+        }
+        state->EnvFollower = env;
+
+        /* Now compress the signal amplitude to output. */
+        for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
+        {
+            for(k = 0;k < NumChannels;k++)
+            {
+                ALfloat gain = state->Gain[j][k];
+                if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
+                    continue;
+
+                for(i = 0;i < td;i++)
+                    SamplesOut[k][base+i] += SamplesIn[j][base+i] * gains[i] * gain;
+            }
+        }
+
+        base += td;
+    }
+}
+
+
+struct CompressorStateFactory final : public EffectStateFactory {
+    CompressorStateFactory() noexcept;
+};
+
+static ALeffectState *CompressorStateFactory_create(CompressorStateFactory *UNUSED(factory))
+{
+    ALcompressorState *state;
+
+    NEW_OBJ0(state, ALcompressorState)();
+    if(!state) return NULL;
+
+    return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_EFFECTSTATEFACTORY_VTABLE(CompressorStateFactory);
+
+CompressorStateFactory::CompressorStateFactory() noexcept
+  : EffectStateFactory{GET_VTABLE2(CompressorStateFactory, EffectStateFactory)}
+{
+}
+
+EffectStateFactory *CompressorStateFactory_getFactory(void)
+{
+    static CompressorStateFactory CompressorFactory{};
+    return STATIC_CAST(EffectStateFactory, &CompressorFactory);
+}
+
+
+void ALcompressor_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{
+    ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_COMPRESSOR_ONOFF:
+            if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF))
+                SETERR_RETURN(context, AL_INVALID_VALUE,, "Compressor state out of range");
+            props->Compressor.OnOff = val;
+            break;
+
+        default:
+            alSetError(context, AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
+                       param);
+    }
+}
+void ALcompressor_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{ ALcompressor_setParami(effect, context, param, vals[0]); }
+void ALcompressor_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param); }
+void ALcompressor_setParamfv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat *UNUSED(vals))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", param); }
+
+void ALcompressor_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{ 
+    const ALeffectProps *props = &effect->Props;
+    switch(param)
+    {
+        case AL_COMPRESSOR_ONOFF:
+            *val = props->Compressor.OnOff;
+            break;
+
+        default:
+            alSetError(context, AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
+                       param);
+    }
+}
+void ALcompressor_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{ ALcompressor_getParami(effect, context, param, vals); }
+void ALcompressor_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat *UNUSED(val))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param); }
+void ALcompressor_getParamfv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat *UNUSED(vals))
+{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", param); }
+
+DEFINE_ALEFFECT_VTABLE(ALcompressor);
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 79d28e23..64a40d95 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -808,9 +808,9 @@ SET(ALC_OBJS
     Alc/mastering.h
     Alc/ringbuffer.c
     Alc/ringbuffer.h
-    Alc/effects/autowah.c
-    Alc/effects/chorus.c
-    Alc/effects/compressor.c
+    Alc/effects/autowah.cpp
+    Alc/effects/chorus.cpp
+    Alc/effects/compressor.cpp
     Alc/effects/dedicated.cpp
     Alc/effects/distortion.cpp
     Alc/effects/echo.cpp
-- 
cgit v1.2.3