BogaudioModules

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

commit 0c8e551d15ebd326bb8c23a48943f4154ce893f3
parent 5e9d8c79fba612f5b52087362b588dc94cda0cdf
Author: Matt Demanett <matt@demanett.net>
Date:   Wed, 20 Jan 2021 22:40:32 -0500

PRESSOR, NSGT, LMTR, CLPR: add option to double effective threshold range. #154

Diffstat:
MREADME-prerelease.md | 2+-
Msrc/Clpr.cpp | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/Clpr.hpp | 10+++++++++-
Msrc/Lmtr.cpp | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/Lmtr.hpp | 10+++++++++-
Msrc/Nsgt.cpp | 37+++++++++++++++++++++++++++++++++++++
Msrc/Nsgt.hpp | 8+++++++-
Msrc/Pressor.cpp | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/Pressor.hpp | 10+++++++++-
9 files changed, 219 insertions(+), 5 deletions(-)

diff --git a/README-prerelease.md b/README-prerelease.md @@ -793,7 +793,7 @@ The module's signal path has two main components: a detector, and the compressor The various controls and ports work as follows: - The MODE switch sets whether the module works as a compressor (COMP) or noise gate (GATE). - - THRESHOLD sets the threshold in decibels. The default 0dB setting corresponds to the 10V peak-to-peak output level of a standard oscillator. The TRSH input expects a unipolar (+10V) input; if in use this is attenuated by the knob. + - THRESHOLD sets the threshold in decibels. The default 0dB setting corresponds to the 10V peak-to-peak output level of a standard oscillator. The TRSH input expects a unipolar (+10V) input; if in use this is attenuated by the knob. The knob's range is -24dB to +6dB; menu option "Threshold range" allows this to be doubled to -48dB to 12dB. - RATIO sets the degree of attenuation applied to a signal. In compressor mode, higher settings attenuate the signal more as the detector output goes above the threshold; at the maximum setting, the compressor becomes a limiter. In noise gate mode, higher ratios more completely attenuate inputs below the threshold. The RATIO CV input is unipolar (0-10V), attenuated by the knob - The COMPRESSION meter provides a visual indication of the amount of attenuation being applied to the input at any given moment, and is proportional to the CV output at ENV. - ATACK and DECAY control lag times in the the movement of the detector signal as the input changes. Each has a corresponding unipolar (+10V) CV attenuated by the corresponding knob. diff --git a/src/Clpr.cpp b/src/Clpr.cpp @@ -1,6 +1,44 @@ #include "Clpr.hpp" +#define THRESHOLD_RANGE "threshold_range" + +float Clpr::ThresholdParamQuantity::getDisplayValue() { + float v = getValue(); + if (!module) { + return v; + } + + v *= 30.0f; + v -= 24.0f; + v *= dynamic_cast<Clpr*>(module)->_thresholdRange; + return v; +} + +void Clpr::ThresholdParamQuantity::setDisplayValue(float v) { + if (!module) { + return; + } + Clpr* m = dynamic_cast<Clpr*>(module); + v /= m->_thresholdRange; + v = clamp(v, -24.0f, 6.0f); + v += 24.0f; + v /= 30.0f; + setValue(v); +} + +json_t* Clpr::toJson(json_t* root) { + json_object_set_new(root, THRESHOLD_RANGE, json_real(_thresholdRange)); + return root; +} + +void Clpr::fromJson(json_t* root) { + json_t* tr = json_object_get(root, THRESHOLD_RANGE); + if (tr) { + _thresholdRange = std::max(0.0f, (float)json_real_value(tr)); + } +} + bool Clpr::active() { return outputs[LEFT_OUTPUT].isConnected() || outputs[RIGHT_OUTPUT].isConnected(); } @@ -31,6 +69,7 @@ void Clpr::modulateChannel(int c) { } e.thresholdDb *= 30.0f; e.thresholdDb -= 24.0f; + e.thresholdDb *= _thresholdRange; float outGain = params[OUTPUT_GAIN_PARAM].getValue(); if (inputs[OUTPUT_GAIN_INPUT].isConnected()) { @@ -97,6 +136,16 @@ struct ClprWidget : BGModuleWidget { addOutput(createOutput<Port24>(leftOutputPosition, module, Clpr::LEFT_OUTPUT)); addOutput(createOutput<Port24>(rightOutputPosition, module, Clpr::RIGHT_OUTPUT)); } + + void contextMenu(Menu* menu) override { + auto m = dynamic_cast<Clpr*>(module); + assert(m); + + OptionsMenuItem* tr = new OptionsMenuItem("Threshold range"); + tr->addItem(OptionMenuItem("1x (-24dB to 6dB)", [m]() { return m->_thresholdRange == 1.0f; }, [m]() { m->_thresholdRange = 1.0f; })); + tr->addItem(OptionMenuItem("2x (-48dB to 12dB)", [m]() { return m->_thresholdRange == 2.0f; }, [m]() { m->_thresholdRange = 2.0f; })); + OptionsMenuItem::addToMenu(tr, menu); + } }; Model* modelClpr = bogaudio::createModel<Clpr, ClprWidget>("Bogaudio-Clpr", "CLPR", "Hard clipper and distortion", "Dynamics", "Distortion", "Polyphonic"); diff --git a/src/Clpr.hpp b/src/Clpr.hpp @@ -43,14 +43,22 @@ struct Clpr : BGModule { Engine* _engines[maxChannels] {}; bool _softKnee = true; + float _thresholdRange = 1.0f; + + struct ThresholdParamQuantity : ParamQuantity { + float getDisplayValue() override; + void setDisplayValue(float v) override; + }; Clpr() { config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS); - configParam(THRESHOLD_PARAM, 0.0f, 1.0f, 0.8f, "Threshold", " dB", 0.0f, 30.0f, -24.0f); + configParam<ThresholdParamQuantity>(THRESHOLD_PARAM, 0.0f, 1.0f, 0.8f, "Threshold", " dB"); configParam(OUTPUT_GAIN_PARAM, 0.0f, 1.0f, 0.0f, "Output gain", " dB", 0.0f, 24.0f); configParam(KNEE_PARAM, 0.0f, 1.0f, 0.0f, "Knee"); } + json_t* toJson(json_t* root) override; + void fromJson(json_t* root) override; bool active() override; int channels() override; void addChannel(int c) override; diff --git a/src/Lmtr.cpp b/src/Lmtr.cpp @@ -1,6 +1,8 @@ #include "Lmtr.hpp" +#define THRESHOLD_RANGE "threshold_range" + void Lmtr::Engine::sampleRateChange() { float sampleRate = APP->engine->getSampleRate(); detector.setSampleRate(sampleRate); @@ -8,12 +10,48 @@ void Lmtr::Engine::sampleRateChange() { releaseSL.setParams(sampleRate, 600.0f); } +float Lmtr::ThresholdParamQuantity::getDisplayValue() { + float v = getValue(); + if (!module) { + return v; + } + + v *= 30.0f; + v -= 24.0f; + v *= dynamic_cast<Lmtr*>(module)->_thresholdRange; + return v; +} + +void Lmtr::ThresholdParamQuantity::setDisplayValue(float v) { + if (!module) { + return; + } + Lmtr* m = dynamic_cast<Lmtr*>(module); + v /= m->_thresholdRange; + v = clamp(v, -24.0f, 6.0f); + v += 24.0f; + v /= 30.0f; + setValue(v); +} + void Lmtr::sampleRateChange() { for (int c = 0; c < _channels; ++c) { _engines[c]->sampleRateChange(); } } +json_t* Lmtr::toJson(json_t* root) { + json_object_set_new(root, THRESHOLD_RANGE, json_real(_thresholdRange)); + return root; +} + +void Lmtr::fromJson(json_t* root) { + json_t* tr = json_object_get(root, THRESHOLD_RANGE); + if (tr) { + _thresholdRange = std::max(0.0f, (float)json_real_value(tr)); + } +} + bool Lmtr::active() { return outputs[LEFT_OUTPUT].isConnected() || outputs[RIGHT_OUTPUT].isConnected(); } @@ -45,6 +83,7 @@ void Lmtr::modulateChannel(int c) { } e.thresholdDb *= 30.0f; e.thresholdDb -= 24.0f; + e.thresholdDb *= _thresholdRange; float outGain = params[OUTPUT_GAIN_PARAM].getValue(); if (inputs[OUTPUT_GAIN_INPUT].isConnected()) { @@ -119,6 +158,16 @@ struct LmtrWidget : BGModuleWidget { addOutput(createOutput<Port24>(leftOutputPosition, module, Lmtr::LEFT_OUTPUT)); addOutput(createOutput<Port24>(rightOutputPosition, module, Lmtr::RIGHT_OUTPUT)); } + + void contextMenu(Menu* menu) override { + auto m = dynamic_cast<Lmtr*>(module); + assert(m); + + OptionsMenuItem* tr = new OptionsMenuItem("Threshold range"); + tr->addItem(OptionMenuItem("1x (-24dB to 6dB)", [m]() { return m->_thresholdRange == 1.0f; }, [m]() { m->_thresholdRange = 1.0f; })); + tr->addItem(OptionMenuItem("2x (-48dB to 12dB)", [m]() { return m->_thresholdRange == 2.0f; }, [m]() { m->_thresholdRange = 2.0f; })); + OptionsMenuItem::addToMenu(tr, menu); + } }; Model* modelLmtr = bogaudio::createModel<Lmtr, LmtrWidget>("Bogaudio-Lmtr", "LMTR", "Limiter", "Limiter", "Dynamics", "Polyphonic"); diff --git a/src/Lmtr.hpp b/src/Lmtr.hpp @@ -50,15 +50,23 @@ struct Lmtr : BGModule { Engine* _engines[maxChannels] {}; bool _softKnee = true; + float _thresholdRange = 1.0f; + + struct ThresholdParamQuantity : ParamQuantity { + float getDisplayValue() override; + void setDisplayValue(float v) override; + }; Lmtr() { config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS); - configParam(THRESHOLD_PARAM, 0.0f, 1.0f, 0.8f, "Threshold", " dB", 0.0f, 30.0f, -24.0f); + configParam<ThresholdParamQuantity>(THRESHOLD_PARAM, 0.0f, 1.0f, 0.8f, "Threshold", " dB"); configParam(OUTPUT_GAIN_PARAM, 0.0f, 1.0f, 0.0f, "Output gain", " dB", 0.0f, 24.0f); configParam(KNEE_PARAM, 0.0f, 1.0f, 0.0f, "Knee"); } void sampleRateChange() override; + json_t* toJson(json_t* root) override; + void fromJson(json_t* root) override; bool active() override; int channels() override; void addChannel(int c) override; diff --git a/src/Nsgt.cpp b/src/Nsgt.cpp @@ -3,11 +3,36 @@ #define ATTACK_MS "attack_ms" #define RELEASE_MS "release_ms" +#define THRESHOLD_RANGE "threshold_range" void Nsgt::Engine::sampleRateChange() { detector.setSampleRate(APP->engine->getSampleRate()); } +float Nsgt::ThresholdParamQuantity::getDisplayValue() { + float v = getValue(); + if (!module) { + return v; + } + + v *= 30.0f; + v -= 24.0f; + v *= dynamic_cast<Nsgt*>(module)->_thresholdRange; + return v; +} + +void Nsgt::ThresholdParamQuantity::setDisplayValue(float v) { + if (!module) { + return; + } + Nsgt* m = dynamic_cast<Nsgt*>(module); + v /= m->_thresholdRange; + v = clamp(v, -24.0f, 6.0f); + v += 24.0f; + v /= 30.0f; + setValue(v); +} + void Nsgt::sampleRateChange() { for (int c = 0; c < _channels; ++c) { _engines[c]->sampleRateChange(); @@ -17,6 +42,7 @@ void Nsgt::sampleRateChange() { json_t* Nsgt::toJson(json_t* root) { json_object_set_new(root, ATTACK_MS, json_real(_attackMs)); json_object_set_new(root, RELEASE_MS, json_real(_releaseMs)); + json_object_set_new(root, THRESHOLD_RANGE, json_real(_thresholdRange)); return root; } @@ -30,6 +56,11 @@ void Nsgt::fromJson(json_t* root) { if (r) { _releaseMs = std::max(0.0f, (float)json_real_value(r)); } + + json_t* tr = json_object_get(root, THRESHOLD_RANGE); + if (tr) { + _thresholdRange = std::max(0.0f, (float)json_real_value(tr)); + } } bool Nsgt::active() { @@ -63,6 +94,7 @@ void Nsgt::modulateChannel(int c) { } e.thresholdDb *= 30.0f; e.thresholdDb -= 24.0f; + e.thresholdDb *= _thresholdRange; float ratio = params[RATIO_PARAM].getValue(); if (inputs[RATIO_INPUT].isConnected()) { @@ -253,6 +285,11 @@ struct NsgtWidget : BGModuleWidget { menu->addChild(new AttackMenuItem(m)); menu->addChild(new ReleaseMenuItem(m)); + + OptionsMenuItem* tr = new OptionsMenuItem("Threshold range"); + tr->addItem(OptionMenuItem("1x (-24dB to 6dB)", [m]() { return m->_thresholdRange == 1.0f; }, [m]() { m->_thresholdRange = 1.0f; })); + tr->addItem(OptionMenuItem("2x (-48dB to 12dB)", [m]() { return m->_thresholdRange == 2.0f; }, [m]() { m->_thresholdRange = 2.0f; })); + OptionsMenuItem::addToMenu(tr, menu); } }; diff --git a/src/Nsgt.hpp b/src/Nsgt.hpp @@ -57,10 +57,16 @@ struct Nsgt : BGModule { bool _softKnee = true; float _attackMs = defaultAttackMs; float _releaseMs = defaultReleaseMs; + float _thresholdRange = 1.0f; + + struct ThresholdParamQuantity : ParamQuantity { + float getDisplayValue() override; + void setDisplayValue(float v) override; + }; Nsgt() { config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS); - configParam(THRESHOLD_PARAM, 0.0f, 1.0f, 0.8f, "Threshold", " dB", 0.0f, 30.0f, -24.0f); + configParam<ThresholdParamQuantity>(THRESHOLD_PARAM, 0.0f, 1.0f, 0.8f, "Threshold", " dB"); configParam<DynamicsRatioParamQuantity>(RATIO_PARAM, 0.0f, 1.0f, 0.55159f, "Ratio"); configParam(KNEE_PARAM, 0.0f, 1.0f, 1.0f, "Knee"); } diff --git a/src/Pressor.cpp b/src/Pressor.cpp @@ -1,16 +1,54 @@ #include "Pressor.hpp" +#define THRESHOLD_RANGE "threshold_range" + void Pressor::Engine::sampleRateChange() { detectorRMS.setSampleRate(APP->engine->getSampleRate()); } +float Pressor::ThresholdParamQuantity::getDisplayValue() { + float v = getValue(); + if (!module) { + return v; + } + + v *= 30.0f; + v -= 24.0f; + v *= dynamic_cast<Pressor*>(module)->_thresholdRange; + return v; +} + +void Pressor::ThresholdParamQuantity::setDisplayValue(float v) { + if (!module) { + return; + } + Pressor* m = dynamic_cast<Pressor*>(module); + v /= m->_thresholdRange; + v = clamp(v, -24.0f, 6.0f); + v += 24.0f; + v /= 30.0f; + setValue(v); +} + void Pressor::sampleRateChange() { for (int c = 0; c < _channels; ++c) { _engines[c]->sampleRateChange(); } } +json_t* Pressor::toJson(json_t* root) { + json_object_set_new(root, THRESHOLD_RANGE, json_real(_thresholdRange)); + return root; +} + +void Pressor::fromJson(json_t* root) { + json_t* tr = json_object_get(root, THRESHOLD_RANGE); + if (tr) { + _thresholdRange = std::max(0.0f, (float)json_real_value(tr)); + } +} + bool Pressor::active() { return ( outputs[LEFT_OUTPUT].isConnected() || @@ -51,6 +89,7 @@ void Pressor::modulateChannel(int c) { } e.thresholdDb *= 30.0f; e.thresholdDb -= 24.0f; + e.thresholdDb *= _thresholdRange; float ratio = params[RATIO_PARAM].getValue(); if (inputs[RATIO_INPUT].isConnected()) { @@ -263,6 +302,16 @@ struct PressorWidget : BGModuleWidget { addOutput(createOutput<Port24>(leftOutputPosition, module, Pressor::LEFT_OUTPUT)); addOutput(createOutput<Port24>(rightOutputPosition, module, Pressor::RIGHT_OUTPUT)); } + + void contextMenu(Menu* menu) override { + auto m = dynamic_cast<Pressor*>(module); + assert(m); + + OptionsMenuItem* tr = new OptionsMenuItem("Threshold range"); + tr->addItem(OptionMenuItem("1x (-24dB to 6dB)", [m]() { return m->_thresholdRange == 1.0f; }, [m]() { m->_thresholdRange = 1.0f; })); + tr->addItem(OptionMenuItem("2x (-48dB to 12dB)", [m]() { return m->_thresholdRange == 2.0f; }, [m]() { m->_thresholdRange = 2.0f; })); + OptionsMenuItem::addToMenu(tr, menu); + } }; Model* modelPressor = bogaudio::createModel<Pressor, PressorWidget>("Bogaudio-Pressor", "PRESSOR", "Stereo compressor and noise gate", "Compressor", "Dynamics", "Polyphonic"); diff --git a/src/Pressor.hpp b/src/Pressor.hpp @@ -74,10 +74,16 @@ struct Pressor : BGModule { bool _compressorMode = true; bool _rmsDetector = true; bool _softKnee = true; + float _thresholdRange = 1.0f; + + struct ThresholdParamQuantity : ParamQuantity { + float getDisplayValue() override; + void setDisplayValue(float v) override; + }; Pressor() { config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS); - configParam(THRESHOLD_PARAM, 0.0f, 1.0f, 0.8f, "Threshold", " dB", 0.0f, 30.0f, -24.0f); + configParam<ThresholdParamQuantity>(THRESHOLD_PARAM, 0.0f, 1.0f, 0.8f, "Threshold", " dB"); configParam<DynamicsRatioParamQuantity>(RATIO_PARAM, 0.0f, 1.0f, 0.55159f, "Ratio"); configParam<ScaledSquaringParamQuantity<500>>(ATTACK_PARAM, 0.0f, 1.0f, 0.31623f, "Attack", " ms"); configParam<ScaledSquaringParamQuantity<2>>(RELEASE_PARAM, 0.0f, 1.0f, 0.31623f, "Release", " s"); @@ -90,6 +96,8 @@ struct Pressor : BGModule { } void sampleRateChange() override; + json_t* toJson(json_t* root) override; + void fromJson(json_t* root) override; bool active() override; int channels() override; void addChannel(int c) override;