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>
38 lines
1.6 KiB
TypeScript
38 lines
1.6 KiB
TypeScript
import type { NodeDef } from '../../NodeRegistry';
|
||
import { asNumber } from '../../values';
|
||
import { exec, num } from '../shared';
|
||
import { fxParamsOf } from './PlaySound';
|
||
|
||
/**
|
||
* Changes the sound FX parameters of a sound that is already playing (#315). It reaches one voice,
|
||
* by the number its `Play sound` handed out: every play is a sound of its own, and two objects with
|
||
* the same engine sound – or one object playing it twice – each keep theirs.
|
||
*/
|
||
export const SetSoundFxParameters: NodeDef = {
|
||
type: 'SetSoundFxParameters',
|
||
label: 'Set sound FX parameters',
|
||
category: 'action',
|
||
description:
|
||
'Changes the sound FX parameters of a sound while it plays: the voice a Play sound handed out ' +
|
||
'glides to the values from a Sound FX parameters node. Keep the voice in a variable to reach it ' +
|
||
'from another event. A parameter not given keeps its value, a voice that has ended is not ' +
|
||
'reached. A pitch changes by speed here, so the sound gets shorter or longer while it is moved. ' +
|
||
'time: seconds the values take to get there, evenly and on the clock of the sound itself, so ' +
|
||
'a sweep sounds the same every time; 0 is a quick glide.',
|
||
inputs: [
|
||
exec(),
|
||
{ ...num('voice'), inline: false },
|
||
{ key: 'fxParams', label: 'fx params', type: 'fxparams' },
|
||
num('time'),
|
||
],
|
||
outputs: [exec()],
|
||
params: [],
|
||
run: (ctx, inputs) => {
|
||
const voice = asNumber(inputs.voice);
|
||
const time = asNumber(inputs.time);
|
||
if (voice > 0)
|
||
ctx.tuneSound(voice, fxParamsOf(inputs.fxParams), Number.isFinite(time) && time > 0 ? time : 0);
|
||
return { next: 'exec' };
|
||
},
|
||
};
|