@@ -97,20 +97,20 @@ struct MidSideWidget : ModuleWidget { | |||||
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | ||||
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | ||||
addParam(createParamCentered<RoundSmallBlackKnob>(mm2px(Vec(18.699, 26.234)), module, MidSide::ENC_WIDTH_PARAM)); | |||||
addParam(createParamCentered<RoundSmallBlackKnob>(mm2px(Vec(18.699, 81.235)), module, MidSide::DEC_WIDTH_PARAM)); | |||||
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(6.699, 26.234)), module, MidSide::ENC_WIDTH_INPUT)); | |||||
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(6.699, 41.234)), module, MidSide::ENC_LEFT_INPUT)); | |||||
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(18.699, 41.234)), module, MidSide::ENC_RIGHT_INPUT)); | |||||
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(6.699, 81.234)), module, MidSide::DEC_WIDTH_INPUT)); | |||||
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(6.699, 96.234)), module, MidSide::DEC_MID_INPUT)); | |||||
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(18.699, 96.234)), module, MidSide::DEC_SIDES_INPUT)); | |||||
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(6.699, 57.253)), module, MidSide::ENC_MID_OUTPUT)); | |||||
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(18.699, 57.253)), module, MidSide::ENC_SIDES_OUTPUT)); | |||||
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(6.699, 112.252)), module, MidSide::DEC_LEFT_OUTPUT)); | |||||
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(18.699, 112.252)), module, MidSide::DEC_RIGHT_OUTPUT)); | |||||
addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(7.285, 25.203)), module, MidSide::ENC_WIDTH_PARAM)); | |||||
addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(7.285, 80.583)), module, MidSide::DEC_WIDTH_PARAM)); | |||||
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(18.122, 25.142)), module, MidSide::ENC_WIDTH_INPUT)); | |||||
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.285, 41.373)), module, MidSide::ENC_LEFT_INPUT)); | |||||
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(18.122, 41.373)), module, MidSide::ENC_RIGHT_INPUT)); | |||||
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(18.122, 80.603)), module, MidSide::DEC_WIDTH_INPUT)); | |||||
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.285, 96.859)), module, MidSide::DEC_MID_INPUT)); | |||||
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(18.122, 96.859)), module, MidSide::DEC_SIDES_INPUT)); | |||||
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.285, 57.679)), module, MidSide::ENC_MID_OUTPUT)); | |||||
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(18.122, 57.679)), module, MidSide::ENC_SIDES_OUTPUT)); | |||||
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.285, 113.115)), module, MidSide::DEC_LEFT_OUTPUT)); | |||||
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(18.122, 113.115)), module, MidSide::DEC_RIGHT_OUTPUT)); | |||||
} | } | ||||
}; | }; | ||||
@@ -167,7 +167,7 @@ struct OctaveWidget : ModuleWidget { | |||||
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.62, 113.115)), module, Octave::PITCH_OUTPUT)); | addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.62, 113.115)), module, Octave::PITCH_OUTPUT)); | ||||
OctaveDisplay* display = createWidget<OctaveDisplay>(mm2px(Vec(-0.011, 13.039))); | |||||
OctaveDisplay* display = createWidget<OctaveDisplay>(mm2px(Vec(0.0, 13.039))); | |||||
display->box.size = mm2px(Vec(15.263, 55.88)); | display->box.size = mm2px(Vec(15.263, 55.88)); | ||||
display->setModule(module); | display->setModule(module); | ||||
addChild(display); | addChild(display); | ||||
@@ -133,8 +133,13 @@ struct QuantizerButton : OpaqueWidget { | |||||
return; | return; | ||||
const float margin = mm2px(1.5); | const float margin = mm2px(1.5); | ||||
Rect r = box.zeroPos().grow(Vec(margin, margin / 2).neg()); | |||||
Rect r = box.zeroPos(); | |||||
nvgBeginPath(args.vg); | |||||
nvgRect(args.vg, RECT_ARGS(r)); | |||||
nvgFillColor(args.vg, nvgRGB(0x12, 0x12, 0x12)); | |||||
nvgFill(args.vg); | |||||
r = r.shrink(Vec(margin, margin / 2)); | |||||
nvgBeginPath(args.vg); | nvgBeginPath(args.vg); | ||||
nvgRect(args.vg, RECT_ARGS(r)); | nvgRect(args.vg, RECT_ARGS(r)); | ||||
if (module ? module->playingNotes[note] : (note == 0)) { | if (module ? module->playingNotes[note] : (note == 0)) { | ||||
@@ -172,13 +177,28 @@ struct QuantizerButton : OpaqueWidget { | |||||
struct QuantizerDisplay : LedDisplay { | struct QuantizerDisplay : LedDisplay { | ||||
void setModule(Quantizer* module) { | void setModule(Quantizer* module) { | ||||
const float margin = mm2px(1.5) / 2; | |||||
const int notes = 12; | |||||
const float height = box.size.y - 2 * margin; | |||||
for (int note = 0; note < notes; note++) { | |||||
// White notes | |||||
static const std::vector<int> whiteNotes = {0, 2, 4, 5, 7, 9, 11}; | |||||
for (size_t i = 0; i < whiteNotes.size(); i++) { | |||||
int note = whiteNotes[i]; | |||||
QuantizerButton* quantizerButton = new QuantizerButton(); | |||||
quantizerButton->box.pos = Vec(0, i * box.size.y / 7); | |||||
quantizerButton->box.size = Vec(box.size.x, box.size.y / 7); | |||||
quantizerButton->module = module; | |||||
quantizerButton->note = note; | |||||
addChild(quantizerButton); | |||||
} | |||||
// Black notes | |||||
static const std::vector<int> blackNotes = {1, 3, 6, 8, 10}; | |||||
static const std::vector<float> blackPos = {1, 2, 4, 5, 6}; | |||||
for (size_t i = 0; i < blackNotes.size(); i++) { | |||||
int note = blackNotes[i]; | |||||
float height = box.size.y * 0.1f; | |||||
float width = box.size.x * 0.6f; | |||||
float pos = blackPos[i] / 7.f * box.size.y - height / 2; | |||||
QuantizerButton* quantizerButton = new QuantizerButton(); | QuantizerButton* quantizerButton = new QuantizerButton(); | ||||
quantizerButton->box.pos = Vec(0, margin + height / notes * note); | |||||
quantizerButton->box.size = Vec(box.size.x, height / notes); | |||||
quantizerButton->box.pos = Vec(0, pos); | |||||
quantizerButton->box.size = Vec(width, height); | |||||
quantizerButton->module = module; | quantizerButton->module = module; | ||||
quantizerButton->note = note; | quantizerButton->note = note; | ||||
addChild(quantizerButton); | addChild(quantizerButton); | ||||
@@ -31,7 +31,7 @@ struct SequentialSwitch : Module { | |||||
SequentialSwitch() { | SequentialSwitch() { | ||||
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | ||||
configSwitch(STEPS_PARAM, 0.0, 2.0, 0.0, "Steps", {"4", "3", "2"}); | |||||
configSwitch(STEPS_PARAM, 0.0, 2.0, 2.0, "Steps", {"2", "3", "4"}); | |||||
configInput(CLOCK_INPUT, "Clock"); | configInput(CLOCK_INPUT, "Clock"); | ||||
configInput(RESET_INPUT, "Reset"); | configInput(RESET_INPUT, "Reset"); | ||||
if (INPUTS == 1) { | if (INPUTS == 1) { | ||||
@@ -64,7 +64,7 @@ struct SequentialSwitch : Module { | |||||
if (resetTrigger.process(rescale(inputs[RESET_INPUT].getVoltage(), 0.1f, 2.f, 0.f, 1.f))) { | if (resetTrigger.process(rescale(inputs[RESET_INPUT].getVoltage(), 0.1f, 2.f, 0.f, 1.f))) { | ||||
index = 0; | index = 0; | ||||
} | } | ||||
int length = 4 - (int) std::round(params[STEPS_PARAM].getValue()); | |||||
int length = 2 + (int) std::round(params[STEPS_PARAM].getValue()); | |||||
if (index >= length) | if (index >= length) | ||||
index = 0; | index = 0; | ||||
@@ -117,6 +117,20 @@ struct SequentialSwitch : Module { | |||||
} | } | ||||
} | } | ||||
} | } | ||||
void fromJson(json_t* rootJ) override { | |||||
Module::fromJson(rootJ); | |||||
// Get version to check if we should transform STEPS_PARAM | |||||
json_t* versionJ = json_object_get(rootJ, "version"); | |||||
if (versionJ) { | |||||
std::string version = json_string_value(versionJ); | |||||
if (string::startsWith(version, "0.") || string::startsWith(version, "1.")) { | |||||
DEBUG("steps %f", params[STEPS_PARAM].getValue()); | |||||
params[STEPS_PARAM].setValue(2 - params[STEPS_PARAM].getValue()); | |||||
} | |||||
} | |||||
} | |||||
}; | }; | ||||
@@ -132,7 +146,7 @@ struct SequentialSwitch1Widget : ModuleWidget { | |||||
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | ||||
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | ||||
addParam(createParamCentered<CKSSThree>(mm2px(Vec(7.555, 20.942)), module, TSequentialSwitch::STEPS_PARAM)); | |||||
addParam(createParamCentered<CKSSThreeHorizontal>(mm2px(Vec(7.555, 20.942)), module, TSequentialSwitch::STEPS_PARAM)); | |||||
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.555, 33.831)), module, TSequentialSwitch::CLOCK_INPUT)); | addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.555, 33.831)), module, TSequentialSwitch::CLOCK_INPUT)); | ||||
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.555, 50.126)), module, TSequentialSwitch::RESET_INPUT)); | addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.555, 50.126)), module, TSequentialSwitch::RESET_INPUT)); | ||||
@@ -143,10 +157,10 @@ struct SequentialSwitch1Widget : ModuleWidget { | |||||
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.555, 102.927)), module, TSequentialSwitch::OUT_OUTPUTS + 2)); | addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.555, 102.927)), module, TSequentialSwitch::OUT_OUTPUTS + 2)); | ||||
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.555, 113.087)), module, TSequentialSwitch::OUT_OUTPUTS + 3)); | addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.555, 113.087)), module, TSequentialSwitch::OUT_OUTPUTS + 3)); | ||||
addChild(createLightCentered<TinyLight<RedLight>>(mm2px(Vec(11.28, 78.863)), module, TSequentialSwitch::CHANNEL_LIGHTS + 0)); | |||||
addChild(createLightCentered<TinyLight<RedLight>>(mm2px(Vec(11.28, 89.023)), module, TSequentialSwitch::CHANNEL_LIGHTS + 1)); | |||||
addChild(createLightCentered<TinyLight<RedLight>>(mm2px(Vec(11.28, 99.183)), module, TSequentialSwitch::CHANNEL_LIGHTS + 2)); | |||||
addChild(createLightCentered<TinyLight<RedLight>>(mm2px(Vec(11.28, 109.343)), module, TSequentialSwitch::CHANNEL_LIGHTS + 3)); | |||||
addChild(createLightCentered<TinyLight<YellowLight>>(mm2px(Vec(11.28, 78.863)), module, TSequentialSwitch::CHANNEL_LIGHTS + 0)); | |||||
addChild(createLightCentered<TinyLight<YellowLight>>(mm2px(Vec(11.28, 89.023)), module, TSequentialSwitch::CHANNEL_LIGHTS + 1)); | |||||
addChild(createLightCentered<TinyLight<YellowLight>>(mm2px(Vec(11.28, 99.183)), module, TSequentialSwitch::CHANNEL_LIGHTS + 2)); | |||||
addChild(createLightCentered<TinyLight<YellowLight>>(mm2px(Vec(11.28, 109.343)), module, TSequentialSwitch::CHANNEL_LIGHTS + 3)); | |||||
} | } | ||||
}; | }; | ||||
@@ -166,7 +180,7 @@ struct SequentialSwitch2Widget : ModuleWidget { | |||||
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | ||||
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | ||||
addParam(createParamCentered<CKSSThree>(mm2px(Vec(7.8, 20.942)), module, TSequentialSwitch::STEPS_PARAM)); | |||||
addParam(createParamCentered<CKSSThreeHorizontal>(mm2px(Vec(7.8, 20.942)), module, TSequentialSwitch::STEPS_PARAM)); | |||||
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.8, 33.831)), module, TSequentialSwitch::CLOCK_INPUT)); | addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.8, 33.831)), module, TSequentialSwitch::CLOCK_INPUT)); | ||||
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.8, 50.126)), module, TSequentialSwitch::RESET_INPUT)); | addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.8, 50.126)), module, TSequentialSwitch::RESET_INPUT)); | ||||
@@ -177,10 +191,10 @@ struct SequentialSwitch2Widget : ModuleWidget { | |||||
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.8, 113.115)), module, TSequentialSwitch::OUT_OUTPUTS + 0)); | addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.8, 113.115)), module, TSequentialSwitch::OUT_OUTPUTS + 0)); | ||||
addChild(createLightCentered<TinyLight<RedLight>>(mm2px(Vec(11.526, 63.259)), module, TSequentialSwitch::CHANNEL_LIGHTS + 0)); | |||||
addChild(createLightCentered<TinyLight<RedLight>>(mm2px(Vec(11.526, 72.795)), module, TSequentialSwitch::CHANNEL_LIGHTS + 1)); | |||||
addChild(createLightCentered<TinyLight<RedLight>>(mm2px(Vec(11.526, 82.955)), module, TSequentialSwitch::CHANNEL_LIGHTS + 2)); | |||||
addChild(createLightCentered<TinyLight<RedLight>>(mm2px(Vec(11.526, 93.115)), module, TSequentialSwitch::CHANNEL_LIGHTS + 3)); | |||||
addChild(createLightCentered<TinyLight<YellowLight>>(mm2px(Vec(11.526, 63.259)), module, TSequentialSwitch::CHANNEL_LIGHTS + 0)); | |||||
addChild(createLightCentered<TinyLight<YellowLight>>(mm2px(Vec(11.526, 72.795)), module, TSequentialSwitch::CHANNEL_LIGHTS + 1)); | |||||
addChild(createLightCentered<TinyLight<YellowLight>>(mm2px(Vec(11.526, 82.955)), module, TSequentialSwitch::CHANNEL_LIGHTS + 2)); | |||||
addChild(createLightCentered<TinyLight<YellowLight>>(mm2px(Vec(11.526, 93.115)), module, TSequentialSwitch::CHANNEL_LIGHTS + 3)); | |||||
} | } | ||||
}; | }; | ||||
@@ -0,0 +1,168 @@ | |||||
#include "plugin.hpp" | |||||
struct VCA_1 : Module { | |||||
enum ParamIds { | |||||
LEVEL_PARAM, | |||||
EXP_PARAM, // removed from panel in 2.0, still in context menu | |||||
NUM_PARAMS | |||||
}; | |||||
enum InputIds { | |||||
CV_INPUT, | |||||
IN_INPUT, | |||||
NUM_INPUTS | |||||
}; | |||||
enum OutputIds { | |||||
OUT_OUTPUT, | |||||
NUM_OUTPUTS | |||||
}; | |||||
enum LightIds { | |||||
NUM_LIGHTS | |||||
}; | |||||
int lastChannels = 1; | |||||
float lastGains[16] = {}; | |||||
VCA_1() { | |||||
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | |||||
configParam(LEVEL_PARAM, 0.0, 1.0, 1.0, "Level", "%", 0, 100); | |||||
configSwitch(EXP_PARAM, 0.0, 1.0, 1.0, "Response mode", {"Exponential", "Linear"}); | |||||
configInput(CV_INPUT, "CV"); | |||||
configInput(IN_INPUT, "Channel"); | |||||
configOutput(OUT_OUTPUT, "Channel"); | |||||
configBypass(IN_INPUT, OUT_OUTPUT); | |||||
} | |||||
void process(const ProcessArgs& args) override { | |||||
int channels = std::max(inputs[IN_INPUT].getChannels(), 1); | |||||
float level = params[LEVEL_PARAM].getValue(); | |||||
for (int c = 0; c < channels; c++) { | |||||
// Get input | |||||
float in = inputs[IN_INPUT].getVoltage(c); | |||||
// Get gain | |||||
float gain = level; | |||||
if (inputs[CV_INPUT].isConnected()) { | |||||
float cv = clamp(inputs[CV_INPUT].getPolyVoltage(c) / 10.f, 0.f, 1.f); | |||||
if (int(params[EXP_PARAM].getValue()) == 0) | |||||
cv = std::pow(cv, 4.f); | |||||
gain *= cv; | |||||
} | |||||
// Apply gain | |||||
in *= gain; | |||||
lastGains[c] = gain; | |||||
// Set output | |||||
outputs[OUT_OUTPUT].setVoltage(in, c); | |||||
} | |||||
outputs[OUT_OUTPUT].setChannels(channels); | |||||
lastChannels = channels; | |||||
} | |||||
}; | |||||
struct VCA_1VUKnob : SliderKnob { | |||||
void drawLayer(const DrawArgs& args, int layer) override { | |||||
if (layer != 1) | |||||
return; | |||||
VCA_1* module = dynamic_cast<VCA_1*>(this->module); | |||||
const Vec margin = Vec(3, 3); | |||||
Rect r = box.zeroPos().grow(margin.neg()); | |||||
int channels = module ? module->lastChannels : 1; | |||||
engine::ParamQuantity* pq = getParamQuantity(); | |||||
float value = pq ? pq->getValue() : 1.f; | |||||
// Segment value | |||||
if (value >= 0.005f) { | |||||
nvgBeginPath(args.vg); | |||||
nvgRect(args.vg, | |||||
r.pos.x, | |||||
r.pos.y + r.size.y * (1 - value), | |||||
r.size.x, | |||||
r.size.y * value); | |||||
nvgFillColor(args.vg, color::mult(color::WHITE, 0.33)); | |||||
nvgFill(args.vg); | |||||
} | |||||
// Segment gain | |||||
nvgBeginPath(args.vg); | |||||
for (int c = 0; c < channels; c++) { | |||||
float gain = module ? module->lastGains[c] : 1.f; | |||||
if (gain >= 0.005f) { | |||||
nvgRect(args.vg, | |||||
r.pos.x + r.size.x * c / channels, | |||||
r.pos.y + r.size.y * (1 - gain), | |||||
r.size.x / channels, | |||||
r.size.y * gain); | |||||
} | |||||
} | |||||
nvgFillColor(args.vg, SCHEME_GREEN); | |||||
nvgFill(args.vg); | |||||
// Invisible separators | |||||
const int segs = 25; | |||||
nvgBeginPath(args.vg); | |||||
for (int i = 1; i < segs; i++) { | |||||
nvgRect(args.vg, | |||||
r.pos.x - 1.0, | |||||
r.pos.y + r.size.y * i / segs, | |||||
r.size.x + 2.0, | |||||
1.0); | |||||
} | |||||
nvgFillColor(args.vg, color::BLACK); | |||||
nvgFill(args.vg); | |||||
} | |||||
}; | |||||
struct VCA_1Display : LedDisplay { | |||||
void setModule(VCA_1* module) { | |||||
VCA_1VUKnob* knob = createParam<VCA_1VUKnob>(mm2px(Vec(0.0, 0.0)), module, VCA_1::LEVEL_PARAM); | |||||
knob->box.size = mm2px(Vec(15.263, 55.88)); | |||||
addChild(knob); | |||||
} | |||||
}; | |||||
struct VCA_1Widget : ModuleWidget { | |||||
VCA_1Widget(VCA_1* module) { | |||||
setModule(module); | |||||
setPanel(createPanel(asset::plugin(pluginInstance, "res/VCA-1.svg"))); | |||||
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0))); | |||||
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); | |||||
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||||
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||||
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.62, 80.603)), module, VCA_1::CV_INPUT)); | |||||
addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.62, 96.859)), module, VCA_1::IN_INPUT)); | |||||
addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.62, 113.115)), module, VCA_1::OUT_OUTPUT)); | |||||
VCA_1Display* display = createWidget<VCA_1Display>(mm2px(Vec(0.0, 13.039))); | |||||
display->box.size = mm2px(Vec(15.263, 55.88)); | |||||
display->setModule(module); | |||||
addChild(display); | |||||
} | |||||
void appendContextMenu(Menu* menu) override { | |||||
VCA_1* module = dynamic_cast<VCA_1*>(this->module); | |||||
assert(module); | |||||
menu->addChild(new MenuSeparator); | |||||
menu->addChild(createBoolMenuItem("Exponential response", "", | |||||
[=]() {return module->params[VCA_1::EXP_PARAM].getValue() == 0.f;}, | |||||
[=](bool value) {module->params[VCA_1::EXP_PARAM].setValue(!value);} | |||||
)); | |||||
} | |||||
}; | |||||
Model* modelVCA_1 = createModel<VCA_1, VCA_1Widget>("VCA-1"); |
@@ -133,160 +133,3 @@ struct VCAWidget : ModuleWidget { | |||||
Model* modelVCA = createModel<VCA, VCAWidget>("VCA"); | Model* modelVCA = createModel<VCA, VCAWidget>("VCA"); | ||||
struct VCA_1 : Module { | |||||
enum ParamIds { | |||||
LEVEL_PARAM, | |||||
EXP_PARAM, | |||||
NUM_PARAMS | |||||
}; | |||||
enum InputIds { | |||||
CV_INPUT, | |||||
IN_INPUT, | |||||
NUM_INPUTS | |||||
}; | |||||
enum OutputIds { | |||||
OUT_OUTPUT, | |||||
NUM_OUTPUTS | |||||
}; | |||||
enum LightIds { | |||||
NUM_LIGHTS | |||||
}; | |||||
int lastChannels = 1; | |||||
float lastGains[16] = {}; | |||||
VCA_1() { | |||||
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | |||||
configParam(LEVEL_PARAM, 0.0, 1.0, 1.0, "Level", "%", 0, 100); | |||||
configSwitch(EXP_PARAM, 0.0, 1.0, 1.0, "Response mode", {"Exponential", "Linear"}); | |||||
configInput(CV_INPUT, "CV"); | |||||
configInput(IN_INPUT, "Channel"); | |||||
configOutput(OUT_OUTPUT, "Channel"); | |||||
configBypass(IN_INPUT, OUT_OUTPUT); | |||||
} | |||||
void process(const ProcessArgs& args) override { | |||||
int channels = std::max(inputs[IN_INPUT].getChannels(), 1); | |||||
float level = params[LEVEL_PARAM].getValue(); | |||||
for (int c = 0; c < channels; c++) { | |||||
// Get input | |||||
float in = inputs[IN_INPUT].getVoltage(c); | |||||
// Get gain | |||||
float gain = level; | |||||
if (inputs[CV_INPUT].isConnected()) { | |||||
float cv = clamp(inputs[CV_INPUT].getPolyVoltage(c) / 10.f, 0.f, 1.f); | |||||
if (int(params[EXP_PARAM].getValue()) == 0) | |||||
cv = std::pow(cv, 4.f); | |||||
gain *= cv; | |||||
} | |||||
// Apply gain | |||||
in *= gain; | |||||
lastGains[c] = gain; | |||||
// Set output | |||||
outputs[OUT_OUTPUT].setVoltage(in, c); | |||||
} | |||||
outputs[OUT_OUTPUT].setChannels(channels); | |||||
lastChannels = channels; | |||||
} | |||||
}; | |||||
struct VCA_1VUKnob : SliderKnob { | |||||
VCA_1* module = NULL; | |||||
VCA_1VUKnob() { | |||||
box.size = mm2px(Vec(10, 46)); | |||||
} | |||||
void draw(const DrawArgs& args) override { | |||||
nvgBeginPath(args.vg); | |||||
nvgRoundedRect(args.vg, 0, 0, box.size.x, box.size.y, 2.0); | |||||
nvgFillColor(args.vg, nvgRGB(0, 0, 0)); | |||||
nvgFill(args.vg); | |||||
} | |||||
void drawLayer(const DrawArgs& args, int layer) override { | |||||
if (layer != 1) | |||||
return; | |||||
const Vec margin = Vec(3, 3); | |||||
Rect r = box.zeroPos().grow(margin.neg()); | |||||
int channels = module ? module->lastChannels : 1; | |||||
engine::ParamQuantity* pq = getParamQuantity(); | |||||
float value = pq ? pq->getValue() : 1.f; | |||||
// Segment value | |||||
if (value >= 0.005f) { | |||||
nvgBeginPath(args.vg); | |||||
nvgRect(args.vg, | |||||
r.pos.x, | |||||
r.pos.y + r.size.y * (1 - value), | |||||
r.size.x, | |||||
r.size.y * value); | |||||
nvgFillColor(args.vg, color::mult(color::WHITE, 0.33)); | |||||
nvgFill(args.vg); | |||||
} | |||||
// Segment gain | |||||
nvgBeginPath(args.vg); | |||||
for (int c = 0; c < channels; c++) { | |||||
float gain = module ? module->lastGains[c] : 1.f; | |||||
if (gain >= 0.005f) { | |||||
nvgRect(args.vg, | |||||
r.pos.x + r.size.x * c / channels, | |||||
r.pos.y + r.size.y * (1 - gain), | |||||
r.size.x / channels, | |||||
r.size.y * gain); | |||||
} | |||||
} | |||||
nvgFillColor(args.vg, SCHEME_GREEN); | |||||
nvgFill(args.vg); | |||||
// Invisible separators | |||||
const int segs = 25; | |||||
nvgBeginPath(args.vg); | |||||
for (int i = 1; i <= segs; i++) { | |||||
nvgRect(args.vg, | |||||
r.pos.x - 1.0, | |||||
r.pos.y + r.size.y * i / segs, | |||||
r.size.x + 2.0, | |||||
1.0); | |||||
} | |||||
nvgFillColor(args.vg, color::BLACK); | |||||
nvgFill(args.vg); | |||||
} | |||||
}; | |||||
struct VCA_1Widget : ModuleWidget { | |||||
VCA_1Widget(VCA_1* module) { | |||||
setModule(module); | |||||
setPanel(createPanel(asset::plugin(pluginInstance, "res/VCA-1.svg"))); | |||||
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0))); | |||||
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); | |||||
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||||
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||||
VCA_1VUKnob* levelParam = createParam<VCA_1VUKnob>(mm2px(Vec(2.62103, 12.31692)), module, VCA_1::LEVEL_PARAM); | |||||
levelParam->module = module; | |||||
addParam(levelParam); | |||||
addParam(createParam<CKSS>(mm2px(Vec(5.24619, 79.9593)), module, VCA_1::EXP_PARAM)); | |||||
addInput(createInput<PJ301MPort>(mm2px(Vec(3.51261, 60.4008)), module, VCA_1::CV_INPUT)); | |||||
addInput(createInput<PJ301MPort>(mm2px(Vec(3.51398, 97.74977)), module, VCA_1::IN_INPUT)); | |||||
addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.51398, 108.64454)), module, VCA_1::OUT_OUTPUT)); | |||||
} | |||||
}; | |||||
Model* modelVCA_1 = createModel<VCA_1, VCA_1Widget>("VCA-1"); |
@@ -232,7 +232,7 @@ struct WTLFO : Module { | |||||
if (channels == 1) { | if (channels == 1) { | ||||
float b = 1.f - phases[0][0]; | float b = 1.f - phases[0][0]; | ||||
lights[PHASE_LIGHT + 0].setSmoothBrightness(b, args.sampleTime * lightDivider.getDivision()); | lights[PHASE_LIGHT + 0].setSmoothBrightness(b, args.sampleTime * lightDivider.getDivision()); | ||||
lights[PHASE_LIGHT + 1].setBrightness(0.f); | |||||
lights[PHASE_LIGHT + 1].setSmoothBrightness(b, args.sampleTime * lightDivider.getDivision()); | |||||
lights[PHASE_LIGHT + 2].setBrightness(0.f); | lights[PHASE_LIGHT + 2].setBrightness(0.f); | ||||
} | } | ||||
else { | else { | ||||