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:
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;