Archilyzer · Source

archilyzer

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

commit 9d2bd76b6f911152667623f503731478bb54d81b
parent 7085e0db117c4329ddae47acc3bd804eef870489
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Thu,  1 Oct 2026 15:01:17 -0400

umtool: the bench's mute preview is the build's — one MUTE_FADE (0.04 s, from deck.mjs), a ramp that ends at the mark

The bench faded over 0.05 s starting at the mark; the build fades over 0.04 s
ending at it (silent from muteFrom exactly). A mark set by ear now sounds in
the bench as it will in the cut.

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

Diffstat:
Mumtool/lib/report/playback.mjs | 14+++++++++-----
Mumtool/lib/report/playback.test.mjs | 12+++++++++---
2 files changed, 18 insertions(+), 8 deletions(-)

diff --git a/umtool/lib/report/playback.mjs b/umtool/lib/report/playback.mjs @@ -38,10 +38,11 @@ export const DECODE_MARGIN = 30; export const START_LEAD = 0.03; /** - * The mute mark's fade, in source seconds: short enough to read as "the sound - * stops here", long enough not to click. + * The mute mark's fade, in source seconds: the BUILD's, so what the bench plays + * is what the cut does -- a ramp that ENDS at the mark, silent from the mark on. */ -export const MUTE_FADE = 0.05; +export { MUTE_FADE } from "umtool-report-to-video/deck"; +import { MUTE_FADE } from "umtool-report-to-video/deck"; /** One Web Audio render quantum, in frames. */ export const RENDER_QUANTUM = 128; @@ -134,7 +135,9 @@ export function playheadAt(from, t0, now, rate, duration) { * { at: 0, fade: 0 } the range starts at or after the mark: silent * from the first sample, the picture still plays * { at, fade } full level until `at`, then a linear ramp to - * nothing over `fade` + * nothing over `fade`, reaching it AT the mark + * (as the build's afade does); a range that starts + * inside the fade ramps from its first sample * * @param {number | null | undefined} muteFrom * @param {number} from @@ -148,7 +151,8 @@ export function muteRamp(muteFrom, from, to, rate = 1, fade = MUTE_FADE) { const r = rate > 0 ? rate : 1; if (muteFrom >= to) return null; if (muteFrom <= from) return { at: 0, fade: 0 }; - return { at: (muteFrom - from) / r, fade: fade / r }; + const st = Math.max(from, muteFrom - fade); + return { at: (st - from) / r, fade: (muteFrom - st) / r }; } /** diff --git a/umtool/lib/report/playback.test.mjs b/umtool/lib/report/playback.test.mjs @@ -95,14 +95,20 @@ test("muteRamp: when the mark silences a playback, in wall seconds from its star // At or before the start: silent throughout, the picture still plays. assert.deepEqual(muteRamp(10, 10, 20), { at: 0, fade: 0 }); assert.deepEqual(muteRamp(5, 10, 20), { at: 0, fade: 0 }); - // Inside: full level until the mark, then the fade. + // Inside: full level until the fade, which ENDS at the mark (the build's). + assert.equal(MUTE_FADE, 0.04); const m = muteRamp(16, 10, 20, 1); - near(m.at, 6); + near(m.at, 6 - MUTE_FADE); near(m.fade, MUTE_FADE); + near(m.at + m.fade, 6); // At 2x the mark arrives in half the time, and so does the fade. const f = muteRamp(16, 10, 20, 2); - near(f.at, 3); + near(f.at, (6 - MUTE_FADE) / 2); near(f.fade, MUTE_FADE / 2); + // A range starting inside the fade ramps from its first sample to the mark. + const g = muteRamp(16, 15.98, 20, 1); + near(g.at, 0); + near(g.fade, 0.02); }); test("elementShouldStop: stops when the end is under half a frame away, at any rate", () => {