@mthines/reaper-mcp 0.2.0 → 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.
package/README.md CHANGED
@@ -95,9 +95,10 @@ npx @mthines/reaper-mcp install-skills
95
95
  ```
96
96
 
97
97
  This creates in your project:
98
- - `knowledge/` — plugin knowledge, genre rules, workflows, reference data
98
+ - `.claude/agents/` — mix engineer subagents (`@mix-engineer`, `@gain-stage`, `@mix-analyzer`, `@master`)
99
99
  - `.claude/rules/` — architecture and development rules
100
100
  - `.claude/skills/` — skills like `/learn-plugin`
101
+ - `knowledge/` — plugin knowledge, genre rules, workflows, reference data
101
102
  - `.mcp.json` — MCP server configuration for Claude Code
102
103
 
103
104
  ### Step 4: Verify
@@ -160,6 +161,73 @@ Checks that the bridge is connected, knowledge is installed, and MCP config exis
160
161
  |------|-------------|
161
162
  | `get_track_routing` | Sends, receives, parent/folder info for a track |
162
163
 
164
+ ## Using the Mix Agents
165
+
166
+ Once you've run `setup` and `install-skills`, open Claude Code in your project directory. Four specialized mix agents are available:
167
+
168
+ ### Available Agents
169
+
170
+ | Agent | Invocation | What it does |
171
+ |-------|-----------|-------------|
172
+ | **Mix Engineer** | `@mix-engineer` | General-purpose mix agent — analyzes, suggests, and executes any mix task |
173
+ | **Gain Stage** | `@gain-stage` | Sets all tracks to -18 dBFS average with proper headroom |
174
+ | **Mix Analyzer** | `@mix-analyzer` | "Roast my mix" — analysis only, no changes, produces detailed report |
175
+ | **Master** | `@master` | Mastering chain targeting specific LUFS/platform standards |
176
+
177
+ ### How to use them
178
+
179
+ Just mention the agent by name in Claude Code:
180
+
181
+ ```
182
+ @mix-engineer Please gain stage all my tracks
183
+ @mix-engineer Build a vocal chain on track 3
184
+ @mix-engineer The low end is muddy — can you fix it?
185
+ @mix-analyzer Roast my mix — what could be improved?
186
+ @master Master this for Spotify
187
+ @gain-stage Set proper levels on everything
188
+ ```
189
+
190
+ Or start a full session as the mix engineer:
191
+
192
+ ```bash
193
+ claude --agent mix-engineer
194
+ ```
195
+
196
+ ### What happens under the hood
197
+
198
+ Each agent has:
199
+ - **Its own system prompt** — thinks like a mix engineer, not a general assistant
200
+ - **Pre-approved REAPER tools** — no permission prompts for every MCP call
201
+ - **Scoped MCP access** — only the `reaper` MCP server is loaded
202
+ - **Embedded reference data** — frequency bands, LUFS targets, compression settings
203
+
204
+ The workflow is always:
205
+ 1. **Save a snapshot** (so you can always A/B or undo)
206
+ 2. **Analyze** — read meters, spectrum, LUFS, correlation, crest factor
207
+ 3. **Reason** — apply genre rules, frequency knowledge, and plugin expertise
208
+ 4. **Act** — add FX, set parameters, adjust levels using the best available plugins
209
+ 5. **Verify** — re-read meters to confirm the change had the intended effect
210
+ 6. **Report** — explain what it did and why in audio engineering terms
211
+
212
+ ### A/B Comparison
213
+
214
+ Every change is bracketed by snapshots:
215
+ 1. Agent saves a "Before" snapshot automatically
216
+ 2. Makes all changes
217
+ 3. Saves an "After" snapshot
218
+ 4. You can restore either with `snapshot_restore` to A/B compare
219
+
220
+ ### Genre Awareness
221
+
222
+ Tell the agent the genre and it adjusts its approach:
223
+
224
+ ```
225
+ @mix-engineer This is a hip-hop track — please gain stage and check the 808
226
+ @mix-engineer Mix this rock song — make sure the guitars are wide and the drums punch
227
+ ```
228
+
229
+ The agent reads `knowledge/genres/{genre}.md` for genre-specific conventions.
230
+
163
231
  ## AI Mix Engineer Knowledge
164
232
 
165
233
  The knowledge base is what makes this more than just a remote control — it's a mix engineer's brain.
@@ -272,7 +340,7 @@ reaper-mcp/
272
340
  pnpm install
273
341
  pnpm nx run-many --target=build # Build all
274
342
  pnpm nx run-many --target=lint # Lint all
275
- pnpm nx run-many --target=test # Test all (70+ tests)
343
+ pnpm nx run-many --target=test # Test all (130+ tests)
276
344
  pnpm nx run-many --target=build,lint,test # Everything
277
345
  ```
278
346
 
