diff --git a/Development.md b/Development.md index 8b581e2..179c42a 100644 --- a/Development.md +++ b/Development.md @@ -19,7 +19,7 @@ php tests/php/accounts.test.php # the API's accoun php tests/php/snapshots.test.php # the snapshot store (#231), the same way php tests/php/releases.test.php # the release store (#266), the same way php tests/php/files.test.php # uploaded files in a folder per account (#344), the same way -node tools/seed-e2e.mjs && node tools/e2e.mjs # 1878 editor checks, screenshots in /tmp/gm-e2e-*.png +node tools/seed-e2e.mjs && node tools/e2e.mjs # 1881 editor checks, screenshots in /tmp/gm-e2e-*.png GM_STEPS=20 node tools/e2e.mjs # or 1-12, or 6,20 — a subset while working on one step ./e2e.sh start [GM_STEPS] # the same run detached, see below ./e2e.sh status # live progress, or the last run's result diff --git a/Instruments.md b/Instruments.md index e99d455..c234f64 100644 --- a/Instruments.md +++ b/Instruments.md @@ -293,7 +293,7 @@ low-pass. | `detached` | As at `Play sound`: the note outlives the object that played it. | | `params` | Values for the instrument's parameters, from an **Instrument parameters** node. | | `tempo` | BPM, what a [Delay synced to beats](Sounds#delay-and-the-tail-of-a-voice) in the instrument counts by. Default 120. | -| `fx` | A [sound FX](Sounds#sound-fx) the note goes through on top of what the instrument does (#346), as through a *Sound FX* node at its Output. Its Pitch moves the note's sound itself, with no latency. | +| `fx` | A [sound FX](Sounds#sound-fx) the note goes through on top of what the instrument does (#346), as through a *Sound FX* node at its Output. *From the asset* (the default) is the row **Sound FX** of the instrument, *None* none (#352). Its Pitch moves the note's sound itself, with no latency. | | `fx params` | Values for that FX's parameters, from a **Sound FX parameters** node. They stay apart from `params`: an FX parameter and an instrument parameter of the same name are two numbers. | | `bus` | The [bus](Sounds#buses-and-the-master) the note is heard on. *From the asset* (the default) takes the row **Bus** of the instrument, which is also what its tab plays on (#325). | diff --git a/Patterns.md b/Patterns.md index 92ec2ca..807c91a 100644 --- a/Patterns.md +++ b/Patterns.md @@ -16,6 +16,7 @@ The notes and the loop are the pattern's own data; everything else is a row in t | **Grid** | What a note is drawn and moved on: 1/1 to 1/32, and the triplets 1/4T to 1/32T. The tab's header has the same choice. | | **Beats per bar**, **Beat** | The meter, any one: 3/4, 6/8, 7/8. It only says where the bars and beats of the ruler are; the notes are kept in ticks, so a pattern changed from 4/4 to 6/8 keeps every note where it was. | | **Tempo (BPM)** | 20 to 400, quarter notes a minute, for hearing the pattern in its tab. A song plays it at its own tempo. | +| **Sound FX** | A [sound FX](Sounds#the-sound-fx-of-an-asset) all its notes go through together, as one chain (#352): `Play pattern`, the tab, and a pattern item of a song that picks none. | | **Bus** | The [bus](Sounds#buses-and-the-master) a `Play pattern` and the tab play it on, *Master* while none is picked (#325). In a song the song's bus counts. | A note is a start and a length in ticks (480 to the quarter note, see @@ -156,7 +157,7 @@ the ruler is only for editing. | `volume` | 0 to 1, how loud every note of it is next to its velocity. | | `loop` | On (the default): round and round until it is stopped. Off: once, and it is over by itself when its last note has ended. | | `detached` | As for a sound: off, the pattern belongs to the object of the behavior and stops when that object is destroyed; on, it outlives it. | -| `fx` | A [sound FX](Sounds#sound-fx) **all of it** goes through as one chain (#346): an LFO runs on across the notes, an echo repeats the whole. A Pitch there is a [shifter of its own](Sounds#a-pitch-on-a-bus), and the notes start as much earlier as it is late. | +| `fx` | A [sound FX](Sounds#sound-fx) **all of it** goes through as one chain (#346): an LFO runs on across the notes, an echo repeats the whole. *From the asset* (the default) is the pattern's own **Sound FX**, *None* none (#352). A Pitch there is a [shifter of its own](Sounds#a-pitch-on-a-bus), and the notes start as much earlier as it is late. | | `fx params` | Values for that FX's parameters, from a **Sound FX parameters** node. `Set sound FX parameters` with the `playback` moves them while it plays. | It gives out `playback`, the number of this playing, and 0 where the pattern has no instrument or diff --git a/Songs.md b/Songs.md index 36e0a86..029deaa 100644 --- a/Songs.md +++ b/Songs.md @@ -16,6 +16,7 @@ inspector. | **Tempo (BPM)** | 20 to 400, quarter notes a minute. The song plays every pattern at this tempo; the tempo set on a pattern is only for its own tab. | | **Beats per bar**, **Beat** | The meter of the ruler. Items are kept in ticks, so a change only moves the bar lines. | | **Grid** | What items snap to: **Bar** (the default), or one of the grids of a pattern, 1/1 to 1/32 and the triplets. The tab's header has the same choice. | +| **Sound FX** | A [sound FX](Sounds#the-sound-fx-of-an-asset) the whole song goes through, after its tracks and the routing, before its bus (#352). `Play song` and the tab play through it. | | **Bus** | The [bus](Sounds#buses-and-the-master) a `Play song` and the tab play it on, *Master* while none is picked (#325). | One tempo and one meter hold for the whole song. A change of tempo within a song would come with @@ -39,8 +40,8 @@ takes both kinds, mixed. | Kind | | |---|---| -| **Pattern** | Plays the pattern's notes. Longer than the pattern, it repeats it; it plays the whole pattern, the loop set on its ruler is for editing. **Transpose** moves every note by semitones (-48 to 48). **Instrument** plays it with another instrument; empty, the pattern's own. A **Sound FX** sends all its notes together through one effect, before the one of the track. | -| **Sound** | Plays the sound as a clip, at its own speed whatever the tempo. Longer than the sound, it repeats it. A **Sound FX** sends it through an effect. | +| **Pattern** | Plays the pattern's notes. Longer than the pattern, it repeats it; it plays the whole pattern, the loop set on its ruler is for editing. **Transpose** moves every note by semitones (-48 to 48). **Instrument** plays it with another instrument; *From the asset* (empty), the pattern's own. A **Sound FX** sends all its notes together through one effect, before the one of the track: *From the asset* (the default) is the pattern's own, *None* none, and one picked takes its place (#352). | +| **Sound** | Plays the sound as a clip, at its own speed whatever the tempo. Longer than the sound, it repeats it. A **Sound FX** sends it through an effect: *From the asset* (the default) is the sound's own, *None* none, and one picked takes its place (#352). | Every item has a **volume** on top of its track's: a sound item from -60 to +12 dB, a pattern item from -60 to 0 dB, since a note is never louder than its velocity makes it. An item whose pattern or sound @@ -89,6 +90,7 @@ right-click menu, not from the inspector. | `Shift`-click an item | Adds it to the pick or takes it out. Dragged on with `Shift`, it is added and moved or sized off the grid instead. | | Right-click an item | Opens its menu: *Delete* deletes it, or every picked item where it is one of them. | | Double-click a pattern item | Opens the pattern in the Pattern tab, with what the song plays around the round clicked into as [ghost notes](Patterns#ghost-notes-from-the-song). | +| Double-click a sound item | Opens the sound in the Sound tab (#352). | | Click **M** or **S** in a head | Mutes or solos the track. | | Click **FX** in a head | A menu of the project's sound FX: one of them for the track, or *No sound FX*. | | Drag along a head's volume or pan | Sets it; while the song plays it is heard as it moves. | @@ -219,7 +221,7 @@ and along its [routing](#routing), on the same scheduler as the tab. | `fade in` | Seconds it rises from silence to `volume`, from its first note on. 0 starts it at once. | | `loop` | On (the default): it plays from its start, and once it gets to the end of the loop on its ruler it goes round that loop until it is stopped. So an intro before the loop is heard once. With no loop set the whole song is the loop. Off: once to its end, and it is over by itself. | | `detached` | As for a sound: off (the default), the song belongs to the object of the behavior and stops when that object is destroyed, which a `Load scene` does unless the object is kept; on, it outlives it. | -| `fx` | A sound FX the whole song goes through, after its tracks and before `volume` and the fades (#346). | +| `fx` | A sound FX the whole song goes through, after its tracks and before `volume` and the fades (#346). *From the asset* (the default) is the song's own **Sound FX**, *None* none (#352). | | `fx params` | Values for that FX's parameters, from a **Sound FX parameters** node. `Set sound FX parameters` with the `playback` moves them while it plays. | It gives out `playback`, the number of this playing, and 0 where the song has nothing on its @@ -250,11 +252,14 @@ A [sound FX](Sounds#sound-fx) can sit on a track, on a pattern item, and on a wh through it **together**. A Phaser's LFO runs on across the notes, a Delay echoes the sum, a Bitcrusher crushes the chord, and it costs one chain however many notes sound. -- **The order:** the notes of a pattern item → its FX → the track's FX → the track's volume and - pan → the links of the [routing](#routing), through other tracks or straight on → the FX of the - `Play song` → its volume and fades → the [bus](Sounds#buses-and-the-master) of the song and the - master. A sound item keeps its own *Sound FX* per - clip as before, then goes on through its track's. +- **The order:** the notes of a pattern item, each through the Sound FX of its instrument → the + item's FX → the track's FX → the track's volume and pan → the links of the [routing](#routing), + through other tracks or straight on → the FX of the song → its volume and fades → the + [bus](Sounds#buses-and-the-master) of the song and the master. A sound item has its *Sound FX* + per clip, then goes on through its track's. +- **Which FX:** an item's FX and the song's are those of the [assets](Sounds#the-sound-fx-of-an-asset) + (the pattern, the sound, the song) unless the item or the `Play song` picks another one or *None* + (#352). In the tab the song's FX sits on a bus of the tab between the output and the song's bus. - A sound FX that is gone or does not compile passes everything on as it is. - Track and item FX play at the defaults of their parameters. Those of a `Play pattern` / `Play song` take `fx params`, and `Set sound FX parameters` moves them by the `playback`. @@ -265,6 +270,11 @@ Bitcrusher crushes the chord, and it costs one chain however many notes sound. Measured (2026-10-07, e2e 65): a sound FX with a Pitch of +12 on the second track, played in the tab at 120 BPM in headless Chrome at 44.1 kHz on the CI's Windows machine: its bus said it was 0.046440 s late (2048 samples), the first note on it was planned for 19.017982 s and started at 18.971542 s, exactly that much earlier, the clip of the first track 2 s before it right on time. Offline, the shifter moved a 1 kHz tone to 2000 Hz, and at 0 semitones handed a click out 2048 samples later at a height of 1.0000001. Stop let the buses of the tab go. +Measured (2026-10-07, e2e 65, #352): the song's own Sound FX with that Pitch, played in the tab, sat +on a bus of its own into the master, 0.046440 s late; the track linked to the output sent to that +bus. Stop let it go. An item's *Sound FX* offered *From the asset*, *None* and the sound FX, and +*None* was stored as `#none`. + ## Not yet A link sends at one volume; it cannot be moved over time, and there is no reverb to send to yet diff --git a/Sounds.md b/Sounds.md index 689113e..2804ced 100644 --- a/Sounds.md +++ b/Sounds.md @@ -34,6 +34,7 @@ signature, so nothing a request claims can decide what is written. | **Reverse** | Plays that stretch backwards, from its end to its start (#321). See [below](#playing-it-backwards). | | **Fade in (ms)**, **Fade out (ms)** | How long the start fades in and the end fades out when that edge lies inside the file, 5 each by default (#345). | | **Fade in curve**, **Fade out curve** | The shape of each fade: *Linear* (the default), *Equal power*, *S-curve*, *Exponential* or *Logarithmic*. See [below](#the-curve-of-a-fade). | +| **Sound FX** | The [sound FX](#sound-fx) it goes through wherever it plays, none while none is picked (#352). See [The sound FX of an asset](#the-sound-fx-of-an-asset). | | **Bus** | The [bus](#buses-and-the-master) it is heard on, *Master* while none is picked (#325). | The preview plays through the very same library a running game uses — same decode, same device — @@ -127,7 +128,7 @@ assets of one file, one of them reversed, give both directions. | 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). Its output `voice` is the number of this one play, for changing it [while it plays](#changing-a-playing-sound), `length` how many seconds one run of the sound lasts, and `tail` that length with the [echoes](#delay-and-the-tail-of-a-voice) of its FX after it, at the values it starts with: how long until the voice is quiet. All three are 0 where nothing was played. `tempo` (BPM, 120 where nothing is connected) is what a [Delay synced to beats](#delay-and-the-tail-of-a-voice) in its FX counts by. | +| **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` is the [sound FX](#sound-fx) it plays through (*From the asset*, the default, takes the one the sound names, *None* plays it without, see [below](#the-sound-fx-of-an-asset)), 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), `length` how many seconds one run of the sound lasts, and `tail` that length with the [echoes](#delay-and-the-tail-of-a-voice) of its FX after it, at the values it starts with: how long until the voice is quiet. All three are 0 where nothing was played. `tempo` (BPM, 120 where nothing is connected) is what a [Delay synced to beats](#delay-and-the-tail-of-a-voice) in its FX counts by. | | **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. With `voice` connected it silences only that one play (the number `Play sound` gave out), and nothing where that is 0. A voice is faded out in 10 ms rather than cut, so it does not click, its echoes with it; **Let ring** stops only the sound and lets the [echoes](#delay-and-the-tail-of-a-voice) fade as they would, and a note of an instrument with an [Envelope](Instruments#envelopes) falls away in its release first. | | **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` or `Play note` gave out, at once or over `time` seconds. It also takes the `playback` of a `Play pattern` or `Play song` and moves the sound FX on all of it (#346). On a note with a sound FX of its own, a name both its instrument and its FX have changes both. | | **Set bus volume** | Sets how loud a [bus](#buses-and-the-master) is, 0 to 1, at once or over `time` seconds along an easing (#325). An empty `bus` is the master. | @@ -228,10 +229,11 @@ sound FX of the bus picked in its `bus` input instead, the master among them. **Pausing** with `Pause game` does nothing to the buses: sounds play on. A game that wants quiet in its pause menu sets a bus volume itself, for instance *SFX* to 0 while *Music* plays on. -**In the editor** the tabs play as the game does: the Sound tab through the bus of the sound, the -Instrument tab through that of the instrument, the Pattern and Song tab through that of the pattern -or song, all of it on through the master. So a compressor on the master is heard while editing, -too. The Sound FX tab plays the sound it tries out on that sound's bus. +**In the editor** the tabs play as the game does: the Sound tab through the sound FX and the bus of +the sound, the Instrument tab through those of the instrument, the Pattern and Song tab through +those of the pattern or song, all of it on through the master. So a compressor on the master is +heard while editing, too. The Sound FX tab plays the sound it tries out on that sound's bus, +through the FX in the tab instead of the sound's own. **A Pitch on a bus or on the master** is allowed. It is late by one frame like every [Pitch on a bus](#a-pitch-on-a-bus), and notes of a song are started that much earlier. @@ -243,6 +245,27 @@ its own with a node graph, like a shader: an **Input** (the sound as `Play sound **Output** (what is heard) and effects between them, wired `out` → `in`. The palette offers the Input and the Output once each; their pins carry sound and fit nothing but each other. +### The sound FX of an asset + +Sounds, instruments, patterns and songs have a row **Sound FX**, right above *Bus* (#352). Empty +is none. What it does depends on the asset: + +| Asset | Goes through its Sound FX | +| --- | --- | +| **Sound** | every voice of it: `Play sound`, an item of a [song](Songs#items), the Sound tab. | +| **Instrument** | every note, after what the [instrument](Instruments) graph does, as through a *Sound FX* node at its Output: `Play note`, every note of a pattern or song, the keys of its tab and of the Pattern tab. | +| **Pattern** | all its notes **together**, as one chain: `Play pattern`, the Pattern tab, a pattern item of a song. | +| **Song** | the whole song, after its tracks and the [routing](Songs#routing), before its bus: `Play song`, the Song tab. | + +`Play sound`, `Play note`, `Play pattern` and `Play song` have an input `fx`, and an item of a song +a row *Sound FX*, with the same choice: **From the asset** (empty, the default) takes the row of the +asset, **None** plays without one, and a sound FX picked there takes its place. It does not come on +top. The Sound FX of an instrument stays on every note of a pattern played with another FX, since +that FX is one step later. + +A sound that is only a source plays without its own: as the Sample of an instrument, as the impulse +response of a Reverb, and as what the Sound FX tab tries out. Its row means nothing there. + | Node | What it does | | --- | --- | | **Gain** | Louder or quieter, in dB (`Gain (dB)`): 0 leaves it alone, -6 is about half as loud, +6 twice. Above 0 the sound can clip. |