Implement the Autowah effect.
This commit is contained in:
@@ -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);
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
@@ -89,6 +91,13 @@ typedef union ALeffectProps {
|
||||
ALfloat LFReference;
|
||||
} Reverb;
|
||||
|
||||
struct {
|
||||
ALfloat AttackTime;
|
||||
ALfloat ReleaseTime;
|
||||
ALfloat PeakGain;
|
||||
ALfloat Resonance;
|
||||
} Autowah;
|
||||
|
||||
struct {
|
||||
ALfloat Delay;
|
||||
ALfloat LRDelay;
|
||||
|
||||
@@ -20,6 +20,7 @@ typedef enum ALfilterType {
|
||||
ALfilterType_Peaking,
|
||||
|
||||
ALfilterType_LowPass,
|
||||
ALfilterType_HighPass,
|
||||
ALfilterType_BandPass,
|
||||
} ALfilterType;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 }
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
+2
-2
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user