The umpire is one man with one microphone, and voiceSeq forgot it
"the drill mode immediately goes into some horrible audio loop" — on a phone, never on desktop, and that is the tell. Three of the four defects can only happen on a phone, and every one of them is a rule voice() had followed for months that my new function quietly broke. - A SUSPENDED AudioContext HAS A FROZEN CLOCK. Scheduling four clips at currentTime +0/+0.9/+1.7/+2.4 while the context is suspended puts all of them in the past by the time it starts, so they fire on one instant through a 1.3s reverb tail. Resume first, re-read the clock after the decode. - A second call layered instead of replacing. voiceSeq SET voiceUntil and never checked it. It stops the outstanding list now. - ...and cancelling a list has to give back the time it booked, or the replacement queues behind a list that will never play. Measured at 6.6s of silence before the call the player is waiting on. - The amplifier was in drill.js: the empty-court net redealt on the spot, making the repair rate the frame rate — and every deal calls the umpire. It goes through the ordinary between-rounds beat now. Verified by tagging each decoded buffer with its source file and counting start() calls: one drill_call, three colours, spaced, zero overlapping voice lines. The earlier "clean" reading counted fetches, which proves nothing — VOICE[] caches, so a repeat play never fetches again. 27 tests; the new one empties the court every frame for six seconds and asserts at most one umpire call per beat. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ef8a413031
commit
435a470ec7
5 files changed
+93
-7
No files matched your search
+8
-2
@@ -127,9 +127,15 @@ export function updateDrill(dt) {
|
||||
// latches loaded) and could not reproduce it afterwards, which is precisely the kind of bug that
|
||||
// comes back in front of a player. Unrepaired it is a dead mode: no targets, no timer, nothing to
|
||||
// tap. Deal a fresh round and leave evidence, same contract as watchdog()'s invariant net.
|
||||
// It goes through the ordinary between-rounds BEAT rather than dealing on the spot, and that is
|
||||
// the load-bearing half: dealing here directly makes the repair rate the FRAME RATE, so a court
|
||||
// that keeps coming up empty redeals sixty times a second — and every deal calls the umpire.
|
||||
// Through the beat, the worst case is one redeal every drillBeat seconds, which is a mode that
|
||||
// limps instead of a phone screaming at you.
|
||||
if (!zombies.some(z => z.bib !== undefined)) {
|
||||
logSwing({ result: 'INVARIANT', reason: 'drill court emptied from outside the round — redealt' });
|
||||
return newDrillRound();
|
||||
logSwing({ result: 'INVARIANT', reason: 'drill court emptied from outside the round — redealing' });
|
||||
d.beat = TUNE.drillBeat;
|
||||
return;
|
||||
}
|
||||
if (d.callT > 0) { // the walk-on: they take their marks
|
||||
d.callT -= dt;
|
||||
|
||||
Reference in new issue
Block a user