commit 3be9295219991151cfa676f6b76277ea9c15b42e
parent fdf7a95b977d6d93edbd15ada922a3b157355476
Author: Matt Demanett <matt@demanett.net>
Date: Mon, 13 Sep 2021 22:37:11 -0400
S&H: add output glide (smoothing) as an option. #181
Diffstat:
4 files changed, 86 insertions(+), 2 deletions(-)
diff --git a/README-prerelease.md b/README-prerelease.md
@@ -911,6 +911,8 @@ Each channel may also be have its output inverted with the INV button.
The GATE input on the lower section is normalled to GATE in the top section (but a press on the top button does not trigger the lower section).
+The GLIDE context menu option applies linear glide (slew limitation, smoothing) to the outputs. The time value, which defaults to 0, determines how long the output will take to change (slew) 10V. This option is ignored in track-and-hold mode.
+
_Polyphony:_ <a href="#polyphony">Polyphonic</a>, with polyphony defined by the GATE input in each section. If the bottom GATE is patched, the two sections will independently take their channels from their respective GATE inputs; if only the top GATE is patched, the bottom section normals to the top input and both sections have the same number of channels. The polyphony port can be changed to IN on the context menu (this change applies to both top and bottom sections of the module; IN does not normal to the bottom section, and both sections will set their channels independently, from whatever is patched to their own IN).
#### <a name="walk2"></a> WALK2
diff --git a/src/SampleHold.cpp b/src/SampleHold.cpp
@@ -5,6 +5,7 @@
#define NOISE_TYPE "noise_type"
#define RANGE_OFFSET "range_offset"
#define RANGE_SCALE "range_scale"
+#define SMOOTHING_MS "smoothing_ms"
void SampleHold::reset() {
for (int i = 0; i < maxChannels; ++i) {
@@ -20,6 +21,7 @@ json_t* SampleHold::toJson(json_t* root) {
json_object_set_new(root, NOISE_TYPE, json_integer((int)_noiseType));
json_object_set_new(root, RANGE_OFFSET, json_real(_rangeOffset));
json_object_set_new(root, RANGE_SCALE, json_real(_rangeScale));
+ json_object_set_new(root, SMOOTHING_MS, json_real(_smoothMS));
return root;
}
@@ -43,6 +45,16 @@ void SampleHold::fromJson(json_t* root) {
if (rs) {
_rangeScale = json_real_value(rs);
}
+
+ json_t* s = json_object_get(root, SMOOTHING_MS);
+ if (s) {
+ _smoothMS = json_real_value(s);
+ }
+}
+
+void SampleHold::modulateChannel(int c) {
+ _outputSL1[c].setParams(APP->engine->getSampleRate(), _smoothMS, 10.0f);
+ _outputSL2[c].setParams(APP->engine->getSampleRate(), _smoothMS, 10.0f);
}
void SampleHold::processAll(const ProcessArgs& args) {
@@ -55,6 +67,7 @@ void SampleHold::processAll(const ProcessArgs& args) {
NULL,
inputs[IN1_INPUT],
_value1,
+ _outputSL1,
outputs[OUT1_OUTPUT]
);
handleChannel(
@@ -66,6 +79,7 @@ void SampleHold::processAll(const ProcessArgs& args) {
&inputs[TRIGGER1_INPUT],
inputs[IN2_INPUT],
_value2,
+ _outputSL2,
outputs[OUT2_OUTPUT]
);
}
@@ -79,6 +93,7 @@ void SampleHold::handleChannel(
Input* altTriggerInput,
Input& in,
float* value,
+ SlewLimiter* outputSL,
Output& out
) {
int n = 0;
@@ -101,8 +116,9 @@ void SampleHold::handleChannel(
triggerIn = altTriggerInput->getPolyVoltage(i);
}
+ bool track = trackParam.getValue() > 0.5f;
bool triggered = trigger[i].process(triggerParam.getValue() + triggerIn);
- if (trackParam.getValue() > 0.5f ? trigger[i].isHigh() : triggered) {
+ if (track ? trigger[i].isHigh() : triggered) {
if (in.isConnected()) {
value[i] = in.getPolyVoltage(i);
}
@@ -115,6 +131,9 @@ void SampleHold::handleChannel(
if (invertParam.getValue() > 0.5f) {
o = -o;
}
+ if (!track) {
+ o = outputSL[i].next(o);
+ }
out.setVoltage(o, i);
}
}
@@ -191,6 +210,61 @@ struct SampleHoldWidget : BGModuleWidget {
{}
};
+ struct SmoothQuantity : Quantity {
+ SampleHold* _module;
+
+ SmoothQuantity(SampleHold* m) : _module(m) {}
+
+ void setValue(float value) override {
+ value = clamp(value, getMinValue(), getMaxValue());
+ if (_module) {
+ _module->_smoothMS = valueToMs(value);
+ }
+ }
+
+ float getValue() override {
+ if (_module) {
+ return msToValue(_module->_smoothMS);
+ }
+ return getDefaultValue();
+ }
+
+ float getMinValue() override { return 0.0f; }
+ float getMaxValue() override { return 1.0f; }
+ float getDefaultValue() override { return getMinValue(); }
+ float getDisplayValue() override { return roundf(valueToMs(getValue())); }
+ void setDisplayValue(float displayValue) override { setValue(msToValue(displayValue)); }
+ std::string getLabel() override { return "Smoothing"; }
+ std::string getUnit() override { return "ms"; }
+ float valueToMs(float v) { return v * v * SampleHold::maxSmoothMS; }
+ float msToValue(float ms) { return sqrtf(ms / SampleHold::maxSmoothMS); };
+ };
+
+ struct SmoothSlider : ui::Slider {
+ SmoothSlider(SmoothQuantity* q) {
+ quantity = q; // q now owned.
+ box.size.x = 200.0f;
+ }
+ virtual ~SmoothSlider() {
+ delete quantity;
+ }
+ };
+
+ struct SmoothMenuItem : MenuItem {
+ SampleHold* _module;
+
+ SmoothMenuItem(SampleHold* m) : _module(m) {
+ this->text = "Glide";
+ this->rightText = "▸";
+ }
+
+ Menu* createChildMenu() override {
+ Menu* menu = new Menu;
+ menu->addChild(new SmoothSlider(new SmoothQuantity(_module)));
+ return menu;
+ }
+ };
+
void contextMenu(Menu* menu) override {
auto m = dynamic_cast<SampleHold*>(module);
assert(m);
@@ -220,6 +294,7 @@ struct SampleHoldWidget : BGModuleWidget {
mi->addItem(RangeOptionMenuItem(m, "0V-1V", 1.0f, 0.5f));
OptionsMenuItem::addToMenu(mi, menu);
}
+ menu->addChild(new SmoothMenuItem(m));
}
};
diff --git a/src/SampleHold.hpp b/src/SampleHold.hpp
@@ -41,6 +41,8 @@ struct SampleHold : BGModule {
RED_NOISE_TYPE
};
+ static constexpr float maxSmoothMS = 10000.0f;
+
Trigger _trigger1[maxChannels];
Trigger _trigger2[maxChannels];
float _value1[maxChannels] {};
@@ -53,6 +55,9 @@ struct SampleHold : BGModule {
float _rangeOffset = 1.0f;
float _rangeScale = 5.0f;
int _polyInputID = TRIGGER1_INPUT;
+ float _smoothMS = 0.0f;
+ SlewLimiter _outputSL1[maxChannels];
+ SlewLimiter _outputSL2[maxChannels];
SampleHold() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS);
@@ -67,6 +72,7 @@ struct SampleHold : BGModule {
void reset() override;
json_t* toJson(json_t* root) override;
void fromJson(json_t* root) override;
+ void modulateChannel(int c) override;
void processAll(const ProcessArgs& args) override;
void handleChannel(
Param& trackParam,
@@ -77,6 +83,7 @@ struct SampleHold : BGModule {
Input* altTriggerInput,
Input& in,
float* value,
+ SlewLimiter* _outputSL,
Output& out
);
float noise();
diff --git a/src/dsp/signal.cpp b/src/dsp/signal.cpp
@@ -177,7 +177,7 @@ void ShapedSlewLimiter::setParams(float sampleRate, float milliseconds, float sh
assert(shape >= minShape);
assert(shape <= maxShape);
_sampleTime = 1.0f / sampleRate;
- _time = milliseconds / 1000.0f;
+ _time = milliseconds / 1000.0f;
_shapeExponent = (shape > -0.05f && shape < 0.05f) ? 0.0f : shape;
_inverseShapeExponent = 1.0f / _shapeExponent;
}