2 * OpenAL cross platform audio library
3 * Copyright (C) 2018 by Raul Herraiz.
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
29 #include "alcontext.h"
30 #include "alAuxEffectSlot.h"
33 #include "filters/biquad.h"
36 #define MIN_FREQ 20.0f
37 #define MAX_FREQ 2500.0f
40 struct ALautowahState final
: public EffectState
{
41 /* Effect parameters */
44 ALfloat mResonanceGain
;
47 ALfloat mBandwidthNorm
;
50 /* Filter components derived from the envelope. */
57 /* Effect filters' history. */
62 /* Effect gains for each output channel */
63 ALfloat CurrentGains
[MAX_OUTPUT_CHANNELS
];
64 ALfloat TargetGains
[MAX_OUTPUT_CHANNELS
];
65 } mChans
[MAX_EFFECT_CHANNELS
];
68 alignas(16) ALfloat mBufferOut
[BUFFERSIZE
];
71 ALboolean
deviceUpdate(const ALCdevice
*device
) override
;
72 void update(const ALCcontext
*context
, const ALeffectslot
*slot
, const ALeffectProps
*props
, const EffectTarget target
) override
;
73 void process(ALsizei samplesToDo
, const ALfloat (*RESTRICT samplesIn
)[BUFFERSIZE
], ALfloat (*RESTRICT samplesOut
)[BUFFERSIZE
], ALsizei numChannels
) override
;
75 DEF_NEWDEL(ALautowahState
)
78 ALboolean
ALautowahState::deviceUpdate(const ALCdevice
*UNUSED(device
))
80 /* (Re-)initializing parameters and clear the buffers. */
84 mResonanceGain
= 10.0f
;
86 mFreqMinNorm
= 4.5e-4f
;
87 mBandwidthNorm
= 0.05f
;
96 for(auto &chan
: mChans
)
98 std::fill(std::begin(chan
.CurrentGains
), std::end(chan
.CurrentGains
), 0.0f
);
99 chan
.Filter
.z1
= 0.0f
;
100 chan
.Filter
.z2
= 0.0f
;
106 void ALautowahState::update(const ALCcontext
*context
, const ALeffectslot
*slot
, const ALeffectProps
*props
, const EffectTarget target
)
108 const ALCdevice
*device
= context
->Device
;
112 ReleaseTime
= clampf(props
->Autowah
.ReleaseTime
, 0.001f
, 1.0f
);
114 mAttackRate
= expf(-1.0f
/ (props
->Autowah
.AttackTime
*device
->Frequency
));
115 mReleaseRate
= expf(-1.0f
/ (ReleaseTime
*device
->Frequency
));
116 /* 0-20dB Resonance Peak gain */
117 mResonanceGain
= sqrtf(log10f(props
->Autowah
.Resonance
)*10.0f
/ 3.0f
);
118 mPeakGain
= 1.0f
- log10f(props
->Autowah
.PeakGain
/AL_AUTOWAH_MAX_PEAK_GAIN
);
119 mFreqMinNorm
= MIN_FREQ
/ device
->Frequency
;
120 mBandwidthNorm
= (MAX_FREQ
-MIN_FREQ
) / device
->Frequency
;
122 mOutBuffer
= target
.FOAOut
->Buffer
;
123 mOutChannels
= target
.FOAOut
->NumChannels
;
124 for(i
= 0;i
< MAX_EFFECT_CHANNELS
;i
++)
125 ComputePanGains(target
.FOAOut
, alu::Matrix::Identity()[i
].data(), slot
->Params
.Gain
,
126 mChans
[i
].TargetGains
);
129 void ALautowahState::process(ALsizei SamplesToDo
, const ALfloat (*RESTRICT SamplesIn
)[BUFFERSIZE
], ALfloat (*RESTRICT SamplesOut
)[BUFFERSIZE
], ALsizei NumChannels
)
131 const ALfloat attack_rate
= mAttackRate
;
132 const ALfloat release_rate
= mReleaseRate
;
133 const ALfloat res_gain
= mResonanceGain
;
134 const ALfloat peak_gain
= mPeakGain
;
135 const ALfloat freq_min
= mFreqMinNorm
;
136 const ALfloat bandwidth
= mBandwidthNorm
;
140 env_delay
= mEnvDelay
;
141 for(i
= 0;i
< SamplesToDo
;i
++)
143 ALfloat w0
, sample
, a
;
145 /* Envelope follower described on the book: Audio Effects, Theory,
146 * Implementation and Application.
148 sample
= peak_gain
* fabsf(SamplesIn
[0][i
]);
149 a
= (sample
> env_delay
) ? attack_rate
: release_rate
;
150 env_delay
= lerp(sample
, env_delay
, a
);
152 /* Calculate the cos and alpha components for this sample's filter. */
153 w0
= minf((bandwidth
*env_delay
+ freq_min
), 0.46f
) * al::MathDefs
<float>::Tau();
154 mEnv
[i
].cos_w0
= cosf(w0
);
155 mEnv
[i
].alpha
= sinf(w0
)/(2.0f
* Q_FACTOR
);
157 mEnvDelay
= env_delay
;
159 for(c
= 0;c
< MAX_EFFECT_CHANNELS
; c
++)
161 /* This effectively inlines BiquadFilter_setParams for a peaking
162 * filter and BiquadFilter_processC. The alpha and cosine components
163 * for the filter coefficients were previously calculated with the
164 * envelope. Because the filter changes for each sample, the
165 * coefficients are transient and don't need to be held.
167 ALfloat z1
= mChans
[c
].Filter
.z1
;
168 ALfloat z2
= mChans
[c
].Filter
.z2
;
170 for(i
= 0;i
< SamplesToDo
;i
++)
172 const ALfloat alpha
= mEnv
[i
].alpha
;
173 const ALfloat cos_w0
= mEnv
[i
].cos_w0
;
174 ALfloat input
, output
;
177 b
[0] = 1.0f
+ alpha
*res_gain
;
178 b
[1] = -2.0f
* cos_w0
;
179 b
[2] = 1.0f
- alpha
*res_gain
;
180 a
[0] = 1.0f
+ alpha
/res_gain
;
181 a
[1] = -2.0f
* cos_w0
;
182 a
[2] = 1.0f
- alpha
/res_gain
;
184 input
= SamplesIn
[c
][i
];
185 output
= input
*(b
[0]/a
[0]) + z1
;
186 z1
= input
*(b
[1]/a
[0]) - output
*(a
[1]/a
[0]) + z2
;
187 z2
= input
*(b
[2]/a
[0]) - output
*(a
[2]/a
[0]);
188 mBufferOut
[i
] = output
;
190 mChans
[c
].Filter
.z1
= z1
;
191 mChans
[c
].Filter
.z2
= z2
;
193 /* Now, mix the processed sound data to the output. */
194 MixSamples(mBufferOut
, NumChannels
, SamplesOut
, mChans
[c
].CurrentGains
,
195 mChans
[c
].TargetGains
, SamplesToDo
, 0, SamplesToDo
);
199 struct AutowahStateFactory final
: public EffectStateFactory
{
200 EffectState
*create() override
;
203 EffectState
*AutowahStateFactory::create()
204 { return new ALautowahState
{}; }
206 EffectStateFactory
*AutowahStateFactory_getFactory(void)
208 static AutowahStateFactory AutowahFactory
{};
209 return &AutowahFactory
;
213 void ALautowah_setParamf(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat val
)
215 ALeffectProps
*props
= &effect
->Props
;
218 case AL_AUTOWAH_ATTACK_TIME
:
219 if(!(val
>= AL_AUTOWAH_MIN_ATTACK_TIME
&& val
<= AL_AUTOWAH_MAX_ATTACK_TIME
))
220 SETERR_RETURN(context
, AL_INVALID_VALUE
,,"Autowah attack time out of range");
221 props
->Autowah
.AttackTime
= val
;
224 case AL_AUTOWAH_RELEASE_TIME
:
225 if(!(val
>= AL_AUTOWAH_MIN_RELEASE_TIME
&& val
<= AL_AUTOWAH_MAX_RELEASE_TIME
))
226 SETERR_RETURN(context
, AL_INVALID_VALUE
,,"Autowah release time out of range");
227 props
->Autowah
.ReleaseTime
= val
;
230 case AL_AUTOWAH_RESONANCE
:
231 if(!(val
>= AL_AUTOWAH_MIN_RESONANCE
&& val
<= AL_AUTOWAH_MAX_RESONANCE
))
232 SETERR_RETURN(context
, AL_INVALID_VALUE
,,"Autowah resonance out of range");
233 props
->Autowah
.Resonance
= val
;
236 case AL_AUTOWAH_PEAK_GAIN
:
237 if(!(val
>= AL_AUTOWAH_MIN_PEAK_GAIN
&& val
<= AL_AUTOWAH_MAX_PEAK_GAIN
))
238 SETERR_RETURN(context
, AL_INVALID_VALUE
,,"Autowah peak gain out of range");
239 props
->Autowah
.PeakGain
= val
;
243 alSetError(context
, AL_INVALID_ENUM
, "Invalid autowah float property 0x%04x", param
);
247 void ALautowah_setParamfv(ALeffect
*effect
, ALCcontext
*context
, ALenum param
, const ALfloat
*vals
)
249 ALautowah_setParamf(effect
, context
, param
, vals
[0]);
252 void ALautowah_setParami(ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, ALint
UNUSED(val
))
254 alSetError(context
, AL_INVALID_ENUM
, "Invalid autowah integer property 0x%04x", param
);
257 void ALautowah_setParamiv(ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, const ALint
*UNUSED(vals
))
259 alSetError(context
, AL_INVALID_ENUM
, "Invalid autowah integer vector property 0x%04x", param
);
262 void ALautowah_getParami(const ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, ALint
*UNUSED(val
))
264 alSetError(context
, AL_INVALID_ENUM
, "Invalid autowah integer property 0x%04x", param
);
266 void ALautowah_getParamiv(const ALeffect
*UNUSED(effect
), ALCcontext
*context
, ALenum param
, ALint
*UNUSED(vals
))
268 alSetError(context
, AL_INVALID_ENUM
, "Invalid autowah integer vector property 0x%04x", param
);
271 void ALautowah_getParamf(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*val
)
274 const ALeffectProps
*props
= &effect
->Props
;
277 case AL_AUTOWAH_ATTACK_TIME
:
278 *val
= props
->Autowah
.AttackTime
;
281 case AL_AUTOWAH_RELEASE_TIME
:
282 *val
= props
->Autowah
.ReleaseTime
;
285 case AL_AUTOWAH_RESONANCE
:
286 *val
= props
->Autowah
.Resonance
;
289 case AL_AUTOWAH_PEAK_GAIN
:
290 *val
= props
->Autowah
.PeakGain
;
294 alSetError(context
, AL_INVALID_ENUM
, "Invalid autowah float property 0x%04x", param
);
299 void ALautowah_getParamfv(const ALeffect
*effect
, ALCcontext
*context
, ALenum param
, ALfloat
*vals
)
301 ALautowah_getParamf(effect
, context
, param
, vals
);
304 DEFINE_ALEFFECT_VTABLE(ALautowah
);