commit 046e22aeb2143822ee7cf9ed9c98aa850fceb855
parent 572f6b29a84e94a18523d5353cbddb9b2b0a2c4b
Author: Matt Demanett <matt@demanett.net>
Date: Wed, 27 Jan 2021 23:21:45 -0500
OFFSET: add "order of operations" mode selection, and make "scale, then offset" the default -- prefiously the offset was applied before the scaling. #160
Diffstat:
3 files changed, 50 insertions(+), 4 deletions(-)
diff --git a/README-prerelease.md b/README-prerelease.md
@@ -1255,11 +1255,17 @@ _Polyphony:_ Polyphonic inputs are duplicated (channels intact) at their corresp
#### <a name="offset"></a> OFFSET
-An offset and scaler. The OFFSET and SCALE knobs have CV inputs. With an input signal, output is `(input + offset) * scale`. With no input connected, the output is constant in the value of `offset * scale`.
+An offset and scaler. The OFFSET and SCALE knobs have CV inputs (unipolar, 0-10V). There are two operating modes, as set by the "Order of operations" context (right-click) menu option:
+
+ - Scale, then offset (the default): the output is `(input * scale) + output`. With no input connected, the output is just `offset`.
+
+ - Offset, then scale: the output is `(input + offset) * scale`. With no input connected, the output is `offset * scale`.
+
+Note that prior to version 1.1.36, there was no mode setting, and the behavior was to offset, then scale. This behavior change might affect older patches.
By default, the output is capped at +/-12 volts (this is a standard in Rack). A context menu option allows this limit to be disabled.
-_Polyphony:_ <a href="#polyphony">Polyphonic</a>, with polyphony defined by the channels of IN input.
+_Polyphony:_ <a href="#polyphony">Polyphonic</a>, with polyphony defined by the channels of the IN input.
#### <a name="slew"></a> SLEW
diff --git a/src/Offset.cpp b/src/Offset.cpp
@@ -1,19 +1,43 @@
#include "Offset.hpp"
+#define OFFSET_FIRST "offset_first"
+
+json_t* Offset::toJson(json_t* root) {
+ root = DisableOutputLimitModule::toJson(root);
+ json_object_set_new(root, OFFSET_FIRST, json_boolean(_offsetFirst));
+ return root;
+}
+
+void Offset::fromJson(json_t* root) {
+ DisableOutputLimitModule::fromJson(root);
+ json_t* of = json_object_get(root, OFFSET_FIRST);
+ if (of) {
+ _offsetFirst = json_boolean_value(of);
+ }
+}
+
int Offset::channels() {
return inputs[IN_INPUT].getChannels();
}
void Offset::processChannel(const ProcessArgs& args, int c) {
float offset = knobValue(params[OFFSET_PARAM], inputs[OFFSET_INPUT], c);
+ offset *= 10.0f;
+
float scale = knobValue(params[SCALE_PARAM], inputs[SCALE_INPUT], c);
scale = scale < 0.0f ? -pow(scale, 2.0f) : pow(scale, 2.0f);
scale *= 10.0;
float out = inputs[IN_INPUT].getVoltage(c);
- out += 10.0f * offset;
- out *= scale;
+ if (_offsetFirst) {
+ out += offset;
+ out *= scale;
+ }
+ else {
+ out *= scale;
+ out += offset;
+ }
if (!_disableOutputLimit) {
out = clamp(out, -12.0f, 12.0f);
}
@@ -58,6 +82,18 @@ struct OffsetWidget : DisableOutputLimitModuleWidget {
addOutput(createOutput<Port24>(outOutputPosition, module, Offset::OUT_OUTPUT));
}
+
+ void contextMenu(Menu* menu) override {
+ DisableOutputLimitModuleWidget::contextMenu(menu);
+
+ auto m = dynamic_cast<Offset*>(module);
+ assert(m);
+
+ OptionsMenuItem* ooo = new OptionsMenuItem("Order of operations");
+ ooo->addItem(OptionMenuItem("Scale, then offset", [m]() { return !m->_offsetFirst; }, [m]() { m->_offsetFirst = false; }));
+ ooo->addItem(OptionMenuItem("Offset, then scale", [m]() { return m->_offsetFirst; }, [m]() { m->_offsetFirst = true; }));
+ OptionsMenuItem::addToMenu(ooo, menu);
+ }
};
Model* modelOffset = bogaudio::createModel<Offset, OffsetWidget>("Bogaudio-Offset", "OFFSET", "CV offset and scaler", "Attenuator", "Polyphonic");
diff --git a/src/Offset.hpp b/src/Offset.hpp
@@ -26,12 +26,16 @@ struct Offset : DisableOutputLimitModule {
NUM_OUTPUTS
};
+ bool _offsetFirst = false;
+
Offset() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS);
configParam(OFFSET_PARAM, -1.0f, 1.0f, 0.0f, "Offset", " V", 0.0f, 10.0f);
configParam<TenXSquaringParamQuantity>(SCALE_PARAM, -1.0f, 1.0f, 0.31623f, "Scale", "x");
}
+ json_t* toJson(json_t* root) override;
+ void fromJson(json_t* root) override;
int channels() override;
void processChannel(const ProcessArgs& args, int c) override;
float knobValue(Param& knob, Input& cv, int c) const;