|  | @@ -24,24 +24,87 @@ struct Branches : Module { | 
														
													
														
															
																|  |  | NUM_OUTPUTS |  |  | NUM_OUTPUTS | 
														
													
														
															
																|  |  | }; |  |  | }; | 
														
													
														
															
																|  |  | enum LightIds { |  |  | enum LightIds { | 
														
													
														
															
																|  |  | MODE1_LIGHT, |  |  |  | 
														
													
														
															
																|  |  | MODE2_LIGHT, |  |  |  | 
														
													
														
															
																|  |  | STATE1_POS_LIGHT, STATE1_NEG_LIGHT, |  |  |  | 
														
													
														
															
																|  |  | STATE2_POS_LIGHT, STATE2_NEG_LIGHT, |  |  |  | 
														
													
														
															
																|  |  |  |  |  | ENUMS(STATE_LIGHTS, 2 * 2), | 
														
													
														
															
																|  |  | NUM_LIGHTS |  |  | NUM_LIGHTS | 
														
													
														
															
																|  |  | }; |  |  | }; | 
														
													
														
															
																|  |  | 
 |  |  | 
 | 
														
													
														
															
																|  |  | dsp::SchmittTrigger gateTriggers[2]; |  |  |  | 
														
													
														
															
																|  |  | dsp::SchmittTrigger modeTriggers[2]; |  |  |  | 
														
													
														
															
																|  |  |  |  |  | dsp::BooleanTrigger gateTriggers[2][16]; | 
														
													
														
															
																|  |  |  |  |  | dsp::BooleanTrigger modeTriggers[2]; | 
														
													
														
															
																|  |  | bool modes[2] = {}; |  |  | bool modes[2] = {}; | 
														
													
														
															
																|  |  | bool outcomes[2] = {}; |  |  |  | 
														
													
														
															
																|  |  |  |  |  | bool outcomes[2][16] = {}; | 
														
													
														
															
																|  |  | 
 |  |  | 
 | 
														
													
														
															
																|  |  | Branches() { |  |  | Branches() { | 
														
													
														
															
																|  |  | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); |  |  | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | 
														
													
														
															
																|  |  | configParam(THRESHOLD1_PARAM, 0.0, 1.0, 0.5, "Probability 1"); |  |  |  | 
														
													
														
															
																|  |  | configParam(MODE1_PARAM, 0.0, 1.0, 0.0, "Mode 1"); |  |  |  | 
														
													
														
															
																|  |  | configParam(THRESHOLD2_PARAM, 0.0, 1.0, 0.5, "Probability 2"); |  |  |  | 
														
													
														
															
																|  |  | configParam(MODE2_PARAM, 0.0, 1.0, 0.0, "Mode 2"); |  |  |  | 
														
													
														
															
																|  |  |  |  |  | configParam(THRESHOLD1_PARAM, 0.0, 1.0, 0.5, "Channel 1 probability", "%", 0, 100); | 
														
													
														
															
																|  |  |  |  |  | configParam(MODE1_PARAM, 0.0, 1.0, 0.0, "Channel 1 mode"); | 
														
													
														
															
																|  |  |  |  |  | configParam(THRESHOLD2_PARAM, 0.0, 1.0, 0.5, "Channel 2 probability", "%", 0, 100); | 
														
													
														
															
																|  |  |  |  |  | configParam(MODE2_PARAM, 0.0, 1.0, 0.0, "Channel 2 mode"); | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | 
 | 
														
													
														
															
																|  |  |  |  |  | void process(const ProcessArgs& args) override { | 
														
													
														
															
																|  |  |  |  |  | for (int i = 0; i < 2; i++) { | 
														
													
														
															
																|  |  |  |  |  | // Get input | 
														
													
														
															
																|  |  |  |  |  | Input* input = &inputs[IN1_INPUT + i]; | 
														
													
														
															
																|  |  |  |  |  | // 2nd input is normalized to 1st. | 
														
													
														
															
																|  |  |  |  |  | if (i == 1 && !input->isConnected()) | 
														
													
														
															
																|  |  |  |  |  | input = &inputs[IN1_INPUT + 0]; | 
														
													
														
															
																|  |  |  |  |  | int channels = std::max(input->getChannels(), 1); | 
														
													
														
															
																|  |  |  |  |  | 
 | 
														
													
														
															
																|  |  |  |  |  | // mode button | 
														
													
														
															
																|  |  |  |  |  | if (modeTriggers[i].process(params[MODE1_PARAM + i].getValue() > 0.f)) | 
														
													
														
															
																|  |  |  |  |  | modes[i] ^= true; | 
														
													
														
															
																|  |  |  |  |  | 
 | 
														
													
														
															
																|  |  |  |  |  | bool lightA = false; | 
														
													
														
															
																|  |  |  |  |  | bool lightB = false; | 
														
													
														
															
																|  |  |  |  |  | 
 | 
														
													
														
															
																|  |  |  |  |  | // Process triggers | 
														
													
														
															
																|  |  |  |  |  | for (int c = 0; c < channels; c++) { | 
														
													
														
															
																|  |  |  |  |  | bool gate = input->getVoltage(c) >= 2.f; | 
														
													
														
															
																|  |  |  |  |  | if (gateTriggers[i][c].process(gate)) { | 
														
													
														
															
																|  |  |  |  |  | // trigger | 
														
													
														
															
																|  |  |  |  |  | // We don't have to clamp here because the threshold comparison works without it. | 
														
													
														
															
																|  |  |  |  |  | float threshold = params[THRESHOLD1_PARAM + i].getValue() + inputs[P1_INPUT + i].getPolyVoltage(c) / 10.f; | 
														
													
														
															
																|  |  |  |  |  | bool toss = (random::uniform() < threshold); | 
														
													
														
															
																|  |  |  |  |  | if (!modes[i]) { | 
														
													
														
															
																|  |  |  |  |  | // direct modes | 
														
													
														
															
																|  |  |  |  |  | outcomes[i][c] = toss; | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | else { | 
														
													
														
															
																|  |  |  |  |  | // toggle modes | 
														
													
														
															
																|  |  |  |  |  | if (toss) | 
														
													
														
															
																|  |  |  |  |  | outcomes[i][c] ^= true; | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | 
 | 
														
													
														
															
																|  |  |  |  |  | // Output gate logic | 
														
													
														
															
																|  |  |  |  |  | bool gateA = !outcomes[i][c] && (modes[i] ? true : gate); | 
														
													
														
															
																|  |  |  |  |  | bool gateB = outcomes[i][c] && (modes[i] ? true : gate); | 
														
													
														
															
																|  |  |  |  |  | 
 | 
														
													
														
															
																|  |  |  |  |  | if (gateA) | 
														
													
														
															
																|  |  |  |  |  | lightA = true; | 
														
													
														
															
																|  |  |  |  |  | if (gateB) | 
														
													
														
															
																|  |  |  |  |  | lightB = true; | 
														
													
														
															
																|  |  |  |  |  | 
 | 
														
													
														
															
																|  |  |  |  |  | // Set output gates | 
														
													
														
															
																|  |  |  |  |  | outputs[OUT1A_OUTPUT + i].setVoltage(gateA ? 10.f : 0.f, c); | 
														
													
														
															
																|  |  |  |  |  | outputs[OUT1B_OUTPUT + i].setVoltage(gateB ? 10.f : 0.f, c); | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | 
 | 
														
													
														
															
																|  |  |  |  |  | outputs[OUT1A_OUTPUT + i].setChannels(channels); | 
														
													
														
															
																|  |  |  |  |  | outputs[OUT1B_OUTPUT + i].setChannels(channels); | 
														
													
														
															
																|  |  |  |  |  | 
 | 
														
													
														
															
																|  |  |  |  |  | lights[STATE_LIGHTS + i * 2 + 1].setSmoothBrightness(lightA, args.sampleTime); | 
														
													
														
															
																|  |  |  |  |  | lights[STATE_LIGHTS + i * 2 + 0].setSmoothBrightness(lightB, args.sampleTime); | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | 
 | 
														
													
														
															
																|  |  |  |  |  | void onReset() override { | 
														
													
														
															
																|  |  |  |  |  | for (int i = 0; i < 2; i++) { | 
														
													
														
															
																|  |  |  |  |  | modes[i] = false; | 
														
													
														
															
																|  |  |  |  |  | for (int c = 0; c < 16; c++) { | 
														
													
														
															
																|  |  |  |  |  | outcomes[i][c] = false; | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | 
 |  |  | 
 | 
														
													
														
															
																|  |  | json_t* dataToJson() override { |  |  | json_t* dataToJson() override { | 
														
													
												
													
														
															
																|  | @@ -64,52 +127,6 @@ struct Branches : Module { | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | 
 |  |  |  | 
														
													
														
															
																|  |  | void process(const ProcessArgs& args) override { |  |  |  | 
														
													
														
															
																|  |  | float gate = 0.0; |  |  |  | 
														
													
														
															
																|  |  | for (int i = 0; i < 2; i++) { |  |  |  | 
														
													
														
															
																|  |  | // mode button |  |  |  | 
														
													
														
															
																|  |  | if (modeTriggers[i].process(params[MODE1_PARAM + i].getValue())) |  |  |  | 
														
													
														
															
																|  |  | modes[i] = !modes[i]; |  |  |  | 
														
													
														
															
																|  |  | 
 |  |  |  | 
														
													
														
															
																|  |  | if (inputs[IN1_INPUT + i].isConnected()) |  |  |  | 
														
													
														
															
																|  |  | gate = inputs[IN1_INPUT + i].getVoltage(); |  |  |  | 
														
													
														
															
																|  |  | 
 |  |  |  | 
														
													
														
															
																|  |  | if (gateTriggers[i].process(gate)) { |  |  |  | 
														
													
														
															
																|  |  | // trigger |  |  |  | 
														
													
														
															
																|  |  | float r = random::uniform(); |  |  |  | 
														
													
														
															
																|  |  | float threshold = clamp(params[THRESHOLD1_PARAM + i].getValue() + inputs[P1_INPUT + i].getVoltage() / 10.f, 0.f, 1.f); |  |  |  | 
														
													
														
															
																|  |  | bool toss = (r < threshold); |  |  |  | 
														
													
														
															
																|  |  | if (!modes[i]) { |  |  |  | 
														
													
														
															
																|  |  | // direct modes |  |  |  | 
														
													
														
															
																|  |  | outcomes[i] = toss; |  |  |  | 
														
													
														
															
																|  |  | } |  |  |  | 
														
													
														
															
																|  |  | else { |  |  |  | 
														
													
														
															
																|  |  | // toggle modes |  |  |  | 
														
													
														
															
																|  |  | outcomes[i] = (outcomes[i] != toss); |  |  |  | 
														
													
														
															
																|  |  | } |  |  |  | 
														
													
														
															
																|  |  | 
 |  |  |  | 
														
													
														
															
																|  |  | if (!outcomes[i]) |  |  |  | 
														
													
														
															
																|  |  | lights[STATE1_POS_LIGHT + 2 * i].value = 1.0; |  |  |  | 
														
													
														
															
																|  |  | else |  |  |  | 
														
													
														
															
																|  |  | lights[STATE1_NEG_LIGHT + 2 * i].value = 1.0; |  |  |  | 
														
													
														
															
																|  |  | } |  |  |  | 
														
													
														
															
																|  |  | 
 |  |  |  | 
														
													
														
															
																|  |  | lights[STATE1_POS_LIGHT + 2 * i].value *= 1.0 - args.sampleTime * 15.0; |  |  |  | 
														
													
														
															
																|  |  | lights[STATE1_NEG_LIGHT + 2 * i].value *= 1.0 - args.sampleTime * 15.0; |  |  |  | 
														
													
														
															
																|  |  | lights[MODE1_LIGHT + i].value = modes[i] ? 1.0 : 0.0; |  |  |  | 
														
													
														
															
																|  |  | 
 |  |  |  | 
														
													
														
															
																|  |  | outputs[OUT1A_OUTPUT + i].setVoltage(outcomes[i] ? 0.0 : gate); |  |  |  | 
														
													
														
															
																|  |  | outputs[OUT1B_OUTPUT + i].setVoltage(outcomes[i] ? gate : 0.0); |  |  |  | 
														
													
														
															
																|  |  | } |  |  |  | 
														
													
														
															
																|  |  | } |  |  |  | 
														
													
														
															
																|  |  | 
 |  |  |  | 
														
													
														
															
																|  |  | void onReset() override { |  |  |  | 
														
													
														
															
																|  |  | for (int i = 0; i < 2; i++) { |  |  |  | 
														
													
														
															
																|  |  | modes[i] = false; |  |  |  | 
														
													
														
															
																|  |  | outcomes[i] = false; |  |  |  | 
														
													
														
															
																|  |  | } |  |  |  | 
														
													
														
															
																|  |  | } |  |  |  | 
														
													
														
															
																|  |  | }; |  |  | }; | 
														
													
														
															
																|  |  | 
 |  |  | 
 | 
														
													
														
															
																|  |  | 
 |  |  | 
 | 
														
													
												
													
														
															
																|  | @@ -118,25 +135,26 @@ struct BranchesWidget : ModuleWidget { | 
														
													
														
															
																|  |  | setModule(module); |  |  | setModule(module); | 
														
													
														
															
																|  |  | setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Branches.svg"))); |  |  | setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Branches.svg"))); | 
														
													
														
															
																|  |  | 
 |  |  | 
 | 
														
													
														
															
																|  |  | addChild(createWidget<ScrewSilver>(Vec(15, 0))); |  |  |  | 
														
													
														
															
																|  |  | addChild(createWidget<ScrewSilver>(Vec(15, 365))); |  |  |  | 
														
													
														
															
																|  |  | 
 |  |  |  | 
														
													
														
															
																|  |  | addParam(createParam<Rogan1PSRed>(Vec(24, 64), module, Branches::THRESHOLD1_PARAM)); |  |  |  | 
														
													
														
															
																|  |  | addParam(createParam<TL1105>(Vec(69, 58), module, Branches::MODE1_PARAM)); |  |  |  | 
														
													
														
															
																|  |  | addInput(createInput<PJ301MPort>(Vec(9, 122), module, Branches::IN1_INPUT)); |  |  |  | 
														
													
														
															
																|  |  | addInput(createInput<PJ301MPort>(Vec(55, 122), module, Branches::P1_INPUT)); |  |  |  | 
														
													
														
															
																|  |  | addOutput(createOutput<PJ301MPort>(Vec(9, 160), module, Branches::OUT1A_OUTPUT)); |  |  |  | 
														
													
														
															
																|  |  | addOutput(createOutput<PJ301MPort>(Vec(55, 160), module, Branches::OUT1B_OUTPUT)); |  |  |  | 
														
													
														
															
																|  |  | 
 |  |  |  | 
														
													
														
															
																|  |  | addParam(createParam<Rogan1PSGreen>(Vec(24, 220), module, Branches::THRESHOLD2_PARAM)); |  |  |  | 
														
													
														
															
																|  |  | addParam(createParam<TL1105>(Vec(69, 214), module, Branches::MODE2_PARAM)); |  |  |  | 
														
													
														
															
																|  |  | addInput(createInput<PJ301MPort>(Vec(9, 278), module, Branches::IN2_INPUT)); |  |  |  | 
														
													
														
															
																|  |  | addInput(createInput<PJ301MPort>(Vec(55, 278), module, Branches::P2_INPUT)); |  |  |  | 
														
													
														
															
																|  |  | addOutput(createOutput<PJ301MPort>(Vec(9, 316), module, Branches::OUT2A_OUTPUT)); |  |  |  | 
														
													
														
															
																|  |  | addOutput(createOutput<PJ301MPort>(Vec(55, 316), module, Branches::OUT2B_OUTPUT)); |  |  |  | 
														
													
														
															
																|  |  | 
 |  |  |  | 
														
													
														
															
																|  |  | addChild(createLight<SmallLight<GreenRedLight>>(Vec(40, 169), module, Branches::STATE1_POS_LIGHT)); |  |  |  | 
														
													
														
															
																|  |  | addChild(createLight<SmallLight<GreenRedLight>>(Vec(40, 325), module, Branches::STATE2_POS_LIGHT)); |  |  |  | 
														
													
														
															
																|  |  |  |  |  | addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0))); | 
														
													
														
															
																|  |  |  |  |  | addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | 
														
													
														
															
																|  |  |  |  |  | 
 | 
														
													
														
															
																|  |  |  |  |  | addParam(createParamCentered<TL1105>(mm2px(Vec(25.852, 22.24)), module, Branches::MODE1_PARAM)); | 
														
													
														
															
																|  |  |  |  |  | addParam(createParamCentered<Rogan1PSRed>(mm2px(Vec(15.057, 28.595)), module, Branches::THRESHOLD1_PARAM)); | 
														
													
														
															
																|  |  |  |  |  | addParam(createParamCentered<TL1105>(mm2px(Vec(25.852, 74.95)), module, Branches::MODE2_PARAM)); | 
														
													
														
															
																|  |  |  |  |  | addParam(createParamCentered<Rogan1PSGreen>(mm2px(Vec(15.057, 81.296)), module, Branches::THRESHOLD2_PARAM)); | 
														
													
														
															
																|  |  |  |  |  | 
 | 
														
													
														
															
																|  |  |  |  |  | addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.112, 45.74)), module, Branches::IN1_INPUT)); | 
														
													
														
															
																|  |  |  |  |  | addInput(createInputCentered<PJ301MPort>(mm2px(Vec(22.991, 45.74)), module, Branches::P1_INPUT)); | 
														
													
														
															
																|  |  |  |  |  | addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.112, 98.44)), module, Branches::IN2_INPUT)); | 
														
													
														
															
																|  |  |  |  |  | addInput(createInputCentered<PJ301MPort>(mm2px(Vec(22.991, 98.44)), module, Branches::P2_INPUT)); | 
														
													
														
															
																|  |  |  |  |  | 
 | 
														
													
														
															
																|  |  |  |  |  | addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.112, 58.44)), module, Branches::OUT1A_OUTPUT)); | 
														
													
														
															
																|  |  |  |  |  | addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(22.991, 58.44)), module, Branches::OUT1B_OUTPUT)); | 
														
													
														
															
																|  |  |  |  |  | addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.112, 111.14)), module, Branches::OUT2A_OUTPUT)); | 
														
													
														
															
																|  |  |  |  |  | addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(22.991, 111.14)), module, Branches::OUT2B_OUTPUT)); | 
														
													
														
															
																|  |  |  |  |  | 
 | 
														
													
														
															
																|  |  |  |  |  | addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(15.052, 58.45)), module, Branches::STATE_LIGHTS + 0 * 2)); | 
														
													
														
															
																|  |  |  |  |  | addChild(createLightCentered<MediumLight<GreenRedLight>>(mm2px(Vec(15.052, 111.151)), module, Branches::STATE_LIGHTS + 1 * 2)); | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | 
 |  |  | 
 | 
														
													
														
															
																|  |  | void appendContextMenu(Menu* menu) override { |  |  | void appendContextMenu(Menu* menu) override { | 
														
													
												
													
														
															
																|  | @@ -145,12 +163,12 @@ struct BranchesWidget : ModuleWidget { | 
														
													
														
															
																|  |  | 
 |  |  | 
 | 
														
													
														
															
																|  |  | struct BranchesModeItem : MenuItem { |  |  | struct BranchesModeItem : MenuItem { | 
														
													
														
															
																|  |  | Branches* branches; |  |  | Branches* branches; | 
														
													
														
															
																|  |  | int channel; |  |  |  | 
														
													
														
															
																|  |  |  |  |  | int i; | 
														
													
														
															
																|  |  | void onAction(const event::Action& e) override { |  |  | void onAction(const event::Action& e) override { | 
														
													
														
															
																|  |  | branches->modes[channel] ^= 1; |  |  |  | 
														
													
														
															
																|  |  |  |  |  | branches->modes[i] ^= 1; | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | void step() override { |  |  | void step() override { | 
														
													
														
															
																|  |  | rightText = branches->modes[channel] ? "Toggle" : "Latch"; |  |  |  | 
														
													
														
															
																|  |  |  |  |  | rightText = branches->modes[i] ? "Latch" : "Toggle"; | 
														
													
														
															
																|  |  | MenuItem::step(); |  |  | MenuItem::step(); | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | }; |  |  | }; | 
														
													
												
													
														
															
																|  | @@ -158,8 +176,8 @@ struct BranchesWidget : ModuleWidget { | 
														
													
														
															
																|  |  | menu->addChild(new MenuSeparator); |  |  | menu->addChild(new MenuSeparator); | 
														
													
														
															
																|  |  | 
 |  |  | 
 | 
														
													
														
															
																|  |  | menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Channels")); |  |  | menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Channels")); | 
														
													
														
															
																|  |  | menu->addChild(construct<BranchesModeItem>(&MenuItem::text, "Channel 1 modes", &BranchesModeItem::branches, branches, &BranchesModeItem::channel, 0)); |  |  |  | 
														
													
														
															
																|  |  | menu->addChild(construct<BranchesModeItem>(&MenuItem::text, "Channel 2 modes", &BranchesModeItem::branches, branches, &BranchesModeItem::channel, 1)); |  |  |  | 
														
													
														
															
																|  |  |  |  |  | menu->addChild(construct<BranchesModeItem>(&MenuItem::text, "Channel 1 mode", &BranchesModeItem::branches, branches, &BranchesModeItem::i, 0)); | 
														
													
														
															
																|  |  |  |  |  | menu->addChild(construct<BranchesModeItem>(&MenuItem::text, "Channel 2 mode", &BranchesModeItem::branches, branches, &BranchesModeItem::i, 1)); | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | }; |  |  | }; | 
														
													
														
															
																|  |  | 
 |  |  | 
 | 
														
													
												
													
														
															
																|  | 
 |