diff --git a/src/SEQ3.cpp b/src/SEQ3.cpp index e382cda..051aee9 100644 --- a/src/SEQ3.cpp +++ b/src/SEQ3.cpp @@ -8,11 +8,11 @@ struct SEQ3 : Module { RUN_PARAM, RESET_PARAM, STEPS_PARAM, - ROW1_PARAM, - ROW2_PARAM = ROW1_PARAM + 8, - ROW3_PARAM = ROW2_PARAM + 8, - GATE_PARAM = ROW3_PARAM + 8, - NUM_PARAMS = GATE_PARAM + 8 + ENUMS(ROW1_PARAM, 8), + ENUMS(ROW2_PARAM, 8), + ENUMS(ROW3_PARAM, 8), + ENUMS(GATE_PARAM, 8), + NUM_PARAMS }; enum InputIds { CLOCK_INPUT, @@ -26,42 +26,43 @@ struct SEQ3 : Module { ROW1_OUTPUT, ROW2_OUTPUT, ROW3_OUTPUT, - GATE_OUTPUT, - NUM_OUTPUTS = GATE_OUTPUT + 8 + ENUMS(GATE_OUTPUT, 8), + NUM_OUTPUTS }; enum LightIds { RUNNING_LIGHT, RESET_LIGHT, GATES_LIGHT, - ROW_LIGHTS, - GATE_LIGHTS = ROW_LIGHTS + 3, - NUM_LIGHTS = GATE_LIGHTS + 8 + ENUMS(ROW_LIGHTS, 3), + ENUMS(GATE_LIGHTS, 8), + NUM_LIGHTS }; bool running = true; - SchmittTrigger clockTrigger; // for external clock - // For buttons + SchmittTrigger clockTrigger; SchmittTrigger runningTrigger; SchmittTrigger resetTrigger; SchmittTrigger gateTriggers[8]; - float phase = 0.0f; + /** Phase of internal LFO */ + float phase = 0.f; int index = 0; - bool gateState[8] = {}; - float resetLight = 0.0f; - float stepLights[8] = {}; - - enum GateMode { - TRIGGER, - RETRIGGER, - CONTINUOUS, - }; - GateMode gateMode = TRIGGER; - PulseGenerator gatePulse; + bool gates[8] = {}; SEQ3() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { onReset(); } - void step() override; + + 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(); @@ -72,15 +73,10 @@ struct SEQ3 : Module { // gates json_t *gatesJ = json_array(); for (int i = 0; i < 8; i++) { - json_t *gateJ = json_integer((int) gateState[i]); - json_array_append_new(gatesJ, gateJ); + json_array_insert_new(gatesJ, i, json_integer((int) gates[i])); } json_object_set_new(rootJ, "gates", gatesJ); - // gateMode - json_t *gateModeJ = json_integer((int) gateMode); - json_object_set_new(rootJ, "gateMode", gateModeJ); - return rootJ; } @@ -96,199 +92,115 @@ struct SEQ3 : Module { for (int i = 0; i < 8; i++) { json_t *gateJ = json_array_get(gatesJ, i); if (gateJ) - gateState[i] = !!json_integer_value(gateJ); + gates[i] = !!json_integer_value(gateJ); } } - - // gateMode - json_t *gateModeJ = json_object_get(rootJ, "gateMode"); - if (gateModeJ) - gateMode = (GateMode)json_integer_value(gateModeJ); } - void onReset() override { - for (int i = 0; i < 8; i++) { - gateState[i] = true; - } + 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 onRandomize() override { - for (int i = 0; i < 8; i++) { - gateState[i] = (randomUniform() > 0.5f); + void step() override { + // Run + if (runningTrigger.process(params[RUN_PARAM].value)) { + running = !running; } - } -}; - - -void SEQ3::step() { - const float lightLambda = 0.075f; - // Run - if (runningTrigger.process(params[RUN_PARAM].value)) { - running = !running; - } - lights[RUNNING_LIGHT].value = running ? 1.0f : 0.0f; - - bool nextStep = false; - if (running) { - if (inputs[EXT_CLOCK_INPUT].active) { - // External clock - if (clockTrigger.process(inputs[EXT_CLOCK_INPUT].value)) { - phase = 0.0f; - nextStep = true; + 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) { - phase -= 1.0f; - nextStep = true; + 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)) { - phase = 0.0f; - index = 8; - nextStep = true; - resetLight = 1.0f; - } - if (nextStep) { - // Advance step - int numSteps = clamp(roundf(params[STEPS_PARAM].value + inputs[STEPS_INPUT].value), 1.0f, 8.0f); - index += 1; - if (index >= numSteps) { - index = 0; + // Reset + if (resetTrigger.process(params[RESET_PARAM].value + inputs[RESET_INPUT].value)) { + setIndex(0); } - stepLights[index] = 1.0f; - gatePulse.trigger(1e-3); - } - resetLight -= resetLight / lightLambda * engineGetSampleTime(); - - bool pulse = gatePulse.process(engineGetSampleTime()); - - // Gate buttons - for (int i = 0; i < 8; i++) { - if (gateTriggers[i].process(params[GATE_PARAM + i].value)) { - gateState[i] = !gateState[i]; + // 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)); } - bool gateOn = (running && i == index && gateState[i]); - if (gateMode == TRIGGER) - gateOn = gateOn && pulse; - else if (gateMode == RETRIGGER) - gateOn = gateOn && !pulse; - outputs[GATE_OUTPUT + i].value = gateOn ? 10.0f : 0.0f; - stepLights[i] -= stepLights[i] / lightLambda * engineGetSampleTime(); - lights[GATE_LIGHTS + i].value = gateState[i] ? 1.0f - stepLights[i] : stepLights[i]; + // Outputs + outputs[ROW1_OUTPUT].value = params[ROW1_PARAM + index].value; + outputs[ROW2_OUTPUT].value = params[ROW2_PARAM + index].value; + outputs[ROW3_OUTPUT].value = params[ROW3_PARAM + index].value; + 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; } - - // Rows - float row1 = params[ROW1_PARAM + index].value; - float row2 = params[ROW2_PARAM + index].value; - float row3 = params[ROW3_PARAM + index].value; - bool gatesOn = (running && gateState[index]); - if (gateMode == TRIGGER) - gatesOn = gatesOn && pulse; - else if (gateMode == RETRIGGER) - gatesOn = gatesOn && !pulse; - - // Outputs - outputs[ROW1_OUTPUT].value = row1; - outputs[ROW2_OUTPUT].value = row2; - outputs[ROW3_OUTPUT].value = row3; - outputs[GATES_OUTPUT].value = gatesOn ? 10.0f : 0.0f; - lights[RESET_LIGHT].value = resetLight; - lights[GATES_LIGHT].value = gatesOn ? 1.0f : 0.0f; - lights[ROW_LIGHTS].value = row1 / 10.0f; - lights[ROW_LIGHTS + 1].value = row2 / 10.0f; - lights[ROW_LIGHTS + 2].value = row3 / 10.0f; -} - - -struct SEQ3Widget : ModuleWidget { - SEQ3Widget(SEQ3 *module); - void appendContextMenu(Menu *menu) override; }; -SEQ3Widget::SEQ3Widget(SEQ3 *module) : ModuleWidget(module) { - setPanel(SVG::load(assetPlugin(plugin, "res/SEQ3.svg"))); - - 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))); - addParam(ParamWidget::create(Vec(18, 56), module, SEQ3::CLOCK_PARAM, -2.0f, 6.0f, 2.0f)); - addParam(ParamWidget::create(Vec(60, 61-1), module, SEQ3::RUN_PARAM, 0.0f, 1.0f, 0.0f)); - addChild(ModuleLightWidget::create>(Vec(64.4f, 64.4f), module, SEQ3::RUNNING_LIGHT)); - addParam(ParamWidget::create(Vec(99, 61-1), module, SEQ3::RESET_PARAM, 0.0f, 1.0f, 0.0f)); - addChild(ModuleLightWidget::create>(Vec(103.4f, 64.4f), module, SEQ3::RESET_LIGHT)); - addParam(ParamWidget::create(Vec(132, 56), module, SEQ3::STEPS_PARAM, 1.0f, 8.0f, 8.0f)); - addChild(ModuleLightWidget::create>(Vec(179.4f, 64.4f), module, SEQ3::GATES_LIGHT)); - addChild(ModuleLightWidget::create>(Vec(218.4f, 64.4f), module, SEQ3::ROW_LIGHTS)); - addChild(ModuleLightWidget::create>(Vec(256.4f, 64.4f), module, SEQ3::ROW_LIGHTS + 1)); - addChild(ModuleLightWidget::create>(Vec(295.4f, 64.4f), module, SEQ3::ROW_LIGHTS + 2)); - - static const float portX[8] = {20, 58, 96, 135, 173, 212, 250, 289}; - addInput(Port::create(Vec(portX[0]-1, 98), Port::INPUT, module, SEQ3::CLOCK_INPUT)); - addInput(Port::create(Vec(portX[1]-1, 98), Port::INPUT, module, SEQ3::EXT_CLOCK_INPUT)); - addInput(Port::create(Vec(portX[2]-1, 98), Port::INPUT, module, SEQ3::RESET_INPUT)); - addInput(Port::create(Vec(portX[3]-1, 98), Port::INPUT, module, SEQ3::STEPS_INPUT)); - addOutput(Port::create(Vec(portX[4]-1, 98), Port::OUTPUT, module, SEQ3::GATES_OUTPUT)); - addOutput(Port::create(Vec(portX[5]-1, 98), Port::OUTPUT, module, SEQ3::ROW1_OUTPUT)); - addOutput(Port::create(Vec(portX[6]-1, 98), Port::OUTPUT, module, SEQ3::ROW2_OUTPUT)); - addOutput(Port::create(Vec(portX[7]-1, 98), Port::OUTPUT, module, SEQ3::ROW3_OUTPUT)); - - for (int i = 0; i < 8; i++) { - addParam(ParamWidget::create(Vec(portX[i]-2, 157), module, SEQ3::ROW1_PARAM + i, 0.0f, 10.0f, 0.0f)); - addParam(ParamWidget::create(Vec(portX[i]-2, 198), module, SEQ3::ROW2_PARAM + i, 0.0f, 10.0f, 0.0f)); - addParam(ParamWidget::create(Vec(portX[i]-2, 240), module, SEQ3::ROW3_PARAM + i, 0.0f, 10.0f, 0.0f)); - addParam(ParamWidget::create(Vec(portX[i]+2, 278-1), module, SEQ3::GATE_PARAM + i, 0.0f, 1.0f, 0.0f)); - addChild(ModuleLightWidget::create>(Vec(portX[i]+6.4f, 281.4f), module, SEQ3::GATE_LIGHTS + i)); - addOutput(Port::create(Vec(portX[i]-1, 307), Port::OUTPUT, module, SEQ3::GATE_OUTPUT + i)); - } -} +struct SEQ3Widget : ModuleWidget { + SEQ3Widget(SEQ3 *module) : ModuleWidget(module) { + setPanel(SVG::load(assetPlugin(plugin, "res/SEQ3.svg"))); + + 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))); + + addParam(ParamWidget::create(Vec(18, 56), module, SEQ3::CLOCK_PARAM, -2.0f, 6.0f, 2.0f)); + addParam(ParamWidget::create(Vec(60, 61-1), module, SEQ3::RUN_PARAM, 0.0f, 1.0f, 0.0f)); + addChild(ModuleLightWidget::create>(Vec(64.4f, 64.4f), module, SEQ3::RUNNING_LIGHT)); + addParam(ParamWidget::create(Vec(99, 61-1), module, SEQ3::RESET_PARAM, 0.0f, 1.0f, 0.0f)); + addChild(ModuleLightWidget::create>(Vec(103.4f, 64.4f), module, SEQ3::RESET_LIGHT)); + addParam(ParamWidget::create(Vec(132, 56), module, SEQ3::STEPS_PARAM, 1.0f, 8.0f, 8.0f)); + addChild(ModuleLightWidget::create>(Vec(179.4f, 64.4f), module, SEQ3::GATES_LIGHT)); + addChild(ModuleLightWidget::create>(Vec(218.4f, 64.4f), module, SEQ3::ROW_LIGHTS)); + addChild(ModuleLightWidget::create>(Vec(256.4f, 64.4f), module, SEQ3::ROW_LIGHTS + 1)); + addChild(ModuleLightWidget::create>(Vec(295.4f, 64.4f), module, SEQ3::ROW_LIGHTS + 2)); + + static const float portX[8] = {20, 58, 96, 135, 173, 212, 250, 289}; + addInput(Port::create(Vec(portX[0]-1, 98), Port::INPUT, module, SEQ3::CLOCK_INPUT)); + addInput(Port::create(Vec(portX[1]-1, 98), Port::INPUT, module, SEQ3::EXT_CLOCK_INPUT)); + addInput(Port::create(Vec(portX[2]-1, 98), Port::INPUT, module, SEQ3::RESET_INPUT)); + addInput(Port::create(Vec(portX[3]-1, 98), Port::INPUT, module, SEQ3::STEPS_INPUT)); + addOutput(Port::create(Vec(portX[4]-1, 98), Port::OUTPUT, module, SEQ3::GATES_OUTPUT)); + addOutput(Port::create(Vec(portX[5]-1, 98), Port::OUTPUT, module, SEQ3::ROW1_OUTPUT)); + addOutput(Port::create(Vec(portX[6]-1, 98), Port::OUTPUT, module, SEQ3::ROW2_OUTPUT)); + addOutput(Port::create(Vec(portX[7]-1, 98), Port::OUTPUT, module, SEQ3::ROW3_OUTPUT)); -struct SEQ3GateModeItem : MenuItem { - SEQ3 *seq3; - SEQ3::GateMode gateMode; - void onAction(EventAction &e) override { - seq3->gateMode = gateMode; - } - void step() override { - rightText = CHECKMARK(seq3->gateMode == gateMode); + for (int i = 0; i < 8; i++) { + addParam(ParamWidget::create(Vec(portX[i]-2, 157), module, SEQ3::ROW1_PARAM + i, 0.0f, 10.0f, 0.0f)); + addParam(ParamWidget::create(Vec(portX[i]-2, 198), module, SEQ3::ROW2_PARAM + i, 0.0f, 10.0f, 0.0f)); + addParam(ParamWidget::create(Vec(portX[i]-2, 240), module, SEQ3::ROW3_PARAM + i, 0.0f, 10.0f, 0.0f)); + addParam(ParamWidget::create(Vec(portX[i]+2, 278-1), module, SEQ3::GATE_PARAM + i, 0.0f, 1.0f, 0.0f)); + addChild(ModuleLightWidget::create>(Vec(portX[i]+6.4f, 281.4f), module, SEQ3::GATE_LIGHTS + i)); + addOutput(Port::create(Vec(portX[i]-1, 307), Port::OUTPUT, module, SEQ3::GATE_OUTPUT + i)); + } } }; -void SEQ3Widget::appendContextMenu(Menu *menu) { - menu->addChild(MenuEntry::create()); - - SEQ3 *seq3 = dynamic_cast(module); - assert(seq3); - - menu->addChild(MenuLabel::create("Gate Mode")); - - SEQ3GateModeItem *triggerItem = MenuItem::create("Trigger"); - triggerItem->seq3 = seq3; - triggerItem->gateMode = SEQ3::TRIGGER; - menu->addChild(triggerItem); - - SEQ3GateModeItem *retriggerItem = MenuItem::create("Retrigger"); - retriggerItem->seq3 = seq3; - retriggerItem->gateMode = SEQ3::RETRIGGER; - menu->addChild(retriggerItem); - - SEQ3GateModeItem *continuousItem = MenuItem::create("Continuous"); - continuousItem->seq3 = seq3; - continuousItem->gateMode = SEQ3::CONTINUOUS; - menu->addChild(continuousItem); -} Model *modelSEQ3 = Model::create("Fundamental", "SEQ3", "SEQ-3", SEQUENCER_TAG);