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
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 */
52 /* Samples processing */
53 ALfilterState LowPass
;
56 static ALvoid
ALautowahState_Destruct(ALautowahState
*UNUSED(state
))
60 static ALboolean
ALautowahState_deviceUpdate(ALautowahState
*state
, ALCdevice
*device
)
62 state
->Frequency
= (ALfloat
)device
->Frequency
;
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
)
86 for(base
= 0;base
< SamplesToDo
;)
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
];
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
);
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
))
144 for(it
= 0;it
< td
;it
++)
145 SamplesOut
[kt
][base
+it
] += gain
* temps
[it
];
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
;
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
;
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
;
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
;
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
;
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
;
245 case AL_AUTOWAH_ATTACK_TIME
:
246 *val
= props
->Autowah
.AttackTime
;
249 case AL_AUTOWAH_RELEASE_TIME
:
250 *val
= props
->Autowah
.ReleaseTime
;
253 case AL_AUTOWAH_RESONANCE
:
254 *val
= props
->Autowah
.Resonance
;
257 case AL_AUTOWAH_PEAK_GAIN
:
258 *val
= props
->Autowah
.PeakGain
;
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
);