From f4de42b40644fdc88a9cca11aed4079bdf05eb24 Mon Sep 17 00:00:00 2001 From: MelekTaus Date: Mon, 5 Oct 2026 20:53:54 +0200 Subject: [PATCH] Describe changing the sound FX of a playing sound Play sound now gives out the number of its voice, and Set sound FX parameters moves that one voice to new values while it plays. - Sounds: rows for the voice output and the new node, a section Changing a playing sound with the pattern through a variable, the glide and the pitch by speed. - Sounds: a Mix or a stage fed by a parameter keeps its place. Every way has a source of its own. The tab is heard at once. - Behaviors: the node is in the list next to Play sound. Co-Authored-By: Claude Opus 5.5 --- Behaviors.md | 2 +- Sounds.md | 46 +++++++++++++++++++++++++++++++++++++--------- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/Behaviors.md b/Behaviors.md index 5d54ced..73c65ce 100644 --- a/Behaviors.md +++ b/Behaviors.md @@ -252,7 +252,7 @@ the inspector). `Mouse position`, `Mouse button`, `Is hovered`, `Is touching`, `Layer of` (the collision layer of an object by name and number, see [Colliders](Colliders)), `Render layer of` (the render layer an object is drawn on, inherited or its own, see [Concepts](Concepts)), `Animation playing` (whether an animation runs on an object, its progress and time left) and `Animation length` (see [Animations](Animations)), `Get velocity`, `Joint position` (how far a hinge or a slider stands from how it was placed, see [Physics](Physics#joints)), `Current state` (name and id), `Is in state` (one boolean per state) · actions `Move` (dx, dy, scale, dt — connect an event's dt for per-second movement; in the object's own axes, which a turned parent turns too; with *Collide with solids* it slides along what it hits, less friction, and outputs `blocked x/y`, `hit` and the contact `normal x/y`, see [Colliders](Colliders)), `Move toward` (x, y in world units, speed, dt: steps the object toward that point by at most speed times dt and stops exactly on it, also below a moved or turned parent; with *Collide with solids* it moves the way `Move` does; outputs `arrived`, the `distance` still left and `hit`. The way to walk an enemy to a spot, home or after the player), `Set position`, `Set Z` (the object's own `z`, which is its draw order: higher is in front from the next frame on, see [Scene editing](Scene-editing)), `Spawn` (asset input + x/y/z, optional parent → outputs the new object; the asset is picked on the node or arrives over an edge, from an `Asset` node, a variable or a list item. A pick saved while it was still a setting reads as that input's value, so older projects spawn as before; the same holds for the asset of `Is asset`), `Destroy` (removes the object with everything below it; with **keep children** on, only the object goes: its children move up to its parent (top level when it had none) and keep their place, rotation and size in the world, and the output `children` is the list of them, nearest level only. Its behaviors, its rigidbody and its joints go with it either way. In the editor run, children that are content of a placed container move up for the run only: their place belongs to the template, so the scene does not learn it), `Add joint` (ties an object to another while the game runs: the picked Joint asset is placed on *object* at x/y and connected to *connected*, the scene when nothing arrives; outputs the new joint, which `Destroy` lets go again, see [Physics](Physics#joints)), `Emit` (starts a particle source over, see *Particles*), - `Play sound` / `Stop sound` (see [Sounds](Sounds): the asset says how loud and whether it loops, `fx` sends it through a sound FX, whose parameters a `Sound FX parameters` node sets through `fx params`, the node's + `Play sound` / `Stop sound` / `Set sound FX parameters` (see [Sounds](Sounds): the asset says how loud and whether it loops, `fx` sends it through a sound FX, whose parameters a `Sound FX parameters` node sets through `fx params`, on a playing sound too, the node's `volume` multiplies that and its `loop` either leaves it alone or overrules it in either direction; every play is a voice of its own, so two in a row overlap, and the voice ends with the behavior that started it unless `detached` says otherwise), diff --git a/Sounds.md b/Sounds.md index 0a9a92d..57dd9a3 100644 --- a/Sounds.md +++ b/Sounds.md @@ -36,8 +36,9 @@ so what the inspector plays is what the graph will play. | Node | What it does | | --- | --- | -| **Play sound** | Starts the sound named by its `sound` input. `volume` **multiplies** the asset's own volume (1 leaves it alone), `loop` is a choice of three (*From the asset*, *On*, *Off*), `detached` says the voice belongs to nobody, `fx` names a [sound FX](#sound-fx) to play it through, and `fx params` takes values for that FX's [parameters](#parameters). | +| **Play sound** | Starts the sound named by its `sound` input. `volume` **multiplies** the asset's own volume (1 leaves it alone), `loop` is a choice of three (*From the asset*, *On*, *Off*), `detached` says the voice belongs to nobody, `fx` names a [sound FX](#sound-fx) to play it through, and `fx params` takes values for that FX's [parameters](#parameters). Its output `voice` is the number of this one play, for changing it [while it plays](#changing-a-playing-sound); 0 where nothing was played. | | **Stop sound** | Silences every voice of the sound it names, however many plays started it — a loop included. Naming no sound silences everything the run is playing. | +| **Set sound FX parameters** | Hands new values to the [parameters](#changing-a-playing-sound) of one voice that is already playing, by the `voice` number its `Play sound` gave out. | | **Sound** | A constant sound, for either of the two to work on through an edge. | The division is deliberate: the **asset says what a sound is**, the node only colours it. A @@ -122,14 +123,17 @@ the node says why, and the tab's header says it too (otherwise it counts the eff **A Pitch on one way only** works: the voice starts one copy of the sound per pitch, all at the same moment and all of the same length, and each runs through its own way. Two ways at the same -pitch share one copy. A Pitch behind the Mix goes to both of its sounds. A Mix that ends up at -exactly 0 or 1 leaves the silent way out, so that one is not even worked out. +pitch play the same copy, each from a source of its own, so each can move on its own later. A Pitch +behind the Mix goes to both of its sounds. A Mix typed in at exactly 0 or 1 leaves the silent way +out, so that one is not even worked out. A Mix fed by a parameter keeps both ways, because the +value may still change while the voice plays. **Listening in the tab.** The header picks one of the project's sounds and plays it through the graph as it stands — the same library as a running game, so it sounds the way the game will. `Stop` there ends only what the tab started. Play is offered only while the graph compiles. Every parameter has a number field in the header, starting at its default: what Play hands in. -Those values are the tab's own and never written into the asset. +Moving a field while the tab plays is heard at once on that sound. Those values are the tab's own +and never written into the asset. ### Parameters @@ -146,16 +150,40 @@ default the Input gives it. Its `params` output goes into `fx params` of `Play s two differ, `Play sound` warns: *The parameters are for another sound FX*. - Renaming a parameter at the Input renames it in every Sound FX parameters node naming that FX, edges and typed-in values included. A removed one stays on those nodes in red. -- **A value counts from the start of a voice.** Changing it later does not change a sound that is - already playing. +- A parameter keeps its place in the chain even at 0 (a Gain at 0 dB, a band at 0), so a value + handed in later still finds it. + +### Changing a playing sound + +Every play of `Play sound` is a sound of its own, and its output `voice` is the number of exactly +that one. **Set sound FX parameters** takes such a `voice` and an `fx params` bundle from a Sound +FX parameters node, and moves that one voice to the new values: an engine whose pitch follows the +speed, or a loop that gets quieter. + +- **Keep the number in a variable** (a Number) to reach the voice from another event: `On start` + → `Play sound` → `Variables` (set `EngineVoice` from `voice`), then `On update` → `Set sound FX + parameters` with `voice` from `EngineVoice`. Run it every frame, or once when something happens. +- Two plays of the same sound, from one object or from two, are two voices with two numbers, and + each is changed on its own. A number can also be handed to another behavior like any other. +- The number is there at once, before the sound has loaded. A number is never given out twice in + a run, so a kept number of a voice that has ended reaches nothing, never a newer sound. +- A parameter not in the bundle keeps the value it has now, not its default. +- Every value **glides** there in about 0.1 s (`setTargetAtTime`, 30 ms time constant), so a jump + does not click. A value that did not change is not sent again, so running it every frame costs + next to nothing. +- **A pitch changes by speed here.** A pitched copy cannot be worked out again in the middle of a + sound, so the voice plays faster or slower to reach the new pitch: it gets that much shorter or + longer. On a loop that is not heard; the next play gets its own length again. +- A sound that has ended and the `voice` 0 of a play that played nothing are not reached. A + detached voice is reached like any other. A voice still waiting for its bytes starts with the + new values. **In the game** the `fx` input of `Play sound` names the asset. An FX that does not compile, or one that was deleted, plays the sound **dry** rather than not at all: a broken effect should not silence the game. One asset can serve any number of sounds, so "everything in the cave sounds hollow" is one asset and one pick per node. -**Known limits.** There is no `Set volume` on a playing voice, no `Is playing`, no fade and no -positional audio: a sound is as loud as the asset and the node together say, from the moment it -starts. There is no mixer and no master volume — the browser's own is the one. And nothing +**Known limits.** There is no `Set volume` on a playing voice (a Gain fed by a parameter does the +job), no `Is playing`, no fade and no positional audio. There is no mixer and no master volume — the browser's own is the one. And nothing collects sound files a project stopped referencing; they stay on the server, which is what makes deleting a sound asset undoable.