Silence and fix some clang-tidy warnings
[openal-soft.git] / alc / effects / autowah.cpp
blobb208996e737845838a380bec044398fce2154ec4
1 /**
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
21 #include "config.h"
23 #include <algorithm>
24 #include <array>
25 #include <cmath>
26 #include <cstdlib>
27 #include <variant>
29 #include "alc/effects/base.h"
30 #include "alnumbers.h"
31 #include "alnumeric.h"
32 #include "alspan.h"
33 #include "core/ambidefs.h"
34 #include "core/bufferline.h"
35 #include "core/context.h"
36 #include "core/device.h"
37 #include "core/effects/base.h"
38 #include "core/effectslot.h"
39 #include "core/mixer.h"
40 #include "intrusive_ptr.h"
42 struct BufferStorage;
44 namespace {
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 */
53 float mAttackRate{};
54 float mReleaseRate{};
55 float mResonanceGain{};
56 float mPeakGain{};
57 float mFreqMinNorm{};
58 float mBandwidthNorm{};
59 float mEnvDelay{};
61 /* Filter components derived from the envelope. */
62 struct FilterParam {
63 float cos_w0{};
64 float alpha{};
66 std::array<FilterParam,BufferLineSize> mEnv;
68 struct ChannelData {
69 uint mTargetChannel{InvalidChannelIndex};
71 struct FilterHistory {
72 float z1{}, z2{};
74 FilterHistory mFilter;
76 /* Effect gains for each output channel */
77 float mCurrentGain{};
78 float mTargetGain{};
80 std::array<ChannelData,MaxAmbiChannels> mChans;
82 /* Effects buffers */
83 alignas(16) FloatBufferLine mBufferOut{};
86 void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override;
87 void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
88 const EffectTarget target) override;
89 void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
90 const al::span<FloatBufferLine> samplesOut) override;
93 void AutowahState::deviceUpdate(const DeviceBase*, const BufferStorage*)
95 /* (Re-)initializing parameters and clear the buffers. */
97 mAttackRate = 1.0f;
98 mReleaseRate = 1.0f;
99 mResonanceGain = 10.0f;
100 mPeakGain = 4.5f;
101 mFreqMinNorm = 4.5e-4f;
102 mBandwidthNorm = 0.05f;
103 mEnvDelay = 0.0f;
105 for(auto &e : mEnv)
107 e.cos_w0 = 0.0f;
108 e.alpha = 0.0f;
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 auto &props = std::get<AutowahProps>(*props_);
124 const DeviceBase *device{context->mDevice};
125 const auto frequency = static_cast<float>(device->Frequency);
127 const float ReleaseTime{std::clamp(props.ReleaseTime, 0.001f, 1.0f)};
129 mAttackRate = std::exp(-1.0f / (props.AttackTime*frequency));
130 mReleaseRate = std::exp(-1.0f / (ReleaseTime*frequency));
131 /* 0-20dB Resonance Peak gain */
132 mResonanceGain = std::sqrt(std::log10(props.Resonance)*10.0f / 3.0f);
133 mPeakGain = 1.0f - std::log10(props.PeakGain / GainScale);
134 mFreqMinNorm = MinFreq / frequency;
135 mBandwidthNorm = (MaxFreq-MinFreq) / frequency;
137 mOutTarget = target.Main->Buffer;
138 auto set_channel = [this](size_t idx, uint outchan, float outgain)
140 mChans[idx].mTargetChannel = outchan;
141 mChans[idx].mTargetGain = outgain;
143 target.Main->setAmbiMixParams(slot->Wet, slot->Gain, set_channel);
146 void AutowahState::process(const size_t samplesToDo,
147 const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
149 const float attack_rate{mAttackRate};
150 const float release_rate{mReleaseRate};
151 const float res_gain{mResonanceGain};
152 const float peak_gain{mPeakGain};
153 const float freq_min{mFreqMinNorm};
154 const float bandwidth{mBandwidthNorm};
156 float env_delay{mEnvDelay};
157 for(size_t i{0u};i < samplesToDo;i++)
159 /* Envelope follower described on the book: Audio Effects, Theory,
160 * Implementation and Application.
162 const float sample{peak_gain * std::fabs(samplesIn[0][i])};
163 const float a{(sample > env_delay) ? attack_rate : release_rate};
164 env_delay = lerpf(sample, env_delay, a);
166 /* Calculate the cos and alpha components for this sample's filter. */
167 const float w0{std::min(bandwidth*env_delay + freq_min, 0.46f) *
168 (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 = mChans.begin();
175 for(const auto &insamples : samplesIn)
177 const size_t outidx{chandata->mTargetChannel};
178 if(outidx == InvalidChannelIndex)
180 ++chandata;
181 continue;
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};
198 const std::array b{
199 1.0f + alpha*res_gain,
200 -2.0f * cos_w0,
201 1.0f - alpha*res_gain};
202 const std::array a{
203 1.0f + alpha/res_gain,
204 -2.0f * cos_w0,
205 1.0f - alpha/res_gain};
207 const float input{insamples[i]};
208 const float 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(al::span{mBufferOut}.first(samplesToDo), samplesOut[outidx],
218 chandata->mCurrentGain, chandata->mTargetGain, samplesToDo);
219 ++chandata;
224 struct AutowahStateFactory final : public EffectStateFactory {
225 al::intrusive_ptr<EffectState> create() override
226 { return al::intrusive_ptr<EffectState>{new AutowahState{}}; }
229 } // namespace
231 EffectStateFactory *AutowahStateFactory_getFactory()
233 static AutowahStateFactory AutowahFactory{};
234 return &AutowahFactory;