picopilot 0.2.1 → 0.3.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 (62) hide show
  1. package/dist/commands/audio.d.ts +23 -2
  2. package/dist/commands/audio.d.ts.map +1 -1
  3. package/dist/commands/audio.js +228 -23
  4. package/dist/commands/audio.js.map +1 -1
  5. package/dist/commands/doctor.d.ts.map +1 -1
  6. package/dist/commands/doctor.js +1 -0
  7. package/dist/commands/doctor.js.map +1 -1
  8. package/dist/commands/music.d.ts +2 -1
  9. package/dist/commands/music.d.ts.map +1 -1
  10. package/dist/commands/music.js +209 -2
  11. package/dist/commands/music.js.map +1 -1
  12. package/dist/commands/sfx.d.ts.map +1 -1
  13. package/dist/commands/sfx.js +99 -11
  14. package/dist/commands/sfx.js.map +1 -1
  15. package/dist/engine/audio/analyze.d.ts +164 -0
  16. package/dist/engine/audio/analyze.d.ts.map +1 -0
  17. package/dist/engine/audio/analyze.js +512 -0
  18. package/dist/engine/audio/analyze.js.map +1 -0
  19. package/dist/engine/audio/index.d.ts +4 -2
  20. package/dist/engine/audio/index.d.ts.map +1 -1
  21. package/dist/engine/audio/index.js +4 -2
  22. package/dist/engine/audio/index.js.map +1 -1
  23. package/dist/engine/audio/score.d.ts +137 -0
  24. package/dist/engine/audio/score.d.ts.map +1 -0
  25. package/dist/engine/audio/score.js +420 -0
  26. package/dist/engine/audio/score.js.map +1 -0
  27. package/dist/engine/audio/section.d.ts +7 -0
  28. package/dist/engine/audio/section.d.ts.map +1 -1
  29. package/dist/engine/audio/section.js +17 -0
  30. package/dist/engine/audio/section.js.map +1 -1
  31. package/dist/engine/audio/sfx.d.ts +114 -12
  32. package/dist/engine/audio/sfx.d.ts.map +1 -1
  33. package/dist/engine/audio/sfx.js +236 -33
  34. package/dist/engine/audio/sfx.js.map +1 -1
  35. package/dist/engine/pico8/harness.d.ts +11 -5
  36. package/dist/engine/pico8/harness.d.ts.map +1 -1
  37. package/dist/engine/pico8/harness.js +24 -10
  38. package/dist/engine/pico8/harness.js.map +1 -1
  39. package/dist/engine/scaffold/scaffold.d.ts.map +1 -1
  40. package/dist/engine/scaffold/scaffold.js +5 -1
  41. package/dist/engine/scaffold/scaffold.js.map +1 -1
  42. package/package.json +1 -1
  43. package/src/commands/audio.test.ts +170 -1
  44. package/src/commands/audio.ts +284 -27
  45. package/src/commands/doctor.test.ts +1 -0
  46. package/src/commands/doctor.ts +1 -0
  47. package/src/commands/music.test.ts +119 -0
  48. package/src/commands/music.ts +251 -1
  49. package/src/commands/sfx.test.ts +55 -0
  50. package/src/commands/sfx.ts +118 -11
  51. package/src/engine/audio/analyze.test.ts +239 -0
  52. package/src/engine/audio/analyze.ts +664 -0
  53. package/src/engine/audio/index.ts +46 -1
  54. package/src/engine/audio/score.test.ts +402 -0
  55. package/src/engine/audio/score.ts +630 -0
  56. package/src/engine/audio/section.ts +19 -0
  57. package/src/engine/audio/sfx.test.ts +219 -4
  58. package/src/engine/audio/sfx.ts +326 -40
  59. package/src/engine/pico8/harness.test.ts +19 -0
  60. package/src/engine/pico8/harness.ts +29 -11
  61. package/src/engine/scaffold/scaffold.ts +5 -1
  62. package/src/skills/picopilot-audio/SKILL.md +74 -184
@@ -1,190 +1,80 @@
1
1
  ---
2
2
  name: picopilot-audio
