Ramp sound FX parameters over a time on the audio clock
game-maker / gate (push) Successful in 3m21s
game-maker / suite (push) Successful in 15m26s

A pitch sweep counted down in On update or On fixed update runs in
game time, while the sound runs on the clock of the audio device. A
short sound magnifies any offset between the two, so the sweep came
out different on every play and most of all on the first.

- Set sound FX parameters has a new input time in seconds. The values
  run evenly to their target in that time, scheduled on the audio
  clock with linearRampToValueAtTime or exponentialRampToValueAtTime.
- Evenly in their own unit: a pitch in semitones and a gain in
  decibels, an EQ band and a Mix share linearly in their value.
- A time of 0 or below keeps the quick glide of about 0.1 s.
- Each change starts from where the voice is now. Whatever was still
  planned is dropped, so a change never queues behind an older one.
  Firefox lacks cancelAndHoldAtTime, there the value is held by hand.
- A voice still waiting on its bytes starts a timed change with the
  sound, so the whole sweep is heard.

Tests:
- node-catalog: time is handed on, a time below 0 is a quick glide.
- sound-library: semitones and decibels ramp in their own steps, a
  timed change on a waiting voice starts with the sound.
- sound-library + node-catalog + soundfx: 419 tests, green.
- e2e 34 on melektaus-atx: ALL PASSED, 31 checks. A 2 s ramp from
  depth 7 to -5 read speed 0.979, 0.926 and 0.778 half a second on.
- Counter-check: folding a timed change on a waiting voice into its
  start values fails the new test.
- The gate did not run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-10-05 21:47:13 +02:00
1 parent ed2b58b3c0
commit 501a1524ed
9 files changed
+154 -29

No files matched your search

+14 -2
View File
@@ -2032,7 +2032,19 @@ const CASES: Record<string, Case[]> = {
name: 'hands the numbers of the bundle to the voice it is given',
inputs: { voice: 4, fxParams: { depth: 3, label: 'x' } },
next: 'exec',
after: (ctx) => expect(ctx.tuned).toEqual([{ voice: 4, fxParams: { depth: 3 } }]),
after: (ctx) => expect(ctx.tuned).toEqual([{ voice: 4, fxParams: { depth: 3 }, time: 0 }]),
},
{
name: 'hands on the time the values take to get there',
inputs: { voice: 4, fxParams: { depth: -64 }, time: 1.28 },
next: 'exec',
after: (ctx) => expect(ctx.tuned).toEqual([{ voice: 4, fxParams: { depth: -64 }, time: 1.28 }]),
},
{
name: 'a time below 0 is a quick glide',
inputs: { voice: 4, fxParams: { depth: 3 }, time: -2 },
next: 'exec',
after: (ctx) => expect(ctx.tuned).toEqual([{ voice: 4, fxParams: { depth: 3 }, time: 0 }]),
},
{
name: 'voice 0 – nothing was played – reaches nothing, and the flow carries on',
@@ -2119,6 +2131,7 @@ const EVENT_NODES: Record<string, string> = {
OnStart: 'graph.test.ts',
OnEnable: 'behavior-events.test.ts',
OnUpdate: 'graph.test.ts',
OnFixedUpdate: 'fixed-update.test.ts',
OnDisable: 'behavior-events.test.ts',
OnDestroy: 'behavior-events.test.ts',
OnKeyDown: 'behavior-events.test.ts',
@@ -2131,7 +2144,6 @@ const EVENT_NODES: Record<string, string> = {
OnClick: 'nodes.test.ts',
OnMouseWheel: 'clip-areas.test.ts',
OnAnimationFinished: 'animation.test.ts',
OnFixedUpdate: 'fixed-update.test.ts',
OnStateEntered: 'state-machine.test.ts',
OnStateExited: 'state-machine.test.ts',
};