Do up to 256 samples at a time with multi-step loops
[openal-soft.git] / Alc / effects / autowah.c
bloba444ace3482a01984e64b4ef453179b01a0c6132
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[MAX_OUTPUT_CHANNELS];
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;
70 attackTime = slot->EffectProps.Autowah.AttackTime * state->Frequency;
71 releaseTime = slot->EffectProps.Autowah.ReleaseTime * state->Frequency;
73 state->AttackRate = powf(1.0f/GAIN_SILENCE_THRESHOLD, 1.0f/attackTime);
74 state->ReleaseRate = powf(GAIN_SILENCE_THRESHOLD/1.0f, 1.0f/releaseTime);
75 state->PeakGain = slot->EffectProps.Autowah.PeakGain;
76 state->Resonance = slot->EffectProps.Autowah.Resonance;
78 ComputeAmbientGains(device, slot->Gain, state->Gain);
81 static ALvoid ALautowahState_process(ALautowahState *state, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[BUFFERSIZE], ALuint NumChannels)
83 ALuint it, kt;
84 ALuint base;
86 for(base = 0;base < SamplesToDo;)
88 ALfloat temps[256];
89 ALuint td = minu(256, SamplesToDo-base);
90 ALfloat gain = state->GainCtrl;
92 for(it = 0;it < td;it++)
94 ALfloat smp = SamplesIn[it+base];
95 ALfloat alpha, w0;
96 ALfloat amplitude;
97 ALfloat cutoff;
99 /* Similar to compressor, we get the current amplitude of the
100 * incoming signal, and attack or release to reach it. */
101 amplitude = fabsf(smp);
102 if(amplitude > gain)
103 gain = minf(gain*state->AttackRate, amplitude);
104 else if(amplitude < gain)
105 gain = maxf(gain*state->ReleaseRate, amplitude);
106 gain = maxf(gain, GAIN_SILENCE_THRESHOLD);
108 /* FIXME: What range does the filter cover? */
109 cutoff = lerp(20.0f, 20000.0f, minf(gain/state->PeakGain, 1.0f));
111 /* The code below is like calling ALfilterState_setParams with
112 * ALfilterType_LowPass. However, instead of passing a bandwidth,
113 * we use the resonance property for Q. This also inlines the call.
115 w0 = F_2PI * cutoff / state->Frequency;
117 /* FIXME: Resonance controls the resonant peak, or Q. How? Not sure
118 * that Q = resonance*0.1. */
119 alpha = sinf(w0) / (2.0f * state->Resonance*0.1f);
120 state->LowPass.b[0] = (1.0f - cosf(w0)) / 2.0f;
121 state->LowPass.b[1] = 1.0f - cosf(w0);
122 state->LowPass.b[2] = (1.0f - cosf(w0)) / 2.0f;
123 state->LowPass.a[0] = 1.0f + alpha;
124 state->LowPass.a[1] = -2.0f * cosf(w0);
125 state->LowPass.a[2] = 1.0f - alpha;
127 state->LowPass.b[2] /= state->LowPass.a[0];
128 state->LowPass.b[1] /= state->LowPass.a[0];
129 state->LowPass.b[0] /= state->LowPass.a[0];
130 state->LowPass.a[2] /= state->LowPass.a[0];
131 state->LowPass.a[1] /= state->LowPass.a[0];
132 state->LowPass.a[0] /= state->LowPass.a[0];
134 temps[it] = ALfilterState_processSingle(&state->LowPass, smp);
136 state->GainCtrl = gain;
138 for(kt = 0;kt < NumChannels;kt++)
140 ALfloat gain = state->Gain[kt];
141 if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
142 continue;
144 for(it = 0;it < td;it++)
145 SamplesOut[kt][base+it] += gain * temps[it];
148 base += td;
152 DECLARE_DEFAULT_ALLOCATORS(ALautowahState)
154 DEFINE_ALEFFECTSTATE_VTABLE(ALautowahState);
157 typedef struct ALautowahStateFactory {
158 DERIVE_FROM_TYPE(ALeffectStateFactory);
159 } ALautowahStateFactory;
161 static ALeffectState *ALautowahStateFactory_create(ALautowahStateFactory *UNUSED(factory))
163 ALautowahState *state;
165 state = ALautowahState_New(sizeof(*state));
166 if(!state) return NULL;
167 SET_VTABLE2(ALautowahState, ALeffectState, state);
169 state->AttackRate = 1.0f;
170 state->ReleaseRate = 1.0f;
171 state->Resonance = 2.0f;
172 state->PeakGain = 1.0f;
173 state->GainCtrl = 1.0f;
175 ALfilterState_clear(&state->LowPass);
177 return STATIC_CAST(ALeffectState, state);
180 DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALautowahStateFactory);
182 ALeffectStateFactory *ALautowahStateFactory_getFactory(void)
184 static ALautowahStateFactory AutowahFactory = { { GET_VTABLE2(ALautowahStateFactory, ALeffectStateFactory) } };
186 return STATIC_CAST(ALeffectStateFactory, &AutowahFactory);
190 void ALautowah_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
191 { SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
192 void ALautowah_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
194 ALautowah_setParami(effect, context, param, vals[0]);
196 void ALautowah_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
198 ALeffectProps *props = &effect->Props;
199 switch(param)
201 case AL_AUTOWAH_ATTACK_TIME:
202 if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME))
203 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
204 props->Autowah.AttackTime = val;
205 break;
207 case AL_AUTOWAH_RELEASE_TIME:
208 if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME))
209 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
210 props->Autowah.ReleaseTime = val;
211 break;
213 case AL_AUTOWAH_RESONANCE:
214 if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
215 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
216 props->Autowah.Resonance = val;
217 break;
219 case AL_AUTOWAH_PEAK_GAIN:
220 if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN))
221 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
222 props->Autowah.PeakGain = val;
223 break;
225 default:
226 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
229 void ALautowah_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
231 ALautowah_setParamf(effect, context, param, vals[0]);
234 void ALautowah_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
235 { SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
236 void ALautowah_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
238 ALautowah_getParami(effect, context, param, vals);
240 void ALautowah_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
242 const ALeffectProps *props = &effect->Props;
243 switch(param)
245 case AL_AUTOWAH_ATTACK_TIME:
246 *val = props->Autowah.AttackTime;
247 break;
249 case AL_AUTOWAH_RELEASE_TIME:
250 *val = props->Autowah.ReleaseTime;
251 break;
253 case AL_AUTOWAH_RESONANCE:
254 *val = props->Autowah.Resonance;
255 break;
257 case AL_AUTOWAH_PEAK_GAIN:
258 *val = props->Autowah.PeakGain;
259 break;
261 default:
262 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
265 void ALautowah_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
267 ALautowah_getParamf(effect, context, param, vals);
270 DEFINE_ALEFFECT_VTABLE(ALautowah);