@hraness/dawg 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/DAWG.md +300 -35
  3. package/README.md +4 -4
  4. package/core/chords.ts +288 -7
  5. package/core/diff.ts +37 -12
  6. package/core/expression.ts +1241 -0
  7. package/core/loop.ts +23 -0
  8. package/core/master.ts +455 -0
  9. package/core/midi.ts +452 -0
  10. package/core/rhythm.ts +7 -2
  11. package/core/score.ts +536 -56
  12. package/core/sdk/eval-child.ts +38 -3
  13. package/core/sdk/print.ts +445 -14
  14. package/core/sdk/v1.ts +1709 -23
  15. package/core/sections.ts +2046 -0
  16. package/core/synth.ts +11 -1
  17. package/core/tempo.ts +1318 -0
  18. package/core/tuning.ts +1180 -0
  19. package/guides/audition.md +26 -0
  20. package/guides/automation.md +26 -0
  21. package/guides/chords.md +28 -0
  22. package/guides/effects.md +27 -0
  23. package/guides/faders.md +29 -0
  24. package/guides/files.md +28 -0
  25. package/guides/getting-started.md +26 -0
  26. package/guides/index.ts +75 -0
  27. package/guides/keys.md +27 -0
  28. package/guides/media.md +27 -0
  29. package/guides/mix.md +23 -0
  30. package/guides/music.md +14 -0
  31. package/guides/notes.md +26 -0
  32. package/guides/performance.md +26 -0
  33. package/guides/play.md +24 -0
  34. package/guides/project.md +13 -0
  35. package/guides/providers.md +25 -0
  36. package/guides/rhythm.md +26 -0
  37. package/guides/sessions.md +21 -0
  38. package/guides/sound.md +15 -0
  39. package/guides/sounds.md +25 -0
  40. package/guides/tempo.md +26 -0
  41. package/guides/tracks.md +25 -0
  42. package/guides/web-search.md +21 -0
  43. package/package.json +3 -1
  44. package/src/agent/agent.ts +2 -0
  45. package/src/agent/brief.ts +77 -3
  46. package/src/agent/chord-tools.ts +9 -1
  47. package/src/agent/expression-tools.ts +336 -0
  48. package/src/agent/master-tools.ts +299 -0
  49. package/src/agent/models.ts +4 -4
  50. package/src/agent/ops.ts +29 -4
  51. package/src/agent/planner.ts +45 -2
  52. package/src/agent/preview-tool.ts +21 -3
  53. package/src/agent/section-tools.ts +411 -0
  54. package/src/agent/time-tools.ts +290 -0
  55. package/src/agent/tools.ts +36 -2
  56. package/src/agent/tuning-tools.ts +301 -0
  57. package/src/agent/xcb-agent.ts +2 -0
  58. package/src/audio/arrange.ts +471 -0
  59. package/src/audio/audition.ts +16 -3
  60. package/src/audio/click.ts +113 -1
  61. package/src/audio/clock.ts +71 -5
  62. package/src/audio/effects/bus.ts +5 -4
  63. package/src/audio/effects/chain.ts +12 -2
  64. package/src/audio/effects/common.ts +30 -1
  65. package/src/audio/effects/convolution.ts +43 -5
  66. package/src/audio/effects/dynamics.ts +2 -1
  67. package/src/audio/effects/filter.ts +5 -3
  68. package/src/audio/effects/modulation.ts +5 -1
  69. package/src/audio/effects/space.ts +197 -50
  70. package/src/audio/engine.ts +175 -20
  71. package/src/audio/live.ts +33 -6
  72. package/src/audio/loudness.ts +551 -0
  73. package/src/audio/master.ts +660 -0
  74. package/src/audio/measure-worker.ts +45 -0
  75. package/src/audio/measure.ts +110 -0
  76. package/src/audio/player.ts +11 -6
  77. package/src/audio/preview.ts +87 -16
  78. package/src/audio/render-worker.ts +6 -1
  79. package/src/audio/renderer.ts +7 -1
  80. package/src/audio/sampler.ts +108 -19
  81. package/src/audio/synth/voice.ts +60 -13
  82. package/src/audio/synth/zzfx.ts +10 -4
  83. package/src/audio/warp.ts +86 -0
  84. package/src/audio/wav.ts +448 -59
  85. package/src/commands/arrange.ts +949 -0
  86. package/src/commands/expression.ts +934 -0
  87. package/src/commands/help.ts +281 -24
  88. package/src/commands/master.ts +361 -0
  89. package/src/commands/music.ts +6 -1
  90. package/src/commands/synth.ts +11 -1
  91. package/src/commands/time.ts +964 -0
  92. package/src/commands/tuning.ts +490 -0
  93. package/src/main.ts +826 -47
  94. package/src/render.ts +109 -7
  95. package/src/session/daemon.ts +18 -8
  96. package/src/session/naming.ts +8 -1
  97. package/src/session/rebase.ts +21 -0
  98. package/src/tui/arrange-menu.ts +390 -0
  99. package/src/tui/audition.ts +58 -5
  100. package/src/tui/euclid.ts +50 -6
  101. package/src/tui/fader.ts +409 -0
  102. package/src/tui/menu-time.ts +401 -0
  103. package/src/tui/menu.ts +656 -16
  104. package/src/tui/performance-menu.ts +235 -0
  105. package/src/tui/play-chords.ts +95 -1
  106. package/src/tui/play-mode.ts +150 -6
  107. package/src/tui/play-session.ts +414 -41
  108. package/tui/app.ts +262 -13
  109. package/tui/arrange-strip.ts +174 -0
  110. package/tui/drawer.ts +478 -0
  111. package/tui/grammar.ts +79 -12
  112. package/tui/guide.ts +351 -0
  113. package/tui/highway.ts +87 -4
  114. package/tui/hits.ts +68 -0
  115. package/tui/input.ts +7 -0
  116. package/tui/keys.ts +72 -0
package/DAWG.md CHANGED
@@ -11,7 +11,7 @@ bun install
11
11
  bun run dawg
