Show a Reverb node as a device with a round screen of its tail (#389)
game-maker / gate (push) Successful in 4m5s
game-maker / suite (push) Successful in 15m49s

The Compressor, the Gain, the Chip and the Envelope are devices in the
graph. The Reverb was still a list of rows, and how long and how dark
its tail rings was left to the numbers.

- The node is a plate of 640x280 in pale lavender. Its inputs are on
  the left, its output on the right.
- A round screen draws the tail as rings out from the sound. A ring's
  radius is its time, as the root of its seconds, on one scale up to
  10.5 s for every Reverb. So a Room is a small disc and a Cave fills
  two thirds.
- A ring is as bright as the tail is loud there, evenly in dB. Its
  colour is how much of the highs is left, from ice to violet, with
  the cut-off of the impulse response (reverbCutoff, now shared).
- The pre-delay is a dark gap round the dot, a dashed ring marks
  -60 dB. Seeded dots stand for the reflections. Wet fades the tail
  against the dot.
- A type of Sound draws the peaks of its sound from the library, each
  against its loudest. It says LOADING until they are there.
- A button for each type, a list of the sounds and four chrome knobs:
  Decay, Damping, Pre-delay and Wet.
- A knob the type does not take is greyed but turns. It goes over to
  Custom with the type's numbers, as one undo step. Picking a sound
  makes the type Sound, also one step.
- NodeParamDrag can write several settings as one drag for that.
- Test hook __gm.reverb.panel(nodeId), for the sound FX and the
  instrument graph.

Tests:
- Unit reverb-view: 10 new tests for the plate, the knob turn, the
  numbers per type, the scale, the tail and the texts. A screen that
  ignores damping turns the highs test red.
- e2e 34 (atx): 1 new check (device between its ports, decay knob of
  a Hall goes to Custom as one undo step, Cave button, a sound from
  the list makes it Sound and draws that sound).
- Probe pictures on stein2: Room, Hall, Cave, Plate, Custom with
  damping 1 and pre-delay 0.3 s, Sound.
- Gate (atx): CHECK EXIT 0, 287 files, 4393 tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-10-10 16:35:59 +02:00
1 parent c75e72d4ee
commit e8021b1fd3
11 files changed
+1249 -29

No files matched your search

+73
View File
@@ -1214,6 +1214,79 @@ export async function sound(t) {
graph.nodes = graph.nodes.filter((n) => n.id !== 'fx-hw');
graph.edges = graph.edges.filter((e) => !['fx-e15', 'fx-e16'].includes(e.id));
graph.edges.push(${JSON.stringify(edge('fx-e3', 'fx-gain', 'out', 'fx-out', 'in'))});`);
// #389: a Reverb node is drawn as a device with a round screen of its tail. A knob its type does
// not take goes over to Custom with the type's numbers, as one undo step; a sound picked on
// it makes the type Sound and the screen draws that sound's tail. It is wired to nothing.
await mutate(`
const graph = __gm.state.project.assets.find((a) => a.id === ${JSON.stringify(fxId)}).graph;
graph.nodes.push(${JSON.stringify(node('fx-rv', 'SoundFxReverb', 0, 400, { room: 'hall' }))});`);
await evaluate('__gm.soundfx.fit()');
await sleep(100);
const reverbPanel = () => json("__gm.reverb.panel('fx-rv')");
const reverbParams = () =>
json(
`__gm.state.project.assets.find((a) => a.id === ${JSON.stringify(fxId)}).graph.nodes.find((n) => n.id === 'fx-rv').params`,
);
const rvShown = await reverbPanel();
const rvRect = rvShown.rect;
const rvBeside = (p) => p.y > rvRect.y && p.y < rvRect.y + rvRect.h;
const rvDial = rvShown.knobs.decay.dial;
await drag(rvDial.x, rvDial.y, rvDial.x, rvDial.y - 20);
const rvTurned = { params: await reverbParams(), panel: await reverbPanel() };
await press('z', { ctrl: true });
const rvUndone = await reverbParams();
await click(rvShown.rooms.cave.x, rvShown.rooms.cave.y);
const rvCave = { params: await reverbParams(), panel: await reverbPanel() };
await click(rvCave.panel.sound.at.x, rvCave.panel.sound.at.y);
const rvOffered = (await json('__gm.menuRows()')) ?? [];
const rvPick = rvOffered.find((row) => row);
const rvPickAt = await t.locate(rvPick, true);
await click(rvPickAt.x, rvPickAt.y);
const rvSound = {
params: await reverbParams(),
panel: await waitFor(reverbPanel, (p) => p.scope.kind === 'sound'),
};
await press('z', { ctrl: true });
const rvSoundUndone = await reverbParams();
check(
'a Reverb node shows its device between its ports; a knob of a room goes to Custom with its numbers as one undo step, a type button switches, a picked sound makes it Sound and draws that sound',
rvShown.shown &&
rvShown.size.w === 640 &&
rvShown.size.h === 280 &&
rvShown.room === 'hall' &&
['decay', 'damping', 'predelay', 'impulse'].every((key) => rvShown.idle.includes(key)) &&
Object.keys(rvShown.inputs).join() === 'in,mix' &&
Object.values(rvShown.inputs).every((p) => p.x < rvRect.x && rvBeside(p)) &&
Object.values(rvShown.outputs).every((p) => p.x > rvRect.x + rvRect.w && rvBeside(p)) &&
rvShown.scope.kind === 'made' &&
rvShown.scope.reach > 0.48 &&
rvShown.scope.reach < 0.5 &&
rvShown.scope.says.ends === 'ENDS 2.52 s' &&
rvShown.highs === 'HIGHS 5.5 kHz' &&
rvTurned.params.room === 'custom' &&
rvTurned.params.decay > 2.5 &&
rvTurned.params.damping === 0.35 &&
rvTurned.params.predelay === 0.02 &&
Object.keys(rvTurned.panel.inputs).length === 5 &&
rvTurned.panel.idle.join() === 'impulse' &&
rvTurned.panel.scope.reach > rvShown.scope.reach &&
rvUndone.room === 'hall' &&
rvUndone.decay === undefined &&
rvCave.params.room === 'cave' &&
rvCave.panel.scope.reach > 0.68 &&
Boolean(rvPick) &&
rvSound.params.room === 'sound' &&
Boolean(rvSound.params.impulse) &&
rvSound.panel.sound.text === rvPick &&
rvSound.panel.scope.kind === 'sound' &&
rvSound.panel.scope.rings > 0 &&
rvSoundUndone.room === 'cave' &&
rvSoundUndone.impulse === undefined,
JSON.stringify({ rvShown, rvTurned, rvUndone, rvCave, rvOffered, rvSound, rvSoundUndone }),
);
await mutate(`
const graph = __gm.state.project.assets.find((a) => a.id === ${JSON.stringify(fxId)}).graph;
graph.nodes = graph.nodes.filter((n) => n.id !== 'fx-rv');`);
await showTab('scene');
await mutate(`
const graph = __gm.state.project.assets.find((a) => a.id === 'sn-logic').graph;