#pragma once #include namespace rack { namespace dsp { /** Detects when a boolean changes from false to true */ struct BooleanTrigger { enum State : uint8_t { LOW, HIGH, UNINITIALIZED }; union { State s = UNINITIALIZED; /** Deprecated */ bool state; }; void reset() { s = UNINITIALIZED; } /** Returns whether the input changed from false to true. */ bool process(bool in) { bool triggered = (s == LOW) && in; s = in ? HIGH : LOW; return triggered; } enum Event { NONE = 0, TRIGGERED = 1, UNTRIGGERED = -1 }; /** Returns TRIGGERED if the input changed from false to true, and UNTRIGGERED if the input changed from true to false. */ Event processEvent(bool in) { Event event = NONE; if (s == LOW && in) { event = TRIGGERED; } else if (s == HIGH && !in) { event = UNTRIGGERED; } s = in ? HIGH : LOW; return event; } bool isHigh() { return s == HIGH; } }; /** Turns HIGH when value reaches a threshold (default 0.f), turns LOW when value reaches a threshold (default 1.f). */ template struct TSchmittTrigger { T state; TSchmittTrigger() { reset(); } void reset() { state = T::mask(); } T process(T in, T lowThreshold = 0.f, T highThreshold = 1.f) { T on = (in >= highThreshold); T off = (in <= lowThreshold); T triggered = ~state & on; state = on | (state & ~off); return triggered; } T isHigh() { return state; } }; template <> struct TSchmittTrigger { enum State : uint8_t { LOW, HIGH, UNINITIALIZED }; union { State s = UNINITIALIZED; /** Deprecated. Backward compatible API */ bool state; }; void reset() { s = UNINITIALIZED; } /** Updates the state of the Schmitt Trigger given a value. Returns true if triggered, i.e. the value increases from 0 to 1. If different trigger thresholds are needed, use process(in, 0.1f, 2.f) for example. */ bool process(float in, float lowThreshold = 0.f, float highThreshold = 1.f) { if (s == LOW && in >= highThreshold) { // LOW to HIGH s = HIGH; return true; } else if (s == HIGH && in <= lowThreshold) { // HIGH to LOW s = LOW; } else if (s == UNINITIALIZED && in >= highThreshold) { // UNINITIALIZED to HIGH s = HIGH; } else if (s == UNINITIALIZED && in <= lowThreshold) { // UNINITIALIZED to LOW s = LOW; } return false; } enum Event { NONE = 0, TRIGGERED = 1, UNTRIGGERED = -1 }; /** Returns TRIGGERED if the input reached highThreshold, and UNTRIGGERED if the input reached lowThreshold. */ Event processEvent(float in, float lowThreshold = 0.f, float highThreshold = 1.f) { Event event = NONE; if (s == LOW && in >= highThreshold) { // LOW to HIGH s = HIGH; event = TRIGGERED; } else if (s == HIGH && in <= lowThreshold) { // HIGH to LOW s = LOW; event = UNTRIGGERED; } else if (s == UNINITIALIZED && in >= highThreshold) { // UNINITIALIZED to HIGH s = HIGH; } else if (s == UNINITIALIZED && in <= lowThreshold) { // UNINITIALIZED to LOW s = LOW; } return event; } bool isHigh() { return s == HIGH; } }; typedef TSchmittTrigger<> SchmittTrigger; /** When triggered, holds a high value for a specified time before going low again */ struct PulseGenerator { float remaining = 0.f; /** Immediately disables the pulse */ void reset() { remaining = 0.f; } /** Advances the state by `deltaTime`. Returns whether the pulse is in the HIGH state. */ bool process(float deltaTime) { if (remaining > 0.f) { remaining -= deltaTime; return true; } return false; } /** Begins a trigger with the given `duration`. */ void trigger(float duration = 1e-3f) { // Keep the previous pulse if the existing pulse will be held longer than the currently requested one. if (duration > remaining) { remaining = duration; } } }; /** Accumulates a timer when process() is called. */ template struct TTimer { T time = 0.f; void reset() { time = 0.f; } /** Returns the time since last reset or initialization. */ T process(T deltaTime) { time += deltaTime; return time; } T getTime() { return time; } }; typedef TTimer<> Timer; /** Counts calls to process(), returning true every `division` calls. Example: if (divider.process()) { // Runs every `division` calls } */ struct ClockDivider { uint32_t clock = 0; uint32_t division = 1; void reset() { clock = 0; } void setDivision(uint32_t division) { this->division = division; } uint32_t getDivision() { return division; } uint32_t getClock() { return clock; } /** Returns true when the clock reaches `division` and resets. */ bool process() { clock++; if (clock >= division) { clock = 0; return true; } return false; } }; } // namespace dsp } // namespace rack