commit 094d0f37db86c74c0b1f8632649808377c634bda
parent 4662efaf2ade3e85e763ac24e2fe4e951acdb35e
Author: Matt Demanett <matt@demanett.net>
Date: Thu, 13 Aug 2020 02:04:16 -0400
RGATE: replace "initial pulse" with initial clock period, which simplifies things; replace RUN port with RESET; add output range options; document in README. #129
Diffstat:
10 files changed, 166 insertions(+), 188 deletions(-)
diff --git a/README.md b/README.md
@@ -372,6 +372,33 @@ A trigger-to-gate utility, with gate duration up to 10 seconds, and an optional
_Polyphony:_ <a href="#polyphony">Polyphonic</a>, with channels defined by the TRIG input. Pressing the trigger button will trigger all channels.
+#### <a name="rgate"></a> RGATE
+
+RGATE is a "clock-relative" gate generator, which outputs gates that have a length that is a ratio of the period of the incoming clock. It can also be used as a clock divider/multiplier.
+
+The LENGTH control sets the length of the output gate relative to the incoming clock period (time between two clock pulses). The length may be varied from a minimum of 1ms to the full clock period, at maximum. With the length at maximum, the output gate will simply stay high. A LEN unipolor (0-10V) CV may be supplied, which if in use is attenuated by the LENGTH knob.
+
+CLK DIV and CLK MUL alter the frequency and length of gate outputs. Increasing CLK DIV will set the number of clock pulses the clock period extends over, while CLK MUL will set how many gates will be emitted in that period. For example, setting CLK DIV to 3 and CLK MUL to 2 yield an output of 2 gates for every three incoming clock pulses, with a maximum gate length of half of 3 times a single clock period.
+
+DIV and MUL are CV inputs for CLK DIV and CLK MUL respectively, each expecting a unipolar (0-10V) CV, and each attenuated by its corresponding knob. For example, if CLK DIV is set to 4, then a 0-2.5V input at DIV will select a division of 1, an input of 2.5-5V will select a division of 2, and so on.
+
+*About determining the clock period:* the module continuously updates its measurement of the clock period on each clock pulse received, setting it to the time since the last clock was received. With a steady and continuous incoming clock, this works just fine; otherwise there are some issues to consider:
+ 1. When the module loads, it has seen no clocks yet, and needs to see two to establish the clock period. To work around this, there is a default clock period which applies only after the first clock is received and until the second is. This is configurable on the context menu, defaulting to 500ms (or 120 BPM).
+ 1. If the clock stops, the module plays out the current (divided, multiplied) clock period, and then output will stop. When the clock starts again, RGate will have measured a very long clock period, and will output a long gate. The RESET function described below can help with this.
+ 1. An irregular or varying clock may cause odd or unpredictable behaviors.
+
+The RESET port allows resetting the state of RGate, with two modes, configurable on the context (right-click) menu:
+ - HARD, the default, resets the calculation of the clock period, and the internal counter that drives the clock divider. On receipt of the next clock after a hard reset, the default clock period applies.
+ - SOFT resets only the clock divider.
+
+The output range of the module may be set on the context menu; it defaults to unipolar 0-10V. It may be set to 0-5V, +/-10V or +/-5V.
+
+The module is usable as a general clock divider/multiplier; in this case it's advisable to set LENGTH to the minimum, as clocks usually output short trigger pulses.
+
+The module can also be used to generate pulse waves from incoming audio, where the output pitch is some ratio of the input, according to CLK DIV and CLK MUL. For example, with CLK DIV set to 2, and CLK MUL to 1, and with a square wave input, the output will be a pulse wave tracking an octave below the input. LENGTH becomes a pulse-width control in this case. CLK MUL will multiply the increasing frequency to a point; at some point the output frequency would be faster than the internals of RGate update, and nothing happens. If using the module this way, it makes sense to set the output to bipolar.
+
+_Polyphony:_ <a href="#polyphony">Polyphonic</a>, with channels defined by the CLOCK input, by default, or the LENGTH input, if so set on the context menu.
+
#### <a name="edge"></a> EDGE
A trigger-to-gate utility, comparator and rising/falling edge detector. RISE and FALL set voltage levels: when the input goes above RISE, the module switches to "high" state and outputs a voltage at the HIGH output. HOLD sets a minimum time that the module stays in the high state; this can be used to avoid jitter on the output if using high-frequency inputs. 1ms trigger pulses are output at RISE and FALL on the corresponding changes (note that if you switch the module state at audio rates, these will essentially always be high).
diff --git a/res-pp/RGate-dark-pp.svg b/res-pp/RGate-dark-pp.svg
@@ -236,13 +236,13 @@ polyline.knob-tick {
</g>
</svg></g>
<text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(52.5 35)">DIV</text>
- <g transform="translate(10.5 40)"><svg id="RUN_INPUT">
+ <g transform="translate(10.5 40)"><svg id="RESET_INPUT">
<g transform="translate(12 12)">
<circle cx="0" cy="0" r="5" stroke-width="1" stroke="#0f0" fill="#0f0"/>
<circle cx="0" cy="0" r="10.5" stroke-width="3" stroke="#0f0" fill="none"/>
</g>
</svg></g>
- <text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(22.5 72)">RUN</text>
+ <text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(22.5 72)">RESET</text>
<g transform="translate(40.5 40)"><svg id="CLOCK_MULTIPLE_INPUT">
<g transform="translate(12 12)">
<circle cx="0" cy="0" r="5" stroke-width="1" stroke="#0f0" fill="#0f0"/>
diff --git a/res-pp/RGate-lowcontrast-pp.svg b/res-pp/RGate-lowcontrast-pp.svg
@@ -236,13 +236,13 @@ polyline.knob-tick {
</g>
</svg></g>
<text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(52.5 35)">DIV</text>
- <g transform="translate(10.5 40)"><svg id="RUN_INPUT">
+ <g transform="translate(10.5 40)"><svg id="RESET_INPUT">
<g transform="translate(12 12)">
<circle cx="0" cy="0" r="5" stroke-width="1" stroke="#0f0" fill="#0f0"/>
<circle cx="0" cy="0" r="10.5" stroke-width="3" stroke="#0f0" fill="none"/>
</g>
</svg></g>
- <text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(22.5 72)">RUN</text>
+ <text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(22.5 72)">RESET</text>
<g transform="translate(40.5 40)"><svg id="CLOCK_MULTIPLE_INPUT">
<g transform="translate(12 12)">
<circle cx="0" cy="0" r="5" stroke-width="1" stroke="#0f0" fill="#0f0"/>
diff --git a/res-pp/RGate-pp.svg b/res-pp/RGate-pp.svg
@@ -172,13 +172,13 @@ polyline.knob-tick {
</g>
</svg></g>
<text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(52.5 35)">DIV</text>
- <g transform="translate(10.5 40)"><svg id="RUN_INPUT">
+ <g transform="translate(10.5 40)"><svg id="RESET_INPUT">
<g transform="translate(12 12)">
<circle cx="0" cy="0" r="5" stroke-width="1" stroke="#0f0" fill="#0f0"/>
<circle cx="0" cy="0" r="10.5" stroke-width="3" stroke="#0f0" fill="none"/>
</g>
</svg></g>
- <text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(22.5 72)">RUN</text>
+ <text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(22.5 72)">RESET</text>
<g transform="translate(40.5 40)"><svg id="CLOCK_MULTIPLE_INPUT">
<g transform="translate(12 12)">
<circle cx="0" cy="0" r="5" stroke-width="1" stroke="#0f0" fill="#0f0"/>
diff --git a/res-src/RGate-src.svg b/res-src/RGate-src.svg
@@ -60,8 +60,8 @@
<text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(22.5 35)">LEN</text>
<def id="CLOCK_DIVIDE_INPUT" xlink:href="#input" transform="translate(40.5 3)"/>
<text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(52.5 35)">DIV</text>
- <def id="RUN_INPUT" xlink:href="#input" transform="translate(10.5 40)"/>
- <text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(22.5 72)">RUN</text>
+ <def id="RESET_INPUT" xlink:href="#input" transform="translate(10.5 40)"/>
+ <text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(22.5 72)">RESET</text>
<def id="CLOCK_MULTIPLE_INPUT" xlink:href="#input" transform="translate(40.5 40)"/>
<text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(52.5 72)">MUL</text>
<def id="CLOCK_INPUT" xlink:href="#input" transform="translate(10.5 77)"/>
diff --git a/res/RGate-dark.svg b/res/RGate-dark.svg
Binary files differ.
diff --git a/res/RGate-lowcontrast.svg b/res/RGate-lowcontrast.svg
Binary files differ.
diff --git a/res/RGate.svg b/res/RGate.svg
Binary files differ.
diff --git a/src/RGate.cpp b/src/RGate.cpp
@@ -1,11 +1,11 @@
#include "RGate.hpp"
-#define RUN_MODE "run_mode"
-#define INITIAL_PULSE_SECONDS "initial_pulse_seconds"
+#define RESET_MODE "reset_mode"
+#define INITIAL_CLOCK_SECONDS "initial_clock_seconds"
#define POLY_INPUT "poly_input"
-void RGate::Engine::reset(bool triggers, bool hard) {
+void RGate::Engine::reset(bool triggers, bool hard, float initialClock) {
if (triggers) {
clockTrigger.reset();
runTrigger.reset();
@@ -13,7 +13,7 @@ void RGate::Engine::reset(bool triggers, bool hard) {
}
if (hard) {
secondsSinceLastClock = -1.0f;
- clockSeconds = -1.0f;
+ clockSeconds = initialClock;
dividedSeconds = -1.0f;
multipliedSeconds = -1.0f;
gateSeconds = 0.0f;
@@ -24,7 +24,7 @@ void RGate::Engine::reset(bool triggers, bool hard) {
void RGate::reset() {
for (int c = 0; c < _channels; ++c) {
- _engines[c]->reset();
+ _engines[c]->reset(true, true, _initialClockPeriod);
}
}
@@ -33,37 +33,31 @@ void RGate::sampleRateChange() {
}
json_t* RGate::toJson(json_t* root) {
- json_object_set_new(root, RUN_MODE, json_integer(_runMode));
- json_object_set_new(root, INITIAL_PULSE_SECONDS, json_real(_initialPulseSeconds));
+ json_object_set_new(root, RESET_MODE, json_integer(_resetMode));
+ json_object_set_new(root, INITIAL_CLOCK_SECONDS, json_real(_initialClockPeriod));
json_object_set_new(root, POLY_INPUT, json_integer(_polyInputID));
return root;
}
void RGate::fromJson(json_t* root) {
- json_t* rm = json_object_get(root, RUN_MODE);
+ json_t* rm = json_object_get(root, RESET_MODE);
if (rm) {
- RunMode m = (RunMode)json_integer_value(rm);
+ ResetMode m = (ResetMode)json_integer_value(rm);
switch (m) {
- case GATED_RUN_RUNMODE:
- case GATED_RUN_RESET_SOFT_RUNMODE:
- case GATED_RUN_RESET_HARD_RUNMODE:
- case TRIGGERED_RUN_RUNMODE:
- case TRIGGERED_RUN_RESET_SOFT_RUNMODE:
- case TRIGGERED_RUN_RESET_HARD_RUNMODE:
- case RESET_SOFT_RUNMODE:
- case RESET_HARD_RUNMODE: {
- _runMode = m;
+ case HARD_RESETMODE:
+ case SOFT_RESETMODE: {
+ _resetMode = m;
break;
}
default: {
- _runMode = defaultRunMode;
+ _resetMode = defaultResetMode;
}
}
}
- json_t* ips = json_object_get(root, INITIAL_PULSE_SECONDS);
- if (ips) {
- _initialPulseSeconds = std::max(0.0f, (float)json_real_value(ips));
+ json_t* ics = json_object_get(root, INITIAL_CLOCK_SECONDS);
+ if (ics) {
+ _initialClockPeriod = std::max(0.0f, (float)json_real_value(ics));
}
json_t* p = json_object_get(root, POLY_INPUT);
@@ -82,7 +76,7 @@ int RGate::channels() {
void RGate::addChannel(int c) {
_engines[c] = new Engine();
- _engines[c]->reset();
+ _engines[c]->reset(true, true, _initialClockPeriod);
}
void RGate::removeChannel(int c) {
@@ -120,90 +114,20 @@ void RGate::modulateChannel(int c) {
void RGate::processChannel(const ProcessArgs& args, int c) {
Engine& e = *_engines[c];
- bool runTriggered = e.runTrigger.process(inputs[RUN_INPUT].getPolyVoltage(c));
- switch (_runMode) {
- case GATED_RUN_RUNMODE: {
- _running = e.runTrigger.isHigh() ? YES_RUNNING : NO_RUNNING;
- break;
- }
- case GATED_RUN_RESET_SOFT_RUNMODE: {
- _running = e.runTrigger.isHigh() ? YES_RUNNING : NO_RUNNING;
- if (runTriggered) {
- e.reset(false, false);
- }
- break;
- }
- case GATED_RUN_RESET_HARD_RUNMODE: {
- _running = e.runTrigger.isHigh() ? YES_RUNNING : NO_RUNNING;
- if (runTriggered) {
- e.reset(false, true);
- }
- break;
- }
- case TRIGGERED_RUN_RUNMODE: {
- if (runTriggered) {
- switch (_running) {
- case UNKNOWN_RUNNING:
- case NO_RUNNING: {
- _running = YES_RUNNING;
- break;
- }
- case YES_RUNNING: {
- _running = NO_RUNNING;
- break;
- }
- }
- }
- break;
- }
- case TRIGGERED_RUN_RESET_SOFT_RUNMODE: {
- if (runTriggered) {
- switch (_running) {
- case UNKNOWN_RUNNING:
- case NO_RUNNING: {
- _running = YES_RUNNING;
- break;
- }
- case YES_RUNNING: {
- _running = NO_RUNNING;
- break;
- }
- }
- e.reset(false, false);
- }
- break;
- }
- case TRIGGERED_RUN_RESET_HARD_RUNMODE: {
- if (runTriggered) {
- switch (_running) {
- case UNKNOWN_RUNNING:
- case NO_RUNNING: {
- _running = YES_RUNNING;
- break;
- }
- case YES_RUNNING: {
- _running = NO_RUNNING;
- break;
- }
- }
- e.reset(false, true);
- }
- break;
- }
- case RESET_SOFT_RUNMODE: {
- if (runTriggered) {
- e.reset(false, false);
+ if (e.runTrigger.process(inputs[RESET_INPUT].getPolyVoltage(c))) {
+ switch (_resetMode) {
+ case HARD_RESETMODE: {
+ e.reset(false, true, _initialClockPeriod);
+ break;
}
- break;
- }
- case RESET_HARD_RUNMODE: {
- if (runTriggered) {
- e.reset(false, true);
+ case SOFT_RESETMODE: {
+ e.reset(false, false, _initialClockPeriod);
+ break;
}
- break;
}
}
+ float out = -1.0f;
bool clock = false;
if (inputs[CLOCK_INPUT].isConnected()) {
clock = e.clockTrigger.process(inputs[CLOCK_INPUT].getPolyVoltage(c));
@@ -212,56 +136,97 @@ void RGate::processChannel(const ProcessArgs& args, int c) {
e.clockSeconds = e.secondsSinceLastClock;
}
e.secondsSinceLastClock = 0.0f;
-
- if (_running == UNKNOWN_RUNNING) {
- _running = YES_RUNNING;
- }
}
- e.secondsSinceLastClock += _sampleTime;
- }
- float out = 0.0f;
- if (e.clockSeconds > 0.0f) {
- e.dividedSeconds = e.clockSeconds * (float)e.division;
- e.multipliedSeconds = e.dividedSeconds / (float)e.multiplication;
- e.gateSeconds = std::max(0.001f, e.multipliedSeconds * e.gatePercentage);
-
- if (clock) {
- if (e.dividerCount < 1) {
- e.dividedProgressSeconds = 0.0f;
+ if (e.secondsSinceLastClock >= 0.0f) {
+ e.secondsSinceLastClock += _sampleTime;
+ e.dividedSeconds = e.clockSeconds * (float)e.division;
+ e.multipliedSeconds = e.dividedSeconds / (float)e.multiplication;
+ e.gateSeconds = std::max(0.001f, e.multipliedSeconds * e.gatePercentage);
+ if (clock) {
+ if (e.dividerCount < 1) {
+ e.dividedProgressSeconds = 0.0f;
+ }
+ else {
+ e.dividedProgressSeconds += _sampleTime;
+ }
+ ++e.dividerCount;
+ if (e.dividerCount >= e.division) {
+ e.dividerCount = 0;
+ }
}
else {
e.dividedProgressSeconds += _sampleTime;
}
- ++e.dividerCount;
- if (e.dividerCount >= e.division) {
- e.dividerCount = 0;
- }
- }
- else {
- e.dividedProgressSeconds += _sampleTime;
- }
- float multipliedProgressSeconds = e.dividedProgressSeconds / e.multipliedSeconds;
- multipliedProgressSeconds -= (float)(int)multipliedProgressSeconds;
- multipliedProgressSeconds *= e.multipliedSeconds;
- out = (float)(multipliedProgressSeconds <= e.gateSeconds);
- }
- else {
- if (clock) {
- ++e.dividerCount;
- if (_initialPulseSeconds > 0.0f) {
- e.initialGatePulseGen.trigger(_initialPulseSeconds);
+ if (e.dividedProgressSeconds < e.dividedSeconds) {
+ float multipliedProgressSeconds = e.dividedProgressSeconds / e.multipliedSeconds;
+ multipliedProgressSeconds -= (float)(int)multipliedProgressSeconds;
+ multipliedProgressSeconds *= e.multipliedSeconds;
+ out += 2.0f * (float)(multipliedProgressSeconds <= e.gateSeconds);
}
}
- e.dividedProgressSeconds += _sampleTime;
- out = (float)e.initialGatePulseGen.process(_sampleTime);
}
+ out += _rangeOffset;
+ out *= _rangeScale;
outputs[GATE_OUTPUT].setChannels(_channels);
- outputs[GATE_OUTPUT].setVoltage(out * (float)(_running == YES_RUNNING) * 10.0f, c);
+ outputs[GATE_OUTPUT].setVoltage(out, c);
}
+struct IPQuantity : Quantity {
+ RGate* _module;
+
+ IPQuantity(RGate* m) : _module(m) {}
+
+ void setValue(float value) override {
+ value = clamp(value, getMinValue(), getMaxValue());
+ if (_module) {
+ _module->_initialClockPeriod = value;
+ }
+ }
+
+ float getValue() override {
+ if (_module) {
+ return _module->_initialClockPeriod;
+ }
+ return RGate::defaultInitialClockPeriod;
+ }
+
+ float getMinValue() override { return 0.0f; }
+ float getMaxValue() override { return 1.0; }
+ float getDefaultValue() override { return RGate::defaultInitialClockPeriod; }
+ float getDisplayValue() override { return getValue() * 1000.0f; }
+ void setDisplayValue(float displayValue) override { setValue(displayValue / 1000.0f); }
+ std::string getLabel() override { return "Initial clock"; }
+ std::string getUnit() override { return "ms"; }
+};
+
+struct IPSlider : ui::Slider {
+ IPSlider(RGate* module) {
+ quantity = new IPQuantity(module);
+ box.size.x = 200.0f;
+ }
+ virtual ~IPSlider() {
+ delete quantity;
+ }
+};
+
+struct IPMenuItem : MenuItem {
+ RGate* _module;
+
+ IPMenuItem(RGate* m) : _module(m) {
+ this->text = "Initial clock";
+ this->rightText = "▸";
+ }
+
+ Menu* createChildMenu() override {
+ Menu* menu = new Menu;
+ menu->addChild(new IPSlider(_module));
+ return menu;
+ }
+};
+
struct RGateWidget : BGModuleWidget {
static constexpr int hp = 5;
@@ -278,7 +243,7 @@ struct RGateWidget : BGModuleWidget {
auto lengthInputPosition = Vec(10.5, 251.0);
auto clockDivideInputPosition = Vec(40.5, 251.0);
- auto runInputPosition = Vec(10.5, 288.0);
+ auto resetInputPosition = Vec(10.5, 288.0);
auto clockMultipleInputPosition = Vec(40.5, 288.0);
auto clockInputPosition = Vec(10.5, 325.0);
@@ -291,7 +256,7 @@ struct RGateWidget : BGModuleWidget {
addInput(createInput<Port24>(lengthInputPosition, module, RGate::LENGTH_INPUT));
addInput(createInput<Port24>(clockDivideInputPosition, module, RGate::CLOCK_DIVIDE_INPUT));
- addInput(createInput<Port24>(runInputPosition, module, RGate::RUN_INPUT));
+ addInput(createInput<Port24>(resetInputPosition, module, RGate::RESET_INPUT));
addInput(createInput<Port24>(clockMultipleInputPosition, module, RGate::CLOCK_MULTIPLE_INPUT));
addInput(createInput<Port24>(clockInputPosition, module, RGate::CLOCK_INPUT));
@@ -307,26 +272,19 @@ struct RGateWidget : BGModuleWidget {
p->addItem(OptionMenuItem("LEN input", [m]() { return m->_polyInputID == RGate::LENGTH_INPUT; }, [m]() { m->_polyInputID = RGate::LENGTH_INPUT; }));
OptionsMenuItem::addToMenu(p, menu);
- OptionsMenuItem* r = new OptionsMenuItem("RUN port");
- r->addItem(OptionMenuItem("Toggle run on trigger, hard reset on start", [m]() { return m->_runMode == RGate::TRIGGERED_RUN_RESET_HARD_RUNMODE; }, [m]() { m->_runMode = RGate::TRIGGERED_RUN_RESET_HARD_RUNMODE; }));
- r->addItem(OptionMenuItem("Toggle run on trigger, soft reset on start", [m]() { return m->_runMode == RGate::TRIGGERED_RUN_RESET_SOFT_RUNMODE; }, [m]() { m->_runMode = RGate::TRIGGERED_RUN_RESET_SOFT_RUNMODE; }));
- r->addItem(OptionMenuItem("Toggle run on trigger", [m]() { return m->_runMode == RGate::TRIGGERED_RUN_RUNMODE; }, [m]() { m->_runMode = RGate::TRIGGERED_RUN_RUNMODE; }));
- r->addItem(OptionMenuItem("Run when gate high, hard reset on rising edge", [m]() { return m->_runMode == RGate::GATED_RUN_RESET_HARD_RUNMODE; }, [m]() { m->_runMode = RGate::GATED_RUN_RESET_HARD_RUNMODE; }));
- r->addItem(OptionMenuItem("Run when gate high, soft reset on rising edge", [m]() { return m->_runMode == RGate::GATED_RUN_RESET_SOFT_RUNMODE; }, [m]() { m->_runMode = RGate::GATED_RUN_RESET_SOFT_RUNMODE; }));
- r->addItem(OptionMenuItem("Run when gate high", [m]() { return m->_runMode == RGate::GATED_RUN_RUNMODE; }, [m]() { m->_runMode = RGate::GATED_RUN_RUNMODE; }));
- r->addItem(OptionMenuItem("Hard reset on trigger", [m]() { return m->_runMode == RGate::RESET_HARD_RUNMODE; }, [m]() { m->_runMode = RGate::RESET_HARD_RUNMODE; }));
- r->addItem(OptionMenuItem("Soft reset on trigger", [m]() { return m->_runMode == RGate::RESET_SOFT_RUNMODE; }, [m]() { m->_runMode = RGate::RESET_SOFT_RUNMODE; }));
+ OptionsMenuItem* r = new OptionsMenuItem("RESET mode");
+ r->addItem(OptionMenuItem("Hard: reset clock period and divider", [m]() { return m->_resetMode == RGate::HARD_RESETMODE; }, [m]() { m->_resetMode = RGate::HARD_RESETMODE; }));
+ r->addItem(OptionMenuItem("Soft: reseet clock divider", [m]() { return m->_resetMode == RGate::SOFT_RESETMODE; }, [m]() { m->_resetMode = RGate::SOFT_RESETMODE; }));
OptionsMenuItem::addToMenu(r, menu);
- OptionsMenuItem* i = new OptionsMenuItem("Initial pulse duration");
- i->addItem(OptionMenuItem("No pulse", [m]() { return m->_initialPulseSeconds == 0.0f; }, [m]() { m->_initialPulseSeconds = 0.0f; }));
- i->addItem(OptionMenuItem("1ms", [m]() { return m->_initialPulseSeconds == 0.001f; }, [m]() { m->_initialPulseSeconds = 0.001f; }));
- i->addItem(OptionMenuItem("100ms", [m]() { return m->_initialPulseSeconds == 0.1f; }, [m]() { m->_initialPulseSeconds = 0.1f; }));
- i->addItem(OptionMenuItem("200ms", [m]() { return m->_initialPulseSeconds == 0.2f; }, [m]() { m->_initialPulseSeconds = 0.2f; }));
- i->addItem(OptionMenuItem("300ms", [m]() { return m->_initialPulseSeconds == 0.3f; }, [m]() { m->_initialPulseSeconds = 0.3f; }));
- i->addItem(OptionMenuItem("400ms", [m]() { return m->_initialPulseSeconds == 0.4f; }, [m]() { m->_initialPulseSeconds = 0.4f; }));
- i->addItem(OptionMenuItem("500ms", [m]() { return m->_initialPulseSeconds == 0.5f; }, [m]() { m->_initialPulseSeconds = 0.4f; }));
- OptionsMenuItem::addToMenu(i, menu);
+ menu->addChild(new IPMenuItem(m));
+
+ OptionsMenuItem* mi = new OptionsMenuItem("Range");
+ mi->addItem(OutputRangeOptionMenuItem(m, "0V-10V", 1.0f, 5.0f));
+ mi->addItem(OutputRangeOptionMenuItem(m, "0V-5V", 1.0f, 2.5f));
+ mi->addItem(OutputRangeOptionMenuItem(m, "+/-10V", 0.0f, 10.0f));
+ mi->addItem(OutputRangeOptionMenuItem(m, "+/-5V", 0.0f, 5.0f));
+ OptionsMenuItem::addToMenu(mi, menu);
}
};
diff --git a/src/RGate.hpp b/src/RGate.hpp
@@ -1,12 +1,13 @@
#pragma once
#include "bogaudio.hpp"
+#include "output_range.hpp"
extern Model* modelRGate;
namespace bogaudio {
-struct RGate : BGModule {
+struct RGate : OutputRangeModule<BGModule> {
enum ParamsIds {
LENGTH_PARAM,
CLOCK_DIVIDE_PARAM,
@@ -17,7 +18,7 @@ struct RGate : BGModule {
enum InputsIds {
LENGTH_INPUT,
CLOCK_DIVIDE_INPUT,
- RUN_INPUT,
+ RESET_INPUT,
CLOCK_MULTIPLE_INPUT,
CLOCK_INPUT,
NUM_INPUTS
@@ -28,23 +29,11 @@ struct RGate : BGModule {
NUM_OUTPUTS
};
- enum RunMode {
- GATED_RUN_RUNMODE,
- GATED_RUN_RESET_SOFT_RUNMODE,
- GATED_RUN_RESET_HARD_RUNMODE,
- TRIGGERED_RUN_RUNMODE,
- TRIGGERED_RUN_RESET_SOFT_RUNMODE,
- TRIGGERED_RUN_RESET_HARD_RUNMODE,
- RESET_SOFT_RUNMODE,
- RESET_HARD_RUNMODE
- };
- static constexpr RunMode defaultRunMode = TRIGGERED_RUN_RESET_HARD_RUNMODE;
-
- enum Running {
- UNKNOWN_RUNNING,
- YES_RUNNING,
- NO_RUNNING
+ enum ResetMode {
+ HARD_RESETMODE,
+ SOFT_RESETMODE
};
+ static constexpr ResetMode defaultResetMode = HARD_RESETMODE;
struct Engine {
Trigger clockTrigger;
@@ -61,14 +50,15 @@ struct RGate : BGModule {
int dividerCount = 0;
float dividedProgressSeconds = 0.0f;
- void reset(bool triggers = true, bool hard = true);
+ void reset(bool triggers, bool hard, float initialClock);
};
+ static constexpr float defaultInitialClockPeriod = 0.5f;
+
Engine* _engines[maxChannels] {};
float _sampleTime = 0.001f;
- RunMode _runMode = defaultRunMode;
- Running _running = UNKNOWN_RUNNING;
- float _initialPulseSeconds = 0.2f;
+ ResetMode _resetMode = defaultResetMode;
+ float _initialClockPeriod = defaultInitialClockPeriod;
int _polyInputID = CLOCK_INPUT;
RGate() {
@@ -76,6 +66,9 @@ struct RGate : BGModule {
configParam(LENGTH_PARAM, 0.0f, 1.0f, 0.5f, "Gate length", "%", 0.0f, 100.0f);
configParam<RoundingParamQuantity<ScaledSquaringParamQuantity<63>>>(CLOCK_DIVIDE_PARAM, 0.0f, 1.0f, 0.0f, "Clock division", "", 0.0f, 1.0f, 1.0f);
configParam<RoundingParamQuantity<ScaledSquaringParamQuantity<63>>>(CLOCK_MULTIPLY_PARAM, 0.0f, 1.0f, 0.0f, "Clock multiplication", "", 0.0f, 1.0f, 1.0f);
+
+ _rangeOffset = 1.0f;
+ _rangeScale = 5.0f;
}
void reset() override;