Two changes that cut playback-time UI jank (measured on-device):
- Expose a deduped isPlaying StateFlow (maps the transport flow through
distinctUntilChanged) and collect THAT in the transport toolbar, so the
PLAY/PAUSE button recomposes on play↔stop instead of ~12×/second as the
playhead advances (the full transport emits every line).
- Precompute the pattern grid's fixed-width text (gutter hex, note names,
velocity/channel) into class-load lookup tables. The grid redraws on
every playhead line and scroll pixel, and the old toString/padStart
formatting allocated a String per cell per frame — steady draw-phase
garbage. The draw is now allocation-free (also improves GlyphCache hit
rate via stable string instances).
gfxinfo over ~12s of playback on the busy track (moto g84):
janky frames 47.2% → 32.0%; 99th-pct frame 129ms → 53ms;
missed vsync 40 → 10; 50th 18ms → 14ms.
No engine/audio change. Remaining jank is the auto-follow scroll
redrawing the whole grid each line (everything shifts), not addressed here.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>