metronome/docs/playback-flow-test.md
Me Here 8bba218f67 Editor controls for playback flow + close web-side divergences
- docs/playback-flow-test.md: on-device verification checklist for the runtime
  (stop / rep / next / relative-goto / boundary / manual-override cases).
- editor.html + editor-beta.html: graphical "At end" control (loop / next / stop /
  goto ±N) plus a rep-count input in the arrangement panel, wired through
  state.rep/state.end -> currentSetup/currentPatch. Authoring is no longer
  text-field-only.
- src/engine.js: patchToSetup now clamps tempo to [5,300] and defaults to a beep:4
  lane when no lanes are given, matching the firmware. The editors keep their
  "no lanes" hint by checking the raw input for a ':' token instead of parsed lanes.
- fixtures: tempo-clamp-high + empty-defaults-to-beep now pass on both engines.

Suite: 41 pass / 1 known (only the intentional vol/cd host boundary remains).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 16:11:43 -05:00

3.1 KiB
Raw Permalink Blame History

Per-track playback flow — on-device test checklist

The parsing/serialization and the _end_plan/_goto_target decision logic are covered by tests/run.mjs and unit tests. What still needs a real device (PM_K-1 / PM_X-1) is the runtime: that the gapless seam fires stop / advance / relative-goto at the right bar.

Setup

  1. Flash the new firmware: copy dist/app.mpy (PM_K) or dist/explorer-app.mpy (PM_X) onto the device as /app.mpy (or use the editor's one-click push — it serves this build).
  2. Author the test tracks in the web editor: paste each program string into the program-string field, Save it as a set-list item, then Save to device. (Multi-item cases: create the items in order in one set-list.)
  3. On the device, select the set-list and press play (Button A).

b<n> sets the cycle length in bars; a cycle = b<bars>, else one master bar. The action fires after rep × cycle bars (rep defaults to 1).

Cases

# Track(s) Expect
1 t120;kick:4 Loops forever. Joystick-right advances manually.
2 t120;b2;kick:4;end=stop Plays 2 bars, then stops by itself (LED → green, transport stops).
3 t120;b2;kick:4;rep=3;end=stop Plays 6 bars (3×2), then stops.
4 A=t120;b2;kick:4;end=next B=t100;b2;snare:4 A plays 2 bars then gaplessly advances to B (no gap, no count-in, tempo jumps to 100).
5 A=t120;b2;kick:4;rep=2;end=next B=t100;b2;snare:4 A plays 4 bars (2×2) then advances to B.
6 1=t120;b2;kick:4 2=t120;b2;snare:4;end=next 3=t120;b2;clap:4;end=-2 3 jumps back 2 → track 1 after its cycle (a looping verse→chorus section).
7 1=t120;b2;kick:4;end=-2 (first item) end=-2 before the start clamps to track 1 → it just loops itself.
8 last item …;end=next Advancing past the last item wraps to the first (set-list loops).
9 any of the above, mid-flow Manual joystick-right / footswitch always advances immediately, regardless of end=.
10 global Continue ON + an item t120;b2;kick:4;end=stop The item still stops — explicit end= overrides the global Continue default.
11 global Continue ON + t120;b2;kick:4 (no end=) Advances after 2 bars — legacy behavior preserved (Continue = default end=next).

What to watch for

  • Seam quality (cases 46): the swap should be click-on-the-beat with no audible gap, dropout, or double-trigger at the boundary. This is the highest-risk part of the change.
  • Stop cleanliness (cases 23): no extra click after the stop; speaker goes quiet; the play session is logged.
  • Ramp/trainer interaction: a track with rmp…/tr… and an end= should ramp/gap normally and still fire the end-action at rep × cycle.
  • Memory: relative-goto and advance both go through _prepare_next (which gc.collect()s before parsing). Watch for any MemoryError fallback (it leaves the track looping instead).

Report any case that misbehaves with its number and what happened.