BogaudioModules

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

commit e7008d731e8ed05a8cb077b0fbedd9776c32def6
parent 0ebc4fdb6a3ef18797bc536740e5b586079ef9df
Author: Matt Demanett <matt@demanett.net>
Date:   Sun, 14 Oct 2018 19:25:41 -0400

Fix LMTR to be a limiter (non-distorting) instead of a clipper (distorting).  Create CLPR to be a clipper.  #22

Diffstat:
MREADME.md | 10+++++++++-
Mdoc/www/effects.png | 0
Mdoc/www/modules2.png | 0
Ares-src/Clpr-src.svg | 144+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ares/Clpr.svg | 0
Asrc/Clpr.cpp | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Clpr.hpp | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/Lmtr.cpp | 20++++++++++++++++++--
Msrc/Lmtr.hpp | 6++++++
Msrc/Pressor.cpp | 2+-
Msrc/bogaudio.cpp | 2++
11 files changed, 332 insertions(+), 4 deletions(-)

diff --git a/README.md b/README.md @@ -278,9 +278,17 @@ The various controls and ports work as follows: Several of the settings can take fairly extreme values (e.g. OUT GAIN); this allows the module to be used as a distortion effect. +#### CLPR + +CLPR is a compact (6HP) [clipper](https://en.wikipedia.org/wiki/Clipping_%28audio%29). Its controls behave the same as the corresponding controls on PRESSOR. + +In contrast to LMTR, CLPR chops a signal at a voltage threshold corresponding to the selected amplitude; this distorts the signal. + #### LMTR -LMTR is a compact (6HP) hard [limiter](https://en.wikipedia.org/wiki/Dynamic_range_compression) or clipper. Its controls behave the same as the corresponding controls on PRESSOR. +LMTR is a compact (6HP) [limiter](https://en.wikipedia.org/wiki/Dynamic_range_compression). Its controls behave the same as the corresponding controls on PRESSOR. + +In contrast to CLPR, LMTR does not distort the signal (or not much); it just reduces the amplitude of the signal to keep it below the threshold. #### NSGT diff --git a/doc/www/effects.png b/doc/www/effects.png Binary files differ. diff --git a/doc/www/modules2.png b/doc/www/modules2.png Binary files differ. diff --git a/res-src/Clpr-src.svg b/res-src/Clpr-src.svg @@ -0,0 +1,144 @@ +<svg + version="1.1" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + width="90" + height="380" + viewBox="0 0 90 380" +> + <style> + text { + fill: #333; + font-family: 'Roboto', sans-serif; + font-weight: bold; + } + text.title { + font-family: 'Comfortaa', sans-serif; + font-weight: normal; + } + text.brand { + font-family: 'Audiowide', sans-serif; + font-weight: bold; + } + </style> + + <defs> + <symbol id="knob38" viewBox="0 0 38px 38px"> + <g transform="translate(19 19)"> + <polyline points="-5,0 5,0" stroke-width="1" stroke="#00f" /> + <polyline points="0,-5 0,5" stroke-width="1" stroke="#00f" /> + <circle cx="0" cy="0" r="18.5" stroke-width="1" stroke="#00f" fill="none" /> + </g> + </symbol> + + <symbol id="knobguide-threshold38" viewBox="0 0 70px 70px"> + <g transform="translate(35 35)"> + <text font-size="6.0pt" transform="rotate(-240) translate(25 0) rotate(240) translate(-10 2.5)">-24</text> + <polyline points="0,0 3,0" stroke-width="0.3" stroke="#333" transform="rotate(-210) translate(21 0)" /> + <text font-size="6.0pt" transform="rotate(-180) translate(25 0) rotate(180) translate(-10 2.5)">-18</text> + <polyline points="0,0 3,0" stroke-width="0.3" stroke="#333" transform="rotate(-150) translate(21 0)" /> + <text font-size="6.0pt" transform="rotate(-120) translate(25 0) rotate(120) translate(-10 2.5)">-12</text> + <polyline points="0,0 3,0" stroke-width="0.3" stroke="#333" transform="rotate(-90) translate(21 0)" /> + <text font-size="6.0pt" transform="rotate(-60) translate(25 0) rotate(60) translate(-2.3 2.5)">-6</text> + <polyline points="0,0 3,0" stroke-width="0.3" stroke="#333" transform="rotate(-30) translate(21 0)" /> + <text font-size="6.0pt" transform="rotate(0) translate(25 0) rotate(0) translate(-2.3 2.5)">0</text> + <polyline points="0,0 3,0" stroke-width="0.3" stroke="#333" transform="rotate(30) translate(21 0)" /> + <text font-size="6.0pt" transform="rotate(60) translate(25 0) rotate(-60) translate(-2.3 2.5)">6</text> + <text font-size="6.0pt" transform="rotate(90) translate(29 0) rotate(-90) translate(-4.7 2.2)">dB</text> + </g> + </symbol> + + <symbol id="knobguide-outputgain38" viewBox="0 0 70px 70px"> + <g transform="translate(35 35)"> + <text font-size="6.0pt" transform="rotate(-240) translate(25 0) rotate(240) translate(-4 2.5)">0</text> + <polyline points="0,0 3,0" stroke-width="0.3" stroke="#333" transform="rotate(-202.5) translate(21 0)" /> + <text font-size="6.0pt" transform="rotate(-165) translate(25 0) rotate(165) translate(-3.5 2.5)">6</text> + <polyline points="0,0 3,0" stroke-width="0.3" stroke="#333" transform="rotate(-127.5) translate(21 0)" /> + <text font-size="6.0pt" transform="rotate(-90) translate(25 0) rotate(90) translate(-5 2.5)">12</text> + <polyline points="0,0 3,0" stroke-width="0.3" stroke="#333" transform="rotate(-52.5) translate(21 0)" /> + <text font-size="6.0pt" transform="rotate(-15) translate(25 0) rotate(15) translate(-2 2.5)">18</text> + <polyline points="0,0 3,0" stroke-width="0.3" stroke="#333" transform="rotate(22.5) translate(21 0)" /> + <text font-size="6.0pt" transform="rotate(60) translate(25 0) rotate(-60) translate(-1 2.5)">24</text> + <text font-size="6.0pt" transform="rotate(90) translate(29 0) rotate(-90) translate(-4.7 2.2)">dB</text> + </g> + </symbol> + + <symbol id="switch" viewBox="0 0 14px 24px"> + <rect width="14px" height="24px" stroke-width="1" stroke="#000" fill="#ddd" /> + <rect width="14px" height="12px" stroke-width="0" fill="#000" /> + </symbol> + + <symbol id="input" viewBox="0 0 24px 24px"> + <g transform="translate(12 12)"> + <circle cx="0" cy="0" r="5" stroke-width="1" stroke="#0f0" fill="#0f0" /> + <circle cx="0" cy="0" r="10.5" stroke-width="3" stroke="#0f0" fill="none" /> + </g> + </symbol> + + <symbol id="output" viewBox="0 0 24px 24px"> + <g transform="translate(12 12)"> + <circle cx="0" cy="0" r="5" stroke-width="1" stroke="#f00" fill="#f00" /> + <circle cx="0" cy="0" r="10.5" stroke-width="3" stroke="#f00" fill="none" /> + </g> + </symbol> + </defs> + + <rect width="100%" height="100%" fill="#ddd" /> + <polyline points="1,1 89,1 89,379 1,379 1,1" stroke="#e4e4e4" stroke-width="0.5" fill="none" /> + <polyline points="0.5,0.5 89.5,0.5 89.5,379.5 0.5,379.5 0.5,0.5" stroke="#ebebeb" stroke-width="0.8" fill="none" /> + <polyline points="0,0 90,0 90,380 0,380 0,0" stroke="#f2f2f2" stroke-width="1" fill="none" /> + + <text class="title" x="38" y="17" font-size="9pt" letter-spacing="3px">CLPR</text> + <g transform="translate(5.5 374)"> + <text class="brand" font-size="6.5pt" letter-spacing="2px">BOGAUDIO</text> + <rect width="1.5" height="2" fill="#ddd" transform="translate(21 -4)" /> + </g> + + <!-- <polyline points="0,0 0,380" stroke="#0f0" stroke-width="1" fill="none" transform="translate(45 0)" /> --> + <!-- <rect width="90" height="15" fill="#0f0" transform="translate(0 18)" /> --> + <!-- <rect width="90" height="15" fill="#0f0" transform="translate(0 102)" /> --> + <!-- <rect width="90" height="7" fill="#0f0" transform="translate(0 185)" /> --> + <!-- <rect width="90" height="7" fill="#0f0" transform="translate(0 232)" /> --> + + <g transform="translate(0 40)"> + <text font-size="8pt" letter-spacing="2px" transform="translate(8 0)">THRESHOLD</text> + <use id="THRESHOLD_PARAM" xlink:href="#knob38" transform="translate(26 12)" /> + <use xlink:href="#knobguide-threshold38" transform="translate(10 -4)" /> + </g> + + <g transform="translate(0 122)"> + <text font-size="8pt" letter-spacing="2px" transform="translate(15.5 0)">OUT GAIN</text> + <use id="OUTPUT_GAIN_PARAM" xlink:href="#knob38" transform="translate(26 12)" /> + <use xlink:href="#knobguide-outputgain38" transform="translate(10 -4)" /> + </g> + + <g transform="translate(40 198)"> + <text font-size="5pt" letter-spacing="2px" transform="translate(-8 25.5) rotate(270)">KNEE</text> + <text font-size="5pt" letter-spacing="2px" transform="translate(-4 -1)">SOFT</text> + <use id="KNEE_PARAM" xlink:href="#switch" transform="translate(0 2)" /> + <text font-size="5pt" letter-spacing="2px" transform="translate(-5 34)">HARD</text> + </g> + + <g transform="translate(11 240)"> + <g transform="translate(0 0)"> + <rect width="68" height="10" fill="#fafafa" transform="translate(0 66)" /> + <rect width="68" height="73" rx="5" fill="#fafafa" /> + <use id="LEFT_INPUT" xlink:href="#input" transform="translate(5 4)" /> + <text font-size="5pt" letter-spacing="2px" transform="translate(15.5 36)">L</text> + <use id="RIGHT_INPUT" xlink:href="#input" transform="translate(39 4)" /> + <text font-size="5pt" letter-spacing="2px" transform="translate(49 36)">R</text> + <use id="THRESHOLD_INPUT" xlink:href="#input" transform="translate(5 40)" /> + <text font-size="5pt" letter-spacing="2px" transform="translate(5.5 72)">TRSH</text> + <use id="OUTPUT_GAIN_INPUT" xlink:href="#input" transform="translate(39 40)" /> + <text font-size="5pt" letter-spacing="2px" transform="translate(42 72)">OGN</text> + </g> + <g transform="translate(0 79)"> + <rect width="68" height="10" fill="#bbb" transform="translate(0 -3)" /> + <rect width="68" height="37" rx="5" fill="#bbb" /> + <use id="LEFT_OUTPUT" xlink:href="#output" transform="translate(5 1)" /> + <text font-size="5pt" letter-spacing="2px" transform="translate(15.5 33)">L</text> + <use id="RIGHT_OUTPUT" xlink:href="#output" transform="translate(39 1)" /> + <text font-size="5pt" letter-spacing="2px" transform="translate(49 33)">R</text> + </g> + </g> +</svg> diff --git a/res/Clpr.svg b/res/Clpr.svg Binary files differ. diff --git a/src/Clpr.cpp b/src/Clpr.cpp @@ -0,0 +1,95 @@ + +#include "Clpr.hpp" + +void Clpr::onReset() { + _modulationStep = modulationSteps; +} + +void Clpr::step() { + if (!(outputs[LEFT_OUTPUT].active || outputs[RIGHT_OUTPUT].active)) { + return; + } + + ++_modulationStep; + if (_modulationStep >= modulationSteps) { + _modulationStep = 0; + + _thresholdDb = params[THRESHOLD_PARAM].value; + if (inputs[THRESHOLD_INPUT].active) { + _thresholdDb *= clamp(inputs[THRESHOLD_INPUT].value / 10.0f, 0.0f, 1.0f); + } + _thresholdDb *= 30.0f; + _thresholdDb -= 24.0f; + + float outGain = params[OUTPUT_GAIN_PARAM].value; + if (inputs[OUTPUT_GAIN_INPUT].active) { + outGain = clamp(outGain + inputs[OUTPUT_GAIN_INPUT].value / 5.0f, 0.0f, 1.0f); + } + outGain *= 24.0f; + if (_outGain != outGain) { + _outGain = outGain; + _outLevel = decibelsToAmplitude(_outGain); + } + + _softKnee = params[KNEE_PARAM].value > 0.97f; + } + + float leftInput = inputs[LEFT_INPUT].value; + float rightInput = inputs[RIGHT_INPUT].value; + float env = abs(leftInput + rightInput); + float detectorDb = amplitudeToDecibels(env / 5.0f); + float compressionDb = _compressor.compressionDb(detectorDb, _thresholdDb, Compressor::maxEffectiveRatio, _softKnee); + _amplifier.setLevel(-compressionDb); + if (outputs[LEFT_OUTPUT].active) { + outputs[LEFT_OUTPUT].value = _saturator.next(_amplifier.next(leftInput) * _outLevel); + } + if (outputs[RIGHT_OUTPUT].active) { + outputs[RIGHT_OUTPUT].value = _saturator.next(_amplifier.next(rightInput) * _outLevel); + } +} + +struct ClprWidget : ModuleWidget { + static constexpr int hp = 6; + + ClprWidget(Clpr* module) : ModuleWidget(module) { + box.size = Vec(RACK_GRID_WIDTH * hp, RACK_GRID_HEIGHT); + + { + SVGPanel *panel = new SVGPanel(); + panel->box.size = box.size; + panel->setBackground(SVG::load(assetPlugin(plugin, "res/Clpr.svg"))); + addChild(panel); + } + + addChild(Widget::create<ScrewSilver>(Vec(0, 0))); + addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 15, 365))); + + // generated by svg_widgets.rb + auto thresholdParamPosition = Vec(26.0, 52.0); + auto outputGainParamPosition = Vec(26.0, 134.0); + auto kneeParamPosition = Vec(39.5, 199.5); + + auto leftInputPosition = Vec(16.0, 244.0); + auto rightInputPosition = Vec(50.0, 244.0); + auto thresholdInputPosition = Vec(16.0, 280.0); + auto outputGainInputPosition = Vec(50.0, 280.0); + + auto leftOutputPosition = Vec(16.0, 320.0); + auto rightOutputPosition = Vec(50.0, 320.0); + // end generated by svg_widgets.rb + + addParam(ParamWidget::create<Knob38>(thresholdParamPosition, module, Clpr::THRESHOLD_PARAM, 0.0, 1.0, 0.8)); + addParam(ParamWidget::create<Knob38>(outputGainParamPosition, module, Clpr::OUTPUT_GAIN_PARAM, 0.0, 1.0, 0.0)); + addParam(ParamWidget::create<SliderSwitch2State14>(kneeParamPosition, module, Clpr::KNEE_PARAM, 0.95, 1.0, 0.0)); + + addInput(Port::create<Port24>(leftInputPosition, Port::INPUT, module, Clpr::LEFT_INPUT)); + addInput(Port::create<Port24>(rightInputPosition, Port::INPUT, module, Clpr::RIGHT_INPUT)); + addInput(Port::create<Port24>(thresholdInputPosition, Port::INPUT, module, Clpr::THRESHOLD_INPUT)); + addInput(Port::create<Port24>(outputGainInputPosition, Port::INPUT, module, Clpr::OUTPUT_GAIN_INPUT)); + + addOutput(Port::create<Port24>(leftOutputPosition, Port::OUTPUT, module, Clpr::LEFT_OUTPUT)); + addOutput(Port::create<Port24>(rightOutputPosition, Port::OUTPUT, module, Clpr::RIGHT_OUTPUT)); + } +}; + +Model* modelClpr = createModel<Clpr, ClprWidget>("Bogaudio-Clpr", "CLPR", "clipper", DYNAMICS_TAG, DISTORTION_TAG); diff --git a/src/Clpr.hpp b/src/Clpr.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include "bogaudio.hpp" +#include "dsp/signal.hpp" + +using namespace bogaudio::dsp; + +extern Model* modelClpr; + +namespace bogaudio { + +struct Clpr : Module { + enum ParamsIds { + THRESHOLD_PARAM, + OUTPUT_GAIN_PARAM, + KNEE_PARAM, + NUM_PARAMS + }; + + enum InputsIds { + LEFT_INPUT, + RIGHT_INPUT, + THRESHOLD_INPUT, + OUTPUT_GAIN_INPUT, + NUM_INPUTS + }; + + enum OutputsIds { + LEFT_OUTPUT, + RIGHT_OUTPUT, + NUM_OUTPUTS + }; + + enum LightsIds { + NUM_LIGHTS + }; + + const int modulationSteps = 100; + int _modulationStep = 0; + float _thresholdDb = 0.0f; + float _outGain = -1.0f; + float _outLevel = 0.0f; + bool _softKnee = true; + + Compressor _compressor; + Amplifier _amplifier; + Saturator _saturator; + + Clpr() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { + onReset(); + } + + void onReset() override; + void step() override; +}; + +} // namespace bogaudio diff --git a/src/Lmtr.cpp b/src/Lmtr.cpp @@ -5,6 +5,14 @@ void Lmtr::onReset() { _modulationStep = modulationSteps; } +void Lmtr::onSampleRateChange() { + float sampleRate = engineGetSampleRate(); + _detector.setSampleRate(sampleRate); + _attackSL.setParams(sampleRate, 150.0f); + _releaseSL.setParams(sampleRate, 600.0f); + _modulationStep = modulationSteps; +} + void Lmtr::step() { if (!(outputs[LEFT_OUTPUT].active || outputs[RIGHT_OUTPUT].active)) { return; @@ -36,7 +44,15 @@ void Lmtr::step() { float leftInput = inputs[LEFT_INPUT].value; float rightInput = inputs[RIGHT_INPUT].value; - float env = abs(leftInput + rightInput); + float env = _detector.next(leftInput + rightInput); + if (env > _lastEnv) { + env = _attackSL.next(env, _lastEnv); + } + else { + env = _releaseSL.next(env, _lastEnv); + } + _lastEnv = env; + float detectorDb = amplitudeToDecibels(env / 5.0f); float compressionDb = _compressor.compressionDb(detectorDb, _thresholdDb, Compressor::maxEffectiveRatio, _softKnee); _amplifier.setLevel(-compressionDb); @@ -92,4 +108,4 @@ struct LmtrWidget : ModuleWidget { } }; -Model* modelLmtr = createModel<Lmtr, LmtrWidget>("Bogaudio-Lmtr", "LMTR", "limiter/clipper", DYNAMICS_TAG, DISTORTION_TAG, EFFECT_TAG); +Model* modelLmtr = createModel<Lmtr, LmtrWidget>("Bogaudio-Lmtr", "LMTR", "limiter", DYNAMICS_TAG); diff --git a/src/Lmtr.hpp b/src/Lmtr.hpp @@ -41,16 +41,22 @@ struct Lmtr : Module { float _outGain = -1.0f; float _outLevel = 0.0f; bool _softKnee = true; + float _lastEnv = 0.0f; + SlewLimiter _attackSL; + SlewLimiter _releaseSL; + RootMeanSquare _detector; Compressor _compressor; Amplifier _amplifier; Saturator _saturator; Lmtr() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { onReset(); + onSampleRateChange(); } void onReset() override; + void onSampleRateChange() override; void step() override; }; diff --git a/src/Pressor.cpp b/src/Pressor.cpp @@ -243,4 +243,4 @@ struct PressorWidget : ModuleWidget { } }; -Model* modelPressor = createModel<Pressor, PressorWidget>("Bogaudio-Pressor", "Pressor", "stereo compressor", COMPRESSOR_TAG, DYNAMICS_TAG, EFFECT_TAG); +Model* modelPressor = createModel<Pressor, PressorWidget>("Bogaudio-Pressor", "Pressor", "stereo compressor", COMPRESSOR_TAG, DYNAMICS_TAG); diff --git a/src/bogaudio.cpp b/src/bogaudio.cpp @@ -7,6 +7,7 @@ #include "AMRM.hpp" #include "Analyzer.hpp" #include "Bool.hpp" +#include "Clpr.hpp" #include "Cmp.hpp" #include "CVD.hpp" #include "DADSRH.hpp" @@ -91,6 +92,7 @@ void init(rack::Plugin *p) { p->addModel(modelAMRM); p->addModel(modelPressor); + p->addModel(modelClpr); p->addModel(modelLmtr); p->addModel(modelNsgt);