diff options
author | Chris Robinson <[email protected]> | 2013-10-03 03:32:54 -0700 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2013-10-03 03:37:03 -0700 |
commit | 99fa5911bc9f427c96fe800f94d31e4942805fd2 (patch) | |
tree | 85a3c6f1ef2e13e8260f1f5b3c5be4edf902cb74 | |
parent | 2f676832bcd4aa9a51a602eb254e60eb831581dc (diff) |
Implement the Autowah effect.
-rw-r--r-- | Alc/effects/autowah.c | 293 | ||||
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | OpenAL32/Include/alAuxEffectSlot.h | 1 | ||||
-rw-r--r-- | OpenAL32/Include/alEffect.h | 17 | ||||
-rw-r--r-- | OpenAL32/Include/alFilter.h | 1 | ||||
-rw-r--r-- | OpenAL32/alAuxEffectSlot.c | 1 | ||||
-rw-r--r-- | OpenAL32/alEffect.c | 7 | ||||
-rw-r--r-- | OpenAL32/alExtension.c | 9 | ||||
-rw-r--r-- | OpenAL32/alFilter.c | 9 | ||||
-rw-r--r-- | alsoftrc.sample | 4 |
10 files changed, 333 insertions, 10 deletions
diff --git a/Alc/effects/autowah.c b/Alc/effects/autowah.c new file mode 100644 index 00000000..cc207858 --- /dev/null +++ b/Alc/effects/autowah.c @@ -0,0 +1,293 @@ +/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2013 by Anis A. Hireche, Nasca Octavian Paul
+ * 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 <stdlib.h>
+
+#include "config.h"
+#include "alu.h"
+#include "alFilter.h"
+#include "alError.h"
+#include "alMain.h"
+#include "alAuxEffectSlot.h"
+
+
+/* You can tweak the octave of this dynamic filter just changing next macro
+ * guitar - (default) 2.0f
+ * bass - 4.0f
+ */
+#define OCTAVE 2.0f
+
+
+typedef struct ALautowahStateFactory {
+ DERIVE_FROM_TYPE(ALeffectStateFactory);
+} ALautowahStateFactory;
+
+static ALautowahStateFactory AutowahFactory;
+
+
+/* We use a lfo with a custom low-pass filter to generate autowah
+ * effect and a high-pass filter to avoid distortion and aliasing.
+ * By adding the two filters up, we obtain a dynamic bandpass filter.
+ */
+
+typedef struct ALautowahState {
+ DERIVE_FROM_TYPE(ALeffectState);
+
+ /* Effect gains for each channel */
+ ALfloat Gain[MaxChannels];
+
+ /* Effect parameters */
+ ALfloat AttackTime;
+ ALfloat ReleaseTime;
+ ALfloat Resonance;
+ ALfloat PeakGain;
+ ALuint Frequency;
+
+ /* Samples processing */
+ ALuint lfo;
+ ALfilterState low_pass;
+ ALfilterState high_pass;
+} ALautowahState;
+
+static ALvoid ALautowahState_Destruct(ALautowahState *state)
+{
+ (void)state;
+}
+
+static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *device)
+{
+ return AL_TRUE;
+ (void)state;
+ (void)device;
+}
+
+static ALvoid ALautowahState_update(ALautowahState *state, ALCdevice *Device, const ALeffectslot *Slot)
+{
+ ALuint i;
+ ALfloat gain = sqrtf(1.0f / Device->NumChan) * Slot->Gain;
+
+ /* computing high-pass cutoff and bandwidth */
+ const ALfloat cutoff = LOWPASSFREQREF / (Device->Frequency * 4.0f);
+ const ALfloat bandwidth = (cutoff / 2.0f) / (cutoff * 0.67f);
+
+ /* computing high-pass filter coefficients */
+ ALfilterState_setParams(&state->high_pass,
+ ALfilterType_HighPass, 1.0f,
+ cutoff, bandwidth);
+
+ state->AttackTime = Slot->EffectProps.Autowah.AttackTime;
+ state->ReleaseTime = Slot->EffectProps.Autowah.ReleaseTime;
+ state->Frequency = Device->Frequency;
+ state->PeakGain = Slot->EffectProps.Autowah.PeakGain;
+ state->Resonance = Slot->EffectProps.Autowah.Resonance;
+
+ state->lfo = 0;
+
+ ALfilterState_clear(&state->low_pass);
+
+ for(i = 0;i < MaxChannels;i++)
+ state->Gain[i] = 0.0f;
+ for(i = 0;i < Device->NumChan;i++)
+ {
+ enum Channel chan = Device->Speaker2Chan[i];
+ state->Gain[chan] = gain;
+ }
+}
+
+static ALvoid ALautowahState_process(ALautowahState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE])
+{
+ ALuint it, kt;
+ ALuint base;
+
+ for(base = 0;base < SamplesToDo;)
+ {
+ ALfloat temps[64];
+ ALuint td = minu(SamplesToDo-base, 64);
+
+ for(it = 0;it < td;it++)
+ {
+ ALfloat smp = SamplesIn[it+base];
+ ALfloat frequency, omega, alpha, peak;
+
+ /* lfo for low-pass shaking */
+ if((state->lfo++) % 30 == 0)
+ {
+ /* Using custom low-pass filter coefficients, to handle the resonance and peak-gain properties. */
+ frequency = (1.0f + cosf(state->lfo * (1.0f / lerp(1.0f, 4.0f, state->AttackTime * state->ReleaseTime)) * 2.0f * F_PI / state->Frequency)) / OCTAVE;
+ frequency = expf((frequency - 1.0f) * 6.0f);
+
+ /* computing cutoff frequency and peak gain */
+ omega = F_PI * frequency;
+ alpha = sinf(omega) / (16.0f * (state->Resonance / AL_AUTOWAH_MAX_RESONANCE));
+ peak = lerp(1.0f, 10.0f, state->PeakGain / AL_AUTOWAH_MAX_PEAK_GAIN);
+
+ /* computing low-pass filter coefficients */
+ state->low_pass.b[0] = (1.0f - cosf(omega)) / 2.0f;
+ state->low_pass.b[1] = 1.0f - cosf(omega);
+ state->low_pass.b[2] = (1.0f - cosf(omega)) / 2.0f;
+ state->low_pass.a[0] = 1.0f + alpha / peak;
+ state->low_pass.a[1] = -2.0f * cosf(omega);
+ state->low_pass.a[2] = 1.0f - alpha / peak;
+
+ state->low_pass.b[2] /= state->low_pass.a[0];
+ state->low_pass.b[1] /= state->low_pass.a[0];
+ state->low_pass.b[0] /= state->low_pass.a[0];
+ state->low_pass.a[2] /= state->low_pass.a[0];
+ state->low_pass.a[1] /= state->low_pass.a[0];
+ state->low_pass.a[0] /= state->low_pass.a[0];
+ }
+
+ /* do high-pass filter */
+ smp = ALfilterState_processSingle(&state->high_pass, smp);
+
+ /* do low-pass filter */
+ temps[it] = ALfilterState_processSingle(&state->low_pass, 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 void ALautowahState_Delete(ALautowahState *state)
+{
+ free(state);
+}
+
+DEFINE_ALEFFECTSTATE_VTABLE(ALautowahState);
+
+
+static ALeffectState *ALautowahStateFactory_create(ALautowahStateFactory *factory)
+{
+ ALautowahState *state;
+ (void)factory;
+
+ state = malloc(sizeof(*state));
+ if(!state) return NULL;
+ SET_VTABLE2(ALautowahState, ALeffectState, state);
+
+ ALfilterState_clear(&state->low_pass);
+ ALfilterState_clear(&state->high_pass);
+
+ return STATIC_CAST(ALeffectState, state);
+}
+
+DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALautowahStateFactory);
+
+ALeffectStateFactory *ALautowahStateFactory_getFactory(void)
+{
+ SET_VTABLE2(ALautowahStateFactory, ALeffectStateFactory, &AutowahFactory);
+ return STATIC_CAST(ALeffectStateFactory, &AutowahFactory);
+}
+
+
+void ALautowah_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); (void)effect;(void)param;(void)val; }
+void ALautowah_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
+{
+ ALautowah_setParami(effect, context, param, vals[0]);
+}
+
+void ALautowah_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
+{
+ ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_AUTOWAH_ATTACK_TIME:
+ if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Autowah.AttackTime = val;
+ break;
+
+ case AL_AUTOWAH_RELEASE_TIME:
+ if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Autowah.ReleaseTime = val;
+ break;
+
+ case AL_AUTOWAH_RESONANCE:
+ if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Autowah.Resonance = val;
+ break;
+
+ case AL_AUTOWAH_PEAK_GAIN:
+ if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN))
+ SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
+ props->Autowah.PeakGain = val;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+
+void ALautowah_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
+{
+ ALautowah_setParamf(effect, context, param, vals[0]);
+}
+
+void ALautowah_getParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
+{ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); (void)effect;(void)param;(void)val; }
+void ALautowah_getParamiv(ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
+{
+ ALautowah_getParami(effect, context, param, vals);
+}
+void ALautowah_getParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
+{
+ const ALeffectProps *props = &effect->Props;
+ switch(param)
+ {
+ case AL_AUTOWAH_ATTACK_TIME:
+ *val = props->Autowah.AttackTime;
+ break;
+
+ case AL_AUTOWAH_RELEASE_TIME:
+ *val = props->Autowah.ReleaseTime;
+ break;
+
+ case AL_AUTOWAH_RESONANCE:
+ *val = props->Autowah.Resonance;
+ break;
+
+ case AL_AUTOWAH_PEAK_GAIN:
+ *val = props->Autowah.PeakGain;
+ break;
+
+ default:
+ SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
+ }
+}
+
+void ALautowah_getParamfv(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
+{
+ ALautowah_getParamf(effect, context, param, vals);
+}
+
+DEFINE_ALEFFECT_VTABLE(ALautowah);
diff --git a/CMakeLists.txt b/CMakeLists.txt index 354a5ae2..f39c910f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -465,6 +465,7 @@ SET(ALC_OBJS Alc/ALc.c Alc/alcRing.c Alc/alcThread.c Alc/bs2b.c + Alc/effects/autowah.c Alc/effects/chorus.c Alc/effects/dedicated.c Alc/effects/distortion.c diff --git a/OpenAL32/Include/alAuxEffectSlot.h b/OpenAL32/Include/alAuxEffectSlot.h index 463a6770..00de7a3b 100644 --- a/OpenAL32/Include/alAuxEffectSlot.h +++ b/OpenAL32/Include/alAuxEffectSlot.h @@ -96,6 +96,7 @@ ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context); ALeffectStateFactory *ALnullStateFactory_getFactory(void); ALeffectStateFactory *ALreverbStateFactory_getFactory(void); +ALeffectStateFactory *ALautowahStateFactory_getFactory(void); ALeffectStateFactory *ALchorusStateFactory_getFactory(void); ALeffectStateFactory *ALdistortionStateFactory_getFactory(void); ALeffectStateFactory *ALechoStateFactory_getFactory(void); diff --git a/OpenAL32/Include/alEffect.h b/OpenAL32/Include/alEffect.h index 8b7b612f..7269c13e 100644 --- a/OpenAL32/Include/alEffect.h +++ b/OpenAL32/Include/alEffect.h @@ -12,13 +12,14 @@ struct ALeffect; enum { EAXREVERB = 0, REVERB, + AUTOWAH, + CHORUS, + DISTORTION, ECHO, + EQUALIZER, + FLANGER, MODULATOR, DEDICATED, - CHORUS, - FLANGER, - EQUALIZER, - DISTORTION, MAX_EFFECTS }; @@ -49,6 +50,7 @@ const struct ALeffectVtable T##_vtable = { \ extern const struct ALeffectVtable ALeaxreverb_vtable; extern const struct ALeffectVtable ALreverb_vtable; +extern const struct ALeffectVtable ALautowah_vtable; extern const struct ALeffectVtable ALchorus_vtable; extern const struct ALeffectVtable ALdistortion_vtable; extern const struct ALeffectVtable ALecho_vtable; @@ -90,6 +92,13 @@ typedef union ALeffectProps { } Reverb; struct { + ALfloat AttackTime; + ALfloat ReleaseTime; + ALfloat PeakGain; + ALfloat Resonance; + } Autowah; + + struct { ALfloat Delay; ALfloat LRDelay; diff --git a/OpenAL32/Include/alFilter.h b/OpenAL32/Include/alFilter.h index 690a496b..c8bdf906 100644 --- a/OpenAL32/Include/alFilter.h +++ b/OpenAL32/Include/alFilter.h @@ -20,6 +20,7 @@ typedef enum ALfilterType { ALfilterType_Peaking, ALfilterType_LowPass, + ALfilterType_HighPass, ALfilterType_BandPass, } ALfilterType; diff --git a/OpenAL32/alAuxEffectSlot.c b/OpenAL32/alAuxEffectSlot.c index eec41081..827820eb 100644 --- a/OpenAL32/alAuxEffectSlot.c +++ b/OpenAL32/alAuxEffectSlot.c @@ -457,6 +457,7 @@ void InitEffectFactoryMap(void) InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_NULL, ALnullStateFactory_getFactory); InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_EAXREVERB, ALreverbStateFactory_getFactory); InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_REVERB, ALreverbStateFactory_getFactory); + InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_AUTOWAH, ALautowahStateFactory_getFactory); InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_CHORUS, ALchorusStateFactory_getFactory); InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DISTORTION, ALdistortionStateFactory_getFactory); InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_ECHO, ALechoStateFactory_getFactory); diff --git a/OpenAL32/alEffect.c b/OpenAL32/alEffect.c index b67e34ae..ed6a1d5a 100644 --- a/OpenAL32/alEffect.c +++ b/OpenAL32/alEffect.c @@ -413,6 +413,13 @@ static void InitEffectParams(ALeffect *effect, ALenum type) effect->Props.Reverb.DecayHFLimit = AL_REVERB_DEFAULT_DECAY_HFLIMIT; effect->vtbl = &ALreverb_vtable; break; + case AL_EFFECT_AUTOWAH: + effect->Props.Autowah.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME; + effect->Props.Autowah.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN; + effect->Props.Autowah.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME; + effect->Props.Autowah.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE; + effect->vtbl = &ALautowah_vtable; + break; case AL_EFFECT_CHORUS: effect->Props.Chorus.Waveform = AL_CHORUS_DEFAULT_WAVEFORM; effect->Props.Chorus.Phase = AL_CHORUS_DEFAULT_PHASE; diff --git a/OpenAL32/alExtension.c b/OpenAL32/alExtension.c index 0261ba7a..236e56d0 100644 --- a/OpenAL32/alExtension.c +++ b/OpenAL32/alExtension.c @@ -38,14 +38,15 @@ const struct EffectList EffectList[] = { { "eaxreverb", EAXREVERB, "AL_EFFECT_EAXREVERB", AL_EFFECT_EAXREVERB }, { "reverb", REVERB, "AL_EFFECT_REVERB", AL_EFFECT_REVERB }, + { "autowah", AUTOWAH, "AL_EFFECT_AUTOWAH", AL_EFFECT_AUTOWAH }, + { "chorus", CHORUS, "AL_EFFECT_CHORUS", AL_EFFECT_CHORUS }, + { "distortion", DISTORTION, "AL_EFFECT_DISTORTION", AL_EFFECT_DISTORTION }, { "echo", ECHO, "AL_EFFECT_ECHO", AL_EFFECT_ECHO }, + { "equalizer", EQUALIZER, "AL_EFFECT_EQUALIZER", AL_EFFECT_EQUALIZER }, + { "flanger", FLANGER, "AL_EFFECT_FLANGER", AL_EFFECT_FLANGER }, { "modulator", MODULATOR, "AL_EFFECT_RING_MODULATOR", AL_EFFECT_RING_MODULATOR }, { "dedicated", DEDICATED, "AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT", AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT }, { "dedicated", DEDICATED, "AL_EFFECT_DEDICATED_DIALOGUE", AL_EFFECT_DEDICATED_DIALOGUE }, - { "chorus", CHORUS, "AL_EFFECT_CHORUS", AL_EFFECT_CHORUS }, - { "flanger", FLANGER, "AL_EFFECT_FLANGER", AL_EFFECT_FLANGER }, - { "equalizer", EQUALIZER, "AL_EFFECT_EQUALIZER", AL_EFFECT_EQUALIZER }, - { "distortion", DISTORTION, "AL_EFFECT_DISTORTION", AL_EFFECT_DISTORTION }, { NULL, 0, NULL, (ALenum)0 } }; diff --git a/OpenAL32/alFilter.c b/OpenAL32/alFilter.c index 7fb6a5e9..fd10addc 100644 --- a/OpenAL32/alFilter.c +++ b/OpenAL32/alFilter.c @@ -405,6 +405,15 @@ void ALfilterState_setParams(ALfilterState *filter, ALfilterType type, ALfloat g filter->a[1] = -2.0f * cosf(w0); filter->a[2] = 1.0f - alpha; break; + case ALfilterType_HighPass: + alpha = sinf(w0) * sinhf(logf(2.0f) / 2.0f * bandwidth * w0 / sinf(w0)); + filter->b[0] = (1.0f + cosf(w0)) / 2.0f; + filter->b[1] = 1.0f + cosf(w0); + filter->b[2] = (1.0f + cosf(w0)) / 2.0f; + filter->a[0] = 1.0f + alpha; + filter->a[1] = -2.0f * cosf(w0); + filter->a[2] = 1.0f - alpha; + break; case ALfilterType_BandPass: alpha = sinf(w0) * sinhf(logf(2.0f) / 2.0f * bandwidth * w0 / sinf(w0)); filter->b[0] = alpha; diff --git a/alsoftrc.sample b/alsoftrc.sample index cfd333da..656f1bf0 100644 --- a/alsoftrc.sample +++ b/alsoftrc.sample @@ -126,8 +126,8 @@ ## excludefx: # Sets which effects to exclude, preventing apps from using them. This can # help for apps that try to use effects which are too CPU intensive for the -# system to handle. Available effects are: eaxreverb,reverb,chorus,distortion, -# echo,equalizer,flanger,modulator,dedicated +# system to handle. Available effects are: eaxreverb,reverb,autowah,chorus, +# distortion,echo,equalizer,flanger,modulator,dedicated #excludefx = ## slots: |