BogaudioModules

BogaudioModules for VCV Rack
Log | Files | Refs | README | LICENSE

commit 27a19dcf8fbc49b2b529a2750b972d0ada2d09cf
parent f7647dc2eb0c50fb731273d5ffa249f6e7706922
Author: Matt Demanett <matt@demanett.net>
Date:   Thu, 14 Jun 2018 22:53:27 -0400

Saturation DSP; apply on VCAmp and Mix4/8.

Diffstat:
Mbenchmarks/signal_benchmark.cpp | 16++++++++++++++++
Msrc/Mix4.cpp | 3+++
Msrc/Mix4.hpp | 1+
Msrc/Mix8.cpp | 3+++
Msrc/Mix8.hpp | 1+
Msrc/Test.cpp | 5+++++
Msrc/Test.hpp | 8+++++++-
Msrc/VCAmp.cpp | 2+-
Msrc/VCAmp.hpp | 1+
Msrc/dsp/signal.cpp | 18++++++++++++++++++
Msrc/dsp/signal.hpp | 6++++++
11 files changed, 62 insertions(+), 2 deletions(-)

diff --git a/benchmarks/signal_benchmark.cpp b/benchmarks/signal_benchmark.cpp @@ -202,3 +202,19 @@ static void BM_DelayLine(benchmark::State& state) { } } BENCHMARK(BM_DelayLine); + +static void BM_Saturator(benchmark::State& state) { + SineOscillator o(500.0, 100.0); + const int n = 256; + float buf[n]; + for (int i = 0; i < n; ++i) { + buf[i] = o.next() * 20.0f; + } + Saturator s; + int i = 0; + for (auto _ : state) { + i = ++i % n; + benchmark::DoNotOptimize(s.next(buf[i])); + } +} +BENCHMARK(BM_Saturator); diff --git a/src/Mix4.cpp b/src/Mix4.cpp @@ -32,6 +32,7 @@ void Mix4::step() { mono += _channel3.out; mono += _channel4.out; mono = _amplifier.next(mono); + mono = _saturator.next(mono); _rmsLevel = _rms.next(mono) / 5.0f; if (stereo) { @@ -41,6 +42,7 @@ void Mix4::step() { left += _channel3.left; left += _channel4.left; left = _amplifier.next(left); + left = _saturator.next(left); outputs[L_OUTPUT].value = left; float right = 0.0f; @@ -49,6 +51,7 @@ void Mix4::step() { right += _channel3.right; right += _channel4.right; right = _amplifier.next(right); + right = _saturator.next(right); outputs[R_OUTPUT].value = right; } else { diff --git a/src/Mix4.hpp b/src/Mix4.hpp @@ -61,6 +61,7 @@ struct Mix4 : Module { MixerChannel _channel4; Amplifier _amplifier; SlewLimiter _slewLimiter; + Saturator _saturator; RootMeanSquare _rms; float _rmsLevel = 0.0f; diff --git a/src/Mix8.cpp b/src/Mix8.cpp @@ -44,6 +44,7 @@ void Mix8::step() { mono += _channel7.out; mono += _channel8.out; mono = _amplifier.next(mono); + mono = _saturator.next(mono); _rmsLevel = _rms.next(mono) / 5.0f; if (stereo) { @@ -57,6 +58,7 @@ void Mix8::step() { left += _channel7.left; left += _channel8.left; left = _amplifier.next(left); + left = _saturator.next(left); outputs[L_OUTPUT].value = left; float right = 0.0f; @@ -69,6 +71,7 @@ void Mix8::step() { right += _channel7.right; right += _channel8.right; right = _amplifier.next(right); + right = _saturator.next(right); outputs[R_OUTPUT].value = right; } else { diff --git a/src/Mix8.hpp b/src/Mix8.hpp @@ -89,6 +89,7 @@ struct Mix8 : Module { MixerChannel _channel8; Amplifier _amplifier; SlewLimiter _slewLimiter; + Saturator _saturator; RootMeanSquare _rms; float _rmsLevel = 0.0f; diff --git a/src/Test.cpp b/src/Test.cpp @@ -383,6 +383,11 @@ void Test::step() { _average.reset(); } outputs[OUT_OUTPUT].value = _average.next(inputs[IN_INPUT].value); + +#elif SATURATOR + float in = inputs[IN_INPUT].value; + outputs[OUT_OUTPUT].value = _saturator.next(in); + outputs[OUT2_OUTPUT].value = clamp(in, -Saturator::limit, Saturator::limit); #endif } diff --git a/src/Test.hpp b/src/Test.hpp @@ -24,7 +24,8 @@ extern Model* modelTest; // #define TABLES 1 // #define SLEW 1 // #define RMS 1 -#define RAVG 1 +// #define RAVG 1 +#define SATURATOR 1 #include "pitch.hpp" #ifdef LPF @@ -78,6 +79,9 @@ extern Model* modelTest; #include "dsp/signal.hpp" #elif RAVG #include "dsp/signal.hpp" +#elif SATURATOR +#include "dsp/oscillator.hpp" +#include "dsp/signal.hpp" #else #error what #endif @@ -196,6 +200,8 @@ struct Test : Module { #elif RAVG RunningAverage _average; SchmittTrigger _reset; +#elif SATURATOR + Saturator _saturator; #endif Test() diff --git a/src/VCAmp.cpp b/src/VCAmp.cpp @@ -16,7 +16,7 @@ void VCAmp::step() { level *= maxDecibels - minDecibels; level += minDecibels; _amplifier.setLevel(_levelSL.next(level)); - outputs[OUT_OUTPUT].value = _amplifier.next(inputs[IN_INPUT].value); + outputs[OUT_OUTPUT].value = _saturator.next(_amplifier.next(inputs[IN_INPUT].value)); _rmsLevel = _rms.next(outputs[OUT_OUTPUT].value / 5.0f); } else { diff --git a/src/VCAmp.hpp b/src/VCAmp.hpp @@ -34,6 +34,7 @@ struct VCAmp : Module { const float minDecibels = Amplifier::minDecibels; Amplifier _amplifier; SlewLimiter _levelSL; + Saturator _saturator; RootMeanSquare _rms; float _rmsLevel = 0.0f; diff --git a/src/dsp/signal.cpp b/src/dsp/signal.cpp @@ -409,3 +409,21 @@ float Limiter::next(float sample) { } return out; } + + +const float Saturator::limit = 12.0f; + +static inline float saturation(float x) { + const float y1 = 0.98765f; // (2*x - 1)/x**2 where x is 0.9. + const float offset = 0.075f / Saturator::limit; // magic. + float x1 = (x + 1.0f) * 0.5f; + return Saturator::limit * (offset + x1 - sqrtf(x1 * x1 - y1 * x) * (1.0f / y1)); +} + +float Saturator::next(float sample) { + float x = sample * (1.0f / limit); + if (sample < 0.0f) { + return -saturation(-x); + } + return saturation(x); +} diff --git a/src/dsp/signal.hpp b/src/dsp/signal.hpp @@ -220,5 +220,11 @@ struct Limiter { float next(float sample); }; +struct Saturator { + static const float limit; + + float next(float sample); +}; + } // namespace dsp } // namespace bogaudio