diff options
Diffstat (limited to 'Alc/effects')
-rw-r--r-- | Alc/effects/chorus.c | 411 | ||||
-rw-r--r-- | Alc/effects/dedicated.c | 183 | ||||
-rw-r--r-- | Alc/effects/distortion.c | 402 | ||||
-rw-r--r-- | Alc/effects/echo.c | 324 | ||||
-rw-r--r-- | Alc/effects/equalizer.c | 499 | ||||
-rw-r--r-- | Alc/effects/flanger.c | 411 | ||||
-rw-r--r-- | Alc/effects/modulator.c | 343 |
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); +} |