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 /* Effect filters' history. */
73 /* Effect gains for each output channel */
74 float CurrentGains
[MAX_OUTPUT_CHANNELS
];
75 float TargetGains
[MAX_OUTPUT_CHANNELS
];
76 } mChans
[MaxAmbiChannels
];
79 alignas(16) float mBufferOut
[BufferLineSize
];
82 void deviceUpdate(const DeviceBase
*device
, const Buffer
&buffer
) override
;
83 void update(const ContextBase
*context
, const EffectSlot
*slot
, const EffectProps
*props
,
84 const EffectTarget target
) override
;
85 void process(const size_t samplesToDo
, const al::span
<const FloatBufferLine
> samplesIn
,
86 const al::span
<FloatBufferLine
> samplesOut
) override
;
88 DEF_NEWDEL(AutowahState
)
91 void AutowahState::deviceUpdate(const DeviceBase
*, const Buffer
&)
93 /* (Re-)initializing parameters and clear the buffers. */
97 mResonanceGain
= 10.0f
;
99 mFreqMinNorm
= 4.5e-4f
;
100 mBandwidthNorm
= 0.05f
;
109 for(auto &chan
: mChans
)
111 std::fill(std::begin(chan
.CurrentGains
), std::end(chan
.CurrentGains
), 0.0f
);
112 chan
.Filter
.z1
= 0.0f
;
113 chan
.Filter
.z2
= 0.0f
;
117 void AutowahState::update(const ContextBase
*context
, const EffectSlot
*slot
,
118 const EffectProps
*props
, const EffectTarget target
)
120 const DeviceBase
*device
{context
->mDevice
};
121 const auto frequency
= static_cast<float>(device
->Frequency
);
123 const float ReleaseTime
{clampf(props
->Autowah
.ReleaseTime
, 0.001f
, 1.0f
)};
125 mAttackRate
= std::exp(-1.0f
/ (props
->Autowah
.AttackTime
*frequency
));
126 mReleaseRate
= std::exp(-1.0f
/ (ReleaseTime
*frequency
));
127 /* 0-20dB Resonance Peak gain */
128 mResonanceGain
= std::sqrt(std::log10(props
->Autowah
.Resonance
)*10.0f
/ 3.0f
);
129 mPeakGain
= 1.0f
- std::log10(props
->Autowah
.PeakGain
/ GainScale
);
130 mFreqMinNorm
= MinFreq
/ frequency
;
131 mBandwidthNorm
= (MaxFreq
-MinFreq
) / frequency
;
133 mOutTarget
= target
.Main
->Buffer
;
134 auto set_gains
= [slot
,target
](auto &chan
, al::span
<const float,MaxAmbiChannels
> coeffs
)
135 { ComputePanGains(target
.Main
, coeffs
.data(), slot
->Gain
, chan
.TargetGains
); };
136 SetAmbiPanIdentity(std::begin(mChans
), slot
->Wet
.Buffer
.size(), set_gains
);
139 void AutowahState::process(const size_t samplesToDo
,
140 const al::span
<const FloatBufferLine
> samplesIn
, const al::span
<FloatBufferLine
> samplesOut
)
142 const float attack_rate
{mAttackRate
};
143 const float release_rate
{mReleaseRate
};
144 const float res_gain
{mResonanceGain
};
145 const float peak_gain
{mPeakGain
};
146 const float freq_min
{mFreqMinNorm
};
147 const float bandwidth
{mBandwidthNorm
};
149 float env_delay
{mEnvDelay
};
150 for(size_t i
{0u};i
< samplesToDo
;i
++)
154 /* Envelope follower described on the book: Audio Effects, Theory,
155 * Implementation and Application.
157 sample
= peak_gain
* std::fabs(samplesIn
[0][i
]);
158 a
= (sample
> env_delay
) ? attack_rate
: release_rate
;
159 env_delay
= lerp(sample
, env_delay
, a
);
161 /* Calculate the cos and alpha components for this sample's filter. */
162 w0
= minf((bandwidth
*env_delay
+ freq_min
), 0.46f
) * (al::numbers::pi_v
<float>*2.0f
);
163 mEnv
[i
].cos_w0
= std::cos(w0
);
164 mEnv
[i
].alpha
= std::sin(w0
)/(2.0f
* QFactor
);
166 mEnvDelay
= env_delay
;
168 auto chandata
= std::addressof(mChans
[0]);
169 for(const auto &insamples
: samplesIn
)
171 /* This effectively inlines BiquadFilter_setParams for a peaking
172 * filter and BiquadFilter_processC. The alpha and cosine components
173 * for the filter coefficients were previously calculated with the
174 * envelope. Because the filter changes for each sample, the
175 * coefficients are transient and don't need to be held.
177 float z1
{chandata
->Filter
.z1
};
178 float z2
{chandata
->Filter
.z2
};
180 for(size_t i
{0u};i
< samplesToDo
;i
++)
182 const float alpha
{mEnv
[i
].alpha
};
183 const float cos_w0
{mEnv
[i
].cos_w0
};
187 b
[0] = 1.0f
+ alpha
*res_gain
;
188 b
[1] = -2.0f
* cos_w0
;
189 b
[2] = 1.0f
- alpha
*res_gain
;
190 a
[0] = 1.0f
+ alpha
/res_gain
;
191 a
[1] = -2.0f
* cos_w0
;
192 a
[2] = 1.0f
- alpha
/res_gain
;
194 input
= insamples
[i
];
195 output
= input
*(b
[0]/a
[0]) + z1
;
196 z1
= input
*(b
[1]/a
[0]) - output
*(a
[1]/a
[0]) + z2
;
197 z2
= input
*(b
[2]/a
[0]) - output
*(a
[2]/a
[0]);
198 mBufferOut
[i
] = output
;
200 chandata
->Filter
.z1
= z1
;
201 chandata
->Filter
.z2
= z2
;
203 /* Now, mix the processed sound data to the output. */
204 MixSamples({mBufferOut
, samplesToDo
}, samplesOut
, chandata
->CurrentGains
,
205 chandata
->TargetGains
, samplesToDo
, 0);
211 struct AutowahStateFactory final
: public EffectStateFactory
{
212 al::intrusive_ptr
<EffectState
> create() override
213 { return al::intrusive_ptr
<EffectState
>{new AutowahState
{}}; }
218 EffectStateFactory
*AutowahStateFactory_getFactory()
220 static AutowahStateFactory AutowahFactory
{};
221 return &AutowahFactory
;