From b79d04b666f7cf18fee57570e198c57cadd4c22d Mon Sep 17 00:00:00 2001 From: Bryan Head Date: Wed, 4 Sep 2024 09:07:16 -0700 Subject: [PATCH] Ebb&LFO: Make clocked mode work with reset and oneshot --- software/src/applets/EbbAndLfo.h | 23 +++++++++++++++-------- software/src/util/util_phase_extractor.h | 23 ++++++++++++++++++----- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/software/src/applets/EbbAndLfo.h b/software/src/applets/EbbAndLfo.h index 9ec699fb..09b31193 100644 --- a/software/src/applets/EbbAndLfo.h +++ b/software/src/applets/EbbAndLfo.h @@ -28,7 +28,8 @@ class EbbAndLfo : public HemisphereApplet { if (knob_accel > (1 << 8)) knob_accel--; - if (Clock(1)) { // reset/retrigger + bool reset = Clock(1); + if (reset) { // reset/retrigger phase = 0; oneshot_active = true; } @@ -36,7 +37,11 @@ class EbbAndLfo : public HemisphereApplet { int freq_div_mul = ratio; pitch_mod = pitch; bool got_clock = Clock(0); - oneshot_active |= got_clock; + + // In clocked mode, Clock(0) is just used to set rate. In unclocked, it will + // start a oneshot but not restart it. + if (!clocked) + oneshot_active |= got_clock; // handle CV inputs slope_mod = slope * (65535 / 127); @@ -74,16 +79,17 @@ class EbbAndLfo : public HemisphereApplet { // TODO: Make oneshot and clocked mode more compatible so you can trigger a // shot with one trig and have it synced to clock on the other trig. - if (oneshot_mode && !oneshot_active) - return; - uint32_t oldphase = phase; if (clocked) { - phase = phase_extractor.Advance(got_clock, freq_div_mul); + phase = phase_extractor.Advance(got_clock, reset, freq_div_mul); } else { uint32_t phase_increment = ComputePhaseIncrement(pitch_mod); phase += phase_increment; } + if (oneshot_mode && !oneshot_active) { + phase = 0; + return; + } // check for rollover and stop for one-shot mode if (phase < oldphase && oneshot_mode) { @@ -150,10 +156,11 @@ class EbbAndLfo : public HemisphereApplet { if (i > 0) gfxLine(i - 1, last, i, next); last = next; - // gfxPixel(i, 50 - disp_sample.unipolar * 35 / 65536); } } - uint32_t p = phase / (0xffffffff / 64); + + // position is first 6 bits of phase, which gives 0 through 63. + uint32_t p = phase >> 26; gfxLine(p, 15, p, 50); const int param_y = 55; diff --git a/software/src/util/util_phase_extractor.h b/software/src/util/util_phase_extractor.h index a32193de..24fb8515 100644 --- a/software/src/util/util_phase_extractor.h +++ b/software/src/util/util_phase_extractor.h @@ -20,12 +20,21 @@ class PhaseExtractor { } // -2 => /3, -1 => /2, 0 => 1, 1 => x2, 2 => x3, etc - uint32_t Advance(bool clock, int simple_ratio) { - return Advance(clock, {static_cast(max(simple_ratio + 1, 1)), - static_cast(max(-simple_ratio + 1, 1))}); + uint32_t Advance(bool clock, bool reset, int simple_ratio) { + return Advance(clock, reset, + {static_cast(max(simple_ratio + 1, 1)), + static_cast(max(-simple_ratio + 1, 1))}); } - uint32_t Advance(bool clock, Ratio r) { + uint32_t Advance(bool clock, bool reset, Ratio r) { + if (reset) { + // TODO: should offset phase as well. This will support oneshot mode too. + clocks_received = ratio.den - 1; + // phase_offset = -phase; + phase = 0; + phase_offset = 0; + set_offset = true; + } if (clock) { next_clock_tick = predictor.Predict(ticks); ticks = 0; @@ -35,6 +44,8 @@ class PhaseExtractor { // discontinuities in the phase clocks_received = 0; ratio = r; + if (set_offset) phase_offset = phase; + set_offset = false; phase = 0; } phase_inc = 0xffffffff / (ratio.den * next_clock_tick) * ratio.num; @@ -51,7 +62,7 @@ class PhaseExtractor { else if (phase_inc < 0 && phase >= static_cast(phase_inc)) phase = 0; } - return phase; + return phase + phase_offset; } private: @@ -62,4 +73,6 @@ class PhaseExtractor { uint32_t ticks; uint32_t phase; int32_t phase_inc; + uint32_t phase_offset; + bool set_offset = false; };