| @@ -19,7 +19,7 @@ struct Gates : Module { | |||||
| FLIP_OUTPUT, | FLIP_OUTPUT, | ||||
| FLOP_OUTPUT, | FLOP_OUTPUT, | ||||
| GATE_OUTPUT, | GATE_OUTPUT, | ||||
| END_OUTPUT, | |||||
| DELAY_OUTPUT, | |||||
| OUTPUTS_LEN | OUTPUTS_LEN | ||||
| }; | }; | ||||
| enum LightId { | enum LightId { | ||||
| @@ -29,20 +29,32 @@ struct Gates : Module { | |||||
| ENUMS(FLIP_LIGHT, 2), | ENUMS(FLIP_LIGHT, 2), | ||||
| ENUMS(FLOP_LIGHT, 2), | ENUMS(FLOP_LIGHT, 2), | ||||
| ENUMS(GATE_LIGHT, 2), | ENUMS(GATE_LIGHT, 2), | ||||
| ENUMS(END_LIGHT, 2), | |||||
| ENUMS(DELAY_LIGHT, 2), | |||||
| LIGHTS_LEN | LIGHTS_LEN | ||||
| }; | }; | ||||
| dsp::BooleanTrigger resetParamTrigger; | dsp::BooleanTrigger resetParamTrigger; | ||||
| bool state[16] = {}; | |||||
| // Bit field for each channel | |||||
| uint16_t stateBits = 0; | |||||
| dsp::SchmittTrigger resetTrigger[16]; | dsp::SchmittTrigger resetTrigger[16]; | ||||
| dsp::PulseGenerator risePulse[16]; | dsp::PulseGenerator risePulse[16]; | ||||
| dsp::PulseGenerator fallPulse[16]; | dsp::PulseGenerator fallPulse[16]; | ||||
| dsp::PulseGenerator eogPulse[16]; | |||||
| bool flop[16] = {}; | bool flop[16] = {}; | ||||
| float gateTime[16] = {}; | float gateTime[16] = {}; | ||||
| dsp::ClockDivider lightDivider; | 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() { | Gates() { | ||||
| config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN); | 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); | 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(FLIP_OUTPUT, "Flip"); | ||||
| configOutput(FLOP_OUTPUT, "Flop"); | configOutput(FLOP_OUTPUT, "Flop"); | ||||
| configOutput(GATE_OUTPUT, "Gate"); | configOutput(GATE_OUTPUT, "Gate"); | ||||
| configOutput(END_OUTPUT, "End of gate"); | |||||
| configOutput(DELAY_OUTPUT, "Gate delay"); | |||||
| lightDivider.setDivision(32); | lightDivider.setDivision(32); | ||||
| for (int c = 0; c < 16; c++) { | for (int c = 0; c < 16; c++) { | ||||
| @@ -71,7 +83,7 @@ struct Gates : Module { | |||||
| bool anyFlip = false; | bool anyFlip = false; | ||||
| bool anyFlop = false; | bool anyFlop = false; | ||||
| bool anyGate = false; | bool anyGate = false; | ||||
| bool anyEnd = false; | |||||
| bool anyDelay = false; | |||||
| // Reset | // Reset | ||||
| bool resetButton = false; | bool resetButton = false; | ||||
| @@ -79,26 +91,32 @@ struct Gates : Module { | |||||
| resetButton = true; | resetButton = true; | ||||
| } | } | ||||
| bool newState = false; | |||||
| // Process channels | // Process channels | ||||
| for (int c = 0; c < channels; c++) { | for (int c = 0; c < channels; c++) { | ||||
| float in = inputs[IN_INPUT].getVoltage(c); | float in = inputs[IN_INPUT].getVoltage(c); | ||||
| if (state[c]) { | |||||
| if (stateBits & (1 << c)) { | |||||
| // HIGH to LOW | // HIGH to LOW | ||||
| if (in <= 0.1f) { | if (in <= 0.1f) { | ||||
| state[c] = false; | |||||
| // states[c] = false | |||||
| stateBits &= ~(1 << c); | |||||
| fallPulse[c].trigger(1e-3f); | fallPulse[c].trigger(1e-3f); | ||||
| newState = true; | |||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| // LOW to HIGH | // LOW to HIGH | ||||
| if (in >= 2.f) { | if (in >= 2.f) { | ||||
| state[c] = true; | |||||
| // states[c] = true | |||||
| stateBits |= (1 << c); | |||||
| risePulse[c].trigger(1e-3f); | risePulse[c].trigger(1e-3f); | ||||
| // Flip flop | // Flip flop | ||||
| flop[c] ^= true; | flop[c] ^= true; | ||||
| // Gate | // Gate | ||||
| gateTime[c] = 0.f; | gateTime[c] = 0.f; | ||||
| newState = true; | |||||
| } | } | ||||
| } | } | ||||
| @@ -118,7 +136,6 @@ struct Gates : Module { | |||||
| gateTime[c] += args.sampleTime; | gateTime[c] += args.sampleTime; | ||||
| if (reset || gateTime[c] >= gateLength) { | if (reset || gateTime[c] >= gateLength) { | ||||
| gateTime[c] = INFINITY; | 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); | outputs[GATE_OUTPUT].setVoltage(gate ? 10.f : 0.f, c); | ||||
| anyGate = anyGate || gate; | 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[RISE_OUTPUT].setChannels(channels); | ||||
| outputs[FALL_OUTPUT].setChannels(channels); | outputs[FALL_OUTPUT].setChannels(channels); | ||||
| outputs[FLIP_OUTPUT].setChannels(channels); | outputs[FLIP_OUTPUT].setChannels(channels); | ||||
| outputs[FLOP_OUTPUT].setChannels(channels); | outputs[FLOP_OUTPUT].setChannels(channels); | ||||
| outputs[GATE_OUTPUT].setChannels(channels); | outputs[GATE_OUTPUT].setChannels(channels); | ||||
| outputs[END_OUTPUT].setChannels(channels); | |||||
| outputs[DELAY_OUTPUT].setChannels(channels); | |||||
| if (lightDivider.process()) { | if (lightDivider.process()) { | ||||
| float lightTime = args.sampleTime * lightDivider.getDivision(); | float lightTime = args.sampleTime * lightDivider.getDivision(); | ||||
| @@ -166,8 +206,8 @@ struct Gates : Module { | |||||
| lights[FLOP_LIGHT + 1].setBrightnessSmooth(anyFlop && channels > 1, lightTime); | lights[FLOP_LIGHT + 1].setBrightnessSmooth(anyFlop && channels > 1, lightTime); | ||||
| lights[GATE_LIGHT + 0].setBrightnessSmooth(anyGate && channels <= 1, lightTime); | lights[GATE_LIGHT + 0].setBrightnessSmooth(anyGate && channels <= 1, lightTime); | ||||
| lights[GATE_LIGHT + 1].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(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(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(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(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(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(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(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(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)); | |||||
| } | } | ||||
| }; | }; | ||||