Get rid of an unnecessary copy of ALeffectProps
[openal-soft.git] / Alc / effects / autowah.c
blob648afc835a13bddf30e0c0c03acf44df7a6ca06b
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 *state)
58 ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
61 static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *device)
63 state->Frequency = (ALfloat)device->Frequency;
64 return AL_TRUE;
67 static ALvoid ALautowahState_update(ALautowahState *state, const ALCdevice *device, const ALeffectslot *slot, const ALeffectProps *props)
69 ALfloat attackTime, releaseTime;
71 attackTime = props->Autowah.AttackTime * state->Frequency;
72 releaseTime = props->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 = props->Autowah.PeakGain;
77 state->Resonance = props->Autowah.Resonance;
79 ComputeAmbientGains(device->Dry, slot->Params.Gain, state->Gain);
82 static ALvoid ALautowahState_process(ALautowahState *state, ALuint SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels)
84 ALuint it, kt;
85 ALuint base;
87 for(base = 0;base < SamplesToDo;)
89 ALfloat temps[256];
90 ALuint td = minu(256, SamplesToDo-base);
91 ALfloat gain = state->GainCtrl;
93 for(it = 0;it < td;it++)
95 ALfloat smp = SamplesIn[0][it+base];
96 ALfloat a[3], b[3];
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_TAU * 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 b[0] = (1.0f - cosf(w0)) / 2.0f;
123 b[1] = 1.0f - cosf(w0);
124 b[2] = (1.0f - cosf(w0)) / 2.0f;
125 a[0] = 1.0f + alpha;
126 a[1] = -2.0f * cosf(w0);
127 a[2] = 1.0f - alpha;
129 state->LowPass.a1 = a[1] / a[0];
130 state->LowPass.a2 = a[2] / a[0];
131 state->LowPass.b1 = b[1] / a[0];
132 state->LowPass.b2 = b[2] / a[0];
133 state->LowPass.input_gain = b[0] / a[0];
135 temps[it] = ALfilterState_processSingle(&state->LowPass, smp);
137 state->GainCtrl = gain;
139 for(kt = 0;kt < NumChannels;kt++)
141 ALfloat gain = state->Gain[kt];
142 if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
143 continue;
145 for(it = 0;it < td;it++)
146 SamplesOut[kt][base+it] += gain * temps[it];
149 base += td;
153 DECLARE_DEFAULT_ALLOCATORS(ALautowahState)
155 DEFINE_ALEFFECTSTATE_VTABLE(ALautowahState);
158 typedef struct ALautowahStateFactory {
159 DERIVE_FROM_TYPE(ALeffectStateFactory);
160 } ALautowahStateFactory;
162 static ALeffectState *ALautowahStateFactory_create(ALautowahStateFactory *UNUSED(factory))
164 ALautowahState *state;
166 state = ALautowahState_New(sizeof(*state));
167 if(!state) return NULL;
168 SET_VTABLE2(ALautowahState, ALeffectState, state);
170 state->AttackRate = 1.0f;
171 state->ReleaseRate = 1.0f;
172 state->Resonance = 2.0f;
173 state->PeakGain = 1.0f;
174 state->GainCtrl = 1.0f;
176 ALfilterState_clear(&state->LowPass);
178 return STATIC_CAST(ALeffectState, state);
181 DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALautowahStateFactory);
183 ALeffectStateFactory *ALautowahStateFactory_getFactory(void)
185 static ALautowahStateFactory AutowahFactory = { { GET_VTABLE2(ALautowahStateFactory, ALeffectStateFactory) } };
187 return STATIC_CAST(ALeffectStateFactory, &AutowahFactory);
191 void ALautowah_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint UNUSED(val))
192 { SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
193 void ALautowah_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
195 ALautowah_setParami(effect, context, param, vals[0]);
197 void ALautowah_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
199 ALeffectProps *props = &effect->Props;
200 switch(param)
202 case AL_AUTOWAH_ATTACK_TIME:
203 if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME))
204 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
205 props->Autowah.AttackTime = val;
206 break;
208 case AL_AUTOWAH_RELEASE_TIME:
209 if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME))
210 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
211 props->Autowah.ReleaseTime = val;
212 break;
214 case AL_AUTOWAH_RESONANCE:
215 if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
216 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
217 props->Autowah.Resonance = val;
218 break;
220 case AL_AUTOWAH_PEAK_GAIN:
221 if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN))
222 SET_ERROR_AND_RETURN(context, AL_INVALID_VALUE);
223 props->Autowah.PeakGain = val;
224 break;
226 default:
227 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
230 void ALautowah_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
232 ALautowah_setParamf(effect, context, param, vals[0]);
235 void ALautowah_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum UNUSED(param), ALint *UNUSED(val))
236 { SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM); }
237 void ALautowah_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
239 ALautowah_getParami(effect, context, param, vals);
241 void ALautowah_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
243 const ALeffectProps *props = &effect->Props;
244 switch(param)
246 case AL_AUTOWAH_ATTACK_TIME:
247 *val = props->Autowah.AttackTime;
248 break;
250 case AL_AUTOWAH_RELEASE_TIME:
251 *val = props->Autowah.ReleaseTime;
252 break;
254 case AL_AUTOWAH_RESONANCE:
255 *val = props->Autowah.Resonance;
256 break;
258 case AL_AUTOWAH_PEAK_GAIN:
259 *val = props->Autowah.PeakGain;
260 break;
262 default:
263 SET_ERROR_AND_RETURN(context, AL_INVALID_ENUM);
266 void ALautowah_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
268 ALautowah_getParamf(effect, context, param, vals);
271 DEFINE_ALEFFECT_VTABLE(ALautowah);