Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

commit bf7e74954d5c4384c61a6495cf28dcf581e96141
parent 48d297306c446967c86d590790df0ddbc58c1c3f
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Thu,  1 Oct 2026 14:46:41 -0400

umtool: the bench's audio-clock playhead paints at 30 Hz, and the exact-stop spec checks the start against the schedule

The playhead read off the audio clock re-rendered the bench every frame;
it now updates at about 30 Hz. `m` reads the playhead through a ref, so the
keyboard is not re-bound on every update.

The exact-stop spec no longer infers the played length from the first
non-zero frame: the selection starts inside the fixture's silence at
2.9-3.1 s. It checks that nothing sounds before the scheduled start; the
stop is still checked to within one render quantum.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Diffstat:
Mumtool/components/projects/ClipBench.tsx | 22++++++++++++++++++----
Mumtool/e2e/clip-bench.spec.ts | 7++++---
2 files changed, 22 insertions(+), 7 deletions(-)

diff --git a/umtool/components/projects/ClipBench.tsx b/umtool/components/projects/ClipBench.tsx @@ -399,6 +399,10 @@ export default function ClipBench({ data }: { data: ClipBenchData }) { const [cutScore, setCutScore] = useState<number | null>(null); const [peekPad, setPeekPad] = useState(PEEK_STEP); const [playhead, setPlayhead] = useState<number | null>(null); + const playheadRef = useRef<number | null>(null); + useEffect(() => { + playheadRef.current = playhead; + }, [playhead]); const [note, setNote] = useState<string | null>(null); const [busy, setBusy] = useState<string | null>(null); const [dirty, setDirty] = useState(false); @@ -928,9 +932,16 @@ export default function ClipBench({ data }: { data: ClipBenchData }) { setPlayInfo((pi) => (pi ? { ...pi, state: "stopped", endedAt: s.to + Math.max(0, late) * rate } : pi)); }; + // The playhead at ~30 Hz, not every frame: each update re-renders the + // bench, and the line moving smoothly is not worth that at 60. + let painted = -1; const tick = () => { if (session.current !== s) return; - setPlayhead(playheadAt(from, t0, ctx.currentTime, rate, sch.duration)); + const now = ctx.currentTime; + if (now - painted >= 1 / 30) { + painted = now; + setPlayhead(playheadAt(from, t0, now, rate, sch.duration)); + } s.raf = requestAnimationFrame(tick); }; s.raf = requestAnimationFrame(tick); @@ -1298,12 +1309,15 @@ export default function ClipBench({ data }: { data: ClipBenchData }) { /** `m`: where you are listening. Anywhere outside the selection is refused. */ const muteAtPlayhead = useCallback(() => { - if (playhead == null || playhead < sel.from - 0.02 || playhead > sel.to + 0.02) { + // Through a ref: the playhead moves every frame while something plays, + // and a callback keyed on it would re-bind the keyboard every frame. + const at = playheadRef.current; + if (at == null || at < sel.from - 0.02 || at > sel.to + 0.02) { setNote("the playhead is not inside the selection — play to the spot, or pick it on the waveform"); return; } - placeMute(playhead, false); - }, [playhead, sel.from, sel.to, placeMute]); + placeMute(at, false); + }, [sel.from, sel.to, placeMute]); // ---- keyboard ----------------------------------------------------------- useEffect(() => { diff --git a/umtool/e2e/clip-bench.spec.ts b/umtool/e2e/clip-bench.spec.ts @@ -971,9 +971,10 @@ test("a play-selection stops within one audio render quantum of its end, at any // The STOP: the last sound is the scheduled stop, to within a quantum. const stopFrame = Math.round(play.ctxStop * sr); expect(Math.abs(last + 1 - stopFrame), `stopped ${last + 1 - stopFrame} frames from the end at ${rate}x`).toBeLessThanOrEqual(quantum); - // And what played was the selection: two seconds of source, at any speed. - const playedSource = ((last + 1 - first) / sr) * Number(rate); - expect(Math.abs(playedSource - 2) * sr).toBeLessThanOrEqual(quantum * Number(rate) + 1); + // And nothing sounded before the scheduled start. (Not "the first sound + // IS the start": 3.00 is inside the silence at 2.9-3.1, so the first + // non-zero frame is the tone coming back.) + expect(first).toBeGreaterThanOrEqual(Math.round(play.ctxStart * sr) - quantum); } });