#include "LFSR.hpp" #include "dsp/digital.hpp" namespace rack_plugin_alto777_LFSR { struct YASeq3 : Module { enum ParamIds { CLOCK_PARAM, RUN_PARAM, RESET_PARAM, STEPS_PARAM, ENUMS(ROW1_PARAM, 8), ENUMS(ROW2_PARAM, 8), ENUMS(ROW3_PARAM, 8), ENUMS(GATE_PARAM, 8), ENUMS(MODE_PARAM, 3), ENUMS(RANGE_PARAM, 3), NUM_PARAMS }; enum InputIds { CLOCK_INPUT, EXT_CLOCK_INPUT, RESET_INPUT, STEPS_INPUT, NUM_INPUTS }; enum OutputIds { GATES_OUTPUT, ROW1_OUTPUT, ROW2_OUTPUT, ROW3_OUTPUT, ENUMS(GATE_OUTPUT, 8), NUM_OUTPUTS }; enum LightIds { RUNNING_LIGHT, RESET_LIGHT, // GATES_LIGHT, // ENUMS(ROW_LIGHTS, 3), ENUMS(GATE_LIGHTS, 8), NUM_LIGHTS }; bool running = true; SchmittTrigger clockTrigger; SchmittTrigger runningTrigger; SchmittTrigger resetTrigger; SchmittTrigger gateTriggers[8]; /** Phase of internal LFO */ float phase = 0.f; int index = 0; bool gates[8] = {}; const int chromaticScale[13] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; const int diatonicScale[13] = {0, 2, 4, 5, 7, 9, 11, 12, 14, 16, 17, 19, 21}; YASeq3() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { onReset(); } void onReset() override { for (int i = 0; i < 8; i++) { gates[i] = true; } } void onRandomize() override { for (int i = 0; i < 8; i++) { gates[i] = (randomUniform() > 0.5f); } } json_t *toJson() override { json_t *rootJ = json_object(); // running json_object_set_new(rootJ, "running", json_boolean(running)); // gates json_t *gatesJ = json_array(); for (int i = 0; i < 8; i++) { json_array_insert_new(gatesJ, i, json_integer((int) gates[i])); } json_object_set_new(rootJ, "gates", gatesJ); return rootJ; } void fromJson(json_t *rootJ) override { // running json_t *runningJ = json_object_get(rootJ, "running"); if (runningJ) running = json_is_true(runningJ); // gates json_t *gatesJ = json_object_get(rootJ, "gates"); if (gatesJ) { for (int i = 0; i < 8; i++) { json_t *gateJ = json_array_get(gatesJ, i); if (gateJ) gates[i] = !!json_integer_value(gateJ); } } } void setIndex(int index) { int numSteps = (int) clamp(roundf(params[STEPS_PARAM].value + inputs[STEPS_INPUT].value), 1.0f, 8.0f); phase = 0.f; this->index = index; if (this->index >= numSteps) this->index = 0; } void step() override { // Run if (runningTrigger.process(params[RUN_PARAM].value)) { running = !running; } bool gateIn = false; if (running) { if (inputs[EXT_CLOCK_INPUT].active) { // External clock if (clockTrigger.process(inputs[EXT_CLOCK_INPUT].value)) { setIndex(index + 1); } gateIn = clockTrigger.isHigh(); } else { // Internal clock float clockTime = powf(2.0f, params[CLOCK_PARAM].value + inputs[CLOCK_INPUT].value); phase += clockTime * engineGetSampleTime(); if (phase >= 1.0f) { setIndex(index + 1); } gateIn = (phase < 0.5f); } } // Reset if (resetTrigger.process(params[RESET_PARAM].value + inputs[RESET_INPUT].value)) { setIndex(0); } // Gate buttons for (int i = 0; i < 8; i++) { if (gateTriggers[i].process(params[GATE_PARAM + i].value)) { gates[i] = !gates[i]; } outputs[GATE_OUTPUT + i].value = (running && gateIn && i == index && gates[i]) ? 10.0f : 0.0f; lights[GATE_LIGHTS + i].setBrightnessSmooth((gateIn && i == index) ? (gates[i] ? 1.f : 0.33) : (gates[i] ? 0.66 : 0.0)); } // Outputs /* figure out how to loopy loop enumerated enums */ int rowSetting = round(params[ROW1_PARAM + index].value + 0.1); if (params[MODE_PARAM + 0].value > 1.1f) { /* infinite */ outputs[ROW1_OUTPUT].value = params[ROW1_PARAM + index].value * ((params[RANGE_PARAM + 0].value > 0.5f) ? 1.0f : 0.1f); } else if (params[MODE_PARAM + 0].value < 0.9f) { /* diatonic */ outputs[ROW1_OUTPUT].value = chromaticScale[rowSetting] / 12.0f; } else { /* chromatic */ outputs[ROW1_OUTPUT].value = diatonicScale[rowSetting] / 12.0f; } rowSetting = round(params[ROW2_PARAM + index].value + 0.1); if (params[MODE_PARAM + 1].value > 1.1f) { /* infinite */ outputs[ROW2_OUTPUT].value = params[ROW2_PARAM + index].value * ((params[RANGE_PARAM + 1].value > 0.5f) ? 1.0f : 0.1f); } else if (params[MODE_PARAM + 1].value < 0.9f) { /* diatonic */ outputs[ROW2_OUTPUT].value = chromaticScale[rowSetting] / 12.0f; } else { /* chromatic */ outputs[ROW2_OUTPUT].value = diatonicScale[rowSetting] / 12.0f; } rowSetting = round(params[ROW3_PARAM + index].value + 0.1); if (params[MODE_PARAM + 2].value > 1.1f) { /* infinite */ outputs[ROW3_OUTPUT].value = params[ROW3_PARAM + index].value * ((params[RANGE_PARAM + 2].value > 0.5f) ? 1.0f : 0.1f); } else if (params[MODE_PARAM + 2].value < 0.9f) { /* diatonic */ outputs[ROW3_OUTPUT].value = chromaticScale[rowSetting] / 12.0f; } else { /* chromatic */ outputs[ROW3_OUTPUT].value = diatonicScale[rowSetting] / 12.0f; } outputs[GATES_OUTPUT].value = (gateIn && gates[index]) ? 10.0f : 0.0f; lights[RUNNING_LIGHT].value = (running); lights[RESET_LIGHT].setBrightnessSmooth(resetTrigger.isHigh()); // lights[GATES_LIGHT].setBrightnessSmooth(gateIn); // lights[ROW_LIGHTS].value = outputs[ROW1_OUTPUT].value / 10.0f; // lights[ROW_LIGHTS + 1].value = outputs[ROW2_OUTPUT].value / 10.0f; // lights[ROW_LIGHTS + 2].value = outputs[ROW3_OUTPUT].value / 10.0f; } }; /* from Playground MyModule */ struct x13SlidePot : SVGSlider { /* 13 steps 6 px per = 78 tall, so */ x13SlidePot() { maxHandlePos = Vec(-1.65, 0);// .plus(margin); minHandlePos = Vec(-1.65, 72);// .plus(margin); setSVGs(SVG::load(assetPlugin(plugin, "res/x13SlidePot.svg")), SVG::load(assetPlugin(plugin, "res/x13SlidePotHandle.svg"))); } }; template struct bigLight : BASE { bigLight() { this->box.size = mm2px(Vec(6.0, 6.0)); } }; struct myOther2Switch : SVGSwitch, ToggleSwitch { myOther2Switch() { addFrame(SVG::load(assetPlugin(plugin, "res/myCKSS_1.svg"))); addFrame(SVG::load(assetPlugin(plugin, "res/myCKSS_0.svg"))); } }; struct my3Switch : SVGSwitch, ToggleSwitch { my3Switch() { addFrame(SVG::load(assetPlugin(plugin, "res/myCKSSThree_0.svg"))); addFrame(SVG::load(assetPlugin(plugin, "res/myCKSSThree_1.svg"))); addFrame(SVG::load(assetPlugin(plugin, "res/myCKSSThree_2.svg"))); } }; // addParam(ParamWidget::create(Vec(330, 87), module, YASeq3::RANGE + 0, 0.0, 1.0, 0.0)); // addParam(ParamWidget::create(Vec(352, 92), module, YASeq3::MODE + 0, 0.0, 2.0, 0.0)); struct YASeq3Widget : ModuleWidget { YASeq3Widget(YASeq3 *module) : ModuleWidget(module) { setPanel(SVG::load(assetPlugin(plugin, "res/YASeq3.svg"))); addChild(Widget::create(Vec(0, 0))); addChild(Widget::create(Vec(box.size.x - 15, 0))); addChild(Widget::create(Vec(0, 365))); addChild(Widget::create(Vec(box.size.x - 15, 365))); addParam(ParamWidget::create(Vec(20, 98 - 50 - 15 - 4), module, YASeq3::CLOCK_PARAM, -2.0f, 6.0f, 2.0f)); addParam(ParamWidget::create(Vec(154, 98 - 50 - 15), module, YASeq3::RUN_PARAM, 0.0f, 1.0f, 0.0f)); addChild(ModuleLightWidget::create>(Vec(154 + 2, 98 - 50 - 15 + 2), module, YASeq3::RUNNING_LIGHT)); addParam(ParamWidget::create(Vec(192, 98 - 50 - 15), module, YASeq3::RESET_PARAM, 0.0f, 1.0f, 0.0f)); addChild(ModuleLightWidget::create>(Vec(192 + 2, 98 - 50 - 15 + 2), module, YASeq3::RESET_LIGHT)); addInput(Port::create(Vec(230, 98 - 50 - 15), Port::INPUT, module, YASeq3::RESET_INPUT)); addParam(ParamWidget::create(Vec(268, 98 - 50 - 15 - 4), module, YASeq3::STEPS_PARAM, 1.0f, 8.0f, 8.0f)); addInput(Port::create(Vec(306, 98 - 50 - 15), Port::INPUT, module, YASeq3::STEPS_INPUT)); // addChild(ModuleLightWidget::create>(Vec(179.4f, 64.4f), module, YASeq3::GATES_LIGHT)); // addChild(ModuleLightWidget::create>(Vec(218.4f, 64.4f), module, YASeq3::ROW_LIGHTS)); // addChild(ModuleLightWidget::create>(Vec(256.4f, 64.4f), module, YASeq3::ROW_LIGHTS + 1)); // addChild(ModuleLightWidget::create>(Vec(295.4f, 64.4f), module, YASeq3::ROW_LIGHTS + 2)); static const float portX[8] = {20, 58, 96, 135, 173, 212, 250, 289}; addInput(Port::create(Vec(64.4f - 6, 98 - 50 - 15), Port::INPUT, module, YASeq3::CLOCK_INPUT)); addInput(Port::create(Vec(103.4 - 12, 98 - 50 - 15), Port::INPUT, module, YASeq3::EXT_CLOCK_INPUT)); addOutput(Port::create(Vec(332, 307 + 50 - 13), Port::OUTPUT, module, YASeq3::GATES_OUTPUT)); addOutput(Port::create(Vec(332, 132), Port::OUTPUT, module, YASeq3::ROW1_OUTPUT)); addOutput(Port::create(Vec(332, 212), Port::OUTPUT, module, YASeq3::ROW2_OUTPUT)); addOutput(Port::create(Vec(332, 292), Port::OUTPUT, module, YASeq3::ROW3_OUTPUT)); addParam(ParamWidget::create(Vec(330, 97), module, YASeq3::RANGE_PARAM + 0, 0.0, 1.0, 0.0)); addParam(ParamWidget::create(Vec(352, 102), module, YASeq3::MODE_PARAM + 0, 0.0, 2.0, 0.0)); addParam(ParamWidget::create(Vec(330, 97 + 80), module, YASeq3::RANGE_PARAM + 1, 0.0, 1.0, 0.0)); addParam(ParamWidget::create(Vec(352, 102 + 80), module, YASeq3::MODE_PARAM + 1, 0.0, 2.0, 0.0)); addParam(ParamWidget::create(Vec(330, 97 + 160), module, YASeq3::RANGE_PARAM + 2, 0.0, 1.0, 0.0)); addParam(ParamWidget::create(Vec(352, 102 + 160), module, YASeq3::MODE_PARAM + 2, 0.0, 2.0, 0.0)); for (int i = 0; i < 8; i++) { addParam(ParamWidget::create(Vec(portX[i]-2 + 10, 157 - 80), module, YASeq3::ROW1_PARAM + i, 0.0f, 12.0f, 0.0f)); addParam(ParamWidget::create(Vec(portX[i]-2 + 10, 198 - 40), module, YASeq3::ROW2_PARAM + i, 0.0f, 12.0f, 0.0f)); addParam(ParamWidget::create(Vec(portX[i]-2 + 10, 240 - 0), module, YASeq3::ROW3_PARAM + i, 0.0f, 12.0f, 0.0f)); /**/ addParam(ParamWidget::create(Vec(portX[i]+2, 278-1 + 50 - 7), module, YASeq3::GATE_PARAM + i, 0.0f, 1.0f, 0.0f)); /**/ addChild(ModuleLightWidget::create>(Vec(portX[i]+6.4f, 281.4f + 50 - 7), module, YASeq3::GATE_LIGHTS + i)); /**/ addOutput(Port::create(Vec(portX[i]-1, 307 + 50 - 13), Port::OUTPUT, module, YASeq3::GATE_OUTPUT + i)); } } }; } // namespace rack_plugin_alto777_LFSR using namespace rack_plugin_alto777_LFSR; RACK_PLUGIN_MODEL_INIT(alto777_LFSR, YASeq3) { Model *modelYASeq3 = Model::create("alto777_LFSR", "YASeq3", "Yet Another SEQ3", SEQUENCER_TAG); return modelYASeq3; }