Clean up the remaining effect struct member names
[openal-soft.git] / Alc / effects / equalizer.cpp
blob814b43a4bf14480305bab81f4a7a3c2a85965555
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 2013 by Mike Gorchak
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 "config.h"
23 #include <math.h>
24 #include <stdlib.h>
26 #include <algorithm>
28 #include "alMain.h"
29 #include "alcontext.h"
30 #include "alAuxEffectSlot.h"
31 #include "alError.h"
32 #include "alu.h"
33 #include "filters/defs.h"
34 #include "vecmat.h"
37 /* The document "Effects Extension Guide.pdf" says that low and high *
38 * frequencies are cutoff frequencies. This is not fully correct, they *
39 * are corner frequencies for low and high shelf filters. If they were *
40 * just cutoff frequencies, there would be no need in cutoff frequency *
41 * gains, which are present. Documentation for "Creative Proteus X2" *
42 * software describes 4-band equalizer functionality in a much better *
43 * way. This equalizer seems to be a predecessor of OpenAL 4-band *
44 * equalizer. With low and high shelf filters we are able to cutoff *
45 * frequencies below and/or above corner frequencies using attenuation *
46 * gains (below 1.0) and amplify all low and/or high frequencies using *
47 * gains above 1.0. *
48 * *
49 * Low-shelf Low Mid Band High Mid Band High-shelf *
50 * corner center center corner *
51 * frequency frequency frequency frequency *
52 * 50Hz..800Hz 200Hz..3000Hz 1000Hz..8000Hz 4000Hz..16000Hz *
53 * *
54 * | | | | *
55 * | | | | *
56 * B -----+ /--+--\ /--+--\ +----- *
57 * O |\ | | | | | | /| *
58 * O | \ - | - - | - / | *
59 * S + | \ | | | | | | / | *
60 * T | | | | | | | | | | *
61 * ---------+---------------+------------------+---------------+-------- *
62 * C | | | | | | | | | | *
63 * U - | / | | | | | | \ | *
64 * T | / - | - - | - \ | *
65 * O |/ | | | | | | \| *
66 * F -----+ \--+--/ \--+--/ +----- *
67 * F | | | | *
68 * | | | | *
69 * *
70 * Gains vary from 0.126 up to 7.943, which means from -18dB attenuation *
71 * up to +18dB amplification. Band width varies from 0.01 up to 1.0 in *
72 * octaves for two mid bands. *
73 * *
74 * Implementation is based on the "Cookbook formulae for audio EQ biquad *
75 * filter coefficients" by Robert Bristow-Johnson *
76 * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt */
79 struct ALequalizerState final : public ALeffectState {
80 struct {
81 /* Effect parameters */
82 BiquadFilter filter[4];
84 /* Effect gains for each channel */
85 ALfloat CurrentGains[MAX_OUTPUT_CHANNELS]{};
86 ALfloat TargetGains[MAX_OUTPUT_CHANNELS]{};
87 } mChans[MAX_EFFECT_CHANNELS];
89 ALfloat mSampleBuffer[MAX_EFFECT_CHANNELS][BUFFERSIZE]{};
92 static ALvoid ALequalizerState_Destruct(ALequalizerState *state);
93 static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *state, ALCdevice *device);
94 static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
95 static ALvoid ALequalizerState_process(ALequalizerState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
96 DECLARE_DEFAULT_ALLOCATORS(ALequalizerState)
98 DEFINE_ALEFFECTSTATE_VTABLE(ALequalizerState);
101 static void ALequalizerState_Construct(ALequalizerState *state)
103 new (state) ALequalizerState{};
104 ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
105 SET_VTABLE2(ALequalizerState, ALeffectState, state);
108 static ALvoid ALequalizerState_Destruct(ALequalizerState *state)
110 ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
111 state->~ALequalizerState();
114 static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *state, ALCdevice *UNUSED(device))
116 for(auto &e : state->mChans)
118 std::for_each(std::begin(e.filter), std::end(e.filter),
119 [](BiquadFilter &f) -> void
120 { BiquadFilter_clear(&f); }
122 std::fill(std::begin(e.CurrentGains), std::end(e.CurrentGains), 0.0f);
124 return AL_TRUE;
127 static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
129 const ALCdevice *device = context->Device;
130 ALfloat frequency = (ALfloat)device->Frequency;
131 ALfloat gain, f0norm;
132 ALuint i;
134 /* Calculate coefficients for the each type of filter. Note that the shelf
135 * filters' gain is for the reference frequency, which is the centerpoint
136 * of the transition band.
138 gain = maxf(sqrtf(props->Equalizer.LowGain), 0.0625f); /* Limit -24dB */
139 f0norm = props->Equalizer.LowCutoff/frequency;
140 BiquadFilter_setParams(&state->mChans[0].filter[0], BiquadType::LowShelf,
141 gain, f0norm, calc_rcpQ_from_slope(gain, 0.75f)
144 gain = maxf(props->Equalizer.Mid1Gain, 0.0625f);
145 f0norm = props->Equalizer.Mid1Center/frequency;
146 BiquadFilter_setParams(&state->mChans[0].filter[1], BiquadType::Peaking,
147 gain, f0norm, calc_rcpQ_from_bandwidth(
148 f0norm, props->Equalizer.Mid1Width
152 gain = maxf(props->Equalizer.Mid2Gain, 0.0625f);
153 f0norm = props->Equalizer.Mid2Center/frequency;
154 BiquadFilter_setParams(&state->mChans[0].filter[2], BiquadType::Peaking,
155 gain, f0norm, calc_rcpQ_from_bandwidth(
156 f0norm, props->Equalizer.Mid2Width
160 gain = maxf(sqrtf(props->Equalizer.HighGain), 0.0625f);
161 f0norm = props->Equalizer.HighCutoff/frequency;
162 BiquadFilter_setParams(&state->mChans[0].filter[3], BiquadType::HighShelf,
163 gain, f0norm, calc_rcpQ_from_slope(gain, 0.75f)
166 /* Copy the filter coefficients for the other input channels. */
167 for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
169 BiquadFilter_copyParams(&state->mChans[i].filter[0], &state->mChans[0].filter[0]);
170 BiquadFilter_copyParams(&state->mChans[i].filter[1], &state->mChans[0].filter[1]);
171 BiquadFilter_copyParams(&state->mChans[i].filter[2], &state->mChans[0].filter[2]);
172 BiquadFilter_copyParams(&state->mChans[i].filter[3], &state->mChans[0].filter[3]);
175 state->OutBuffer = device->FOAOut.Buffer;
176 state->OutChannels = device->FOAOut.NumChannels;
177 for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
178 ComputePanGains(&device->FOAOut, aluMatrixf::Identity.m[i], slot->Params.Gain,
179 state->mChans[i].TargetGains);
182 static ALvoid ALequalizerState_process(ALequalizerState *state, ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesIn)[BUFFERSIZE], ALfloat (*RESTRICT SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
184 ALfloat (*RESTRICT temps)[BUFFERSIZE] = state->mSampleBuffer;
185 ALsizei c;
187 for(c = 0;c < MAX_EFFECT_CHANNELS;c++)
189 BiquadFilter_process(&state->mChans[c].filter[0], temps[0], SamplesIn[c], SamplesToDo);
190 BiquadFilter_process(&state->mChans[c].filter[1], temps[1], temps[0], SamplesToDo);
191 BiquadFilter_process(&state->mChans[c].filter[2], temps[2], temps[1], SamplesToDo);
192 BiquadFilter_process(&state->mChans[c].filter[3], temps[3], temps[2], SamplesToDo);
194 MixSamples(temps[3], NumChannels, SamplesOut,
195 state->mChans[c].CurrentGains, state->mChans[c].TargetGains,
196 SamplesToDo, 0, SamplesToDo
202 struct EqualizerStateFactory final : public EffectStateFactory {
203 ALeffectState *create() override;
206 ALeffectState *EqualizerStateFactory::create()
208 ALequalizerState *state;
209 NEW_OBJ0(state, ALequalizerState)();
210 return state;
213 EffectStateFactory *EqualizerStateFactory_getFactory(void)
215 static EqualizerStateFactory EqualizerFactory{};
216 return &EqualizerFactory;
220 void ALequalizer_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
221 { alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param); }
222 void ALequalizer_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
223 { alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", param); }
224 void ALequalizer_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
226 ALeffectProps *props = &effect->Props;
227 switch(param)
229 case AL_EQUALIZER_LOW_GAIN:
230 if(!(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN))
231 SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer low-band gain out of range");
232 props->Equalizer.LowGain = val;
233 break;
235 case AL_EQUALIZER_LOW_CUTOFF:
236 if(!(val >= AL_EQUALIZER_MIN_LOW_CUTOFF && val <= AL_EQUALIZER_MAX_LOW_CUTOFF))
237 SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer low-band cutoff out of range");
238 props->Equalizer.LowCutoff = val;
239 break;
241 case AL_EQUALIZER_MID1_GAIN:
242 if(!(val >= AL_EQUALIZER_MIN_MID1_GAIN && val <= AL_EQUALIZER_MAX_MID1_GAIN))
243 SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid1-band gain out of range");
244 props->Equalizer.Mid1Gain = val;
245 break;
247 case AL_EQUALIZER_MID1_CENTER:
248 if(!(val >= AL_EQUALIZER_MIN_MID1_CENTER && val <= AL_EQUALIZER_MAX_MID1_CENTER))
249 SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid1-band center out of range");
250 props->Equalizer.Mid1Center = val;
251 break;
253 case AL_EQUALIZER_MID1_WIDTH:
254 if(!(val >= AL_EQUALIZER_MIN_MID1_WIDTH && val <= AL_EQUALIZER_MAX_MID1_WIDTH))
255 SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid1-band width out of range");
256 props->Equalizer.Mid1Width = val;
257 break;
259 case AL_EQUALIZER_MID2_GAIN:
260 if(!(val >= AL_EQUALIZER_MIN_MID2_GAIN && val <= AL_EQUALIZER_MAX_MID2_GAIN))
261 SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid2-band gain out of range");
262 props->Equalizer.Mid2Gain = val;
263 break;
265 case AL_EQUALIZER_MID2_CENTER:
266 if(!(val >= AL_EQUALIZER_MIN_MID2_CENTER && val <= AL_EQUALIZER_MAX_MID2_CENTER))
267 SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid2-band center out of range");
268 props->Equalizer.Mid2Center = val;
269 break;
271 case AL_EQUALIZER_MID2_WIDTH:
272 if(!(val >= AL_EQUALIZER_MIN_MID2_WIDTH && val <= AL_EQUALIZER_MAX_MID2_WIDTH))
273 SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid2-band width out of range");
274 props->Equalizer.Mid2Width = val;
275 break;
277 case AL_EQUALIZER_HIGH_GAIN:
278 if(!(val >= AL_EQUALIZER_MIN_HIGH_GAIN && val <= AL_EQUALIZER_MAX_HIGH_GAIN))
279 SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer high-band gain out of range");
280 props->Equalizer.HighGain = val;
281 break;
283 case AL_EQUALIZER_HIGH_CUTOFF:
284 if(!(val >= AL_EQUALIZER_MIN_HIGH_CUTOFF && val <= AL_EQUALIZER_MAX_HIGH_CUTOFF))
285 SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer high-band cutoff out of range");
286 props->Equalizer.HighCutoff = val;
287 break;
289 default:
290 alSetError(context, AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param);
293 void ALequalizer_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
294 { ALequalizer_setParamf(effect, context, param, vals[0]); }
296 void ALequalizer_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
297 { alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param); }
298 void ALequalizer_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
299 { alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", param); }
300 void ALequalizer_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
302 const ALeffectProps *props = &effect->Props;
303 switch(param)
305 case AL_EQUALIZER_LOW_GAIN:
306 *val = props->Equalizer.LowGain;
307 break;
309 case AL_EQUALIZER_LOW_CUTOFF:
310 *val = props->Equalizer.LowCutoff;
311 break;
313 case AL_EQUALIZER_MID1_GAIN:
314 *val = props->Equalizer.Mid1Gain;
315 break;
317 case AL_EQUALIZER_MID1_CENTER:
318 *val = props->Equalizer.Mid1Center;
319 break;
321 case AL_EQUALIZER_MID1_WIDTH:
322 *val = props->Equalizer.Mid1Width;
323 break;
325 case AL_EQUALIZER_MID2_GAIN:
326 *val = props->Equalizer.Mid2Gain;
327 break;
329 case AL_EQUALIZER_MID2_CENTER:
330 *val = props->Equalizer.Mid2Center;
331 break;
333 case AL_EQUALIZER_MID2_WIDTH:
334 *val = props->Equalizer.Mid2Width;
335 break;
337 case AL_EQUALIZER_HIGH_GAIN:
338 *val = props->Equalizer.HighGain;
339 break;
341 case AL_EQUALIZER_HIGH_CUTOFF:
342 *val = props->Equalizer.HighCutoff;
343 break;
345 default:
346 alSetError(context, AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param);
349 void ALequalizer_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
350 { ALequalizer_getParamf(effect, context, param, vals); }
352 DEFINE_ALEFFECT_VTABLE(ALequalizer);