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
*state
)
58 ALeffectState_Destruct(STATIC_CAST(ALeffectState
,state
));
61 static ALboolean
ALautowahState_deviceUpdate(ALautowahState
*state
, ALCdevice
*device
)
63 state
->Frequency
= (ALfloat
)device
->Frequency
;
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
)
87 for(base
= 0;base
< SamplesToDo
;)
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
];
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
);
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
;
126 a
[1] = -2.0f
* cosf(w0
);
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
))
145 for(it
= 0;it
< td
;it
++)
146 SamplesOut
[kt
][base
+it
] += gain
* temps
[it
];
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
;
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
;
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
;
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
;
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
;
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
;
246 case AL_AUTOWAH_ATTACK_TIME
:
247 *val
= props
->Autowah
.AttackTime
;
250 case AL_AUTOWAH_RELEASE_TIME
:
251 *val
= props
->Autowah
.ReleaseTime
;
254 case AL_AUTOWAH_RESONANCE
:
255 *val
= props
->Autowah
.Resonance
;
258 case AL_AUTOWAH_PEAK_GAIN
:
259 *val
= props
->Autowah
.PeakGain
;
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
);