Commit Graph
10 Commits
Author SHA1 Message Date
adminandClaude Opus 5.5 84cd36f902 Bake a pattern or song to Opus, played in its place by a game (#340)
game-maker / gate (push) Successful in 3m49s
game-maker / suite (push) Successful in 15m6s
A pattern or song played by a game runs every note through the
scheduler, the instruments and their FX, live. Bake works that out
once, keeps it as a file on the asset, and Play pattern and Play song
play the file instead, as one voice, for as long as nothing it is made
of has changed. The tabs always play live.

- Bake is an icon in the header of the Pattern and the Song tab, lit
  while the bake holds. A click bakes, cancels a bake that runs, or
  offers Bake again and Remove bake. The toolbar shows the progress.
  The info of the tab says "baked" or "bake outdated".
- The bake holds everything up to the output of the pattern or song.
  The sound FX of the node, fx params, volume, fade in, bus, Master
  routing and stopping with a fade stay live.
- A print of the worked-out plan decides whether it holds: notes and
  clips, tempo, length, loop, tracks, the build of each instrument for
  each note, sound FX, files, cuts and levels. A rename keeps it, an
  undo back to the baked state makes it hold again.
- The file holds the straight run and then one more round of the loop
  as heard the second time, tails included. Looped, it plays to the
  loop end and then round that round, to the sample. Once, it fades
  out over its last 10 ms. Let ring fades it out over 0.3 s.
- It is worked out by a second SoundLibrary on an OfflineAudioContext,
  its scheduler woken by suspends of the offline clock, and what
  reaches the master is taken. A worklet probe tells whether that was
  mono, so a mono bake goes on exactly as the notes did.
- Opus in Ogg, 160 kbit/s stereo or 96 kbit/s mono, encoded with
  WebCodecs, the Ogg pages written by hand. Normalized to full scale,
  the peak kept and played back.
- The file goes up into the sounds of the account as "[bake] name",
  which the sound picker leaves out. An old bake file is deleted once
  the project is saved without it. Only a bake that holds is loaded.
- Ogg files are decoded at 48 kHz: resampled to 44.1 kHz, the loop of
  a bake lost a sample a round.
- Baking and removing a bake are no undo steps, like the EQ's zoom.

Findings:
- Chrome's AudioEncoder refuses opus.format 'ogg' (NotSupportedError)
  but hands out the OpusHead with a pre-skip of 312 for 'opus'.
- The Song tab's header is full at 1280 px: a captioned Bake pushed
  Fit off the tab. Hence the icon.

Tests:
- Unit: tests/core/ogg.test.ts (6), tests/core/bake.test.ts (16),
  four new in sound-library.test.ts, one in song-play.test.ts. One
  test cuts, reverses and fades a clip on a song's track: each change
  outdates the bake, each undo makes it hold again.
- e2e 64 (melektaus-atx): ALL PASSED, 36 checks (6 new). Live and baked
  rendered offline differ by at most 0.06 dB over 114 windows of
  20 ms, two loop ends among them. e2e 65 had no red check with the
  icon in its header.
- Gate (melektaus-atx): CHECK EXIT 0, 277 files, 4281 tests.
- Counter-checks: print without instrument builds, print taking the
  sound's file instead of the clip's cut, round taken from the first
  pass, bake voice without its looping source, Play pattern or Play
  song ignoring the bake: one unit test red each. Pre-skip set to 0:
  the e2e comparison 4.8 dB off, only that check red.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 19:13:11 +02:00
adminandClaude Opus 5.5 af2312a5e7 Play every tab's preview straight into the master (#368)
Since #351 the tabs played their asset along its links in the Master
routing, as the game does. A song linked into a reverb bus was heard
through that bus while it was edited. A preview should sound like the
asset itself, so the tabs leave the Master routing out again.

- The Sound, Sound FX, Instrument, Pattern and Song tab play into the
  master. The sound FX of the asset still applies, and so does the
  master's own volume and FX.
- Inside a song its own routing of tracks and sends still counts.
- The game is unchanged: Play nodes go along the routing as before.

Tests:
- e2e 34 (melektaus-atx): ALL PASSED, 96 checks. The routing check
  now holds the mix of the two buses as before, and the Sound tab on
  the master with the bus silent.
- Counter-probe e2e: the Sound tab along the routing again, the
  routing check red.
- Gate (melektaus-atx): CHECK EXIT 0, 274 files, 4230 tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 13:09:36 +02:00
adminandClaude Opus 5.5 0e73fd85b9 Route sounds, songs and buses in one Master routing graph (#351)
game-maker / gate (push) Successful in 4m20s
game-maker / suite (push) Failing after 15m17s
A project bus could only go into one other bus, and a sound, an
instrument, a pattern or a song into exactly one bus. A shared reverb
needs a sound to go there in part and on dry at the same time. Instead
of a list of sends on every asset, the project gets one routing graph
like the routing of a song (#350), one level up.

- Menu -> Master routing opens a tab with one graph per project. It has
  a node per sound, instrument, pattern, song and bus, named after its
  asset, and one Master. Embedded assets have none, their song routes
  them.
- The nodes follow the assets. Every edit settles the routing in the
  same undo step (KeepRoutingCommand), so an undone delete brings the
  links of its asset back. A new asset gets a node and a link to the
  master. What went only into a deleted bus goes to the master.
- New nodes start in three columns: what plays, the buses, and the
  master a row above, so no link into it runs behind a bus.
- Links are sends: -60 to +12 dB, and a Mode on links out of a bus or a
  song. Out of a sound, an instrument or a pattern a link always takes
  the sound after its volume and FX. Loops are refused.
- A node without a link is silent and says "Goes nowhere".
- The rows Bus and Into are gone. Play nodes with a picked bus still
  overrule the routing.
- A click on a node shows its asset in the inspector, the Master shows
  the master volume and sound FX. A double click opens the asset's tab.
- The graph lives in project.routing and is held to the assets when a
  project is read, but nothing is added there. The editor adds the
  nodes when it opens a project. An asset without a node plays on the
  master, so old projects sound as before without a migration.
- In the mix an asset with one link at 0 dB goes straight into that bus.
  A sound or instrument with more gets a bus of its own with the sends.
  A pattern or song sends from the master of its playing, which is
  where its Mode taps. Buses of the mix can have sends now too.

Found:

- The check "the scheduler plans a looped bar" in e2e 34 wobbles. It
  failed in 2 of 5 runs here and in CI task 580 at 490fcba, before
  this change. HEAD passed it 2 of 2. Not fixed here.

Tests:

- Unit master-routing (new, 10), sound-library (2 new), buses
  rewritten onto the routing, 5 lists without the Bus row. 5 counter
  checks red.
- e2e 34 (melektaus-atx): ALL PASSED, 93 checks (3 new, 2 rewritten).
  Counter check with the Sound tab ignoring the routing: the routing
  check red.
- Gate (melektaus-atx): CHECK EXIT 0, 273 files, 4225 tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:18:46 +02:00
adminandClaude Opus 5.5 25c1d02b06 Keep the audio thread free of ended voices and too many vocoders (#356)
game-maker / gate (push) Successful in 3m45s
game-maker / suite (push) Successful in 14m44s
A song that had been played a few times in a long session stuttered
at regular intervals until the page was reloaded. A quick run up the
keys of a sampled instrument made every sound drop out for a second
or two. Both came from work the audio thread should not have had.

- Every worklet node of a voice listened on its port. Chrome never
  collects a port that is listened to, and through its listener not
  the voice either. Every node of the voice stayed on its bus and was
  worked on in every block. A way or stage on the worklet now closes
  its port once it ends or is stopped, and the processor closes its
  own side when it is told to stop.
- A voice without echoes now leaves its bus as soon as its sound and
  what its stages hold back are through. Before, only a voice with
  echoes did.
- At most 12 ways play through the pitch worklet at once. One more
  cuts the oldest in 10 ms, a note already let go before one still
  held. Each runs a phase vocoder, whose first block alone took
  2.7 ms of the 2.67 ms a block has on stein2.
- Play for the game ends everything the tabs play: the piece holding
  the transport and every key or preview (`stopPreviews`).
- Closing a Sound, Sound FX, Instrument, Pattern or Song tab stops
  what it plays of that asset (`AssetTabEditor.closed`).
- A preset in the Instrument tab plays nothing any more. With
  Audition on it is heard like any other change.
- Test hooks: `__gm.instrument.play`, `__gm.sound.playing`, and
  `vocoders` in the stats of the sound library.

Measured:
- A copy of the project, its Song 2 played for 12 s and stopped, six
  times (Chrome on stein2, CDP WebAudio): before, 746 nodes were left
  after the first time and 4469 after the sixth, the load at rest
  grew from 0.01 to 0.06. After, 7 nodes are left every time (the
  master and one bus) and the load at rest stays at 0.01 to 0.02.
- Pitch-player voices live in Chrome on melektaus-atx: 12 take 0.50
  of the audio thread, 16 take 0.71, 24 take 0.99. 30 struck 40 ms
  apart held it at 0.99 from the first second on.

Tests:
- Unit sound-library: 110 green (3 new), 4 counter-probes each red
- e2e 34 (melektaus-atx): ALL PASSED, 86 checks (3 new, 2 changed)
- e2e counter-probe with game start and preset undone: their 3
  checks red, and the old check of Play note in the game as well,
  which now counts on the preview being gone
- e2e counter-probe with tab close undone: exactly its check red. A
  first form of it held a key and stayed green, since the keys let go
  of a held key by themselves once the tab shows no asset
- Gate (melektaus-atx): 270 files, 4181 tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 17:57:18 +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 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 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 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
adminandClaude Opus 5.5 4ea752feef Draw notes for an instrument in a piano roll and loop them #333
game-maker / gate (push) Successful in 3m22s
game-maker / suite (push) Failing after 13m18s
Notes could only be played one at a time, by a key or a behavior. A
pattern is a stretch of notes for one instrument, drawn in a piano
roll and played in time through the scheduler of #332. Songs (#334)
will lay patterns out on tracks.

- New asset type pattern under the heading Patterns. Instruments are
  listed under Instruments now, since a heading is one per type.
- Notes are a start and a length in ticks, a note number and a
  velocity, kept to their rules on load and in every command. The
  instrument, the length in bars, the grid, the meter and the tempo
  are rows in the inspector. The loop is the asset's own data, the
  whole pattern while none is set.
- Grids from 1/1 to 1/32 and the triplets 1/4T to 1/32T, all whole
  ticks. The meter only moves the bar lines, the notes stay put.
- The tab shows the keys down the left, a ruler with bars, beats,
  loop and start along the top, the notes between them and a
  velocity lane below. Keys the instrument plays nothing for are
  grey, the names of a drum kit stand on its keys.
- A click draws a note on the grid line before the pointer, as long
  as the last one drawn or lengthened and as hard as the last
  velocity set. The body moves a note, the right end lengthens it,
  Alt leaves the grid. A drag on empty grid picks with a band, Shift
  adds, a right-click deletes.
- Keys: Space plays, Delete, Ctrl+C/V/D/A, the arrows by a semitone
  or a grid step and with Shift by an octave or a bar. A paste lands
  where playing starts. Every gesture is one undo step.
- A click on the ruler sets where playing starts, a drag the loop or
  one of its ends, its menu loops a bar or the whole pattern.
- The wheel scrolls, Shift across, on the ruler or with Alt it zooms.
  Ctrl+wheel stays with the browser, Pixi listens passively.
- Play runs the loop through the scheduler. An edit goes to it on
  the next window without a restart, a new loop or tempo counts from
  what is planned. Another pattern shown or a project opened stops it.
- The time axis and the ruler are their own pieces in editor/music
  for the song tab. The rules of the roll are pure functions in
  rollView and rollEdit.

Findings:
- Music.play could start after a stop that came while it was still
  loading its files. A run counter makes the late start give up.
  Music.edit now loads what a new note needs as well.
- A click on an end of the loop would have set the loop again. A
  press there only moves the end once it is dragged.

Tests:
- pattern.test.ts (new, 9) and piano-roll.test.ts (new, 16): model,
  grids, meter, held notes, sequence, commands, loop, round trip,
  drum names, time lines, rows, hits, bands, lane, edit rules.
- e2e 64 (new) on melektaus-atx: ALL PASSED, 20 checks. e2e 4 with
  29 asset types: ALL PASSED, 62 checks.
- Gate on melektaus-atx: CHECK EXIT 0, 247 test files, 3839 tests.
- Counter-checks: the move without its clamp, the sequence without
  the end of the pattern, the duplicate offset without whole grid
  steps each fail 1 test. Without passing edits to the scheduler the
  e2e check for a note drawn while playing fails, the rest passes.
- Probe on stein2: the tab with a drum kit, before and while playing,
  looked at in two screenshots.

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