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 "alc/effects/base.h"
31 #include "alnumbers.h"
32 #include "alnumeric.h"
34 #include "core/ambidefs.h"
35 #include "core/bufferline.h"
36 #include "core/context.h"
37 #include "core/devformat.h"
38 #include "core/device.h"
39 #include "core/effectslot.h"
40 #include "core/mixer.h"
41 #include "intrusive_ptr.h"
46 constexpr float GainScale{31621.0f};
47 constexpr float MinFreq{20.0f};
48 constexpr float MaxFreq{2500.0f};
49 constexpr float QFactor{5.0f};
51 struct AutowahState final : public EffectState {
52 /* Effect parameters */
61 /* Filter components derived from the envelope. */
65 } mEnv[BufferLineSize];
68 uint mTargetChannel{InvalidChannelIndex};
70 /* Effect filters' history. */
75 /* Effect gains for each output channel */
78 } mChans[MaxAmbiChannels];
81 alignas(16) float mBufferOut[BufferLineSize];
84 void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override;
85 void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
86 const EffectTarget target) override;
87 void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
88 const al::span<FloatBufferLine> samplesOut) override;
90 DEF_NEWDEL(AutowahState)
93 void AutowahState::deviceUpdate(const DeviceBase*, const BufferStorage*)
95 /* (Re-)initializing parameters and clear the buffers. */
99 mResonanceGain = 10.0f;
101 mFreqMinNorm = 4.5e-4f;
102 mBandwidthNorm = 0.05f;
111 for(auto &chan : mChans)
113 chan.mTargetChannel = InvalidChannelIndex;
114 chan.mFilter.z1 = 0.0f;
115 chan.mFilter.z2 = 0.0f;
116 chan.mCurrentGain = 0.0f;
120 void AutowahState::update(const ContextBase *context, const EffectSlot *slot,
121 const EffectProps *props, const EffectTarget target)
123 const DeviceBase *device{context->mDevice};
124 const auto frequency = static_cast<float>(device->Frequency);
126 const float ReleaseTime{clampf(props->Autowah.ReleaseTime, 0.001f, 1.0f)};
128 mAttackRate = std::exp(-1.0f / (props->Autowah.AttackTime*frequency));
129 mReleaseRate = std::exp(-1.0f / (ReleaseTime*frequency));
130 /* 0-20dB Resonance Peak gain */
131 mResonanceGain = std::sqrt(std::log10(props->Autowah.Resonance)*10.0f / 3.0f);
132 mPeakGain = 1.0f - std::log10(props->Autowah.PeakGain / GainScale);
133 mFreqMinNorm = MinFreq / frequency;
134 mBandwidthNorm = (MaxFreq-MinFreq) / frequency;
136 mOutTarget = target.Main->Buffer;
137 auto set_channel = [this](size_t idx, uint outchan, float outgain)
139 mChans[idx].mTargetChannel = outchan;
140 mChans[idx].mTargetGain = outgain;
142 target.Main->setAmbiMixParams(slot->Wet, slot->Gain, set_channel);
145 void AutowahState::process(const size_t samplesToDo,
146 const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
148 const float attack_rate{mAttackRate};
149 const float release_rate{mReleaseRate};
150 const float res_gain{mResonanceGain};
151 const float peak_gain{mPeakGain};
152 const float freq_min{mFreqMinNorm};
153 const float bandwidth{mBandwidthNorm};
155 float env_delay{mEnvDelay};
156 for(size_t i{0u};i < samplesToDo;i++)
160 /* Envelope follower described on the book: Audio Effects, Theory,
161 * Implementation and Application.
163 sample = peak_gain * std::fabs(samplesIn[0][i]);
164 a = (sample > env_delay) ? attack_rate : release_rate;
165 env_delay = lerpf(sample, env_delay, a);
167 /* Calculate the cos and alpha components for this sample's filter. */
168 w0 = minf((bandwidth*env_delay + freq_min), 0.46f) * (al::numbers::pi_v<float>*2.0f);
169 mEnv[i].cos_w0 = std::cos(w0);
170 mEnv[i].alpha = std::sin(w0)/(2.0f * QFactor);
172 mEnvDelay = env_delay;
174 auto chandata = std::begin(mChans);
175 for(const auto &insamples : samplesIn)
177 const size_t outidx{chandata->mTargetChannel};
178 if(outidx == InvalidChannelIndex)
184 /* This effectively inlines BiquadFilter_setParams for a peaking
185 * filter and BiquadFilter_processC. The alpha and cosine components
186 * for the filter coefficients were previously calculated with the
187 * envelope. Because the filter changes for each sample, the
188 * coefficients are transient and don't need to be held.
190 float z1{chandata->mFilter.z1};
191 float z2{chandata->mFilter.z2};
193 for(size_t i{0u};i < samplesToDo;i++)
195 const float alpha{mEnv[i].alpha};
196 const float cos_w0{mEnv[i].cos_w0};
200 b[0] = 1.0f + alpha*res_gain;
201 b[1] = -2.0f * cos_w0;
202 b[2] = 1.0f - alpha*res_gain;
203 a[0] = 1.0f + alpha/res_gain;
204 a[1] = -2.0f * cos_w0;
205 a[2] = 1.0f - alpha/res_gain;
207 input = insamples[i];
208 output = input*(b[0]/a[0]) + z1;
209 z1 = input*(b[1]/a[0]) - output*(a[1]/a[0]) + z2;
210 z2 = input*(b[2]/a[0]) - output*(a[2]/a[0]);
211 mBufferOut[i] = output;
213 chandata->mFilter.z1 = z1;
214 chandata->mFilter.z2 = z2;
216 /* Now, mix the processed sound data to the output. */
217 MixSamples({mBufferOut, samplesToDo}, samplesOut[outidx].data(), chandata->mCurrentGain,
218 chandata->mTargetGain, samplesToDo);
224 struct AutowahStateFactory final : public EffectStateFactory {
225 al::intrusive_ptr<EffectState> create() override
226 { return al::intrusive_ptr<EffectState>{new AutowahState{}}; }
231 EffectStateFactory *AutowahStateFactory_getFactory()
233 static AutowahStateFactory AutowahFactory{};
234 return &AutowahFactory;