diff --git a/src/Gates.cpp b/src/Gates.cpp index 514f980..ab8e417 100644 --- a/src/Gates.cpp +++ b/src/Gates.cpp @@ -19,7 +19,7 @@ struct Gates : Module { FLIP_OUTPUT, FLOP_OUTPUT, GATE_OUTPUT, - END_OUTPUT, + DELAY_OUTPUT, OUTPUTS_LEN }; enum LightId { @@ -29,20 +29,32 @@ struct Gates : Module { ENUMS(FLIP_LIGHT, 2), ENUMS(FLOP_LIGHT, 2), ENUMS(GATE_LIGHT, 2), - ENUMS(END_LIGHT, 2), + ENUMS(DELAY_LIGHT, 2), LIGHTS_LEN }; dsp::BooleanTrigger resetParamTrigger; - bool state[16] = {}; + // Bit field for each channel + uint16_t stateBits = 0; dsp::SchmittTrigger resetTrigger[16]; dsp::PulseGenerator risePulse[16]; dsp::PulseGenerator fallPulse[16]; - dsp::PulseGenerator eogPulse[16]; bool flop[16] = {}; float gateTime[16] = {}; dsp::ClockDivider lightDivider; + double time = 0.0; + struct StateEvent { + double time; + uint16_t stateBits; + + bool operator<(const StateEvent& other) const { + return time < other.time; + } + }; + // TODO Change this to a circular buffer with binary search, to avoid allocations. + std::set stateEvents; + Gates() { config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN); configParam(LENGTH_PARAM, std::log2(1e-3f), std::log2(10.f), std::log2(0.1f), "Gate length", " ms", 2, 1000); @@ -55,7 +67,7 @@ struct Gates : Module { configOutput(FLIP_OUTPUT, "Flip"); configOutput(FLOP_OUTPUT, "Flop"); configOutput(GATE_OUTPUT, "Gate"); - configOutput(END_OUTPUT, "End of gate"); + configOutput(DELAY_OUTPUT, "Gate delay"); lightDivider.setDivision(32); for (int c = 0; c < 16; c++) { @@ -71,7 +83,7 @@ struct Gates : Module { bool anyFlip = false; bool anyFlop = false; bool anyGate = false; - bool anyEnd = false; + bool anyDelay = false; // Reset bool resetButton = false; @@ -79,26 +91,32 @@ struct Gates : Module { resetButton = true; } + bool newState = false; + // Process channels for (int c = 0; c < channels; c++) { float in = inputs[IN_INPUT].getVoltage(c); - if (state[c]) { + if (stateBits & (1 << c)) { // HIGH to LOW if (in <= 0.1f) { - state[c] = false; + // states[c] = false + stateBits &= ~(1 << c); fallPulse[c].trigger(1e-3f); + newState = true; } } else { // LOW to HIGH if (in >= 2.f) { - state[c] = true; + // states[c] = true + stateBits |= (1 << c); risePulse[c].trigger(1e-3f); // Flip flop flop[c] ^= true; // Gate gateTime[c] = 0.f; + newState = true; } } @@ -118,7 +136,6 @@ struct Gates : Module { gateTime[c] += args.sampleTime; if (reset || gateTime[c] >= gateLength) { gateTime[c] = INFINITY; - eogPulse[c].trigger(1e-3f); } } @@ -141,17 +158,40 @@ struct Gates : Module { outputs[GATE_OUTPUT].setVoltage(gate ? 10.f : 0.f, c); anyGate = anyGate || gate; - bool end = eogPulse[c].process(args.sampleTime); - outputs[END_OUTPUT].setVoltage(end ? 10.f : 0.f, c); - anyEnd = anyEnd || end; + // Gate delay output + bool delayGate = false; + // Timestamp of past gate + double delayTime = time - gateLength; + // Find event less than or equal to delayTime. + // If not found, gate will be off. + auto eventIt = stateEvents.upper_bound({delayTime, 0}); + if (eventIt != stateEvents.begin()) { + eventIt--; + const StateEvent& event = *eventIt; + delayGate = event.stateBits & (1 << c); + } + + outputs[DELAY_OUTPUT].setVoltage(delayGate ? 10.f : 0.f, c); + anyDelay = anyDelay || delayGate; + } + + // Push state event + if (newState) { + // Keep buffer a reasonable size + if (stateEvents.size() >= (1 << 12) - 1) { + stateEvents.erase(stateEvents.begin()); + } + + stateEvents.insert({time, stateBits}); } + time += args.sampleTime; outputs[RISE_OUTPUT].setChannels(channels); outputs[FALL_OUTPUT].setChannels(channels); outputs[FLIP_OUTPUT].setChannels(channels); outputs[FLOP_OUTPUT].setChannels(channels); outputs[GATE_OUTPUT].setChannels(channels); - outputs[END_OUTPUT].setChannels(channels); + outputs[DELAY_OUTPUT].setChannels(channels); if (lightDivider.process()) { float lightTime = args.sampleTime * lightDivider.getDivision(); @@ -166,8 +206,8 @@ struct Gates : Module { lights[FLOP_LIGHT + 1].setBrightnessSmooth(anyFlop && channels > 1, lightTime); lights[GATE_LIGHT + 0].setBrightnessSmooth(anyGate && channels <= 1, lightTime); lights[GATE_LIGHT + 1].setBrightnessSmooth(anyGate && channels > 1, lightTime); - lights[END_LIGHT + 0].setBrightnessSmooth(anyEnd && channels <= 1, lightTime); - lights[END_LIGHT + 1].setBrightnessSmooth(anyEnd && channels > 1, lightTime); + lights[DELAY_LIGHT + 0].setBrightnessSmooth(anyDelay && channels <= 1, lightTime); + lights[DELAY_LIGHT + 1].setBrightnessSmooth(anyDelay && channels > 1, lightTime); } } }; @@ -195,14 +235,14 @@ struct GatesWidget : ModuleWidget { addOutput(createOutputCentered(mm2px(Vec(7.297, 97.958)), module, Gates::FLIP_OUTPUT)); addOutput(createOutputCentered(mm2px(Vec(18.134, 97.958)), module, Gates::FLOP_OUTPUT)); addOutput(createOutputCentered(mm2px(Vec(7.297, 113.115)), module, Gates::GATE_OUTPUT)); - addOutput(createOutputCentered(mm2px(Vec(18.134, 113.115)), module, Gates::END_OUTPUT)); + addOutput(createOutputCentered(mm2px(Vec(18.134, 113.115)), module, Gates::DELAY_OUTPUT)); addChild(createLightCentered>>(mm2px(Vec(11.027, 79.007)), module, Gates::RISE_LIGHT)); addChild(createLightCentered>>(mm2px(Vec(21.864, 79.007)), module, Gates::FALL_LIGHT)); addChild(createLightCentered>>(mm2px(Vec(11.027, 94.233)), module, Gates::FLIP_LIGHT)); addChild(createLightCentered>>(mm2px(Vec(21.864, 94.233)), module, Gates::FLOP_LIGHT)); addChild(createLightCentered>>(mm2px(Vec(11.027, 109.393)), module, Gates::GATE_LIGHT)); - addChild(createLightCentered>>(mm2px(Vec(21.864, 109.393)), module, Gates::END_LIGHT)); + addChild(createLightCentered>>(mm2px(Vec(21.864, 109.393)), module, Gates::DELAY_LIGHT)); } };