|
|
@@ -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)); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|