A cut between two videos flashed white: the sprite switched on had
no picture yet, because its player only opened then or had been
dropped meanwhile. A driven video now waits muted at its time while
its sprite is off, in the editor and in a run.
- A disabled sprite with Video clock Driven asks for its player
without drawing it. A Free video still opens when shown.
- A run's draw list keeps disabled driven sprites, marked as off.
Before, collectWorldItems dropped every disabled object, so the
game view never held a player for them.
- VideoLibrary.wear() counts as asking. The sweeper dropped a held
player after 2 to 4 s and it was loading again at the cut.
- The speed of the asked time is read over a window that grows to
500 ms. Over 100 ms one 1/60 s step more or less read as 17 % off,
the playback rate jumped and the sound dropped out.
- A stream feeds every packet it has loaded to the decoder at once
and waits once. One round trip per 20 ms packet let the decoder
fall behind on a busy main thread, which made e2e 34 flaky in CI.
Tests:
- Unit streaming, video-library, video-follow: green, 4 new
- e2e 34 + 66 (atx): ALL PASSED, stream skipped 0, 66m new
- e2e 66 (atx), two runs: ALL PASSED, 25 checks, 66n new
- Gate (atx): CHECK EXIT 0, 297 files, 4557 tests
- Counter-checks: without the draw list fix 66n finds no player,
without the wear() fix and with the old speed reading the new
unit tests turn red
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The e2e check that a streamed sound costs less than a decoded one
compared how much the renderer process grew. Alone, module 34 saw it
grow by 1 to 8 MB while streaming. After the 33 modules before it in
CI it grew by 25 to 66 MB, and suite went red (CI tasks 611, 614 and
617, the last with 66.7 MB streamed against 95.9 MB decoded).
- SoundLibrary.stats tells decodedBytes, the samples of every file
decoded whole, and streaming.held, every head of a copy and what is
sent ahead to the worklet.
- StreamFile.headBytes and StreamFeed.ahead give their share.
- The e2e check now asks the library: streamed it holds less than
4 MB more than before and nothing decoded whole, decoded it holds
more than 40 MB, the five minutes being 50 MB at 44.1 kHz.
- The growth of the renderer stays in the log of that check. The
counter-proof that the renderer grows by more than 40 MB when the
sound is decoded stays as it was.
Tests:
- Unit sound-library: the streaming test also checks the bytes held.
- e2e 34 (melektaus-atx): 136 checks. Streamed 0.76 MB more held and
none decoded whole, decoded 50.5 MB. The renderer grew by 4 MB and
49 MB.
- Gate (melektaus-atx): 288 files, 4416 tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A sound was decoded whole as soon as the project opened, in the editor
and in a released game: five minutes of stereo held some 115 MB for as
long as the page was open. A sound with Stream on now plays from a copy
as Opus in Ogg, fetched a piece at a time and decoded a few seconds
ahead by WebCodecs, so a game holds a few megabytes for it.
- The Stream switch is a row of the sound, off by default. The Sound
tab suggests it from a minute on and says how far the copy has got.
- StreamKeeper makes the copies in the browser, a moment after the
last change, one at a time: the sound's own and one per clip of its
cut, fades and Reverse baked in. A copy that is no longer wanted is
taken off and deleted once the project is saved without it.
- streamTable says which played names stream from which copy. A clip
without a fitting copy, a Sample and a Reverb impulse stay decoded.
A sound with Stream on is never decoded ahead.
- The page fetches by Range and takes the whole file from a server
that ignores it, as the CI's PHP server does. It keeps the bytes
and the first two seconds decoded, the rest goes four seconds ahead.
- A jump decodes 32 packets before the place first. Measured in
Chrome, what follows is then the same to the bit as decoding from
the start. With 4 packets it was 0.06 off.
- The stream-player on the FX worklet reads the samples at the rate
of the context along a Catmull-Rom curve. What comes late is silent
and skipped once it is there, so a clip keeps its beat.
- The opening stretch goes with the player as it is made. Sent by
message it came a few milliseconds late, and 557 samples of the
attack were lost.
- Pitch on a streamed sound is a shifter after the player, as on a
bus: the length stays and it is heard 42.7 ms late.
- Bakes of patterns and songs (#340) always stream now.
- The song timeline draws a streamed sound from its copy decoded at
8 kHz, keeping only the peaks.
Tests:
- Gate: CHECK EXIT 0, 280 files, 4312 tests (31 new: ogg +3,
stream-queue 6, streams 11, streaming 6, sound-library +5).
- e2e 34: ALL PASSED, 106 checks (7 new). Five minutes of mono,
4.8 MB as WAV, 3.6 MB as its copy: streamed the renderer grows by
5.8 MB, decoded by 69.6 MB. Offline at 48 kHz, start, loop seam and
a reversed clip are the copy decoded whole to the sample (0).
- Counter-checks red as they should be: no pre-roll, no catch-up of
late samples at the end, no opening stretch, a streamed sound
decoded ahead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>