2 * OpenAL cross platform audio library
3 * Copyright (C) 2013 by Mike Gorchak
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/filters/biquad.h"
40 #include "core/mixer.h"
41 #include "core/mixer/defs.h"
42 #include "intrusive_ptr.h"
48 struct DistortionState final
: public EffectState
{
49 /* Effect gains for each channel */
50 std::array
<float,MaxAmbiChannels
> mGain
{};
52 /* Effect parameters */
53 BiquadFilter mLowpass
;
54 BiquadFilter mBandpass
;
58 alignas(16) std::array
<FloatBufferLine
,2> mBuffer
{};
61 void deviceUpdate(const DeviceBase
*device
, const BufferStorage
*buffer
) override
;
62 void update(const ContextBase
*context
, const EffectSlot
*slot
, const EffectProps
*props
,
63 const EffectTarget target
) override
;
64 void process(const size_t samplesToDo
, const al::span
<const FloatBufferLine
> samplesIn
,
65 const al::span
<FloatBufferLine
> samplesOut
) override
;
68 void DistortionState::deviceUpdate(const DeviceBase
*, const BufferStorage
*)
74 void DistortionState::update(const ContextBase
*context
, const EffectSlot
*slot
,
75 const EffectProps
*props_
, const EffectTarget target
)
77 auto &props
= std::get
<DistortionProps
>(*props_
);
78 const DeviceBase
*device
{context
->mDevice
};
80 /* Store waveshaper edge settings. */
81 const float edge
{std::min(std::sin(al::numbers::pi_v
<float>*0.5f
* props
.Edge
), 0.99f
)};
82 mEdgeCoeff
= 2.0f
* edge
/ (1.0f
-edge
);
84 float cutoff
{props
.LowpassCutoff
};
85 /* Bandwidth value is constant in octaves. */
86 float bandwidth
{(cutoff
/ 2.0f
) / (cutoff
* 0.67f
)};
87 /* Divide normalized frequency by the amount of oversampling done during
90 auto frequency
= static_cast<float>(device
->Frequency
);
91 mLowpass
.setParamsFromBandwidth(BiquadType::LowPass
, cutoff
/frequency
/4.0f
, 1.0f
, bandwidth
);
93 cutoff
= props
.EQCenter
;
94 /* Convert bandwidth in Hz to octaves. */
95 bandwidth
= props
.EQBandwidth
/ (cutoff
* 0.67f
);
96 mBandpass
.setParamsFromBandwidth(BiquadType::BandPass
, cutoff
/frequency
/4.0f
, 1.0f
, bandwidth
);
98 static constexpr auto coeffs
= CalcDirectionCoeffs(std::array
{0.0f
, 0.0f
, -1.0f
});
100 mOutTarget
= target
.Main
->Buffer
;
101 ComputePanGains(target
.Main
, coeffs
, slot
->Gain
*props
.Gain
, mGain
);
104 void DistortionState::process(const size_t samplesToDo
, const al::span
<const FloatBufferLine
> samplesIn
, const al::span
<FloatBufferLine
> samplesOut
)
106 const float fc
{mEdgeCoeff
};
107 for(size_t base
{0u};base
< samplesToDo
;)
109 /* Perform 4x oversampling to avoid aliasing. Oversampling greatly
110 * improves distortion quality and allows to implement lowpass and
111 * bandpass filters using high frequencies, at which classic IIR
112 * filters became unstable.
114 size_t todo
{std::min(BufferLineSize
, (samplesToDo
-base
) * 4_uz
)};
116 /* Fill oversample buffer using zero stuffing. Multiply the sample by
117 * the amount of oversampling to maintain the signal's power.
119 for(size_t i
{0u};i
< todo
;i
++)
120 mBuffer
[0][i
] = !(i
&3) ? samplesIn
[0][(i
>>2)+base
] * 4.0f
: 0.0f
;
122 /* First step, do lowpass filtering of original signal. Additionally
123 * perform buffer interpolation and lowpass cutoff for oversampling
124 * (which is fortunately first step of distortion). So combine three
125 * operations into the one.
127 mLowpass
.process(al::span
{mBuffer
[0]}.first(todo
), mBuffer
[1]);
129 /* Second step, do distortion using waveshaper function to emulate
130 * signal processing during tube overdriving. Three steps of
131 * waveshaping are intended to modify waveform without boost/clipping/
132 * attenuation process.
134 auto proc_sample
= [fc
](float smp
) -> float
136 smp
= (1.0f
+ fc
) * smp
/(1.0f
+ fc
*std::fabs(smp
));
137 smp
= (1.0f
+ fc
) * smp
/(1.0f
+ fc
*std::fabs(smp
)) * -1.0f
;
138 smp
= (1.0f
+ fc
) * smp
/(1.0f
+ fc
*std::fabs(smp
));
141 std::transform(mBuffer
[1].begin(), mBuffer
[1].begin()+todo
, mBuffer
[0].begin(),
144 /* Third step, do bandpass filtering of distorted signal. */
145 mBandpass
.process(al::span
{mBuffer
[0]}.first(todo
), mBuffer
[1]);
148 auto outgains
= mGain
.cbegin();
149 auto proc_bufline
= [this,base
,todo
,&outgains
](FloatBufferSpan output
)
151 /* Fourth step, final, do attenuation and perform decimation,
152 * storing only one sample out of four.
154 const float gain
{*(outgains
++)};
155 if(!(std::fabs(gain
) > GainSilenceThreshold
))
158 auto src
= mBuffer
[1].cbegin();
159 const auto dst
= al::span
{output
}.subspan(base
, todo
);
160 auto dec_sample
= [gain
,&src
](float sample
) noexcept
-> float
162 sample
+= *src
* gain
;
166 std::transform(dst
.begin(), dst
.end(), dst
.begin(), dec_sample
);
168 std::for_each(samplesOut
.begin(), samplesOut
.end(), proc_bufline
);
175 struct DistortionStateFactory final
: public EffectStateFactory
{
176 al::intrusive_ptr
<EffectState
> create() override
177 { return al::intrusive_ptr
<EffectState
>{new DistortionState
{}}; }
182 EffectStateFactory
*DistortionStateFactory_getFactory()
184 static DistortionStateFactory DistortionFactory
{};
185 return &DistortionFactory
;