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
28 #include "al/auxeffectslot.h"
30 #include "alcontext.h"
32 #include "filters/biquad.h"
37 #define MIN_FREQ 20.0f
38 #define MAX_FREQ 2500.0f
41 struct AutowahState final
: public EffectState
{
42 /* Effect parameters */
51 /* Filter components derived from the envelope. */
58 /* Effect filters' history. */
63 /* Effect gains for each output channel */
64 float CurrentGains
[MAX_OUTPUT_CHANNELS
];
65 float TargetGains
[MAX_OUTPUT_CHANNELS
];
66 } mChans
[MAX_AMBI_CHANNELS
];
69 alignas(16) float mBufferOut
[BUFFERSIZE
];
72 void deviceUpdate(const ALCdevice
*device
) override
;
73 void update(const ALCcontext
*context
, const ALeffectslot
*slot
, const EffectProps
*props
, const EffectTarget target
) override
;
74 void process(const size_t samplesToDo
, const al::span
<const FloatBufferLine
> samplesIn
, const al::span
<FloatBufferLine
> samplesOut
) override
;
76 DEF_NEWDEL(AutowahState
)
79 void AutowahState::deviceUpdate(const ALCdevice
*)
81 /* (Re-)initializing parameters and clear the buffers. */
85 mResonanceGain
= 10.0f
;
87 mFreqMinNorm
= 4.5e-4f
;
88 mBandwidthNorm
= 0.05f
;
97 for(auto &chan
: mChans
)
99 std::fill(std::begin(chan
.CurrentGains
), std::end(chan
.CurrentGains
), 0.0f
);
100 chan
.Filter
.z1
= 0.0f
;
101 chan
.Filter
.z2
= 0.0f
;
105 void AutowahState::update(const ALCcontext
*context
, const ALeffectslot
*slot
, const EffectProps
*props
, const EffectTarget target
)
107 const ALCdevice
*device
{context
->mDevice
.get()};
108 const auto frequency
= static_cast<float>(device
->Frequency
);
110 const float ReleaseTime
{clampf(props
->Autowah
.ReleaseTime
, 0.001f
, 1.0f
)};
112 mAttackRate
= std::exp(-1.0f
/ (props
->Autowah
.AttackTime
*frequency
));
113 mReleaseRate
= std::exp(-1.0f
/ (ReleaseTime
*frequency
));
114 /* 0-20dB Resonance Peak gain */
115 mResonanceGain
= std::sqrt(std::log10(props
->Autowah
.Resonance
)*10.0f
/ 3.0f
);
116 mPeakGain
= 1.0f
- std::log10(props
->Autowah
.PeakGain
/AL_AUTOWAH_MAX_PEAK_GAIN
);
117 mFreqMinNorm
= MIN_FREQ
/ frequency
;
118 mBandwidthNorm
= (MAX_FREQ
-MIN_FREQ
) / frequency
;
120 mOutTarget
= target
.Main
->Buffer
;
121 auto set_gains
= [slot
,target
](auto &chan
, al::span
<const float,MAX_AMBI_CHANNELS
> coeffs
)
122 { ComputePanGains(target
.Main
, coeffs
.data(), slot
->Params
.Gain
, chan
.TargetGains
); };
123 SetAmbiPanIdentity(std::begin(mChans
), slot
->Wet
.Buffer
.size(), set_gains
);
126 void AutowahState::process(const size_t samplesToDo
, const al::span
<const FloatBufferLine
> samplesIn
, const al::span
<FloatBufferLine
> samplesOut
)
128 const float attack_rate
{mAttackRate
};
129 const float release_rate
{mReleaseRate
};
130 const float res_gain
{mResonanceGain
};
131 const float peak_gain
{mPeakGain
};
132 const float freq_min
{mFreqMinNorm
};
133 const float bandwidth
{mBandwidthNorm
};
135 float env_delay
{mEnvDelay
};
136 for(size_t i
{0u};i
< samplesToDo
;i
++)
140 /* Envelope follower described on the book: Audio Effects, Theory,
141 * Implementation and Application.
143 sample
= peak_gain
* std::fabs(samplesIn
[0][i
]);
144 a
= (sample
> env_delay
) ? attack_rate
: release_rate
;
145 env_delay
= lerp(sample
, env_delay
, a
);
147 /* Calculate the cos and alpha components for this sample's filter. */
148 w0
= minf((bandwidth
*env_delay
+ freq_min
), 0.46f
) * al::MathDefs
<float>::Tau();
149 mEnv
[i
].cos_w0
= std::cos(w0
);
150 mEnv
[i
].alpha
= std::sin(w0
)/(2.0f
* Q_FACTOR
);
152 mEnvDelay
= env_delay
;
154 auto chandata
= std::addressof(mChans
[0]);
155 for(const auto &insamples
: samplesIn
)
157 /* This effectively inlines BiquadFilter_setParams for a peaking
158 * filter and BiquadFilter_processC. The alpha and cosine components
159 * for the filter coefficients were previously calculated with the
160 * envelope. Because the filter changes for each sample, the
161 * coefficients are transient and don't need to be held.
163 float z1
{chandata
->Filter
.z1
};
164 float z2
{chandata
->Filter
.z2
};
166 for(size_t i
{0u};i
< samplesToDo
;i
++)
168 const float alpha
{mEnv
[i
].alpha
};
169 const float cos_w0
{mEnv
[i
].cos_w0
};
173 b
[0] = 1.0f
+ alpha
*res_gain
;
174 b
[1] = -2.0f
* cos_w0
;
175 b
[2] = 1.0f
- alpha
*res_gain
;
176 a
[0] = 1.0f
+ alpha
/res_gain
;
177 a
[1] = -2.0f
* cos_w0
;
178 a
[2] = 1.0f
- alpha
/res_gain
;
180 input
= insamples
[i
];
181 output
= input
*(b
[0]/a
[0]) + z1
;
182 z1
= input
*(b
[1]/a
[0]) - output
*(a
[1]/a
[0]) + z2
;
183 z2
= input
*(b
[2]/a
[0]) - output
*(a
[2]/a
[0]);
184 mBufferOut
[i
] = output
;
186 chandata
->Filter
.z1
= z1
;
187 chandata
->Filter
.z2
= z2
;
189 /* Now, mix the processed sound data to the output. */
190 MixSamples({mBufferOut
, samplesToDo
}, samplesOut
, chandata
->CurrentGains
,
191 chandata
->TargetGains
, samplesToDo
, 0);
197 void Autowah_setParamf(EffectProps
*props
, ALenum param
, float val
)
201 case AL_AUTOWAH_ATTACK_TIME
:
202 if(!(val
>= AL_AUTOWAH_MIN_ATTACK_TIME
&& val
<= AL_AUTOWAH_MAX_ATTACK_TIME
))
203 throw effect_exception
{AL_INVALID_VALUE
, "Autowah attack time out of range"};
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 throw effect_exception
{AL_INVALID_VALUE
, "Autowah release time out of range"};
210 props
->Autowah
.ReleaseTime
= val
;
213 case AL_AUTOWAH_RESONANCE
:
214 if(!(val
>= AL_AUTOWAH_MIN_RESONANCE
&& val
<= AL_AUTOWAH_MAX_RESONANCE
))
215 throw effect_exception
{AL_INVALID_VALUE
, "Autowah resonance out of range"};
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 throw effect_exception
{AL_INVALID_VALUE
, "Autowah peak gain out of range"};
222 props
->Autowah
.PeakGain
= val
;
226 throw effect_exception
{AL_INVALID_ENUM
, "Invalid autowah float property 0x%04x", param
};
229 void Autowah_setParamfv(EffectProps
*props
, ALenum param
, const float *vals
)
230 { Autowah_setParamf(props
, param
, vals
[0]); }
232 void Autowah_setParami(EffectProps
*, ALenum param
, int)
233 { throw effect_exception
{AL_INVALID_ENUM
, "Invalid autowah integer property 0x%04x", param
}; }
234 void Autowah_setParamiv(EffectProps
*, ALenum param
, const int*)
236 throw effect_exception
{AL_INVALID_ENUM
, "Invalid autowah integer vector property 0x%04x",
240 void Autowah_getParamf(const EffectProps
*props
, ALenum param
, float *val
)
244 case AL_AUTOWAH_ATTACK_TIME
:
245 *val
= props
->Autowah
.AttackTime
;
248 case AL_AUTOWAH_RELEASE_TIME
:
249 *val
= props
->Autowah
.ReleaseTime
;
252 case AL_AUTOWAH_RESONANCE
:
253 *val
= props
->Autowah
.Resonance
;
256 case AL_AUTOWAH_PEAK_GAIN
:
257 *val
= props
->Autowah
.PeakGain
;
261 throw effect_exception
{AL_INVALID_ENUM
, "Invalid autowah float property 0x%04x", param
};
265 void Autowah_getParamfv(const EffectProps
*props
, ALenum param
, float *vals
)
266 { Autowah_getParamf(props
, param
, vals
); }
268 void Autowah_getParami(const EffectProps
*, ALenum param
, int*)
269 { throw effect_exception
{AL_INVALID_ENUM
, "Invalid autowah integer property 0x%04x", param
}; }
270 void Autowah_getParamiv(const EffectProps
*, ALenum param
, int*)
272 throw effect_exception
{AL_INVALID_ENUM
, "Invalid autowah integer vector property 0x%04x",
276 DEFINE_ALEFFECT_VTABLE(Autowah
);
279 struct AutowahStateFactory final
: public EffectStateFactory
{
280 EffectState
*create() override
{ return new AutowahState
{}; }
281 EffectProps
getDefaultProps() const noexcept override
;
282 const EffectVtable
*getEffectVtable() const noexcept override
{ return &Autowah_vtable
; }
285 EffectProps
AutowahStateFactory::getDefaultProps() const noexcept
288 props
.Autowah
.AttackTime
= AL_AUTOWAH_DEFAULT_ATTACK_TIME
;
289 props
.Autowah
.ReleaseTime
= AL_AUTOWAH_DEFAULT_RELEASE_TIME
;
290 props
.Autowah
.Resonance
= AL_AUTOWAH_DEFAULT_RESONANCE
;
291 props
.Autowah
.PeakGain
= AL_AUTOWAH_DEFAULT_PEAK_GAIN
;
297 EffectStateFactory
*AutowahStateFactory_getFactory()
299 static AutowahStateFactory AutowahFactory
{};
300 return &AutowahFactory
;