diff --git a/src/SequentialSwitch.cpp b/src/SequentialSwitch.cpp index 37b6e2c..75e64e0 100644 --- a/src/SequentialSwitch.cpp +++ b/src/SequentialSwitch.cpp @@ -29,6 +29,8 @@ struct SequentialSwitch : Module { dsp::ClockDivider lightDivider; dsp::SlewLimiter clickFilters[4]; + bool declick = false; + SequentialSwitch() { config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); configSwitch(STEPS_PARAM, 0.0, 2.0, 2.0, "Steps", {"2", "3", "4"}); @@ -56,7 +58,16 @@ struct SequentialSwitch : Module { lightDivider.setDivision(512); } + void onReset(const ResetEvent& e) override { + Module::onReset(e); + declick = false; + } + void process(const ProcessArgs& args) override { + using simd::float_4; + + int length = 2 + int(params[STEPS_PARAM].getValue()); + // Determine current index if (clockTrigger.process(rescale(inputs[CLOCK_INPUT].getVoltage(), 0.1f, 2.f, 0.f, 1.f))) { index++; @@ -64,50 +75,72 @@ struct SequentialSwitch : Module { if (resetTrigger.process(rescale(inputs[RESET_INPUT].getVoltage(), 0.1f, 2.f, 0.f, 1.f))) { index = 0; } - int length = 2 + (int) std::round(params[STEPS_PARAM].getValue()); if (index >= length) index = 0; - // Use first input to get number of channels - int channels = std::max(inputs[IN_INPUTS + 0].getChannels(), 1); + // Get number of polyphony channels + int channels = 1; + for (int i = 0; i < INPUTS; i++) { + channels = std::max(channels, inputs[IN_INPUTS + i].getChannels()); + } if (INPUTS == 1) { - // <1, 4> - // Get input - float* in = inputs[IN_INPUTS + 0].getVoltages(); + // 1 -> many - // Set output for (int i = 0; i < OUTPUTS; i++) { - float gain = clickFilters[i].process(args.sampleTime, index == i); outputs[OUT_OUTPUTS + i].setChannels(channels); - if (gain != 0.f) { - for (int c = 0; c < channels; c++) { - float out = in[c] * gain; - outputs[OUT_OUTPUTS + i].setVoltage(out, c); + + if (declick) { + // Step click filter + float gain = clickFilters[i].process(args.sampleTime, i == index); + if (gain != 0.f) { + for (int c = 0; c < channels; c += 4) { + // Get input + float_4 in = inputs[IN_INPUTS + 0].template getVoltageSimd(c); + // Set output + outputs[OUT_OUTPUTS + i].setVoltageSimd(in * gain, c); + } + } + else { + outputs[OUT_OUTPUTS + i].clearVoltages(); } } else { - outputs[OUT_OUTPUTS + i].clearVoltages(); + // Set output + if (i == index) + outputs[OUT_OUTPUTS + i].writeVoltages(inputs[IN_INPUTS + 0].getVoltages()); + else + outputs[OUT_OUTPUTS + i].clearVoltages(); } } } else { - // <4, 1> - // Get input - float out[16] = {}; - for (int i = 0; i < INPUTS; i++) { - float gain = clickFilters[i].process(args.sampleTime, index == i); - if (gain != 0.f) { - for (int c = 0; c < channels; c++) { - float in = inputs[IN_INPUTS + i].getVoltage(c); - out[c] += in * gain; + // many -> 1 + + outputs[OUT_OUTPUTS + 0].setChannels(channels); + + if (declick) { + // Get mixed output + float_4 out[4] = {}; + for (int i = 0; i < INPUTS; i++) { + float gain = clickFilters[i].process(args.sampleTime, i == index); + if (gain != 0.f) { + for (int c = 0; c < channels; c += 4) { + float_4 in = inputs[IN_INPUTS + i].template getPolyVoltageSimd(c); + out[c / 4] += in * gain; + } } } - } - // Set output - outputs[OUT_OUTPUTS + 0].setChannels(channels); - outputs[OUT_OUTPUTS + 0].writeVoltages(out); + // Set output + for (int c = 0; c < channels; c += 4) { + outputs[OUT_OUTPUTS + 0].setVoltageSimd(out[c / 4], c); + } + } + else { + // Get and set output + outputs[OUT_OUTPUTS + 0].writeVoltages(inputs[IN_INPUTS + index].getVoltages()); + } } // Set lights @@ -125,79 +158,97 @@ struct SequentialSwitch : Module { // If version <2.0 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()); + string::Version version(json_string_value(versionJ)); + if (version < string::Version("2")) { params[STEPS_PARAM].setValue(2 - params[STEPS_PARAM].getValue()); } } } + + json_t* dataToJson() override { + json_t* rootJ = json_object(); + json_object_set_new(rootJ, "declick", json_boolean(declick)); + return rootJ; + } + + void dataFromJson(json_t* rootJ) override { + json_t* declickJ = json_object_get(rootJ, "declick"); + if (declickJ) + declick = json_boolean_value(declickJ); + // In <2.5.0, SequentialSwitch always de-clicked. + else + declick = true; + } }; -struct SequentialSwitch1Widget : ModuleWidget { - typedef SequentialSwitch<1, 4> TSequentialSwitch; +template +struct SequentialSwitchWidget : ModuleWidget { + typedef SequentialSwitch TSequentialSwitch; - SequentialSwitch1Widget(TSequentialSwitch* module) { + SequentialSwitchWidget(TSequentialSwitch* module) { setModule(module); - setPanel(createPanel(asset::plugin(pluginInstance, "res/SequentialSwitch1.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))); + if (INPUTS == 1 && OUTPUTS == 4) { + setPanel(createPanel(asset::plugin(pluginInstance, "res/SequentialSwitch1.svg"))); - addParam(createParamCentered(mm2px(Vec(7.555, 20.942)), module, TSequentialSwitch::STEPS_PARAM)); + 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))); - addInput(createInputCentered(mm2px(Vec(7.555, 33.831)), module, TSequentialSwitch::CLOCK_INPUT)); - addInput(createInputCentered(mm2px(Vec(7.555, 50.126)), module, TSequentialSwitch::RESET_INPUT)); - addInput(createInputCentered(mm2px(Vec(7.555, 66.379)), module, TSequentialSwitch::IN_INPUTS + 0)); + addParam(createParamCentered(mm2px(Vec(7.555, 20.942)), module, TSequentialSwitch::STEPS_PARAM)); - addOutput(createOutputCentered(mm2px(Vec(7.555, 82.607)), module, TSequentialSwitch::OUT_OUTPUTS + 0)); - addOutput(createOutputCentered(mm2px(Vec(7.555, 92.767)), module, TSequentialSwitch::OUT_OUTPUTS + 1)); - addOutput(createOutputCentered(mm2px(Vec(7.555, 102.927)), module, TSequentialSwitch::OUT_OUTPUTS + 2)); - addOutput(createOutputCentered(mm2px(Vec(7.555, 113.087)), module, TSequentialSwitch::OUT_OUTPUTS + 3)); + addInput(createInputCentered(mm2px(Vec(7.555, 33.831)), module, TSequentialSwitch::CLOCK_INPUT)); + addInput(createInputCentered(mm2px(Vec(7.555, 50.126)), module, TSequentialSwitch::RESET_INPUT)); + addInput(createInputCentered(mm2px(Vec(7.555, 66.379)), module, TSequentialSwitch::IN_INPUTS + 0)); - addChild(createLightCentered>>(mm2px(Vec(11.28, 78.863)), module, TSequentialSwitch::CHANNEL_LIGHTS + 2 * 0)); - addChild(createLightCentered>>(mm2px(Vec(11.28, 89.023)), module, TSequentialSwitch::CHANNEL_LIGHTS + 2 * 1)); - addChild(createLightCentered>>(mm2px(Vec(11.28, 99.183)), module, TSequentialSwitch::CHANNEL_LIGHTS + 2 * 2)); - addChild(createLightCentered>>(mm2px(Vec(11.28, 109.343)), module, TSequentialSwitch::CHANNEL_LIGHTS + 2 * 3)); - } -}; + addOutput(createOutputCentered(mm2px(Vec(7.555, 82.607)), module, TSequentialSwitch::OUT_OUTPUTS + 0)); + addOutput(createOutputCentered(mm2px(Vec(7.555, 92.767)), module, TSequentialSwitch::OUT_OUTPUTS + 1)); + addOutput(createOutputCentered(mm2px(Vec(7.555, 102.927)), module, TSequentialSwitch::OUT_OUTPUTS + 2)); + addOutput(createOutputCentered(mm2px(Vec(7.555, 113.087)), module, TSequentialSwitch::OUT_OUTPUTS + 3)); + addChild(createLightCentered>>(mm2px(Vec(11.28, 78.863)), module, TSequentialSwitch::CHANNEL_LIGHTS + 2 * 0)); + addChild(createLightCentered>>(mm2px(Vec(11.28, 89.023)), module, TSequentialSwitch::CHANNEL_LIGHTS + 2 * 1)); + addChild(createLightCentered>>(mm2px(Vec(11.28, 99.183)), module, TSequentialSwitch::CHANNEL_LIGHTS + 2 * 2)); + addChild(createLightCentered>>(mm2px(Vec(11.28, 109.343)), module, TSequentialSwitch::CHANNEL_LIGHTS + 2 * 3)); + } -Model* modelSequentialSwitch1 = createModel, SequentialSwitch1Widget>("SequentialSwitch1"); + if (INPUTS == 4 && OUTPUTS == 1) { + setPanel(createPanel(asset::plugin(pluginInstance, "res/SequentialSwitch2.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))); -struct SequentialSwitch2Widget : ModuleWidget { - typedef SequentialSwitch<4, 1> TSequentialSwitch; + addParam(createParamCentered(mm2px(Vec(7.8, 20.942)), module, TSequentialSwitch::STEPS_PARAM)); - SequentialSwitch2Widget(TSequentialSwitch* module) { - setModule(module); - setPanel(createPanel(asset::plugin(pluginInstance, "res/SequentialSwitch2.svg"))); + addInput(createInputCentered(mm2px(Vec(7.8, 33.831)), module, TSequentialSwitch::CLOCK_INPUT)); + addInput(createInputCentered(mm2px(Vec(7.8, 50.126)), module, TSequentialSwitch::RESET_INPUT)); + addInput(createInputCentered(mm2px(Vec(7.8, 66.379)), module, TSequentialSwitch::IN_INPUTS + 0)); + addInput(createInputCentered(mm2px(Vec(7.8, 76.539)), module, TSequentialSwitch::IN_INPUTS + 1)); + addInput(createInputCentered(mm2px(Vec(7.8, 86.699)), module, TSequentialSwitch::IN_INPUTS + 2)); + addInput(createInputCentered(mm2px(Vec(7.8, 96.859)), module, TSequentialSwitch::IN_INPUTS + 3)); - 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))); + addOutput(createOutputCentered(mm2px(Vec(7.8, 113.115)), module, TSequentialSwitch::OUT_OUTPUTS + 0)); - addParam(createParamCentered(mm2px(Vec(7.8, 20.942)), module, TSequentialSwitch::STEPS_PARAM)); + addChild(createLightCentered>>(mm2px(Vec(11.526, 63.259)), module, TSequentialSwitch::CHANNEL_LIGHTS + 2 * 0)); + addChild(createLightCentered>>(mm2px(Vec(11.526, 72.795)), module, TSequentialSwitch::CHANNEL_LIGHTS + 2 * 1)); + addChild(createLightCentered>>(mm2px(Vec(11.526, 82.955)), module, TSequentialSwitch::CHANNEL_LIGHTS + 2 * 2)); + addChild(createLightCentered>>(mm2px(Vec(11.526, 93.115)), module, TSequentialSwitch::CHANNEL_LIGHTS + 2 * 3)); + } + } - addInput(createInputCentered(mm2px(Vec(7.8, 33.831)), module, TSequentialSwitch::CLOCK_INPUT)); - addInput(createInputCentered(mm2px(Vec(7.8, 50.126)), module, TSequentialSwitch::RESET_INPUT)); - addInput(createInputCentered(mm2px(Vec(7.8, 66.379)), module, TSequentialSwitch::IN_INPUTS + 0)); - addInput(createInputCentered(mm2px(Vec(7.8, 76.539)), module, TSequentialSwitch::IN_INPUTS + 1)); - addInput(createInputCentered(mm2px(Vec(7.8, 86.699)), module, TSequentialSwitch::IN_INPUTS + 2)); - addInput(createInputCentered(mm2px(Vec(7.8, 96.859)), module, TSequentialSwitch::IN_INPUTS + 3)); + void appendContextMenu(Menu* menu) override { + TSequentialSwitch* module = getModule(); - addOutput(createOutputCentered(mm2px(Vec(7.8, 113.115)), module, TSequentialSwitch::OUT_OUTPUTS + 0)); + menu->addChild(new MenuSeparator); - addChild(createLightCentered>>(mm2px(Vec(11.526, 63.259)), module, TSequentialSwitch::CHANNEL_LIGHTS + 2 * 0)); - addChild(createLightCentered>>(mm2px(Vec(11.526, 72.795)), module, TSequentialSwitch::CHANNEL_LIGHTS + 2 * 1)); - addChild(createLightCentered>>(mm2px(Vec(11.526, 82.955)), module, TSequentialSwitch::CHANNEL_LIGHTS + 2 * 2)); - addChild(createLightCentered>>(mm2px(Vec(11.526, 93.115)), module, TSequentialSwitch::CHANNEL_LIGHTS + 2 * 3)); + menu->addChild(createBoolPtrMenuItem("De-click", "", &module->declick)); } }; -Model* modelSequentialSwitch2 = createModel, SequentialSwitch2Widget>("SequentialSwitch2"); +Model* modelSequentialSwitch1 = createModel, SequentialSwitchWidget<1, 4>>("SequentialSwitch1"); +Model* modelSequentialSwitch2 = createModel, SequentialSwitchWidget<4, 1>>("SequentialSwitch2");