Avoid taking the square-root of the ambient gain
[openal-soft.git] / Alc / effects / autowah.c
blobf4aaf49ff285ec5e37838341f78726be36978456
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 2013 by Anis A. Hireche, Nasca Octavian Paul
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include <stdlib.h>
23 #include "config.h"
24 #include "alu.h"
25 #include "alFilter.h"
26 #include "alError.h"
27 #include "alMain.h"
28 #include "alAuxEffectSlot.h"
31 /* Auto-wah is simply a low-pass filter with a cutoff frequency that shifts up
32 * or down depending on the input signal, and a resonant peak at the cutoff.
34 * Currently, we assume a cutoff frequency range of 20hz (no amplitude) to
35 * 20khz (peak gain). Peak gain is assumed to be in normalized scale.
38 typedef struct ALautowahState {
39 DERIVE_FROM_TYPE(ALeffectState);
41 /* Effect gains for each channel */
42 ALfloat Gain[MaxChannels];
44 /* Effect parameters */
45 ALfloat AttackRate;
46 ALfloat ReleaseRate;
47 ALfloat Resonance;
48 ALfloat PeakGain;
49 ALfloat GainCtrl;
50 ALfloat Frequency;
52 /* Samples processing */
53 ALfilterState LowPass;
54 } ALautowahState;
56 static ALvoid ALautowahState_Destruct(ALautowahState *UNUSED(state))
60 static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *device)
62 state->Frequency = (ALfloat)device->Frequency;
63 return AL_TRUE;
66 static ALvoid ALautowahState_update(ALautowahState *state, ALCdevice *device, const ALeffectslot *slot)
68 ALfloat attackTime, releaseTime;
69 ALfloat gain;
71 attackTime = slot->EffectProps.Autowah.AttackTime * state->Frequency;
72 releaseTime = slot->EffectProps.Autowah.ReleaseTime * state->Frequency;
74 state->AttackRate = powf(1.0f/GAIN_SILENCE_THRESHOLD, 1.0f/attackTime);
75 state->ReleaseRate = powf(GAIN_SILENCE_THRESHOLD/1.0f, 1.0f/releaseTime);
76 state->PeakGain = slot->EffectProps.Autowah.PeakGain;
77 state->Resonance = slot->EffectProps.Autowah.Resonance;
79 gain = 1.0f/device->NumSpeakers * slot->Gain;
80 SetGains(device, gain, state->Gain);
83 static ALvoid ALautowahState_process(ALautowahState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE])
85 ALuint it, kt;
86 ALuint base;
88 for(base = 0;base < SamplesToDo;)
90 ALfloat temps[64];
91 ALuint td = minu(SamplesToDo-base, 64);
92 ALfloat gain = state->GainCtrl;
94 for(it = 0;it < td;it++)
96 ALfloat smp = SamplesIn[it+base];
97 ALfloat alpha, w0;
98 ALfloat amplitude;
99 ALfloat cutoff;
101 /* Similar to compressor, we get the current amplitude of the
102 * incoming signal, and attack or release to reach it. */
103 amplitude = fabsf(smp);
104 if(amplitude > gain)
105 gain = minf(gain*state->AttackRate, amplitude);
106 else if(amplitude < gain)
107 gain = maxf(gain*state->ReleaseRate, amplitude);
108 gain = maxf(gain, GAIN_SILENCE_THRESHOLD);
110 /* FIXME: What range does the filter cover? */
111 cutoff = lerp(20.0f, 20000.0f, minf(gain/state->PeakGain, 1.0f));
113 /* The code below is like calling ALfilterState_setParams with
114 * ALfilterType_LowPass. However, instead of passing a bandwidth,
115 * we use the resonance property for Q. This also inlines the call.
117 w0 = F_2PI * cutoff / state->Frequency;
119 /* FIXME: Resonance controls the resonant peak, or Q. How? Not sure
120 * that Q = resonance*0.1. */
121 alpha = sinf(w0) / (2.0f * state->Resonance*0.1f);
122 state->LowPass.b[0] = (1.0f - cosf(w0)) / 2.0f;
123 state->LowPass.b[1] = 1.0f - cosf(w0);
124 state->LowPass.b[2] = (1.0f - cosf(w0)) / 2.0f;
125 state->LowPass.a[0] = 1.0f + alpha;
126 state->LowPass.a[1] = -2.0f * cosf(w0);
127 state->LowPass.a[2] = 1.0f - alpha;
129 state->LowPass.b[2] /= state->LowPass.a[0];
130 state->LowPass.b[1] /= state->LowPass.a[0];
131 state->LowPass.b[0] /= state->LowPass.a[0];
132 state->LowPass.a[2] /= state->LowPass.a[0];
133 state->LowPass.a[1] /= state->LowPass.a[0];
134 state->LowPass.a[0] /= state->LowPass.a[0];
136 temps[it] = ALfilterState_processSingle(&state->LowPass, smp);
138 state->GainCtrl = gain;
140 for(kt = 0;kt < MaxChannels;kt++)
142 ALfloat gain = state->Gain[kt];
143 if(!(gain > GAIN_SILENCE_THRESHOLD))
144 continue;
146 for(it = 0;it < td;it++)
147 SamplesOut[kt][base+it] += gain * temps[it];
150 base += td;
154 DECLARE_DEFAULT_ALLOCATORS(ALautowahState)
156 DEFINE_ALEFFECTSTATE_VTABLE(ALautowahState);
159 typedef struct ALautowahStateFactory {
160 DERIVE_FROM_TYPE(ALeffectStateFactory);
161 } ALautowahStateFactory;
163 static ALeffectState *ALautowahStateFactory_create(ALautowahStateFactory *UNUSED(factory))
165 ALautowahState *state;
167 state = ALautowahState_New(sizeof(*state));
168 if(!state) return NULL;
169 SET_VTABLE2(ALautowahState, ALeffectState, state);
171 state->AttackRate = 1.0f;
172 state->ReleaseRate = 1.0f;
173 state->Resonance = 2.0f;
174 state->PeakGain = 1.0f;
175 state->GainCtrl = 1.0f;
177 ALfilterState_clear(&state->LowPass);
179 return STATIC_CAST(ALeffectState, state);
182 DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALautowahStateFactory);
184 ALeffectStateFactory *ALautowahStateFactory_getFactory(void)
186 static ALautowahStateFactory AutowahFactory = { { GET_VTABLE2(ALautowahStateFactory, ALeffectStateFactory) } };
188 return STATIC_CAST(ALeffectStateFactory, &AutowahFactory);
192 void ALautowah_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
193 { SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
194 void ALautowah_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
196 ALautowah_setParami(effect, context, param, vals[0]);
198 void ALautowah_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
200 ALeffectProps *props = &effect->Props;
201 switch(param)
203 case AL_AUTOWAH_ATTACK_TIME:
204 if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME))
205 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
206 props->Autowah.AttackTime = val;
207 break;
209 case AL_AUTOWAH_RELEASE_TIME:
210 if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME))
211 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
212 props->Autowah.ReleaseTime = val;
213 break;
215 case AL_AUTOWAH_RESONANCE:
216 if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
217 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
218 props->Autowah.Resonance = val;
219 break;
221 case AL_AUTOWAH_PEAK_GAIN:
222 if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN))
223 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
224 props->Autowah.PeakGain = val;
225 break;
227 default:
228 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
231 void ALautowah_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
233 ALautowah_setParamf(effect, context, param, vals[0]);
236 void ALautowah_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
237 { SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
238 void ALautowah_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
240 ALautowah_getParami(effect, context, param, vals);
242 void ALautowah_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
244 const ALeffectProps *props = &effect->Props;
245 switch(param)
247 case AL_AUTOWAH_ATTACK_TIME:
248 *val = props->Autowah.AttackTime;
249 break;
251 case AL_AUTOWAH_RELEASE_TIME:
252 *val = props->Autowah.ReleaseTime;
253 break;
255 case AL_AUTOWAH_RESONANCE:
256 *val = props->Autowah.Resonance;
257 break;
259 case AL_AUTOWAH_PEAK_GAIN:
260 *val = props->Autowah.PeakGain;
261 break;
263 default:
264 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
267 void ALautowah_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
269 ALautowah_getParamf(effect, context, param, vals);
272 DEFINE_ALEFFECT_VTABLE(ALautowah);