3
- description: Compose PICO-8 sound as text with picopilot. The #1 reframe (no tempo, compose in TRACKER ROWS at one speed), the picopilot-MML grammar (waveform/volume/effect per note + the 5 SFX filters for designed sounds like explosion/engine/pad), structural music assembly from SFX references, the loud refusals, and the compose to hear ears loop. Use when authoring __sfx__ / __music__.
3
+ description: Compose PICO-8 sound and music as text with picopilot, and judge it without ears. Covers tracker-row picopilot-MML (held notes, volume envelopes, the 5 SFX filters), whole songs with `music from-score`, the render then analyze loop, and the taste rules that keep game music soft, varied and under the SFX. Use when authoring __sfx__ / __music__.
4
4
  ---
5
5
 
6
6
  # picopilot audio
7
7
 
8
- SFX and music live in the cart as hex blobs you cannot read. picopilot lets you
9
- compose sound as TEXT you can reason about, then transpiles it into `__sfx__` /
10
- `__music__`. (See `picopilot-overview` for the `#include` discipline; never
11
- hand-write the audio hex.) The exact byte layout + the full grammar/mapping
12
- tables are the canonical reference in
13
- `work/notes/findings/pico8-audio-sfx-music-layout-and-mml-subset.md` (Part A =
14
- layout, Part B = the picopilot-MML grammar + mapping). This skill carries what a
15
- model gets WRONG, not the whole finding.
16
-
17
- ## Read this first: no tempo, compose in TRACKER ROWS (ADR-0008)
18
-
19
- The one thing a model gets wrong: it reaches for sheet music. PICO-8 has NO
20
- per-note duration and NO tempo/BPM. An SFX is a column of up to 32 tracker ROWS,
21
- and ALL rows share ONE SFX `speed` (ticks/row, set with `s<N>`). So a
22
- picopilot-MML length is a ROW COUNT, not a musical note value: `c4` = the note C
23
- held for 4 ROWS (emitted as the note plus 3 tie rows), NOT a quarter note. There
24
- is no tempo token; a tempo-style input is refused with a clear message, not
25
- guessed at. Consequences you must design around:
26
-
27
- - **No true sustain across rows.** A held note is RE-STRUCK each following row
28
- (PICO-8 has no cross-row sustain bit). For a smooth hold use effect `e1`
29
- (slide) or a low `s<N>` speed, not a long length. Do not expect a legato tie.
30
- - **Dotted notes round to whole rows.** `c4.` = 6 rows; a fractional result
31
- rounds to the nearest whole row and the result WARNS. Rows are integers.
32
- - **32-row hard cap.** A melody past 32 rows is refused (see below). Splitting it
33
- into several SFX arranged by a music pattern is a STRUCTURAL choice YOU make,
34
- not something the transpiler does silently.
35
-
36
- ## The ears loop: `sfx from-mml` -> `music from-patterns` -> `audio render`
37
-
38
- This is the audio analogue of the `picopilot-art` render/look/set eyes loop:
39
-
40
- 1. `picopilot sfx from-mml <slot> "<mml>"`: compose one sound as text.
41
- 2. `picopilot music from-patterns "<json>"`: arrange SFX into a song.
42
- 3. `picopilot audio render`: RECORD a WAV and HEAR it, then iterate.
43
-
44
- These steps are wired as CTAs (a merged SFX or song points you at `verify` then
45
- `audio render`), so follow the suggested next command.
46
-
47
- ## `audio render` / `audio record`: get a WAV by RECORDING (ADR-0009)
48
-
49
- PICO-8 has NO working offline WAV export (it SIGFPEs upstream), so picopilot gets audio-to-WAV the only way that works: by RECORDING a running session (`extcmd("audio_rec")`/`audio_end`). Two commands, both PICO-8-gated and both REAL-TIME recordings of playback, NOT deterministic offline exports. They work headless (no display, no sound card): PICO-8 records its own mixer, not the audio device, and when there is no display picopilot runs the session on a private virtual display by itself (ADR-0017). You do not need to do anything for that.
50
-
51
- - **`picopilot audio render`** injects a THROWAWAY play-harness for a chosen
52
- target and records it, so you write NO playback code. Pick the target with
53
- `--sfx <n>` (one SFX slot), `--pattern <p>` (one music pattern), or NEITHER
54
- (the whole song, `music 0`). The record window is derived from an `--sfx`
55
- target's speed+rows; override any target with `--seconds <n>`. An empty slot or
56
- out-of-range target is REFUSED loudly (nothing to render), never a silent
57
- recording of silence.
58
- - **`picopilot audio record <cart>`** records the cart's OWN playback (it keeps
59
- the cart's code and appends a cooperative recorder); use it to capture a whole
60
- run's audio. `picopilot run --record-audio` does the same alongside a normal
61
- `run` (a WAV next to the screenshots).
62
-
63
- The WAV goes to an isolated dir (`--wav-dir`, default a temp dir), never
64
- `~/Desktop` or the carts root (the `audio_end(1)`-saves-to-the-current-folder
65
- trap; picopilot steers it with PICO-8's `root_path`). PICO-8 absent -> a
66
- structured `pico8-not-found` (remedy `set PICO8_PATH or install PICO-8`), never a
67
- crash.
68
-
69
- When a recording produces no WAV (`captured:false`), the result says why: `noAudioReason` (the backstop fired before the recorder finished, PICO-8 exited early, or the capture was empty) and `pico8LogTail` (the end of PICO-8's own log, where load errors show up). Read both before retrying; a cart that fails to load never reaches the end of the recording, so run `picopilot verify` on it. If the machine has no display AND no Xvfb, the command fails up front with `display-unavailable`, whose message names the package to install; `picopilot doctor` reports the same thing before you try.
70
-
71
- ## `sfx from-mml`: one MML string -> one SFX slot
72
-
73
- `picopilot sfx from-mml <slot> "<mml>"` transpiles picopilot-MML into one
74
- `__sfx__` slot (`0-63`) and merges it (only that slot changes; every other
75
- section stays byte-identical). Cart is `--cart` (default `main.p8`); pass a
76
- longer source with `--file` (give EITHER the argument OR `--file`).
77
-
78
- Write notes AND their waveform/volume/effect in MML: that is exactly the
79
- information PICO-8 needs and hand-written hex hides. This is why picopilot-MML,
80
- not ABC: ABC is score-level and cannot name a per-note waveform, volume, or
81
- effect, so an ABC-authored SFX is timbre-less ("blind audio in a nicer font").
82
-
83
- ### The picopilot-MML grammar in brief
84
-
85
- Modal state (sticky until changed): `@<0-7>` waveform (`@8`-`@f` = SFX 0-7 as a
86
- custom instrument), `v<0-7>` volume, `e<0-7>` effect (0 none, 1 slide, 2 vibrato,
87
- 3 drop, 4 fade in, 5 fade out, 6 arpeggio fast, 7 arpeggio slow), `o<0-5>` octave
88
- (default `o2`), `l<N>` default length in rows, `>`/`<` octave shift. A bare note
89
- (`c d e f g a b`, `+`/`#` sharp, `-` flat) emits one row in the current state.
90
- `r<N>` = N silent rows; `^<N>` extends the previous note; `s<N>` = SFX speed
91
- (default `s16`); `{`/`}` = loop markers (a LONE `{` marks the pattern LEN, the
92
- finding's LEN special case). SFX-level FILTERS (per-SFX, order-free): `!dampen`/
93
- `!dampen2`, `!reverb`, `!noiz`, `!buzz`, `!detune1`/`!detune2` (see the filters
94
- section below). Whitespace + `|` bars are cosmetic. See finding Part B / A.7 for
95
- the full tables.
96
-
97
- Note `e` is BOTH a note letter AND the effect prefix: `e0`-`e7` is the EFFECT; a
98
- bare `e` is note E (hold it with `l4 e`, not `e4`). Effects have NO letter
99
- mnemonics (the finding's `sl`/`vb`/... sugar did NOT ship: each collides with a
100
- real note-plus-length sequence); use the numeric `e0`-`e7`.
101
-
102
- Example: `picopilot sfx from-mml 0 "s8 @3 v6 e2 c e g > c"` is a square-wave
103
- (`@3`) vibrato (`e2`) arpeggio at speed 8.
104
-
105
- ### SFX FILTERS: how to reach DESIGNED sounds (explosion, engine, pad)
106
-
107
- Pitch + waveform + volume + effect get you notes; the 5 per-SFX FILTERS are what
108
- make a sound DESIGNED. They are `!`-prefixed SFX-level directives (per-SFX like
109
- `s<N>`, place them anywhere in the string, order-free): they set the SFX's first
110
- header byte and change nothing per-note. Use them when a boom reads as a
111
- machine-gun rattle, or an engine as a buzz: no amount of pitch/volume MML can
112
- synthesize the low BODY or resonant TAIL, only these filters can.
113
-
114
- - **`!dampen` / `!dampen2`** — a low-pass at 2 levels. This is the BODY/weight:
115
- turns raw `@6` noise from a hiss/rattle into a "boom". `!dampen2` is heavier.
116
- The single biggest lever for an explosion.
117
- - **`!reverb`** — an echo/tail. The resonant TAIL that makes a boom ring out (and
118
- a pad bloom). On/off (a single switch, see the caveat below).
119
- - **`!noiz`** — pure white-noise texture; applies ONLY to instrument 6 (`@6`), a
120
- no-op on any other waveform. The noise character under an explosion.
121
- - **`!buzz`** — a buzzier alteration (all waveforms). With `@6` + `!buzz` and NOIZ
122
- off you get brown noise.
123
- - **`!detune1`** (flange, second detuned voice) / **`!detune2`** (a voice tuned up
124
- ~an octave) — thickness for a lead or pad. `!detune` needs an explicit 1 or 2.
125
-
126
- The canonical DESIGNED explosion: `!dampen2 !reverb @6` over a descending pitch
127
- sweep + volume decay, e.g.
128
- `picopilot sfx from-mml 3 "s6 !dampen2 !reverb @6 o3 v7 g o1 c o0 g v6 e v5 c v4 g v3 c v2 g v1 c"`.
129
- Strip the two filters and the SAME notes read as a descending zap / rattle: the
130
- filters are the tool gap the audio dogfood surfaced (they set header byte `e0`).
131
-
132
- CAVEAT (finding A.7): the filter byte layout was decoded byte-for-byte on PICO-8
133
- v0.2.7, but the byte has no non-colliding room for a REVERB level 2 under the
134
- verified layout, so picopilot exposes `!reverb` as a single on/off switch (DAMPEN
135
- keeps its 2 levels). A bad level (`!dampen3`, `!noiz2`, `!reverb2`, `!detune`
136
- without 1/2) is a loud `audio-mml-filter-level-out-of-range` refusal, not a clamp.
137
-
138
- ## `music from-patterns`: structural, NOT a notation
139
-
140
- `__music__` holds NO melody: melody lives entirely in the referenced SFX. A song
141
- is an ORDERED LIST of up-to-4 SFX-channel references per pattern.
142
- `picopilot music from-patterns "<json>"` writes `__music__` (only `__music__`
143
- changes; `__sfx__` and every other section stay byte-identical). So author each
144
- voice as an SFX with `sfx from-mml`, THEN arrange the references here; you never
145
- write music in a melodic notation. Cart is `--cart` (default `main.p8`); longer
146
- songs via `--file`.
147
-
148
- ### The pattern-list JSON
149
-
150
- An array of patterns, each
151
- `{"channels":[c0,c1,c2,c3], "loopStart"?, "loopBack"?, "stop"?}`. Pattern order =
152
- song order. A channel is an SFX index `0-63`, OR `null` = OFF (silent this
153
- pattern). **`null` is NOT `sfx 0`**: `null` sets the channel's off bit; `0` is a
154
- real, playing SFX. Keep them distinct. The 3 flow flags (optional, default
155
- false): `loopStart`, `loopBack` (jump back to the loop-start pattern at this
156
- pattern's end), `stop` (music stops after this pattern); they combine. A
157
- `loopBack` with NO `loopStart` anywhere is allowed but WARNS (PICO-8 falls back
158
- to pattern 0).
159
-
160
- Example: `[{"channels":[0,1,null,3],"loopStart":true},{"channels":[0,1,null,3],"loopBack":true}]`
161
- is a 2-pattern loop whose channel 2 is silent.
162
-
163
- **Pattern length is INHERITED from the SFX, not stored here.** PICO-8 ends a
164
- pattern when its LEFT-MOST non-looping channel's SFX finishes, so pattern timing
165
- comes from the referenced SFX (its `speed` + LEN). To change how long a pattern
166
- plays, change the referenced SFX, not the music.
167
-
168
- ## The two refusals: loud, never clamped
169
-
170
- Both stop you with a structured error + nonzero exit (no bytes change on a
171
- refusal) so a refusal is ACTIONABLE, not a dead end:
172
-
173
- - **`audio-mml-pitch-out-of-range`** (`sfx from-mml`): a note fell below C0 or
174
- above D#5 (`0-63`). PICO-8 has no notes outside that range. Fix: TRANSPOSE, pick
175
- an octave (`o0`-`o5`) that keeps every note in range, rather than transposing
176
- off the keyboard.
177
- - **`audio-mml-sfx-overflow`** (`sfx from-mml`): more than 32 rows. Fix: SPLIT the
178
- melody across multiple SFX slots yourself, then arrange them with
179
- `music from-patterns`.
180
- - **`audio-mml-filter-level-out-of-range`** (`sfx from-mml`): a filter level PICO-8
181
- does not have (`!dampen3`, `!reverb2`, `!noiz2`, or `!detune` without a mode).
182
- Fix: `!dampen` has levels 1-2 (`!dampen`/`!dampen2`); `!reverb`/`!noiz`/`!buzz`
183
- are on/off; `!detune` needs `!detune1` (flange) or `!detune2` (octave).
184
-
185
- Related: `music from-patterns` refuses an out-of-range SFX index with
186
- `audio-music-sfx-out-of-range` (to silence a channel use `null`, not an
187
- out-of-range number); a malformed JSON payload is `audio-music-parse-error`. And
188
- `audio render`/`audio record` require PICO-8, so they return a structured
189
- `pico8-not-found` (remedy `set PICO8_PATH or install PICO-8`) when absent, never
190
- a crash. The text authoring + transpile steps need neither PICO-8 nor shrinko.
8
+ You cannot hear, so you compose sound as TEXT (picopilot-MML), let picopilot write `__sfx__` / `__music__`, and judge the result with NUMBERS from `audio analyze`. Never hand-write the audio hex. The byte layout and full grammar live in `work/notes/findings/pico8-audio-sfx-music-layout-and-mml-subset.md`; the measurements behind the taste rules in `work/notes/findings/pico8-audio-music-taste-and-analysis.md`.
9
+
10
+ ## The loop: compose, render, analyze, iterate
11
+
12
+ 1. **Compose.** Game SFX: `picopilot sfx from-mml <slot> "<mml>"` (one sound, up to 32 rows). Music: `picopilot music from-score --file score.json --slots 16-63` (whole songs, any length).
13
+ 2. **Render.** `picopilot audio render --pattern <startPattern> --seconds <n> --analyze`. Record more than twice the song's `seconds` (from-score reports it, and its next-step hint does this) so the loop length is measurable. `--sfx <slot> --analyze` analyzes a one-shot with only the checks that apply to it (near-silent, near-clipping).
14
+ 3. **Analyze.** Read `analysis.warnings` and the numbers. Check music against an SFX with `picopilot audio analyze music.wav --against sfx.wav`.
15
+ 4. **Iterate** until the music analysis has no warnings and the score follows the taste rules below. A clean analysis is the done bar; "it compiled" is not.
16
+
17
+ ## Tracker rows, not sheet music (ADR-0008)
18
+
19
+ PICO-8 has no tempo and no per-note duration. An SFX is up to 32 ROWS at one `speed` (`s<N>` ticks per row, 1/120 s each). A length is a ROW COUNT: `c4` is C held for 4 rows. A tempo token is refused.
20
+
21
+ **A held note is ONE note (ADR-0019).** PICO-8 plays consecutive same-pitch rows as one continuous tone, so picopilot shapes a held note across its rows: fade in (`e4`) on its first row, fade out (`e5`) as a volume ramp ending on its last row, drop (`e3`) as a one-row hit then silence; vibrato, slide and arpeggio continue. Separate one-row notes (`e5 c c c c`) still re-trigger, which is how you write repeated plucks. Shape held notes with:
22
+
23
+ - **`v[...]` volume envelope**, one digit per row, last value held, the part after `/` is the release on each note's last rows: `v[3 2 2 2 1/1 0] a12`. Modal; `v<N>` clears it.
24
+ - **`~N` delayed vibrato**: rows from N on get vibrato, so a long note starts plain and blooms: `~6`. `~0` turns it off.
25
+
26
+ The `e` trap: `e0`-`e7` is an EFFECT, so note E with a length is `l8 e`, never `e8` (refused) or `e4` (silently effect 4).
27
+
28
+ ## Taste: what makes game music pleasant (measured)
29
+
30
+ These rules come from a soundtrack a listener called shrill and grating, a revision called "far too violent", and the accepted versions (the taste finding has the numbers).
31
+
32
+ - **Register.** Lead at `o3` or below (`o2 a` is 440 Hz), bass around `o1`, pads `o1`-`o2`. A lead and arpeggio at `o4` on square/pulse is what made the first track shrill.
33
+ - **Timbre.** Soft: triangle `@0` and a quiet dampened organ `@5`. Harsh at volume: square `@3`, pulse `@4`, saw `@2`. Put `!dampen` on music voices. Noise drums and drop-kicks (`e3`) read as aggressive at default volumes; leave drums out of ambient music, or keep them at `v1`-`v2`.
34
+ - **Density.** Rests are music. Write held notes with envelopes and gaps, not wall-to-wall 16th-note arpeggios at one volume.
35
+ - **Form.** Title and in-game are DIFFERENT songs. In-game loops run about a minute, with sections (A/B), and a melody that plays only in some bars (a silent channel in a pattern costs nothing: it becomes OFF).
36
+ - **Volume headroom.** Music mostly `v1`-`v2`, `v3` only on attacks; SFX `v4`-`v7` sit on top.
37
+ - **Leave a channel for SFX.** Write music on channels 0-2 and play it with a mask: `music(n, fade, 7)` (channels 0-2), so `sfx()` always has channel 3.
38
+
39
+ The analyzer's thresholds, for reading its output: mean RMS above 0.12 is loud (accepted music 0.07-0.10); centroid above 900 Hz or more than 8 % of energy above 2 kHz is shrill (accepted: under 500 Hz, about 0.2 %); dynamic range under 4 dB is monotonous (accepted 7-8 dB); a loop under 20 s is repetitive; with `--against`, music should sit at least 3 dB under the SFX.
40
+
41
+ ## `music from-score`: whole songs (ADR-0020)
42
+
43
+ The score is JSON: songs, each up to 4 channels of picopilot-MML of ANY length.
44
+
45
+ ```json
46
+ {"songs": [
47
+ {"name": "title", "speed": 16, "channels": [
48
+ {"filters": "!dampen", "mml": "@0 ~6 v[3 2 2 2 1/1 0] o2 a12 > d4 f8 l8 e | d16 c8 < a+8"},
49
+ {"filters": "!dampen", "mml": "@0 v[2 2 2 1 1 1/0] o1 d16 a16 | < a+16 > f16"},
50
+ null]},
51
+ {"name": "game", "speed": 20, "loopFrom": 0, "channels": ["...", "...", "..."]}
52
+ ]}
53
+ ```
54
+
55
+ picopilot cuts every channel at the same 32-row boundaries (`patternRows` for other bar lengths), turns a channel silent for a whole pattern into OFF, shares identical SFX, writes `__sfx__` + `__music__`, and reports each song's `startPattern` (play it with `music(startPattern, 0, 7)`), its `seconds`, and which slot holds what. Per song: `speed` (shared by its channels), `loop` (default true; false stops at the end), `loopFrom`. `|` bars are cosmetic: use them to check every channel's bars add up to the same rows.
56
+
57
+ Slots: pass `--slots 16-63` (a range the music OWNS: overwritten and cleared on every run, so re-runs are clean). Without it, only empty slots are used, from 63 down, and nothing with notes is touched. `picopilot sfx clear <slots>` empties slots (`5`, `16-63`, `3,5,9`).
58
+
59
+ `music from-patterns '<json>'` still exists for arranging hand-made SFX: a list of `{"channels":[c0,c1,c2,c3], loopStart?, loopBack?, stop?}` where a channel is an SFX index or `null` (OFF, which is different from SFX 0). Music is structural either way: melody lives in SFX, `__music__` only references them.
60
+
61
+ ## `sfx from-mml`: one sound
62
+
63
+ Modal (sticky) state: `@0`-`@7` waveform (0 triangle, 1 tilted saw, 2 saw, 3 square, 4 pulse, 5 organ, 6 noise, 7 phaser; `@8`-`@f` = SFX 0-7 as an instrument), `v0`-`v7` or `v[...]`, `e0`-`e7` effect (1 slide, 2 vibrato, 3 drop, 4 fade in, 5 fade out, 6/7 arpeggio fast/slow), `~N`, `o0`-`o5` octave (default `o2`), `l<N>` default length, `>`/`<` octave shift. Notes `c d e f g a b` with `+`/`#`/`-`; `r<N>` rest; `^<N>` extends the previous note; `s<N>` speed; `{`/`}` loop (a lone `{` sets the LEN). picopilot-MML, not ABC, because ABC cannot name a per-note waveform, volume or effect.
64
+
65
+ **Filters make DESIGNED sounds** (per SFX, anywhere in the string): `!dampen`/`!dampen2` low-pass body, `!reverb` echo tail, `!noiz` (only on `@6`), `!buzz`, `!detune1` (flange) / `!detune2` (octave). An explosion is `!dampen2 !reverb @6` over a falling pitch and volume: `s6 !dampen2 !reverb @6 o3 v7 g o1 c o0 g v6 e v5 c v4 g v3 c v2 g v1 c`.
66
+
67
+ ## `audio render` / `record` / `analyze`
68
+
69
+ PICO-8 has no working offline export of audio, so `render` and `record` are REAL-TIME recordings of a PICO-8 session (ADR-0009), not an offline export. They work headless (picopilot starts a virtual display when there is none) and require PICO-8. `render` plays a target you pick (`--sfx`, `--pattern`, or the whole song); `record <cart>` captures the cart's own playback. `--seconds` sets the window and the safety backstop follows it. The WAV goes to an isolated temp dir (`--wav-dir`). When `captured` is false, read `noAudioReason` and `pico8LogTail`, then run `picopilot verify`.
70
+
71
+ `audio analyze <wav>` needs neither PICO-8 nor Python. It skips the first second (as the calibration did; `--skip 0` for an SFX, plus `--kind sfx`) and reports loudness (mean, loudest frame and when), brightness (centroid, share above 2 kHz), dynamic range, a per-half-second loudness `sparkline`, the loop length, and `warnings` with the fix for each.
72
+
73
+ ## Refusals (no bytes change)
74
+
75
+ - `audio-mml-pitch-out-of-range`: a note outside C0..D#5. Pick an octave in range (in a score, the message names song and channel; watch accumulated `<`/`>`).
76
+ - `audio-mml-sfx-overflow`: over 32 rows in `sfx from-mml`. Use `music from-score`.
77
+ - `audio-mml-tie-state-change`: a `v`/`e`/`@`/`~`/`v[...]` between a note and its `^`. Use an envelope or a new note.
78
+ - `audio-mml-filter-level-out-of-range`: `!dampen3`, `!reverb2`, `!noiz2`, `!detune` without 1/2.
79
+ - `audio-score-*`: speed mismatch between channels, `{`/`}` in a score, over 64 patterns, not enough free slots (pass `--slots`).
80
+ - `audio-backstop-too-small`: an explicit `--backstop-ms` that cannot fit the window. Omit it.