Describe the clips of a sound (#357)

- Sounds: a new section Clips. The Sound tab is rewritten for the
  strip of clip bands, the cut works on the picked clip, and Whole
  file is gone.
- Songs, Instruments: a sound field offers the clips of each sound.
- Development: 1897 editor checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
adminandClaude Opus 5.5 committed 2026-10-08 20:18:51 +02:00
1 parent f64e35898c
commit f340ff662b
4 files changed
+79 -37

No files matched your search

+1 -1
@@ -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 # 1894 editor checks, screenshots in /tmp/gm-e2e-*.png
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1897 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
+1 -1
@@ -20,7 +20,7 @@ A at 440 Hz, 127 the highest. A note is named with its octave after it and no da
| Node | What it does |
| --- | --- |
| **Notes** | Where the notes come in. `any` triggers what it is connected to for every note from **From** to **To**. `note` is the number of the note played, `frequency` its pitch in Hz (`440 * 2^((note - 69) / 12)`), `velocity` how hard it is struck, 0 to 1. With **Single notes** there is one trigger output for every note in the range as well, and each can have a **name** of its own (*Name of C2* in the inspector): the output shows it, and so do the keys of the [piano roll](Patterns#the-pattern-tab). |
| **Sample** | Plays a sound asset when its `trigger` is on. With **Follow note** (on by default) it plays higher or lower by the distance from **Root**, the note it sounds as it is (C4 by default), and **keeps its length** — it is played by the same worklet as a Pitch (#317). Off, every note plays it as it is, as for a drum. **Play** says how it is played while the note is held and once it is let go (table below). **Loop start** and **Loop end** are in seconds, 0 at the end being the end of the sound, counted in the stretch the sound is [cut to](Sounds#the-sound-tab). The volume of the sound asset counts. |
| **Sample** | Plays a sound asset when its `trigger` is on. With **Follow note** (on by default) it plays higher or lower by the distance from **Root**, the note it sounds as it is (C4 by default), and **keeps its length** — it is played by the same worklet as a Pitch (#317). Off, every note plays it as it is, as for a drum. **Play** says how it is played while the note is held and once it is let go (table below). **Loop start** and **Loop end** are in seconds, 0 at the end being the end of the sound, counted in the stretch of the [clip](Sounds#clips) when it plays one. The volume of the sound asset counts. |
| **Chip** | Makes a raw wave for every note that triggers it, the way sfxr makes its sounds: square, triangle, saw, sine or noise, with a slide, vibrato, an arpeggio and repeat. It needs no sound file. See [Chips](#chips). |
| **Parameters** | The instrument's own parameters, each a number output with a default — like the parameters of a sound FX's Input. One per instrument. |
| **Sound FX** | Sends the sound through a sound FX asset, as if it were the sound at the FX's Input. Every parameter of that FX is an input here; one left open is the FX's default. The node is titled after the FX, and a double click opens it. |
+11 -7
@@ -91,7 +91,7 @@ right-click menu, not from the inspector.
| Right-click an item | Opens its menu: *Open* opens what it plays (as a double-click does), *Make project asset* or *Embed a copy* (see [Embedded assets](#embedded-assets)), and *Delete* deletes it, or every picked item where it is one of them. |
| Right-click an empty spot of a track | *Create pattern*: a new pattern the song keeps to itself (see [A pattern made in the song](#a-pattern-made-in-the-song)). |
| 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). |
| Double-click a sound item | Opens the sound in the Sound tab (#352). An item playing a [clip](Sounds#clips) opens it with that clip picked (#357). |
| 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. |
@@ -205,7 +205,9 @@ names of the inputs show once recording has been allowed, and the browser keeps
- **Stop**, **Play** or `Space` keep it: it goes up as a WAV file (one channel, 16 bits, at the
rate of the sound device), becomes the sound *Take 1*, 2 … that the song keeps to itself (see
[Embedded assets](#embedded-assets), numbered per song) and lies on its track from where playing
started. Sound, track and item are one undo step. `Esc` throws it away, nothing goes up. Should the
started. The item plays the whole take; to play a part of it, make a [clip](Sounds#clips) in its
tab and pick it in the item's *Sound* row (#357). Sound, track and item are one undo step. `Esc`
throws it away, nothing goes up. Should the
song be deleted while the take goes up, the take becomes a project sound instead.
- It is cut so that what was played along to the song lies where it was heard: the delay of the
output and of the input, as far as the browser tells them, are taken off. What is left over can be
@@ -218,14 +220,16 @@ names of the inputs show once recording has been allowed, and the browser keeps
## Embedded assets
A sound, a pattern or a sound FX can belong to one song instead of the project (#355). A take is
one. It is edited like any other: a double-click on its item opens it in its tab, where *Reverse*,
the cut and the fades work as always, and the tab's title has the song's name in front of its own.
But it has no row in the Assets panel, no picker offers it (not the inspector, not a node), and
its name only has to be free within the song.
one. It is edited like any other: a double-click on its item opens it in its tab, where its
[clips](Sounds#clips) are made and cut as always, and the tab's title has the song's name in front
of its own. But it has no row in the Assets panel, no picker offers it (not the inspector, not a
node), and its name only has to be free within the song. Only the *Sound* row of its own item
offers it and its clips, under a line after the project's sounds.
- **One user.** An embedded asset is used by exactly one place of its song: an item, a track or the
song itself. A split, `Ctrl+D` and a paste give every new item a copy of its own, so setting one
half of a split take to *Reverse* leaves the other half as it was. The audio file is not copied:
half of a split take to another clip leaves the other half as it was. A copy gets copies of the
clips, and an item on a clip plays the same clip of its copy. The audio file is not copied:
files are stored by their content.
- **It goes with its user.** Deleting the item, its track or the song deletes the embedded asset
in the same undo step. A copied item keeps it in the clipboard, so a paste after the delete brings
+66 -28
@@ -30,30 +30,64 @@ signature, so nothing a request claims can decide what is written.
| --- | --- |
| **Volume** | How loud this sound is by itself, 0-1. |
| **Loop** | Whether it is music: it repeats until something stops it. |
| **Start (s)**, **End (s)** | The stretch of the file the sound is, set in its [tab](#the-sound-tab). End 0 is the end of the file. |
| **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 —
so what the inspector plays is what the graph will play, cut and all.
so what the inspector plays is what the graph will play.
These rows hold for the sound **and all its [clips](#clips)**. The sound itself is always the whole
file.
## Clips
A **clip** (#357) is a stretch of a sound's file with a name of its own, as a frame is a cut-out of a
[texture](Textures). A sound has as many as you make, named *Clip 1*, *Clip 2* … at first.
- **Every sound field offers the clips** right after their sound: *Kick*, *Kick / Clip 1*,
*Kick / Clip 2*. That is `Play sound`, `Stop sound`, the Sample of an [instrument](Instruments),
the impulse of a Reverb, the *Sound* of a [song](Songs) item and the test sound of the Sound FX
tab. A clip that is gone reads as its id with a `?`, as a frame does.
- **A clip has its own cut**: start, end, the two fades and their curves, and Reverse. **Volume,
Loop, Sound FX and Bus are the sound's** and hold for every clip of it.
- **Picked in the Sound tab**, its inspector has these rows, a **Delete clip** button below them:
| Row | Meaning |
| --- | --- |
| **Name** | What the fields call it after the sound's name. |
| **Start (s)**, **End (s)** | The stretch of the file the clip is. End 0 is the end of the file. |
| **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). |
- **Delete** (the key, or the button) takes the picked clip away. Whatever names it then reads as
missing and plays nothing, and Ctrl+Z brings it back, as with a frame.
- **A copy of a sound** gets copies of its clips with ids of their own: *Duplicate*, *Embed a
copy* and a song duplicated with the takes it keeps. A song item on a clip then plays the same
clip of the copy.
- The clips are not listed in the Assets panel, only in the fields.
## The Sound tab
Opening a sound (double-click in the Assets panel, or Enter) brings up its tab (#343): the file as
one waveform under a ruler in seconds, and the **stretch of it the asset is** between two
handles. What lies outside is darker. **That stretch is the sample**: `Play sound`, an item in a
[song](Songs) and the Sample of an [instrument](Instruments) all play it and nothing else, and
everything that counts in seconds counts in it — `length`, the rounds of a song item and their
waveform, a Sample's loop points, a Pitch.
one waveform under a ruler in seconds. Under the ruler runs a **strip** with the
[clips](#clips) of the sound as bands, each with its name (#357). A clip is **the sample** for
whatever plays it: `Play sound`, an item in a [song](Songs) and the Sample of an
[instrument](Instruments) play its stretch and nothing else, and everything that counts in seconds
counts in it — `length`, the rounds of a song item and their waveform, a Sample's loop points, a
Pitch.
- **Drag a handle** to move that edge. **Drag anywhere else** to span a new stretch from where you
pressed. One drag is one undo step, the two edges together. An edge cannot pass the other.
- **Whole file** in the header puts both handles back on the ends of the file.
- **Reverse**, the button with the play triangle pointing left, plays the stretch
- **Drag in the strip** to make a new clip from where you pressed. It is picked right away.
- **Click a band** to pick its clip, **click beside the bands** to pick the sound again. The
picked clip's band is lit, and its stretch stands between **two handles** in the wave. What lies
outside is darker. With no clip picked the whole file is bright and has no handles.
- **Drag a handle** to move that edge of the picked clip. **Drag anywhere else** in the wave to
span it anew from where you pressed. With no clip picked, a drag in the wave makes a new clip, as
in the strip. One drag is one undo step, the two edges together. An edge cannot pass the other.
- **Reverse**, the button with the play triangle pointing left, plays the picked clip
[backwards](#playing-it-backwards). It is lit while it is on.
- **Reverse, the fades and their curves** in the header work on the picked clip and are greyed
out while none is picked.
- **Fade in** and **out** in the header, in milliseconds (*Fade in (ms)* and *Fade out (ms)* in
the inspector), are the ramps at the two
edges **inside** the file: the stretch rises from silence over the one and falls back to it
@@ -63,7 +97,8 @@ waveform, a Sample's loop points, a Pitch.
the **curve** of that fade. It shows the curve as a small picture, rising for the fade in and
falling for the fade out; its tooltip names it. The ramps are drawn in the wave, each in its
curve.
- **Play** (or Space) plays the stretch once, at the asset's volume, with a playhead. It ends
- **Play** (or Space) plays the picked clip once, else the whole file, at the sound's volume, with
a playhead. It ends
whatever pattern or song the music tabs were playing, as they end each other. Closing the tab
and starting the game end it.
- The **wheel** zooms about the pointer, the **middle button** pans, as in the music tabs. Zoomed
@@ -74,24 +109,27 @@ waveform, a Sample's loop points, a Pitch.
stops at the edge. The fade ramps grow with the wave. **Fit** brings back the whole file at its
full height.
- A stereo file shows **one** wave, the loudest of its channels; it plays in stereo.
- The header's readout says how long the stretch is, and of how long a file.
- The header's readout says how long the picked clip is, and of how long a file.
**The file is never changed.** The cut lives on the asset, so a stretch can be made longer again
at any time, and **two cuts of one file** are two assets (duplicate the asset, pick another
stretch) that share the file on the server.
**The file is never changed.** The cut lives on the clip, so a stretch can be made longer again at
any time, and **two cuts of one file** are two clips of one sound.
**How it plays.** A cut sound is played by a name of its own,
**How it plays.** A clip is played by a name of its own,
`<file>#<start>~<end>~<fadeIn>~<fadeOut>~<curveIn>~<curveOut>`, with `~reverse` after it when it
plays backwards. The library fetches and decodes the file once
and makes the stretch from it the first time that name plays: a copy of those samples with the ramps worked in. Length and waveform of a cut are read
from the file itself, without a copy. Copies are made again whenever they are needed, so the
library lets the oldest go once they hold more than 64 Mi samples together — a cut dragged about
in its tab leaves no pile of copies behind. A sound over the whole file is no cut at all and plays
the file as it always did; assets saved before the tab existed are such sounds.
in its tab leaves no pile of copies behind. The sound itself, and a clip over the whole file, is no
cut at all and plays the file as it is.
**Song items** keep their place and their length when their sound is cut anew: they play from the
**Song items** keep their place and their length when their clip is cut anew: they play from the
new start, and one longer than the new stretch plays it round and round.
**Before #357** the cut was set on the sound itself. That is not read anymore: such a sound plays
the whole file. The projects were changed over by hand when clips came: every cut sound got a
*Clip 1* with its cut, and everything that named the sound names the clip now.
### The curve of a fade
A fade runs from silence to full. Its curve says how loud it is on the way, at `t` from 0 (the
@@ -111,24 +149,24 @@ start with 5 ms each and *Linear* on both.
### Playing it backwards
**Reverse** (#321) turns the stretch around. The handles keep marking the same part of the file
**Reverse** (#321) turns the stretch of a clip around. The handles keep marking the same part of the file
and the wave is drawn as it is. Only the order changes: the end handle is heard first, the start
handle last, and the playhead of the tab runs from right to left.
- **The fades go with what is heard.** *Fade in* is the ramp at the end handle, because that is
where the sound starts now. *Fade out* is the ramp at the start handle. The wave draws them
there. As before, an edge on the start or the end of the file gets no ramp.
- **Everything that plays the sound plays it backwards**: `Play sound`, a song item, the Sample
- **Everything that plays the clip plays it backwards**: `Play sound`, a song item, the Sample
of an instrument, a Pitch in a sound FX. The reversed stretch is made once, like any cut, and
the FX work on it as on any other sound.
- **The length stays.** `length` and the rounds of a song item are as long as forwards. The
waveform of a song item is drawn backwards, as it is heard.
- A sound over the whole file with Reverse on is a cut of its own, the whole file backwards.
- A clip over the whole file with Reverse on is a cut of its own, the whole file backwards.
There is **no Reverse node** in a sound FX. Playing backwards needs the whole sound in advance.
A [bus](Songs#sound-fx-on-tracks-items-and-whole-songs) only has what is sounding right now, and a
Chip has no end until its note decides. The switch on the asset covers the sound files, and two
assets of one file, one of them reversed, give both directions.
Chip has no end until its note decides. The switch on the clip covers the sound files, and two
clips of one stretch, one of them reversed, give both directions.
## Playing it