game-maker / suite (push) Successful in 12m1s
Sounds follow the road fonts already paved: the file goes to the server content-addressed under its sha256, the project keeps a named asset that points at it, and nothing is ever re-encoded. The format is read from the file's own signature, not from what the browser claims - the same .m4a arrives as audio/mp4, audio/x-m4a or as nothing at all. Playing is two nodes. "Play sound" takes the asset and may raise or lower its volume; every call is its own voice, so two jumps in a row are two jumps heard. Its loop input is a choice of three - from the asset, on, off - because a checkbox has to spend one of its two positions on "not set" and would lose a case: an asset that loops could then never be played once. Below the graph core stays browser-free: NodeContext hands out an asset id, BehaviorSystem resolves it against the project and folds the asset's own volume and loop into the call, and only viewport/SoundLibrary knows about Web Audio. A run without a sink plays nothing instead of throwing. The CI browser is muted, so the ear is no witness: the debug hook counts started and stopped voices, and step 34 reads that counter.
20 lines
601 B
TypeScript
20 lines
601 B
TypeScript
import type { NodeDef } from '../../NodeRegistry';
|
||
import { asString } from '../../values';
|
||
import { exec, soundPort } from '../shared';
|
||
|
||
export const StopSound: NodeDef = {
|
||
type: 'StopSound',
|
||
label: 'Stop sound',
|
||
category: 'action',
|
||
description:
|
||
'Silences every voice of a sound, however many plays started it – including a looping one. ' +
|
||
'With no sound picked it silences everything the run is playing.',
|
||
inputs: [exec(), soundPort()],
|
||
outputs: [exec()],
|
||
params: [],
|
||
run: (ctx, inputs) => {
|
||
ctx.stopSound(asString(inputs.sound));
|
||
return { next: 'exec' };
|
||
},
|
||
};
|