diff --git a/res/VCVBezelBig.svg b/res/VCVBezelBig.svg
new file mode 100644
index 0000000..17d7d5e
--- /dev/null
+++ b/res/VCVBezelBig.svg
@@ -0,0 +1,50 @@
+
+
+
diff --git a/src/Compare.cpp b/src/Compare.cpp
index 25073fa..9520e2c 100644
--- a/src/Compare.cpp
+++ b/src/Compare.cpp
@@ -30,6 +30,8 @@ struct Compare : Module {
LIGHTS_LEN
};
+ dsp::ClockDivider lightDivider;
+
Compare() {
config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN);
configParam(B_PARAM, -10.f, 10.f, 0.f, "B offset", " V");
@@ -43,6 +45,8 @@ struct Compare : Module {
configOutput(LIMGATE_OUTPUT, "Limit gate");
configOutput(GREATER_OUTPUT, "A>B");
configOutput(LESS_OUTPUT, "A b ? 10.f : 0.f, c);
- if (a > b)
- anyGreater = true;
+ anyGreater = anyGreater || (a > b);
outputs[LESS_OUTPUT].setVoltage(a < b ? 10.f : 0.f, c);
- if (a < b)
- anyLess = true;
+ anyLess = anyLess || (a < b);
}
outputs[MAX_OUTPUT].setChannels(channels);
@@ -99,14 +99,17 @@ struct Compare : Module {
outputs[GREATER_OUTPUT].setChannels(channels);
outputs[LESS_OUTPUT].setChannels(channels);
- lights[CLIP_LIGHT + 0].setBrightnessSmooth(anyClipped && channels <= 1, args.sampleTime);
- lights[CLIP_LIGHT + 1].setBrightnessSmooth(anyClipped && channels > 1, args.sampleTime);
- lights[LIM_LIGHT + 0].setBrightnessSmooth(anyLimmed && channels <= 1, args.sampleTime);
- lights[LIM_LIGHT + 1].setBrightnessSmooth(anyLimmed && channels > 1, args.sampleTime);
- lights[GREATER_LIGHT + 0].setBrightnessSmooth(anyGreater && channels <= 1, args.sampleTime);
- lights[GREATER_LIGHT + 1].setBrightnessSmooth(anyGreater && channels > 1, args.sampleTime);
- lights[LESS_LIGHT + 0].setBrightnessSmooth(anyLess && channels <= 1, args.sampleTime);
- lights[LESS_LIGHT + 1].setBrightnessSmooth(anyLess && channels > 1, args.sampleTime);
+ if (lightDivider.process()) {
+ float lightTime = args.sampleTime * lightDivider.getDivision();
+ lights[CLIP_LIGHT + 0].setBrightnessSmooth(anyClipped && channels <= 1, lightTime);
+ lights[CLIP_LIGHT + 1].setBrightnessSmooth(anyClipped && channels > 1, lightTime);
+ lights[LIM_LIGHT + 0].setBrightnessSmooth(anyLimmed && channels <= 1, lightTime);
+ lights[LIM_LIGHT + 1].setBrightnessSmooth(anyLimmed && channels > 1, lightTime);
+ lights[GREATER_LIGHT + 0].setBrightnessSmooth(anyGreater && channels <= 1, lightTime);
+ lights[GREATER_LIGHT + 1].setBrightnessSmooth(anyGreater && channels > 1, lightTime);
+ lights[LESS_LIGHT + 0].setBrightnessSmooth(anyLess && channels <= 1, lightTime);
+ lights[LESS_LIGHT + 1].setBrightnessSmooth(anyLess && channels > 1, lightTime);
+ }
}
};
diff --git a/src/Logic.cpp b/src/Logic.cpp
index 186f3aa..7131cbb 100644
--- a/src/Logic.cpp
+++ b/src/Logic.cpp
@@ -88,16 +88,55 @@ struct Logic : Module {
// Set lights
if (lightDivider.process()) {
+ float lightTime = args.sampleTime * lightDivider.getDivision();
lights[B_BUTTON_LIGHT].setBrightness(bPush);
for (int i = 0; i < 8; i++) {
- lights[NOTA_LIGHT + 2 * i + 0].setBrightness(anyState[i] && channels == 1);
- lights[NOTA_LIGHT + 2 * i + 1].setBrightness(anyState[i] && channels > 1);
+ lights[NOTA_LIGHT + 2 * i + 0].setBrightnessSmooth(anyState[i] && channels == 1, lightTime);
+ lights[NOTA_LIGHT + 2 * i + 1].setBrightnessSmooth(anyState[i] && channels > 1, lightTime);
}
}
}
};
+struct VCVBezelBig : app::SvgSwitch {
+ VCVBezelBig() {
+ momentary = true;
+ addFrame(Svg::load(asset::plugin(pluginInstance, "res/VCVBezelBig.svg")));
+ }
+};
+
+
+template
+struct VCVBezelLightBig : TBase {
+ VCVBezelLightBig() {
+ this->borderColor = color::BLACK_TRANSPARENT;
+ this->bgColor = color::BLACK_TRANSPARENT;
+ this->box.size = mm2px(math::Vec(9.53, 9.53));
+ }
+};
+
+
+template
+struct LightButton : TBase {
+ app::ModuleLightWidget* light;
+
+ LightButton() {
+ light = new TLight;
+ // Move center of light to center of box
+ light->box.pos = this->box.size.div(2).minus(light->box.size.div(2));
+ this->addChild(light);
+ }
+
+ app::ModuleLightWidget* getLight() {
+ return light;
+ }
+};
+
+
+using VCVBezelLightBigWhite = LightButton>;
+
+
struct LogicWidget : ModuleWidget {
LogicWidget(Logic* module) {
setModule(module);
@@ -108,7 +147,7 @@ struct LogicWidget : ModuleWidget {
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(createLightParamCentered>(mm2px(Vec(12.7, 26.755)), module, Logic::B_PARAM, Logic::B_BUTTON_LIGHT));
+ addParam(createLightParamCentered(mm2px(Vec(12.7, 26.755)), module, Logic::B_PARAM, Logic::B_BUTTON_LIGHT));
addInput(createInputCentered(mm2px(Vec(7.299, 52.31)), module, Logic::A_INPUT));
addInput(createInputCentered(mm2px(Vec(18.136, 52.31)), module, Logic::B_INPUT));
diff --git a/src/Process.cpp b/src/Process.cpp
index 02e2b29..edde2c3 100644
--- a/src/Process.cpp
+++ b/src/Process.cpp
@@ -4,7 +4,7 @@
struct Process : Module {
enum ParamId {
SLEW_PARAM,
- PUSH_PARAM,
+ GATE_PARAM,
PARAMS_LEN
};
enum InputId {
@@ -23,25 +23,94 @@ struct Process : Module {
OUTPUTS_LEN
};
enum LightId {
+ GATE_LIGHT,
LIGHTS_LEN
};
+ bool state[16] = {};
+ float sample1[16] = {};
+ float sample2[16] = {};
+ float holdValue[16] = {};
+ float slewValue[16] = {};
+ float glideValue[16] = {};
+
Process() {
config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN);
- configParam(SLEW_PARAM, std::log2(1e-3f), std::log2(10.f), std::log2(0.1f), "Slew", " ms/V", 2, 1000);
- configButton(PUSH_PARAM, "Gate");
+ configParam(SLEW_PARAM, std::log2(1e-3f), std::log2(10.f), std::log2(1e-3f), "Slew", " ms/V", 2, 1000);
+ configButton(GATE_PARAM, "Gate");
configInput(SLEW_INPUT, "Slew");
configInput(IN_INPUT, "Voltage");
configInput(GATE_INPUT, "Gate");
configOutput(SH1_OUTPUT, "Sample & hold");
configOutput(SH2_OUTPUT, "Sample & hold 2");
- configOutput(TH_OUTPUT, "Trigger & hold");
- configOutput(HT_OUTPUT, "Hold & trigger");
+ configOutput(TH_OUTPUT, "Track & hold");
+ configOutput(HT_OUTPUT, "Hold & track");
configOutput(SLEW_OUTPUT, "Slew");
configOutput(GLIDE_OUTPUT, "Glide");
}
void process(const ProcessArgs& args) override {
+ int channels = inputs[IN_INPUT].getChannels();
+ bool gateButton = params[GATE_PARAM].getValue() > 0.f;
+
+ for (int c = 0; c < channels; c++) {
+ float in = inputs[IN_INPUT].getVoltage(c);
+
+ float slewPitch = -params[SLEW_PARAM].getValue() - inputs[SLEW_INPUT].getPolyVoltage(c);
+ // V/s
+ float slew = dsp::approxExp2_taylor5(slewPitch + 30.f) / 1073741824;
+
+ float gateValue = inputs[GATE_INPUT].getPolyVoltage(c);
+
+ if (!state[c]) {
+ if (gateValue >= 2.f || gateButton) {
+ // Triggered
+ state[c] = true;
+ // Hold and track
+ holdValue[c] = in;
+ // Sample and hold
+ sample2[c] = sample1[c];
+ sample1[c] = in;
+ // Glide
+ // TODO delay timer
+ glideValue[c] = in;
+ }
+ }
+ else {
+ if (gateValue <= 0.1f && !gateButton) {
+ // Untriggered
+ state[c] = false;
+ // Track and hold
+ holdValue[c] = in;
+ }
+ }
+
+ // Slew each value
+ float slewDelta = slew * args.sampleTime;
+ if (state[c]) {
+ slewValue[c] = in;
+ }
+ else {
+ slewValue[c] += clamp(in - slewValue[c], -slewDelta, slewDelta);
+ }
+ glideValue[c] += clamp(in - glideValue[c], -slewDelta, slewDelta);
+
+ outputs[SH1_OUTPUT].setVoltage(sample1[c], c);
+ outputs[SH2_OUTPUT].setVoltage(sample2[c], c);
+ outputs[TH_OUTPUT].setVoltage(state[c] ? holdValue[c] : in, c);
+ outputs[HT_OUTPUT].setVoltage(state[c] ? in : holdValue[c], c);
+ outputs[SLEW_OUTPUT].setVoltage(slewValue[c], c);
+ outputs[GLIDE_OUTPUT].setVoltage(glideValue[c], c);
+ }
+
+ outputs[SH1_OUTPUT].setChannels(channels);
+ outputs[SH2_OUTPUT].setChannels(channels);
+ outputs[TH_OUTPUT].setChannels(channels);
+ outputs[HT_OUTPUT].setChannels(channels);
+ outputs[SLEW_OUTPUT].setChannels(channels);
+ outputs[GLIDE_OUTPUT].setChannels(channels);
+
+ lights[GATE_LIGHT].setBrightness(gateButton);
}
};
@@ -57,7 +126,7 @@ struct ProcessWidget : ModuleWidget {
addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addParam(createParamCentered(mm2px(Vec(12.646, 26.755)), module, Process::SLEW_PARAM));
- // addParam(createParamCentered(mm2px(Vec(18.136, 52.31)), module, Process::PUSH_PARAM));
+ addParam(createLightParamCentered>(mm2px(Vec(18.136, 52.31)), module, Process::GATE_PARAM, Process::GATE_LIGHT));
addInput(createInputCentered(mm2px(Vec(7.299, 52.31)), module, Process::SLEW_INPUT));
addInput(createInputCentered(mm2px(Vec(7.297, 67.53)), module, Process::IN_INPUT));