diff --git a/plugin.json b/plugin.json
index 99e1452..c0295d7 100644
--- a/plugin.json
+++ b/plugin.json
@@ -362,6 +362,15 @@
"Utility",
"Polyphonic"
]
+ },
+ {
+ "slug": "Rescale",
+ "name": "Rescale",
+ "description": "Rescales voltages with gain and offset",
+ "tags": [
+ "Utility",
+ "Polyphonic"
+ ]
}
]
}
\ No newline at end of file
diff --git a/res/Rescale.svg b/res/Rescale.svg
new file mode 100644
index 0000000..6393908
--- /dev/null
+++ b/res/Rescale.svg
@@ -0,0 +1,351 @@
+
+
diff --git a/src/Rescale.cpp b/src/Rescale.cpp
new file mode 100644
index 0000000..8576fb3
--- /dev/null
+++ b/src/Rescale.cpp
@@ -0,0 +1,153 @@
+#include "plugin.hpp"
+
+
+struct Rescale : Module {
+ enum ParamId {
+ GAIN_PARAM,
+ OFFSET_PARAM,
+ MAX_PARAM,
+ MIN_PARAM,
+ PARAMS_LEN
+ };
+ enum InputId {
+ IN_INPUT,
+ INPUTS_LEN
+ };
+ enum OutputId {
+ OUT_OUTPUT,
+ OUTPUTS_LEN
+ };
+ enum LightId {
+ LIGHTS_LEN
+ };
+
+ float multiplier = 1.f;
+ bool reflectMin = false;
+ bool reflectMax = false;
+
+ Rescale() {
+ config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN);
+
+ struct GainQuantity : ParamQuantity {
+ float getDisplayValue() override {
+ Rescale* module = reinterpret_cast(this->module);
+ if (module->multiplier == 1.f) {
+ unit = "%";
+ displayMultiplier = 100.f;
+ }
+ else {
+ unit = "x";
+ displayMultiplier = module->multiplier;
+ }
+ return ParamQuantity::getDisplayValue();
+ }
+ };
+ configParam(GAIN_PARAM, -1.f, 1.f, 0.f, "Gain", "%", 0, 100);
+ configParam(OFFSET_PARAM, -10.f, 10.f, 0.f, "Offset", " V");
+ configParam(MAX_PARAM, -10.f, 10.f, 10.f, "Maximum", " V");
+ configParam(MIN_PARAM, -10.f, 10.f, -10.f, "Minimum", " V");
+ configInput(IN_INPUT, "Signal");
+ configOutput(OUT_OUTPUT, "Signal");
+ configBypass(IN_INPUT, OUT_OUTPUT);
+ }
+
+ void process(const ProcessArgs& args) override {
+ using simd::float_4;
+
+ int channels = std::max(1, inputs[IN_INPUT].getChannels());
+
+ float gain = params[GAIN_PARAM].getValue() * multiplier;
+ float offset = params[OFFSET_PARAM].getValue();
+ float min = params[MIN_PARAM].getValue();
+ float max = params[MAX_PARAM].getValue();
+
+ for (int c = 0; c < channels; c += 4) {
+ float_4 x = inputs[IN_INPUT].getPolyVoltageSimd(c);
+ x = x * gain + offset;
+
+ if (reflectMin && reflectMax) {
+ // TODO find a pen to work this out
+ }
+ else if (reflectMin) {
+ x = simd::fabs(x - min) + min;
+ x = simd::fmin(x, max);
+ }
+ else if (reflectMax) {
+ x = max - simd::fabs(max - x);
+ x = simd::fmax(x, min);
+ }
+ else {
+ x = simd::clamp(x, min, max);
+ }
+
+ outputs[OUT_OUTPUT].setVoltageSimd(x, c);
+ }
+
+ outputs[OUT_OUTPUT].setChannels(channels);
+ }
+
+ json_t* dataToJson() override {
+ json_t* rootJ = json_object();
+ json_object_set_new(rootJ, "multiplier", json_real(multiplier));
+ json_object_set_new(rootJ, "reflectMin", json_boolean(reflectMin));
+ json_object_set_new(rootJ, "reflectMax", json_boolean(reflectMax));
+ return rootJ;
+ }
+
+ void dataFromJson(json_t* rootJ) override {
+ json_t* multiplierJ = json_object_get(rootJ, "multiplier");
+ if (multiplierJ)
+ multiplier = json_number_value(multiplierJ);
+
+ json_t* reflectMinJ = json_object_get(rootJ, "reflectMin");
+ if (reflectMinJ)
+ reflectMin = json_number_value(reflectMinJ);
+
+ json_t* reflectMaxJ = json_object_get(rootJ, "reflectMax");
+ if (reflectMaxJ)
+ reflectMax = json_number_value(reflectMaxJ);
+ }
+};
+
+
+struct RescaleWidget : ModuleWidget {
+ RescaleWidget(Rescale* module) {
+ setModule(module);
+ setPanel(createPanel(asset::plugin(pluginInstance, "res/Rescale.svg")));
+
+ addChild(createWidget(Vec(RACK_GRID_WIDTH, 0)));
+ addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
+ addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
+ addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
+
+ addParam(createParamCentered(mm2px(Vec(7.62, 24.723)), module, Rescale::GAIN_PARAM));
+ addParam(createParamCentered(mm2px(Vec(7.617, 43.031)), module, Rescale::OFFSET_PARAM));
+ addParam(createParamCentered(mm2px(Vec(7.612, 64.344)), module, Rescale::MAX_PARAM));
+ addParam(createParamCentered(mm2px(Vec(7.612, 80.597)), module, Rescale::MIN_PARAM));
+
+ addInput(createInputCentered(mm2px(Vec(7.62, 96.859)), module, Rescale::IN_INPUT));
+
+ addOutput(createOutputCentered(mm2px(Vec(7.62, 113.115)), module, Rescale::OUT_OUTPUT));
+ }
+
+ void appendContextMenu(Menu* menu) override {
+ Rescale* module = getModule();
+
+ menu->addChild(new MenuSeparator);
+
+ menu->addChild(createIndexSubmenuItem("Gain multiplier", {"1x", "10x", "100x", "1000x"},
+ [=]() {
+ return (int) std::log10(module->multiplier);
+ },
+ [=](int mode) {
+ module->multiplier = std::pow(10.f, (float) mode);
+ }
+ ));
+
+ menu->addChild(createBoolPtrMenuItem("Reflect at minimum", "", &module->reflectMin));
+ menu->addChild(createBoolPtrMenuItem("Reflect at maximum", "", &module->reflectMax));
+ }
+};
+
+
+Model* modelRescale = createModel("Rescale");
\ No newline at end of file
diff --git a/src/plugin.cpp b/src/plugin.cpp
index bdf417e..095f460 100644
--- a/src/plugin.cpp
+++ b/src/plugin.cpp
@@ -41,4 +41,5 @@ void init(Plugin* p) {
p->addModel(modelGates);
p->addModel(modelProcess);
p->addModel(modelMult);
+ p->addModel(modelRescale);
}
diff --git a/src/plugin.hpp b/src/plugin.hpp
index 910e193..cc754a5 100644
--- a/src/plugin.hpp
+++ b/src/plugin.hpp
@@ -41,6 +41,7 @@ extern Model* modelCompare;
extern Model* modelGates;
extern Model* modelProcess;
extern Model* modelMult;
+extern Model* modelRescale;
struct DigitalDisplay : Widget {