| @@ -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<StateEvent> 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<PJ301MPort>(mm2px(Vec(7.297, 97.958)), module, Gates::FLIP_OUTPUT)); | |||
| addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(18.134, 97.958)), module, Gates::FLOP_OUTPUT)); | |||
| addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.297, 113.115)), module, Gates::GATE_OUTPUT)); | |||
| addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(18.134, 113.115)), module, Gates::END_OUTPUT)); | |||
| addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(18.134, 113.115)), module, Gates::DELAY_OUTPUT)); | |||
| addChild(createLightCentered<TinyLight<YellowBlueLight<>>>(mm2px(Vec(11.027, 79.007)), module, Gates::RISE_LIGHT)); | |||
| addChild(createLightCentered<TinyLight<YellowBlueLight<>>>(mm2px(Vec(21.864, 79.007)), module, Gates::FALL_LIGHT)); | |||
| addChild(createLightCentered<TinyLight<YellowBlueLight<>>>(mm2px(Vec(11.027, 94.233)), module, Gates::FLIP_LIGHT)); | |||
| addChild(createLightCentered<TinyLight<YellowBlueLight<>>>(mm2px(Vec(21.864, 94.233)), module, Gates::FLOP_LIGHT)); | |||
| addChild(createLightCentered<TinyLight<YellowBlueLight<>>>(mm2px(Vec(11.027, 109.393)), module, Gates::GATE_LIGHT)); | |||
| addChild(createLightCentered<TinyLight<YellowBlueLight<>>>(mm2px(Vec(21.864, 109.393)), module, Gates::END_LIGHT)); | |||
| addChild(createLightCentered<TinyLight<YellowBlueLight<>>>(mm2px(Vec(21.864, 109.393)), module, Gates::DELAY_LIGHT)); | |||
| } | |||
| }; | |||