Browse Source

Added Hexmix VCA.

tags/v1.1.0^2
hemmer 4 years ago
parent
commit
30536e4ce4
5 changed files with 29291 additions and 1 deletions
  1. +11
    -0
      plugin.json
  2. +29100
    -0
      res/HexmixVCA.svg
  3. +178
    -0
      src/HexmixVCA.cpp
  4. +1
    -0
      src/plugin.cpp
  5. +1
    -1
      src/plugin.hpp

+ 11
- 0
plugin.json View File

@@ -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"
]
}
]
}

+ 29100
- 0
res/HexmixVCA.svg
File diff suppressed because it is too large
View File


+ 178
- 0
src/HexmixVCA.cpp View File

@@ -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<Knurlie>(Vec(RACK_GRID_WIDTH, 0)));
addChild(createWidget<Knurlie>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
addChild(createWidget<Knurlie>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addChild(createWidget<Knurlie>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addParam(createParamCentered<BefacoTinyKnobWhite>(mm2px(Vec(20.412, 15.51)), module, HexmixVCA::SHAPE_PARAM + 0));
addParam(createParamCentered<BefacoTinyKnobWhite>(mm2px(Vec(20.412, 34.115)), module, HexmixVCA::SHAPE_PARAM + 1));
addParam(createParamCentered<BefacoTinyKnobWhite>(mm2px(Vec(20.412, 52.72)), module, HexmixVCA::SHAPE_PARAM + 2));
addParam(createParamCentered<BefacoTinyKnobWhite>(mm2px(Vec(20.412, 71.325)), module, HexmixVCA::SHAPE_PARAM + 3));
addParam(createParamCentered<BefacoTinyKnobWhite>(mm2px(Vec(20.412, 89.93)), module, HexmixVCA::SHAPE_PARAM + 4));
addParam(createParamCentered<BefacoTinyKnobWhite>(mm2px(Vec(20.412, 108.536)), module, HexmixVCA::SHAPE_PARAM + 5));
addParam(createParamCentered<BefacoTinyKnobRed>(mm2px(Vec(35.458, 15.51)), module, HexmixVCA::VOL_PARAM + 0));
addParam(createParamCentered<BefacoTinyKnobRed>(mm2px(Vec(35.458, 34.115)), module, HexmixVCA::VOL_PARAM + 1));
addParam(createParamCentered<BefacoTinyKnobRed>(mm2px(Vec(35.458, 52.72)), module, HexmixVCA::VOL_PARAM + 2));
addParam(createParamCentered<BefacoTinyKnobRed>(mm2px(Vec(35.458, 71.325)), module, HexmixVCA::VOL_PARAM + 3));
addParam(createParamCentered<BefacoTinyKnobRed>(mm2px(Vec(35.458, 89.93)), module, HexmixVCA::VOL_PARAM + 4));
addParam(createParamCentered<BefacoTinyKnobRed>(mm2px(Vec(35.458, 108.536)), module, HexmixVCA::VOL_PARAM + 5));
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(6.581, 15.51)), module, HexmixVCA::IN_INPUT + 0));
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(6.581, 34.115)), module, HexmixVCA::IN_INPUT + 1));
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(6.581, 52.72)), module, HexmixVCA::IN_INPUT + 2));
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(6.581, 71.325)), module, HexmixVCA::IN_INPUT + 3));
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(6.581, 89.93)), module, HexmixVCA::IN_INPUT + 4));
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(6.581, 108.536)), module, HexmixVCA::IN_INPUT + 5));
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(52.083, 15.51)), module, HexmixVCA::CV_INPUT + 0));
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(52.083, 34.115)), module, HexmixVCA::CV_INPUT + 1));
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(52.083, 52.72)), module, HexmixVCA::CV_INPUT + 2));
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(52.083, 71.325)), module, HexmixVCA::CV_INPUT + 3));
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(52.083, 89.93)), module, HexmixVCA::CV_INPUT + 4));
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(52.083, 108.536)), module, HexmixVCA::CV_INPUT + 5));
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(64.222, 15.51)), module, HexmixVCA::OUT_OUTPUT + 0));
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(64.222, 34.115)), module, HexmixVCA::OUT_OUTPUT + 1));
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(64.222, 52.72)), module, HexmixVCA::OUT_OUTPUT + 2));
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(64.222, 71.325)), module, HexmixVCA::OUT_OUTPUT + 3));
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(64.222, 89.93)), module, HexmixVCA::OUT_OUTPUT + 4));
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(64.222, 108.536)), module, HexmixVCA::OUT_OUTPUT + 5));
}
};
Model* modelHexmixVCA = createModel<HexmixVCA, HexmixVCAWidget>("HexmixVCA");

+ 1
- 0
src/plugin.cpp View File

@@ -14,4 +14,5 @@ void init(rack::Plugin *p) {
p->addModel(modelSlewLimiter);
p->addModel(modelDualAtenuverter);
p->addModel(modelPercall);
p->addModel(modelHexmixVCA);
}

+ 1
- 1
src/plugin.hpp View File

@@ -14,7 +14,7 @@ extern Model *modelMixer;
extern Model *modelSlewLimiter;
extern Model *modelDualAtenuverter;
extern Model *modelPercall;
extern Model *modelHexmixVCA;

struct Knurlie : SvgScrew {
Knurlie() {


Loading…
Cancel
Save