android: the handoff — the pending rounds, the pipeline, and what is unverified
Everything written this session that has not been through the device: rounds 43 to 48 (each patch adversarially verified, 43's transient-notice expectation fixed), a partial round 49, the pipeline scripts, the Android Auto design and the icon comparison mock. Also the two harness changes that were never A/B'd — adaptive waits, which may only shorten a declared wait and never lengthen it, and a ship that runs the mock walk and the deploy at the same time. The A/B and its 255-step baseline are in handoff/tools; main stays at what the phone has actually walked.
This commit is contained in:
1 parent
867fd42972
commit
9f0601027c
400 files changed
+26401
-3
No files matched your search
@@ -0,0 +1,57 @@
|
||||
/* ── the measured envelope, and the rough pass over it ───────────────────
|
||||
The desktop's editor offers the same two things (LyricsSyncEditor.svelte):
|
||||
a strip showing where the recording has voice in it, and a fill that lays
|
||||
the lines across the ACTIVE stretches only. src/lib/lyric-sync.js is the
|
||||
desktop's own generator — pure, shared, unit-tested — so the phone imports
|
||||
it rather than keeping a second copy of the arithmetic. It refuses instead
|
||||
of guessing when the measurement shows nothing usable: an estimate is a
|
||||
starting point for the tap, never finished timing, and the room says so
|
||||
everywhere it shows. The envelope belongs to the track, not to the store —
|
||||
it is measured on open and thrown away with the room. */
|
||||
async function loadActivity(id) {
|
||||
/* a streamed track is a cache file the engine may still be filling, and
|
||||
the reducer refuses one on the same decode path for the same reason */
|
||||
if (!id?.path || player.track?.stream) { tapSync.reading = "none"; return; }
|
||||
tapSync.reading = "reading";
|
||||
try {
|
||||
const a = await api.lyricsVocalActivity(id.path);
|
||||
if (tapSync.identity?.key !== id.key) return; // the room moved on
|
||||
tapSync.activity = a?.frames?.length ? a : null;
|
||||
tapSync.reading = tapSync.activity ? "ready" : "none";
|
||||
} catch {
|
||||
if (tapSync.identity?.key === id.key) tapSync.reading = "none";
|
||||
}
|
||||
}
|
||||
/** Lay every line across the stretches the envelope says are sung, then put
|
||||
* the cursor back at the top: the taps CORRECT the estimate from there. */
|
||||
export function estimateTimings() {
|
||||
const a = tapSync.activity;
|
||||
if (!a || !tapSync.rows.length) return false;
|
||||
/* the room's rows ARE the lines: openTapSync built them from the document
|
||||
the sheet is showing. So the generator's two halves are called directly
|
||||
rather than roughSync's own text parse, which drops a section label a
|
||||
SYNCED document still carries and would hand back a shorter list that
|
||||
no longer lines up with the rows the room is asking about. */
|
||||
const filled = distributeLines(tapSync.rows.map((r) => String(r.text ?? "").trim()), activeSegments(a.frames, a.frameMs));
|
||||
/* it refuses rather than guesses when there is too little measured voice
|
||||
to lay the lines across — and that is the only reason it refuses, so
|
||||
the room can say which one it is */
|
||||
if (!filled) {
|
||||
tapSync.error = "There is not enough voice in this song to estimate from.";
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < filled.length; i++) tapSync.rows[i].time_ms = filled[i].time_ms;
|
||||
tapSync.cursor = 0;
|
||||
tapSync.estimated = true;
|
||||
tapSync.error = "";
|
||||
return true;
|
||||
}
|
||||
/** Drop it: every line back to no time, ready for the tap. A rough fill
|
||||
* stamps every row at once, so Undo has nothing left to take back — this
|
||||
* is the way out of an estimate that is wrong. */
|
||||
export function clearEstimate() {
|
||||
for (const r of tapSync.rows) r.time_ms = null;
|
||||
tapSync.cursor = 0;
|
||||
tapSync.estimated = false;
|
||||
tapSync.error = "";
|
||||
}
|
||||
Reference in new issue
Block a user