@mthines/reaper-mcp 0.19.0 → 0.70.2

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/README.md +19 -570
  2. package/bridge-ui/mcp_bridge.html +129 -0
  3. package/bridge-ui/mcp_bridge.webmanifest +16 -0
  4. package/bridge-ui/mcp_bridge_icon_192.png +0 -0
  5. package/bridge-ui/mcp_bridge_icon_512.png +0 -0
  6. package/bridge-ui/mcp_bridge_sw.js +35 -0
  7. package/claude/agents/plugin-researcher.md +188 -0
  8. package/claude/knowledge/reference/aesthetics-scoring.md +48 -0
  9. package/{knowledge → claude/knowledge}/reference/common-mistakes.md +1 -1
  10. package/claude/knowledge/reference/lorekit-setup.md +36 -0
  11. package/claude/knowledge/reference/memory-protocol.md +377 -0
  12. package/{knowledge → claude/knowledge}/reference/metering.md +5 -5
  13. package/claude/knowledge/reference/perceived-loudness.md +182 -0
  14. package/{knowledge → claude/knowledge}/workflows/editing.md +4 -0
  15. package/{knowledge → claude/knowledge}/workflows/gain-staging.md +21 -23
  16. package/{knowledge → claude/knowledge}/workflows/learn-plugin.md +15 -48
  17. package/{knowledge → claude/knowledge}/workflows/master-bus.md +13 -1
  18. package/claude/knowledge/workflows/section-levels.md +138 -0
  19. package/{knowledge → claude/knowledge}/workflows/session-prep.md +4 -0
  20. package/claude/knowledge/workflows/transient-lighting.md +52 -0
  21. package/claude/skills/critique/SKILL.md +131 -0
  22. package/claude/skills/editing/SKILL.md +281 -0
  23. package/claude/skills/learn-plugin/SKILL.md +210 -0
  24. package/claude/skills/mastering/SKILL.md +151 -0
  25. package/claude/skills/mix-memory/SKILL.md +140 -0
  26. package/claude/skills/mixer/SKILL.md +253 -0
  27. package/claude/skills/new-extension/SKILL.md +324 -0
  28. package/main.js +7843 -1940
  29. package/package.json +17 -15
  30. package/reaper/CLAUDE.md +22 -4
  31. package/reaper/mcp_actions_lib.lua +375 -0
  32. package/reaper/mcp_analyzer.jsfx +27 -12
  33. package/reaper/mcp_arrange_lib.lua +949 -0
  34. package/reaper/mcp_bridge.lua +2321 -758
  35. package/reaper/mcp_edit_views_lib.lua +1378 -0
  36. package/reaper/mcp_envelopes_lib.lua +748 -0
  37. package/reaper/mcp_extensions_lib.lua +55 -0
  38. package/reaper/mcp_fxstate_lib.lua +144 -0
  39. package/reaper/mcp_gain_trace.jsfx +126 -0
  40. package/reaper/mcp_items_lib.lua +877 -0
  41. package/reaper/mcp_lufs_meter.jsfx +16 -13
  42. package/reaper/mcp_midi_lib.lua +555 -0
  43. package/reaper/mcp_mixing_lib.lua +1021 -0
  44. package/reaper/mcp_plan_lib.lua +801 -0
  45. package/reaper/mcp_prefs_lib.lua +127 -0
  46. package/reaper/mcp_project_lib.lua +1088 -0
  47. package/reaper/mcp_recording_lib.lua +595 -0
  48. package/reaper/mcp_render_lib.lua +492 -0
  49. package/reaper/mcp_snapshot_lib.lua +547 -120
  50. package/reaper/mcp_snapshot_next.lua +10 -4
  51. package/reaper/mcp_snapshot_prev.lua +11 -4
  52. package/reaper/mcp_snapshot_quick_save.lua +7 -32
  53. package/reaper/mcp_snapshots_ui_lib.lua +258 -0
  54. package/reaper/mcp_tracks_lib.lua +409 -0
  55. package/sidecar/analysis.py +661 -0
  56. package/sidecar/chords.py +96 -0
  57. package/sidecar/mixcheck.py +295 -0
  58. package/sidecar/requirements-chords.txt +6 -0
  59. package/sidecar/requirements-separation.txt +3 -0
  60. package/sidecar/requirements.txt +17 -0
  61. package/sidecar/separation.py +142 -0
  62. package/sidecar/server.py +622 -0
  63. package/sidecar/tests/test_analysis.py +249 -0
  64. package/sidecar/tests/test_chords.py +117 -0
  65. package/sidecar/tests/test_mixcheck.py +163 -0
  66. package/sidecar/tests/test_separation.py +73 -0
  67. package/sidecar/tests/test_server.py +464 -0
  68. package/sidecar/tests/test_verify.py +243 -0
  69. package/sidecar/tests/test_voice_render.py +268 -0
  70. package/sidecar/tests/test_voice_sketch.py +140 -0
  71. package/sidecar/verify.py +224 -0
  72. package/sidecar/voice_render.py +295 -0
  73. package/sidecar/voice_sketch.py +280 -0
  74. package/claude-agents/critique.md +0 -147
  75. package/claude-agents/editor.md +0 -184
  76. package/claude-agents/levels.md +0 -107
  77. package/claude-agents/mastering.md +0 -129
  78. package/claude-agents/mixer.md +0 -248
  79. package/claude-agents/preflight.md +0 -149
  80. package/claude-agents/producer.md +0 -159
  81. package/claude-agents/setup.md +0 -150
  82. package/claude-agents/stems.md +0 -135
  83. package/claude-rules/architecture.md +0 -39
  84. package/claude-rules/development.md +0 -54
  85. package/claude-rules/lua-bridge.md +0 -50
  86. package/claude-rules/testing.md +0 -42
  87. package/claude-skills/learn-plugin.md +0 -123
  88. package/knowledge/CLAUDE.md +0 -71
  89. package/knowledge/plugins/_template.md +0 -82
  90. package/knowledge/plugins/fabfilter/pro-c-2.md +0 -117
  91. package/knowledge/plugins/fabfilter/pro-l-2.md +0 -95
  92. package/knowledge/plugins/fabfilter/pro-q-3.md +0 -112
  93. package/knowledge/plugins/neural-dsp/helix-native.md +0 -104
  94. package/knowledge/plugins/stock-reaper/js-1175-compressor.md +0 -94
  95. package/knowledge/plugins/stock-reaper/rea-comp.md +0 -100
  96. package/knowledge/plugins/stock-reaper/rea-delay.md +0 -95
  97. package/knowledge/plugins/stock-reaper/rea-eq.md +0 -103
  98. package/knowledge/plugins/stock-reaper/rea-gate.md +0 -99
  99. package/knowledge/plugins/stock-reaper/rea-limit.md +0 -75
  100. package/knowledge/plugins/stock-reaper/rea-verb.md +0 -76
  101. package/knowledge/reference/perceived-loudness.md +0 -122
  102. package/reaper/mcp_snapshot_manager.lua +0 -1001
  103. /package/{knowledge → claude/knowledge}/genres/_template.md +0 -0
  104. /package/{knowledge → claude/knowledge}/genres/electronic.md +0 -0
  105. /package/{knowledge → claude/knowledge}/genres/hip-hop.md +0 -0
  106. /package/{knowledge → claude/knowledge}/genres/metal.md +0 -0
  107. /package/{knowledge → claude/knowledge}/genres/orchestral.md +0 -0
  108. /package/{knowledge → claude/knowledge}/genres/pop.md +0 -0
  109. /package/{knowledge → claude/knowledge}/genres/rock.md +0 -0
  110. /package/{knowledge → claude/knowledge}/reference/compression.md +0 -0
  111. /package/{knowledge → claude/knowledge}/reference/frequencies.md +0 -0
  112. /package/{knowledge → claude/knowledge}/workflows/delivery.md +0 -0
  113. /package/{knowledge → claude/knowledge}/workflows/drum-bus.md +0 -0
  114. /package/{knowledge → claude/knowledge}/workflows/low-end.md +0 -0
  115. /package/{knowledge → claude/knowledge}/workflows/stem-prep.md +0 -0
  116. /package/{knowledge → claude/knowledge}/workflows/vocal-chain.md +0 -0
