summaryrefslogtreecommitdiffstats
path: root/Alc/effects
diff options
context:
space:
mode:
Diffstat (limited to 'Alc/effects')
-rw-r--r--Alc/effects/chorus.c411
-rw-r--r--Alc/effects/dedicated.c183
-rw-r--r--Alc/effects/distortion.c402
-rw-r--r--Alc/effects/echo.c324
-rw-r--r--Alc/effects/equalizer.c499
-rw-r--r--Alc/effects/flanger.c411
-rw-r--r--Alc/effects/modulator.c343
7 files changed, 2573 insertions, 0 deletions
diff --git a/Alc/effects/chorus.c b/Alc/effects/chorus.c
new file mode 100644
index 00000000..e5a20b5d
--- /dev/null
+++ b/Alc/effects/chorus.c
@@ -0,0 +1,411 @@
+/**
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, 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"
+
+
+typedef struct ALchorusStateFactory {
+ DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALchorusStateFactory;
+
+static ALchorusStateFactory ChorusFactory;
+
+
+typedef struct ALchorusState {
+ DERIVE_FROM_TYPE(ALeffectState);
+
+ ALfloat *SampleBufferLeft;
+ ALfloat *SampleBufferRight;
+ ALuint BufferLength;
+ ALint offset;
+ ALfloat lfo_coeff;
+ ALint lfo_disp;
+
+ /* Gains for left and right sides */
+ ALfloat Gain[2][MaxChannels];
+
+ /* effect parameters */
+ ALint waveform;
+ ALint delay;
+ ALfloat depth;
+ ALfloat feedback;
+} ALchorusState;
+
+static ALvoid ALchorusState_Destruct(ALchorusState *state)
+{
+ free(state->SampleBufferLeft);
+ state->SampleBufferLeft = NULL;
+
+ free(state->SampleBufferRight);
+ state->SampleBufferRight = NULL;
+}
+
+static ALboolean ALchorusState_DeviceUpdate(ALchorusState *state, ALCdevice *Device)
+{
+ ALuint maxlen;
+ ALuint it;
+
+ maxlen = fastf2u(AL_CHORUS_MAX_DELAY * 3.0f * Device->Frequency) + 1;
+ maxlen = NextPowerOf2(maxlen);
+
+ if(maxlen != state->BufferLength)
+ {
+ void *temp;
+
+ temp = realloc(state->SampleBufferLeft, maxlen * sizeof(ALfloat));
+ if(!temp) return AL_FALSE;
+ state->SampleBufferLeft = temp;
+
+ temp = realloc(state->SampleBufferRight, maxlen * sizeof(ALfloat));
+ if(!temp) return AL_FALSE;
+ state->SampleBufferRight = temp;
+
+ state->BufferLength = maxlen;
+ }
+
+ for(it = 0;it < state->BufferLength;it++)
+ {
+ state->SampleBufferLeft[it] = 0.0f;
+ state->SampleBufferRight[it] = 0.0f;
+ }
+
+ return AL_TRUE;
+}
+
+static ALvoid ALchorusState_Update(ALchorusState *state, ALCdevice *Device, const ALeffectslot *Slot)
+{
+ ALfloat frequency = (ALfloat)Device->Frequency;
+ ALfloat rate;
+ ALint phase;
+ ALuint it;
+
+ for (it = 0; it < MaxChannels; it++)
+ {
+ state->Gain[0][it] = 0.0f;
+ state->Gain[1][it] = 0.0f;
+ }
+
+ state->waveform = Slot->effect.Chorus.Waveform;
+ state->depth = Slot->effect.Chorus.Depth;
+ state->feedback = Slot->effect.Chorus.Feedback;
+ state->delay = fastf2i(Slot->effect.Chorus.Delay * frequency);
+
+ /* Gains for left and right sides */
+ ComputeAngleGains(Device, atan2f(-1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[0]);
+ ComputeAngleGains(Device, atan2f(+1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[1]);
+
+ phase = Slot->effect.Chorus.Phase;
+ rate = Slot->effect.Chorus.Rate;
+
+ /* Calculate LFO coefficient */
+ switch (state->waveform)
+ {
+ case AL_CHORUS_WAVEFORM_TRIANGLE:
+ if(rate == 0.0f)
+ state->lfo_coeff = 0.0f;
+ else
+ state->lfo_coeff = 1.0f / (frequency / rate);
+ break;
+ case AL_CHORUS_WAVEFORM_SINUSOID:
+ if(rate == 0.0f)
+ state->lfo_coeff = 0.0f;
+ else
+ state->lfo_coeff = F_PI*2.0f / (frequency / rate);
+ break;
+ }
+
+ /* Calculate lfo phase displacement */
+ if(phase == 0 || rate == 0.0f)
+ state->lfo_disp = 0;
+ else
+ state->lfo_disp = fastf2i(frequency / rate / (360.0f/phase));
+}
+
+static __inline void Triangle(ALint *delay_left, ALint *delay_right, ALint offset, const ALchorusState *state)
+{
+ ALfloat lfo_value;
+
+ lfo_value = 2.0f - fabsf(2.0f - fmodf(state->lfo_coeff*offset*4.0f, 4.0f));
+ lfo_value *= state->depth * state->delay;
+ *delay_left = fastf2i(lfo_value) + state->delay;
+
+ lfo_value = 2.0f - fabsf(2.0f - fmodf(state->lfo_coeff *
+ (offset+state->lfo_disp)*4.0f,
+ 4.0f));
+ lfo_value *= state->depth * state->delay;
+ *delay_right = fastf2i(lfo_value) + state->delay;
+}
+
+static __inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALint offset, const ALchorusState *state)
+{
+ ALfloat lfo_value;
+
+ lfo_value = 1.0f + sinf(fmodf(state->lfo_coeff*offset, 2.0f*F_PI));
+ lfo_value *= state->depth * state->delay;
+ *delay_left = fastf2i(lfo_value) + state->delay;
+
+ lfo_value = 1.0f + sinf(fmodf(state->lfo_coeff*(offset+state->lfo_disp),
+ 2.0f*F_PI));
+ lfo_value *= state->depth * state->delay;
+ *delay_right = fastf2i(lfo_value) + state->delay;
+}
+
+#define DECL_TEMPLATE(func) \
+static void Process##func(ALchorusState *state, ALuint SamplesToDo, \
+ const ALfloat *restrict SamplesIn, \
+ ALfloat (*restrict SamplesOut)[BUFFERSIZE]) \
+{ \
+ const ALint mask = state->BufferLength-1; \
+ ALint offset = state->offset; \
+ ALuint it, kt; \
+ ALuint base; \
+ \
+ for(base = 0;base < SamplesToDo;) \
+ { \
+ ALfloat temps[64][2]; \
+ ALuint td = minu(SamplesToDo-base, 64); \
+ \
+ for(it = 0;it < td;it++,offset++) \
+ { \
+ ALint delay_left, delay_right; \
+ (func)(&delay_left, &delay_right, offset, state); \
+ \
+ temps[it][0] = state->SampleBufferLeft[(offset-delay_left)&mask]; \
+ state->SampleBufferLeft[offset&mask] = (temps[it][0] + \
+ SamplesIn[it+base]) * \
+ state->feedback; \
+ \
+ temps[it][1] = state->SampleBufferRight[(offset-delay_right)&mask];\
+ state->SampleBufferRight[offset&mask] = (temps[it][1] + \
+ SamplesIn[it+base]) * \
+ state->feedback; \
+ } \
+ \
+ for(kt = 0;kt < MaxChannels;kt++) \
+ { \
+ ALfloat gain = state->Gain[0][kt]; \
+ if(gain > 0.00001f) \
+ { \
+ for(it = 0;it < td;it++) \
+ SamplesOut[kt][it+base] += temps[it][0] * gain; \
+ } \
+ \
+ gain = state->Gain[1][kt]; \
+ if(gain > 0.00001f) \
+ { \
+ for(it = 0;it < td;it++) \
+ SamplesOut[kt][it+base] += temps[it][1] * gain; \
+ } \
+ } \
+ \
+ base += td; \
+ } \
+ \
+ state->offset = offset; \
+}
+
+DECL_TEMPLATE(Triangle)
+DECL_TEMPLATE(Sinusoid)
+
+#undef DECL_TEMPLATE
+
+static ALvoid ALchorusState_Process(ALchorusState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
+{
+ if(state->waveform == AL_CHORUS_WAVEFORM_TRIANGLE)
+ ProcessTriangle(state, SamplesToDo, SamplesIn, SamplesOut);
+ else if(state->waveform == AL_CHORUS_WAVEFORM_SINUSOID)
+ ProcessSinusoid(state, SamplesToDo, SamplesIn, SamplesOut);
+}
+
+static ALeffectStateFactory *ALchorusState_getCreator(void)
+{
+ return STATIC_CAST(ALeffectStateFactory, &ChorusFactory);
+}
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALchorusState);
+
+
+static ALeffectState *ALchorusStateFactory_create(void)
+{
+ ALchorusState *state;
+
+ state = malloc(sizeof(*state));
+ if(!state) return NULL;
+ SET_VTABLE2(ALchorusState, ALeffectState, state);
+
+ state->BufferLength = 0;
+ state->SampleBufferLeft = NULL;
+ state->SampleBufferRight = NULL;
+ state->offset = 0;
+
+ return STATIC_CAST(ALeffectState, state);
+}
+
+static ALvoid ALchorusStateFactory_destroy(ALeffectState *effect)
+{
+ ALchorusState *state = STATIC_UPCAST(ALchorusState, ALeffectState, effect);
+ ALchorusState_Destruct(state);
+ free(state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALchorusStateFactory);
+
+
+static void init_chorus_factory(void)
+{
+ SET_VTABLE2(ALchorusStateFactory, ALeffectStateFactory, &ChorusFactory);
+}
+
+ALeffectStateFactory *ALchorusStateFactory_getFactory(void)
+{
+ static pthread_once_t once = PTHREAD_ONCE_INIT;
+ pthread_once(&once, init_chorus_factory);
+ return STATIC_CAST(ALeffectStateFactory, &ChorusFactory);
+}
+
+
+void chorus_SetParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{
+ switch(param)
+ {
+ case AL_CHORUS_WAVEFORM:
+ if(val >= AL_CHORUS_MIN_WAVEFORM && val <= AL_CHORUS_MAX_WAVEFORM)
+ effect->Chorus.Waveform = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ case AL_CHORUS_PHASE:
+ if(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_PHASE)
+ effect->Chorus.Phase = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM);
+ break;
+ }
+}
+void chorus_SetParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+ chorus_SetParami(effect, context, param, vals[0]);
+}
+void chorus_SetParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+ switch(param)
+ {
+ case AL_CHORUS_RATE:
+ if(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE)
+ effect->Chorus.Rate = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ case AL_CHORUS_DEPTH:
+ if(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_DEPTH)
+ effect->Chorus.Depth = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ case AL_CHORUS_FEEDBACK:
+ if(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_FEEDBACK)
+ effect->Chorus.Feedback = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ case AL_CHORUS_DELAY:
+ if(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_DELAY)
+ effect->Chorus.Delay = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM);
+ break;
+ }
+}
+void chorus_SetParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+ chorus_SetParamf(effect, context, param, vals[0]);
+}
+
+void chorus_GetParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{
+ switch(param)
+ {
+ case AL_CHORUS_WAVEFORM:
+ *val = effect->Chorus.Waveform;
+ break;
+
+ case AL_CHORUS_PHASE:
+ *val = effect->Chorus.Phase;
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM);
+ break;
+ }
+}
+void chorus_GetParamiv(ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+ chorus_GetParami(effect, context, param, vals);
+}
+void chorus_GetParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+ switch(param)
+ {
+ case AL_CHORUS_RATE:
+ *val = effect->Chorus.Rate;
+ break;
+
+ case AL_CHORUS_DEPTH:
+ *val = effect->Chorus.Depth;
+ break;
+
+ case AL_CHORUS_FEEDBACK:
+ *val = effect->Chorus.Feedback;
+ break;
+
+ case AL_CHORUS_DELAY:
+ *val = effect->Chorus.Delay;
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM);
+ break;
+ }
+}
+void chorus_GetParamfv(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+ chorus_GetParamf(effect, context, param, vals);
+}
diff --git a/Alc/effects/dedicated.c b/Alc/effects/dedicated.c
new file mode 100644
index 00000000..bd266b0e
--- /dev/null
+++ b/Alc/effects/dedicated.c
@@ -0,0 +1,183 @@
+/**
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include "alMain.h"
+#include "alFilter.h"
+#include "alAuxEffectSlot.h"
+#include "alError.h"
+#include "alu.h"
+
+
+typedef struct ALdedicatedStateFactory {
+ DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALdedicatedStateFactory;
+
+static ALdedicatedStateFactory DedicatedFactory;
+
+
+typedef struct ALdedicatedState {
+ DERIVE_FROM_TYPE(ALeffectState);
+
+ ALfloat gains[MaxChannels];
+} ALdedicatedState;
+
+
+static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state)
+{
+ (void)state;
+}
+
+static ALboolean ALdedicatedState_DeviceUpdate(ALdedicatedState *state, ALCdevice *Device)
+{
+ return AL_TRUE;
+ (void)state;
+ (void)Device;
+}
+
+static ALvoid ALdedicatedState_Update(ALdedicatedState *state, ALCdevice *device, const ALeffectslot *Slot)
+{
+ ALfloat Gain;
+ ALsizei s;
+
+ Gain = Slot->Gain * Slot->effect.Dedicated.Gain;
+ for(s = 0;s < MaxChannels;s++)
+ state->gains[s] = 0.0f;
+
+ if(Slot->effect.type == AL_EFFECT_DEDICATED_DIALOGUE)
+ ComputeAngleGains(device, atan2f(0.0f, 1.0f), 0.0f, Gain, state->gains);
+ else if(Slot->effect.type == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
+ state->gains[LFE] = Gain;
+}
+
+static ALvoid ALdedicatedState_Process(ALdedicatedState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
+{
+ const ALfloat *gains = state->gains;
+ ALuint i, c;
+
+ for(c = 0;c < MaxChannels;c++)
+ {
+ if(!(gains[c] > 0.00001f))
+ continue;
+
+ for(i = 0;i < SamplesToDo;i++)
+ SamplesOut[c][i] = SamplesIn[i] * gains[c];
+ }
+}
+
+static ALeffectStateFactory *ALdedicatedState_getCreator(void)
+{
+ return STATIC_CAST(ALeffectStateFactory, &DedicatedFactory);
+}
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALdedicatedState);
+
+
+ALeffectState *ALdedicatedStateFactory_create(void)
+{
+ ALdedicatedState *state;
+ ALsizei s;
+
+ state = malloc(sizeof(*state));
+ if(!state) return NULL;
+ SET_VTABLE2(ALdedicatedState, ALeffectState, state);
+
+ for(s = 0;s < MaxChannels;s++)
+ state->gains[s] = 0.0f;
+
+ return STATIC_CAST(ALeffectState, state);
+}
+
+static ALvoid ALdedicatedStateFactory_destroy(ALeffectState *effect)
+{
+ ALdedicatedState *state = STATIC_UPCAST(ALdedicatedState, ALeffectState, effect);
+ ALdedicatedState_Destruct(state);
+ free(state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALdedicatedStateFactory);
+
+
+static void init_dedicated_factory(void)
+{
+ SET_VTABLE2(ALdedicatedStateFactory, ALeffectStateFactory, &DedicatedFactory);
+}
+
+ALeffectStateFactory *ALdedicatedStateFactory_getFactory(void)
+{
+ static pthread_once_t once = PTHREAD_ONCE_INIT;
+ pthread_once(&once, init_dedicated_factory);
+ return STATIC_CAST(ALeffectStateFactory, &DedicatedFactory);
+}
+
+
+void ded_SetParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{ (void)effect;(void)param;(void)val; alSetError(context, AL_INVALID_ENUM); }
+void ded_SetParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+ ded_SetParami(effect, context, param, vals[0]);
+}
+void ded_SetParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+ switch(param)
+ {
+ case AL_DEDICATED_GAIN:
+ if(val >= 0.0f && isfinite(val))
+ effect->Dedicated.Gain = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM);
+ break;
+ }
+}
+void ded_SetParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+ ded_SetParamf(effect, context, param, vals[0]);
+}
+
+void ded_GetParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{ (void)effect;(void)param;(void)val; alSetError(context, AL_INVALID_ENUM); }
+void ded_GetParamiv(ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+ ded_GetParami(effect, context, param, vals);
+}
+void ded_GetParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+ switch(param)
+ {
+ case AL_DEDICATED_GAIN:
+ *val = effect->Dedicated.Gain;
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM);
+ break;
+ }
+}
+void ded_GetParamfv(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+ ded_GetParamf(effect, context, param, vals);
+}
diff --git a/Alc/effects/distortion.c b/Alc/effects/distortion.c
new file mode 100644
index 00000000..7828377c
--- /dev/null
+++ b/Alc/effects/distortion.c
@@ -0,0 +1,402 @@
+/**
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, 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"
+
+
+typedef struct ALdistortionStateFactory {
+ DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALdistortionStateFactory;
+
+static ALdistortionStateFactory DistortionFactory;
+
+
+/* Filters 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 enum ALEQFilterType {
+ LOWPASS,
+ BANDPASS,
+} ALEQFilterType;
+
+typedef struct ALEQFilter {
+ ALEQFilterType type;
+ ALfloat x[2]; /* History of two last input samples */
+ ALfloat y[2]; /* History of two last output samples */
+ ALfloat a[3]; /* Transfer function coefficients "a" */
+ ALfloat b[3]; /* Transfer function coefficients "b" */
+} ALEQFilter;
+
+typedef struct ALdistortionState {
+ DERIVE_FROM_TYPE(ALeffectState);
+
+ /* Effect gains for each channel */
+ ALfloat Gain[MaxChannels];
+
+ /* Effect parameters */
+ ALEQFilter bandpass;
+ ALEQFilter lowpass;
+ ALfloat attenuation;
+ ALfloat edge_coeff;
+} ALdistortionState;
+
+static ALvoid ALdistortionState_Destruct(ALdistortionState *state)
+{
+ (void)state;
+}
+
+static ALboolean ALdistortionState_DeviceUpdate(ALdistortionState *state, ALCdevice *device)
+{
+ return AL_TRUE;
+ (void)state;
+ (void)device;
+}
+
+static ALvoid ALdistortionState_Update(ALdistortionState *state, ALCdevice *Device, const ALeffectslot *Slot)
+{
+ ALfloat gain = sqrtf(1.0f / Device->NumChan) * Slot->Gain;
+ ALfloat frequency = (ALfloat)Device->Frequency;
+ ALuint it;
+ ALfloat w0;
+ ALfloat alpha;
+ ALfloat bandwidth;
+ ALfloat cutoff;
+ ALfloat edge;
+
+ for(it = 0;it < MaxChannels;it++)
+ state->Gain[it] = 0.0f;
+ for(it = 0;it < Device->NumChan;it++)
+ {
+ enum Channel chan = Device->Speaker2Chan[it];
+ state->Gain[chan] = gain;
+ }
+
+ /* Store distorted signal attenuation settings */
+ state->attenuation = Slot->effect.Distortion.Gain;
+
+ /* Store waveshaper edge settings */
+ edge = sinf(Slot->effect.Distortion.Edge * (F_PI/2.0f));
+ state->edge_coeff = 2.0f * edge / (1.0f-edge);
+
+ /* Lowpass filter */
+ cutoff = Slot->effect.Distortion.LowpassCutoff;
+ /* Bandwidth value is constant in octaves */
+ bandwidth = (cutoff / 2.0f) / (cutoff * 0.67f);
+ w0 = 2.0f*F_PI * cutoff / (frequency*4.0f);
+ alpha = sinf(w0) * sinhf(logf(2.0f) / 2.0f * bandwidth * w0 / sinf(w0));
+ state->lowpass.b[0] = (1.0f - cosf(w0)) / 2.0f;
+ state->lowpass.b[1] = 1.0f - cosf(w0);
+ state->lowpass.b[2] = (1.0f - cosf(w0)) / 2.0f;
+ state->lowpass.a[0] = 1.0f + alpha;
+ state->lowpass.a[1] = -2.0f * cosf(w0);
+ state->lowpass.a[2] = 1.0f - alpha;
+
+ /* Bandpass filter */
+ cutoff = Slot->effect.Distortion.EQCenter;
+ /* Convert bandwidth in Hz to octaves */
+ bandwidth = Slot->effect.Distortion.EQBandwidth / (cutoff * 0.67f);
+ w0 = 2.0f*F_PI * cutoff / (frequency*4.0f);
+ alpha = sinf(w0) * sinhf(logf(2.0f) / 2.0f * bandwidth * w0 / sinf(w0));
+ state->bandpass.b[0] = alpha;
+ state->bandpass.b[1] = 0;
+ state->bandpass.b[2] = -alpha;
+ state->bandpass.a[0] = 1.0f + alpha;
+ state->bandpass.a[1] = -2.0f * cosf(w0);
+ state->bandpass.a[2] = 1.0f - alpha;
+}
+
+static ALvoid ALdistortionState_Process(ALdistortionState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
+{
+ const ALfloat fc = state->edge_coeff;
+ float oversample_buffer[64][4];
+ ALfloat tempsmp;
+ ALuint base;
+ ALuint it;
+ ALuint ot;
+ ALuint kt;
+
+ for(base = 0;base < SamplesToDo;)
+ {
+ ALfloat temps[64];
+ ALuint td = minu(SamplesToDo-base, 64);
+
+ /* 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. */
+
+ /* Fill oversample buffer using zero stuffing */
+ for(it = 0;it < td;it++)
+ {
+ oversample_buffer[it][0] = SamplesIn[it+base];
+ oversample_buffer[it][1] = 0.0f;
+ oversample_buffer[it][2] = 0.0f;
+ oversample_buffer[it][3] = 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. */
+ for(it = 0;it < td;it++)
+ {
+ for(ot = 0;ot < 4;ot++)
+ {
+ tempsmp = state->lowpass.b[0] / state->lowpass.a[0] * oversample_buffer[it][ot] +
+ state->lowpass.b[1] / state->lowpass.a[0] * state->lowpass.x[0] +
+ state->lowpass.b[2] / state->lowpass.a[0] * state->lowpass.x[1] -
+ state->lowpass.a[1] / state->lowpass.a[0] * state->lowpass.y[0] -
+ state->lowpass.a[2] / state->lowpass.a[0] * state->lowpass.y[1];
+
+ state->lowpass.x[1] = state->lowpass.x[0];
+ state->lowpass.x[0] = oversample_buffer[it][ot];
+ state->lowpass.y[1] = state->lowpass.y[0];
+ state->lowpass.y[0] = tempsmp;
+ /* Restore signal power by multiplying sample by amount of oversampling */
+ oversample_buffer[it][ot] = tempsmp * 4.0f;
+ }
+ }
+
+ for(it = 0;it < td;it++)
+ {
+ /* 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(ot = 0;ot < 4;ot++)
+ {
+ ALfloat smp = oversample_buffer[it][ot];
+
+ 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));
+
+ /* Third step, do bandpass filtering of distorted signal */
+ tempsmp = state->bandpass.b[0] / state->bandpass.a[0] * smp +
+ state->bandpass.b[1] / state->bandpass.a[0] * state->bandpass.x[0] +
+ state->bandpass.b[2] / state->bandpass.a[0] * state->bandpass.x[1] -
+ state->bandpass.a[1] / state->bandpass.a[0] * state->bandpass.y[0] -
+ state->bandpass.a[2] / state->bandpass.a[0] * state->bandpass.y[1];
+
+ state->bandpass.x[1] = state->bandpass.x[0];
+ state->bandpass.x[0] = smp;
+ state->bandpass.y[1] = state->bandpass.y[0];
+ state->bandpass.y[0] = tempsmp;
+
+ oversample_buffer[it][ot] = tempsmp;
+ }
+
+ /* Fourth step, final, do attenuation and perform decimation, */
+ /* store only one sample out of 4. */
+ temps[it] = oversample_buffer[it][0] * state->attenuation;
+ }
+
+ for(kt = 0;kt < MaxChannels;kt++)
+ {
+ ALfloat gain = state->Gain[kt];
+ if(!(gain > 0.00001f))
+ continue;
+
+ for(it = 0;it < td;it++)
+ SamplesOut[kt][base+it] += gain * temps[it];
+ }
+
+ base += td;
+ }
+}
+
+static ALeffectStateFactory *ALdistortionState_getCreator(void)
+{
+ return STATIC_CAST(ALeffectStateFactory, &DistortionFactory);
+}
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState);
+
+
+static ALeffectState *ALdistortionStateFactory_create(void)
+{
+ ALdistortionState *state;
+
+ state = malloc(sizeof(*state));
+ if(!state) return NULL;
+ SET_VTABLE2(ALdistortionState, ALeffectState, state);
+
+ state->bandpass.type = BANDPASS;
+ state->lowpass.type = LOWPASS;
+
+ /* Initialize sample history only on filter creation to avoid */
+ /* sound clicks if filter settings were changed in runtime. */
+ state->bandpass.x[0] = 0.0f;
+ state->bandpass.x[1] = 0.0f;
+ state->lowpass.y[0] = 0.0f;
+ state->lowpass.y[1] = 0.0f;
+
+ return STATIC_CAST(ALeffectState, state);
+}
+
+static ALvoid ALdistortionStateFactory_destroy(ALeffectState *effect)
+{
+ ALdistortionState *state = STATIC_UPCAST(ALdistortionState, ALeffectState, effect);
+ ALdistortionState_Destruct(state);
+ free(state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALdistortionStateFactory);
+
+
+static void init_distortion_factory(void)
+{
+ SET_VTABLE2(ALdistortionStateFactory, ALeffectStateFactory, &DistortionFactory);
+}
+
+ALeffectStateFactory *ALdistortionStateFactory_getFactory(void)
+{
+ static pthread_once_t once = PTHREAD_ONCE_INIT;
+ pthread_once(&once, init_distortion_factory);
+ return STATIC_CAST(ALeffectStateFactory, &DistortionFactory);
+}
+
+
+void distortion_SetParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{
+ effect=effect;
+ val=val;
+
+ switch(param)
+ {
+ default:
+ alSetError(context, AL_INVALID_ENUM);
+ break;
+ }
+}
+void distortion_SetParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+ distortion_SetParami(effect, context, param, vals[0]);
+}
+void distortion_SetParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+ switch(param)
+ {
+ case AL_DISTORTION_EDGE:
+ if(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE)
+ effect->Distortion.Edge = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ case AL_DISTORTION_GAIN:
+ if(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN)
+ effect->Distortion.Gain = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ case AL_DISTORTION_LOWPASS_CUTOFF:
+ if(val >= AL_DISTORTION_MIN_LOWPASS_CUTOFF && val <= AL_DISTORTION_MAX_LOWPASS_CUTOFF)
+ effect->Distortion.LowpassCutoff = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ case AL_DISTORTION_EQCENTER:
+ if(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER)
+ effect->Distortion.EQCenter = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ case AL_DISTORTION_EQBANDWIDTH:
+ if(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH)
+ effect->Distortion.EQBandwidth = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM);
+ break;
+ }
+}
+void distortion_SetParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+ distortion_SetParamf(effect, context, param, vals[0]);
+}
+
+void distortion_GetParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{
+ effect=effect;
+ val=val;
+
+ switch(param)
+ {
+ default:
+ alSetError(context, AL_INVALID_ENUM);
+ break;
+ }
+}
+void distortion_GetParamiv(ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+ distortion_GetParami(effect, context, param, vals);
+}
+void distortion_GetParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+ switch(param)
+ {
+ case AL_DISTORTION_EDGE:
+ *val = effect->Distortion.Edge;
+ break;
+
+ case AL_DISTORTION_GAIN:
+ *val = effect->Distortion.Gain;
+ break;
+
+ case AL_DISTORTION_LOWPASS_CUTOFF:
+ *val = effect->Distortion.LowpassCutoff;
+ break;
+
+ case AL_DISTORTION_EQCENTER:
+ *val = effect->Distortion.EQCenter;
+ break;
+
+ case AL_DISTORTION_EQBANDWIDTH:
+ *val = effect->Distortion.EQBandwidth;
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM);
+ break;
+ }
+}
+void distortion_GetParamfv(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+ distortion_GetParamf(effect, context, param, vals);
+}
diff --git a/Alc/effects/echo.c b/Alc/effects/echo.c
new file mode 100644
index 00000000..d5915295
--- /dev/null
+++ b/Alc/effects/echo.c
@@ -0,0 +1,324 @@
+/**
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, 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"
+
+
+typedef struct ALechoStateFactory {
+ DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALechoStateFactory;
+
+static ALechoStateFactory EchoFactory;
+
+
+typedef struct ALechoState {
+ DERIVE_FROM_TYPE(ALeffectState);
+
+ ALfloat *SampleBuffer;
+ ALuint BufferLength;
+
+ // The echo is two tap. The delay is the number of samples from before the
+ // current offset
+ struct {
+ ALuint delay;
+ } Tap[2];
+ ALuint Offset;
+ /* The panning gains for the two taps */
+ ALfloat Gain[2][MaxChannels];
+
+ ALfloat FeedGain;
+
+ FILTER iirFilter;
+} ALechoState;
+
+static ALvoid ALechoState_Destruct(ALechoState *state)
+{
+ free(state->SampleBuffer);
+ state->SampleBuffer = NULL;
+}
+
+static ALboolean ALechoState_DeviceUpdate(ALechoState *state, ALCdevice *Device)
+{
+ ALuint maxlen, i;
+
+ // 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 = fastf2u(AL_ECHO_MAX_DELAY * Device->Frequency) + 1;
+ maxlen += fastf2u(AL_ECHO_MAX_LRDELAY * Device->Frequency) + 1;
+ maxlen = NextPowerOf2(maxlen);
+
+ if(maxlen != state->BufferLength)
+ {
+ void *temp;
+
+ temp = realloc(state->SampleBuffer, maxlen * sizeof(ALfloat));
+ if(!temp)
+ return AL_FALSE;
+ state->SampleBuffer = temp;
+ state->BufferLength = maxlen;
+ }
+ for(i = 0;i < state->BufferLength;i++)
+ state->SampleBuffer[i] = 0.0f;
+
+ return AL_TRUE;
+}
+
+static ALvoid ALechoState_Update(ALechoState *state, ALCdevice *Device, const ALeffectslot *Slot)
+{
+ ALuint frequency = Device->Frequency;
+ ALfloat lrpan, cw, g, gain;
+ ALfloat dirGain;
+ ALuint i;
+
+ state->Tap[0].delay = fastf2u(Slot->effect.Echo.Delay * frequency) + 1;
+ state->Tap[1].delay = fastf2u(Slot->effect.Echo.LRDelay * frequency);
+ state->Tap[1].delay += state->Tap[0].delay;
+
+ lrpan = Slot->effect.Echo.Spread;
+
+ state->FeedGain = Slot->effect.Echo.Feedback;
+
+ cw = cosf(F_PI*2.0f * LOWPASSFREQREF / frequency);
+ g = 1.0f - Slot->effect.Echo.Damping;
+ state->iirFilter.coeff = lpCoeffCalc(g, cw);
+
+ gain = Slot->Gain;
+ for(i = 0;i < MaxChannels;i++)
+ {
+ state->Gain[0][i] = 0.0f;
+ state->Gain[1][i] = 0.0f;
+ }
+
+ dirGain = fabsf(lrpan);
+
+ /* First tap panning */
+ ComputeAngleGains(Device, atan2f(-lrpan, 0.0f), (1.0f-dirGain)*F_PI, gain, state->Gain[0]);
+
+ /* Second tap panning */
+ ComputeAngleGains(Device, atan2f(+lrpan, 0.0f), (1.0f-dirGain)*F_PI, gain, state->Gain[1]);
+}
+
+static ALvoid ALechoState_Process(ALechoState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
+{
+ const ALuint mask = state->BufferLength-1;
+ const ALuint tap1 = state->Tap[0].delay;
+ const ALuint tap2 = state->Tap[1].delay;
+ ALuint offset = state->Offset;
+ ALfloat smp;
+ ALuint base;
+ ALuint i, k;
+
+ for(base = 0;base < SamplesToDo;)
+ {
+ ALfloat temps[64][2];
+ ALuint td = minu(SamplesToDo-base, 64);
+
+ for(i = 0;i < td;i++)
+ {
+ /* First tap */
+ temps[i][0] = state->SampleBuffer[(offset-tap1) & mask];
+ /* Second tap */
+ temps[i][1] = state->SampleBuffer[(offset-tap2) & mask];
+
+ // Apply damping and feedback gain to the second tap, and mix in the
+ // new sample
+ smp = lpFilter2P(&state->iirFilter, temps[i][1]+SamplesIn[i]);
+ state->SampleBuffer[offset&mask] = smp * state->FeedGain;
+ }
+
+ for(k = 0;k < MaxChannels;k++)
+ {
+ ALfloat gain = state->Gain[0][k];
+ if(gain > 0.00001f)
+ {
+ for(i = 0;i < td;i++)
+ SamplesOut[k][i+base] += temps[i][0] * gain;
+ }
+
+ gain = state->Gain[1][k];
+ if(gain > 0.00001f)
+ {
+ for(i = 0;i < td;i++)
+ SamplesOut[k][i+base] += temps[i][1] * gain;
+ }
+ }
+
+ base += td;
+ }
+
+ state->Offset = offset;
+}
+
+static ALeffectStateFactory *ALechoState_getCreator(void)
+{
+ return STATIC_CAST(ALeffectStateFactory, &EchoFactory);
+}
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALechoState);
+
+
+ALeffectState *ALechoStateFactory_create(void)
+{
+ ALechoState *state;
+
+ state = malloc(sizeof(*state));
+ if(!state) return NULL;
+ SET_VTABLE2(ALechoState, ALeffectState, state);
+
+ state->BufferLength = 0;
+ state->SampleBuffer = NULL;
+
+ state->Tap[0].delay = 0;
+ state->Tap[1].delay = 0;
+ state->Offset = 0;
+
+ state->iirFilter.coeff = 0.0f;
+ state->iirFilter.history[0] = 0.0f;
+ state->iirFilter.history[1] = 0.0f;
+
+ return STATIC_CAST(ALeffectState, state);
+}
+
+static ALvoid ALechoStateFactory_destroy(ALeffectState *effect)
+{
+ ALechoState *state = STATIC_UPCAST(ALechoState, ALeffectState, effect);
+ ALechoState_Destruct(state);
+ free(state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALechoStateFactory);
+
+
+static void init_echo_factory(void)
+{
+ SET_VTABLE2(ALechoStateFactory, ALeffectStateFactory, &EchoFactory);
+}
+
+ALeffectStateFactory *ALechoStateFactory_getFactory(void)
+{
+ static pthread_once_t once = PTHREAD_ONCE_INIT;
+ pthread_once(&once, init_echo_factory);
+ return STATIC_CAST(ALeffectStateFactory, &EchoFactory);
+}
+
+
+void echo_SetParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{ (void)effect;(void)param;(void)val; alSetError(context, AL_INVALID_ENUM); }
+void echo_SetParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+ echo_SetParami(effect, context, param, vals[0]);
+}
+void echo_SetParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+ switch(param)
+ {
+ case AL_ECHO_DELAY:
+ if(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY)
+ effect->Echo.Delay = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ case AL_ECHO_LRDELAY:
+ if(val >= AL_ECHO_MIN_LRDELAY && val <= AL_ECHO_MAX_LRDELAY)
+ effect->Echo.LRDelay = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ case AL_ECHO_DAMPING:
+ if(val >= AL_ECHO_MIN_DAMPING && val <= AL_ECHO_MAX_DAMPING)
+ effect->Echo.Damping = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ case AL_ECHO_FEEDBACK:
+ if(val >= AL_ECHO_MIN_FEEDBACK && val <= AL_ECHO_MAX_FEEDBACK)
+ effect->Echo.Feedback = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ case AL_ECHO_SPREAD:
+ if(val >= AL_ECHO_MIN_SPREAD && val <= AL_ECHO_MAX_SPREAD)
+ effect->Echo.Spread = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM);
+ break;
+ }
+}
+void echo_SetParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+ echo_SetParamf(effect, context, param, vals[0]);
+}
+
+void echo_GetParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{ (void)effect;(void)param;(void)val; alSetError(context, AL_INVALID_ENUM); }
+void echo_GetParamiv(ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+ echo_GetParami(effect, context, param, vals);
+}
+void echo_GetParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+ switch(param)
+ {
+ case AL_ECHO_DELAY:
+ *val = effect->Echo.Delay;
+ break;
+
+ case AL_ECHO_LRDELAY:
+ *val = effect->Echo.LRDelay;
+ break;
+
+ case AL_ECHO_DAMPING:
+ *val = effect->Echo.Damping;
+ break;
+
+ case AL_ECHO_FEEDBACK:
+ *val = effect->Echo.Feedback;
+ break;
+
+ case AL_ECHO_SPREAD:
+ *val = effect->Echo.Spread;
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM);
+ break;
+ }
+}
+void echo_GetParamfv(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+ echo_GetParamf(effect, context, param, vals);
+}
diff --git a/Alc/effects/equalizer.c b/Alc/effects/equalizer.c
new file mode 100644
index 00000000..d397a9ba
--- /dev/null
+++ b/Alc/effects/equalizer.c
@@ -0,0 +1,499 @@
+/**
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, 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"
+
+
+typedef struct ALequalizerStateFactory {
+ DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALequalizerStateFactory;
+
+static ALequalizerStateFactory EqualizerFactory;
+
+
+/* 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 enum ALEQFilterType {
+ LOW_SHELF,
+ HIGH_SHELF,
+ PEAKING
+} ALEQFilterType;
+
+typedef struct ALEQFilter {
+ ALEQFilterType type;
+ ALfloat x[2]; /* History of two last input samples */
+ ALfloat y[2]; /* History of two last output samples */
+ ALfloat a[3]; /* Transfer function coefficients "a" */
+ ALfloat b[3]; /* Transfer function coefficients "b" */
+} ALEQFilter;
+
+typedef struct ALequalizerState {
+ DERIVE_FROM_TYPE(ALeffectState);
+
+ /* Effect gains for each channel */
+ ALfloat Gain[MaxChannels];
+
+ /* Effect parameters */
+ ALEQFilter bandfilter[4];
+} ALequalizerState;
+
+static ALvoid ALequalizerState_Destruct(ALequalizerState *state)
+{
+ (void)state;
+}
+
+static ALboolean ALequalizerState_DeviceUpdate(ALequalizerState *state, ALCdevice *device)
+{
+ return AL_TRUE;
+ (void)state;
+ (void)device;
+}
+
+static ALvoid ALequalizerState_Update(ALequalizerState *state, ALCdevice *device, const ALeffectslot *slot)
+{
+ ALfloat frequency = (ALfloat)device->Frequency;
+ ALfloat gain = sqrtf(1.0f / device->NumChan) * slot->Gain;
+ ALuint it;
+
+ for(it = 0;it < MaxChannels;it++)
+ state->Gain[it] = 0.0f;
+ for(it = 0; it < device->NumChan; it++)
+ {
+ enum Channel chan = device->Speaker2Chan[it];
+ state->Gain[chan] = gain;
+ }
+
+ /* Calculate coefficients for the each type of filter */
+ for(it = 0; it < 4; it++)
+ {
+ ALfloat gain;
+ ALfloat filter_frequency;
+ ALfloat bandwidth = 0.0f;
+ ALfloat w0;
+ ALfloat alpha = 0.0f;
+
+ /* convert linear gains to filter gains */
+ switch (it)
+ {
+ case 0: /* Low Shelf */
+ gain = powf(10.0f, (20.0f * log10f(slot->effect.Equalizer.LowGain)) / 40.0f);
+ filter_frequency = slot->effect.Equalizer.LowCutoff;
+ break;
+ case 1: /* Peaking */
+ gain = powf(10.0f, (20.0f * log10f(slot->effect.Equalizer.Mid1Gain)) / 40.0f);
+ filter_frequency = slot->effect.Equalizer.Mid1Center;
+ bandwidth = slot->effect.Equalizer.Mid1Width;
+ break;
+ case 2: /* Peaking */
+ gain = powf(10.0f, (20.0f * log10f(slot->effect.Equalizer.Mid2Gain)) / 40.0f);
+ filter_frequency = slot->effect.Equalizer.Mid2Center;
+ bandwidth = slot->effect.Equalizer.Mid2Width;
+ break;
+ case 3: /* High Shelf */
+ gain = powf(10.0f, (20.0f * log10f(slot->effect.Equalizer.HighGain)) / 40.0f);
+ filter_frequency = slot->effect.Equalizer.HighCutoff;
+ break;
+ }
+
+ w0 = 2.0f*F_PI * filter_frequency / frequency;
+
+ /* Calculate filter coefficients depending on filter type */
+ switch(state->bandfilter[it].type)
+ {
+ case LOW_SHELF:
+ alpha = sinf(w0) / 2.0f * sqrtf((gain + 1.0f / gain) *
+ (1.0f / 0.75f - 1.0f) + 2.0f);
+ state->bandfilter[it].b[0] = gain * ((gain + 1.0f) -
+ (gain - 1.0f) * cosf(w0) +
+ 2.0f * sqrtf(gain) * alpha);
+ state->bandfilter[it].b[1] = 2.0f * gain * ((gain - 1.0f) -
+ (gain + 1.0f) * cosf(w0));
+ state->bandfilter[it].b[2] = gain * ((gain + 1.0f) -
+ (gain - 1.0f) * cosf(w0) -
+ 2.0f * sqrtf(gain) * alpha);
+ state->bandfilter[it].a[0] = (gain + 1.0f) +
+ (gain - 1.0f) * cosf(w0) +
+ 2.0f * sqrtf(gain) * alpha;
+ state->bandfilter[it].a[1] = -2.0f * ((gain - 1.0f) +
+ (gain + 1.0f) * cosf(w0));
+ state->bandfilter[it].a[2] = (gain + 1.0f) +
+ (gain - 1.0f) * cosf(w0) -
+ 2.0f * sqrtf(gain) * alpha;
+ break;
+ case HIGH_SHELF:
+ alpha = sinf(w0) / 2.0f * sqrtf((gain + 1.0f / gain) *
+ (1.0f / 0.75f - 1.0f) + 2.0f);
+ state->bandfilter[it].b[0] = gain * ((gain + 1.0f) +
+ (gain - 1.0f) * cosf(w0) +
+ 2.0f * sqrtf(gain) * alpha);
+ state->bandfilter[it].b[1] = -2.0f * gain * ((gain - 1.0f) +
+ (gain + 1.0f) *
+ cosf(w0));
+ state->bandfilter[it].b[2] = gain * ((gain + 1.0f) +
+ (gain - 1.0f) * cosf(w0) -
+ 2.0f * sqrtf(gain) * alpha);
+ state->bandfilter[it].a[0] = (gain + 1.0f) -
+ (gain - 1.0f) * cosf(w0) +
+ 2.0f * sqrtf(gain) * alpha;
+ state->bandfilter[it].a[1] = 2.0f * ((gain - 1.0f) -
+ (gain + 1.0f) * cosf(w0));
+ state->bandfilter[it].a[2] = (gain + 1.0f) -
+ (gain - 1.0f) * cosf(w0) -
+ 2.0f * sqrtf(gain) * alpha;
+ break;
+ case PEAKING:
+ alpha = sinf(w0) * sinhf(logf(2.0f) / 2.0f * bandwidth * w0 / sinf(w0));
+ state->bandfilter[it].b[0] = 1.0f + alpha * gain;
+ state->bandfilter[it].b[1] = -2.0f * cosf(w0);
+ state->bandfilter[it].b[2] = 1.0f - alpha * gain;
+ state->bandfilter[it].a[0] = 1.0f + alpha / gain;
+ state->bandfilter[it].a[1] = -2.0f * cosf(w0);
+ state->bandfilter[it].a[2] = 1.0f - alpha / gain;
+ break;
+ }
+ }
+}
+
+static ALvoid ALequalizerState_Process(ALequalizerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
+{
+ ALuint base;
+ ALuint it;
+ ALuint kt;
+ ALuint ft;
+
+ for(base = 0;base < SamplesToDo;)
+ {
+ ALfloat temps[64];
+ ALuint td = minu(SamplesToDo-base, 64);
+
+ for(it = 0;it < td;it++)
+ {
+ ALfloat smp = SamplesIn[base+it];
+ ALfloat tempsmp;
+
+ for(ft = 0;ft < 4;ft++)
+ {
+ ALEQFilter *filter = &state->bandfilter[ft];
+
+ tempsmp = filter->b[0] / filter->a[0] * smp +
+ filter->b[1] / filter->a[0] * filter->x[0] +
+ filter->b[2] / filter->a[0] * filter->x[1] -
+ filter->a[1] / filter->a[0] * filter->y[0] -
+ filter->a[2] / filter->a[0] * filter->y[1];
+
+ filter->x[1] = filter->x[0];
+ filter->x[0] = smp;
+ filter->y[1] = filter->y[0];
+ filter->y[0] = tempsmp;
+ smp = tempsmp;
+ }
+
+ temps[it] = smp;
+ }
+
+ for(kt = 0;kt < MaxChannels;kt++)
+ {
+ ALfloat gain = state->Gain[kt];
+ if(!(gain > 0.00001f))
+ continue;
+
+ for(it = 0;it < td;it++)
+ SamplesOut[kt][base+it] += gain * temps[it];
+ }
+
+ base += td;
+ }
+}
+
+static ALeffectStateFactory *ALequalizerState_getCreator(void)
+{
+ return STATIC_CAST(ALeffectStateFactory, &EqualizerFactory);
+}
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALequalizerState);
+
+
+ALeffectState *ALequalizerStateFactory_create(void)
+{
+ ALequalizerState *state;
+ int it;
+
+ state = malloc(sizeof(*state));
+ if(!state) return NULL;
+ SET_VTABLE2(ALequalizerState, ALeffectState, state);
+
+ state->bandfilter[0].type = LOW_SHELF;
+ state->bandfilter[1].type = PEAKING;
+ state->bandfilter[2].type = PEAKING;
+ state->bandfilter[3].type = HIGH_SHELF;
+
+ /* Initialize sample history only on filter creation to avoid */
+ /* sound clicks if filter settings were changed in runtime. */
+ for(it = 0; it < 4; it++)
+ {
+ state->bandfilter[it].x[0] = 0.0f;
+ state->bandfilter[it].x[1] = 0.0f;
+ state->bandfilter[it].y[0] = 0.0f;
+ state->bandfilter[it].y[1] = 0.0f;
+ }
+
+ return STATIC_CAST(ALeffectState, state);
+}
+
+static ALvoid ALequalizerStateFactory_destroy(ALeffectState *effect)
+{
+ ALequalizerState *state = STATIC_UPCAST(ALequalizerState, ALeffectState, effect);
+ ALequalizerState_Destruct(state);
+ free(state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALequalizerStateFactory);
+
+
+static void init_equalizer_factory(void)
+{
+ SET_VTABLE2(ALequalizerStateFactory, ALeffectStateFactory, &EqualizerFactory);
+}
+
+ALeffectStateFactory *ALequalizerStateFactory_getFactory(void)
+{
+ static pthread_once_t once = PTHREAD_ONCE_INIT;
+ pthread_once(&once, init_equalizer_factory);
+ return STATIC_CAST(ALeffectStateFactory, &EqualizerFactory);
+}
+
+
+void equalizer_SetParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{
+ effect=effect;
+ val=val;
+
+ switch(param)
+ {
+ default:
+ alSetError(context, AL_INVALID_ENUM);
+ break;
+ }
+}
+void equalizer_SetParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+ equalizer_SetParami(effect, context, param, vals[0]);
+}
+void equalizer_SetParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+ switch(param)
+ {
+ case AL_EQUALIZER_LOW_GAIN:
+ if(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN)
+ effect->Equalizer.LowGain = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ case AL_EQUALIZER_LOW_CUTOFF:
+ if(val >= AL_EQUALIZER_MIN_LOW_CUTOFF && val <= AL_EQUALIZER_MAX_LOW_CUTOFF)
+ effect->Equalizer.LowCutoff = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ case AL_EQUALIZER_MID1_GAIN:
+ if(val >= AL_EQUALIZER_MIN_MID1_GAIN && val <= AL_EQUALIZER_MAX_MID1_GAIN)
+ effect->Equalizer.Mid1Gain = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ case AL_EQUALIZER_MID1_CENTER:
+ if(val >= AL_EQUALIZER_MIN_MID1_CENTER && val <= AL_EQUALIZER_MAX_MID1_CENTER)
+ effect->Equalizer.Mid1Center = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ case AL_EQUALIZER_MID1_WIDTH:
+ if(val >= AL_EQUALIZER_MIN_MID1_WIDTH && val <= AL_EQUALIZER_MAX_MID1_WIDTH)
+ effect->Equalizer.Mid1Width = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ case AL_EQUALIZER_MID2_GAIN:
+ if(val >= AL_EQUALIZER_MIN_MID2_GAIN && val <= AL_EQUALIZER_MAX_MID2_GAIN)
+ effect->Equalizer.Mid2Gain = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ case AL_EQUALIZER_MID2_CENTER:
+ if(val >= AL_EQUALIZER_MIN_MID2_CENTER && val <= AL_EQUALIZER_MAX_MID2_CENTER)
+ effect->Equalizer.Mid2Center = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ case AL_EQUALIZER_MID2_WIDTH:
+ if(val >= AL_EQUALIZER_MIN_MID2_WIDTH && val <= AL_EQUALIZER_MAX_MID2_WIDTH)
+ effect->Equalizer.Mid2Width = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ case AL_EQUALIZER_HIGH_GAIN:
+ if(val >= AL_EQUALIZER_MIN_HIGH_GAIN && val <= AL_EQUALIZER_MAX_HIGH_GAIN)
+ effect->Equalizer.HighGain = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ case AL_EQUALIZER_HIGH_CUTOFF:
+ if(val >= AL_EQUALIZER_MIN_HIGH_CUTOFF && val <= AL_EQUALIZER_MAX_HIGH_CUTOFF)
+ effect->Equalizer.HighCutoff = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM);
+ break;
+ }
+}
+void equalizer_SetParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+ equalizer_SetParamf(effect, context, param, vals[0]);
+}
+
+void equalizer_GetParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{
+ effect=effect;
+ val=val;
+
+ switch(param)
+ {
+ default:
+ alSetError(context, AL_INVALID_ENUM);
+ break;
+ }
+}
+void equalizer_GetParamiv(ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+ equalizer_GetParami(effect, context, param, vals);
+}
+void equalizer_GetParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+ switch(param)
+ {
+ case AL_EQUALIZER_LOW_GAIN:
+ *val = effect->Equalizer.LowGain;
+ break;
+
+ case AL_EQUALIZER_LOW_CUTOFF:
+ *val = effect->Equalizer.LowCutoff;
+ break;
+
+ case AL_EQUALIZER_MID1_GAIN:
+ *val = effect->Equalizer.Mid1Gain;
+ break;
+
+ case AL_EQUALIZER_MID1_CENTER:
+ *val = effect->Equalizer.Mid1Center;
+ break;
+
+ case AL_EQUALIZER_MID1_WIDTH:
+ *val = effect->Equalizer.Mid1Width;
+ break;
+
+ case AL_EQUALIZER_MID2_GAIN:
+ *val = effect->Equalizer.Mid2Gain;
+ break;
+
+ case AL_EQUALIZER_MID2_CENTER:
+ *val = effect->Equalizer.Mid2Center;
+ break;
+
+ case AL_EQUALIZER_MID2_WIDTH:
+ *val = effect->Equalizer.Mid2Width;
+ break;
+
+ case AL_EQUALIZER_HIGH_GAIN:
+ *val = effect->Equalizer.HighGain;
+ break;
+
+ case AL_EQUALIZER_HIGH_CUTOFF:
+ *val = effect->Equalizer.HighCutoff;
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM);
+ break;
+ }
+}
+void equalizer_GetParamfv(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+ equalizer_GetParamf(effect, context, param, vals);
+}
diff --git a/Alc/effects/flanger.c b/Alc/effects/flanger.c
new file mode 100644
index 00000000..a0f94a46
--- /dev/null
+++ b/Alc/effects/flanger.c
@@ -0,0 +1,411 @@
+/**
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, 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"
+
+
+typedef struct ALflangerStateFactory {
+ DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALflangerStateFactory;
+
+static ALflangerStateFactory FlangerFactory;
+
+
+typedef struct ALflangerState {
+ DERIVE_FROM_TYPE(ALeffectState);
+
+ ALfloat *SampleBufferLeft;
+ ALfloat *SampleBufferRight;
+ ALuint BufferLength;
+ ALint offset;
+ ALfloat lfo_coeff;
+ ALint lfo_disp;
+
+ /* Gains for left and right sides */
+ ALfloat Gain[2][MaxChannels];
+
+ /* effect parameters */
+ ALint waveform;
+ ALint delay;
+ ALfloat depth;
+ ALfloat feedback;
+} ALflangerState;
+
+static ALvoid ALflangerState_Destruct(ALflangerState *state)
+{
+ free(state->SampleBufferLeft);
+ state->SampleBufferLeft = NULL;
+
+ free(state->SampleBufferRight);
+ state->SampleBufferRight = NULL;
+}
+
+static ALboolean ALflangerState_DeviceUpdate(ALflangerState *state, ALCdevice *Device)
+{
+ ALuint maxlen;
+ ALuint it;
+
+ maxlen = fastf2u(AL_FLANGER_MAX_DELAY * 3.0f * Device->Frequency) + 1;
+ maxlen = NextPowerOf2(maxlen);
+
+ if(maxlen != state->BufferLength)
+ {
+ void *temp;
+
+ temp = realloc(state->SampleBufferLeft, maxlen * sizeof(ALfloat));
+ if(!temp) return AL_FALSE;
+ state->SampleBufferLeft = temp;
+
+ temp = realloc(state->SampleBufferRight, maxlen * sizeof(ALfloat));
+ if(!temp) return AL_FALSE;
+ state->SampleBufferRight = temp;
+
+ state->BufferLength = maxlen;
+ }
+
+ for(it = 0;it < state->BufferLength;it++)
+ {
+ state->SampleBufferLeft[it] = 0.0f;
+ state->SampleBufferRight[it] = 0.0f;
+ }
+
+ return AL_TRUE;
+}
+
+static ALvoid ALflangerState_Update(ALflangerState *state, ALCdevice *Device, const ALeffectslot *Slot)
+{
+ ALfloat frequency = (ALfloat)Device->Frequency;
+ ALfloat rate;
+ ALint phase;
+ ALuint it;
+
+ for(it = 0;it < MaxChannels;it++)
+ {
+ state->Gain[0][it] = 0.0f;
+ state->Gain[1][it] = 0.0f;
+ }
+
+ state->waveform = Slot->effect.Flanger.Waveform;
+ state->depth = Slot->effect.Flanger.Depth;
+ state->feedback = Slot->effect.Flanger.Feedback;
+ state->delay = fastf2i(Slot->effect.Flanger.Delay * frequency);
+
+ /* Gains for left and right sides */
+ ComputeAngleGains(Device, atan2f(-1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[0]);
+ ComputeAngleGains(Device, atan2f(+1.0f, 0.0f), 0.0f, Slot->Gain, state->Gain[1]);
+
+ phase = Slot->effect.Flanger.Phase;
+ rate = Slot->effect.Flanger.Rate;
+
+ /* Calculate LFO coefficient */
+ switch(state->waveform)
+ {
+ case AL_FLANGER_WAVEFORM_TRIANGLE:
+ if(rate == 0.0f)
+ state->lfo_coeff = 0.0f;
+ else
+ state->lfo_coeff = 1.0f / (frequency / rate);
+ break;
+ case AL_FLANGER_WAVEFORM_SINUSOID:
+ if(rate == 0.0f)
+ state->lfo_coeff = 0.0f;
+ else
+ state->lfo_coeff = F_PI * 2.0f / (frequency / rate);
+ break;
+ }
+
+ /* Calculate lfo phase displacement */
+ if(phase == 0 || rate == 0.0f)
+ state->lfo_disp = 0;
+ else
+ state->lfo_disp = fastf2i(frequency / rate / (360.0f/phase));
+}
+
+static __inline void Triangle(ALint *delay_left, ALint *delay_right, ALint offset, const ALflangerState *state)
+{
+ ALfloat lfo_value;
+
+ lfo_value = 2.0f - fabsf(2.0f - fmodf(state->lfo_coeff * offset * 4.0f, 4.0f));
+ lfo_value *= state->depth * state->delay;
+ *delay_left = fastf2i(lfo_value) + state->delay;
+
+ lfo_value = 2.0f - fabsf(2.0f - fmodf(state->lfo_coeff *
+ (offset+state->lfo_disp) * 4.0f,
+ 4.0f));
+ lfo_value *= state->depth * state->delay;
+ *delay_right = fastf2i(lfo_value) + state->delay;
+}
+
+static __inline void Sinusoid(ALint *delay_left, ALint *delay_right, ALint offset, const ALflangerState *state)
+{
+ ALfloat lfo_value;
+
+ lfo_value = 1.0f + sinf(fmodf(state->lfo_coeff * offset, 2.0f*F_PI));
+ lfo_value *= state->depth * state->delay;
+ *delay_left = fastf2i(lfo_value) + state->delay;
+
+ lfo_value = 1.0f + sinf(fmodf(state->lfo_coeff * (offset+state->lfo_disp),
+ 2.0f*F_PI));
+ lfo_value *= state->depth * state->delay;
+ *delay_right = fastf2i(lfo_value) + state->delay;
+}
+
+#define DECL_TEMPLATE(func) \
+static void Process##func(ALflangerState *state, ALuint SamplesToDo, \
+ const ALfloat *restrict SamplesIn, \
+ ALfloat (*restrict SamplesOut)[BUFFERSIZE]) \
+{ \
+ const ALint mask = state->BufferLength-1; \
+ ALint offset = state->offset; \
+ ALuint it, kt; \
+ ALuint base; \
+ \
+ for(base = 0;base < SamplesToDo;) \
+ { \
+ ALfloat temps[64][2]; \
+ ALuint td = minu(SamplesToDo-base, 64); \
+ \
+ for(it = 0;it < td;it++,offset++) \
+ { \
+ ALint delay_left, delay_right; \
+ (func)(&delay_left, &delay_right, offset, state); \
+ \
+ temps[it][0] = state->SampleBufferLeft[(offset-delay_left)&mask]; \
+ state->SampleBufferLeft[offset&mask] = (temps[it][0] + \
+ SamplesIn[it+base]) * \
+ state->feedback; \
+ \
+ temps[it][1] = state->SampleBufferRight[(offset-delay_right)&mask];\
+ state->SampleBufferRight[offset&mask] = (temps[it][1] + \
+ SamplesIn[it+base]) * \
+ state->feedback; \
+ } \
+ \
+ for(kt = 0;kt < MaxChannels;kt++) \
+ { \
+ ALfloat gain = state->Gain[0][kt]; \
+ if(gain > 0.00001f) \
+ { \
+ for(it = 0;it < td;it++) \
+ SamplesOut[kt][it+base] += temps[it][0] * gain; \
+ } \
+ \
+ gain = state->Gain[1][kt]; \
+ if(gain > 0.00001f) \
+ { \
+ for(it = 0;it < td;it++) \
+ SamplesOut[kt][it+base] += temps[it][1] * gain; \
+ } \
+ } \
+ \
+ base += td; \
+ } \
+ \
+ state->offset = offset; \
+}
+
+DECL_TEMPLATE(Triangle)
+DECL_TEMPLATE(Sinusoid)
+
+#undef DECL_TEMPLATE
+
+static ALvoid ALflangerState_Process(ALflangerState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
+{
+ if(state->waveform == AL_FLANGER_WAVEFORM_TRIANGLE)
+ ProcessTriangle(state, SamplesToDo, SamplesIn, SamplesOut);
+ else if(state->waveform == AL_FLANGER_WAVEFORM_SINUSOID)
+ ProcessSinusoid(state, SamplesToDo, SamplesIn, SamplesOut);
+}
+
+static ALeffectStateFactory *ALflangerState_getCreator(void)
+{
+ return STATIC_CAST(ALeffectStateFactory, &FlangerFactory);
+}
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALflangerState);
+
+
+ALeffectState *ALflangerStateFactory_create(void)
+{
+ ALflangerState *state;
+
+ state = malloc(sizeof(*state));
+ if(!state) return NULL;
+ SET_VTABLE2(ALflangerState, ALeffectState, state);
+
+ state->BufferLength = 0;
+ state->SampleBufferLeft = NULL;
+ state->SampleBufferRight = NULL;
+ state->offset = 0;
+
+ return STATIC_CAST(ALeffectState, state);
+}
+
+static ALvoid ALflangerStateFactory_destroy(ALeffectState *effect)
+{
+ ALflangerState *state = STATIC_UPCAST(ALflangerState, ALeffectState, effect);
+ ALflangerState_Destruct(state);
+ free(state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALflangerStateFactory);
+
+
+static void init_flanger_factory(void)
+{
+ SET_VTABLE2(ALflangerStateFactory, ALeffectStateFactory, &FlangerFactory);
+}
+
+ALeffectStateFactory *ALflangerStateFactory_getFactory(void)
+{
+ static pthread_once_t once = PTHREAD_ONCE_INIT;
+ pthread_once(&once, init_flanger_factory);
+ return STATIC_CAST(ALeffectStateFactory, &FlangerFactory);
+}
+
+
+void flanger_SetParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{
+ switch(param)
+ {
+ case AL_FLANGER_WAVEFORM:
+ if(val >= AL_FLANGER_MIN_WAVEFORM && val <= AL_FLANGER_MAX_WAVEFORM)
+ effect->Flanger.Waveform = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ case AL_FLANGER_PHASE:
+ if(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE)
+ effect->Flanger.Phase = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM);
+ break;
+ }
+}
+void flanger_SetParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+ flanger_SetParami(effect, context, param, vals[0]);
+}
+void flanger_SetParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+ switch(param)
+ {
+ case AL_FLANGER_RATE:
+ if(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE)
+ effect->Flanger.Rate = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ case AL_FLANGER_DEPTH:
+ if(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH)
+ effect->Flanger.Depth = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ case AL_FLANGER_FEEDBACK:
+ if(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK)
+ effect->Flanger.Feedback = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ case AL_FLANGER_DELAY:
+ if(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY)
+ effect->Flanger.Delay = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM);
+ break;
+ }
+}
+void flanger_SetParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+ flanger_SetParamf(effect, context, param, vals[0]);
+}
+
+void flanger_GetParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{
+ switch(param)
+ {
+ case AL_FLANGER_WAVEFORM:
+ *val = effect->Flanger.Waveform;
+ break;
+
+ case AL_FLANGER_PHASE:
+ *val = effect->Flanger.Phase;
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM);
+ break;
+ }
+}
+void flanger_GetParamiv(ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+ flanger_GetParami(effect, context, param, vals);
+}
+void flanger_GetParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+ switch(param)
+ {
+ case AL_FLANGER_RATE:
+ *val = effect->Flanger.Rate;
+ break;
+
+ case AL_FLANGER_DEPTH:
+ *val = effect->Flanger.Depth;
+ break;
+
+ case AL_FLANGER_FEEDBACK:
+ *val = effect->Flanger.Feedback;
+ break;
+
+ case AL_FLANGER_DELAY:
+ *val = effect->Flanger.Delay;
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM);
+ break;
+ }
+}
+void flanger_GetParamfv(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+ flanger_GetParamf(effect, context, param, vals);
+}
diff --git a/Alc/effects/modulator.c b/Alc/effects/modulator.c
new file mode 100644
index 00000000..ec99bd53
--- /dev/null
+++ b/Alc/effects/modulator.c
@@ -0,0 +1,343 @@
+/**
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, 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"
+
+
+typedef struct ALmodulatorStateFactory {
+ DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALmodulatorStateFactory;
+
+static ALmodulatorStateFactory ModulatorFactory;
+
+
+typedef struct ALmodulatorState {
+ DERIVE_FROM_TYPE(ALeffectState);
+
+ enum {
+ SINUSOID,
+ SAWTOOTH,
+ SQUARE
+ } Waveform;
+
+ ALuint index;
+ ALuint step;
+
+ ALfloat Gain[MaxChannels];
+
+ FILTER iirFilter;
+} ALmodulatorState;
+
+#define WAVEFORM_FRACBITS 24
+#define WAVEFORM_FRACONE (1<<WAVEFORM_FRACBITS)
+#define WAVEFORM_FRACMASK (WAVEFORM_FRACONE-1)
+
+static __inline ALfloat Sin(ALuint index)
+{
+ return sinf(index * (F_PI*2.0f / WAVEFORM_FRACONE) - F_PI)*0.5f + 0.5f;
+}
+
+static __inline ALfloat Saw(ALuint index)
+{
+ return (ALfloat)index / WAVEFORM_FRACONE;
+}
+
+static __inline ALfloat Square(ALuint index)
+{
+ return (ALfloat)((index >> (WAVEFORM_FRACBITS - 1)) & 1);
+}
+
+
+static __inline ALfloat hpFilter1P(FILTER *iir, ALfloat input)
+{
+ ALfloat *history = iir->history;
+ ALfloat a = iir->coeff;
+ ALfloat output = input;
+
+ output = output + (history[0]-output)*a;
+ history[0] = output;
+
+ return input - output;
+}
+
+
+#define DECL_TEMPLATE(func) \
+static void Process##func(ALmodulatorState *state, ALuint SamplesToDo, \
+ const ALfloat *restrict SamplesIn, \
+ ALfloat (*restrict SamplesOut)[BUFFERSIZE]) \
+{ \
+ const ALuint step = state->step; \
+ ALuint index = state->index; \
+ ALuint base; \
+ \
+ for(base = 0;base < SamplesToDo;) \
+ { \
+ ALfloat temps[64]; \
+ ALuint td = minu(SamplesToDo-base, 64); \
+ ALuint i, k; \
+ \
+ for(i = 0;i < td;i++) \
+ { \
+ ALfloat samp; \
+ samp = SamplesIn[base+i]; \
+ samp = hpFilter1P(&state->iirFilter, samp); \
+ \
+ index += step; \
+ index &= WAVEFORM_FRACMASK; \
+ temps[i] = samp * func(index); \
+ } \
+ \
+ for(k = 0;k < MaxChannels;k++) \
+ { \
+ ALfloat gain = state->Gain[k]; \
+ if(!(gain > 0.00001f)) \
+ continue; \
+ \
+ for(i = 0;i < td;i++) \
+ SamplesOut[k][base+i] += gain * temps[i]; \
+ } \
+ \
+ base += td; \
+ } \
+ state->index = index; \
+}
+
+DECL_TEMPLATE(Sin)
+DECL_TEMPLATE(Saw)
+DECL_TEMPLATE(Square)
+
+#undef DECL_TEMPLATE
+
+
+static ALvoid ALmodulatorState_Destruct(ALmodulatorState *state)
+{
+ (void)state;
+}
+
+static ALboolean ALmodulatorState_DeviceUpdate(ALmodulatorState *state, ALCdevice *Device)
+{
+ return AL_TRUE;
+ (void)state;
+ (void)Device;
+}
+
+static ALvoid ALmodulatorState_Update(ALmodulatorState *state, ALCdevice *Device, const ALeffectslot *Slot)
+{
+ ALfloat gain, cw, a = 0.0f;
+ ALuint index;
+
+ if(Slot->effect.Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
+ state->Waveform = SINUSOID;
+ else if(Slot->effect.Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH)
+ state->Waveform = SAWTOOTH;
+ else if(Slot->effect.Modulator.Waveform == AL_RING_MODULATOR_SQUARE)
+ state->Waveform = SQUARE;
+
+ state->step = fastf2u(Slot->effect.Modulator.Frequency*WAVEFORM_FRACONE /
+ Device->Frequency);
+ if(state->step == 0) state->step = 1;
+
+ cw = cosf(F_PI*2.0f * Slot->effect.Modulator.HighPassCutoff /
+ Device->Frequency);
+ a = (2.0f-cw) - sqrtf(powf(2.0f-cw, 2.0f) - 1.0f);
+ state->iirFilter.coeff = a;
+
+ gain = sqrtf(1.0f/Device->NumChan);
+ gain *= Slot->Gain;
+ for(index = 0;index < MaxChannels;index++)
+ state->Gain[index] = 0.0f;
+ for(index = 0;index < Device->NumChan;index++)
+ {
+ enum Channel chan = Device->Speaker2Chan[index];
+ state->Gain[chan] = gain;
+ }
+}
+
+static ALvoid ALmodulatorState_Process(ALmodulatorState *state, ALuint SamplesToDo, const ALfloat *restrict SamplesIn, ALfloat (*restrict SamplesOut)[BUFFERSIZE])
+{
+ switch(state->Waveform)
+ {
+ case SINUSOID:
+ ProcessSin(state, SamplesToDo, SamplesIn, SamplesOut);
+ break;
+
+ case SAWTOOTH:
+ ProcessSaw(state, SamplesToDo, SamplesIn, SamplesOut);
+ break;
+
+ case SQUARE:
+ ProcessSquare(state, SamplesToDo, SamplesIn, SamplesOut);
+ break;
+ }
+}
+
+static ALeffectStateFactory *ALmodulatorState_getCreator(void)
+{
+ return STATIC_CAST(ALeffectStateFactory, &ModulatorFactory);
+}
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALmodulatorState);
+
+
+static ALeffectState *ALmodulatorStateFactory_create(void)
+{
+ ALmodulatorState *state;
+
+ state = malloc(sizeof(*state));
+ if(!state) return NULL;
+ SET_VTABLE2(ALmodulatorState, ALeffectState, state);
+
+ state->index = 0;
+ state->step = 1;
+
+ state->iirFilter.coeff = 0.0f;
+ state->iirFilter.history[0] = 0.0f;
+
+ return STATIC_CAST(ALeffectState, state);
+}
+
+static ALvoid ALmodulatorStateFactory_destroy(ALeffectState *effect)
+{
+ ALmodulatorState *state = STATIC_UPCAST(ALmodulatorState, ALeffectState, effect);
+ ALmodulatorState_Destruct(state);
+ free(state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALmodulatorStateFactory);
+
+
+static void init_modulator_factory(void)
+{
+ SET_VTABLE2(ALmodulatorStateFactory, ALeffectStateFactory, &ModulatorFactory);
+}
+
+ALeffectStateFactory *ALmodulatorStateFactory_getFactory(void)
+{
+ static pthread_once_t once = PTHREAD_ONCE_INIT;
+ pthread_once(&once, init_modulator_factory);
+ return STATIC_CAST(ALeffectStateFactory, &ModulatorFactory);
+}
+
+
+void mod_SetParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+ switch(param)
+ {
+ case AL_RING_MODULATOR_FREQUENCY:
+ if(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY)
+ effect->Modulator.Frequency = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
+ if(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF)
+ effect->Modulator.HighPassCutoff = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM);
+ break;
+ }
+}
+void mod_SetParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+ mod_SetParamf(effect, context, param, vals[0]);
+}
+void mod_SetParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{
+ switch(param)
+ {
+ case AL_RING_MODULATOR_FREQUENCY:
+ case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
+ mod_SetParamf(effect, context, param, (ALfloat)val);
+ break;
+
+ case AL_RING_MODULATOR_WAVEFORM:
+ if(val >= AL_RING_MODULATOR_MIN_WAVEFORM && val <= AL_RING_MODULATOR_MAX_WAVEFORM)
+ effect->Modulator.Waveform = val;
+ else
+ alSetError(context, AL_INVALID_VALUE);
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM);
+ break;
+ }
+}
+void mod_SetParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+ mod_SetParami(effect, context, param, vals[0]);
+}
+
+void mod_GetParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{
+ switch(param)
+ {
+ case AL_RING_MODULATOR_FREQUENCY:
+ *val = (ALint)effect->Modulator.Frequency;
+ break;
+ case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
+ *val = (ALint)effect->Modulator.HighPassCutoff;
+ break;
+ case AL_RING_MODULATOR_WAVEFORM:
+ *val = effect->Modulator.Waveform;
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM);
+ break;
+ }
+}
+void mod_GetParamiv(ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+ mod_GetParami(effect, context, param, vals);
+}
+void mod_GetParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+ switch(param)
+ {
+ case AL_RING_MODULATOR_FREQUENCY:
+ *val = effect->Modulator.Frequency;
+ break;
+ case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
+ *val = effect->Modulator.HighPassCutoff;
+ break;
+
+ default:
+ alSetError(context, AL_INVALID_ENUM);
+ break;
+ }
+}
+void mod_GetParamfv(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+ mod_GetParamf(effect, context, param, vals);
+}