A voice used to end the moment its sources did, and Stop sound cut it dead. An echo has to outlive its sound, and a stop must not click. - Delay node: a delay line with a damped feedback loop, added at mix next to the untouched sound. Time 0.01 to 2 s, feedback up to 0.95, damping 20 kHz to 500 Hz evenly in octaves, all fed by parameters and moved by Set sound FX parameters like any other number. - Ping-pong is a setting of the node. It takes the sound into the left of the line and swaps the sides every round. - A voice with an echo rings on for its tail: until the last echo is 60 dB below the sound, from time, feedback and mix (fxTailOf). It stays reachable meanwhile, and a change counts the tail again. - The end of a tail is timed on the audio clock with a silent ConstantSourceNode, so a paused run holds it. Then the voice is disconnected, since an echo is a loop of nodes. - Stop sound fades a voice out in 10 ms instead of cutting it. Let ring fades only its sources and keeps the voice for its echoes. Where the context is suspended it cuts at once. - Stop sound takes a voice. Connected, it stops only that play, and a 0 from a Play sound that played nothing stops nothing. - Play sound hands out tail: length plus the echoes at its start values. - Destroying a behavior and ending a run still cut, echoes included. - No limit on voices. The emit of a sound FX node now also gets the node's settings. Finding: in Chrome every round of a feedback loop comes one render quantum late (128 samples, 2.7 ms at 48 kHz), measured as 0, 128, 256, 384 for the first four echoes. Left so, since a browser without it would then hear the echoes early. Written in the wiki. Tests: - sound-library, soundfx, node-catalog, sounds, keep-on-scene-load: 489 tests green. New: fade on stop, cut while suspended, echo and ping-pong wiring, ringing until the tail, tail counted again, let ring, stop by voice, echo knobs, delay compile, tail sums. - Counter-checks: no tail, a stop without fade, a connected voice 0 read as unconnected. Each fails its tests, files back by cmp. - e2e 34 (melektaus-atx): ALL PASSED, 37 checks. A blip through a ping-pong Delay (0.3 s, 0.5, 0.5) rang 2.97 s, 2.95 s worked out. Let ring kept the voice, a stop after it cut it. Rendered offline with the player's own stageNodes the echoes are 0.25 left, 0.125 right, 0.063 left. 64 stereo voices of it took about 0.3 s for 2 s, so about 400 play at once. - Gate (melektaus-atx): CHECK EXIT 0, 242 test files, 3713 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6e70421b92
commit
cf6799a784
16 files changed
+987
-72
No files matched your search
+5
-1
@@ -272,7 +272,11 @@ export function fakeContext(overrides: Partial<NodeContext> = {}): FakeContext {
|
||||
ctx.played.push({ assetId, volume, loop, detached, fx, fxParams }),
|
||||
// Every sound lasts a quarter of a second, as if it had been loaded.
|
||||
soundLength: () => 0.25,
|
||||
stopSound: (assetId) => ctx.silenced.push(assetId),
|
||||
// The tail of a voice is its number in tenths of a second, so a test can tell one from another.
|
||||
soundTail: (voice) => voice / 10,
|
||||
// A voice by its number, and letting ring, are written into the line, so one list says it all.
|
||||
stopSound: (assetId, letRing) => ctx.silenced.push(letRing ? `${assetId} (let ring)` : assetId),
|
||||
stopVoice: (voice, letRing) => ctx.silenced.push(`voice ${voice}${letRing ? ' (let ring)' : ''}`),
|
||||
tuned: [],
|
||||
tuneSound: (voice, fxParams, time, ease) => ctx.tuned.push({ voice, fxParams, time, ease }),
|
||||
loadScene: (sceneId) => ctx.loaded.push(sceneId),
|
||||
|
||||
Reference in new issue
Block a user