12
12
  ```
13
13
 
14
- Run `dawg` from any directory. It creates `.dawg/session` on first use and reuses that session in later terminal windows. Use `dawg --new` for a separate composition, `dawg --session <name|id>` to attach explicitly (an unknown value is `no session named "…" · dawg sessions`, never a new session), and `dawg --track bass` to focus a named track. `dawg --version` prints the version; unknown subcommands and options are rejected with usage before `.dawg/` exists, and the launch that creates `.dawg/` says `created .dawg/ · add it to .gitignore` in the strip. Every window connects to `dawgd`, a per-session daemon the first window starts in the background. It is the single writer: windows send operations with a base revision and an idempotency key, duplicate keys are no-ops, a stale full composition receives a typed rebase diagnostic, and a stale `operations` intent (what the agent sends) is replayed on the current score when nothing it touches changed since its base (the notes it updates or removes, the tracks it rewrites or clears, tempo and length, and ids it creates; at most 64 revisions back, with the base recovered by rewinding the event log); anything else still gets the rebase diagnostic. Every event stores a compact reverse delta (`rewind`) rather than a copy of the previous score, so a session record grows by about the size of each edit; when it nears its 4 MiB cap the oldest rewinds are compacted away and only that older history becomes unreachable. Rebased events record `rebasedFrom`, and their rewind leads back to the score they replayed on, so undo drops only that change, and on the file fallback the agent commits the full composition with the strict base check. Accepted commits are persisted through the same atomic snapshot store before being broadcast to every window. The daemon also owns the only transport and audio player, broadcasting play, pause, seek, and tempo with a timestamp so every window draws the same hit line. It keeps a presence table (`clientId`, `pid`, focused track) and can atomically claim the first unfocused track for a new window. The daemon exits 30 seconds after its last window closes, removes its socket on SIGTERM, and a crashed daemon's socket and lock are reclaimed by the next window. If `dawgd` cannot be started (or `DAWG_DAEMON=0`), windows fall back to the snapshot under the file lock, watching the session directory with `fs.watch` (so renames and edits from other windows arrive immediately) with a 1 s backstop poll, or a 200 ms poll where watching is unavailable, with presence kept in per-window heartbeat files. `dawg sessions` lists sessions in the current workspace. `dawg render <out.wav> [--session <name|id>] [--import <file>]` reads the session record from disk (no daemon, no audio) and writes a stereo 16-bit WAV through the playback renderer; the same score always yields the same bytes, and the command prints the size and sha256. `/status` reports `status · <name> · rev <n> · <digest> · shared via dawgd` (or `saved locally · no daemon`), where the digest is the 16-hex composition digest dawgd broadcasts. In demo mode a drum track is seeded with a one-bar kick, snare and hat groove instead of melodic notes.
14
+ Run `dawg` from any directory. It creates `.dawg/session` on first use and reuses that session in later terminal windows. Use `dawg --new` for a separate composition, `dawg --session <name|id>` to attach explicitly (an unknown value is `no session named "…" · dawg sessions`, never a new session), and `dawg --track bass` to focus a named track. `dawg --version` prints the version; unknown subcommands and options are rejected with usage before `.dawg/` exists, and the launch that creates `.dawg/` says `created .dawg/ · add it to .gitignore` in the strip. Every window connects to `dawgd`, a per-session daemon the first window starts in the background. It is the single writer: windows send operations with a base revision and an idempotency key, duplicate keys are no-ops, a stale full composition receives a typed rebase diagnostic, and a stale `operations` intent (what the agent sends) is replayed on the current score when nothing it touches changed since its base (the notes it updates or removes, the tracks it rewrites or clears, tempo and length, and ids it creates; at most 64 revisions back, with the base recovered by rewinding the event log); anything else still gets the rebase diagnostic. Every event stores a compact reverse delta (`rewind`) rather than a copy of the previous score, so a session record grows by about the size of each edit; when it nears its 4 MiB cap the oldest rewinds are compacted away and only that older history becomes unreachable. Rebased events record `rebasedFrom`, and their rewind leads back to the score they replayed on, so undo drops only that change, and on the file fallback the agent commits the full composition with the strict base check. Accepted commits are persisted through the same atomic snapshot store before being broadcast to every window. The daemon also owns the only transport and audio player, broadcasting play, pause, seek, and tempo with a timestamp so every window draws the same hit line. It keeps a presence table (`clientId`, `pid`, focused track) and can atomically claim the first unfocused track for a new window. The daemon exits 30 seconds after its last window closes, removes its socket on SIGTERM, and a crashed daemon's socket and lock are reclaimed by the next window. If `dawgd` cannot be started (or `DAWG_DAEMON=0`), windows fall back to the snapshot under the file lock, watching the session directory with `fs.watch` (so renames and edits from other windows arrive immediately) with a 1 s backstop poll, or a 200 ms poll where watching is unavailable, with presence kept in per-window heartbeat files. `dawg sessions` lists sessions in the current workspace. `dawg render <out.wav|out.mid> [--session <name|id>] [--import <file>]` reads the session record from disk (no daemon, no audio) and writes a stereo 16-bit WAV through the playback renderer (or a Standard MIDI File with tempo and time-signature meta events for `.mid`, see **Tempo and meter**); the same score always yields the same bytes, and the command prints the size and sha256. `/status` reports `status · <name> · rev <n> · <digest> · shared via dawgd` (or `saved locally · no daemon`), where the digest is the 16-hex composition digest dawgd broadcasts. In demo mode a drum track is seeded with a one-bar kick, snare and hat groove instead of melodic notes.
15
15
 
16
16
  ### Sessions, names and forks
17
17
 
@@ -31,11 +31,13 @@ The header (`dawg` · track · ▶/⏸ BPM · key · session · N windows, then
31
31
 
32
32
  Keys: `Space` (empty prompt) toggles playback. `Enter` submits, or queues in QUEUE mode. `Shift+Enter`/`Ctrl+J` inserts a newline, `Alt+Enter` queues and `Ctrl+Q` toggles STEER/QUEUE. `Ctrl+Z`/`Ctrl+Y` undo and redo, and `Ctrl+O` or `/transcript` opens the transcript, which scrolls with ↑/↓, PgUp/PgDn and Home/End, and `/` cycles its filter (all, requests, ops, errors). `Esc` cancels an agent turn, closes the overlay or clears the draft. `Ctrl+L` redraws and `Ctrl+C` exits. Bracketed paste keeps multiline text intact.
33
33
 
34
- Themes: `/theme default|high-contrast|mono`, `--theme`, `DAWG_THEME`. Color falls back through truecolor, 256-color, 16-color and monochrome. `NO_COLOR` and `TERM=dumb` are supported. `/motion off`, `--reduce-motion` and `DAWG_REDUCE_MOTION=1` switch to static states in the same positions.
34
+ Themes: `/theme default|high-contrast|mono`, `--theme`, `DAWG_THEME`. Color falls back through truecolor, 256-color, 16-color and monochrome. `NO_COLOR` and `TERM=dumb` are supported. `/motion off`, `--reduce-motion` and `DAWG_REDUCE_MOTION=1` switch to static states in the same positions. `--no-mouse` or `DAWG_MOUSE=0` keeps the terminal's own mouse selection (see **Mouse**).
35
35
 
36
36
  Other code reports into the activity strip through `ActivityFeed` (`tui/activity.ts`): `pushCard(text, {tone, baseRevision, resultRevision, hint})`, `pushError(text)`, `setSpinner(label | undefined)`, `setQueueDepth(n)` and `applyAgentEvent(event)`, which accepts the agent's streaming `AgentEvent`s unchanged. Command handlers return a `Receipt` (`{ok: true | false | "warn", text}`), so a failure such as `main is not a drum track` is red because it says so, not because of its wording; `receiptTone` only classifies the legacy strings that remain. The `^z undo` hint rides only on receipts that changed the score, and only the first three in a session. `/help`, `/sessions` and `/tracks` open a scrollable text overlay (`TuiApp.openText`) and leave one summary card in the strip; `/help` is generated from `src/commands/help.ts`, which also feeds `dawg --help` and the usage hints that answer an unknown `/word` or a near-miss such as `pan 3` without a model call. A known verb followed by a sentence (three or more words, no numbers: `add a walking bass in A minor`) goes to the agent instead. An unknown `/word` names the nearest command (`unknown command /clik · did you mean /click? · /help`); a bare word one edit from a command whose remaining words parse as its arguments is suggested locally (`tempoo 90 · did you mean tempo 90?`); other bare words are requests for the agent.
37
37
 
38
- `/help` opens a short, task-first guide: **start here** (type a request, Ctrl-P, Ctrl-K, `?`, undo), **play notes**, **make drums**, **shape the sound**, **chords**, and **more**. `/help all` is the full reference; `/help music`, `/help session`, `/help window` and `/help keys` show one group.
38
+ `/help` opens a short, task-first guide: **start here** (type a request, Ctrl-P, Ctrl-K, `?`, undo), **play notes**, **make drums**, **shape the sound**, **chords**, **song structure**, and **more**. `/help all` is the full reference; `/help music`, `/help session`, `/help window` and `/help keys` show one group, and `/help arrange` lists every arranging command (`section`, `form`, `build`, `drop`, `fill`) with its full usage.
39
+
40
+ `/guide` (or F1) opens the user guides: one short page per feature (`guides/*.md`, shipped in the package and rendered unchanged on dawg.sh/docs), each showing what to ask the agent and the command, key or menu path that does the same by hand. The pane is a tree: ↑↓ (j k) move, → l or Enter expand a section then open a guide, ← h collapse or go to the parent and back from a page, `/` filters by title and text, Esc clears the filter, then steps back, then closes. `/guide chords` opens one guide directly. `guides/guides.test.ts` keeps every guide within 20 rows at 80 columns and checks every `/command` a guide names against the help reference.
39
41
 
40
42
  The local command path understands requests such as:
41
43
 
@@ -114,6 +116,8 @@ automate <lane> points <beat:value> [<beat:value> ...] merge points into a lan
114
116
  automate <lane> remove <beat> drop one point
115
117
  track name <text> rename the focused track
116
118
  meter <beats per bar 1..16>
119
+ tempo 90 at bar 9 ramp | rit 4 bars to 80 | fermata at 31 2 | meter 7/8 at bar 5
120
+ track rate 3/2 | track phasing 4 | track phasing 3 hold 8 | track time off
117
121
  solo | unsolo
118
122
  undo | redo
119
123
  ```
@@ -176,7 +180,7 @@ Every subprocess goes through the injectable `CommandRunner` in `src/auth/runner
176
180
 
177
181
  `/login` in the TUI calls `handoff()`: it stops the frame timer, detaches stdin, leaves raw mode, bracketed paste and the alternate screen, runs the same flow on the real terminal, then re-enters, clears and forces a full redraw.
178
182
 
179
- The gateway and OpenRouter share `src/agent/gateway.ts`, an OpenAI-compatible streaming client with tool calls that requests `stream_options.include_usage`. `src/agent/usage.ts` prices each usage chunk, using the provider's own `cost` when present and otherwise tokens × the models.dev price. It keeps the session total and a daily ledger in `~/.config/dawg/usage.json` (31 days, lock file plus atomic rename) that windows share, and draws the spend line under the prompt (`$0.12 session · $0.48 today · opus-5.5 · gateway`; `subscription` for xcb; `no model · dawg login` offline; it narrows by dropping today, then session). Billed web searches add to the same meter. Prices come from `https://models.dev/api.json`, cached in `~/.config/dawg/cache/` for 24 h, fetched with a timeout and size cap, with a stale cache preferred to nothing offline; on OpenRouter its own `/models` prices win. The picker's `~$0.004/prompt` is `TYPICAL_PROMPT` (≈ 13,200 input + 600 output tokens, measured from the system prompt, tool schemas and a fixture brief over about 2 requests) × price.
183
+ The gateway and OpenRouter share `src/agent/gateway.ts`, an OpenAI-compatible streaming client with tool calls that requests `stream_options.include_usage`. `src/agent/usage.ts` prices each usage chunk, using the provider's own `cost` when present and otherwise tokens × the models.dev price. It keeps the session total and a daily ledger in `~/.config/dawg/usage.json` (31 days, lock file plus atomic rename) that windows share, and draws the spend line under the prompt (`$0.12 session · $0.48 today · opus-5.5 · gateway`; `subscription` for xcb; `no model · dawg login` offline; it narrows by dropping today, then session). Billed web searches add to the same meter. Prices come from `https://models.dev/api.json`, cached in `~/.config/dawg/cache/` for 24 h, fetched with a timeout and size cap, with a stale cache preferred to nothing offline; on OpenRouter its own `/models` prices win. The picker's `~$0.005/prompt` is `TYPICAL_PROMPT` (≈ 22,100 input + 600 output tokens, measured from the system prompt, tool schemas and a fixture brief over about 2 requests) × price.
180
184
 
181
185
  The xcb provider (`src/agent/xcb.ts`, `src/agent/xcb-agent.ts`) calls `xcb --json generate` with one `{version:1, account, model, prompt, timeoutMs, maxOutputBytes}` request on stdin. xcb exposes zero tools and does not stream, so the prompt carries the system rules, the composition brief and the tool catalog as JSON schemas, and asks for exactly one `{ops:[{tool,args}], say?, done}` object. The reply is untrusted. dawg takes the first balanced JSON object in at most 64 KiB, allows at most 16 ops and caps `say` at 400 characters. Each op then goes through `executeCall`, the same argument checks, operation validator, reducer dry run and per-op revision commit used by the gateway loop, and emits the same `tool-applied`/`tool-rejected`/`text-delta` events. If a reply cannot be parsed, an op is rejected or `done` is false, dawg makes another call with the per-op results, up to 3 calls and within the normal turn budgets. Esc aborts the turn, which terminates the xcb child and keeps every accepted revision. The child timeout is `timeoutMs + 75 s`, because the first `generate` per binding (and after an xcb or provider update) admits the account and can take up to a minute longer. A `busy` result, when two first calls hit one account, is retried with backoff. Accounts come from `xcb --json generate --capabilities`, parsed field by field from `unknown`. dawg never runs the xcb installer.
182
186
 
@@ -338,6 +342,64 @@ Parameters (**bold** effect = shown in the simple menu; Lane = automation lane):
338
342
 
339
343
  Strudel mapping notes: Strudel's `lpf`/`hpf`/`bpf` each set a separate filter; dawg has one track filter whose `type` selects the response, so `lpf(800)` is `filter {type: "lpf", cutoff: 800}` and `lpq`/`hpq`/`bpq` map to `resonance`. `delay` in Strudel is the wet level (dawg `delay.mix`), `delaytime` is seconds (dawg `delay.time`; `beats` is the tempo-synced form), `delayfeedback` is `delay.feedback`. `room` is `reverb.mix`, `size`/`roomsize` is `reverb.size`, `roomfade`/`roomlp`/`roomdim` are `fade`/`lowpass`/`dim`. `distort` and `shape` are the distortion drive with `type: "shape"` for Strudel's `shape` curve; `crush` is bits and `coarse` is the sample-hold factor. `phaser`/`phaserdepth`/`phasercenter`/`phasersweep`, `tremolo*`, `leslie`/`lrate`/`lsize`, `postgain` and `compressor` keep their names. `orbit` groups tracks for `duckorbit`/`duckdepth`/`duckattack`/`duckonset` sidechaining; By default each track keeps its own delay and reverb; `fx orbit 2 shared on` makes the track send to its orbit's one shared delay and reverb, as Strudel orbits do (see **Orbit buses**). `iresponse`/`ir` is `reverb.ir`.
340
344
 
345
+ ## Master and loudness
346
+
347
+ The song master is an optional chain after every track, orbit bus and duck are summed: **EQ** (low shelf, two bells, high shelf) → **glue** (stereo-linked bus compressor with soft knee, parallel mix and a sidechain high-pass) → **tape** (saturation with bias and a tone roll-off) → **width** (mid/side, mono below a cutoff) → **limiter** (true-peak brickwall with lookahead). A song with no master renders byte-identically to 0.4; an absent unit is skipped and a 0 dB EQ band is skipped.
348
+
349
+ | Command | Does |
350
+ | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
351
+ | `master glue on` / `master glue off` | a unit with defaults, or drop it |
352
+ | `master glue preset glue` | presets: eq `air warm mud-cut smile`, glue `gentle glue pump`, tape `warm hot crush`, width `narrow wide vinyl` (vinyl: mono below 150 Hz), limiter `transparent loud brick` |
353
+ | `master glue ratio 4` | one parameter (turns the unit on) |
354
+ | `master streaming` / `master target -14` | a loudness target by name or LUFS; `master -14` and `master on` (streaming) are shorthands, as are `master spotify`/`youtube`/`tidal`; a positive number is read as negative LUFS and the reply says so; `master target off` drops it |
355
+ | `master measure` | integrated, short-term max and momentary max LUFS, loudness range, true peak, correlation |
356
+ | `master off` | remove the master |
357
+
358
+ Targets: `streaming` -14 LUFS / -1 dBTP, `apple` and `podcast` -16, `broadcast` -23 (EBU R 128), `classical` -20, `ambient` -18, `club` -8 / -2 dBTP (with the `loud` limiter) and `loud` -6 / -2 dBTP (with the `brick` limiter, for hyperpop, gabber and hardcore; masters louder than -14 LUFS stay under -2 dBTP because lossy encoding of loud, dense material adds inter-sample overs). With the limiter on, a target sets the limiter's input gain: a bracketing search on the measured integrated loudness that starts from the linear estimate, gives the same answer for the same mix every time, and stops early with `reached: false` once more drive no longer raises the loudness. At -6 LUFS the limiter alone may stop short; `tape preset crush` or a lower glue threshold before it helps; without it, the gain is capped so the true peak stays at or below -1 dBTP, so a quiet target is always met and a loud one may fall short (the measurement says so).
359
+
360
+ Measurement follows ITU-R BS.1770-4 and EBU R 128: K-weighting derived for any sample rate, 400 ms momentary and 3 s short-term windows (maxima on a 10 ms grid), the -70 LUFS absolute and -10 LU relative gates for integrated loudness, the EBU Tech 3342 loudness range (-20 LU gate, 10th to 95th percentile of short-term values), and true peak from the BS.1770-4 Annex 2 4x oversampling filter. Tests check EBU Tech 3341 cases 1–5, 9–14 (11 and 14 modelled) and 15–19 and Tech 3342 cases 1–4 within their tolerances. The parameters are in `core/master.ts` (`MASTER_SPECS`, `MASTER_PRESETS`, `LOUDNESS_TARGETS`).
361
+
362
+ Parameters (**bold** unit = shown in the simple menu; the rest are under `advanced`). Glue's `auto` make-up adds half the reduction a full-scale peak gets, so glue on and off compare near level-matched. Clean EQ corners above 0.45 × the render rate are clamped there.
363
+
364
+ <!-- master-params:start -->
365
+
366
+ | Unit | Param | Range | Default | Does |
367
+ | ----------- | --------- | -------------- | ------- | -------------------------------------------------------------------------------------- |
368
+ | **eq** | low | -12..12 dB | 0 | low shelf gain |
369
+ | eq | lowfreq | 20..1000 Hz | 100 | low shelf corner |
370
+ | **eq** | bell1 | -12..12 dB | 0 | first bell gain |
371
+ | eq | bell1freq | 40..16000 Hz | 400 | first bell centre |
372
+ | eq | bell1q | 0.1..10 | 1 | first bell width: higher is narrower |
373
+ | **eq** | bell2 | -12..12 dB | 0 | second bell gain |
374
+ | eq | bell2freq | 200..18000 Hz | 3000 | second bell centre |
375
+ | eq | bell2q | 0.1..10 | 1 | second bell width: higher is narrower |
376
+ | **eq** | high | -12..12 dB | 0 | high shelf gain |
377
+ | eq | highfreq | 1000..20000 Hz | 10000 | high shelf corner |
378
+ | **glue** | threshold | -40..0 dB | -18 | level where compression starts |
379
+ | **glue** | ratio | 1..10 | 2 | input:output above threshold (2 and 4 are the classic bus settings) |
380
+ | **glue** | attack | 0.1..30 ms | 10 | how fast it clamps down; slower lets transients through |
381
+ | **glue** | release | 50..1200 ms | 300 | how fast it lets go; set it to breathe with the tempo |
382
+ | glue | knee | 0..12 dB | 6 | soft-knee width |
383
+ | **glue** | makeup | 0..24 dB | 0 | gain after compression (on top of auto) |
384
+ | **glue** | auto | on / off | on | automatic make-up (half the reduction at 0 dBFS) so on/off compares near level-matched |
385
+ | glue | mix | 0..1 | 1 | dry/wet: below 1 is parallel compression |
386
+ | glue | hpf | 0..400 Hz | 0 | sidechain high-pass so the bass does not pump the mix; 0 is off |
387
+ | **tape** | drive | 0..24 dB | 6 | push into the curve: denser and louder |
388
+ | tape | bias | 0..0.5 | 0.1 | asymmetry: adds even harmonics |
389
+ | tape | tone | 2000..20000 Hz | 20000 | high-frequency roll-off after the curve; 20000 is off |
390
+ | **tape** | mix | 0..1 | 1 | dry/wet |
391
+ | **width** | width | 0..2 | 1 | side level: 0 is mono, 1 unchanged, 2 twice as wide |
392
+ | **width** | mono | 0..300 Hz | 120 | below this the mix is mono (keeps bass centred); 0 is off |
393
+ | **limiter** | ceiling | -12..0 dBTP | -1 | highest true peak out |
394
+ | **limiter** | gain | 0..24 dB | 0 | drive into the limiter (a target sets it itself) |
395
+ | **limiter** | release | 1..1000 ms | 100 | recovery time; short is louder, long is cleaner |
396
+ | limiter | lookahead | 0.5..10 ms | 5 | how early gain reduction starts before a peak |
397
+ | limiter | truepeak | on / off | on | catch peaks between samples (4x oversampled detection) |
398
+
399
+ <!-- master-params:end -->
400
+
401
+ `dawg render out.wav --normalize streaming` (or a LUFS number) applies a target for that export only, without changing the song; `--measure` prints the loudness line after any render. While the loop plays, the header shows `-14.1 LUFS (-14) · TP -1.0` for the last rendered loop, with or without a master, so you can read a mix before mastering it. The bracket is the target, and the reading turns to a warning colour when it is more than 0.5 LU off the target or the true peak passes the limiter's ceiling (-1 dBTP without one). With a master the loop monitors at the engine's 22,050 Hz while exports and `master measure` run at 48 kHz; the header shows the 48 kHz reading once it is measured in the background (a `~` marks the monitor estimate until then). EQ above about 9.9 kHz is not audible while monitoring but is in the export. The menu has it under **Mix & automation › master** (`/menu master`), where `Space` stages master changes for A/B like other sound edits. The agent has `set_master` and `measure_mix` (integrated, short-term and momentary LUFS, loudness range, true peak, spectral balance in five bands (sub, bass, low-mid, high-mid, high) and stereo correlation, with `bypass_master` to compare); it masters only when asked and measures before and after. In the SDK: `song({ master: { glue: { ratio: 2 }, limiter: { ceiling: -1 }, target: -14 } })` (SDK 1.17.0).
402
+
341
403
  ## Synth
342
404
 
343
405
  A synth track's voice is shaped by `track.synth`, a map of Strudel (superdough) parameter names to values. Parameter names are Strudel's wherever one means the same thing, and every Strudel alias is accepted on input (`att`, `lpe`, `fmi`, `vmod`…); the stored and printed form is the canonical name in the table. Only the parameters a document sets are stored, and an unset one takes its default, as in Strudel. A track with no `synth` and one of dawg's original instruments (`sine piano pluck bass saw square triangle`) renders byte-for-byte as before.
@@ -678,10 +740,11 @@ Prompt grammar: `euclid kick 4 16`, `euclid hat 7 16 rotate 2`, `euclid hat swin
678
740
  | `← →` / `h l` / `- +` | nudge the selected parameter |
679
741
  | `Tab` / `Shift-Tab` (`] [`) | next / previous parameter |
680
742
  | digits, `.`, `-`, Backspace | type a value, Enter applies |
681
- | Enter | add a row for a voice without one |
682
- | Space | audition the voice |
743
+ | Enter | add a row for a voice without one; keep (looping) |
744
+ | Space | start or stop the audition loop (staging) |
745
+ | `a` / `c` | A/B / solo ↔ in context, while the loop plays |
683
746
  | `x` / Delete, `f` | remove the row and its notes / freeze it to notes |
684
- | Esc | back (cancels typing, then closes) |
747
+ | Esc | cancel typing, revert staged changes, then close |
685
748
 
686
749
  ## Chords
687
750
 
@@ -711,29 +774,98 @@ Agent. `suggest_progression {key?, chords? | style?, length?, seed?, sevenths?,
711
774
 
712
775
  SDK. `chord("Cm7", start, length, opts)` and `progression("ii7 V7 Imaj7", { key, from, each, perform, pattern, rate, octaves, strum, seed, voicing, spread, part, bass })` expand to notes at evaluation; see [docs/project-format.md](./docs/project-format.md). The vendored SDK stays one import-free file: `core/sdk/v1.ts` carries a generated copy of the engine (`bun core/sdk/sync-chords.ts`, checked by a test).
713
776
 
777
+ ## Performance and expression
778
+
779
+ Notes can say how they are played, and tracks how they perform. Every field is optional: a note or track without them sounds exactly as before. Expression is applied at render to copies of the notes, so the score keeps what you wrote.
780
+
781
+ Per note (the focused track; a target is `all`, the default, `last` (the note added last), `bar 3`, `bars 2-4` or note ids; the reply names the scope and how many notes changed):
782
+
783
+ | Command | Does |
784
+ | ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
785
+ | `art staccato\|legato\|accent\|tenuto\|marcato\|ghost\|off` | staccato half length; legato held into the next note with a short overlap; accent +0.2 velocity; tenuto full length +0.05; marcato two thirds +0.3; ghost half length ×0.4 |
786
+ | `glide 60ms` with a target | portamento into each note from the previous pitch |
787
+ | `bend -200 \| scoop \| fall \| doit \| 0:-200 0.25:0` | a pitch curve over the note in cents (`at` 0..1 of its length), linear between points |
788
+ | `vibrato 5.5 30 [0.2]` | rate Hz, depth cents either side, delay s (then a 0.15 s fade-in) |
789
+
790
+ Per track:
791
+
792
+ | Command | Does |
793
+ | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
794
+ | `glide 80ms [legato\|mono\|poly]` | `legato` (default): one voice, slides only into a note that overlaps the last, no envelope retrigger; a note with its own glide also slides from a note ending at most a 16th earlier (the TB-303 slide, set on the destination note); `mono` always slides; `poly` slides each voice from the previous chord. Mono and legato glides approach the pitch exponentially (RC), poly linearly; `glide 0` is off |
795
+ | `pedal 0-3.5 4-7.5`, `pedal bars`, `pedal down\|half\|up <beat>`, `pedal off` | sustain pedal (CC64; spans in beats from 0, `bars 2-3` in bars from 1): notes released while it is down ring until it lifts; `half` lets them fade; `bars` re-pedals on each downbeat (lift and catch) and replaces the lane |
796
+ | `velcurve linear\|soft\|hard\|fixed [v]` | soft (v^0.5) brings quiet notes up, hard (v^2) needs a firm touch, fixed plays every note at `v` (default 0.8) |
797
+ | `humanize 8 [5 [10]] [seed n]` | timing ±ms, velocity ±%, length ±%, seeded per note at render (`humanize reseed` for a new take, `off` to remove) |
798
+ | `expression` | what the focused track does |
799
+
800
+ Precedence with the synth: a note's settings override the track's synth. A note's `vibrato` replaces the synth's `vib`/`vibmod`, a `bend` replaces the pitch envelope (`penv`), and a gliding note ignores ZzFX `slide`. Render order: articulation, then humanize velocity, then pedal, then glide and mono voicing (on written timing), then humanize timing and length, then the velocity curve.
801
+
802
+ Menu: **Sound › performance** has glide time (ms) and mode, sustain pedal (off or every bar), velocity curve, humanize timing, velocity and length, and new take; the loop stages them for A/B like any other sound change. Agent: `set_expression` (articulation, glide, bend, vibrato and humanize over note ids or a beat range) and `set_performance` (track glide, pedal, velocity curve and humanize). SDK (1.15.0): `note("C4", 0, 1, 0.8, { art: "staccato", glide: 0.05, bend: [[0, -200], [0.25, 0]], vibrato: { rate: 5.5, depth: 30 }, humanize: { timing: 10 } })`, `expr(notes, { art: "ghost" })` for many notes, and `track({ glide: 0.08, pedal: [[0, "down"], [4, "up"]], velocityCurve: "soft", humanize: { timing: 8, seed: 7 } })`.
803
+
804
+ ## Tunings and scales
805
+
806
+ Every project plays in 12-tone equal temperament at A4 = 440 Hz until it says otherwise. A song tuning, a track tuning or a note's cents change only the frequencies; notes stay MIDI keys, so editing, chords, play mode and exports work the same. MIDI export carries a tuning with the MIDI Tuning Standard (a single-note tuning SysEx per tuned track, selected with RPN 3), and writes glides, bends, vibrato and note cents as pitch bend (range ±24 semitones) on notes that sound alone on their track; synths without MTS play 12-TET keys. A project without any of these renders byte-identically to 0.4.
807
+
808
+ Tunings (`core/tuning.ts`). A tuning is one table (`edo: 19`, `ratios: ["9/8", "5/4", …, "2/1"]`, `cents: [231, 474, …, 1200]`, a Scala `scl` file, or a library `name`) plus `ref` (the 12-TET A4 in Hz, default 440, that fixes the root key's pitch), `root` (the key of degree 0) and `map` (`linear` or `nearest`). The last table entry is the period, usually 1200 cents (2/1).
809
+
810
+ - The library: `12-tet`, `19-edo`, `24-edo`, `31-edo`, `pythagorean`, `just` (5-limit), `7-limit`, `well-tuned-piano` (La Monte Young's 7-limit key map from E♭, after Kyle Gann's published ratios), `pelog` and `slendro` (Kunst's and Surjodiningrat's averages), `nyamaropa` (a Shona mbira after Berliner), `hindustani` (twelve just svaras), `shruti` (the 22 shrutis), maqam and dastgah tables (`rast`, `bayati`, `saba`, `sikah`, `huzam`, `shur`, `homayoun`, `chahargah`, `segah`, `nava`) and raga tables (`yaman`, `bhairav`, `kafi`, `bhairavi`, `todi`, `marwa`, `darbari`, `malkauns` and more, each built from the raga's own svaras over 5-limit defaults). `tuning list` shows each one with a line about it. The gamelan, mbira, maqam and raga tables are marked approximate: every gamelan and every mbira is tuned differently, and maqam and raga intonation varies by tradition and performer.
811
+ - Anchoring follows Scala and Surge's tuning library: the root key sounds at its 12-TET frequency under `ref`, and the table counts up from it. So `ref` is the 12-TET A4 that fixes the root, and A4 itself sounds at exactly `ref` only when the root is an A (in `just` from C, A4 is 5/3 above C4, about 436 Hz at ref 440). For an exact reference key and frequency, use a `.kbm` keyboard mapping. The root defaults to the library tuning's own (E♭ for the Well-Tuned Piano), else the song key's tonic in octave 4, else C4.
812
+ - Mapping. Twelve-step tables retune the twelve keys. Other sizes default to `linear`, one key per step, as Scala, Surge and Ableton's tuning system do (19-EDO puts the octave 19 keys up). `map: "nearest"` keeps the piano layout instead and plays each key at the nearest table pitch, which suits pentatonic gamelan tables on a normal keyboard.
813
+ - Frequencies at or above 20 kHz count as unmapped keys and stay silent, so a coarse table (`edo: 1`) cannot alias at the top of the keyboard.
814
+ - Scala. `tuning scl tunings/slendro.scl [kbm tunings/white.kbm]` copies a file from outside the project into `tunings/` and stores the path. The `.scl` parser follows the Scala specification: `!` comments, a description line, the count, then one pitch per line (a period means cents, otherwise a ratio or integer), the 1/1 implicit and the last pitch the period. A `.kbm` keyboard mapping (size, first and last key, middle key, reference key and frequency, octave degree, then the map with `x` for unmapped keys) overrides `ref` and `root`. Bad files are refused with a line number.
815
+ - Track tuning overrides the song's field by field: a track with its own table uses it, and `ref`, `root` and `map` fall back to the song's. Drum kits ignore tunings.
816
+ - Note cents: `note("E4-14c", 0)`, `add E4-14c at 0` or `cents n3 -14` give one note a static offset of up to ±1200 cents on top of any tuning.
817
+
818
+ Scales (`core/chords.ts`). The song key string now names any scale: `"D dorian"`, `"A harmonic-minor"`, `"E hijaz"`, `"C yaman"`, `"C messiaen-3"`. The library has the church modes, harmonic and melodic minor, phrygian dominant, major and minor pentatonic, blues and major blues, the maqamat `hijaz`, `bayati`, `rast`, `saba`, `kurd`, `nahawand` and `nikriz`, the dastgahs `shur`, `homayoun`, `chahargah`, `segah` and `nava`, the neutral-third maqamat `sikah` and `huzam`, common Hindustani ragas (`yaman`, `bhairav`, `kafi`, `bhairavi`, `asavari`, `khamaj`, `todi`, `purvi`, `marwa`, `darbari`, `malkauns`, `bhupali`, `durga`) by their thaat or aroha notes, and Messiaen's seven modes of limited transposition. Every existing key string reads as before. Quarter-tone scales (Bayati, Rast, Saba) name their half-flat degrees; setting such a scale suggests the matching library tuning so they sound.
819
+
820
+ Chords. The chord engine stays twelve-tone: a scale outside the seven diatonic modes uses the nearest diatonic mode for key-mode chords. In a twelve-key tuning chords keep their keys (so in `just` they sound pure); in a linear non-12 tuning such as 19-EDO, the chord tools and play-mode chord phrases move each written pitch to the key that sounds nearest, so a C major triad becomes steps 0, 6 and 11.
821
+
822
+ Play mode. `i` (or `/play degrees`) toggles scale-degree mapping: the home row `A S D F G H J K L ; '` plays consecutive degrees of the song scale, or every step of a linear non-12 tuning (the nearest step to each scale note when a key is set), and the upper row is off. When a tuning has more steps than the home row (19- or 31-EDO with no key), `Z`/`X` page by eleven degrees instead of a period, so every step is reachable. `/play chromatic` turns it back off. The header shows the scale and tuning.
823
+
824
+ Highway. A note that sounds more than half a cent away from 12-TET shows a compact cents tag (`+14`, `−32`) after its label when there is room. In a twelve-key table the tag is measured from the note's own lane. In a linear non-12 table (19-EDO, slendro) the lane is only the key, so the tag names the 12-TET pitch it is measured from (`D#−47`, or `C` when it sounds right on it), and lane labels mark the tuning's periods (every 19 keys from the root) instead of every C.
825
+
826
+ Commands and menu.
827
+
828
+ | Command | Does |
829
+ | ------------------------------------------- | ------------------------------------------------------ |
830
+ | `tuning <name>` · `edo <n>` · `off` | song tuning (`off` is 12-TET) |
831
+ | `tuning ratios 9/8 5/4 … 2/1` · `cents …` | a custom table |
832
+ | `tuning scl <file> [kbm <file>]` | a Scala scale and optional keyboard mapping |
833
+ | `tuning ref <hz>` · `root <note>` · `map …` | reference pitch, root key, `linear` or `nearest` |
834
+ | `tuning track <…>` · `track off` | the focused track's own tuning; `off` follows the song |
835
+ | `tuning list` · `scale list` | the libraries |
836
+ | `scale [<tonic>] <name>` | the song key and scale (`scale D hijaz`) |
837
+ | `cents <id> <±c>` | detune one note |
838
+
839
+ The menu has the same in Project › tuning & scale (song tuning, ref, root, map, equal steps, ratios, cents, Scala file, keyboard map, scale and tonic; `/menu tuning` jumps there) and Sound › tuning (the track's, showing inherited song values as `· song`). The Chords tonic row keeps a library scale (`D hijaz` stays hijaz). Agent: `set_tuning {target?, name? | edo? | ratios? | cents? | scl?, kbm?, ref?, root?, map?, off?}` and `set_scale {tonic?, scale}` run the same commands, `add_notes` and `update_notes` take an optional `cents` per note (0 clears it), and the agent brief carries the song and track tunings and a `cents` column when a focused note has one. SDK 1.16.0: `song({ tuning })`, `track({ tuning })` (a name string or an object), and pitch strings with a cents suffix (`note("E4-14c", 0)`, `seq("C4 E4-14c G4+2c")`).
840
+
841
+ Rendering. Synth and wavetable voices start at the tuned frequency; keyed samplers repitch by the ratio between the tuned and the 12-TET frequency, and one-shot samplers apply note cents only. Live playback, audition and export use the same table, so what you hear is what renders.
842
+
714
843
  ## Play mode (computer keyboard)
715
844
 
716
845
  `Ctrl-P` or `/play` turns the computer keyboard into a piano for the focused track, using the "musical typing" layout GarageBand, Logic, BandLab, FL Studio and Ableton share. `Esc` or `/play off` leaves it and every normal binding is back. Typing `/` starts a slash command without leaving the mode (`/click 40%`, `/play off`).
717
846
 
718
- | Key | Does |
719
- | ----------------------- | ------------------------------------------------------------------ |
720
- | `A S D F G H J K L ; '` | white keys C D E F G A B C D E F from the base octave |
721
- | `W E T Y U O P` | black keys C♯ D♯ F♯ G♯ A♯ C♯ D♯ (none on `R` or `I`, like a piano) |
722
- | `Z` / `X` | octave down / up (clamped to the score's pitch range) |
723
- | `C` / `V` | velocity down / up in steps of 16 (1–127, shown in the header) |
724
- | Shift + note | sustained note: rings until a plain key or `Tab` |
725
- | `Tab` | sustain latch on/off (off releases every sustained note) |
726
- | `R` | record arm on/off |
727
- | `Shift-R` | replace: bars you play over are cleared first (default: overdub) |
728
- | `M` | click on/off |
729
- | `Space` | play/stop; with record armed and stopped, counts in, then records |
730
- | `?` | the play-mode keys and current settings (any key closes) |
731
- | `/` | type a slash command without leaving (`/click 40%`) |
732
- | `Esc` | leave play mode |
847
+ | Key | Does |
848
+ | ----------------------- | ------------------------------------------------------------------------ |
849
+ | `A S D F G H J K L ; '` | white keys C D E F G A B C D E F from the base octave |
850
+ | `W E T Y U O P` | black keys C♯ D♯ F♯ G♯ A♯ C♯ D♯ (none on `R` or `I`, like a piano) |
851
+ | `Z` / `X` | octave down / up (clamped to the score's pitch range) |
852
+ | `C` / `V` | velocity down / up in steps of 16 (1–127, shown in the header) |
853
+ | Shift + note | sustained note: rings until a plain key or `Tab` |
854
+ | `Tab` | sustain pedal latch on/off (off releases every sustained note) |
855
+ | `R` | record arm on/off |
856
+ | `Shift-R` | replace: bars you play over are cleared first (default: overdub) |
857
+ | `M` | click on/off |
858
+ | `I` | scale-degree keys on/off (see [Tunings and scales](#tunings-and-scales)) |
859
+ | `Space` | play/stop; with record armed and stopped, counts in, then records |
860
+ | `?` | the play-mode keys and current settings (any key closes) |
861
+ | `/` | type a slash command without leaving (`/click 40%`) |
862
+ | `Esc` | leave play mode |
863
+
864
+ Recording keeps each note's velocity from `C`/`V`. Sustain is recorded the way Logic's Musical Typing records its `Tab` sustain key: as pedal events (`down` when `Tab` latches or Shift starts holding, `up` when it lets go) on the track, at the playhead and not quantized, while the notes keep the length the key was held. Terminals report key-down only, so `Tab` latches rather than holds. A chord-mode press keeps its held length on its voices instead.
733
865
 
734
866
  ### Chord mode
735
867
 
736
- Play mode has a chord sub-mode modelled on the Orchid's Key mode. It is `auto` by default when the focused track can play chords (pitched synths, piano, soundfonts, keyed samplers; not tracks whose instrument, name or id says bass, kit, drum or perc), otherwise `manual`. Choosing a mode by hand (`Q`, `/chords`, the menu) sticks for the session.
868
+ Play mode has a chord sub-mode modelled on the Orchid's Key mode. It is `auto` by default when the focused track can play chords (pitched synths, piano, soundfonts, keyed samplers; not tracks whose instrument, name or id says bass, kit, drum or perc) in 12-TET without a mono or legato glide, otherwise `manual`, so a track in pelog, just intonation or another non-12 tuning, or a TB-303-style legato line, records single notes. Choosing a mode by hand (`Q`, `/chords`, the menu) sticks for the session.
737
869
 
738
870
  - `auto`: each note key plays the diatonic chord of the song key on that root (C major: `S` plays Dm, `G` plays G). Keys outside the scale borrow from the parallel major or minor. The strip labels every white and black key with its chord.
739
871
  - `manual`: note keys play single notes as before; latch a chord type or extension and they play that chord on the pressed root.
@@ -772,6 +904,39 @@ Recording: with record armed and the transport running, each note is quantized t
772
904
 
773
905
  `/click on|off|<volume>` (`/click 40%`, `/click 0.4`) or `M` in play mode. An accented downbeat and lighter beats at the transport tempo and the score's meter, mixed as a separate monitoring bus. It is never part of a loop render, a stem, `dawg render`, or `/export`; tests compare those byte for byte with the click on. `/count-in 0|1|2` sets how many bars of click play before recording starts (default 1); the header counts down and flashes the beat, so it also works with backend `none`.
774
906
 
907
+ ## Tempo and meter
908
+
909
+ Ticks stay score time; an optional song `time` field turns them into seconds (`core/tempo.ts`). A beat is a quarter note and BPM counts quarter notes, as in Standard MIDI Files, so a 7/8 bar lasts 3.5 beats. Without `time` and without track `time` everything is the 0.4 arithmetic and renders byte-identically.
910
+
911
+ ```text
912
+ tempo 90 at bar 9 step change on bar 9's downbeat (beats count from 0, bars from 1)
913
+ tempo 140 at 64 ramp glide from the previous tempo into 140 at beat 64 (linear, equal BPM per beat)
914
+ tempo 70 at bar 17 exp exponential glide: equal ratio per beat, even to the ear
915
+ tempo remove bar 9 | tempo clear | tempo map
916
+ rit 4 bars to 80 ritardando over the last 4 bars; rit/accel default to 75% / 133% over the last 2 bars
917
+ a tempo [at bar <n>] step back to the tempo before the last rit/accel (default: the bar after it ends)
918
+ tempo primo [at bar <n>] step back to the start tempo
919
+ accel 8 bars to 174 at bar 9 accelerando from bar 9; `beats` instead of `bars`, `exp` for an exponential curve
920
+ fermata at 31 2 hold beat 31 for 2 extra beats; fermata [at <beat>|[at] bar <n>|[at] end] [<extra beats>], default 2
921
+ meter 7/8 at bar 5 meter change on a bar line, lasting until the next one; meter 7/8 alone sets the whole song
922
+ meter remove bar 5 | meter clear
923
+ track rate 3/2 polytempo: the focused track plays at 1.5× the song tempo (0.125..8 or a/b)
924
+ track phase 0.5 start the track half a beat later
925
+ track cycle 3 polymeter: loop the track's first 3 beats against the song's bars
926
+ track phasing 3 [over 48] continuous drift: a 3-beat cycle gains one cycle every 48 beats, then realigns (needs a loop of whole spans)
927
+ track phasing 3 hold 8 stepped, as in Piano Phase: hold in step 8 cycles, move a sixteenth ahead over 2 (drift 2, shift 0.25)
928
+ track time off follow the song again
929
+ ```
930
+
931
+ - **Tempo events** (`time.tempo: [{tick, bpm, ramp?}]`) follow `tempoBpm`, which stays the start tempo. An event without `ramp` is a step; `ramp: "linear"` or `"exp"` glides from the previous tempo into the event. Ramps are defined over score position (beats), as Logic's and Cubase's tempo curves and MuseScore's gradual tempo changes are, and the renderer integrates them in closed form, so a ramp lands on the same sample however it is rendered.
932
+ - **rit / accel** write a pin at the start (the tempo in effect there) and a ramp to the target. MuseScore's defaults are used when no target is given: ritardando to 75%, accelerando to 133%. A rit refuses a faster target and an accel a slower one. `a tempo` and `tempo primo` are plain steps back.
933
+ - **Fermatas** (`time.fermatas: [{tick, beats}]`) lengthen the beat that starts at `tick` to `1 + beats` times its length (the meter's felt beat: a dotted quarter in 6/8 or 12/8, a quarter otherwise): everything inside that beat slows evenly and everything later moves back. WAV and MIDI export time it the same way (MIDI writes a slower tempo over the beat), so a held beat may last at most 16.777 s, the slowest tempo a MIDI file can write; a longer hold is refused with a message.
934
+ - **Meter changes** (`time.meter: [{bar, beatsPerBar, beatUnit}]`, `bar` 0-based in the file) take effect on bar lines only. The bar lasts `beatsPerBar × 4 / beatUnit` beats; `beatsPerBar` on the song stays the default meter. The click accents each bar's downbeat and clicks the meter's beat unit (dotted in compound meters such as 6/8), the count-in uses the meter and tempo at the punch-in bar, the highway draws bar lines and numbers from the meter map, and play-mode replace erases the bars the meter map says.
935
+ - **Track time** (`track.time: {rate, phase, cycle, steps?}`, phase, cycle and shift in ticks in the file) places a track's notes on the song timeline: the first `cycle` ticks repeat every `cycle / rate` song ticks, shifted `phase` song ticks later, restarting with every song loop. Two identical tracks with one on `track phasing 4` drift apart a little each cycle and line up again after `over` beats (default the loop), the continuous tape phasing of Reich's _It's Gonna Rain_ and _Come Out_; `over` and the cycle must divide the loop, and the prompt names the bars it needs otherwise. `track phasing 3 hold 8` stores `steps: {shift, hold, drift}` instead of a rate: the track holds in step for `hold` cycles, then moves `shift` ahead over `drift` cycles, and repeats, the shift-and-lock process of _Piano Phase_ and _Drumming_. Automation stays in song time.
936
+ - **Everywhere**: the offline renderer, the live engine and audition loop, the transport clock (beat ⇄ wall time, shared by every window through dawgd), click and count-in, recording quantization and the highway use the same map. `/export song.mid` and `dawg render song.mid` write a format-1 Standard MIDI File: track 0 carries the time-signature (FF 58) and tempo (FF 51) meta events, ramps are written as tempo steps every sixteenth whose BPM is the exact average over the step, so each step boundary lands on the same second as the WAV.
937
+
938
+ The menu has the same controls under **Project › Tempo & meter** (`/menu tempo`): the tempo map (add a change, a ramp, a rit or accel, a tempo, tempo primo, a fermata), meter changes, and the focused track's rate, phase, cycle, phasing and stepped phasing. The agent's `set_time` tool takes the same actions, and the SDK has `tempo`, `ramp`, `rit`, `accel`, `aTempo`, `tempoPrimo`, `fermata`, `meter`, `phasing` and `stepPhasing` (see **Project files and SDK**).
939
+
775
940
  ## Drum patterns and kits
776
941
 
777
942
  **Patterns.** dawg ships a library of 31 starting grooves, written for dawg from the defining placements of each style (no transcriptions). A pattern is a set of rhythm rows, one per voice, so after applying it every part is still a few Euclidean or grid parameters you can change in `/euclid`, with the prompt grammar, or in `track.ts`.
@@ -801,28 +966,70 @@ Patterns: `house`, `disco`, `techno`, `minimal`, `electro`, `breakbeat`, `amen-s
801
966
 
802
967
  Sample kits from packs (`/kit 909` and the rest, see **Sample packs**) sit in the same picker after the synth kits. `/kit syn909` on a sampler kit turns it back into a synth kit track, moving hits and rows to the drum voices of the same name. The agent has `list_drum_patterns`, `apply_drum_pattern {name, trackId?, tempo: auto|keep|set}` and `set_drum_kit {kit, trackId?}`, and its prompt starts genre grooves from a pattern.
803
968
 
969
+ ## Arrange (sections and form)
970
+
971
+ A song can name its parts. A **section** is a named bar range (`intro`, `verse`, `pre`, `chorus`, `build`, `drop`, `breakdown`, `bridge`, `outro`, or any name up to 32 characters) with optional per-section track mutes and variations. The **form** is an ordered list of sections with repeats (`verse verse chorus verse`, `intro verse chorus*2 outro`); a repeated pass plays identically, so `section dup chorus as last chorus` and a variation on the copy make a different last chorus that playback and export follow. A song without sections or a form plays and renders exactly as before, byte for byte.
972
+
973
+ Sections are markers over the timeline, like the arranger track in Studio One or Cubase and Logic's arranger: marking one never moves notes, while `dup`, `move` and `delete` take the section's bars with them and ripple the bars after it. Sections may overlap, so `chorus` and `chorus 2` can share bars and differ only by mutes and variations. Bars are numbered from 1 at the prompt, as a DAW ruler shows them, and from 0 in the score and the SDK.
974
+
975
+ | Command | Does |
976
+ | --------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
977
+ | `section` | list sections, the form and the looped section |
978
+ | `section [mark] chorus 9-16` | mark bars 9–16 as `chorus` (re-marks an existing one) |
979
+ | `section add [<name>] [<n> bars]` | a new section after the last one, growing the song |
980
+ | `section dup <name> [as <new>]` | copy the section and its bars right after it |
981
+ | `section move <name> to <bar>` / `left` / `right` / `before <x>` | move it with its bars |
982
+ | `section rename <name> to <new>` | rename it (the form and the loop follow) |
983
+ | `section delete <name>` / `section unmark <name>` | remove it with its bars (ripple), or only the marker |
984
+ | `section mute <name> [<track>…]` / `unmute` | silence tracks in that section (the focused track by default) |
985
+ | `section vary <name> [<track>] +12 [gain 0.8]` / `off` | transpose (semitones) or scale velocities of a track in that section |
986
+ | `section reset <name>` | clear its mutes and variations |
987
+ | `section loop <name>` / `section loop off` | loop that section in playback (the per-song cycle, saved with the project) |
988
+ | `section jump <name>` | move the playhead to it |
989
+ | `form intro verse*2 chorus outro` / `form off` / `form bake` | set the form; clear it; or write it out as a linear score (one undo step) |
990
+ | `build into <section> [<n> bars]` | a build on the n bars (default 4) before the section, landing on its downbeat |
991
+ | `build [<section> \| <a>-<b>] [<n> bars] [riser] [roll] [sweep] [uplifter]` | a build over the section (its last n bars with `<n> bars`) or the bars; default the song's last four bars; all four layers by default |
992
+ | `drop [<section> \| at <bar>] [cut <beats>] [no impact]` | a pre-drop cut (1 beat of silence by default, up to two bars) and an impact on the downbeat; bare `drop` lands on the section named `drop` or `chorus`, else on the bar after the last build (adding that bar when the build ends the song); with neither it asks for `drop chorus` or `drop at 17`. A cut that clips a build's uplifter retunes its rise to end at the cut |
993
+ | `fill [<section> \| at <bar>] [toms\|roll\|kick] [<n> beats] [no crash]` | a drum fill on the beats before the section (1 by default, ½ beat up to two bars), or at every section boundary |
994
+
995
+ Playback follows the form; with a section looped it loops just that section, with its mutes and variations, and the highway, play mode and auditions stay inside it (an audition region is clipped to the looped section). Export (`dawg render`, the agent's preview) always plays the whole form and ignores the section loop; `dawg render out.wav --section chorus` renders one section. Every WAV covers the whole song (or section) plus its tail; a long song renders in windows and is capped at 15 minutes, and a held note that crosses a window seam is crossfaded so long drones stay smooth. Exports mark the form: WAV renders carry a `cue ` point and `LIST adtl` label at each section start, and MIDI exports an FF 06 marker. Sections count bars in one meter: a compound meter held from bar 1 (`meter 6/8`, `song({ meter: [12, 8] })`) works, while meter changes later in the song and sections exclude each other.
996
+
997
+ Generators write ordinary notes, tracks and automation, so everything they make can be edited or undone (one step per command):
998
+
999
+ - **riser**: a `riser` track of filtered white noise whose cutoff and volume ramp up over the range.
1000
+ - **roll**: an accelerating snare roll on the kit track (quarters, 8ths, 16ths, then 32nds, crescendo), on a `roll` kit track when the song has none.
1001
+ - **sweep**: unfiltered or high-pass pitched tracks playing in the range get a high-pass sweep up to 1.2 kHz, and low-pass tracks open from a tenth of their cutoff to it; both sweep on an octave (log-frequency) curve and snap back on the next downbeat. The riser's cutoff rises the same way.
1002
+ - **uplifter**: a `uplifter` supersaw with a rising pitch envelope, tempo-synced to land on the bar after the range (at most 10 s); builds of another length get their own `uplifter-<bars>` track so each lands on time.
1003
+ - **cut** and **impact**: notes starting in the last beats before the drop are removed and held notes are shortened; an `impact` sine with a pitch drop and noise hits the downbeat.
1004
+ - **fill**: 16ths replace the groove in the fill's beats: `toms` starts on the snare and steps down high, mid and low toms (GM 50, 47, 45; the built-in kit folds them onto its one tom), `roll` is snare and `kick` kick and snare. The crash on the next downbeat is an open hat plus a kick, since the built-in kit has no cymbal.
1005
+
1006
+ Section mutes and variations govern every bar of their section: a note held from an earlier section stops at the bar where a section that mutes its track begins, so a song sounds the same played straight through and through the form. Generators place their cut and crash by bar order in the score; with a form that reorders sections, run them on the bars that precede the target in the form (`build 13-16`, `fill at 17`).
1007
+
1008
+ The arrangement strip is one row under the header that shows the sections over the timeline (`▏verse ▏chorus`), the looped section reversed, the section under the playhead bold, and the playhead as `▼`. It appears only when the song has sections. The menu's **Arrange** section (`/menu arrange`) lists every section; each opens loop, jump here, a mute toggle for every track, transpose and gain, build (into it, over it, or custom length and layers), drop (cut length and impact), fill (style, beats and crash), duplicate, duplicate as, move to, move left and right, rename, clear mutes and variations, unmark and delete. Agent tools: `list_sections`, `edit_section` (mark, add, duplicate, move, rename, delete, unmark, mute, unmute, vary, reset, loop, unloop), `set_form` and `add_transition` (build into or over a section, drop, fill). In `song.ts`: `song({ sections: [{ name: "verse", startBar: 0, bars: 8 }, …], form: "intro verse*2 chorus", loopSection: "chorus" })` (SDK 1.18.0).
1009
+
804
1010
  ## Menus
805
1011
 
806
- `/menu` or `Ctrl-K` (on an empty prompt, in play mode too) opens the edit menu, drawn with the same overlay as the model picker. Every edit the agent can make is reachable from it with keys alone. Each row shows a plain label and the current value with its unit (s, Hz, oct, st, dB, BPM, bars); the line under the list describes the focused row and shows, dimmed, the prompt command the row runs, so the menu teaches the commands. `/menu <section>` opens a section directly (`/menu effects`); the old names `parameters`, `sounds`, `track`, `automation` and `transport` still work.
1012
+ `/menu` or `Ctrl-K` (on an empty prompt, in play mode too) opens the edit menu, drawn with the same overlay as the model picker. Every edit the agent can make is reachable from it with keys alone. Each row shows a plain label and the current value with its unit (s, Hz, oct, st, dB, BPM, bars); the line under the list describes the focused row and shows, dimmed, the prompt command the row runs, so the menu teaches the commands. `/menu <section>` opens a section directly (`/menu effects`, `/menu arrange`); the old names `parameters`, `sounds`, `track`, `automation` and `transport` still work.
807
1013
 
808
- | Section | Rows (most used first) |
809
- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
810
- | Sound | instrument, preset; a synth's attack, decay, sustain, release, filter cutoff/res/env, detune, vibrato, FM amount, then **advanced** with every synth parameter; a wavetable track's table and wavetable parameters; a sampler's mode and voices; **browse sounds** (instruments, wavetables, packs) |
811
- | Effects | the core effects (filter, auto filter, distortion, tremolo, compressor, chorus, delay, reverb) with presets and simple parameters, **more effects** (dj filter, vowel, bitcrush, phaser, leslie, post gain, orbit, duck), and **advanced** per effect (see Effects) |
812
- | Rhythm | the euclid editor (`/euclid`), drum patterns (`/pattern`), drum kits (`/kit`, synth then samples) |
813
- | Chords | play-mode chord mode, key tonic and mode, voicing, spread, bass, sevenths, perform, pattern, arp rate, arp octaves, progression, style |
814
- | Mix & automation | the focused track's name, mute, solo, volume, pan; **all tracks** (choosing one focuses it); **automation**: each `AUTOMATION_LANES` lane with its points as `beat N value` rows, add points, ramp, clear lane |
815
- | Project | play, tempo, beats per bar, loop length, grid, click, count-in bars |
1014
+ | Section | Rows (most used first) |
1015
+ | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1016
+ | Sound | instrument, preset; a synth's attack, decay, sustain, release, filter cutoff/res/env, detune, vibrato, FM amount, then **advanced** with every synth parameter; a wavetable track's table and wavetable parameters; a sampler's mode and voices; **performance** (glide, pedal, velocity curve, humanize); **browse sounds** (instruments, wavetables, packs) |
1017
+ | Effects | the core effects (filter, auto filter, distortion, tremolo, compressor, chorus, delay, reverb) with presets and simple parameters, **more effects** (dj filter, vowel, bitcrush, phaser, leslie, post gain, orbit, duck), and **advanced** per effect (see Effects) |
1018
+ | Rhythm | the euclid editor (`/euclid`), drum patterns (`/pattern`), drum kits (`/kit`, synth then samples) |
1019
+ | Chords | play-mode chord mode, key tonic and mode, voicing, spread, bass, sevenths, perform, pattern, arp rate, arp octaves, progression, style |
1020
+ | Mix & automation | the focused track's name, mute, solo, volume, pan; **all tracks** (choosing one focuses it); **automation**: each `AUTOMATION_LANES` lane with its points as `beat N value` rows, add points, ramp, clear lane; **master**: target, eq, glue, tape, width, limiter |
1021
+ | Project | play, tempo, beats per bar, loop length, grid, click, count-in bars |
1022
+ | Arrange | every section (loop, jump, mute, transpose, gain, build, drop, fill, duplicate, move, rename, unmark, delete), mark bars, add section, form, loop off (see Arrange) |
816
1023
 
817
1024
  Every list, picker and editor uses the same keys (see **Keys** below). In the menu:
818
1025
 
819
1026
  | Key | Does |
820
1027
  | --------------------------- | ------------------------------------------------------------------------- |
821
1028
  | `↑` `↓` / `k` `j` | move |
822
- | `Enter` / `→` / `l` | open a section, pick from a list, or start typing a value |
1029
+ | `Enter` / `→` / `l` | open a section, pick from a list, or open a number's fader drawer |
823
1030
  | `←` `→` / `h` `l` / `-` `+` | adjust a value by its step (cutoff moves 25%) or cycle a choice |
824
1031
  | `Space` | toggle on/off; elsewhere, hear the focused track (see Previewing changes) |
825
- | digits | type a value on a focused value row; `Enter` sets it, `Esc` cancels |
1032
+ | digits | type a value; `Enter` stages it in the fader drawer, `Esc` cancels |
826
1033
  | `/` | filter the current list by name, value or command |
827
1034
  | `x` / `Delete` | reset the focused value to its default; on an automation point, remove it |
828
1035
  | `Esc` / `←` / `h` | clear the filter, then back one level, then close |
@@ -830,13 +1037,67 @@ Every list, picker and editor uses the same keys (see **Keys** below). In the me
830
1037
 
831
1038
  Automation rows take `beat:value` pairs (`2:800` or `0:200 4:8000`); a ramp is two pairs, start and end, and the renderer interpolates between points. Turning an effect's first field up switches it on with defaults. Each change runs the command it shows through the normal prompt path, so it is one `ScoreOperation`, one receipt, one undo step, and it syncs to other windows and the project files.
832
1039
 
1040
+ ## Fader drawer
1041
+
1042
+ Editing a number opens a fader drawer: a panel docked directly above the prompt, over the bottom of the piano roll, which stays visible above it. It opens from `Enter` (or a second click) on a number row in the menu, or from a bare parameter at the prompt: `volume`, `pan`, `fx filter` (every filter param, focused on the first number) or `fx reverb mix` (focused on mix). The drawer stacks every param of that device (all of the filter's, or the Mix screen's), so one drawer covers a device.
1043
+
1044
+ ```text
1045
+ ╭─ menu › Effects › Filter ─────────── loop off · B staged 1 · ● staged [keep] [revert]─╮
1046
+ │ type lpf │ hpf │ bpf │
1047
+ │ │
1048
+ │ › cutoff 1200 Hz ← 800 Hz 20 Hz … 20000 Hz │
1049
+ │ [−] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┃━━━●───────────────────────────────── [+] │
1050
+ ╰──────── ←→ adjust · ⇧←→ coarse · [ ] fine · ↑↓ param · 0-9 type · d default · esc revert ─╯
1051
+ ```
1052
+
1053
+ Each field shows its name, current value with unit, the committed value while a change is staged (`1200 Hz ← 800 Hz`), its range, and a bar with `[−]` `[+]` buttons; the bar marks the committed position with `┃`. Wide positive ranges (cutoff, delay time) map on a log scale. Choice params (filter type, presets, on/off) are segmented selectors in the same drawer. Ranges, steps, units and formatting come from the menu row, which takes them from `core/params.ts`, the effect specs and the automation lanes.
1054
+
1055
+ Every change is staged on the audition loop (see **Previewing changes**), filed under its field so repeated nudges replace one staged edit; the piano roll and the loop, when it plays, follow at once. `Enter` keeps everything staged as one revision and one undo step; `Esc` reverts. A setting the loop cannot stage (tempo, loop length) applies directly.
1056
+
1057
+ | Key | In the drawer |
1058
+ | --------------------------- | ----------------------------------------------------- |
1059
+ | `←` `→` / `-` `+` / `h` `l` | step by the param's step |
1060
+ | `Shift`-`←` `→` / `{` `}` | coarse step (five steps) |
1061
+ | `[` `]` / `Alt`-`←` `→` | fine step (a tenth of a step) |
1062
+ | `PgUp` `PgDn` | big step (twenty) |
1063
+ | `Home` `End` | minimum / maximum |
1064
+ | `1`-`9` `.` | type an exact value; `Enter` sets it, `Esc` cancels |
1065
+ | `0` / `d` | back to the default |
1066
+ | `↑` `↓` / `Tab` `Shift-Tab` | previous / next param of this device |
1067
+ | `Enter` | keep every staged change (one undo step); none: close |
1068
+ | `Esc` | revert staged changes and close |
1069
+ | `Space` `a` `c` | audition loop · A/B · solo ↔ in context |
1070
+ | `?` | these keys |
1071
+
1072
+ On a choice, `←` `→` move between options and `1`-`9` pick one by number. Sizes: two rows per field (value line, then bar) while the drawer takes at most half the piano roll; one row per field when shorter, as a window that follows the focused field; and at the 8-row terminal minimum a single borderless row with the focused field. The piano roll always keeps at least 40% of its rows above the drawer (at least three).
1073
+
1074
+ ## Mouse
1075
+
1076
+ dawg turns on SGR mouse reporting (modes 1000, 1002 and 1006) and turns it off again on exit, on `SIGTERM`/`SIGHUP`, on a crash, and around external editors. `--no-mouse` or `DAWG_MOUSE=0` (and `TERM=dumb`) leave it off, so the terminal's own selection and scrollback work; a terminal without mouse support ignores the modes and every key still works. Legacy X10 reports are swallowed rather than typed into the prompt.
1077
+
1078
+ | Where | Click / wheel |
1079
+ | ------------------- | -------------------------------------------------------------------- |
1080
+ | fader `[−]` `[+]` | step (shift-click: coarse) |
1081
+ | fader bar | set the value at that point; drag to slide it (past the ends clamps) |
1082
+ | fader option | choose it |
1083
+ | fader name | focus that field |
1084
+ | `[keep]` `[revert]` | the same as `Enter` / `Esc` |
1085
+ | wheel on a fader | step it (up raises; shift: coarse) |
1086
+ | list / menu row | select it; a click on the selected row opens it (`Enter`) |
1087
+ | wheel on a list | move through it |
1088
+ | header `▶/⏸ BPM` | play / pause |
1089
+ | header track name | the track list (`/tracks`); click a track to focus it |
1090
+ | header model | the model picker |
1091
+
1092
+ Hit-testing uses the same paint pass that draws the frame: each painter records its click regions into the frame's `HitMap` (`tui/hits.ts`), so targets never drift from what is on screen. The piano roll does not place notes on click (a note needs pitch, length and velocity that a click does not carry); clicks there are ignored.
1093
+
833
1094
  ## Previewing changes
834
1095
 
835
1096
  Hear a sound change before you keep it. In the edit menu (every section: Sound, Effects, Rhythm, Chords, Mix), `Space` starts a short loop of the focused track; `Space` again stops it. The song pauses while the loop plays, so only one thing sounds at a time.
836
1097
 
837
1098
  The loop is the track's own notes over its loop region when that is four bars or shorter, otherwise the two bars under the playhead (or the track's first two bars with notes, if those are empty). A track with no notes plays a short phrase by role: a chord for pads and keys, a riff for bass and leads, a groove for kits and drums, and one held note for wavetables so position and envelope changes are audible. It plays solo by default; `c` switches to the whole mix with the track in it.
838
1099
 
839
- While the loop plays, each change you make is **staged**, not committed. The loop re-renders only the changed track through the normal renderer and stem cache, in the render worker, and swaps it in within about 100 ms. Held keys are coalesced so only the latest value renders. The menu title shows `●` and `B staged N`, and each changed row shows the staged value beside the committed one (`mix 0.5 ← 0.3`).
1100
+ While the loop plays, each change you make is **staged**, not committed. The loop re-renders only the changed track through the normal renderer and stem cache, in the render worker, and swaps it in within about 100 ms (a reverb mix change re-mixes the cached tail and is heard in about 65 ms). Every swap, here and in the song player, crossfades old to new over 20 ms at the same beat, so changes never click; renders and exports never pass through the crossfade. Held keys are coalesced so only the latest value renders. The menu title shows `●` and `B staged N`, and each changed row shows the staged value beside the committed one (`mix 0.5 ← 0.3`).
840
1101
 
841
1102
  | Key | While auditioning |
842
1103
  | ------- | ------------------------------------------------------------ |
@@ -850,6 +1111,8 @@ While the loop plays, each change you make is **staged**, not committed. The loo
850
1111
 
851
1112
  Kept changes are one `ScoreOperation` (`preview.commit`, listing the commands), so `Ctrl-Z` takes them all back at once, and they sync to other windows and the project files like any edit. If the score changes underneath (another window, the agent, an undo), the staged commands are re-applied on top of the new score; any that no longer apply are dropped, with a notice. Leaving the menu reverts anything staged. With the loop off, the menu behaves as before: each change is committed right away.
852
1113
 
1114
+ **The rhythm editor and the chord settings stage too.** `/euclid` and the chord settings (the menu's Chords section, `/menu chords`) use the same loop and keys. In `/euclid`, `Space` loops the drum track; pulses, steps, rotate, typed values, a new row, `off` and `freeze` are staged, the title shows `●` and `B staged N`, and a changed lane shows `E(5,16) ← E(4,16)`. Chord settings are window settings rather than score edits, so while the Chords section is open the loop plays the focused track's chord phrase (two bars of the song key's progression, voiced and performed by the current settings: inversion, spread, bass, sevenths, block/strum/arp, pattern); a staged setting changes the B phrase, and `a` flips back to the committed settings. `Enter` keeps every staged change as one undo step: rhythm edits as one `preview.commit` revision, and chord settings applied at once (a key change, the only one stored in the score, as one revision). `Esc` reverts with nothing written. With the loop off, both screens commit each change at once, as before.
1115
+
853
1116
  **Lists audition on hover.** With the loop on, moving the cursor through a list plays the highlighted item on the loop: the wavetable list (built-in, pack and project tables), instruments, drum kits and patterns, and every choice list (filter type, warp mode, presets). It is the browser-preview model of Ableton and Bitwig, applied to the loop you are already hearing. Each move replaces the previous hover, so the staged count stays at one, and fast moves skip straight to the latest item. A pack item that has to be fetched shows `fetching…` in the title; the cursor keeps moving and the item plays once it arrives. `Enter` chooses the item (it stays staged until you keep), `Esc` or `←` leaves the list and drops the hover. The `/kit` and `/pattern` pickers work the same way: `Space` starts the loop, moving hears each kit or groove, `Enter` keeps it, `Esc` cancels.
854
1117
 
855
1118
  | Key in a list | While auditioning |
@@ -886,6 +1149,8 @@ One grammar for every picker (`/model`, `/pattern`, `/kit`, `/resume`, the wavet
886
1149
  | `?` | the keys for the current screen, drawn over it; any key closes |
887
1150
  | digits | type a value, only where a value is focused |
888
1151
 
1152
+ The fader drawer adds coarse, fine and big steps, Home/End and `0`/`d` default (see **Fader drawer**); clicks and the wheel mirror these keys (see **Mouse**).
1153
+
889
1154
  Every screen ends in a one-line footer of its keys that fits 80 columns (parts drop from the middle when narrower; `esc` and `? keys` stay). `?` on an empty prompt lists the prompt keys and the three ways in. Play mode is the one exception: its letters and number row are piano keys and chord latches (the GarageBand "Musical Typing" convention); its `?` panel says so.
890
1155
 
891
1156
  ## Release
package/README.md CHANGED
@@ -15,17 +15,17 @@ curl -fsSL https://dawg.sh/install | sh
15
15
  or install the release tarball from GitHub directly:
16
16
 
17
17
  ```sh
18
- bun add -g https://github.com/hraness/dawg/releases/download/v0.4.0/hraness-dawg-0.4.0.tgz
18
+ bun add -g https://github.com/hraness/dawg/releases/download/v0.5.0/hraness-dawg-0.5.0.tgz
19
19
  dawg --help
20
20
  ```
21
21
 
22
22
  Each [release](https://github.com/hraness/dawg/releases) is immutable and ships the tarball, a `SHA256SUMS` file and a build provenance attestation. To check a download before installing it:
23
23
 
24
24
  ```sh
25
- gh release download v0.4.0 --repo hraness/dawg
25
+ gh release download v0.5.0 --repo hraness/dawg
26
26
  shasum -a 256 -c SHA256SUMS
27
- gh attestation verify hraness-dawg-0.4.0.tgz --repo hraness/dawg
28
- bun add -g "$PWD/hraness-dawg-0.4.0.tgz"
27
+ gh attestation verify hraness-dawg-0.5.0.tgz --repo hraness/dawg
28
+ bun add -g "$PWD/hraness-dawg-0.5.0.tgz"
29
29
  ```
30
30
 
31
31
  dawg is also on npm as [`@hraness/dawg`](https://www.npmjs.com/package/@hraness/dawg):