Commit Graph
18 Commits
Author SHA1 Message Date
adminandClaude Opus 5.5 8c0d1cfd3f Snap the routing graph of a song with the header's magnet (#350)
The routing of a song is the same graph editor as a sound FX, but its
tab had no magnet, so its nodes could not be put on the grid.

- The Song tab carries the magnet next to Routing. It is off at first
  and acts on the routing graph.
- It is greyed out on the timeline, as Grid is greyed out in the
  routing. Switching the view only updates these two buttons, so a
  playing song is not handed its sequence again.
- The check hooks give the magnet and the snapping of the routing.

Tests:
- e2e 65 (melektaus-atx): ALL PASSED, 52 checks (1 new)
- Counter-check (magnet enabled on the timeline): exactly this check red
- Gate (melektaus-atx): CHECK EXIT 0, 270 files, 4178 tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 12:13:23 +02:00
adminandClaude Opus 5.5 668682c4ff Give a song, a track or an item a sound FX of its own (#355)
A sound FX for one place in a song had to be a project asset, which
then stood in every picker. Now every pick of a sound FX in a song
can make one the song keeps for that place alone, on top of the
embedded assets of the first two parts.

- Create sound FX sits at the end of the Sound FX row of the song,
  a track and an item, and of the menu of a track's FX button. It
  makes "Sound FX 1", 2, … per song with the Input wired to the
  Output, picks it and opens it. That is one undo step.
- Embed a copy, while the place names a project FX, gives the place
  a copy under the same name.
- While the place names its own, the menu shows it with the mark and
  offers Edit sound FX and Make project asset.
- Picking anything else lets it go in the same undo step. For a
  track or an item `settleEmbedded` did that already. For the song's
  own row `AssetPropertyCommand` now settles the song after `fx`.
- The mark `•` moved to the core. `assetRefLabel` puts it in front of
  an embedded asset, so the inspector rows show it like the timeline.
- `FieldHandlers.moreChoices` lets a caller end a reference menu
  with entries of its own. `embeddedPatternName` is `embeddedName`
  with a base now.

Tests:
- Unit own-fx (new): 6 green, 6 counter-checks each red
- Unit embedded and 80 neighbouring files: 1434 green
- e2e 65 (melektaus-atx): ALL PASSED, 51 checks (2 new)
- Gate (melektaus-atx): CHECK EXIT 0, 270 files, 4178 tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 11:32:41 +02:00
adminandClaude Opus 5.5 49669017b1 Make a pattern right in a song with a right-click (#355)
A pattern for one song had to be made in the Assets panel first and
dragged in, and then stood in every picker of the project. Now a
right-click on an empty spot of a track makes one the song keeps to
itself, on top of the embedded assets of the first part.

- Create pattern lays an item from the grid line before the spot,
  a bar long, and opens the pattern in its tab. Pattern, item and
  name are one undo step. The name is "Pattern 1", 2, … per song.
- It plays the instrument of the track's pattern item that starts
  last before the spot, else of the first one after it. An item's
  own pick counts before its pattern's.
- An embedded pattern runs on the song's clock. It takes the tempo
  and the meter of the song, and a change on the song moves it along
  in the same undo step (`AssetPropertyCommand`).
- Its rows Tempo, Beats per bar, Beat and Bus are hidden, as the
  song decides them. Length, grid, instrument and sound FX stay.
- A copy that comes to a song (Embed a copy, a paste from another
  song) takes that song's clock. Made a project asset, it keeps the
  values it had.
- `CreateAssetCommand` takes an owner. `embeddedCopy` takes the song
  now, not its id.
- The cleanup of e2e 65 also takes what the song keeps.

Tests:
- Unit embedded: 18 green (5 new), 6 counter-checks each red
- Unit take, song, pattern and the asset commands: green
- e2e 65 (melektaus-atx): ALL PASSED, 49 checks (2 new)
- Gate (melektaus-atx): CHECK EXIT 0, 269 files, 4172 tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 10:54:40 +02:00
adminandClaude Opus 5.5 23cb34f781 Keep a recorded take inside its song (#355)
A take used to land in the Assets panel as "Song – Take N", next to
every other sound of the project. Now a song can keep a sound, a
pattern or a sound FX to itself: an ordinary asset whose new field
`owner` names the song. Everything that finds an asset by its id
finds it, so playing, the tabs and the game need no change. Only
the places that list assets leave it out.

- `core/music/embedded.ts` holds the rules: which types can be
  embedded, `listedAssets`, the title "Song › Take 1", and the
  rows an embedded sound hides (Loop, Bus).
- One user per embedded asset. `SetSongCommand` settles the song
  after every edit: a place naming an asset an earlier place
  claimed, or one of another song, gets a copy, and what nothing
  names any more goes in the same undo step. The outcome is worked
  out once, so a redo brings back the same copies.
- A split, Ctrl+D and a paste get that for free. The clipboard
  keeps the embedded assets of the copied items, so a paste after a
  delete brings the take back.
- Deleting a song deletes what it keeps. Duplicating it copies
  that and points the copy at the copies. A duplicate of an
  embedded asset is a project asset.
- The item menu has Open, Make project asset and Embed a copy.
- An embedded item carries a bullet in front of its name. The
  first pick was a diamond, which the gate's font test refused:
  the UI font has no glyph for it.
- A take is "Take 1", 2, … numbered per song. With the song gone
  while it uploads, it becomes a project sound under a free name.
- Left out of: the Assets panel, every asset picker, the sound FX
  menu of a track, the test sound of the Sound FX tab and the
  naming of new assets. Delete on an embedded asset only says
  where it lives. A ping lights up its song.
- Loading drops an `owner` whose song is gone, the asset stays as
  a project asset. No migration: older projects have no owner.

Tests:
- Unit embedded (new): 13 green, 6 counter-checks each red
- Unit take, song, song-edit, asset-fx, asset-ref, serialize and
  neighbours: 116 green
- e2e 65 (melektaus-atx): ALL PASSED, 47 checks (1 new)
- Counter-check e2e 65 (asset panel unfiltered): exactly that
  check red
- Gate (melektaus-atx): CHECK EXIT 0, 269 files, 4167 tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 10:36:25 +02:00
adminandClaude Opus 5.5 a295d233ed Give sounds, instruments, patterns and songs a sound FX (#352)
A sound FX could only be picked where something was played: on a Play
node, a track or an item of a song. A sound that should always sound
through the same effect needed that pick everywhere it was used. Now
the asset says it once, and every place that plays it follows.

- New row Sound FX on a sound, an instrument, a pattern and a song,
  right before Bus. A sound goes through it on every voice, an
  instrument on every note after its graph, a pattern on all its
  notes together and a song on its output.
- The fx input of Play sound, Play note, Play pattern and Play song
  and the Sound FX of a song item now read "From the asset" while
  empty. None is a pick of its own, stored as `#none` like `#master`.
  A pick replaces the FX of the asset, it does not come on top.
- A pattern item's Instrument reads "From the asset" instead of None.
  The meaning is unchanged.
- A double-click on a sound item opens the sound, as one on a pattern
  item opens the pattern.
- The tabs play through the FX of their asset. The Pattern and Song
  tab put it on a bus of their own between the output and the bus of
  the asset. The keys of the Pattern tab play there too.
- Notes of a pattern or song go through the FX of their instrument.
  Its chain is loaded with the other needs of the sequence.

Decided without asking:
- Old items and Play nodes with an empty fx now mean "From the
  asset". No asset has an FX yet, so nothing sounds different.
- The label is "From the asset" as on Loop and Bus, not "From asset".
- A sound used as a Sample, as the impulse of a Reverb or as the test
  sound of the Sound FX tab plays without its own FX.

Findings:
- In e2e, `__gm.inspectorField('Sound FX')` and `('Instrument')` find
  the group of the Assets panel first. The check asks for the last
  match.

Tests:
- Unit asset-fx (new): 8 green. Six counter-checks each turned red.
- Field lists of sound, pattern and song: Sound FX added.
- e2e 65 (melektaus-atx): ALL PASSED, 46 checks (3 new). Counter-check
  without the FX bus of the tab: that check red.
- Gate (melektaus-atx): CHECK EXIT 0, 268 files, 4142 tests.
- Not checked: how it sounds, the Sound, Instrument and Pattern tab
  in the browser.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 22:02:26 +02:00
adminandClaude Opus 5.5 daeffe0f9d Route the tracks of a song through a graph (#350)
game-maker / gate (push) Successful in 3m47s
game-maker / suite (push) Failing after 14m20s
Every track of a song went straight into its output. Now the Song tab
has a second view, Routing: a graph with a node per track and one
Output, the links between them sends. A track can go into other
tracks, into several at once, and a link has its own volume and the
point of the track it taps.

- The song asset has a graph like an instrument (SongTrack, SongOutput).
  heldSongGraph keeps it in step with the tracks on load and in
  SetSongCommand: a node per track named after it, one output, a new
  track and every track of an old song linked to the output. Links of
  gone tracks, doubled links and links closing a loop are dropped.
- A link carries an optional send (volume in dB, tap). The inspector
  shows Volume and Mode for a picked link of a song: Post volume (Post
  pan), Pre volume (Post FX), Pre volume (Pre FX). SetSendCommand.
- NodeDef has two new flags. `fixed` nodes are never added or deleted
  by hand. Links between `acyclic` nodes may not close a loop, checked
  in canConnect, dropTargetPort and ConnectCommand.
- songBuses gives each track its sends instead of one `into`, and a
  bus `song:<song>:<track>:#items` for its own items. That bus is what
  solo closes on a track that only passes a soloed one on.
- Mute and solo now act on the sends (trackGates). A muted track passes
  nothing on, on any tap. A soloed track is heard with what goes into
  it, through the tracks it goes through, their own items silent.
  Only sends on a way through a soloed track stay open, so a track
  going into it and into the output is heard through it only.
- SoundLibrary: a bus has either `into` or sends. Each send is tap,
  delay, level, target. busLatency takes the latest way (arrivalOf),
  and each send is held back so all ways of a track arrive together
  (sendDelays). Taps are wired again whenever a chain comes or goes.
- The Song tab has Timeline and Routing in its header. The routing
  uses GraphCanvas with an empty palette. A track linked nowhere shows
  "Goes nowhere". Delete takes links, never a track node.
- Game playings route the same way, the output being the master of
  the playing.

Decided without asking: a send ranges -60 to +12 dB like a track,
default 0 dB Post. Mute closes the sends, the track's own level stays.

Found on the way: the inspector built the Mode row with the send as it
was when the rows were made. Setting Volume and then Mode wrote the old
volume back. Both rows now read the current send when they change.

Tests:
- Unit song-routing (new): 13 green. Sound library: 4 new, 94 green.
  11 counter-probes (loop on load, canConnect, solo, solo past the
  soloed track, mute, preFx latency, graph sync, delete guard, delays,
  taps, glide), each red.
- Existing tests adjusted to the new bus names and send shape: song,
  bus-fx, song-play, graph registry, edge description.
- e2e 65 on melektaus-atx: ALL PASSED, 43 checks (5 new). The first
  run caught the inspector bug. Counter-probe with the old closure:
  2 FAILED.
- Gate on melektaus-atx: typecheck, lint, format clean, 2 tests red
  (registry rule, edge shape), fixed and rerun locally: 56 green.
- Not rerun after the per-send solo fix: e2e 65 and the gate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 20:18:09 +02:00
adminandClaude Opus 5.5 3a52f2290f Mix sounds through buses and a master (#325)
game-maker / gate (push) Successful in 3m36s
game-maker / suite (push) Failing after 14m30s
Until now every sound went straight to the speakers. There was no
point where all of them came together, so nothing could turn the
music down against the effects, and a compressor or a shared echo
over everything had nowhere to sit. The bus technique of #346 was
there already, only the end of the chain was missing.

- Bus asset (Audio): Volume, Sound FX and Into. Into is empty for
  the master and never offers the bus itself nor a bus that already
  goes into it. The list is worked out when the menu opens, so a
  change made meanwhile counts. A loop that is in a project anyway
  sends the buses on it into the master.
- The master is no asset. Its volume and sound FX are two new rows
  under Master in Project settings, one undo step each. A project
  written before has 1 and none, which is how it sounded.
- Sound, instrument, pattern and song have a row Bus, empty for the
  master. Play sound, Play note, Play pattern and Play song have an
  input bus: From the asset, Master or a bus. Inside a song or a
  pattern only the bus of the song or pattern counts.
- Set bus volume (bus, volume, time, easing) and Bus volume. An
  empty bus is the master. The volume set holds for the whole run,
  across scenes, over that of the asset. GameRun resets it at start
  and end. Set sound FX parameters without a voice now reaches the
  sound FX of the bus picked in its new bus input.
- SoundLibrary: the master is the bus "mix", each bus "mix:<id>".
  Every bus, the playing masters of #346 included, ends there, and
  only the master is heard. setMix sets them up once a context
  exists, so an editor nobody listened to opens no audio device. The
  same mix again changes nothing, which lets the editor hand it over
  on every change of the project.
- The tabs play as the game does: Sound, Sound FX, Instrument,
  Pattern and Song tab through the bus of their asset and the master.
- Pause game does nothing to the buses (the user's answer 4b). A
  game that wants quiet in its pause menu sets a bus volume itself.
- Streaming long music files is #349, split off as asked.

Finding on the way: the Into menu first worked from a list made when
the inspector row was built. After another bus was sent into the
open one, e2e 34 saw it still offered. It is now asked on open.

The SoundLibrary tests expected voices to end at the destination.
They now end in the master, which adds its input, level and pan to
every wiring and two gains to every list. Those expectations are
updated, nothing else in them changed.

Tests:
- Unit buses (new): 15 green. Ten counter-checks, each red on its
  own: loop check, bus picked, reset at run start, bus of Set sound
  FX parameters, master volume in the settings, and in SoundLibrary
  default into the master, voices into the master, run volume over
  the mix, no context before a play, a playing on its bus.
- Unit sound-library: 90 green (5 new). Node catalog: 416 green
  (9 new cases).
- e2e 34 (melektaus-atx): ALL PASSED, 75 checks (3 new). The voice
  of the Sound tab was heard on mix:<bus>, that bus into the other,
  that one at 0.5 into the master. Master volume 0.6 via the dialog,
  undo back to 1.
- Gate (melektaus-atx): CHECK EXIT 0, 262 files, 4072 tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 19:13:32 +02:00
adminandClaude Opus 5.5 d505f6ed27 Show what the song plays around a pattern as ghost notes (#339)
A bass line is written against the chords it goes with. Until now the
piano roll showed only the pattern itself, so writing a part to fit the
rest of a song meant switching between the Pattern and the Song tab.

- A double-click on a pattern item remembers song, item and the round
  clicked into for that pattern. The place lives in the session, not in
  the project, and goes by the item's id, so moving the item moves the
  ghost notes along.
- Opened from the Assets panel, the pattern shows the item of the last
  double-click. Without one, a pattern that lies in exactly one item of
  all the songs shows that item's first round.
- Ghost notes are the pattern items of the other tracks all along the
  song, and on the own track the items before and after and the other
  rounds of the item. The round edited itself does not count. Sound
  items have no notes.
- A ghost note sits on the row it sounds with: its own transpose, less
  the transpose of the item the pattern was opened from.
- The roll draws them grey, without an edge, under the notes. Resting
  on one names its track and its pattern.
- The header has a Ghosts switch, on by default, and names where they
  come from, e.g. "Tune · Bass · bar 5".
- The time axis pans back to the start of the song, with bar and grid
  lines before the pattern. Fit still frames only the pattern.
- A click or a paste outside the pattern no longer lands. Before, only
  the time left of bar 1 was closed, notes could still be drawn past
  the end, where they are kept but never played.
- The rounds of a pattern item are now worked out in one place,
  itemNotes() in song.ts, which songSequence() and the ghost notes
  share.

Tests:
- Unit ghosts (new): 5 green. Counter-check with the round edited
  not left out and the own transpose not taken off: 2 red.
- Unit piano-roll: 1 new (pan back to the song, lines before bar 1).
  Counter-check without the song's start: red.
- e2e 65 (melektaus-atx): ALL PASSED - 38 checks (3 new). The first
  run was red on the tooltip check: the ghost note it rested on lay a
  fifth up, outside the rows Fit frames. The test now uses a tone.
- e2e 64 (melektaus-atx): ALL PASSED - 30 checks.
- Gate (melektaus-atx): CHECK EXIT 0, 260 files, 4027 tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 15:06:32 +02:00
adminandClaude Opus 5.5 c33d6d98e5 Keep a song item's left edge from snapping on to its own end
Dragging the left edge of a song item to the right snapped to the grid
and to the points where the item's content starts again. One of those
points lies exactly on the item's end. When the edge caught it, the
item shrank to a single tick and showed as a thin white line. The
right edge already refused to snap back on to its own start, the left
edge had no such rule.

- trimDelta: a snapped start on or past the item's end now takes the
  last grid line before the end instead.
- If that grid line lies before the current start, the start stays
  where it is.
- Free trimming with Alt or Shift is unchanged and may still shrink
  the item to one tick.
- Dragging the left edge far past the end now leaves one grid step,
  as the right edge already did, instead of one tick.

Tests:
- song-edit unit tests: 14 green (2 new expectations, 1 changed)
- Counter-check with the rule removed: 2 red (1399 instead of 960,
  1919 instead of 1800)
- Prettier, ESLint, tsc clean

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:00:58 +02:00
adminandClaude Opus 5.5 b519cd192f Send a track, a pattern item or a whole song through a sound FX (#346)
A sound FX could only be put on a single sound. Now a track, a pattern
item, a Play pattern and a Play song send everything they play through
one chain of it, as an insert in a DAW. A Play note gets one of its own.

- Track: an FX box in the head next to M and S opens a menu of the
  project's sound FX, and the track's inspector has the row too.
  Order: items, track FX, volume, pan.
- Pattern item: Sound FX in its inspector. Its notes play on a bus of
  their own into the track's bus.
- Play pattern and Play song take fx and fx params. The chain sits on
  the master of the playing, after the tracks and before the level and
  the fades. Set sound FX parameters reaches it by the playback.
- Play note takes fx and fx params next to the instrument's params.
  The FX parameters go by "fx:" so the two sets stay apart. Set sound
  FX parameters on a note changes a name both have in both.
- A Pitch on a bus is a new PitchShifter: the phase vocoder of the
  PitchPlayer, fed while it plays. It cannot look ahead and is exactly
  one frame late at every pitch, 2048 samples.
- That latency is made up. The other way of a Mix is held back by a
  delay of the same length. Every note and clip starts as much earlier
  as the buses it passes are late. The scheduler plans that much
  further ahead, and the first note waits that much after Play.
- The Song tab waits for the chains before it plays and lets its buses
  go a moment after Stop. A playing in a game lets them go after its
  echoes and what the shifters still hold.
- songBuses moved to its own file. Kept in song.ts it pulled the FX
  compiler into the node registry through an import cycle.

Decisions of the user: one chain for the whole (1a), FX everywhere
asked for (2a), Set sound FX parameters on playbacks (3a), and a
pitch shifter of its own with exact latency compensation for a Pitch
on a bus.

Tests:
- Unit pitch-shifter (new): 11 green. At 0 semitones the output is
  the input 2048 samples later, off by at most 1.2e-7.
- Unit sound-library: 84 green, 8 new. Counter-checks (no early
  start, no Mix delay, chain made every time, tune misses the master)
  each turned their test red.
- Unit bus-fx (new): 6 green, 4 counter-checks red. Scheduler ahead: 1
  new, its counter-check red.
- e2e 65 (melektaus-atx): ALL PASSED, 35 checks, 4 new, first run.
  Chrome at 44.1 kHz: the bus was 0.046440 s late, the note started
  exactly that much early. Offline the shifter moved 1 kHz to 2000 Hz
  and passed a click 2048 samples later.
- Gate (melektaus-atx): CHECK EXIT 0, 259 files, 4021 tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:15:52 +02:00
adminandClaude Opus 5.5 f1f4a57589 Cut a sound to a stretch of its file in its own Sound tab (#343)
game-maker / gate (push) Successful in 3m32s
game-maker / suite (push) Failing after 18m20s
A sound asset now opens in a tab that shows its file as a waveform.
Two handles set where the sound starts and ends in the file, and that
stretch is the sample: Play sound, a song item and an instrument's
Sample all play it and nothing else. The file itself is never changed.

- The asset has three new rows: Start (s), End (s) and Edge fade (ms).
  End 0 is the end of the file, the fade is 10 ms by default. Older
  assets have no values and play the whole file as before.
- Every way of playing asks playedSoundOf() for a name instead of the
  file: the file, or <file>#<start>~<end>~<fade> for a cut.
- SoundLibrary makes the cut from the decoded file the first time that
  name plays and ramps each edge inside the file over the fade. Length
  and peaks of a cut are read from the file without a copy.
- Cut buffers can be made again at any time, so the library lets the
  oldest go past 64 Mi samples. A voice holds its buffers itself.
- The tab draws min/max per pixel from 256-sample block peaks, and the
  samples as a line when zoomed in far. Wheel zooms, the middle button
  pans, as in the music tabs. Wave, cut and ruler sit under a mask,
  so a handle panned out of view does not draw over the panels beside.
- Dragging a handle moves that edge, dragging elsewhere spans a new
  stretch. One drag is one undo step for both edges. Whole file puts
  the handles back, Play or Space plays the stretch with a playhead.
- The inspector preview and the Sound FX preview play the cut too, and
  the song lanes draw and repeat it.

Tests:
- Unit: sounds 20, wave-view 8 (new), sound-library 68, plus 258 in
  the touched areas (instrument, song, pattern, inspector, tabs)
- e2e 34 on melektaus-atx: ALL PASSED, 57 checks (7 new)
- Gate on melektaus-atx: CHECK EXIT 0, 253 files, 3936 tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 21:42:21 +02:00
adminandClaude Opus 5.5 2fdf97f7de Record a take from the microphone in the Song tab (#342)
game-maker / gate (push) Successful in 3m32s
game-maker / suite (push) Failing after 15m51s
A song could only be filled with sounds that were made somewhere else.
Record plays the song from the cursor and takes the input meanwhile, so
a part played along lands on the bar it was played to.

- An audio worklet takes the input's samples on the clock the song
  plays on. Each run carries the frame it was taken at, so the take is
  cut where the song was heard to start. The output's and the input's
  latency are taken off, as far as the browser reports them.
- The take goes up as mono 16-bit WAV. Sound, a new track "Recording"
  under the track clicked last and the item at the cursor are one undo
  step. The sound is named "Song – Take 1", 2, 3 and so on.
- While recording the take grows red with its waveform on its track,
  and a meter beside the input dropdown shows the level. The lanes take
  no edits then, only a middle-drag pans.
- The loop is not used while recording. The song plays once through
  and on past its end until Stop, Play or Space keep the take. Esc
  throws it away and nothing goes up.
- Echo cancelling, noise suppression and auto gain are off, nothing is
  played back. The input picked is kept in localStorage.
- A take stops by itself at the size a sound may have. A failed upload
  offers Retry, Download WAV and Discard.
- The API takes sounds up to 256 MB and bodies of the same size. nginx
  and PHP were raised to match outside the repo (body 256m, upload and
  post 256M, memory_limit 512M).
- The pure rules (cut, cap, WAV header, name, track) live in
  core/music/take.ts. The scheduler tells where it started on the
  audio clock.
- The e2e browser gets chromium's fake microphone and skips the
  permission prompt. Each run leaves its take in the sound store.

Found on the way:
- The header was too full with the new widgets and cut the track and
  item count on the right. Input dropdown and meter are narrower now.

Tests:
- Unit take.test.ts: 7 new, svg-icons adjusted to 8 toolbar icons
- e2e 65 (melektaus-atx): ALL PASSED – 31 checks (+4)
- Gate (melektaus-atx): CHECK EXIT 0, 252 files, 3923 tests
- Probe on stein2: a 3 s take of the test tone, peak 32767,
  285100 bytes, waveform seen in the screenshot

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 20:58:17 +02:00
adminandClaude Opus 5.5 5fde3e975e Reset a track slider with a double-click in the Song tab
game-maker / gate (push) Successful in 3m30s
game-maker / suite (push) Successful in 16m37s
The heads of the Song tab had four rough spots. Panned down, the
first track had no line above it. The M and S letters sat high in
their buttons. A volume or pan slider could only be dragged back to
the middle by eye. Muting a track paled its volume slider.

- The first track draws a border along its top, in the lanes and in
  its head, like the one below every track.
- M and S sit in the middle of their buttons. A label of the heads
  can now carry a height and is centred in its box.
- A double-click on a head's volume or pan sets it back to 0: the
  volume to 0 dB, the pan to the middle. It is one undo step of its
  own after the step the first click set.
- Mute only lights its own button. The volume slider keeps its
  colour, so a muted head no longer looks out of step.

Tests:
- e2e 65 on melektaus-atx: ALL PASSED, 27 checks (+1)
- typecheck, ESLint, Prettier: clean (also after the mute change)
- screenshot on stein2: border above track 1, M and S with 4 px free
  above and below the letter in a 16 px button

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 19:36:07 +02:00
adminandClaude Opus 5.5 5b96d0b329 Paste under the pointer and pull song items out left in music tabs
game-maker / gate (push) Successful in 3m25s
game-maker / suite (push) Successful in 16m33s
A round of fixes to the Pattern and Song tabs: where a paste lands,
what Shift does on a drag, how far the rows zoom, and how an item of
a song is sized at its ends.

- Shift does what Alt does on a drag: off the grid, on the tick. A
  Shift click on a note or an item still adds or unpicks it. A Shift
  press only decides on letting go: no drag toggles, a drag moves.
- Ctrl+V lands under the pointer. The earliest copy goes on the grid
  line before it, the highest note on its row or the topmost item on
  its track, held to the keyboard or the tracks there are. Off the
  grid or left of bar 1 it lands at the start of playing as before.
- The rows of the piano roll zoom to 64 px instead of 28, the tracks
  of a song to 320 px instead of 160.
- Fit in the Pattern tab zooms the rows as well, so the notes fill
  the height, no longer held to the default of 14 px.
- An item of a song can be pulled out by its left end past the start
  of what it plays, which then repeats. The offset goes round to the
  end of a round. A sound not loaded yet still stops at its start.
- Both ends of an item snap to the start of a round of what it plays
  as well as to the grid, whichever is nearer. The end of a round
  between two ticks is cut to the tick before. A start put within
  half a tick of a round goes right on it, so it plays from the very
  beginning.
- An offset past a whole round is not folded back to 0, so a pattern
  that grows later still plays from where the item said.
- A middle-drag pans past the first and the last row, as it pans
  left of bar 1: the highest note or the first track up to half the
  height down, the other end up to half the height up. The room past
  them is dark in the piano roll and empty in the song.

Tests:
- Unit: song-edit 14, piano-roll 19, song 14 tests. New cases for
  the paste spot, pulling out left, the round snap and Fit.
- e2e 64+65 on melektaus-atx: ALL PASSED - 54 checks (+4). New:
  Shift drag and Shift click, Ctrl+V under the pointer in both tabs,
  Fit zooming the rows, pulling a pattern out left and snapping a
  sound to the end of its second round. e2e 64 zooms the rows out
  again after Fit, so the keys it plays later are on screen.
- Gate on melektaus-atx: 251 files, 3912 tests, CHECK EXIT 0.
- Then the pan down the rows: e2e 64 on melektaus-atx ALL PASSED -
  30 checks. Its pan check now also pans the top row down, half the
  grid's height, and a click above it draws nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 19:14:28 +02:00
adminandClaude Opus 5.5 a3101c590c Size notes by either end and pan before bar one in music tabs
A round of small fixes to the Pattern and Song tabs: what the ruler
and a pan do in both, and a few things the tabs carried that were in
the way.

- A middle-drag can take bar 1 up to half the grid or lanes in. The
  time before it is drawn dark on the ruler, the grid and the lanes.
  Nothing lands there: a click draws no note, a drop shows no frame.
- A press or drag on the ruler puts the start on the nearest grid
  line, no longer on the one before the pointer.
- A new song has one track instead of four. The "+ Track" row under
  the heads is gone, a head's menu adds tracks above or below. A song
  still keeps at least one, so that menu is always there.
- A note can be sized by its left end too: the start goes to the
  nearest grid line and the end stays put, never before tick 0.
- An end of a note under the pointer lights up and shows a resize
  cursor, as on an item of the Song tab.

Tests:
- Unit: piano-roll, song-edit, song, song-play 52 tests. Two new
  cases for the left end and the pan, the song fixtures add their
  tracks themselves now.
- e2e 64+65 on melektaus-atx: ALL PASSED - 50 checks (+2).
  e2e 65 adds its tracks through the head's menu and clicks the
  ruler three quarters into a bar, which only the nearest line
  passes.
- Gate on melektaus-atx: 251 files, 3909 tests, CHECK EXIT 0.
- A probe shot of the piano roll panned in: the time before bar 1
  dark, the left end of a note lit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 18:40:13 +02:00
adminandClaude Opus 5.5 aa097bd076 Zoom with the wheel and set the loop on its own strip in music tabs
The ruler of the Pattern and Song tabs did two things with one drag,
and the wheel did three things with Alt and Shift. Both now do one
thing each, and the Song tab gets a few edits it was missing.

- The ruler has a loop strip along its top. A drag there sets the
  loop, a press or drag below it moves only where playing starts.
- The wheel only zooms: the time over the grid, lanes and ruler, the
  rows over the keys, the tracks over their heads. Alt and Shift no
  longer change it. A middle-drag still pans.
- Tracks can be zoomed from 28 to 160 px. Below 46 px a head shows
  only its name, M and S. An old saved view gets the old height.
- A click on nothing in the lanes picks the song itself.
- A right-click on an item opens a menu with Delete instead of
  deleting it. The inspector has no delete buttons any more.
- A sound item goes up to +12 dB, a track up to +12 dB. A pattern
  item stays at 0 dB, since a note is held to unity.
- An end of an item under the pointer lights up and shows a resize
  cursor. A long item marks every point where it starts over.
- The Pattern tab has an Audition switch in its header, off at first.
  Only with it on does a note sound when drawn, grabbed or moved.
  The keys down the left sound either way.
- A looped sound item draws every round alike. Each round is drawn
  from its own start, and each column shows the loudest part of the
  sound it covers instead of one sampled bucket.
- A song is at least one bar long instead of four. An empty song
  fits that bar across, and Play song ends after the last item.
- The ghost notes held back in #334 got their own issue, #339.

Finding: suite 542 for f97961c failed 5 game checks in e2e 64 and 65
while my runs on the same Windows machine overlapped it. Modules
60-65 alone without load passed, so this push's suite has to show it.

Tests:
- Unit: song-edit 11, piano-roll 17, song 14, sound-library 67.
  Four of them are new, one in song changed.
- e2e 64+65 on melektaus-atx: ALL PASSED - 46 checks (+3).
- e2e 60-65 on melektaus-atx: ALL PASSED - 86 checks.
- Gate on melektaus-atx: 251 files, 3907 tests, CHECK EXIT 0.
- Audition, after the gate: e2e 64 alone ALL PASSED - 25 checks
  (+1), transitions and piano-roll 30 tests, typecheck and ESLint
  clean.
- Waveform and one bar, after the gate: song, song-edit, song-play
  and the node catalog 438 tests. e2e 65 alone ALL PASSED - 25
  checks (+1). A probe shot of a looped pop sound: all six rounds
  pixel for pixel alike.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 18:25:49 +02:00
adminandClaude Opus 5.5 f97961c8ff Play and stop a song from a behavior in the game #336
game-maker / gate (push) Successful in 3m35s
game-maker / suite (push) Failing after 16m36s
A song could only be heard in its tab. Now a game plays it through
Play song and ends it through Stop song, with its tracks as they are
set. A playing pattern or song also no longer undoes the pause of the
toolbar.

- Play song plays from the start of the song and then round the loop
  of its ruler, so an intro is heard once. Without loop it plays once
  to its end. Inputs are song, volume, fade in, loop (on by default)
  and detached (off by default). It gives out playback.
- Stop song stops one song, every song for none, or one playback by
  its number, over its fade out. Stop sound with no sound named stops
  songs too. Stop pattern with no pattern named stops only patterns.
- Songs play side by side, as patterns do. They are owned like a
  sound, so leaving the scene ends one unless it is detached. Pause
  game and the time scale leave the music alone.
- Each playing is heard through a master of its own, silent until its
  first note. The fades move it, and a song's tracks sit inside it on
  buses of their own. They go once the playing and its voices have.
- Notes and clips are struck the same way in the editor and the game
  (strikeNote, playClip). What a sequence needs comes from one place
  (core/music/needs).

Findings:
- Reading the audio clock resumed a suspended context. A playing
  pattern read it every 25 ms and so undid the toolbar's pause. The
  library now leaves a context alone while the toolbar holds it.
- A pattern played once to its end asked the list of playings about
  its voices, and that list asked the pattern again. Once its last
  voice was gone this ran until the stack overflowed. The scheduler of
  a playing now asks only after voices.
- e2e 64 reads the playings through __gm.sound.playbacks() now.

Tests:
- song-play.test.ts (new): 8 tests, node catalog +8 cases
- sound-library.test.ts: 66 tests, 4 new
- e2e 64 (melektaus-atx): 23 of 23 checks passed
- e2e 65 (melektaus-atx): ALL PASSED, 22 checks (+4)
- Gate (melektaus-atx): CHECK EXIT 0, 251 files, 3903 tests
- Counter-checks: the old way of asking after voices, a clock read
  that wakes a held context, and a start after a fade out began each
  fail 1 test

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 17:38:44 +02:00
adminandClaude Opus 5.5 8f9c250eb6 Lay patterns and sounds out on tracks and play them as a song #334
game-maker / gate (push) Successful in 3m29s
game-maker / suite (push) Successful in 16m29s
Patterns could only be heard one at a time, in their own tab or from
a behavior. A song lays patterns and sounds out on tracks along one
time axis and plays them through the scheduler of #332, the third
piece of the music tools of #330.

- New asset type song under the heading Songs, with four empty tracks,
  120 BPM in 4/4 and a grid of a bar. Tempo, meter and grid are rows
  in the inspector. Tracks, items and the loop are the asset's data,
  held to their rules on load and in every command.
- A track has a name, volume, pan, mute and solo. Each is heard on a
  bus of its own, so a change reaches what is sounding at once.
- A pattern item repeats its pattern for as long as it is, with a
  transpose and another instrument if wanted. A sound item plays its
  sound as a clip at its own speed, round and round, through a sound
  FX if wanted. Both have a volume on top of their track's.
- Cut back at the left end or split, an item starts as far into what
  it plays as the cut is: ticks into a pattern, seconds into a sound.
- The tab shows the heads with M, S, volume and pan, the ruler of the
  Pattern tab and the items, a pattern with its notes, a sound with
  its waveform, a missing asset red. Patterns and sounds are dragged
  in from the Assets panel. Body, right and left end move, lengthen
  and cut back, Alt leaves the grid. Ctrl+C/V/D/A, Ctrl+E splits
  where playing starts, Delete, arrows. A double-click opens a
  pattern item in its tab.
- What is picked is a new selection of its own, so the inspector
  shows the item or the track, with a button to delete it.
- The scheduler plans clips beside notes. Where the playhead starts
  or jumps back inside a clip, it is taken up that far into its
  sound. A clip fades out at the end of its item or of the loop.
- The editor plays one piece at a time: a pattern started stops the
  song and the other way round.
- The rules are pure functions in core/music/song, editor/song/
  songView and songEdit.

Findings:
- The suites of 4ea752f and 2a3b90b were red at e2e 54: the Pattern
  pushed Struct below the fold of the + menu. The module now scrolls
  to it, as e2e 47 does for Vector art. It also left the menu open,
  which made e2e 55 fail behind it.
- The Assets panel only dragged assets a scene can place. Patterns
  and sounds are dragged now as well, and a scene says why it does
  not take one instead of calling every asset a scene.

Tests:
- song.test.ts (new, 14) and song-edit.test.ts (new, 9): model,
  round trip, sequence of patterns and clips, mute and solo, cuts,
  clips on the scheduler, moving, sizing, trimming, splitting,
  duplicating, hits, head parts. sound-library.test.ts +5: clip
  offset, end fade, pitched clip, track bus, peaks.
- e2e 65 (new) on melektaus-atx: ALL PASSED, 18 checks. e2e 54 and
  55 with the fix: ALL PASSED, 33 checks. e2e 4 with 30 asset
  types: ALL PASSED, 62 checks.
- Gate on melektaus-atx: CHECK EXIT 0, 250 test files, 3884 tests.
- Counter-checks: no taking up of a clip, no pattern offset, no clip
  end each fail 3, 1 and 1 tests.
- Probe on stein2: the tab with four tracks, before and while
  playing, looked at in two screenshots.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 16:28:25 +02:00