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
28 #include "alAuxEffectSlot.h"
33 /* The document "Effects Extension Guide.pdf" says that low and high *
34 * frequencies are cutoff frequencies. This is not fully correct, they *
35 * are corner frequencies for low and high shelf filters. If they were *
36 * just cutoff frequencies, there would be no need in cutoff frequency *
37 * gains, which are present. Documentation for "Creative Proteus X2" *
38 * software describes 4-band equalizer functionality in a much better *
39 * way. This equalizer seems to be a predecessor of OpenAL 4-band *
40 * equalizer. With low and high shelf filters we are able to cutoff *
41 * frequencies below and/or above corner frequencies using attenuation *
42 * gains (below 1.0) and amplify all low and/or high frequencies using *
45 * Low-shelf Low Mid Band High Mid Band High-shelf *
46 * corner center center corner *
47 * frequency frequency frequency frequency *
48 * 50Hz..800Hz 200Hz..3000Hz 1000Hz..8000Hz 4000Hz..16000Hz *
52 * B -----+ /--+--\ /--+--\ +----- *
53 * O |\ | | | | | | /| *
54 * O | \ - | - - | - / | *
55 * S + | \ | | | | | | / | *
56 * T | | | | | | | | | | *
57 * ---------+---------------+------------------+---------------+-------- *
58 * C | | | | | | | | | | *
59 * U - | / | | | | | | \ | *
60 * T | / - | - - | - \ | *
61 * O |/ | | | | | | \| *
62 * F -----+ \--+--/ \--+--/ +----- *
66 * Gains vary from 0.126 up to 7.943, which means from -18dB attenuation *
67 * up to +18dB amplification. Band width varies from 0.01 up to 1.0 in *
68 * octaves for two mid bands. *
70 * Implementation is based on the "Cookbook formulae for audio EQ biquad *
71 * filter coefficients" by Robert Bristow-Johnson *
72 * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt */
75 /* The maximum number of sample frames per update. */
76 #define MAX_UPDATE_SAMPLES 256
78 typedef struct ALequalizerState
{
79 DERIVE_FROM_TYPE(ALeffectState
);
81 /* Effect gains for each channel */
82 ALfloat Gain
[MAX_EFFECT_CHANNELS
][MAX_OUTPUT_CHANNELS
];
84 /* Effect parameters */
85 ALfilterState filter
[4][MAX_EFFECT_CHANNELS
];
87 ALfloat SampleBuffer
[4][MAX_EFFECT_CHANNELS
][MAX_UPDATE_SAMPLES
];
90 static ALvoid
ALequalizerState_Destruct(ALequalizerState
*state
)
92 ALeffectState_Destruct(STATIC_CAST(ALeffectState
,state
));
95 static ALboolean
ALequalizerState_deviceUpdate(ALequalizerState
*UNUSED(state
), ALCdevice
*UNUSED(device
))
100 static ALvoid
ALequalizerState_update(ALequalizerState
*state
, const ALCdevice
*device
, const ALeffectslot
*slot
)
102 ALfloat frequency
= (ALfloat
)device
->Frequency
;
103 ALfloat gain
, freq_mult
;
107 aluMatrixfSet(&matrix
,
108 1.0f
, 0.0f
, 0.0f
, 0.0f
,
109 0.0f
, 1.0f
, 0.0f
, 0.0f
,
110 0.0f
, 0.0f
, 1.0f
, 0.0f
,
111 0.0f
, 0.0f
, 0.0f
, 1.0f
114 STATIC_CAST(ALeffectState
,state
)->OutBuffer
= device
->FOAOut
.Buffer
;
115 STATIC_CAST(ALeffectState
,state
)->OutChannels
= device
->FOAOut
.NumChannels
;
116 for(i
= 0;i
< MAX_EFFECT_CHANNELS
;i
++)
117 ComputeFirstOrderGains(device
->FOAOut
, matrix
.m
[i
], slot
->Params
.Gain
,
120 /* Calculate coefficients for the each type of filter. Note that the shelf
121 * filters' gain is for the reference frequency, which is the centerpoint
122 * of the transition band.
124 gain
= sqrtf(slot
->Params
.EffectProps
.Equalizer
.LowGain
);
125 freq_mult
= slot
->Params
.EffectProps
.Equalizer
.LowCutoff
/frequency
;
126 ALfilterState_setParams(&state
->filter
[0][0], ALfilterType_LowShelf
,
127 gain
, freq_mult
, calc_rcpQ_from_slope(gain
, 0.75f
)
129 /* Copy the filter coefficients for the other input channels. */
130 for(i
= 1;i
< MAX_EFFECT_CHANNELS
;i
++)
132 state
->filter
[0][i
].a1
= state
->filter
[0][0].a1
;
133 state
->filter
[0][i
].a2
= state
->filter
[0][0].a2
;
134 state
->filter
[0][i
].b1
= state
->filter
[0][0].b1
;
135 state
->filter
[0][i
].b2
= state
->filter
[0][0].b2
;
136 state
->filter
[0][i
].input_gain
= state
->filter
[0][0].input_gain
;
137 state
->filter
[0][i
].process
= state
->filter
[0][0].process
;
140 gain
= slot
->Params
.EffectProps
.Equalizer
.Mid1Gain
;
141 freq_mult
= slot
->Params
.EffectProps
.Equalizer
.Mid1Center
/frequency
;
142 ALfilterState_setParams(&state
->filter
[1][0], ALfilterType_Peaking
,
143 gain
, freq_mult
, calc_rcpQ_from_bandwidth(
144 freq_mult
, slot
->Params
.EffectProps
.Equalizer
.Mid1Width
147 for(i
= 1;i
< MAX_EFFECT_CHANNELS
;i
++)
149 state
->filter
[1][i
].a1
= state
->filter
[1][0].a1
;
150 state
->filter
[1][i
].a2
= state
->filter
[1][0].a2
;
151 state
->filter
[1][i
].b1
= state
->filter
[1][0].b1
;
152 state
->filter
[1][i
].b2
= state
->filter
[1][0].b2
;
153 state
->filter
[1][i
].input_gain
= state
->filter
[1][0].input_gain
;
154 state
->filter
[1][i
].process
= state
->filter
[1][0].process
;
157 gain
= slot
->Params
.EffectProps
.Equalizer
.Mid2Gain
;
158 freq_mult
= slot
->Params
.EffectProps
.Equalizer
.Mid2Center
/frequency
;
159 ALfilterState_setParams(&state
->filter
[2][0], ALfilterType_Peaking
,
160 gain
, freq_mult
, calc_rcpQ_from_bandwidth(
161 freq_mult
, slot
->Params
.EffectProps
.Equalizer
.Mid2Width
164 for(i
= 1;i
< MAX_EFFECT_CHANNELS
;i
++)
166 state
->filter
[2][i
].a1
= state
->filter
[2][0].a1
;
167 state
->filter
[2][i
].a2
= state
->filter
[2][0].a2
;
168 state
->filter
[2][i
].b1
= state
->filter
[2][0].b1
;
169 state
->filter
[2][i
].b2
= state
->filter
[2][0].b2
;
170 state
->filter
[2][i
].input_gain
= state
->filter
[2][0].input_gain
;
171 state
->filter
[2][i
].process
= state
->filter
[2][0].process
;
174 gain
= sqrtf(slot
->Params
.EffectProps
.Equalizer
.HighGain
);
175 freq_mult
= slot
->Params
.EffectProps
.Equalizer
.HighCutoff
/frequency
;
176 ALfilterState_setParams(&state
->filter
[3][0], ALfilterType_HighShelf
,
177 gain
, freq_mult
, calc_rcpQ_from_slope(gain
, 0.75f
)
179 for(i
= 1;i
< MAX_EFFECT_CHANNELS
;i
++)
181 state
->filter
[3][i
].a1
= state
->filter
[3][0].a1
;
182 state
->filter
[3][i
].a2
= state
->filter
[3][0].a2
;
183 state
->filter
[3][i
].b1
= state
->filter
[3][0].b1
;
184 state
->filter
[3][i
].b2
= state
->filter
[3][0].b2
;
185 state
->filter
[3][i
].input_gain
= state
->filter
[3][0].input_gain
;
186 state
->filter
[3][i
].process
= state
->filter
[3][0].process
;
190 static ALvoid
ALequalizerState_process(ALequalizerState
*state
, ALuint SamplesToDo
, const ALfloat (*restrict SamplesIn
)[BUFFERSIZE
], ALfloat (*restrict SamplesOut
)[BUFFERSIZE
], ALuint NumChannels
)
192 ALfloat (*Samples
)[MAX_EFFECT_CHANNELS
][MAX_UPDATE_SAMPLES
] = state
->SampleBuffer
;
196 for(base
= 0;base
< SamplesToDo
;)
198 ALuint td
= minu(MAX_UPDATE_SAMPLES
, SamplesToDo
-base
);
200 for(ft
= 0;ft
< MAX_EFFECT_CHANNELS
;ft
++)
201 ALfilterState_process(&state
->filter
[0][ft
], Samples
[0][ft
], &SamplesIn
[ft
][base
], td
);
202 for(ft
= 0;ft
< MAX_EFFECT_CHANNELS
;ft
++)
203 ALfilterState_process(&state
->filter
[1][ft
], Samples
[1][ft
], Samples
[0][ft
], td
);
204 for(ft
= 0;ft
< MAX_EFFECT_CHANNELS
;ft
++)
205 ALfilterState_process(&state
->filter
[2][ft
], Samples
[2][ft
], Samples
[1][ft
], td
);
206 for(ft
= 0;ft
< MAX_EFFECT_CHANNELS
;ft
++)
207 ALfilterState_process(&state
->filter
[3][ft
], Samples
[3][ft
], Samples
[2][ft
], td
);
209 for(ft
= 0;ft
< MAX_EFFECT_CHANNELS
;ft
++)
211 for(kt
= 0;kt
< NumChannels
;kt
++)
213 ALfloat gain
= state
->Gain
[ft
][kt
];
214 if(!(fabsf(gain
) > GAIN_SILENCE_THRESHOLD
))
217 for(it
= 0;it
< td
;it
++)
218 SamplesOut
[kt
][base
+it
] += gain
* Samples
[3][ft
][it
];
226 DECLARE_DEFAULT_ALLOCATORS(ALequalizerState
)
228 DEFINE_ALEFFECTSTATE_VTABLE(ALequalizerState
);
231 typedef struct ALequalizerStateFactory
{
232 DERIVE_FROM_TYPE(ALeffectStateFactory
);
233 } ALequalizerStateFactory
;
235 ALeffectState
*ALequalizerStateFactory_create(ALequalizerStateFactory
*UNUSED(factory
))
237 ALequalizerState
*state
;
240 state
= ALequalizerState_New(sizeof(*state
));
241 if(!state
) return NULL
;
242 SET_VTABLE2(ALequalizerState
, ALeffectState
, state
);
244 /* Initialize sample history only on filter creation to avoid */
245 /* sound clicks if filter settings were changed in runtime. */
246 for(it
= 0; it
< 4; it
++)
248 for(ft
= 0;ft
< MAX_EFFECT_CHANNELS
;ft
++)
249 ALfilterState_clear(&state
->filter
[it
][ft
]);
252 return STATIC_CAST(ALeffectState
, state
);
255 DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALequalizerStateFactory
);
257 ALeffectStateFactory
*ALequalizerStateFactory_getFactory(void)
259 static ALequalizerStateFactory EqualizerFactory
= { { GET_VTABLE2(ALequalizerStateFactory
, ALeffectStateFactory
) } };
261 return STATIC_CAST(ALeffectStateFactory
, &EqualizerFactory
);
265 void ALequalizer_setParami(ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum
UNUSED(param
), ALint
UNUSED(val
))
266 { SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
); }
267 void ALequalizer_setParamiv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
269 ALequalizer_setParami(effect
, context
, param
, vals
[0]);
271 void ALequalizer_setParamf(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat val
)
273 ALeffectProps
*props
= &effect
->Props
;
276 case AL_EQUALIZER_LOW_GAIN
:
277 if(!(val
>= AL_EQUALIZER_MIN_LOW_GAIN
&& val
<= AL_EQUALIZER_MAX_LOW_GAIN
))
278 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
279 props
->Equalizer
.LowGain
= val
;
282 case AL_EQUALIZER_LOW_CUTOFF
:
283 if(!(val
>= AL_EQUALIZER_MIN_LOW_CUTOFF
&& val
<= AL_EQUALIZER_MAX_LOW_CUTOFF
))
284 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
285 props
->Equalizer
.LowCutoff
= val
;
288 case AL_EQUALIZER_MID1_GAIN
:
289 if(!(val
>= AL_EQUALIZER_MIN_MID1_GAIN
&& val
<= AL_EQUALIZER_MAX_MID1_GAIN
))
290 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
291 props
->Equalizer
.Mid1Gain
= val
;
294 case AL_EQUALIZER_MID1_CENTER
:
295 if(!(val
>= AL_EQUALIZER_MIN_MID1_CENTER
&& val
<= AL_EQUALIZER_MAX_MID1_CENTER
))
296 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
297 props
->Equalizer
.Mid1Center
= val
;
300 case AL_EQUALIZER_MID1_WIDTH
:
301 if(!(val
>= AL_EQUALIZER_MIN_MID1_WIDTH
&& val
<= AL_EQUALIZER_MAX_MID1_WIDTH
))
302 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
303 props
->Equalizer
.Mid1Width
= val
;
306 case AL_EQUALIZER_MID2_GAIN
:
307 if(!(val
>= AL_EQUALIZER_MIN_MID2_GAIN
&& val
<= AL_EQUALIZER_MAX_MID2_GAIN
))
308 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
309 props
->Equalizer
.Mid2Gain
= val
;
312 case AL_EQUALIZER_MID2_CENTER
:
313 if(!(val
>= AL_EQUALIZER_MIN_MID2_CENTER
&& val
<= AL_EQUALIZER_MAX_MID2_CENTER
))
314 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
315 props
->Equalizer
.Mid2Center
= val
;
318 case AL_EQUALIZER_MID2_WIDTH
:
319 if(!(val
>= AL_EQUALIZER_MIN_MID2_WIDTH
&& val
<= AL_EQUALIZER_MAX_MID2_WIDTH
))
320 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
321 props
->Equalizer
.Mid2Width
= val
;
324 case AL_EQUALIZER_HIGH_GAIN
:
325 if(!(val
>= AL_EQUALIZER_MIN_HIGH_GAIN
&& val
<= AL_EQUALIZER_MAX_HIGH_GAIN
))
326 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
327 props
->Equalizer
.HighGain
= val
;
330 case AL_EQUALIZER_HIGH_CUTOFF
:
331 if(!(val
>= AL_EQUALIZER_MIN_HIGH_CUTOFF
&& val
<= AL_EQUALIZER_MAX_HIGH_CUTOFF
))
332 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
333 props
->Equalizer
.HighCutoff
= val
;
337 SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
);
340 void ALequalizer_setParamfv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
342 ALequalizer_setParamf(effect
, context
, param
, vals
[0]);
345 void ALequalizer_getParami(const ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum
UNUSED(param
), ALint
*UNUSED(val
))
346 { SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
); }
347 void ALequalizer_getParamiv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
349 ALequalizer_getParami(effect
, context
, param
, vals
);
351 void ALequalizer_getParamf(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*val
)
353 const ALeffectProps
*props
= &effect
->Props
;
356 case AL_EQUALIZER_LOW_GAIN
:
357 *val
= props
->Equalizer
.LowGain
;
360 case AL_EQUALIZER_LOW_CUTOFF
:
361 *val
= props
->Equalizer
.LowCutoff
;
364 case AL_EQUALIZER_MID1_GAIN
:
365 *val
= props
->Equalizer
.Mid1Gain
;
368 case AL_EQUALIZER_MID1_CENTER
:
369 *val
= props
->Equalizer
.Mid1Center
;
372 case AL_EQUALIZER_MID1_WIDTH
:
373 *val
= props
->Equalizer
.Mid1Width
;
376 case AL_EQUALIZER_MID2_GAIN
:
377 *val
= props
->Equalizer
.Mid2Gain
;
380 case AL_EQUALIZER_MID2_CENTER
:
381 *val
= props
->Equalizer
.Mid2Center
;
384 case AL_EQUALIZER_MID2_WIDTH
:
385 *val
= props
->Equalizer
.Mid2Width
;
388 case AL_EQUALIZER_HIGH_GAIN
:
389 *val
= props
->Equalizer
.HighGain
;
392 case AL_EQUALIZER_HIGH_CUTOFF
:
393 *val
= props
->Equalizer
.HighCutoff
;
397 SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
);
400 void ALequalizer_getParamfv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
402 ALequalizer_getParamf(effect
, context
, param
, vals
);
405 DEFINE_ALEFFECT_VTABLE(ALequalizer
);