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"
30 #include "alnumbers.h"
31 #include "alnumeric.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"
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 */
55 float mResonanceGain
{};
58 float mBandwidthNorm
{};
61 /* Filter components derived from the envelope. */
66 std::array
<FilterParam
,BufferLineSize
> mEnv
;
69 uint mTargetChannel
{InvalidChannelIndex
};
71 struct FilterHistory
{
74 FilterHistory mFilter
;
76 /* Effect gains for each output channel */
80 std::array
<ChannelData
,MaxAmbiChannels
> mChans
;
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. */
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 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
)
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
};
199 1.0f
+ alpha
*res_gain
,
201 1.0f
- alpha
*res_gain
};
203 1.0f
+ alpha
/res_gain
,
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
);
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
;