| @@ -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 { | ||||