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
28 #include "alc/effects/base.h"
30 #include "alnumbers.h"
31 #include "alnumeric.h"
33 #include "core/bufferline.h"
34 #include "core/context.h"
35 #include "core/devformat.h"
36 #include "core/device.h"
37 #include "core/effectslot.h"
38 #include "core/filters/biquad.h"
39 #include "core/mixer.h"
40 #include "core/mixer/defs.h"
41 #include "intrusive_ptr.h"
46 struct DistortionState final
: public EffectState
{
47 /* Effect gains for each channel */
48 float mGain
[MaxAmbiChannels
]{};
50 /* Effect parameters */
51 BiquadFilter mLowpass
;
52 BiquadFilter mBandpass
;
56 alignas(16) float mBuffer
[2][BufferLineSize
]{};
59 void deviceUpdate(const DeviceBase
*device
, const Buffer
&buffer
) override
;
60 void update(const ContextBase
*context
, const EffectSlot
*slot
, const EffectProps
*props
,
61 const EffectTarget target
) override
;
62 void process(const size_t samplesToDo
, const al::span
<const FloatBufferLine
> samplesIn
,
63 const al::span
<FloatBufferLine
> samplesOut
) override
;
65 DEF_NEWDEL(DistortionState
)
68 void DistortionState::deviceUpdate(const DeviceBase
*, const Buffer
&)
74 void DistortionState::update(const ContextBase
*context
, const EffectSlot
*slot
,
75 const EffectProps
*props
, const EffectTarget target
)
77 const DeviceBase
*device
{context
->mDevice
};
79 /* Store waveshaper edge settings. */
80 const float edge
{minf(std::sin(al::numbers::pi_v
<float>*0.5f
* props
->Distortion
.Edge
),
82 mEdgeCoeff
= 2.0f
* edge
/ (1.0f
-edge
);
84 float cutoff
{props
->Distortion
.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
->Distortion
.EQCenter
;
94 /* Convert bandwidth in Hz to octaves. */
95 bandwidth
= props
->Distortion
.EQBandwidth
/ (cutoff
* 0.67f
);
96 mBandpass
.setParamsFromBandwidth(BiquadType::BandPass
, cutoff
/frequency
/4.0f
, 1.0f
, bandwidth
);
98 static constexpr auto coeffs
= CalcDirectionCoeffs({0.0f
, 0.0f
, -1.0f
});
100 mOutTarget
= target
.Main
->Buffer
;
101 ComputePanGains(target
.Main
, coeffs
.data(), slot
->Gain
*props
->Distortion
.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
{minz(BufferLineSize
, (samplesToDo
-base
) * 4)};
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({mBuffer
[0], 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::abs(smp
));
137 smp
= (1.0f
+ fc
) * smp
/(1.0f
+ fc
*std::abs(smp
)) * -1.0f
;
138 smp
= (1.0f
+ fc
) * smp
/(1.0f
+ fc
*std::abs(smp
));
141 std::transform(std::begin(mBuffer
[1]), std::begin(mBuffer
[1])+todo
, std::begin(mBuffer
[0]),
144 /* Third step, do bandpass filtering of distorted signal. */
145 mBandpass
.process({mBuffer
[0], todo
}, mBuffer
[1]);
148 const float *outgains
{mGain
};
149 for(FloatBufferLine
&output
: samplesOut
)
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 for(size_t i
{0u};i
< todo
;i
++)
159 output
[base
+i
] += gain
* mBuffer
[1][i
*4];
167 struct DistortionStateFactory final
: public EffectStateFactory
{
168 al::intrusive_ptr
<EffectState
> create() override
169 { return al::intrusive_ptr
<EffectState
>{new DistortionState
{}}; }
174 EffectStateFactory
*DistortionStateFactory_getFactory()
176 static DistortionStateFactory DistortionFactory
{};
177 return &DistortionFactory
;