@@ -0,0 +1,377 @@
1
+ # Memory Protocol
2
+
3
+ How the mix skills recall and persist what they know.
4
+
5
+ | What | Where |
6
+ |------|-------|
7
+ | **Reference knowledge** — genres, workflows, reference material | `~/.claude/knowledge/` (symlink to the repo's `knowledge/`, versioned in git) |
8
+ | **Plugin knowledge + process lessons** | **LoreKit**, scope `project::reaper-mcp`, via the `memory.*` MCP tools — in two layers, below |
9
+
10
+ ## The two layers
11
+
12
+ | Layer | Owner | Keys | Tag | Who writes it |
13
+ |-------|-------|------|-----|---------------|
14
+ | **Built-in** | the `reaper-mcp` LoreKit org — curated by the maintainer | `plugins::<vendor>/<plugin>`, `mixing-lessons::<slug>` | `layer::built-in` | Only the maintainer, via `/mix-memory publish`. **Read-only for the skills.** |
15
+ | **User** | each user's personal store | `user::plugins::<vendor>/<plugin>`, `user::mixing-lessons::<slug>` | `layer::user` | The mix skills, automatically, for whoever runs them |
16
+
17
+ **Reading the built-in layer — everyone, no LoreKit account needed:** the `reaper`
18
+ MCP server ships a public read-only LoreKit token and exposes the layer as two tools:
19
+
20
+ | Tool | Does |
21
+ |------|------|
22
+ | `list_builtin_knowledge { kind?, tags?, query?, limit? }` | `kind`: `plugins` / `lessons` / `all`. Without `query`: summaries (key, tags, preview), `tags` OR-filtered (e.g. `["category::eq"]`). With `query`: full-text search with full bodies |
23
+ | `get_builtin_knowledge { key }` | One record by its built-in key (`plugins::…` / `mixing-lessons::…`), or `record: null` |
24
+
25
+ **Reading and writing the user layer** goes through the user's own LoreKit
26
+ connection (`memory.*`). Org members also get the built-in rows back from `memory.*`
27
+ (org and personal rows come back together); the `layer::` tag tells them apart
28
+ (responses carry no owner field), so every record carries one — skip
29
+ `layer::built-in` rows there, the built-in tools already returned them.
30
+
31
+ Without the user's own LoreKit connected, the skills still get the built-in layer;
32
+ they just can't write anything.
33
+
34
+ ### Hard rules for the two layers
35
+
36
+ 1. **Never write a built-in key** (`plugins::…`, `mixing-lessons::…`) from a skill.
37
+ Every skill write goes to the `user::` key, with no `org`.
38
+ 2. **Never `memory.delete` a built-in key.** A delete without `org` archives
39
+ *every* row with that scope + key the caller can write — for an org member that
40
+ includes the built-in row. Only ever delete `user::` keys.
41
+ 3. **The `user::` prefix is mandatory, not cosmetic.** When a personal row and an org
42
+ row share scope + key, `memory.read` silently returns whichever was updated
43
+ last; distinct keys are what keep both layers visible.
44
+
45
+ **Not connected?** If the `memory.*` tools are unavailable, say so in one line and
46
+ continue on the built-in layer plus general mixing knowledge — nothing is written.
47
+ If the built-in tools error (offline), continue on the user layer alone.
48
+
49
+ ## Privacy guard
50
+
51
+ Only write audio knowledge — plugins and mixing. Never secrets, license keys,
52
+ names of the user's clients/songs, or paths containing personal data. A candidate
53
+ record that contains one is **dropped, not written**.
54
+
55
+ ---
56
+
57
+ ## The self-improvement loop (every skill)
58
+
59
+ This loop is **general to all the mix skills** — `/mixer`, `/mastering`,
60
+ `/critique`, `/learn-plugin`. The **only** thing that breaks it is failing to
61
+ capture — so capture is **mandatory and reported**, never deferred to "later".
62
+
63
+ 1. **Recall (start of every task)** — the read step below. A skill that changes
64
+ nothing (`/critique`) still recalls; lessons sharpen what it looks for.
65
+ 2. **Capture at the correction point — immediately, before your next action.**
66
+ The moment the user reverts / rejects / overrides you, or a re-measure shows
67
+ you made it worse, write the lesson; then **say you did** (key + one-line
68
+ takeaway). *No capture at all is the dominant failure of this loop — treat a
69
+ silent skip as a bug.*
70
+ 3. **End-of-session retrospective — always, even on a clean run.** Before the
71
+ final report, ask: did the user correct or override me; did a measurement
72
+ surprise me; did I guess at something that nearly didn't pay off; should a
73
+ lesson I already had have fired earlier? Did a lesson I applied work? Write or
74
+ UPDATE a lesson for anything that surfaces. Write nothing only when the
75
+ retrospective is genuinely empty — and say which.
76
+ 4. **Report what you captured.** The final message names the record(s) written
77
+ (or states that none were), so a skipped capture is visible to the user.
78
+
79
+ "Read-only" skills (`/critique`) still write **process lessons about their own
80
+ analysis**. Read-only means they don't change the *mix*, not that they don't learn.
81
+
82
+ ---
83
+
84
+ ## Read step (start of every task)
85
+
86
+ Lessons are capped — at most 5 per layer per read, so memory never crowds out the session.
87
+
88
+ ```text
89
+ # built-in
90
+ list_builtin_knowledge { kind: "lessons", query: "<task / area keywords>", limit: 5 }
91
+ # yours
92
+ memory.list { scope: "project::reaper-mcp", tags: ["loop::mixing-lessons"], order: "rank", limit: 5 }
93
+ # when the task names an area (drum bus, vocals, limiting, track numbering…), one targeted search:
94
+ memory.search { q: "<area keywords>", scopes: ["project::reaper-mcp"], tags: ["loop::mixing-lessons", "layer::user"], limit: 5 }
95
+ ```
96
+
97
+ Match each lesson's **Applies when** line against the task and apply the matching
98
+ **Do this instead** lines as **soft constraints** — they bias the run unless they
99
+ conflict with the user's stated intent (the user wins; say so). When a built-in
100
+ lesson and a `user::` lesson with the same slug both match, the user's wins.
101
+
102
+ ### Plugin lookup
103
+
104
+ **Pick a plugin for a job** (which EQ / compressor / limiter to use):
105
+
106
+ ```text
107
+ list_available_fx # what is installed
108
+ list_builtin_knowledge { kind: "plugins", tags: ["category::<category>"], limit: 100 } # built-in
109
+ memory.list { scope: "project::reaper-mcp", tags: ["category::<category>"], view: "summary", limit: 100 } # yours
110
+ ```
111
+
112
+ A record matches an installed FX when any of its `fx::<pattern>` tags is a
113
+ case-insensitive substring of the installed name. Group the results by
114
+ `plugin::<vendor>/<plugin>`; when a plugin has both layers, the **user record's
115
+ tags win** (its `preference::` overrides the built-in one). The highest
116
+ `preference` wins; when the user asks for a character (`transparent`,
117
+ `vintage`, …) prefer a matching `style::` tag. Nothing matches → fall back to the
118
+ stock REAPER plugin for that category.
119
+
120
+ **Before touching a plugin** (adding it, setting a parameter, interpreting one on
121
+ the chain), read both layers:
122
+
123
+ ```text
124
+ get_builtin_knowledge { key: "plugins::<vendor>/<plugin>" } # built-in (may be null)
125
+ memory.read { scope: "project::reaper-mcp", key: "user::plugins::<vendor>/<plugin>" } # yours (may be absent)
126
+ ```
127
+
128
+ No key at hand (you only have the FX name from a track)? Find it via the `fx::`
129
+ tags of `list_builtin_knowledge { kind: "plugins", limit: 100 }` and
130
+ `memory.list { scope: "project::reaper-mcp", tags: ["plugin-knowledge"], view: "summary", limit: 100 }`.
131
+ Neither layer has it → bootstrap it (below) before using the plugin.
132
+
133
+ **Combining the two:** the user record is an overlay. A section it contains
134
+ replaces the built-in section of the same heading; `## Learned notes` from both
135
+ apply (user notes first, and they win on a conflict); everything else comes from
136
+ the built-in record.
137
+
138
+ ---
139
+
140
+ ## Plugin knowledge
141
+
142
+ ### The record
143
+
144
+ One record per plugin per layer, pure markdown (no frontmatter, no HTML comments,
145
+ no dates or counts — the store and tags carry those). The header line repeats the
146
+ tag facts for human readers; the tags are what lookups filter on, so keep the two
147
+ in step.
148
+
149
+ ```markdown
150
+ # <Plugin name>
151
+
152
+ **Category:** <category> · **Style:** <style> · **Vendor:** <Vendor> · **Preference:** <n>/100
153
+ **Matches FX:** `<exact REAPER FX name>`, `<alternate>`
154
+ **Replaces:** `<stock id>`
155
+
156
+ ## What it does
157
+
158
+ <one paragraph — purpose, sonic character, primary use cases>
159
+
160
+ ## Key parameters by name
161
+
162
+ | Parameter | Range | Description |
163
+ |-----------|-------|-------------|
164
+
165
+ ## Recommended settings
166
+
167
+ ### <Use case>
168
+
169
+ | Parameter | Value | Why |
170
+ |-----------|-------|-----|
171
+
172
+ ## Presets worth knowing
173
+
174
+ ## When to prefer this
175
+
176
+ ## Learned notes
177
+
178
+ **Param indices (<VST3|VST|AU|JS>, N params):** 0 Name · 1 Name · …
179
+
180
+ **Normalized (0–1) scaling, verified by probing:**
181
+
182
+ - <Param>: <mapping> (<probe → readback>)
183
+
184
+ **Gotchas:** <…>
185
+
186
+ **Kept settings (with context):**
187
+
188
+ - <Context — genre, source, goal>: <setting the user kept and why it worked>
189
+ ```
190
+
191
+ A **user overlay** for a plugin that already has a built-in record needs only the
192
+ header (so its tags can be set) and the sections it adds or overrides — usually
193
+ just `## Learned notes`. A plugin the built-in layer doesn't know gets a full record.
194
+
195
+ Field guide:
196
+ - **category** — `eq`, `compressor`, `limiter`, `saturator`, `reverb`, `delay`,
197
+ `gate`, `de-esser`, `amp-sim`, `channel-strip`, `analyzer`, `stereo-imager`,
198
+ `multiband`, `pitch-correction`.
199
+ - **style** — `transparent`, `character`, `vintage`, `modern`, `surgical`.
200
+ - **preference** — 30–50 stock/fallback, 55–70 good third-party, 70–85 go-to,
201
+ 85–95 industry standard. Decides which plugin wins in a category.
202
+ - **fx patterns** — the exact strings REAPER shows (`VST3: Pro-Q 3 (FabFilter)`)
203
+ plus a short form (`Pro-Q 3`); add VST and VST3 variants. Matching is a
204
+ case-insensitive substring.
205
+ - **replaces** — stock plugin slugs this supersedes (`rea-eq`, `rea-comp`).
206
+ - **extension** — tag `extension::<name>` when an opt-in extension (`docs/EXTENSIONS.md`) drives the
207
+ plugin's internals, e.g. `extension::vital`. The note in `## Learned notes` says which tools to use; a skill
208
+ uses them only when `list_tool_categories` shows that extension's category.
209
+
210
+ Write (user layer — the only layer a skill writes):
211
+
212
+ ```text
213
+ memory.write {
214
+ scope: "project::reaper-mcp",
215
+ key: "user::plugins::<vendor>/<plugin>",
216
+ value: "<the markdown record or overlay>",
217
+ tags: ["plugin-knowledge", "layer::user", "plugin::<vendor>/<plugin>", "category::<cat>",
218
+ "vendor::<vendor>", "preference::<n>", "style::<style>", "fx::<pattern>", …, "replaces::<id>", …],
219
+ kind: "signal",
220
+ host: "mixing",
221
+ trigger: "<manual | gotcha | paid-off>",
222
+ source_agent: "<mixer | mastering | critique | learn-plugin>"
223
+ }
224
+ ```
225
+
226
+ No `ttl_days` — plugin knowledge does not expire. An overlay copies the built-in
227
+ record's tags (changing only what the user overrides) so lookups by category and
228
+ FX name still find it. A write **replaces** the whole record (value *and* tags), so
229
+ always read-merge-write the `user::` key.
230
+
231
+ ### Bootstrapping an unknown plugin
232
+ Asked to use a plugin neither layer knows, create a user record *before* mixing
233
+ with it. Dispatch the **`plugin-researcher`** agent with the plugin name (and
234
+ what you need it for) and wait for its one-paragraph report. That keeps the web
235
+ pages and parameter dumps out of your context. Its steps, which you follow
236
+ inline when the agent isn't available:
237
+
238
+ 1. **Research the web** (`WebSearch` / `WebFetch`): what it is, its category and
239
+ character, key parameters, typical settings. Prefer the manufacturer's manual.
240
+ 2. **Test it on a scratch track** (`[MCP] plugin-research`, never the user's
241
+ tracks): `get_fx_parameters` for the **real parameter names and indices** of
242
+ this build (ground truth). Probe the key params at 0/0.25/0.5/0.75/1 and read
243
+ the formatted values back to get their scaling. Then delete the scratch track.
244
+ 3. **Write the record** above: research in the main sections, the verified param
245
+ map and scaling under `## Learned notes`. The preference is a proposal from
246
+ the field guide.
247
+ 4. **Then use it**, and keep refining it as you go. Mention the proposed
248
+ preference to the user once; don't block the mix on it.
249
+
250
+ `/learn-plugin` runs the same agent, then asks the user only what it couldn't
251
+ work out.
252
+
253
+ ### Update — on a durable, reusable learning
254
+ Fold into the `user::` record's `## Learned notes` (creating the overlay if the
255
+ plugin only has a built-in record) when you discover something worth knowing next
256
+ time:
257
+ - a **param-index map / normalized scaling** verified via `get_fx_parameters`
258
+ - a **setting the user kept or approved** (not one you tried and reverted)
259
+ - a **gotcha** (non-linear param, VST2/VST3 index differs, a mode that breaks something)
260
+
261
+ No learning → no write. Refine notes in place, don't duplicate; if a new
262
+ observation contradicts an old one (or the built-in notes), keep both with their
263
+ differing context. Context is mandatory on kept settings — a setting that works on
264
+ a bright pop vocal can be wrong on a dark rock one.
265
+
266
+ ---
267
+
268
+ ## Process memory (mixing lessons)
269
+
270
+ Procedural "do better next time" lessons about **your own process** — not facts
271
+ about the user's mix. Advisory input, never an automatic action.
272
+
273
+ ### Write — at a correction point (immediately) or in the retrospective
274
+ Triggers:
275
+ - the user **reverted** a snapshot you made, said "no, that's wrong", or
276
+ **overrode** a target/decision you proposed
277
+ - a **re-measure** (meters/LUFS/correlation/crest) showed your change made it worse
278
+ - you hit a **recurring trap** (over-compressed, widened before checking mono, clipped the bus)
279
+ - the **retrospective** surfaced friction, or a lesson you applied paid off
280
+
281
+ 1. **Deduplicate** — a recurrence must UPDATE, not add:
282
+
283
+ ```text
284
+ memory.search { q: "<key words of the lesson>", scopes: ["project::reaper-mcp"], tags: ["loop::mixing-lessons"], limit: 10 }
285
+ ```
286
+
287
+ Also run `list_builtin_knowledge { kind: "lessons", query: "<same words>" }`.
288
+ A match on a `user::mixing-lessons::<slug>` → rewrite that key. A match on a
289
+ **built-in** `mixing-lessons::<slug>` → write `user::mixing-lessons::<slug>`
290
+ (same slug, so the two read as one lesson; it carries your recurrence count).
291
+
292
+ 2. **Write** — to the user layer only:
293
+
294
+ ```text
295
+ memory.write {
296
+ scope: "project::reaper-mcp",
297
+ key: "user::mixing-lessons::<kebab-slug>",
298
+ value: "<markdown body — shape below>",
299
+ tags: ["loop::mixing-lessons", "layer::user", "source::<trigger>", "topic::<area>"], # + "status::structural" for a design gap
300
+ trigger: "<assumption-wrong | gotcha | near-miss | stuck-loop | paid-off>",
301
+ ttl_days: 90,
302
+ source_agent: "<mixer | mastering | critique | learn-plugin>",
303
+ cited: ["project::reaper-mcp::mixing-lessons::<slug>", …] # lessons that actually shaped this run
304
+ }
305
+ ```
306
+
307
+ Same scope + key overwrites in place: **the store increments `seen_count`**
308
+ and re-passing `ttl_days` refreshes the expiry. Never write a count, date or
309
+ status into the body. A lesson you applied that *worked* is still worth an
310
+ UPDATE — successful application is recurrence evidence.
311
+
312
+ Body shape — one lesson per record, ~1,500 characters max:
313
+
314
+ ```markdown
315
+ # <one-line takeaway — what to do, not what it is about>
316
+
317
+ **Applies when:** <concrete signal — task, bus/track type, tool name, measurement>
318
+
319
+ **What happened:** <the concrete observable>
320
+ **Why:** <root cause, or "unknown">
321
+ **Do this instead:** <prescriptive, testable instruction>
322
+ **Promotion target:** <skill → Hard Rules, or "none">
323
+ ```
324
+
325
+ No run residue (no song names, session narrative or timestamps) — a lesson is
326
+ written for the *next* session, which has none of that context.
327
+
328
+ ### Self-heal (the slow tier)
329
+ When a `user::` lesson has **`seen_count >= 3`** (the store's column) or carries
330
+ `status::structural`, surface it — never act silently:
331
+
332
+ > "I've hit *'read crest before bus comp'* 3× now. Promote it to a permanent rule
333
+ > in the mixer skill?"
334
+
335
+ On approval, add a one-line hard rule to the owning skill's `## Hard Rules`
336
+ (`.claude/skills/{mixer|mastering|critique}/SKILL.md`), then UPDATE the lesson
337
+ adding the `status::promoted` tag (keep it as the audit trail of why the rule
338
+ exists; it stops re-suggesting). A lesson worth sharing with every user can also be
339
+ published to the built-in layer (below) — suggest it, never do it unasked.
340
+
341
+ ### Entrenchment guards (non-negotiable)
342
+ 1. Lessons are **advisory**; the only path to a behavior guarantee is the
343
+ confirmed promotion edit above.
344
+ 2. **Recurrence** (`seen_count >= 3`), not one bad run, gates promotion.
345
+ 3. User lessons **expire** (`ttl_days: 90`, refreshed on recurrence) — the store
346
+ drops stale ones. Built-in lessons and plugin records are curated, so they do
347
+ not expire.
348
+ 4. **Contradictions are flagged, not overwritten** — the dedup search finds the
349
+ prior lesson; a genuine reversal is the user's call.
350
+ 5. A lesson must **never** relax an existing hard rule (e.g. "always snapshot
351
+ before changes"). Those are invariants.
352
+ 6. **Lesson reads are capped** (5 per query) so memory never crowds out the session.
353
+
354
+ ---
355
+
356
+ ## Publishing to the built-in layer (maintainer only)
357
+
358
+ `/mix-memory publish <key>` turns a proven `user::` record into a built-in one. It
359
+ needs write access to the `reaper-mcp` org (member/admin/owner) — for everyone
360
+ else the write lands in their personal store, so check `org.list` first and stop
361
+ if the role is `viewer` or the org is absent.
362
+
363
+ 1. Read the `user::` record and, if one exists, the built-in record of the same
364
+ name; merge them (for a plugin: the overlay's sections over the built-in ones,
365
+ notes folded together) and show the result for approval.
366
+ 2. Write it with `org: "reaper-mcp"`, the key **without** `user::`, tag
367
+ `layer::built-in` instead of `layer::user`, and **no** `ttl_days`.
368
+ 3. `memory.delete` the `user::` key (safe — never delete the built-in key).
369
+
370
+ ## Consolidation
371
+
372
+ Run via `/mix-memory consolidate` when the user's notes sprawl or lessons overlap.
373
+ It only ever touches `user::` records:
374
+ - **Plugin overlays** → fold proven, repeated kept settings from `## Learned notes`
375
+ into the overlay's own `## Recommended settings`; keep param maps and gotchas.
376
+ - **Lessons** → merge near-duplicate `user::` lessons into the older key (rewrite
377
+ its body, then `memory.delete` the duplicate). Expiry is automatic.
@@ -161,14 +161,14 @@ For a healthy mix signal chain:
161
161
 
162
162
  | Scenario | Meter Says | Listener Hears | Correct Action |
163
163
  |----------|-----------|---------------|----------------|
164
- | Bass and vocal at same RMS | "Equal volume" | Bass is much quieter | Bass should read 3-6 dB hotter on meters |
165
- | Hi-hat and kick at same peak | "Equal transients" | Hi-hat is much louder | Pull hi-hat fader down 3-5 dB |
164
+ | Bass and vocal at same RMS | "Equal volume" | Not equal: RMS ignores how the ear weights frequency | Compare short-term LUFS (LU), never RMS; see `perceived-loudness.md` |
165
+ | Hi-hat and kick at same peak | "Equal transients" | Hi-hat is much louder | Compare in LU and in `kDb` bands; expect the hats lower |
166
166
  | "Flat" spectrum on analyzer | "Balanced mix" | Presence-heavy, harsh | A well-balanced mix has a gentle downward slope on the analyzer |
167
- | LUFS matches target | "Correct loudness" | Depends on spectral content | LUFS uses K-weighting which partially compensates, but per-track balance still needs perceptual awareness |
167
+ | LUFS matches target | "Correct loudness" | Correct overall, not per element | Per-element balance: LU of each element against the mix bus, same section, post-fader |
168
168
 
169
169
  ### LUFS and K-Weighting
170
170
 
171
- LUFS measurements already incorporate some psychoacoustic compensation via K-weighting (a +4 dB shelf at ~1.7 kHz + a 100 Hz high-pass). This means LUFS is better than raw RMS for perceived loudness — but it's a broadband measure. It tells you the overall perceived loudness, NOT whether individual instruments within the mix are perceptually balanced against each other. Per-track balance decisions still require perceived loudness awareness.
171
+ LUFS measurements already incorporate psychoacoustic weighting via K-weighting (a +4 dB shelf above ~1.5 kHz and a high-pass that rolls off the deep lows). This makes LUFS the measure for **every** level comparison, per track as well as for the whole mix. Read each element's short-term LUFS over the same section as the mix bus and compare in LU. For per-band decisions, use the K-weighted `kDb` band values. `perceived-loudness.md` has the per-band K gains and the stem reference points.
172
172
 
173
173
  ## Common Metering Mistakes
174
174
 
@@ -181,5 +181,5 @@ LUFS measurements already incorporate some psychoacoustic compensation via K-wei
181
181
  | Not accounting for intersample peaks | Always use a true peak limiter (ISP mode) |
182
182
  | Comparing short-term LUFS between songs | Compare integrated LUFS for an apples-to-apples comparison |
183
183
  | Ignoring crest factor | A mix with good integrated LUFS but low crest factor is over-compressed |
184
- | Setting all tracks to the same dBFS target | Account for perceived loudness — bass instruments need higher meter readings than presence-range instruments |
184
+ | Balancing tracks on dBFS / RMS readings | Gain-stage on dBFS (headroom); balance on LU against the mix bus (`perceived-loudness.md`) |
185
185
  | Treating a "flat" spectrum as balanced | The ear amplifies 2-5 kHz naturally; a flat spectrum sounds harsh. Expect a gentle downward slope from low to high |
@@ -0,0 +1,182 @@
1
+ # Perceived Loudness Reference
2
+
3
+ Human hearing does not perceive all frequencies equally. A bass note and a vocal at the same dB SPL sound like very different volumes. Every balance and EQ decision is made on **perceived** loudness, the level the listener hears, and never on raw meter values.
4
+
5
+ ## The Rule: Perceived Values Only, Never Raw
6
+
7
+ | Decision | Measure with | Never with |
8
+ |----------|--------------|------------|
9
+ | Level of one track/bus against another (balance) | **Short-term LUFS** (`read_track_lufs`, `measure_tracks` with `lufs`), as **LU relative to the mix bus**, all read over the same section | Peak dBFS, RMS dBFS, fader positions on their own |
10
+ | Tonal balance, masking, "which band is too much" | **`kDb`** of `read_track_spectrum` / `measure_tracks` bands (K-weighted) | The band's raw `db`, raw FFT bins |
11
+ | Mix loudness against a target | Integrated LUFS over the whole song (`reference/metering.md`) | RMS |
12
+ | Headroom and clipping only | Peak / true-peak dBFS, `read_track_meters` | (allowed here: this is not a balance decision) |
13
+ | Dynamics only | Crest factor (peak minus RMS) | (allowed here) |
14
+
15
+ Why: a raw value counts every frequency equally, but the ear does not. Equal RMS on a bass and a vocal does not mean equal loudness, and raw band levels understate 2–5 kHz by ~4 dB against the low mids and overstate the deep lows by 4–10 dB (see the K-weighting table). Even a perceptual number can mislead when it is read wrong: in Emptiness (2026-10-29) a raw band table made the bass look fine while it sounded far too loud. The real cause was a changed EQ band, which only the ear and a parameter diff revealed.
16
+
17
+ Rules that follow from it:
18
+
19
+ 1. **Same section, same window.** Compare LU only between readings taken over the same passage, ideally in one `measure_tracks` call. A chorus reading against a verse reading compares the arrangement, not the balance. Measure where every compared element is playing (check the regions first).
20
+ 2. **Track meters are pre-fader.** The LUFS and spectrum meters sit in the track's FX chain, before the fader. A track's level in the mix is its reading **plus its fader (dB)**. A bus, which sums its children after their faders, reads them post-fader. Seen live: VOX at +5 dB read -10.4 LU against MIX A, so it really sat at -5.4 LU.
21
+ 3. **A spectrum read is a ~0.5 s snapshot.** One band moves several dB with the note being played. Read 3 or more times across the section and judge the pattern, not one value.
22
+ 4. **The ear wins.** When the user reports a balance by ear, that is the finding. Measurements size the fix and check it worked; they never overrule the report. If a measurement and the ear disagree, suspect the measurement first (a changed plugin parameter, a bypassed meter, a pre-fader reading, a different section).
23
+
24
+ ## Reference Points
25
+
26
+ ### K-weighting per 1/3-octave band
27
+
28
+ K-weighting is the ITU-R BS.1770 filter behind every LUFS reading: a high-pass that rolls off the deep lows plus a +4 dB shelf above ~1.5 kHz. The spectrum tools add these gains to each band's raw `db` to get `kDb` (`K_WEIGHT_DB` in `apps/reaper-mcp-server/src/tools/spectrum.ts`; keep both tables equal). Values are the filter's gain at the band centre, computed from the standard's 48 kHz coefficients.
29
+
30
+ | Band (Hz) | 25 | 31.5 | 40 | 50 | 63 | 80 | 100 | 125 | 160 | 200 | 250 | 315 | 400 | 500 | 630 |
31
+ |---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
32
+ | **K gain (dB)** | -10.4 | -7.8 | -5.6 | -3.9 | -2.7 | -1.7 | -1.1 | -0.7 | -0.4 | -0.3 | -0.2 | -0.1 | 0 | 0 | +0.1 |
33
+
34
+ | Band (Hz) | 800 | 1k | 1.25k | 1.6k | 2k | 2.5k | 3.15k | 4k | 5k | 6.3k | 8k | 10k | 12.5k | 16k |
35
+ |---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
36
+ | **K gain (dB)** | +0.3 | +0.7 | +1.3 | +2.3 | +3.1 | +3.6 | +3.8 | +4.0 | +4.0 | +4.0 | +4.0 | +4.0 | +4.0 | +4.0 |
37
+
38
+ So a 4 kHz band and a 100 Hz band with the same raw `db` differ by **5.1 dB** in `kDb`, and a 40 Hz band by **9.6 dB**.
39
+
40
+ K-weighting is a deliberately gentle model: it is nearly flat from 100 Hz to 1 kHz, while the ear at moderate levels is less sensitive at 100 Hz than that (see the equal-loudness section below). Two consequences:
41
+
42
+ - At the **same LU**, a bass-dominant source can still sound a little quieter than a mid-range one. Don't add a fixed correction for this. Place the element with the stem reference points below, then confirm by ear.
43
+ - At **low monitoring levels** the gap grows (bass drops out first). Make balance decisions at a moderate level (~75–85 dB SPL).
44
+
45
+ ### Stem balance (dense rock / metal)
46
+
47
+ Starting points for short-term LUFS of each element (post-fader), in **LU below the mix bus**, read together over the densest section (chorus). They are working heuristics from mixing practice and the measured sessions below, not a published standard. They place an element; they are not a pass/fail target. Confirm by ear, and when the user settles a balance, record it as a `user::mixing-lessons::` lesson so the next mix starts from their taste.
48
+
49
+ | Element | LU below mix bus | Notes |
50
+ |---------|-----------------|-------|
51
+ | Lead vocal | -2 to -5 | The lead element sits closest to the mix level. In metal the vocal and the guitar bus are often within ~1–2 LU of each other |
52
+ | Rhythm guitars (bus) | -3 to -7 | Guitars ~2 LU or more above the vocal is the usual cause of "vocal too quiet, guitars too loud" |
53
+ | Drums (bus) | -5 to -9 | Snare and kick attack carry it. If the drums read under -10 LU, check the snare before the bus fader |
54
+ | Bass | -7 to -11 | Usually the lowest in LU yet still audible. A bass within ~3 LU of the guitars will sound heavy. Above -6 LU: check the low end before anything else |
55
+
56
+ Other genres: pop and hip-hop keep the lead vocal closest (-1 to -3 LU); in hip-hop and EDM the bass/808 can come up to the drums. Put a genre's own numbers in its `genres/{genre}.md` when they become known.
57
+
58
+ Measured data points (Emptiness, metal, 2026-10-29), LU below the MIX A bus, post-fader:
59
+
60
+ | Stage | Guitars | Vocal | Drums | Bass |
61
+ |-------|---------|-------|-------|------|
62
+ | Whole song, offline stem renders (ungated), before any change | -3.4 | -5.4 | -9.0 | -9.0 |
63
+ | Chorus 2 (208–222 s, live), after VOX +5 / GUITAR -1.5 | -3.6 | -5.4 | -8.6 | -8.7 |
64
+
65
+ By ear, the user first found the vocal too quiet and the guitars a bit loud, which matches the table: the guitars read above the vocal. After the +5 the vocal is still ~2 LU under the guitars in the chorus, because the guitars own 2–4 kHz (see masking below).
66
+
67
+ ### Spectral checks on `kDb`
68
+
69
+ - **Compare a band only with the same band.** Compare one track's band with the same band on another track, or on a reference. Never compare one band with a different band in the same spectrum. K-weighting gives a correct broadband loudness, but it is not an equal-loudness curve per band. Also, 1/3-octave bands get wider towards the top, so a raw or K-weighted music spectrum often peaks at 60–125 Hz even in a well-balanced rock or metal mix. "The loudest band is the low end" is not, by itself, a finding.
70
+ - **Comparing to a reference.** Measure a reference track the same way: same section type, same `fftSize`, `kDb`, and both level-matched to the same short-term LUFS first. A difference of more than ~3 dB across 3 or more adjacent bands is a real tonal difference. One band alone is usually arrangement noise.
71
+ - **Masking between two tracks.** Compare their `kDb` in the contested band (kick/bass 60–125 Hz, bass/guitars 125–250 Hz, guitars/vocal 1–4 kHz), each with its fader added. The track that is more than ~3 dB higher there owns the band. To let the other one through, cut the owner there, or boost the other. Live example (Emptiness, chorus 2): the guitars read -41 to -45 `kDb` across 2–4 kHz, against the vocal's -57 to -64 (both with their faders added). The guitars own the vocal's presence range, so more vocal fader alone won't unbury it. Carve the guitars at 2–4 kHz (ideally a dynamic EQ keyed from the vocal) before raising the vocal further.
72
+ - **Analyzer slope.** A balanced mix's raw `db` bands slope downward from lows to highs. In `kDb` the slope is flatter, because the +4 dB shelf lifts the top. Never "flatten" either curve by EQ.
73
+
74
+ ## Equal-Loudness Contours (ISO 226:2003)
75
+
76
+ Equal-loudness contours (historically "Fletcher-Munson curves") map the SPL required at each frequency to sound equally loud to the human ear. Key takeaways:
77
+
78
+ | Frequency Range | Perceived Loudness at Same dB | Implication for Mixing |
79
+ |----------------|------------------------------|----------------------|
80
+ | 20-80 Hz (sub/bass) | Sounds **much quieter** than meter shows | Bass instruments need higher dB to feel balanced |
81
+ | 80-250 Hz (upper bass) | Sounds **somewhat quieter** | Warmth region; needs modest boost over mids |
82
+ | 250 Hz-1 kHz (mids) | Sounds **roughly accurate** to meter | Meters are fairly reliable here |
83
+ | 1-5 kHz (presence) | Sounds **louder** than meter shows | Most sensitive hearing range — a little goes a long way |
84
+ | 2-5 kHz (peak sensitivity) | Sounds **significantly louder** | The ear is 10-15 dB more sensitive here than at 100 Hz |
85
+ | 5-8 kHz (sibilance) | Sounds **louder** | Sibilance cuts through even at low levels |
86
+ | 8-20 kHz (air) | Sensitivity **drops off** | Needs more energy to be perceived, but fatigues quickly if excessive |
87
+
88
+ ### The Critical Insight
89
+
90
+ At moderate listening levels (~70-85 dB SPL, typical mixing/monitoring level):
91
+ - **3-4 kHz is ~10-15 dB more sensitive** than 100 Hz
92
+ - **A snare drum** (energy at 200 Hz + 2-5 kHz) sounds louder than **a bass guitar** (energy at 60-250 Hz) even at the same RMS dB reading
93
+ - **Vocals** (fundamental 100-400 Hz, formants at 1-5 kHz) naturally cut through a mix because their energy is in the ear's most sensitive band
94
+ - **Kick drum sub** (40-80 Hz) needs significantly more energy than hi-hats (8-12 kHz) to feel "present" — but hi-hats fatigue faster
95
+
96
+ ## Perceived Loudness by Instrument
97
+
98
+ Instruments ranked by how loud they **sound** relative to their **metered level**, from "sounds louder than meters suggest" to "sounds quieter":
99
+
100
+ | Instrument | Dominant Frequency Range | Perceived vs. Metered | Mixing Compensation |
101
+ |-----------|------------------------|----------------------|-------------------|
102
+ | Vocals | 1-5 kHz (formants) | Sounds **louder** | Can sit 2-4 dB below other elements on meters and still cut through |
103
+ | Snare drum | 200 Hz + 2-5 kHz (crack) | Sounds **louder** | Crack frequency is in the sensitive zone; don't over-boost |
104
+ | Hi-hat / cymbals | 6-12 kHz | Sounds **moderately louder** | Small dB changes are very audible; mix lower than you think |
105
+ | Acoustic guitar | 2-5 kHz (string attack) | Sounds **moderately louder** | Attack frequency carries; back off presence boosts |
106
+ | Electric guitar | 1-4 kHz (midrange) | Sounds **about right** | Meter-reading is fairly reliable for perceived level |
107
+ | Piano | 250 Hz-4 kHz (wide range) | Sounds **about right** | Depends on register; upper notes sound louder than lower |
108
+ | Kick drum | 60-100 Hz (thump) | Sounds **quieter** | Needs 3-6 dB more than meters suggest for balanced feel |
109
+ | Bass guitar | 60-250 Hz (fundamental) | Sounds **quieter** | Needs 2-5 dB more on meters than mid-range instruments |
110
+ | 808 / sub bass | 30-80 Hz | Sounds **much quieter** | On small speakers, nearly inaudible; rely on harmonics (150-300 Hz) |
111
+ | Pads / drones | Varies | Depends on spectral content | Low pads need more level; bright pads need less |
112
+
113
+ ## How to Apply This When Mixing
114
+
115
+ ### Balance by Perception, Not Meters
116
+
117
+ When setting or judging track balance:
118
+
119
+ 1. **Read short-term LUFS for every element and the mix bus over the same section** (one `measure_tracks` call), and express each element as LU below the mix bus.
120
+ 2. **Place each element against the stem reference points above**, starting with the lead element (usually the vocal), then guitars, drums, bass.
121
+ 3. **Never match RMS or peak levels across tracks**, and never infer balance from fader positions. Raw levels make low-frequency instruments look louder than they sound and presence-range instruments look quieter.
122
+ 4. **Move the fader by the LU difference you want.** 1 dB of fader is 1 LU for that element. Re-measure, then listen.
123
+ 5. **Verify by ear.** The numbers place the element; the listener decides.
124
+
125
+ ### Gain Staging Is Not Balance
126
+
127
+ Gain staging (`workflows/gain-staging.md`) sets each track's raw level (around -18 dBFS RMS, peaks below -6 dBFS) so plugins see a sensible input and the bus keeps headroom. This is the one place raw dBFS is the right measure, because it is about electrical headroom, not loudness. Don't use different raw targets per instrument to "pre-balance" the mix. Balance is a separate step, done afterwards in LU.
128
+
129
+ ### EQ Decisions Informed by Perception
130
+
131
+ - **Boosting 2-5 kHz**: Even +1 dB is very audible here. Be conservative — the ear amplifies this range naturally.
132
+ - **Cutting 200-400 Hz**: The ear is less sensitive here, so cuts may need to be larger (-3 to -6 dB) to make an audible difference.
133
+ - **Boosting sub-bass (30-80 Hz)**: Large boosts (+4-6 dB) may be needed to feel present, but check on multiple speaker systems — headphones and club systems reproduce this range very differently.
134
+ - **Air boosts (10-16 kHz)**: Sensitivity drops off, so +2-3 dB can sound subtle. But accumulated boosts across many tracks cause listening fatigue.
135
+
136
+ ### Compression and Dynamics
137
+
138
+ - **Fast attack on presence-range instruments** (vocals, snare) tames perceived loudness spikes more effectively than on bass instruments
139
+ - **Bass compression** with slower attack preserves the transient that helps the ear "locate" the note — critical since the fundamental is in a low-sensitivity range
140
+ - **Sidechain filtering** on bus compressors: if the compressor is triggered by low-frequency content, it may pump on bass hits even though those hits don't sound that loud — use a sidechain HPF to make the compressor react to what the listener hears
141
+
142
+ ### Monitoring Level Matters
143
+
144
+ Equal-loudness contours change shape at different SPL levels:
145
+ - **Low monitoring levels** (~65 dB SPL): Bass and treble perception drops off dramatically. Mixes balanced at low volume tend to have too much bass and treble when played loud.
146
+ - **Moderate levels** (~75-85 dB SPL): Best for critical balance decisions. The ear's response is most "flat" here.
147
+ - **Loud levels** (~90+ dB SPL): Perception flattens further, but ear fatigue sets in quickly. Not reliable for balance.
148
+
149
+ **Best practice**: Mix at a moderate, conversational level (~79 dB SPL is often cited). Check at low and high volumes occasionally, but make balance decisions at moderate levels.
150
+
151
+ ## Volume-Dependent Perception (The "Loudness Button" Effect)
152
+
153
+ At lower playback volumes (casual listening, phone speakers):
154
+ - Bass nearly disappears
155
+ - Presence range dominates
156
+ - The mix sounds "thin" compared to studio monitoring
157
+
158
+ At higher playback volumes (club, concert):
159
+ - Bass becomes overwhelming
160
+ - Presence range can feel harsh
161
+ - The mix sounds "boomy" compared to studio monitoring
162
+
163
+ **Implication**: A well-balanced mix at moderate monitoring levels translates best across playback systems. Don't chase balance at extreme volumes.
164
+
165
+ ## Frequency Masking and Perceived Loudness
166
+
167
+ Two instruments in the same frequency range don't just compete for spectral space — they mask each other's perceived loudness:
168
+
169
+ - **Kick + bass** competing at 60-120 Hz: Neither sounds as loud as it would solo. Use sidechain compression or frequency separation (kick at 60 Hz, bass at 100 Hz) so each is perceived clearly.
170
+ - **Vocals + guitars** competing at 1-4 kHz: Guitars mask vocal presence. Cut guitars 2-4 kHz or boost vocals there — a small EQ move has outsized perceptual impact because this is the sensitivity peak.
171
+ - **Multiple instruments in the mud zone** (200-500 Hz): Cumulative energy in a low-sensitivity range creates "mud" — the ear can't separate the sources. HPF everything that doesn't need this range.
172
+
173
+ ## Quick Decision Guide
174
+
175
+ | Situation | Wrong Approach | Perceived-Loudness-Aware Approach |
176
+ |-----------|---------------|----------------------------------|
177
+ | Bass feels too quiet / too loud | Compare the bass RMS or raw bands with the other tracks | Read short-term LU of the bass vs the mix bus; place it against the stem reference points; check the low-end `kDb` |
178
+ | Vocals feel buried | Boost the vocal fader until it's the loudest meter | Compare the vocal LU with the guitars (guitars above the vocal = buried); check 1–4 kHz `kDb` masking from guitars/keys; a small presence move goes far |
179
+ | Cymbals are harsh | "They're not even that loud on meters!" | Read their 6–12 kHz `kDb`. The raw reading understates them by ~4 dB. Pull the fader or EQ there |
180
+ | Mix sounds thin | Boost everything below 200 Hz | Check bass and kick LU against the reference points, and the monitoring level (low volume exaggerates thinness) |
181
+ | Mix sounds muddy | Cut all low-mids on every track | Find which tracks lead the 200–500 Hz `kDb`; cut those. The ear needs bigger cuts here to notice |
182
+ | A change "measures fine" but the user says it sounds wrong | Defend the measurement | The ear wins. Diff plugin parameters against the before-snapshot and re-check the measurement setup |
@@ -230,6 +230,10 @@ params:
230
230
  description: "Audio editing complete — crossfades, timing, phase, cleanup done"
231
231
  ```
232
232
 
233
+ ### A/B-testing MIDI or arrangement variants
234
+
235
+ Comparing two versions of a part (a drum groove, a chorus) is done in fixed lanes, one complete version per lane. The procedure and its gotchas are in the `/editing` skill (`.claude/skills/editing/SKILL.md`), not here.
236
+
233
237
  ## Verification
234
238
 
235
239
  After completing audio editing: