Avoid some pointer arithmetic
[openal-soft.git] / alc / effects / distortion.cpp
blob6d5ba670b0d994c95d9c4fc5dc775992d236bf9f
1 /**
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
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/filters/biquad.h"
40 #include "core/mixer.h"
41 #include "core/mixer/defs.h"
42 #include "intrusive_ptr.h"
44 struct BufferStorage;
46 namespace {
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;
55 float mAttenuation{};
56 float mEdgeCoeff{};
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*)
70 mLowpass.clear();
71 mBandpass.clear();
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
88 * processing.
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({mBuffer[0].data(), 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));
139 return smp;
141 std::transform(mBuffer[1].begin(), mBuffer[1].begin()+todo, mBuffer[0].begin(),
142 proc_sample);
144 /* Third step, do bandpass filtering of distorted signal. */
145 mBandpass.process({mBuffer[0].data(), todo}, mBuffer[1]);
147 todo >>= 2;
148 auto outgains = mGain.cbegin();
149 for(FloatBufferLine &RESTRICT 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))
156 continue;
158 for(size_t i{0u};i < todo;i++)
159 output[base+i] += gain * mBuffer[1][i*4];
162 base += todo;
167 struct DistortionStateFactory final : public EffectStateFactory {
168 al::intrusive_ptr<EffectState> create() override
169 { return al::intrusive_ptr<EffectState>{new DistortionState{}}; }
172 } // namespace
174 EffectStateFactory *DistortionStateFactory_getFactory()
176 static DistortionStateFactory DistortionFactory{};
177 return &DistortionFactory;