Check what a streamed sound holds by the library's own count (#378)
game-maker / gate (push) Successful in 4m8s
game-maker / suite (push) Successful in 16m22s

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>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-10-10 19:01:00 +02:00
1 parent d4a45c127b
commit 3f7eb03143
4 files changed
+53 -10

No files matched your search

+12 -2
View File
@@ -1574,6 +1574,8 @@ export class SoundLibrary implements AudioSink {
tuned: number;
tunedAt: number | null;
decoded: number;
/** Bytes of samples in the files decoded whole: what a long sound costs when it does not stream. */
decodedBytes: number;
failed: number;
/** Whether the pitch worklet is there; null while nothing has asked for it or it is loading. */
worklet: boolean | null;
@@ -1582,9 +1584,10 @@ export class SoundLibrary implements AudioSink {
last: LastVoice | null;
/**
* Streaming (#349): sources playing from a copy now, samples that came too late to be heard, the
* bytes fetched of every copy, and the copies whose head is decoded.
* bytes fetched of every copy, the copies whose head is decoded, and the bytes of samples held for
* streaming: every head and what is sent ahead to the worklet.
*/
streaming: { playing: number; skipped: number; fetched: number; heads: number };
streaming: { playing: number; skipped: number; fetched: number; heads: number; held: number };
} {
const files = [...this.streamFiles.values()];
return {
@@ -1594,6 +1597,10 @@ export class SoundLibrary implements AudioSink {
tuned: this.tuned,
tunedAt: this.tunedAt,
decoded: this.buffers.size,
decodedBytes: [...this.buffers.values()].reduce(
(sum, buffer) => sum + buffer.length * buffer.numberOfChannels * Float32Array.BYTES_PER_ELEMENT,
0,
),
failed: this.failed.size,
worklet: this.hasWorklet,
vocoders: this.voices.reduce((sum, voice) => sum + vocodersOf(voice), 0),
@@ -1603,6 +1610,9 @@ export class SoundLibrary implements AudioSink {
skipped: [...this.feeds].reduce((sum, feed) => sum + feed.skipped, this.skippedBefore),
fetched: files.reduce((sum, file) => sum + file.bytes, 0),
heads: files.filter((file) => file.head !== null).length,
held:
files.reduce((sum, file) => sum + file.headBytes, 0) +
[...this.feeds].reduce((sum, feed) => sum + feed.ahead, 0),
},
};
}