From 26f23debb8fbdeea6358cdf8636a9b2e3719bfe6 Mon Sep 17 00:00:00 2001 From: meleki Date: Fri, 9 Oct 2026 19:13:21 +0200 Subject: [PATCH] Describe baking a pattern or song (#340) - Songs: a section Baking, what is in a bake and what stays live, when it holds, the loop, the file and its memory, measured in e2e 64. - Patterns: Play pattern plays a bake that holds, with a link there. - Architecture: bake and ogg in core/music, bakeRender, the bake voice and the offline library in viewport, BakeButton in editor/music. - Development: 1928 checks. Co-Authored-By: Claude Opus 5.5 --- Architecture.md | 17 ++++++++++++----- Development.md | 2 +- Patterns.md | 4 ++++ Songs.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 6 deletions(-) diff --git a/Architecture.md b/Architecture.md index fbef841..08e4837 100644 --- a/Architecture.md +++ b/Architecture.md @@ -170,7 +170,10 @@ src/core/music/ time (ticks, 480 to the quarter, tempo and meter, a posit plays on, mute and solo as gates), mixer (the master bus and `mix:`), needs (a sound FX with its parameters, or none), playbacks (what a sequence loads before it plays), embedded (#355: what a song keeps to itself), ghosts (#339: the notes around a - pattern item), take (#342: a recorded take as 16-bit WAV, cut to where the song started) + pattern item), take (#342: a recorded take as 16-bit WAV, cut to where the song started), + bake (#340: what a bake is made from as a print, where the parts of its file lie, how they + are put together from the playing looped and the one played once, and played back) + + ogg (#340: Opus in Ogg, the pages around an encoder's packets and back) src/core/soundfx/ the sound FX graph (#311): nodes (every effect and what it adds to a plan), compile (the graph walked back from the Output to a plan; a parameter stays open until a voice starts; a tapped node gets a `tap` in front and behind, #374), parametricEq (#367: @@ -247,7 +250,10 @@ src/viewport/ WorldViewport (the drawing of a world or a holder, the ba voice of what reaches its Output, each way with its own file, #331; a voice can start and be released at a second of the audio clock, #332; a clip of a song from a second into its sound until its end, a bus per track of a song with its level and pan, #334; the buses of - the mix and the Master routing, #325/#351), music (the scheduler on + the mix and the Master routing, #325/#351; a bake as one voice on the master of its + playing, and a library of its own on an offline context to bake with, #340; a channel + probe that says whether a bake is mono), bakeRender (#340: a pattern or song played + offline, what reaches its master taken, encoded as Opus with WebCodecs), music (the scheduler on the library's clock, its notes played by the project's instruments and its clips by the library: the player of the Pattern and Song tabs, #333/#334, and `__gm.music`) + musicTimer (the Web Worker whose interval is the scheduler's beat, #332), takeRecorder + takeWorklet @@ -383,13 +389,14 @@ src/editor/curve/ CurveEditor (header: Preset, the picked point's type, Pre graph drawn from curveValue, points and handles edited with the vector art gestures, the preview dot and its rail) + curveView (the curve laid onto a picture for those gestures and read back, a point held between its neighbours, a point put down on free space) -src/editor/pattern/ PatternEditor (header: Play, Grid; plays through the scheduler and follows edits, #333), +src/editor/pattern/ PatternEditor (header: Play, Grid, Bake; plays through the scheduler and follows edits, #333), PianoRoll (keys, ruler, notes and velocity lane in one widget, the gestures and keys) + rollView (its geometry: rows, hits, bands) + rollEdit (what a gesture does to the notes) src/editor/music/ timeView (the time axis: ticks to pixels, zoom, the lines of bars, beats and grid) + TimeRuler (bars, loop, start and playhead) — for the piano roll and the song tab — + transport (one - piece plays at a time: a song or a pattern, #334) -src/editor/song/ SongEditor (header: Play, Grid; plays through the scheduler, sets the tracks' buses and + piece plays at a time: a song or a pattern, #334) + BakeButton (#340: bakes the pattern + or song shown, lit while its bake holds) +src/editor/song/ SongEditor (header: Play, Grid, Bake; plays through the scheduler, sets the tracks' buses and follows edits, #334), SongTimeline (heads, ruler and lanes in one widget, dropping assets in, the gestures and keys) + songView (its geometry: tracks, items and their ends, the parts of a head) + songEdit (what a gesture does to the items) + songActions (deleting diff --git a/Development.md b/Development.md index bdf27dd..34bdecb 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 # 1922 editor checks, screenshots in /tmp/gm-e2e-*.png +node tools/seed-e2e.mjs && node tools/e2e.mjs # 1928 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/Patterns.md b/Patterns.md index 22bedd1..cf94696 100644 --- a/Patterns.md +++ b/Patterns.md @@ -173,6 +173,10 @@ Its notes fade out in 10 ms; with **Let ring** a note of an instrument with an naming no sound it stops everything, patterns included, and its `voice` takes a `playback`. The end of the run stops every pattern. +A pattern can be **baked** with the icon in the header of its tab (#340): `Play pattern` then plays +the bake, one voice of a file, in place of its notes, as long as nothing it is made of has changed. +The tab plays the notes. See [Baking](Songs#baking). + To play several patterns and sounds together, lay them out in a [song](Songs) and play that with [Play song](Songs#playing-one-in-a-game). A pattern has no tempo input in the game yet, and nothing tells a graph that a pattern played once has ended. diff --git a/Songs.md b/Songs.md index 6530e6f..99e75a6 100644 --- a/Songs.md +++ b/Songs.md @@ -348,6 +348,55 @@ there, played by `Play song` at 120 BPM: the clip once, the pattern's notes 2 s left sat inside it at 0.501 and -0.5. The toolbar's pause held the clock, and `Stop song` with a fade of 1 s ended it, its buses with it. +## Baking + +**Bake** (#340), the icon with a wave in a frame in the header of the Song and the +[Pattern](Patterns) tab, works out once what the song or pattern plays and keeps it on the asset as a +file. `Play song` and `Play pattern` in a game then play that file instead of every note: one voice +in place of many, and nothing for the scheduler to plan. The tab itself always plays live, so an +edit is heard at once. + +- **What is in it:** everything up to the output of the song or pattern. For a pattern, its notes + through the instrument and the instrument's sound FX. For a song, its tracks, items and clips, the + FX of tracks and items, the routing, mute and solo. +- **What stays live:** everything after that: the sound FX of the node (or of the asset), `fx params` + and `Set sound FX parameters`, `volume`, `fade in`, the bus, the Master routing and a stop with a + fade. A node behaves the same with a bake and without. +- **When it holds:** a bake keeps a print of what it was made from: the notes and clips, tempo, + length and loop, the tracks with their level, pan and FX, how each instrument plays each of its + notes with its sound FX, and the files, cuts and levels of the sounds. A change to any of them + outdates it, also one to an instrument or a sound the pattern only names. Then the icon is no + longer lit, the info of the tab says *bake outdated*, and a game plays the notes again. Renaming + changes nothing. An undo back to what was baked makes it hold again without baking anew. Baking + and removing a bake are no undo steps. +- **Looped:** the file holds the playing straight from the start, then the loop once more as it + sounds the second time round, with what rings on from the first round in it. Played looped it runs + to the loop end and then round that second round, to the sample. Played once it runs to the end + and fades out over its last 10 ms. What would ring on past the end is not in it: to keep a tail, + make the song or pattern longer, by an empty bar for instance. +- **Stopping:** `Stop song` and `Stop pattern` with **Let ring** fade a bake out over 0.3 s, as its + notes cannot ring on one by one. +- **The click:** not baked, it bakes. While it bakes, the toolbar says how far it has got and a + click cancels it. Baked, it offers **Bake again** and **Remove bake**. A change made while it bakes + throws the result away. +- **The file:** Opus in Ogg, 160 kbit/s in stereo, 96 kbit/s where everything reaching the output is + mono, encoded in the browser with WebCodecs. Baking therefore needs Chrome. Where a browser + cannot decode the file, a game plays the notes. It is raised to full scale and played at the + level it had. It goes up into the sounds of your account under *[bake] name*, and the sound picker + leaves it out. The old file of a bake made again or removed is deleted once the project is saved + without it, unless a snapshot or a release still names it. +- **Memory:** a bake that holds is loaded and decoded with the project like a sound, at 48 kHz: + about 23 MB a minute in stereo. An outdated one is not loaded. Streaming it instead is #349. + +Measured (2026-10-09, e2e 64 on the CI's Windows machine): a bar of quarters at 150 BPM with one +more note just before its end, baked: 76800 frames for the bar and 76800 for the second round, +38 KB. Played offline from its notes and from its bake for 4 s, looped, the level of every 20 ms in +which it was heard differed by at most 0.06 dB (114 windows, two loop ends among them). The game +played it as one voice with no note planned; a note drawn outdated it, Ctrl+Z made it hold again, +and Remove bake deleted its file once the project was saved. With the pre-skip of the Opus file +left out on purpose, the same comparison was 4.8 dB off. In a probe, Chrome worked out 60 s of +sound in 0.3 s, or in 2.1 s with many pitched voices, and encoded it in 0.6 s. + ## Sound FX on tracks, items and whole songs A [sound FX](Sounds#sound-fx) can sit on a track, on a pattern item, and on a whole `Play pattern` or