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
+31
@@ -520,6 +520,37 @@ no penalty because there was no way to earn one.
|
||||
- Ten tests now (26 total), including a named regression for the auto-aim being gone and one that
|
||||
deals 40 rounds and asserts colours, counts and shapes all move.
|
||||
|
||||
**THIRD PASS, same day — "the drill mode immediately goes into some horrible audio loop" (phone).**
|
||||
Never reproduced on desktop, and that is the tell: **three of the four defects behind it can only
|
||||
happen on a phone, and my whole verification had been stepping frames with rAF killed on a machine
|
||||
whose AudioContext was already running.** All four are in `voiceSeq`, which is new code, and every
|
||||
one of them is a rule `voice()` had followed for months and my function quietly broke:
|
||||
|
||||
1. **A SUSPENDED AudioContext HAS A FROZEN CLOCK.** On a phone the context starts suspended and
|
||||
`currentTime` does not advance until it resumes. Scheduling four clips at `currentTime + 0, +0.9,
|
||||
+1.7, +2.4` against a frozen clock puts every one of them in the PAST by the time the context
|
||||
actually starts — so they all fire on the same instant, on top of each other, through a 1.3 s
|
||||
reverb tail. Resume FIRST, and re-read the clock after the decode.
|
||||
2. **A SECOND CALL LAYERED INSTEAD OF REPLACING.** `voice()` has always enforced one man, one
|
||||
microphone; `voiceSeq` SET that clock (`voiceUntil`) and never checked it. Two calls close
|
||||
together = two overlapping lists. It stops the outstanding one now.
|
||||
3. **CANCELLING A LIST HAS TO GIVE BACK THE TIME IT BOOKED.** The first cut of fix 2 stopped the old
|
||||
sources but left `voiceUntil` standing, so the replacement queued behind a list that would never
|
||||
play — measured at **6.6 s of silence** before the call the player is waiting on, and every
|
||||
redeal made it worse. `seqUntilBefore` rewinds the booking.
|
||||
4. **THE AMPLIFIER, in drill.js: the empty-court net redealt ON THE SPOT**, which makes the repair
|
||||
rate the FRAME RATE — and every deal calls the umpire. It goes through the ordinary
|
||||
between-rounds beat now, so the worst case is one redeal every `drillBeat` seconds: a mode that
|
||||
limps instead of a phone screaming at you. Pinned by a test that empties the court every frame
|
||||
for six seconds and asserts at most one call per beat.
|
||||
|
||||
**And a measurement lesson worth more than the fix.** The first "it's clean" reading was taken by
|
||||
counting `fetch`es of `assets/audio/*` — which proved nothing, because `VOICE[]` caches every clip
|
||||
and a repeat play never fetches again. The honest probe tags each decoded buffer with the file it
|
||||
came from (patch `decodeAudioData`, pair it with the pending fetch URL) and counts
|
||||
`AudioBufferSourceNode.start` — then "did two umpire lines overlap" is a real question with a real
|
||||
answer. Counting requests measures loading; counting `start()` measures sound.
|
||||
|
||||
**Open, and every one of them is Oliver's call after he plays it:**
|
||||
1. **A ball that carries through the lane can complete two steps at once** — measured: one serve
|
||||
killed bib 1 and then bib 2 standing behind it, banking two correct steps in one shot. Had the
|
||||
|
||||
Reference in new issue
Block a user