From 30536e4ce4709a75dc611f388854a3ca9277ed6e Mon Sep 17 00:00:00 2001 From: hemmer <915048+hemmer@users.noreply.github.com> Date: Sun, 4 Apr 2021 18:38:41 +0100 Subject: [PATCH] Added Hexmix VCA. --- plugin.json | 11 + res/HexmixVCA.svg | 29100 ++++++++++++++++++++++++++++++++++++++++++++ src/HexmixVCA.cpp | 178 + src/plugin.cpp | 1 + src/plugin.hpp | 2 +- 5 files changed, 29291 insertions(+), 1 deletion(-) create mode 100644 res/HexmixVCA.svg create mode 100644 src/HexmixVCA.cpp diff --git a/plugin.json b/plugin.json index 39f7e68..6cfb7c4 100644 --- a/plugin.json +++ b/plugin.json @@ -99,6 +99,17 @@ "Mixer", "Hardware clone" ] + }, + { + "slug": "HexmixVCA", + "name": "HexmixVCA", + "description": "Six channel VCA with response curve range from logarithmic to linear and to exponential", + "modularGridUrl": "https://www.modulargrid.net/e/befaco-hexmix-vca", + "tags": [ + "Mixer", + "Hardware clone", + "VCA" + ] } ] } \ No newline at end of file diff --git a/res/HexmixVCA.svg b/res/HexmixVCA.svg new file mode 100644 index 0000000..8b6c78e --- /dev/null +++ b/res/HexmixVCA.svg @@ -0,0 +1,29100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/HexmixVCA.cpp b/src/HexmixVCA.cpp new file mode 100644 index 0000000..90a6e98 --- /dev/null +++ b/src/HexmixVCA.cpp @@ -0,0 +1,178 @@ +#include "plugin.hpp" + +struct BefacoTinyKnobRed : app::SvgKnob { + BefacoTinyKnobRed() { + minAngle = -0.8 * M_PI; + maxAngle = 0.8 * M_PI; + setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/BefacoTinyKnobRed.svg"))); + } +}; + +struct BefacoTinyKnobWhite : app::SvgKnob { + BefacoTinyKnobWhite() { + minAngle = -0.8 * M_PI; + maxAngle = 0.8 * M_PI; + setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/BefacoTinyKnob.svg"))); + } +}; + +static float gainFunction(float x, float shape) { + float lin = x; + if (shape > 0.f) { + float log = 11.f * x / (10.f * x + 1.f); + return crossfade(lin, log, shape); + } + else { + float exp = std::pow(x, 4); + return crossfade(lin, exp, -shape); + } +} + +struct HexmixVCA : Module { + enum ParamIds { + ENUMS(SHAPE_PARAM, 6), + ENUMS(VOL_PARAM, 6), + NUM_PARAMS + }; + enum InputIds { + ENUMS(IN_INPUT, 6), + ENUMS(CV_INPUT, 6), + NUM_INPUTS + }; + enum OutputIds { + ENUMS(OUT_OUTPUT, 6), + NUM_OUTPUTS + }; + enum LightIds { + NUM_LIGHTS + }; + + const static int numRows = 6; + dsp::ClockDivider cvDivider; + float outputLevels[numRows] = {1.f}; + float shapes[numRows] = {0.f}; + + HexmixVCA() { + config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); + for (int i = 0; i < numRows; ++i) { + configParam(SHAPE_PARAM + i, -1.f, 1.f, 0.f, "VCA response"); + configParam(VOL_PARAM + i, 0.f, 1.f, 1.f, "Output level"); + } + cvDivider.setDivision(16); + } + + void process(const ProcessArgs& args) override { + simd::float_4 mix[4] = {}; + int maxChannels = 1; + + // only calculate gains/shapes every 16 samples + if (cvDivider.process()) { + for (int row = 0; row < numRows; ++row) { + shapes[row] = params[SHAPE_PARAM + row].getValue(); + outputLevels[row] = params[VOL_PARAM + row].getValue(); + } + } + + for (int row = 0; row < numRows; ++row) { + + int channels = 1; + simd::float_4 in[4] = {}; + bool input_is_connected = inputs[IN_INPUT + row].isConnected(); + if (input_is_connected) { + channels = inputs[row].getChannels(); + maxChannels = std::max(maxChannels, channels); + + float cvGain = clamp(inputs[CV_INPUT + row].getNormalVoltage(10.f) / 10.f, 0.f, 1.f); + float gain = gainFunction(cvGain, shapes[row]); + + for (int c = 0; c < channels; c += 4) { + in[c / 4] = simd::float_4::load(inputs[row].getVoltages(c)) * gain * outputLevels[row]; + } + } + + // if not the final row + if (row != numRows - 1) { + if (outputs[OUT_OUTPUT + row].isConnected()) { + outputs[OUT_OUTPUT + row].setChannels(channels); + for (int c = 0; c < channels; c += 4) { + in[c / 4].store(outputs[OUT_OUTPUT + row].getVoltages(c)); + } + } + else { + // else add to mix + for (int c = 0; c < channels; c += 4) { + mix[c / 4] += in[c / 4]; + } + } + } + // final row + else { + if (outputs[OUT_OUTPUT + row].isConnected()) { + + outputs[OUT_OUTPUT + row].setChannels(maxChannels); + + // last channel must always go into mix + for (int c = 0; c < channels; c += 4) { + mix[c / 4] += in[c / 4]; + } + + for (int c = 0; c < maxChannels; c += 4) { + mix[c / 4].store(outputs[OUT_OUTPUT + row].getVoltages(c)); + } + } + } + + } + } +}; + + +struct HexmixVCAWidget : ModuleWidget { + HexmixVCAWidget(HexmixVCA* module) { + setModule(module); + setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/HexmixVCA.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(20.412, 15.51)), module, HexmixVCA::SHAPE_PARAM + 0)); + addParam(createParamCentered(mm2px(Vec(20.412, 34.115)), module, HexmixVCA::SHAPE_PARAM + 1)); + addParam(createParamCentered(mm2px(Vec(20.412, 52.72)), module, HexmixVCA::SHAPE_PARAM + 2)); + addParam(createParamCentered(mm2px(Vec(20.412, 71.325)), module, HexmixVCA::SHAPE_PARAM + 3)); + addParam(createParamCentered(mm2px(Vec(20.412, 89.93)), module, HexmixVCA::SHAPE_PARAM + 4)); + addParam(createParamCentered(mm2px(Vec(20.412, 108.536)), module, HexmixVCA::SHAPE_PARAM + 5)); + + addParam(createParamCentered(mm2px(Vec(35.458, 15.51)), module, HexmixVCA::VOL_PARAM + 0)); + addParam(createParamCentered(mm2px(Vec(35.458, 34.115)), module, HexmixVCA::VOL_PARAM + 1)); + addParam(createParamCentered(mm2px(Vec(35.458, 52.72)), module, HexmixVCA::VOL_PARAM + 2)); + addParam(createParamCentered(mm2px(Vec(35.458, 71.325)), module, HexmixVCA::VOL_PARAM + 3)); + addParam(createParamCentered(mm2px(Vec(35.458, 89.93)), module, HexmixVCA::VOL_PARAM + 4)); + addParam(createParamCentered(mm2px(Vec(35.458, 108.536)), module, HexmixVCA::VOL_PARAM + 5)); + + addInput(createInputCentered(mm2px(Vec(6.581, 15.51)), module, HexmixVCA::IN_INPUT + 0)); + addInput(createInputCentered(mm2px(Vec(6.581, 34.115)), module, HexmixVCA::IN_INPUT + 1)); + addInput(createInputCentered(mm2px(Vec(6.581, 52.72)), module, HexmixVCA::IN_INPUT + 2)); + addInput(createInputCentered(mm2px(Vec(6.581, 71.325)), module, HexmixVCA::IN_INPUT + 3)); + addInput(createInputCentered(mm2px(Vec(6.581, 89.93)), module, HexmixVCA::IN_INPUT + 4)); + addInput(createInputCentered(mm2px(Vec(6.581, 108.536)), module, HexmixVCA::IN_INPUT + 5)); + + addInput(createInputCentered(mm2px(Vec(52.083, 15.51)), module, HexmixVCA::CV_INPUT + 0)); + addInput(createInputCentered(mm2px(Vec(52.083, 34.115)), module, HexmixVCA::CV_INPUT + 1)); + addInput(createInputCentered(mm2px(Vec(52.083, 52.72)), module, HexmixVCA::CV_INPUT + 2)); + addInput(createInputCentered(mm2px(Vec(52.083, 71.325)), module, HexmixVCA::CV_INPUT + 3)); + addInput(createInputCentered(mm2px(Vec(52.083, 89.93)), module, HexmixVCA::CV_INPUT + 4)); + addInput(createInputCentered(mm2px(Vec(52.083, 108.536)), module, HexmixVCA::CV_INPUT + 5)); + + addOutput(createOutputCentered(mm2px(Vec(64.222, 15.51)), module, HexmixVCA::OUT_OUTPUT + 0)); + addOutput(createOutputCentered(mm2px(Vec(64.222, 34.115)), module, HexmixVCA::OUT_OUTPUT + 1)); + addOutput(createOutputCentered(mm2px(Vec(64.222, 52.72)), module, HexmixVCA::OUT_OUTPUT + 2)); + addOutput(createOutputCentered(mm2px(Vec(64.222, 71.325)), module, HexmixVCA::OUT_OUTPUT + 3)); + addOutput(createOutputCentered(mm2px(Vec(64.222, 89.93)), module, HexmixVCA::OUT_OUTPUT + 4)); + addOutput(createOutputCentered(mm2px(Vec(64.222, 108.536)), module, HexmixVCA::OUT_OUTPUT + 5)); + } +}; + + +Model* modelHexmixVCA = createModel("HexmixVCA"); \ No newline at end of file diff --git a/src/plugin.cpp b/src/plugin.cpp index ef5e791..cf8fa03 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -14,4 +14,5 @@ void init(rack::Plugin *p) { p->addModel(modelSlewLimiter); p->addModel(modelDualAtenuverter); p->addModel(modelPercall); + p->addModel(modelHexmixVCA); } diff --git a/src/plugin.hpp b/src/plugin.hpp index bd0c725..d2997f0 100644 --- a/src/plugin.hpp +++ b/src/plugin.hpp @@ -14,7 +14,7 @@ extern Model *modelMixer; extern Model *modelSlewLimiter; extern Model *modelDualAtenuverter; extern Model *modelPercall; - +extern Model *modelHexmixVCA; struct Knurlie : SvgScrew { Knurlie() {