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 */
74 typedef struct ALequalizerState
{
75 DERIVE_FROM_TYPE(ALeffectState
);
77 /* Effect gains for each channel */
78 ALfloat Gain
[MaxChannels
];
80 /* Effect parameters */
81 ALfilterState filter
[4];
84 static ALvoid
ALequalizerState_Destruct(ALequalizerState
*UNUSED(state
))
88 static ALboolean
ALequalizerState_deviceUpdate(ALequalizerState
*UNUSED(state
), ALCdevice
*UNUSED(device
))
93 static ALvoid
ALequalizerState_update(ALequalizerState
*state
, ALCdevice
*device
, const ALeffectslot
*slot
)
95 ALfloat frequency
= (ALfloat
)device
->Frequency
;
96 ALfloat gain
= sqrtf(1.0f
/ device
->NumChan
) * slot
->Gain
;
98 SetGains(device
, gain
, state
->Gain
);
100 /* Calculate coefficients for the each type of filter */
101 ALfilterState_setParams(&state
->filter
[0], ALfilterType_LowShelf
,
102 sqrtf(slot
->EffectProps
.Equalizer
.LowGain
),
103 slot
->EffectProps
.Equalizer
.LowCutoff
/frequency
,
106 ALfilterState_setParams(&state
->filter
[1], ALfilterType_Peaking
,
107 sqrtf(slot
->EffectProps
.Equalizer
.Mid1Gain
),
108 slot
->EffectProps
.Equalizer
.Mid1Center
/frequency
,
109 slot
->EffectProps
.Equalizer
.Mid1Width
);
111 ALfilterState_setParams(&state
->filter
[2], ALfilterType_Peaking
,
112 sqrtf(slot
->EffectProps
.Equalizer
.Mid2Gain
),
113 slot
->EffectProps
.Equalizer
.Mid2Center
/frequency
,
114 slot
->EffectProps
.Equalizer
.Mid2Width
);
116 ALfilterState_setParams(&state
->filter
[3], ALfilterType_HighShelf
,
117 sqrtf(slot
->EffectProps
.Equalizer
.HighGain
),
118 slot
->EffectProps
.Equalizer
.HighCutoff
/frequency
,
122 static ALvoid
ALequalizerState_process(ALequalizerState
*state
, ALuint SamplesToDo
, const ALfloat
*restrict SamplesIn
, ALfloat (*restrict SamplesOut
)[BUFFERSIZE
])
129 for(base
= 0;base
< SamplesToDo
;)
132 ALuint td
= minu(SamplesToDo
-base
, 64);
134 for(it
= 0;it
< td
;it
++)
136 ALfloat smp
= SamplesIn
[base
+it
];
138 for(ft
= 0;ft
< 4;ft
++)
139 smp
= ALfilterState_processSingle(&state
->filter
[ft
], smp
);
144 for(kt
= 0;kt
< MaxChannels
;kt
++)
146 ALfloat gain
= state
->Gain
[kt
];
147 if(!(gain
> GAIN_SILENCE_THRESHOLD
))
150 for(it
= 0;it
< td
;it
++)
151 SamplesOut
[kt
][base
+it
] += gain
* temps
[it
];
158 DECLARE_DEFAULT_ALLOCATORS(ALequalizerState
)
160 DEFINE_ALEFFECTSTATE_VTABLE(ALequalizerState
);
163 typedef struct ALequalizerStateFactory
{
164 DERIVE_FROM_TYPE(ALeffectStateFactory
);
165 } ALequalizerStateFactory
;
167 ALeffectState
*ALequalizerStateFactory_create(ALequalizerStateFactory
*UNUSED(factory
))
169 ALequalizerState
*state
;
172 state
= ALequalizerState_New(sizeof(*state
));
173 if(!state
) return NULL
;
174 SET_VTABLE2(ALequalizerState
, ALeffectState
, state
);
176 /* Initialize sample history only on filter creation to avoid */
177 /* sound clicks if filter settings were changed in runtime. */
178 for(it
= 0; it
< 4; it
++)
179 ALfilterState_clear(&state
->filter
[it
]);
181 return STATIC_CAST(ALeffectState
, state
);
184 DEFINE_ALEFFECTSTATEFACTORY_VTABLE(ALequalizerStateFactory
);
186 ALeffectStateFactory
*ALequalizerStateFactory_getFactory(void)
188 static ALequalizerStateFactory EqualizerFactory
= { { GET_VTABLE2(ALequalizerStateFactory
, ALeffectStateFactory
) } };
190 return STATIC_CAST(ALeffectStateFactory
, &EqualizerFactory
);
194 void ALequalizer_setParami(ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum
UNUSED(param
), ALint
UNUSED(val
))
195 { SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
); }
196 void ALequalizer_setParamiv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALint
*vals
)
198 ALequalizer_setParami(effect
, context
, param
, vals
[0]);
200 void ALequalizer_setParamf(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat val
)
202 ALeffectProps
*props
= &effect
->Props
;
205 case AL_EQUALIZER_LOW_GAIN
:
206 if(!(val
>= AL_EQUALIZER_MIN_LOW_GAIN
&& val
<= AL_EQUALIZER_MAX_LOW_GAIN
))
207 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
208 props
->Equalizer
.LowGain
= val
;
211 case AL_EQUALIZER_LOW_CUTOFF
:
212 if(!(val
>= AL_EQUALIZER_MIN_LOW_CUTOFF
&& val
<= AL_EQUALIZER_MAX_LOW_CUTOFF
))
213 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
214 props
->Equalizer
.LowCutoff
= val
;
217 case AL_EQUALIZER_MID1_GAIN
:
218 if(!(val
>= AL_EQUALIZER_MIN_MID1_GAIN
&& val
<= AL_EQUALIZER_MAX_MID1_GAIN
))
219 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
220 props
->Equalizer
.Mid1Gain
= val
;
223 case AL_EQUALIZER_MID1_CENTER
:
224 if(!(val
>= AL_EQUALIZER_MIN_MID1_CENTER
&& val
<= AL_EQUALIZER_MAX_MID1_CENTER
))
225 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
226 props
->Equalizer
.Mid1Center
= val
;
229 case AL_EQUALIZER_MID1_WIDTH
:
230 if(!(val
>= AL_EQUALIZER_MIN_MID1_WIDTH
&& val
<= AL_EQUALIZER_MAX_MID1_WIDTH
))
231 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
232 props
->Equalizer
.Mid1Width
= val
;
235 case AL_EQUALIZER_MID2_GAIN
:
236 if(!(val
>= AL_EQUALIZER_MIN_MID2_GAIN
&& val
<= AL_EQUALIZER_MAX_MID2_GAIN
))
237 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
238 props
->Equalizer
.Mid2Gain
= val
;
241 case AL_EQUALIZER_MID2_CENTER
:
242 if(!(val
>= AL_EQUALIZER_MIN_MID2_CENTER
&& val
<= AL_EQUALIZER_MAX_MID2_CENTER
))
243 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
244 props
->Equalizer
.Mid2Center
= val
;
247 case AL_EQUALIZER_MID2_WIDTH
:
248 if(!(val
>= AL_EQUALIZER_MIN_MID2_WIDTH
&& val
<= AL_EQUALIZER_MAX_MID2_WIDTH
))
249 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
250 props
->Equalizer
.Mid2Width
= val
;
253 case AL_EQUALIZER_HIGH_GAIN
:
254 if(!(val
>= AL_EQUALIZER_MIN_HIGH_GAIN
&& val
<= AL_EQUALIZER_MAX_HIGH_GAIN
))
255 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
256 props
->Equalizer
.HighGain
= val
;
259 case AL_EQUALIZER_HIGH_CUTOFF
:
260 if(!(val
>= AL_EQUALIZER_MIN_HIGH_CUTOFF
&& val
<= AL_EQUALIZER_MAX_HIGH_CUTOFF
))
261 SET_ERROR_AND_RETURN(context
, AL_INVALID_VALUE
);
262 props
->Equalizer
.HighCutoff
= val
;
266 SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
);
269 void ALequalizer_setParamfv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
271 ALequalizer_setParamf(effect
, context
, param
, vals
[0]);
274 void ALequalizer_getParami(const ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum
UNUSED(param
), ALint
*UNUSED(val
))
275 { SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
); }
276 void ALequalizer_getParamiv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALint
*vals
)
278 ALequalizer_getParami(effect
, context
, param
, vals
);
280 void ALequalizer_getParamf(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*val
)
282 const ALeffectProps
*props
= &effect
->Props
;
285 case AL_EQUALIZER_LOW_GAIN
:
286 *val
= props
->Equalizer
.LowGain
;
289 case AL_EQUALIZER_LOW_CUTOFF
:
290 *val
= props
->Equalizer
.LowCutoff
;
293 case AL_EQUALIZER_MID1_GAIN
:
294 *val
= props
->Equalizer
.Mid1Gain
;
297 case AL_EQUALIZER_MID1_CENTER
:
298 *val
= props
->Equalizer
.Mid1Center
;
301 case AL_EQUALIZER_MID1_WIDTH
:
302 *val
= props
->Equalizer
.Mid1Width
;
305 case AL_EQUALIZER_MID2_GAIN
:
306 *val
= props
->Equalizer
.Mid2Gain
;
309 case AL_EQUALIZER_MID2_CENTER
:
310 *val
= props
->Equalizer
.Mid2Center
;
313 case AL_EQUALIZER_MID2_WIDTH
:
314 *val
= props
->Equalizer
.Mid2Width
;
317 case AL_EQUALIZER_HIGH_GAIN
:
318 *val
= props
->Equalizer
.HighGain
;
321 case AL_EQUALIZER_HIGH_CUTOFF
:
322 *val
= props
->Equalizer
.HighCutoff
;
326 SET_ERROR_AND_RETURN(context
, AL_INVALID_ENUM
);
329 void ALequalizer_getParamfv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
331 ALequalizer_getParamf(effect
, context
, param
, vals
);
334 DEFINE_ALEFFECT_VTABLE(ALequalizer
);