/////////////////////////// // Bene2 - Big thx to Strum Mental and JW jeremywen for sharing their magic code !! // still some fix to do as usuall ;) // /////////////////////// #include "dBiz.hpp" #include "dsp/digital.hpp" using namespace std; namespace rack_plugin_dBiz { struct Bene2 : Module { enum ParamIds { RESET_LINE, RESET_COL, RUNL_PARAM, RUNC_PARAM, GATE_PARAM, KNOB_PARAM=GATE_PARAM+16, NUM_PARAMS = KNOB_PARAM + 16 }; enum InputIds { RESETL_INPUT, RESETC_INPUT, RUNC_INPUT, RUNL_INPUT, RESETL, RESETC = RESETL + 4, UP = RESETC + 4, DOWN = UP + 4, LEFT = DOWN + 4, RIGHT = LEFT + 4, NUM_INPUTS = RIGHT + 4 }; enum OutputIds { GATES_ROW_OUT, GATES_COL_OUT = GATES_ROW_OUT + 4, ROW_OUT = GATES_COL_OUT + 4, COLUMN_OUT = ROW_OUT + 4, NUM_OUTPUTS = COLUMN_OUT + 4 }; enum LightIds { RESETL_LIGHT, RESETC_LIGHT, RUNL_LIGHT, RUNC_LIGHT, GATE_LIGHT, STEPS_LIGHT = GATE_LIGHT + 16, NUM_LIGHTS = STEPS_LIGHT + 16 }; SchmittTrigger leftTrigger[4]={}; SchmittTrigger rightTrigger[4]={}; SchmittTrigger upTrigger[4]={}; SchmittTrigger downTrigger[4]={}; SchmittTrigger resetCoTrigger[4]={}; SchmittTrigger resetLiTrigger[4]={}; SchmittTrigger resetALTrigger; SchmittTrigger resetACTrigger; SchmittTrigger runningLTrigger; SchmittTrigger runningCTrigger; SchmittTrigger gateTriggers[16]={}; SchmittTrigger button_triggers[4][4]; float row_outs[4] = {0.0,0.0,0.0,0.0}; float column_outs[4] = {0.0,0.0,0.0,0.0}; int posX[4] = {}; int posY[4] = {}; int index[16]={}; int indexl[4] = {}; bool gateState[16] = {}; bool runningL = true; bool runningC = true; bool ignoreGateOnPitchOut = false; enum GateMode { TRIGGER, RETRIGGER, CONTINUOUS }; GateMode gateMode = TRIGGER; PulseGenerator gatePulse; Bene2() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {} void step() override; json_t *toJson() override { json_t *rootJ = json_object(); json_object_set_new(rootJ, "running Line", json_boolean(runningL)); json_object_set_new(rootJ, "running Column", json_boolean(runningC)); json_object_set_new(rootJ, "ignoreGateOnPitchOut", json_boolean(ignoreGateOnPitchOut)); // gates json_t *gatesJ = json_array(); for (int i = 0; i < 16; i++) { json_t *gateJ = json_integer((int)gateState[i]); json_array_append_new(gatesJ, gateJ); } json_object_set_new(rootJ, "gates", gatesJ); // gateMode json_t *gateModeJ = json_integer((int)gateMode); json_object_set_new(rootJ, "gateMode", gateModeJ); return rootJ; } void fromJson(json_t *rootJ) override { json_t *runningLJ = json_object_get(rootJ, "running Line"); if (runningLJ) runningL = json_is_true(runningLJ); json_t *runningCJ = json_object_get(rootJ, "running Column"); if (runningCJ) runningC = json_is_true(runningCJ); json_t *ignoreGateOnPitchOutJ = json_object_get(rootJ, "ignoreGateOnPitchOut"); if (ignoreGateOnPitchOutJ) ignoreGateOnPitchOut = json_is_true(ignoreGateOnPitchOutJ); // gates json_t *gatesJ = json_object_get(rootJ, "gates"); if (gatesJ) { for (int i = 0; i < 16; i++) { json_t *gateJ = json_array_get(gatesJ, i); if (gateJ) gateState[i] = !!json_integer_value(gateJ); } } // gateMode json_t *gateModeJ = json_object_get(rootJ, "gateMode"); if (gateModeJ) gateMode = (GateMode)json_integer_value(gateModeJ); } void reset() override { for (int i = 0; i < 16; i++) { gateState[i] = false; } } void randomize() override { randomizeGateStates(); } void randomizeGateStates() { for (int i = 0; i < 16; i++) { gateState[i] = (randomUniform() > 0.50); } } void handleMoveRight() { for (int i=0; i<4; i++) { posX[i] = posX[i] == 3 ? 0 : posX[i] + 1; } } void handleMoveLeft() { for (int i=0; i<4; i++) { posX[i] = posX[i] == 0 ? 3 : posX[i] - 1; } } void handleMoveDown() { for (int i=0; i<4; i++) { posY[i] = posY[i] == 3 ? 0 : posY[i] + 1; } } void handleMoveUp() { for (int i=0; i<4; i++) { posY[i] = posY[i] == 0 ? 3 : posY[i] - 1; } } }; /////////////////////////////////////////////////////////////////////////////////////////////////// // STEP /////////////////////////////////////////////////////////////////////////////////////////////////// void Bene2::step() { bool step_right[4] = {false}; bool step_left[4] = {false}; bool step_up[4] = {false}; bool step_down[4] = {false}; const float lightLambda = 0.1; /////////////////////////////////////////////////////////////////////////////// // Running Buttons ///////////////////////////////////////////////////////////////////////////// if (runningLTrigger.process(params[RUNL_PARAM].value+inputs[RUNL_INPUT].value)) { runningL = !runningL; } lights[RUNL_LIGHT].value = runningL ? 1.0 : 0.0; if (runningCTrigger.process(params[RUNC_PARAM].value)) { runningC = !runningC; } lights[RUNC_LIGHT].value = runningC ? 1.0 : 0.0; /////////////////////////////////////////////////////////////////////////////// // RESET ///////////////////////////////////////////////////////////////////////////// for (int i = 0; i < 4; i++) { if (resetLiTrigger[i].process(inputs[RESETL + i].value)) { posX[i] = 0; step_right[i] = false; step_left[i] = false; lights[STEPS_LIGHT + posX[i] + i * 4].value = 0.8; } if (resetCoTrigger[i].process(inputs[RESETC + i].value)) { posY[i] = 0; step_up[i] = false; step_down[i] = false; lights[STEPS_LIGHT + i + posY[i] * 4].value = 0.8; } } if(resetALTrigger.process(params[RESET_LINE].value+inputs[RESETL_INPUT].value)) { lights[RESETL_LIGHT].value = 1.0; for (int i=0;i<4;i++) { posX[i] = 0; step_right[i] = false; step_left[i] = false; lights[STEPS_LIGHT + posX[i] + i * 4].value = 0.8; } } if(resetACTrigger.process(params[RESET_COL].value+inputs[RESETC_INPUT].value)) { lights[RESETC_LIGHT].value = 1.0; for (int i = 0; i < 4; i++) { posY[i] = 0; step_up[i] = false; step_down[i] = false; lights[STEPS_LIGHT + i + posY[i] * 4].value = 0.8; } } if (lights[RESETL_LIGHT].value > 0) { lights[RESETL_LIGHT].value -= lights[RESETL_LIGHT].value / lightLambda / engineGetSampleRate(); } if (lights[RESETC_LIGHT].value > 0) { lights[RESETC_LIGHT].value -= lights[RESETC_LIGHT].value / lightLambda / engineGetSampleRate(); } /////////////////////////////////////////////////////////////////////////////// // RUN ///////////////////////////////////////////////////////////////////////////// if (runningL) { for (int i = 0; i < 4; i++) { // handle clock inputs if (inputs[RIGHT + i].active) { if (rightTrigger[i].process(inputs[RIGHT + i].value)) { step_right[i] = true; } } if (inputs[LEFT + i].active) { if (leftTrigger[i].process(inputs[LEFT + i].value)) { step_left[i] = true; } } } } if (runningC) { for (int i = 0; i < 4; i++) { if (inputs[DOWN + i].active) { if (downTrigger[i].process(inputs[DOWN + i].value)) { step_down[i] = true; } } if (inputs[UP + i].active) { if (upTrigger[i].process(inputs[UP + i].value)) { step_up[i] = true; } } } } for (int i = 0; i < 4; i++) { index[i]=posX[i]+(posY[i]*4); } // change x and y for (int i = 0; i < 4; i++) { if (step_right[i]) { posX[i] += 1; if (posX[i] > 3) posX[i] = 0; lights[STEPS_LIGHT + posX[i] + i * 4].value = 0.8; gatePulse.trigger(1e-3); } if (step_left[i]) { posX[i] -= 1; if (posX[i] < 0) posX[i] = 3; lights[STEPS_LIGHT + posX[i] + i * 4].value = 0.8; gatePulse.trigger(1e-3); } if (step_down[i]) { posY[i] += 1; if (posY[i] > 3) posY[i] = 0; lights[STEPS_LIGHT + i + posY[i] * 4].value = 0.8; gatePulse.trigger(1e-3); } if (step_up[i]) { posY[i] -= 1; if (posY[i] < 0) posY[i] = 3; lights[STEPS_LIGHT + i + posY[i] * 4].value = 0.8; gatePulse.trigger(1e-3); } } bool pulse = gatePulse.process(1.0 / engineGetSampleRate()); for (int i = 0; i < 16; i++) { if (gateTriggers[i].process(params[GATE_PARAM + i].value)) { gateState[i] = !gateState[i]; } if (lights[STEPS_LIGHT + i].value > 0) { lights[STEPS_LIGHT + i].value -= lights[STEPS_LIGHT + i].value / lightLambda / engineGetSampleRate(); } lights[GATE_LIGHT + i].value = gateState[i] ? 1.0 - lights[STEPS_LIGHT + i].value : lights[STEPS_LIGHT + i].value; } // Outputs for (int i=0;i<4;i++) { bool gatesOnL = (runningL && gateState[i + posY[i] * 4]); if (gateMode == TRIGGER) gatesOnL = gatesOnL && pulse; else if (gateMode == RETRIGGER) gatesOnL = gatesOnL && !pulse; bool gatesOnC = (runningC && gateState[posX[i] + i * 4]); if (gateMode == TRIGGER) gatesOnC = gatesOnC && pulse; else if (gateMode == RETRIGGER) gatesOnC = gatesOnC && !pulse; row_outs[i] = (params[KNOB_PARAM + i + posY[i] * 4].value); column_outs[i] = (params[KNOB_PARAM + posX[i] + i * 4].value); if (gatesOnL || ignoreGateOnPitchOut) { outputs[ROW_OUT + i].value = row_outs[i]; } if (gatesOnC || ignoreGateOnPitchOut) { outputs[COLUMN_OUT + i].value = column_outs[i]; } outputs[GATES_COL_OUT + i].value = gatesOnC ? 10.0 : 0.0; outputs[GATES_ROW_OUT + i].value = gatesOnL ? 10.0 : 0.0; } } template struct RunLight : BASE { RunLight() { this->box.size = mm2px(Vec(5.5, 5.5)); } }; struct Bene2Widget : ModuleWidget { void appendContextMenu(Menu *menu) override; Bene2Widget(Bene2 *module) : ModuleWidget(module) { box.size = Vec(15*20, 380); int top = 90; int top2 = 35; int left = 118; int column_spacing = 37; int jacks=30; int row_spacing = 37; int lb=18; { SVGPanel *panel = new SVGPanel(); panel->box.size = box.size; panel->setBackground(SVG::load(assetPlugin(plugin,"res/Bene2.svg"))); addChild(panel); } for (int i=0;i<4;i++) { addInput(Port::create(Vec(lb, top+jacks*i), Port::INPUT, module, Bene2::LEFT+i)); addInput(Port::create(Vec(lb+27, top+jacks*i), Port::INPUT, module, Bene2::RIGHT+i)); addInput(Port::create(Vec(lb + 27 + 27, top + jacks * i), Port::INPUT, module, Bene2::RESETL + i)); addInput(Port::create(Vec(lb, top+jacks*i + 140), Port::INPUT, module, Bene2::UP+i)); addInput(Port::create(Vec(lb + 27, top + jacks * i + 140), Port::INPUT, module, Bene2::DOWN + i)); addInput(Port::create(Vec(lb + 27 + 27, top + jacks * i + 140), Port::INPUT, module, Bene2::RESETC + i)); } addParam(ParamWidget::create(Vec(lb,5+ 10 ), module, Bene2::RUNL_PARAM, 0.0, 1.0, 0.0)); addParam(ParamWidget::create(Vec(lb,5+ 10+30), module, Bene2::RUNC_PARAM, 0.0, 1.0, 0.0)); addChild(GrayModuleLightWidget::create>(Vec(lb+3,5+ 10+3), module, Bene2::RUNL_LIGHT)); addChild(GrayModuleLightWidget::create>(Vec(lb+3,5+ 10+3+30), module, Bene2::RUNC_LIGHT)); addInput(Port::create(Vec(lb+30,5+ 9), Port::INPUT, module, Bene2::RUNL_INPUT)); addInput(Port::create(Vec(lb+ 30,5+ 9 + 30), Port::INPUT, module, Bene2::RUNC_INPUT)); addParam(ParamWidget::create(Vec(lb+ 120, 5 + 10), module, Bene2::RESET_LINE, 0.0, 1.0, 0.0)); addParam(ParamWidget::create(Vec(lb+ 120, 5 + 10 + 30), module, Bene2::RESET_COL, 0.0, 1.0, 0.0)); addChild(GrayModuleLightWidget::create>(Vec(lb + 120+3, 5 + 10 + 3), module, Bene2::RESETL_LIGHT)); addChild(GrayModuleLightWidget::create>(Vec(lb + 120+3, 5 + 10 + 3 + 30), module, Bene2::RESETC_LIGHT)); addInput(Port::create(Vec(lb + 150, 5 + 9), Port::INPUT, module, Bene2::RESETL_INPUT)); addInput(Port::create(Vec(lb + 150, 5 + 9 + 30), Port::INPUT, module, Bene2::RESETC_INPUT)); //addInput(Port::create(Vec(left + column_spacing * 3, top ), Port::INPUT, module, Bene2::RESET)); for ( int i = 0 ; i < 4 ; i++) { for ( int j = 0 ; j < 4 ; j++) { addParam(ParamWidget::create(Vec(left + column_spacing * i, top2 + row_spacing * j + 70), module, Bene2::KNOB_PARAM + i + j * 4, 0.0, 2.0, 1.0)); addParam(ParamWidget::create(Vec(left + column_spacing * i + 7.0, top2 + row_spacing * j + 70 + 7.0), module, Bene2::GATE_PARAM + i + j * 4, 0.0, 1.0, 0.0)); addChild(GrayModuleLightWidget::create>(Vec(left + column_spacing * i + 10, top2 + row_spacing * j + 70 + 10), module, Bene2::STEPS_LIGHT + i + j * 4)); addChild(GrayModuleLightWidget::create>(Vec(left + column_spacing * i + 10, top2 + row_spacing * j + 70 + 10), module, Bene2::GATE_LIGHT + i + j * 4)); } addOutput(Port::create(Vec(left+column_spacing * i+5, top2 + row_spacing * 4 + 75 ), Port::OUTPUT, module, Bene2::ROW_OUT + i)); addOutput(Port::create(Vec(left+column_spacing * 4+5, top2 + row_spacing * i + 75 ), Port::OUTPUT, module, Bene2::COLUMN_OUT + i)); addOutput(Port::create(Vec(left + column_spacing*i,300), Port::OUTPUT, module, Bene2::GATES_COL_OUT + i)); addOutput(Port::create(Vec(left + column_spacing * i,330), Port::OUTPUT, module, Bene2::GATES_ROW_OUT + i)); } addChild(Widget::create(Vec(15, 0))); addChild(Widget::create(Vec(box.size.x-30, 0))); addChild(Widget::create(Vec(15, 365))); addChild(Widget::create(Vec(box.size.x-30, 365))); } }; struct Bene2PitchMenuItem : MenuItem { Bene2 *bene2; void onAction(EventAction &e) override { bene2->ignoreGateOnPitchOut = !bene2->ignoreGateOnPitchOut; } void step() override { rightText = (bene2->ignoreGateOnPitchOut) ? "✔" : ""; } }; struct Bene2GateModeItem : MenuItem { Bene2 *bene2; Bene2::GateMode gateMode; void onAction(EventAction &e) override { bene2->gateMode = gateMode; } void step() override { rightText = (bene2->gateMode == gateMode) ? "✔" : ""; } }; void Bene2Widget::appendContextMenu(Menu *menu) { MenuLabel *spacerLabel = new MenuLabel(); menu->addChild(spacerLabel); Bene2 *bene2 = dynamic_cast(module); assert(bene2); menu->addChild(MenuLabel::create("Gate Mode")); Bene2GateModeItem *triggerItem = MenuItem::create("Trigger"); triggerItem->bene2 = bene2; triggerItem->gateMode = Bene2::TRIGGER; menu->addChild(triggerItem); Bene2GateModeItem *retriggerItem = MenuItem::create("Retrigger"); retriggerItem->bene2 = bene2; retriggerItem->gateMode = Bene2::RETRIGGER; menu->addChild(retriggerItem); Bene2GateModeItem *continuousItem = MenuItem::create("Continuous"); continuousItem->bene2 = bene2; continuousItem->gateMode = Bene2::CONTINUOUS; menu->addChild(continuousItem); MenuLabel *spacerLabel2 = new MenuLabel(); menu->addChild(spacerLabel2); Bene2PitchMenuItem *pitchMenuItem = MenuItem::create("Ignore Gate for V/OCT Out"); pitchMenuItem->bene2 = bene2; menu->addChild(pitchMenuItem); } } // namespace rack_plugin_dBiz using namespace rack_plugin_dBiz; RACK_PLUGIN_MODEL_INIT(dBiz, Bene2) { Model *modelBene2 = Model::create("dBiz", "Bene2", "Bene2", SEQUENCER_TAG); return modelBene2; }