diff --git a/plugin.json b/plugin.json index 76532ee..f13a266 100644 --- a/plugin.json +++ b/plugin.json @@ -210,6 +210,15 @@ "Polyphonic", "Visual" ] + }, + { + "slug": "MidSide", + "name": "Mid/Side", + "description": "Mid/side stereo encoder/decoder", + "tags": [ + "Utility", + "Polyphonic" + ] } ] } \ No newline at end of file diff --git a/res/MidSide.svg b/res/MidSide.svg new file mode 100644 index 0000000..ae206d3 --- /dev/null +++ b/res/MidSide.svg @@ -0,0 +1,541 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/MidSide.cpp b/src/MidSide.cpp new file mode 100644 index 0000000..ecc604b --- /dev/null +++ b/src/MidSide.cpp @@ -0,0 +1,108 @@ +#include "plugin.hpp" + + +struct MidSide : Module { + enum ParamIds { + ENC_WIDTH_PARAM, + DEC_WIDTH_PARAM, + NUM_PARAMS + }; + enum InputIds { + ENC_WIDTH_INPUT, + ENC_LEFT_INPUT, + ENC_RIGHT_INPUT, + DEC_WIDTH_INPUT, + DEC_MID_INPUT, + DEC_SIDES_INPUT, + NUM_INPUTS + }; + enum OutputIds { + ENC_MID_OUTPUT, + ENC_SIDES_OUTPUT, + DEC_LEFT_OUTPUT, + DEC_RIGHT_OUTPUT, + NUM_OUTPUTS + }; + enum LightIds { + NUM_LIGHTS + }; + + MidSide() { + config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); + configParam(ENC_WIDTH_PARAM, 0.f, 2.f, 1.f, "Encoder width", "%", 0, 100); + configParam(DEC_WIDTH_PARAM, 0.f, 2.f, 1.f, "Decoder width", "%", 0, 100); + } + + void process(const ProcessArgs &args) override { + using simd::float_4; + + // Encoder + { + int channels = std::max(inputs[ENC_LEFT_INPUT].getChannels(), inputs[ENC_RIGHT_INPUT].getChannels()); + outputs[ENC_MID_OUTPUT].setChannels(channels); + outputs[ENC_SIDES_OUTPUT].setChannels(channels); + + for (int c = 0; c < channels; c += 4) { + float_4 width = params[ENC_WIDTH_PARAM].getValue(); + width += inputs[ENC_WIDTH_INPUT].getPolyVoltageSimd(c) / 10 * 2; + width = simd::fmax(width, 0.f); + float_4 left = inputs[ENC_LEFT_INPUT].getVoltageSimd(c); + float_4 right = inputs[ENC_RIGHT_INPUT].getVoltageSimd(c); + float_4 mid = (left + right) / 2; + float_4 sides = (left - right) / 2 * width; + outputs[ENC_MID_OUTPUT].setVoltageSimd(mid, c); + outputs[ENC_SIDES_OUTPUT].setVoltageSimd(sides, c); + } + } + + // Decoder + { + int channels = std::max(inputs[DEC_MID_INPUT].getChannels(), inputs[DEC_SIDES_INPUT].getChannels()); + outputs[DEC_LEFT_OUTPUT].setChannels(channels); + outputs[DEC_RIGHT_OUTPUT].setChannels(channels); + + for (int c = 0; c < channels; c += 4) { + float_4 width = params[DEC_WIDTH_PARAM].getValue(); + width += inputs[DEC_WIDTH_INPUT].getPolyVoltageSimd(c) / 10 * 2; + width = simd::fmax(width, 0.f); + float_4 mid = inputs[DEC_MID_INPUT].getVoltageSimd(c); + float_4 sides = inputs[DEC_SIDES_INPUT].getVoltageSimd(c); + float_4 left = (mid + sides * width) / 2; + float_4 right = (mid - sides * width) / 2; + outputs[DEC_LEFT_OUTPUT].setVoltageSimd(left, c); + outputs[DEC_RIGHT_OUTPUT].setVoltageSimd(right, c); + } + } + } +}; + + +struct MidSideWidget : ModuleWidget { + MidSideWidget(MidSide *module) { + setModule(module); + setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/MidSide.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(18.699, 26.234)), module, MidSide::ENC_WIDTH_PARAM)); + addParam(createParamCentered(mm2px(Vec(18.699, 81.235)), module, MidSide::DEC_WIDTH_PARAM)); + + addInput(createInputCentered(mm2px(Vec(6.699, 26.234)), module, MidSide::ENC_WIDTH_INPUT)); + addInput(createInputCentered(mm2px(Vec(6.699, 41.234)), module, MidSide::ENC_LEFT_INPUT)); + addInput(createInputCentered(mm2px(Vec(18.699, 41.234)), module, MidSide::ENC_RIGHT_INPUT)); + addInput(createInputCentered(mm2px(Vec(6.699, 81.234)), module, MidSide::DEC_WIDTH_INPUT)); + addInput(createInputCentered(mm2px(Vec(6.699, 96.234)), module, MidSide::DEC_MID_INPUT)); + addInput(createInputCentered(mm2px(Vec(18.699, 96.234)), module, MidSide::DEC_SIDES_INPUT)); + + addOutput(createOutputCentered(mm2px(Vec(6.699, 57.253)), module, MidSide::ENC_MID_OUTPUT)); + addOutput(createOutputCentered(mm2px(Vec(18.699, 57.253)), module, MidSide::ENC_SIDES_OUTPUT)); + addOutput(createOutputCentered(mm2px(Vec(6.699, 112.252)), module, MidSide::DEC_LEFT_OUTPUT)); + addOutput(createOutputCentered(mm2px(Vec(18.699, 112.252)), module, MidSide::DEC_RIGHT_OUTPUT)); + } +}; + + +Model *modelMidSide = createModel("MidSide"); \ No newline at end of file diff --git a/src/plugin.cpp b/src/plugin.cpp index 86f0e0e..f70a267 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -28,4 +28,5 @@ void init(rack::Plugin *p) { p->addModel(modelMerge); p->addModel(modelSum); p->addModel(modelViz); + p->addModel(modelMidSide); } diff --git a/src/plugin.hpp b/src/plugin.hpp index 7e9a5cb..a468122 100644 --- a/src/plugin.hpp +++ b/src/plugin.hpp @@ -28,5 +28,5 @@ extern Model *modelSplit; extern Model *modelMerge; extern Model *modelSum; extern Model *modelViz; - +extern Model *modelMidSide;