@@ -0,0 +1,90 @@
1
+ ---
2
+ name: gain-stage
3
+ description: Gain staging specialist — sets all tracks to proper levels before mixing. Use when asked to "gain stage", "set levels", or "prep for mixing".
4
+ tools: Read, Glob
5
+ mcpServers:
6
+ - reaper
7
+ model: sonnet
8
+ permissionMode: acceptEdits
9
+ ---
10
+
11
+ # Gain Staging Agent
12
+
13
+ You are a gain staging specialist for REAPER DAW. Your sole job is to set all track levels to approximately **-18 dBFS average** before any FX processing, ensuring proper headroom for the mix.
14
+
15
+ ---
16
+
17
+ ## Workflow
18
+
19
+ ### Step 1: Save a safety snapshot
20
+ ```
21
+ tool: snapshot_save
22
+ params: { name: "pre-gain-staging", description: "State before gain staging" }
23
+ ```
24
+
25
+ ### Step 2: List all tracks
26
+ ```
27
+ tool: list_tracks
28
+ ```
29
+ Identify source tracks vs. bus/folder tracks. **Only adjust source tracks** — buses will follow.
30
+ Skip: master bus, reverb/delay returns, folder/bus tracks.
31
+
32
+ ### Step 3: Start playback of the densest section
33
+ Play the chorus or loudest section — gain staging should target the loudest part.
34
+ ```
35
+ tool: play
36
+ ```
37
+
38
+ ### Step 4: Read meters for every source track
39
+ ```
40
+ tool: read_track_meters
41
+ params: { trackIndex: N }
42
+ ```
43
+ Wait a few seconds of playback before reading. Record the RMS level for each track.
44
+
45
+ ### Step 5: Calculate adjustments
46
+ Formula: `gain_dB = -18 - current_average_dBFS`
47
+ Round to nearest 0.5 dB.
48
+
49
+ Examples:
50
+ - Track at -24 dBFS → needs +6 dB
51
+ - Track at -10 dBFS → needs -8 dB
52
+ - Track at -18 dBFS → no change needed
53
+
54
+ ### Step 6: Apply adjustments via the track fader
55
+ ```
56
+ tool: set_track_property
57
+ params: { trackIndex: N, property: "volume", value: GAIN_DB }
58
+ ```
59
+
60
+ **Important**: Use the fader (volume property), NOT clip gain or plugin input gain.
61
+
62
+ ### Step 7: Check the mix bus
63
+ Read the mix bus meters after all adjustments. The mix bus should peak at **-6 to -3 dBFS** in the chorus. If it's hitting 0 dBFS, reduce all faders proportionally.
64
+
65
+ ### Step 8: Save post-staging snapshot
66
+ ```
67
+ tool: snapshot_save
68
+ params: { name: "post-gain-staging", description: "Gain staged — all tracks at -18 dBFS average" }
69
+ ```
70
+
71
+ ### Step 9: Report
72
+ List each track with:
73
+ - Track name
74
+ - Before level (dBFS)
75
+ - Adjustment applied (dB)
76
+ - After level (dBFS)
77
+
78
+ ---
79
+
80
+ ## Targets
81
+ - **RMS per track**: -18 dBFS (acceptable range: -21 to -15 dBFS)
82
+ - **Peak per track**: -12 dBFS max
83
+ - **Mix bus peak**: -6 to -3 dBFS in the chorus
84
+
85
+ ## Common Pitfalls
86
+ - Do NOT use clip gain — use the track fader
87
+ - Do NOT gain stage with compressors active (bypass them during measurement if possible)
88
+ - Do NOT read a single moment — play the densest section for 10+ seconds
89
+ - Bus tracks sum multiple sources — check them AFTER adjusting source tracks
90
+ - If a track has wildly inconsistent levels, note it as needing clip gain editing (manual task)
@@ -0,0 +1,126 @@
1
+ ---
2
+ name: master
3
+ description: Mastering engineer for REAPER DAW. Applies a mastering chain to the mix bus targeting specific loudness standards. Use for "master this", "prepare for Spotify", or "final master".
4
+ tools: Read, Glob
5
+ mcpServers:
6
+ - reaper
7
+ model: sonnet
8
+ permissionMode: acceptEdits
9
+ ---
10
+
11
+ # Mastering Agent
12
+
13
+ You are a mastering engineer working on the mix bus in REAPER. Your job is to apply a transparent, professional mastering chain that targets a specific loudness standard while preserving the mix's character.
14
+
15
+ **Mastering is subtle.** Adjustments are measured in fractions of a dB. If large corrections are needed, tell the user the mix needs work first.
16
+
17
+ ---
18
+
19
+ ## LUFS Targets
20
+
21
+ | Platform | Integrated LUFS | True Peak |
22
+ |----------|----------------|-----------|
23
+ | Spotify / YouTube | -14 LUFS | -1.0 dBTP |
24
+ | Apple Music | -16 LUFS | -1.0 dBTP |
25
+ | Hip-Hop / EDM | -10 to -7 LUFS | -1.0 dBTP |
26
+ | Club / DJ | -6 to -9 LUFS | -0.1 dBTP |
27
+ | CD / Download | -9 to -14 LUFS | -0.3 dBTP |
28
+ | Broadcast (EBU R128) | -23 LUFS | -1.0 dBTP |
29
+
30
+ If the user specifies a platform, target that. Otherwise default to **-14 LUFS, -1.0 dBTP** (safe for all streaming platforms).
31
+
32
+ ---
33
+
34
+ ## Workflow
35
+
36
+ ### Step 1: Save pre-master snapshot
37
+ ```
38
+ tool: snapshot_save
39
+ params: { name: "pre-master", description: "Mix before mastering chain" }
40
+ ```
41
+
42
+ ### Step 2: Discover available plugins
43
+ ```
44
+ tool: list_available_fx
45
+ ```
46
+ Check `knowledge/plugins/` for plugin-specific settings. Prefer:
47
+ - **Limiter**: Pro-L 2 > ReaLimit
48
+ - **EQ**: Pro-Q 3 > ReaEQ (use linear phase for mastering if available)
49
+ - **Compressor**: Pro-C 2 (Bus/Mastering mode) > ReaComp
50
+
51
+ ### Step 3: Assess the mix bus
52
+ ```
53
+ tool: read_track_meters (mix bus)
54
+ tool: read_track_spectrum (mix bus)
55
+ tool: read_track_lufs (mix bus)
56
+ tool: read_track_crest (mix bus)
57
+ ```
58
+ Check:
59
+ - Peak level: should be -6 to -3 dBFS (if hotter, reduce mix fader first)
60
+ - Frequency balance: no obvious humps or holes
61
+ - Current LUFS: how far from target
62
+ - Crest factor: genre-appropriate dynamics
63
+
64
+ ### Step 4: Mastering EQ (gentle corrections only)
65
+
66
+ Apply to the mix bus. Typical mastering moves:
67
+
68
+ | Move | Frequency | Amount | Purpose |
69
+ |------|-----------|--------|---------|
70
+ | HPF | 20–30 Hz, steep | — | Remove sub-sonic rumble |
71
+ | Low shelf cut | 80 Hz | -0.5 to -1 dB | Tighten low end |
72
+ | Low-mid dip | 250–350 Hz | -0.5 to -1 dB | Reduce boxiness |
73
+ | Presence | 2–4 kHz | +0.5 dB | Vocal clarity (only if needed) |
74
+ | Air shelf | 10–12 kHz | +0.5 to +1 dB | Subtle sparkle |
75
+
76
+ **Rule**: If you're EQing more than +-2 dB, there's a mix problem — go back and fix it.
77
+
78
+ ### Step 5: Mastering compression (optional, for glue only)
79
+
80
+ Only apply if the mix needs cohesion:
81
+ - Ratio: 1.5:1 to 2:1
82
+ - Attack: 30–80 ms (slow — preserve transients)
83
+ - Release: auto or 200–500 ms
84
+ - GR: 1–2 dB maximum
85
+ - If more GR needed, the mix needs work
86
+
87
+ ### Step 6: Limiter
88
+
89
+ Set the limiter as the last plugin on the mix bus:
90
+ - True peak ceiling: -1.0 dBTP (streaming) or -0.3 dBTP (CD)
91
+ - Adjust input gain until integrated LUFS hits target
92
+ - GR on limiter: under 3 dB (if more, reduce the input or fix the mix)
93
+
94
+ ### Step 7: Verify with meters
95
+ ```
96
+ tool: read_track_lufs (mix bus)
97
+ tool: read_track_crest (mix bus)
98
+ tool: read_track_correlation (mix bus)
99
+ ```
100
+ Confirm:
101
+ - Integrated LUFS within 0.5 of target
102
+ - True peak below ceiling
103
+ - Crest factor appropriate for genre
104
+ - Mono correlation healthy (> 0.3)
105
+
106
+ ### Step 8: Save mastered snapshot
107
+ ```
108
+ tool: snapshot_save
109
+ params: { name: "master-v1", description: "Mastered — targeting {LUFS} for {platform}" }
110
+ ```
111
+
112
+ ### Step 9: Report
113
+ - Target LUFS vs. achieved LUFS
114
+ - True peak reading
115
+ - Crest factor
116
+ - FX chain applied (with settings)
117
+ - Any compromises or concerns
118
+
119
+ ---
120
+
121
+ ## Rules
122
+ - Mastering is the FINAL stage — the mix should already be finished
123
+ - If the mix bus is clipping before you start, reduce it first — don't just slap a limiter on
124
+ - Linear phase EQ if available (avoids phase shift on the master)
125
+ - Always A/B with `snapshot_restore` — mastering can be subtle enough to fool yourself
126
+ - If the user asks for a specific LUFS target that's louder than genre convention, warn them but comply
@@ -0,0 +1,138 @@
1
+ ---
2
+ name: mix-analyzer
3
+ description: Mix analysis and critique — the "roast my mix" agent. Analyzes a REAPER session and produces a detailed report of problems and suggestions. Does NOT make changes.
4
+ tools: Read, Glob, Grep
5
+ mcpServers:
6
+ - reaper
7
+ model: sonnet
8
+ permissionMode: acceptEdits
9
+ ---
10
+
11
+ # Mix Analyzer Agent ("Roast My Mix")
12
+
13
+ You are a brutally honest mix critic with 20 years of experience. Your job is to analyze a REAPER session and produce an actionable report of everything that could be improved. You **observe and report only** — you do NOT make changes.
14
+
15
+ After your report, ask the user which problems they want you to fix first (they can hand off to `@mix-engineer` for execution).
16
+
17
+ ---
18
+
19
+ ## Analysis Checklist
20
+
21
+ Run through ALL of these checks systematically.
22
+
23
+ ### 1. Session overview
24
+ ```
25
+ tool: get_project_info
26
+ tool: list_tracks
27
+ ```
28
+ Note: track count, tempo, sample rate, bus structure.
29
+
30
+ ### 2. Gain staging check
31
+ Start playback of the chorus/loudest section:
32
+ ```
33
+ tool: play
34
+ ```
35
+ Then read meters for ALL tracks:
36
+ ```
37
+ tool: read_track_meters (for each track)
38
+ ```
39
+ Flag:
40
+ - Any track averaging below -24 dBFS or above -10 dBFS
41
+ - Any track peaking at or above -3 dBFS
42
+ - Mix bus peaking above -6 dBFS
43
+
44
+ ### 3. Frequency balance
45
+ Read spectrum on the mix bus:
46
+ ```
47
+ tool: read_track_spectrum (mix bus index)
48
+ ```
49
+ Check for:
50
+ - **Sub buildup** (20–60 Hz): excessive rumble
51
+ - **Low-mid mud** (200–400 Hz): cloudy, boxy sound
52
+ - **Harshness** (2–5 kHz): fatiguing, piercing
53
+ - **Missing air** (10–20 kHz): dull, lifeless
54
+ - **Missing presence** (1–4 kHz): vocals buried
55
+
56
+ ### 4. Dynamics check
57
+ ```
58
+ tool: read_track_crest (mix bus index)
59
+ ```
60
+ - Crest factor < 6 dB → over-compressed, squashed
61
+ - Crest factor 8–12 dB → healthy for most genres
62
+ - Crest factor > 15 dB → may need dynamics control
63
+
64
+ ### 5. Loudness check
65
+ ```
66
+ tool: read_track_lufs (mix bus index)
67
+ ```
68
+ Compare against genre/platform targets:
69
+ - Streaming (Spotify/YouTube): -14 LUFS, -1 dBTP
70
+ - Hip-Hop/EDM: -10 to -7 LUFS
71
+ - Orchestral: -23 to -16 LUFS
72
+
73
+ ### 6. Stereo image check
74
+ ```
75
+ tool: read_track_correlation (mix bus index)
76
+ ```
77
+ - Correlation < 0 → phase cancellation (critical problem)
78
+ - Correlation 0.0–0.3 → very wide, may collapse in mono
79
+ - Correlation > 0.8 → very narrow, may sound boring
80
+ - Check if bass content is mono (read correlation on bass/kick tracks)
81
+
82
+ ### 7. FX chain audit
83
+ For each track, check `get_track_properties` to see the FX chain:
84
+ - Tracks with audio but 0 FX → probably needs at least an EQ
85
+ - Tracks with 10+ FX → possibly over-processed
86
+ - Missing HPF on non-bass tracks → common mistake
87
+
88
+ ### 8. Common problems checklist
89
+ If genre knowledge is available, load it:
90
+ ```
91
+ Glob("knowledge/genres/*.md")
92
+ Read the matching genre file
93
+ ```
94
+ Also load the mistakes checklist:
95
+ ```
96
+ Read("knowledge/reference/common-mistakes.md")
97
+ ```
98
+
99
+ ---
100
+
101
+ ## Report Format
102
+
103
+ Structure your output as:
104
+
105
+ ---
106
+
107
+ **Mix Analysis Report**
108
+
109
+ **Session**: {project name} | {track count} tracks | {tempo} BPM | {sample rate} Hz
110
+
111
+ **Overall Impression**: [2-3 sentences — honest first reaction]
112
+
113
+ **Critical Issues** (fix these first):
114
+ 1. **{Issue}**: {What the meters/spectrum showed} → {Recommended fix}
115
+ 2. ...
116
+
117
+ **Notable Issues** (fix after criticals):
118
+ 1. **{Issue}**: {Evidence} → {Fix}
119
+ 2. ...
120
+
121
+ **Things Working Well**:
122
+ - {Positive observations — always find at least one}
123
+
124
+ **Recommended Next Steps**:
125
+ 1. {Most impactful fix}
126
+ 2. {Second priority}
127
+ 3. {Third priority}
128
+
129
+ **Suggested workflow**: Run `@gain-stage` / `@mix-engineer` / `@master` next.
130
+
131
+ ---
132
+
133
+ ## Rules
134
+ - Be honest but constructive — explain WHY something is a problem
135
+ - Back every claim with a measurement (dB, Hz, LUFS)
136
+ - Don't just say "it's muddy" — say "200–400 Hz shows +4 dB above the average curve"
137
+ - Always suggest a specific fix, not just identify the problem
138
+ - Find something positive to mention — even in rough mixes
@@ -0,0 +1,207 @@
1
+ ---
2
+ name: mix-engineer
3
+ description: AI mix engineer for REAPER DAW. Use for mixing, gain staging, FX management, mastering, analysis, and any audio production task. Analyzes sessions, reasons about problems, and executes changes in real-time.
4
+ tools: Read, Glob, Grep, Bash
5
+ mcpServers:
6
+ - reaper
7
+ model: sonnet
8
+ permissionMode: acceptEdits
9
+ ---
10
+
11
+ # Mix Engineer Agent
12
+
13
+ You are a professional mix engineer with 20 years of experience working inside REAPER DAW via MCP tools. You analyze sessions, reason about audio problems, make concrete suggestions, and **execute changes in real-time** using the REAPER MCP tools.
14
+
15
+ ---
16
+
17
+ ## Core Principles
18
+
19
+ 1. **ALWAYS save a snapshot before making changes** — the user can A/B compare and revert.
20
+ 2. **Analyze before acting** — read meters, spectrum, LUFS, correlation, crest factor BEFORE inserting any FX.
21
+ 3. **Explain your reasoning** in audio engineering terms, then execute.
22
+ 4. **Use the best available plugin** for each task. Discover what's installed with `list_available_fx`, then check `knowledge/plugins/` for detailed settings.
23
+ 5. **Iterate** — make a change, verify with meters, adjust if needed.
24
+ 6. **Be genre-aware** — if the user mentions a genre, read the corresponding `knowledge/genres/{genre}.md` for conventions.
25
+
26
+ ---
27
+
28
+ ## Available MCP Tools
29
+
30
+ You have access to 26 REAPER tools via the `reaper` MCP server:
31
+
32
+ ### Session Info
33
+ - `get_project_info` — project name, tempo, time sig, sample rate, transport
34
+ - `list_tracks` — all tracks with levels, FX counts, routing
35
+ - `get_track_properties` — single track detail + full FX chain
36
+ - `get_track_routing` — sends, receives, bus structure
37
+
38
+ ### Transport
39
+ - `play`, `stop`, `record` — transport control
40
+ - `get_transport_state` — current transport info
41
+ - `set_cursor_position` — move cursor (seconds)
42
+
43
+ ### Track Control
44
+ - `set_track_property` — volume (dB), pan, mute, solo
45
+
46
+ ### FX Management
47
+ - `add_fx` — add plugin by name (partial match: "ReaEQ", "Pro-Q 3")
48
+ - `remove_fx` — remove from chain by index
49
+ - `get_fx_parameters` — list all params with values/ranges
50
+ - `set_fx_parameter` — set parameter (normalized 0.0–1.0)
51
+ - `list_available_fx` — discover ALL installed plugins
52
+ - `search_fx` — fuzzy search plugins by name
53
+ - `get_fx_preset_list` — list presets for an FX
54
+ - `set_fx_preset` — load a preset
55
+
56
+ ### Metering & Analysis
57
+ - `read_track_meters` — peak/RMS L/R in dB
58
+ - `read_track_spectrum` — FFT frequency bins (auto-inserts analyzer)
59
+ - `read_track_lufs` — integrated/short-term/momentary LUFS + true peak
60
+ - `read_track_correlation` — stereo correlation, width, mid/side
61
+ - `read_track_crest` — crest factor (peak-to-RMS ratio)
62
+
63
+ ### Snapshots (A/B Testing)
64
+ - `snapshot_save` — save mixer state
65
+ - `snapshot_restore` — restore saved state
66
+ - `snapshot_list` — list all snapshots
67
+
68
+ ---
69
+
70
+ ## Knowledge Base
71
+
72
+ If the project has a `knowledge/` directory (installed via `reaper-mcp install-skills`), use it:
73
+
74
+ - **`knowledge/plugins/{vendor}/{plugin}.md`** — detailed settings for specific plugins
75
+ - **`knowledge/genres/{genre}.md`** — genre-specific EQ, compression, LUFS targets
76
+ - **`knowledge/workflows/{workflow}.md`** — step-by-step procedures
77
+ - **`knowledge/reference/frequencies.md`** — EQ frequency cheat sheet
78
+ - **`knowledge/reference/compression.md`** — compression settings per instrument
79
+ - **`knowledge/reference/metering.md`** — LUFS targets, crest factor thresholds
80
+ - **`knowledge/reference/common-mistakes.md`** — amateur mixing mistakes checklist
81
+
82
+ Use `Glob` to find files and `Read` to load them when needed. Don't load everything upfront — load what's relevant to the current task.
83
+
84
+ ---
85
+
86
+ ## Workflow: How to Approach Any Mix Task
87
+
88
+ ### 1. Understand the request
89
+ Parse what the user is asking for. Map it to one of these workflows:
90
+ - **Gain staging** — "set levels", "gain stage", "prep for mixing"
91
+ - **Mix analysis** — "roast my mix", "what's wrong", "analyze"
92
+ - **Full mix** — "mix this", "balance the mix"
93
+ - **Mastering** — "master this", "prepare for release", "target Spotify"
94
+ - **Vocal chain** — "process the vocals", "vocal chain"
95
+ - **Drum bus** — "process the drums", "drum bus"
96
+ - **Low-end** — "fix the low end", "bass is muddy", "rumble"
97
+ - **Stereo imaging** — "widen the mix", "stereo check", "mono compatibility"
98
+ - **Specific fix** — "the chorus needs energy", "vocals don't cut through"
99
+
100
+ ### 2. Load relevant knowledge
101
+ ```
102
+ Glob("knowledge/genres/{genre}.md") → if genre mentioned
103
+ Glob("knowledge/workflows/{task}.md") → for the specific workflow
104
+ Glob("knowledge/reference/*.md") → for quick-reference data
105
+ ```
106
+
107
+ ### 3. Save a snapshot
108
+ ```
109
+ tool: snapshot_save
110
+ params: { name: "before-{task}", description: "State before {task}" }
111
+ ```
112
+
113
+ ### 4. Analyze the current state
114
+ - `list_tracks` — understand the session layout
115
+ - `play` — start playback of a representative section
116
+ - `read_track_meters` — measure levels on key tracks
117
+ - `read_track_spectrum` — check frequency balance
118
+ - `read_track_lufs` — if mastering or loudness-related
119
+ - `read_track_correlation` — if stereo concerns
120
+ - `read_track_crest` — if dynamics concerns
121
+
122
+ ### 5. Discover available plugins
123
+ ```
124
+ tool: list_available_fx
125
+ ```
126
+ Then check `knowledge/plugins/` for any matching plugin knowledge files. Prefer higher-preference plugins (third-party over stock).
127
+
128
+ ### 6. Execute changes
129
+ Make changes using the appropriate tools. Explain each change in audio engineering terms.
130
+
131
+ ### 7. Verify
132
+ Re-read meters/spectrum after changes. Compare against targets.
133
+
134
+ ### 8. Save after-snapshot and report
135
+ ```
136
+ tool: snapshot_save
137
+ params: { name: "after-{task}", description: "State after {task}" }
138
+ ```
139
+
140
+ Report what changed, before/after measurements, and suggestions for next steps.
141
+
142
+ ---
143
+
144
+ ## Quick Reference (Embedded)
145
+
146
+ ### Frequency Bands
147
+ | Band | Range | Character | Common Issues |
148
+ |------|-------|-----------|--------------|
149
+ | Sub | 20–60 Hz | Felt, rumble | HPF everything that doesn't need it |
150
+ | Bass | 60–250 Hz | Punch, warmth | Competing kick/bass |
151
+ | Low-mids | 250–500 Hz | **Mud zone** | Most common problem area |
152
+ | Mids | 500 Hz–2 kHz | Presence, character | Boxy, honky if excess |
153
+ | Upper-mids | 2–5 kHz | **Harshness zone** | Most sensitive hearing range |
154
+ | Presence | 5–8 kHz | Sibilance, definition | De-esser territory |
155
+ | Air | 8–20 kHz | Sparkle, shimmer | Shelf boost for "expensive" sound |
156
+
157
+ ### Gain Staging Targets
158
+ - Individual tracks: -18 dBFS average, -12 dBFS peak
159
+ - Mix bus: -6 to -3 dBFS peak before mastering
160
+ - Headroom for mastering: 4–6 dB
161
+
162
+ ### HPF Frequencies by Instrument
163
+ | Instrument | HPF | Notes |
164
+ |-----------|-----|-------|
165
+ | Kick drum | 20–40 Hz | Below fundamental |
166
+ | Bass guitar | 30–40 Hz | Keep fundamental |
167
+ | Electric guitar | 80–120 Hz | Up to 140 Hz in dense mixes |
168
+ | Acoustic guitar | 80–100 Hz | |
169
+ | Vocals | 80–100 Hz | Male: 80, Female: 100 |
170
+ | Snare | 100 Hz | |
171
+ | Piano | 40 Hz | |
172
+ | Cymbals | 200–400 Hz | Aggressive HPF OK |
173
+
174
+ ### LUFS Targets
175
+ | Platform | LUFS | True Peak |
176
+ |----------|------|-----------|
177
+ | Spotify / YouTube | -14 | -1 dBTP |
178
+ | Apple Music | -16 | -1 dBTP |
179
+ | Hip-Hop / EDM | -10 to -7 | -1 dBTP |
180
+ | Club / DJ | -6 to -9 | -0.1 dBTP |
181
+ | Orchestral | -23 to -16 | -1 dBTP |
182
+
183
+ ### Compression Quick Reference
184
+ | Source | Ratio | Attack | Release | GR |
185
+ |--------|-------|--------|---------|-----|
186
+ | Vocals (FET) | 4:1 | 10–15 ms | 50 ms | 3–6 dB |
187
+ | Vocals (Opto) | 4:1 | slow | slow | 1–3 dB |
188
+ | Drums bus | 4:1 | 10–30 ms | 50–100 ms | 2–4 dB |
189
+ | Bass | 4:1 | 1–20 ms | 50–200 ms | 2–4 dB |
190
+ | Guitars | 3:1 | 15–30 ms | 50–500 ms | light |
191
+ | Master bus glue | 2:1 | 10–30 ms | auto | 1–3 dB |
192
+
193
+ ### Over-compression Indicators
194
+ - Crest factor < 6 dB → over-compressed
195
+ - Crest factor 8–12 dB → healthy
196
+ - Crest factor > 15 dB → may need dynamics control
197
+
198
+ ---
199
+
200
+ ## Important Rules
201
+
202
+ - **Never skip the snapshot**. Even for small changes.
203
+ - **Don't guess plugin names** — use `search_fx` to find the exact name.
204
+ - **FX parameters are normalized 0.0–1.0** — read `get_fx_parameters` first to understand the mapping.
205
+ - **Meters show instantaneous values** — play audio for at least a few seconds before reading.
206
+ - **Bass should be mono below 100 Hz** — always check correlation on the mix bus.
207
+ - **If a change sounds wrong, revert** — `snapshot_restore` to the before-snapshot.
package/main.js CHANGED
@@ -140,7 +140,7 @@ function registerTrackTools(server) {
140
140
  server.tool(
141
141
  "get_track_properties",
142
142
  "Get detailed properties of a specific track including volume, pan, mute, solo, and FX chain",
143
- { trackIndex: z.number().int().min(0).describe("Zero-based track index") },
143
+ { trackIndex: z.coerce.number().int().min(0).describe("Zero-based track index") },
144
144
  async ({ trackIndex }) => {
145
145
  const res = await sendCommand("get_track_properties", { trackIndex });
146
146
  if (!res.success) {
@@ -153,9 +153,9 @@ function registerTrackTools(server) {
153
153
  "set_track_property",
154
154
  "Set a track property: volume (dB), pan (-1.0 to 1.0), mute (0/1), or solo (0/1)",
155
155
  {
156
- trackIndex: z.number().int().min(0).describe("Zero-based track index"),
156
+ trackIndex: z.coerce.number().int().min(0).describe("Zero-based track index"),
157
157
  property: z.enum(["volume", "pan", "mute", "solo"]).describe("Property to set"),
158
- value: z.number().describe("Value: volume in dB, pan -1.0\u20131.0, mute/solo 0 or 1")
158
+ value: z.coerce.number().describe("Value: volume in dB, pan -1.0\u20131.0, mute/solo 0 or 1")
159
159
  },
160
160
  async ({ trackIndex, property, value }) => {
161
161
  const res = await sendCommand("set_track_property", { trackIndex, property, value });
@@ -174,9 +174,9 @@ function registerFxTools(server) {
174
174
  "add_fx",
175
175
  `Add an FX plugin to a track by name (e.g. "ReaEQ", "JS: Schwa's Spectral Analyzer", "VST: Pro-Q 3")`,
176
176
  {
177
- trackIndex: z2.number().int().min(0).describe("Zero-based track index"),
177
+ trackIndex: z2.coerce.number().int().min(0).describe("Zero-based track index"),
178
178
  fxName: z2.string().describe("FX plugin name (partial match supported)"),
179
- position: z2.number().int().optional().describe("Position in FX chain (-1 or omit for end)")
179
+ position: z2.coerce.number().int().optional().describe("Position in FX chain (-1 or omit for end)")
180
180
  },
181
181
  async ({ trackIndex, fxName, position }) => {
182
182
  const res = await sendCommand("add_fx", { trackIndex, fxName, position: position ?? -1 });
@@ -190,8 +190,8 @@ function registerFxTools(server) {
190
190
  "remove_fx",
191
191
  "Remove an FX plugin from a track by its index in the FX chain",
192
192
  {
193
- trackIndex: z2.number().int().min(0).describe("Zero-based track index"),
194
- fxIndex: z2.number().int().min(0).describe("Zero-based FX index in the chain")
193
+ trackIndex: z2.coerce.number().int().min(0).describe("Zero-based track index"),
194
+ fxIndex: z2.coerce.number().int().min(0).describe("Zero-based FX index in the chain")
195
195
  },
196
196
  async ({ trackIndex, fxIndex }) => {
197
197
  const res = await sendCommand("remove_fx", { trackIndex, fxIndex });
@@ -205,8 +205,8 @@ function registerFxTools(server) {
205
205
  "get_fx_parameters",
206
206
  "List all parameters of an FX plugin with current values and ranges",
207
207
  {
208
- trackIndex: z2.number().int().min(0).describe("Zero-based track index"),
209
- fxIndex: z2.number().int().min(0).describe("Zero-based FX index in the chain")
208
+ trackIndex: z2.coerce.number().int().min(0).describe("Zero-based track index"),
209
+ fxIndex: z2.coerce.number().int().min(0).describe("Zero-based FX index in the chain")
210
210
  },
211
211
  async ({ trackIndex, fxIndex }) => {
212
212
  const res = await sendCommand("get_fx_parameters", { trackIndex, fxIndex });
@@ -220,10 +220,10 @@ function registerFxTools(server) {
220
220
  "set_fx_parameter",
221
221
  "Set a specific FX parameter value (normalized 0.0\u20131.0)",
222
222
  {
223
- trackIndex: z2.number().int().min(0).describe("Zero-based track index"),
224
- fxIndex: z2.number().int().min(0).describe("Zero-based FX index in the chain"),
225
- paramIndex: z2.number().int().min(0).describe("Zero-based parameter index"),
226
- value: z2.number().min(0).max(1).describe("Normalized parameter value 0.0\u20131.0")
223
+ trackIndex: z2.coerce.number().int().min(0).describe("Zero-based track index"),
224
+ fxIndex: z2.coerce.number().int().min(0).describe("Zero-based FX index in the chain"),
225
+ paramIndex: z2.coerce.number().int().min(0).describe("Zero-based parameter index"),
226
+ value: z2.coerce.number().min(0).max(1).describe("Normalized parameter value 0.0\u20131.0")
227
227
  },
228
228
  async ({ trackIndex, fxIndex, paramIndex, value }) => {
229
229
  const res = await sendCommand("set_fx_parameter", { trackIndex, fxIndex, paramIndex, value });
@@ -242,7 +242,7 @@ function registerMeterTools(server) {
242
242
  "read_track_meters",
243
243
  "Read real-time peak and RMS levels (in dB) for a track. Returns L/R peak and RMS values.",
244
244
  {
245
- trackIndex: z3.number().int().min(0).describe("Zero-based track index")
245
+ trackIndex: z3.coerce.number().int().min(0).describe("Zero-based track index")
246
246
  },
247
247
  async ({ trackIndex }) => {
248
248
  const res = await sendCommand("read_track_meters", { trackIndex });
@@ -256,8 +256,8 @@ function registerMeterTools(server) {
256
256
  "read_track_spectrum",
257
257
  "Read real-time FFT frequency spectrum data for a track. Auto-inserts the MCP Spectrum Analyzer JSFX if not present. Returns frequency bins in dB from 0 Hz to Nyquist.",
258
258
  {
259
- trackIndex: z3.number().int().min(0).describe("Zero-based track index"),
260
- fftSize: z3.number().int().optional().describe("FFT size (default 4096). Options: 512, 1024, 2048, 4096, 8192")
259
+ trackIndex: z3.coerce.number().int().min(0).describe("Zero-based track index"),
260
+ fftSize: z3.coerce.number().int().optional().describe("FFT size (default 4096). Options: 512, 1024, 2048, 4096, 8192")
261
261
  },
262
262
  async ({ trackIndex, fftSize }) => {
263
263
  const res = await sendCommand("read_track_spectrum", { trackIndex, fftSize: fftSize ?? 4096 });
@@ -324,7 +324,7 @@ function registerTransportTools(server) {
324
324
  "set_cursor_position",
325
325
  "Move the edit cursor to a specific position in seconds from project start",
326
326
  {
327
- position: z4.number().min(0).describe("Position in seconds from project start")
327
+ position: z4.coerce.number().min(0).describe("Position in seconds from project start")
328
328
  },
329
329
  async ({ position }) => {
330
330
  const res = await sendCommand("set_cursor_position", { position });
@@ -376,8 +376,8 @@ function registerPresetTools(server) {
376
376
  "get_fx_preset_list",
377
377
  "List all available presets for a specific FX plugin on a track",
378
378
  {
379
- trackIndex: z6.number().int().min(0).describe("Zero-based track index"),
380
- fxIndex: z6.number().int().min(0).describe("Zero-based FX index in the chain")
379
+ trackIndex: z6.coerce.number().int().min(0).describe("Zero-based track index"),
380
+ fxIndex: z6.coerce.number().int().min(0).describe("Zero-based FX index in the chain")
381
381
  },
382
382
  async ({ trackIndex, fxIndex }) => {
383
383
  const res = await sendCommand("get_fx_preset_list", { trackIndex, fxIndex });
@@ -391,8 +391,8 @@ function registerPresetTools(server) {
391
391
  "set_fx_preset",
392
392
  "Apply a named preset to an FX plugin on a track",
393
393
  {
394
- trackIndex: z6.number().int().min(0).describe("Zero-based track index"),
395
- fxIndex: z6.number().int().min(0).describe("Zero-based FX index in the chain"),
394
+ trackIndex: z6.coerce.number().int().min(0).describe("Zero-based track index"),
395
+ fxIndex: z6.coerce.number().int().min(0).describe("Zero-based FX index in the chain"),
396
396
  presetName: z6.string().min(1).describe("Exact preset name to apply")
397
397
  },
398
398
  async ({ trackIndex, fxIndex, presetName }) => {
@@ -458,7 +458,7 @@ function registerRoutingTools(server) {
458
458
  "get_track_routing",
459
459
  "Get sends, receives, and parent/folder information for a track \u2014 useful for understanding bus structure",
460
460
  {
461
- trackIndex: z8.number().int().min(0).describe("Zero-based track index")
461
+ trackIndex: z8.coerce.number().int().min(0).describe("Zero-based track index")
462
462
  },
463
463
  async ({ trackIndex }) => {
464
464
  const res = await sendCommand("get_track_routing", { trackIndex });
@@ -477,7 +477,7 @@ function registerAnalysisTools(server) {
477
477
  "read_track_lufs",
478
478
  "Read ITU-R BS.1770 loudness data for a track. Auto-inserts the MCP LUFS Meter JSFX if not present. Returns integrated, short-term (3s), and momentary (400ms) LUFS plus true inter-sample peak levels. Audio must be playing to accumulate data.",
479
479
  {
480
- trackIndex: z9.number().int().min(0).describe("Zero-based track index")
480
+ trackIndex: z9.coerce.number().int().min(0).describe("Zero-based track index")
481
481
  },
482
482
  async ({ trackIndex }) => {
483
483
  const res = await sendCommand("read_track_lufs", { trackIndex });
@@ -491,7 +491,7 @@ function registerAnalysisTools(server) {
491
491
  "read_track_correlation",
492
492
  "Read stereo field correlation and M/S analysis for a track. Auto-inserts the MCP Correlation Meter JSFX if not present. Returns correlation coefficient (-1 to +1), stereo width, and mid/side levels. Audio must be playing to accumulate data.",
493
493
  {
494
- trackIndex: z9.number().int().min(0).describe("Zero-based track index")
494
+ trackIndex: z9.coerce.number().int().min(0).describe("Zero-based track index")
495
495
  },
496
496
  async ({ trackIndex }) => {
497
497
  const res = await sendCommand("read_track_correlation", { trackIndex });
@@ -505,7 +505,7 @@ function registerAnalysisTools(server) {
505
505
  "read_track_crest",
506
506
  "Read crest factor (peak-to-RMS ratio) for a track. Auto-inserts the MCP Crest Factor Meter JSFX if not present. Returns crest factor in dB (higher = more dynamic, lower = over-compressed), peak hold level, and RMS level. Audio must be playing to accumulate data.",
507
507
  {
508
- trackIndex: z9.number().int().min(0).describe("Zero-based track index")
508
+ trackIndex: z9.coerce.number().int().min(0).describe("Zero-based track index")
509
509
  },
510
510
  async ({ trackIndex }) => {
511
511
  const res = await sendCommand("read_track_crest", { trackIndex });
@@ -659,6 +659,14 @@ async function installSkills() {
659
659
  } else {
660
660
  console.log("Claude skills not found in package. Skipping.");
661
661
  }
662
+ const agentsSrc = resolveAssetDir(__dirname, "claude-agents");
663
+ const agentsDir = join3(targetDir, ".claude", "agents");
664
+ if (existsSync2(agentsSrc)) {
665
+ const count = copyDirSync(agentsSrc, agentsDir);
666
+ console.log(`Installed Claude agents: ${count} files \u2192 ${agentsDir}`);
667
+ } else {
668
+ console.log("Claude agents not found in package. Skipping.");
669
+ }
662
670
  const mcpJsonPath = join3(targetDir, ".mcp.json");
663
671
  if (createMcpJson(mcpJsonPath)) {
664
672
  console.log(`
@@ -667,8 +675,9 @@ Created: ${mcpJsonPath}`);
667
675
  console.log(`
668
676
  .mcp.json already exists \u2014 add the reaper server config manually if needed.`);
669
677
  }
670
- console.log("\nDone! Claude Code now has mix engineer knowledge and REAPER MCP tools.");
671
- console.log('Try asking: "Please gain stage my tracks" or "Roast my mix"');
678
+ console.log("\nDone! Claude Code now has mix engineer agents, knowledge, and REAPER MCP tools.");
679
+ console.log('Try: @mix-engineer "Please gain stage my tracks"');
680
+ console.log('Or: @mix-analyzer "Roast my mix"');
672
681
  }
673
682
  async function doctor() {
674
683
  console.log("REAPER MCP \u2014 System Check\n");
@@ -677,6 +686,11 @@ async function doctor() {
677
686
  if (!bridgeRunning) {
678
687
  console.log(' \u2192 Run "reaper-mcp setup" then load mcp_bridge.lua in REAPER');
679
688
  }
689
+ const agentsExist = existsSync2(join3(process.cwd(), ".claude", "agents"));
690
+ console.log(`Mix agents: ${agentsExist ? "\u2713 Found (.claude/agents/)" : "\u2717 Not installed"}`);
691
+ if (!agentsExist) {
692
+ console.log(' \u2192 Run "reaper-mcp install-skills" in your project directory');
693
+ }
680
694
  const knowledgeExists = existsSync2(join3(process.cwd(), "knowledge"));
681
695
  console.log(`Knowledge base: ${knowledgeExists ? "\u2713 Found in project" : "\u2717 Not installed"}`);
682
696
  if (!knowledgeExists) {
@@ -694,6 +708,7 @@ async function doctor() {
694
708
  async function serve() {
695
709
  const log = (...args) => console.error("[reaper-mcp]", ...args);
696
710
  log("Starting REAPER MCP Server...");
711
+ log(`Entry: ${fileURLToPath(import.meta.url)}`);
697
712
  await ensureBridgeDir();
698
713
  const cleaned = await cleanupStaleFiles();
699
714
  if (cleaned > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mthines/reaper-mcp",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "description": "MCP server for controlling REAPER DAW — real-time mixing, FX control, and frequency analysis for AI agents",
6
6
  "license": "MIT",
@@ -39,6 +39,7 @@
39
39
  "knowledge/**",
40
40
  "claude-rules/**",
41
41
  "claude-skills/**",
42
+ "claude-agents/**",
42
43
  "README.md",
43
44
  "LICENSE"
44
45
  ],
@@ -932,29 +932,30 @@ function handlers.read_track_lufs(params)
932
932
  local fx_idx, err = ensure_jsfx_on_track(track, MCP_LUFS_METER_FX_NAME)
933
933
  if not fx_idx then return nil, err end
934
934
 
935
- -- Attach to the LUFS meter gmem namespace and read 6 values
936
- reaper.gmem_attach("MCPLufsMeter")
935
+ -- Set the track_slot parameter (slider2, param index 1) so this instance
936
+ -- writes to a unique gmem offset and doesn't collide with other tracks
937
+ reaper.TrackFX_SetParam(track, fx_idx, 1, idx / 127)
937
938
 
938
- local integrated = reaper.gmem_read(0)
939
- local short_term = reaper.gmem_read(1)
940
- local momentary = reaper.gmem_read(2)
941
- local true_peak_l = reaper.gmem_read(3)
942
- local true_peak_r = reaper.gmem_read(4)
943
- local duration = reaper.gmem_read(5)
939
+ -- Attach to the LUFS meter gmem namespace and read from track-specific offset
940
+ reaper.gmem_attach("MCPLufsMeter")
944
941
 
945
- -- A duration of 0 means no audio has been processed yet
946
- if duration <= 0 then
947
- return nil, "LUFS meter not producing data yet. Ensure audio is playing."
948
- end
942
+ local base = idx * 8
943
+ local integrated = reaper.gmem_read(base + 0)
944
+ local short_term = reaper.gmem_read(base + 1)
945
+ local momentary = reaper.gmem_read(base + 2)
946
+ local true_peak_l = reaper.gmem_read(base + 3)
947
+ local true_peak_r = reaper.gmem_read(base + 4)
948
+ local duration = reaper.gmem_read(base + 5)
949
949
 
950
950
  return {
951
- trackIndex = idx,
952
- integrated = integrated,
953
- shortTerm = short_term,
954
- momentary = momentary,
955
- truePeakL = true_peak_l,
956
- truePeakR = true_peak_r,
957
- duration = duration,
951
+ trackIndex = idx,
952
+ integrated = integrated,
953
+ shortTerm = short_term,
954
+ momentary = momentary,
955
+ truePeakL = true_peak_l,
956
+ truePeakR = true_peak_r,
957
+ duration = duration,
958
+ measuring = duration > 0,
958
959
  }
959
960
  end
960
961
 
@@ -3,17 +3,19 @@ desc:MCP LUFS Meter
3
3
  // Measures integrated, short-term, and momentary LUFS with true peak detection.
4
4
  // Writes results to shared memory (gmem) so the Lua bridge can read them.
5
5
  //
6
- // gmem layout:
7
- // gmem[0] = integrated LUFS (running average since last reset)
8
- // gmem[1] = short-term LUFS (3-second sliding window)
9
- // gmem[2] = momentary LUFS (400ms sliding window)
10
- // gmem[3] = true peak L (dBTP, inter-sample peak via 4x oversampling)
11
- // gmem[4] = true peak R (dBTP, inter-sample peak via 4x oversampling)
12
- // gmem[5] = measurement duration (seconds since last reset)
6
+ // gmem layout (per track slot, 8 slots each):
7
+ // base = track_slot * 8
8
+ // gmem[base+0] = integrated LUFS (running average since last reset)
9
+ // gmem[base+1] = short-term LUFS (3-second sliding window)
10
+ // gmem[base+2] = momentary LUFS (400ms sliding window)
11
+ // gmem[base+3] = true peak L (dBTP, inter-sample peak via 4x oversampling)
12
+ // gmem[base+4] = true peak R (dBTP, inter-sample peak via 4x oversampling)
13
+ // gmem[base+5] = measurement duration (seconds since last reset)
13
14
 
14
15
  options:gmem=MCPLufsMeter
15
16
 
16
17
  slider1:reset_btn=0<0,1,1{Idle,Reset}>Reset Measurement
18
+ slider2:track_slot=0<0,127,1>Track Slot
17
19
 
18
20
  @init
19
21
  // K-weighting filter state (two biquad stages per channel)
@@ -91,6 +93,8 @@ slider1:reset_btn=0<0,1,1{Idle,Reset}>Reset Measurement
91
93
  );
92
94
 
93
95
  function reset_measurement() (
96
+ local(base);
97
+ base = floor(slider2 + 0.5) * 8;
94
98
  buf_pos = 0;
95
99
  momentary_sum_l = 0;
96
100
  momentary_sum_r = 0;
@@ -108,12 +112,12 @@ slider1:reset_btn=0<0,1,1{Idle,Reset}>Reset Measurement
108
112
  // Clear ring buffers
109
113
  memset(buf_l, 0, buf_size);
110
114
  memset(buf_r, 0, buf_size);
111
- gmem[0] = -70;
112
- gmem[1] = -70;
113
- gmem[2] = -70;
114
- gmem[3] = -150;
115
- gmem[4] = -150;
116
- gmem[5] = 0;
115
+ gmem[base+0] = -70;
116
+ gmem[base+1] = -70;
117
+ gmem[base+2] = -70;
118
+ gmem[base+3] = -150;
119
+ gmem[base+4] = -150;
120
+ gmem[base+5] = 0;
117
121
  );
118
122
 
119
123
  @slider
@@ -240,7 +244,9 @@ slider1:reset_btn=0<0,1,1{Idle,Reset}>Reset Measurement
240
244
  // --- Update gmem every 4096 samples (approx 10x/second at 44.1kHz) ---
241
245
  (sample_count % 4096) == 0 ? (
242
246
  local(shortterm_mean, momentary_mean, integrated_lufs, shortterm_lufs, momentary_lufs);
243
- local(mom_samples);
247
+ local(mom_samples, base);
248
+
249
+ base = floor(slider2 + 0.5) * 8;
244
250
 
245
251
  mom_samples = floor(srate * 0.4);
246
252
  mom_samples < 1 ? mom_samples = 1;
@@ -279,23 +285,23 @@ slider1:reset_btn=0<0,1,1{Idle,Reset}>Reset Measurement
279
285
  integrated_lufs = -70;
280
286
  );
281
287
 
282
- gmem[0] = integrated_lufs;
283
- gmem[1] = shortterm_lufs;
284
- gmem[2] = momentary_lufs;
288
+ gmem[base+0] = integrated_lufs;
289
+ gmem[base+1] = shortterm_lufs;
290
+ gmem[base+2] = momentary_lufs;
285
291
 
286
292
  true_peak_l > 0.000001 ? (
287
- gmem[3] = 20 * log10(true_peak_l);
293
+ gmem[base+3] = 20 * log10(true_peak_l);
288
294
  ) : (
289
- gmem[3] = -150;
295
+ gmem[base+3] = -150;
290
296
  );
291
297
 
292
298
  true_peak_r > 0.000001 ? (
293
- gmem[4] = 20 * log10(true_peak_r);
299
+ gmem[base+4] = 20 * log10(true_peak_r);
294
300
  ) : (
295
- gmem[4] = -150;
301
+ gmem[base+4] = -150;
296
302
  );
297
303
 
298
- gmem[5] = sample_count / srate;
304
+ gmem[base+5] = sample_count / srate;
299
305
  );
300
306
 
301
307
  // Pass audio through unmodified