diff --git a/plugin.json b/plugin.json
index f2f4f03..f333793 100644
--- a/plugin.json
+++ b/plugin.json
@@ -290,6 +290,16 @@
"Random",
"Sample and hold"
]
+ },
+ {
+ "slug": "CVMix",
+ "name": "CV Mix",
+ "description": "Mixes 3 CV signals with attenuverters",
+ "tags": [
+ "Utility",
+ "Polyphonic",
+ "Mixer"
+ ]
}
]
}
\ No newline at end of file
diff --git a/res/CVMix.svg b/res/CVMix.svg
new file mode 100644
index 0000000..aa175de
--- /dev/null
+++ b/res/CVMix.svg
@@ -0,0 +1,367 @@
+
+
diff --git a/src/CVMix.cpp b/src/CVMix.cpp
new file mode 100644
index 0000000..499ae79
--- /dev/null
+++ b/src/CVMix.cpp
@@ -0,0 +1,86 @@
+#include "plugin.hpp"
+
+
+struct CVMix : Module {
+ enum ParamId {
+ ENUMS(LEVEL_PARAMS, 3),
+ PARAMS_LEN
+ };
+ enum InputId {
+ ENUMS(CV_INPUTS, 3),
+ INPUTS_LEN
+ };
+ enum OutputId {
+ MIX_OUTPUT,
+ OUTPUTS_LEN
+ };
+ enum LightId {
+ LIGHTS_LEN
+ };
+
+ CVMix() {
+ config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN);
+ for (int i = 0; i < 3; i++)
+ configParam(LEVEL_PARAMS + i, -1.f, 1.f, 0.f, string::f("Level %d", i + 1), "%", 0, 100);
+ for (int i = 0; i < 3; i++)
+ configInput(CV_INPUTS + i, string::f("CV %d", i + 1));
+ configOutput(MIX_OUTPUT, "Mix");
+ }
+
+ void process(const ProcessArgs& args) override {
+ if (!outputs[MIX_OUTPUT].isConnected())
+ return;
+
+ // Get number of channels
+ int channels = 1;
+ for (int i = 0; i < 3; i++)
+ channels = std::max(channels, inputs[CV_INPUTS + i].getChannels());
+
+ for (int c = 0; c < channels; c++) {
+ // Sum CV inputs
+ float mix = 0.f;
+ for (int i = 0; i < 3; i++) {
+ float cv;
+ // Normalize first input to 10V
+ if (i == 0)
+ cv = inputs[CV_INPUTS + i].getNormalVoltage(10.f, c);
+ else
+ cv = inputs[CV_INPUTS + i].getVoltage(c);
+
+ // Apply gain
+ cv *= params[LEVEL_PARAMS + i].getValue();
+ mix += cv;
+ }
+
+ // Set mix output
+ outputs[MIX_OUTPUT].setVoltage(mix, c);
+ }
+ outputs[MIX_OUTPUT].setChannels(channels);
+ }
+};
+
+
+struct CVMixWidget : ModuleWidget {
+ CVMixWidget(CVMix* module) {
+ setModule(module);
+ setPanel(createPanel(asset::plugin(pluginInstance, "res/CVMix.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, CVMix::LEVEL_PARAMS + 0));
+ addParam(createParamCentered(mm2px(Vec(7.62, 41.327)), module, CVMix::LEVEL_PARAMS + 1));
+ addParam(createParamCentered(mm2px(Vec(7.62, 57.922)), module, CVMix::LEVEL_PARAMS + 2));
+
+ addInput(createInputCentered(mm2px(Vec(7.62, 76.539)), module, CVMix::CV_INPUTS + 0));
+ addInput(createInputCentered(mm2px(Vec(7.62, 86.699)), module, CVMix::CV_INPUTS + 1));
+ addInput(createInputCentered(mm2px(Vec(7.62, 96.859)), module, CVMix::CV_INPUTS + 2));
+
+ addOutput(createOutputCentered(mm2px(Vec(7.62, 113.115)), module, CVMix::MIX_OUTPUT));
+ }
+};
+
+
+Model* modelCVMix = createModel("CVMix");
\ No newline at end of file
diff --git a/src/plugin.cpp b/src/plugin.cpp
index 07d5017..4816544 100644
--- a/src/plugin.cpp
+++ b/src/plugin.cpp
@@ -34,4 +34,5 @@ void init(Plugin* p) {
p->addModel(modelMidSide);
p->addModel(modelNoise);
p->addModel(modelRandom);
+ p->addModel(modelCVMix);
}
diff --git a/src/plugin.hpp b/src/plugin.hpp
index fa5bd9c..09e4ba2 100644
--- a/src/plugin.hpp
+++ b/src/plugin.hpp
@@ -34,6 +34,7 @@ extern Model* modelViz;
extern Model* modelMidSide;
extern Model* modelNoise;
extern Model* modelRandom;
+extern Model* modelCVMix;
struct DigitalDisplay : Widget {