livepilot 1.5.0 → 1.6.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.
@@ -0,0 +1,272 @@
1
+ # Automation Atlas — LivePilot v1.6
2
+
3
+ Complete knowledge corpus for musically intelligent automation. Load this reference when working with automation tools.
4
+
5
+ ---
6
+
7
+ ## 1. Curve Theory
8
+
9
+ ### Why exponential for filters
10
+ Filter frequency perception is logarithmic (octaves). 100Hz → 200Hz is the same perceptual distance as 1kHz → 2kHz. An exponential curve in the normalized 0–1 range maps to perceptually even movement across the frequency spectrum. Linear automation on a filter sounds like it's rushing through the highs and crawling through the lows. Use `exponential` with `factor` 2.0–3.0 for filter cutoff.
11
+
12
+ ### Why logarithmic for volume
13
+ Human loudness perception follows a logarithmic curve (Weber-Fechner law). -6dB = half perceived loudness, not half amplitude. A logarithmic fade-in sounds smooth; a linear fade-in sounds like nothing happens then suddenly gets loud. Use `logarithmic` with `factor` 2.5–3.5 for volume fades.
14
+
15
+ ### Why S-curve for crossfades
16
+ Natural acceleration/deceleration. Avoids the "hole in the middle" of linear crossfades where combined energy dips. The smoothstep function (3t² - 2t³) ensures the rate of change is zero at both endpoints, so the transition feels organic. Use `s_curve` for any A→B crossfade.
17
+
18
+ ### Why spike for throws
19
+ Dub production technique — instant send level spike creates a single reflection that decays naturally through the reverb/delay tail. Any other shape creates unnatural sustained reflections. The exponential decay (peak × e^(-decay×t)) models how real acoustic energy dissipates.
20
+
21
+ ### Pan is linear
22
+ Stereo position perception is roughly linear. A pan pot moving at constant speed sounds even. No need for perceptual correction on pan automation.
23
+
24
+ ### Resonance is dangerous
25
+ Filter resonance is non-linear — subtle changes at low values, dramatic and potentially destructive at high values. Self-oscillation begins above ~0.85 on most filters. Never automate resonance above 0.85 without explicit user intent. Use `breathing` recipe with reduced amplitude (0.05–0.10) for subtle resonance movement.
26
+
27
+ ---
28
+
29
+ ## 2. The Perception-Action Loop (5 levels)
30
+
31
+ **MANDATORY for all automation work.** Never write automation blind — always perceive first.
32
+
33
+ ### Level 1: Reactive
34
+ Single read, single action. The simplest loop.
35
+ 1. `get_master_spectrum` → read frequency content
36
+ 2. Identify one issue (e.g., "mud below 200Hz")
37
+ 3. Apply one automation (e.g., HP filter sweep)
38
+ 4. `get_master_spectrum` → verify improvement
39
+
40
+ ### Level 2: Diagnostic
41
+ Multi-step investigation using EQ as a microscope. See Section 3 (Diagnostic Filter Technique).
42
+
43
+ ### Level 3: Verification
44
+ Act → measure → adjust cycle. For every automation written:
45
+ 1. Read spectrum BEFORE
46
+ 2. Write automation
47
+ 3. Read spectrum AFTER
48
+ 4. Compare: did the problem frequency range improve?
49
+ 5. If not, `clear_clip_automation` → adjust parameters → try again
50
+ 6. Log the successful parameters in memory for reuse
51
+
52
+ ### Level 4: Cross-Track
53
+ Solo each track, build spectral map, write complementary automation.
54
+ 1. For each track: solo → `get_master_spectrum` → record fingerprint
55
+ 2. Find frequency overlaps between tracks (masking)
56
+ 3. Write complementary automation: as kick's filter opens, bass's filter narrows
57
+ 4. Verify no new masking was introduced
58
+ 5. Store the cross-track map in memory
59
+
60
+ ### Level 5: Full Pipeline
61
+ Per-track analysis across entire session.
62
+ 1. Run Level 4 for all tracks
63
+ 2. Build session-wide spectral map
64
+ 3. Write automation considering all interactions
65
+ 4. Verify global mix spectrum
66
+ 5. Iterate until balanced
67
+
68
+ ---
69
+
70
+ ## 3. Diagnostic Filter Technique
71
+
72
+ Using EQ Eight as a measurement instrument. Step-by-step:
73
+
74
+ 1. **Load EQ Eight** on target track via `find_and_load_device`
75
+ 2. **Configure band 1** as narrow bandpass: Q=8, gain=0dB, frequency=100Hz
76
+ - Use `set_device_parameter` to set Filter Type to Band Pass, Q to 8.0
77
+ 3. **Solo the track** via `set_track_solo`
78
+ 4. **Read spectrum** at 100Hz: `get_master_spectrum` → note the `sub` value
79
+ 5. **Sweep frequency** through key ranges:
80
+ - 100Hz, 200Hz, 300Hz, 500Hz, 1kHz, 2kHz, 5kHz, 10kHz
81
+ - At each: `set_device_parameter` (frequency) → `get_master_spectrum` → record
82
+ 6. **Build frequency map**: `{100Hz: 0.18, 200Hz: 0.25, 300Hz: 0.09, ...}`
83
+ 7. **Identify problems**: resonance buildup, mud, harshness, dead zones
84
+ 8. **Remove diagnostic EQ** — always clean up: `delete_device`
85
+ 9. **Un-solo the track**: `set_track_solo` (toggle off)
86
+ 10. **Write targeted automation** addressing what was found
87
+
88
+ ### What to look for:
89
+ - **Mud** (200-400Hz): values > 0.20 suggest buildup → HP filter automation
90
+ - **Resonance** (narrow peak in 300-800Hz): ringing → notch filter or gentle sweep
91
+ - **Harshness** (2-5kHz): values > 0.25 suggest brightness → LP filter or shelf automation
92
+ - **Dead zone** (any range with 0.00): frequency hole → boost or different device
93
+
94
+ ---
95
+
96
+ ## 4. Genre Recipes
97
+
98
+ ### Techno
99
+ - `filter_sweep_up` on LP cutoff, 32 bars, factor 2.0 — the classic build
100
+ - `sidechain_pump` on pad volume via Utility gain, 1 beat loop
101
+ - `stutter` on vocals before drop, 0.5 beats, frequency 16
102
+ - `stereo_narrow` on master bus, 8 bars before drop → instant widen at drop
103
+ - Long transitions: combine HP filter + reverb send + stereo width, all exponential
104
+
105
+ ### Dub
106
+ - `dub_throw` on Send A at each snare position, 1 beat duration
107
+ - `tape_stop` on clip transpose for transitions, 0.5 beats
108
+ - `washout` on delay feedback, 4 bars → cut at phrase boundary
109
+ - `breathing` on filter cutoff, 4 bars — everything breathes in dub
110
+ - Tip: never automate delay feedback above 0.85 (infinite feedback risk)
111
+
112
+ ### Ambient
113
+ - `breathing` on filter cutoff, 4 bars, amplitude 0.10 — subtle is key
114
+ - `perlin` noise on reverb mix, 8 bars, amplitude 0.15, seed varies
115
+ - `brownian` drift on wavetable position, 16 bars, volatility 0.05
116
+ - Polyrhythmic automation: 3-beat filter + 5-beat reverb + 7-beat pan = 105-beat cycle
117
+ - Less is more. Ambient automation should be felt, not heard.
118
+
119
+ ### Hip Hop
120
+ - `tape_stop` on clip transpose, 0.5 beats — classic vinyl stop
121
+ - `vinyl_crackle` on Redux bit depth, 16 bars — lo-fi character
122
+ - `sidechain_pump` on bass under kick, 0.5 beat — tight pocket
123
+ - `fade_out` on filter for transitions between sections
124
+ - Keep automation sparse — hip hop is about groove, not modulation
125
+
126
+ ### IDM / Experimental
127
+ - `stutter` on volume, 0.5 beats, frequency 16 — micro-editing
128
+ - `euclidean` on filter cutoff: 5 hits across 8 steps — rhythmic intelligence
129
+ - `stochastic` on grain parameters, narrowing 0.7 — controlled chaos
130
+ - `spring` on filter cutoff for overshoot character
131
+ - Layer multiple polyrhythmic automations for generative evolution
132
+
133
+ ---
134
+
135
+ ## 5. Sound Design Automation
136
+
137
+ ### Wavetable position
138
+ Exponential sweep for timbral morphing over 8–16 bars. The timbral change is often perceptually logarithmic, so exponential automation creates even-sounding evolution.
139
+
140
+ ### Grain size
141
+ Sine modulation at 0.5–2Hz for alive textures. The slow oscillation prevents the grainular artifacts from sounding static. Use `breathing` recipe adapted: center 0.5, amplitude 0.15.
142
+
143
+ ### Reverb decay
144
+ Link inversely to volume: quieter passages get longer tails for natural space. As volume drops, reverb decay increases. Automate both with complementary curves.
145
+
146
+ ### Delay feedback
147
+ Spike for throws (dub technique). NEVER exceed 0.9 on feedback — infinite feedback creates dangerous signal buildup. Use `dub_throw` recipe with peak capped at 0.85.
148
+
149
+ ### Filter resonance
150
+ Subtle sine modulation creates vocal/crying quality. Watch for self-oscillation above 0.85. Use amplitude 0.05–0.10 for subtle character, 0.15–0.25 for expressive.
151
+
152
+ ---
153
+
154
+ ## 6. Arrangement Techniques
155
+
156
+ ### The Build (16–32 bars before drop)
157
+ Combine multiple automations, all exponential:
158
+ 1. HP filter: 0→0.6 (remove low end gradually)
159
+ 2. Volume: 0.7→1.0 (subtle lift)
160
+ 3. Reverb send: 0→0.5 (add wash)
161
+ 4. Stereo width: 1.0→0.0 (collapse to mono)
162
+ 5. At drop: instant step back to defaults
163
+
164
+ ### The Drop (instant)
165
+ Step automation restoring all build parameters to default values. No curves — immediate snap. Use `steps` curve with a single value at time 0.
166
+
167
+ ### The Strip (8–16 bars)
168
+ Gradual HP filter rise removing elements one by one:
169
+ 1. First: HP filter on bass track (removes bass)
170
+ 2. Then: HP filter on drums (removes kick)
171
+ 3. Then: HP filter on pads (removes mids)
172
+ 4. Only high frequencies remain → transition point
173
+
174
+ ### The Crossfade (2–8 bars)
175
+ S-curve volume automation between two sections:
176
+ - Outgoing track: s_curve from 1.0 to 0.0
177
+ - Incoming track: s_curve from 0.0 to 1.0
178
+ - S-curve prevents the energy dip of linear crossfades
179
+
180
+ ---
181
+
182
+ ## 7. Micro-Editing and Humanization
183
+
184
+ ### Velocity vs volume
185
+ Velocity is timbre — it changes how the sound is generated (harder hits, brighter tone). Volume automation is loudness — it changes amplitude after generation. They are different tools.
186
+
187
+ ### Per-note filter accent
188
+ Automate filter cutoff to spike on accented notes. Use `spike` with duration matching the note length. Creates timbral accents without touching velocity.
189
+
190
+ ### Spatial punctuation
191
+ Send spikes on specific hits create spatial depth variation. Snare → reverb throw on beat 2 and 4. Hi-hat → delay throw on off-beats. Use `dub_throw` recipe.
192
+
193
+ ### Humanization via Perlin noise
194
+ Tiny pitch/filter variations make programmed music feel human:
195
+ - `perlin` on detune: amplitude 0.02, frequency 0.5
196
+ - `perlin` on filter cutoff: amplitude 0.05, frequency 0.3
197
+ - Different seeds per track — each voice drifts independently
198
+
199
+ ---
200
+
201
+ ## 8. Polyrhythmic Automation
202
+
203
+ ### The concept
204
+ Unlinked automation envelopes with different loop lengths create long, non-repeating cycles:
205
+ - 4-beat clip loop + 3-beat filter envelope + 5-beat reverb envelope
206
+ - Total cycle: LCM(4, 3, 5) = 60 beats before exact repetition
207
+ - 60 beats ≈ 15 bars at 4/4 — feels evolving, never static
208
+
209
+ ### Prime number rule
210
+ Use prime-number beat lengths for maximum non-repetition:
211
+ - 3, 5, 7, 11, 13 beats
212
+ - LCM of primes = their product (all coprime)
213
+ - 3 × 5 × 7 = 105 beats ≈ 26 bars before exact repeat
214
+
215
+ ### Implementation
216
+ 1. Create clip with N-beat loop (your base rhythm)
217
+ 2. `apply_automation_shape` with `duration` = M beats (M ≠ N, preferably prime)
218
+ 3. The automation loops at M beats, the clip loops at N beats
219
+ 4. Add another automation with duration = P beats (another prime)
220
+ 5. Total cycle = LCM(N, M, P) beats
221
+
222
+ ### Use cases
223
+ Essential for ambient, installation, generative work. Creates the sense of "always evolving" that distinguishes produced music from loops.
224
+
225
+ ---
226
+
227
+ ## 9. Cross-Track Spectral Mapping
228
+
229
+ ### Process
230
+ 1. **Solo each track** one at a time
231
+ 2. **Read spectrum**: `get_master_spectrum` → record {sub, low, mid, high, presence, air}
232
+ 3. **Build spectral fingerprint** for each track:
233
+ ```
234
+ Kick: sub=0.45, low=0.30, mid=0.05, high=0.02
235
+ Bass: sub=0.35, low=0.40, mid=0.15, high=0.03
236
+ Pad: sub=0.05, low=0.15, mid=0.35, high=0.25
237
+ ```
238
+ 4. **Find overlaps**: kick and bass both strong in sub/low = masking
239
+ 5. **Write complementary automation**: as kick opens, bass narrows
240
+ 6. **Verify**: un-solo all, check master spectrum for improvement
241
+
242
+ ### Complementary automation patterns
243
+ - **Kick + Bass**: sidechain pump on bass volume, or complementary filter sweeps
244
+ - **Vocals + Pads**: automate pad filter to duck in vocal frequency range (1–4kHz)
245
+ - **Multiple synths**: different breathing rates (frequency 0.3, 0.5, 0.7) to weave in/out
246
+
247
+ ### Store findings
248
+ Use `memory_learn` to save spectral maps and successful automation combinations. Build a knowledge base about the session's sounds for consistent decisions.
249
+
250
+ ---
251
+
252
+ ## 10. Golden Rules
253
+
254
+ 1. **Always use Utility gain for volume automation** — preserve the mixer fader for mixing. Automate the Utility device's Gain parameter, not track volume.
255
+
256
+ 2. **Exponential for filters, logarithmic for volume** — never linear for either. This is perceptual science, not preference.
257
+
258
+ 3. **Subtle automation (5–15% range) for organic feel; dramatic (full range) for transitions.** Most automation should be felt, not heard. Save the big sweeps for builds and drops.
259
+
260
+ 4. **ALWAYS verify with `get_master_spectrum` after writing automation.** The feedback loop is mandatory, not optional.
261
+
262
+ 5. **Use `clear_clip_automation` before rewriting.** Don't stack conflicting curves — clear first, then write fresh.
263
+
264
+ 6. **Use the diagnostic filter technique before guessing at problem frequencies.** Measurement beats intuition.
265
+
266
+ 7. **Store spectral findings in memory.** Build a knowledge base about this session's sounds. Cross-session awareness makes every future decision better.
267
+
268
+ 8. **Delay feedback NEVER above 0.9.** Infinite feedback creates dangerous signal buildup. Cap at 0.85 for safety, even in experimental contexts.
269
+
270
+ 9. **density 16–32 for most curves.** 16 points per bar gives smooth results for gentle curves. 32 for fast modulations or detailed shapes. Above 64 is rarely worth the overhead.
271
+
272
+ 10. **When in doubt, use a recipe.** The 15 built-in recipes encode production wisdom. Start with the recipe, then customize if needed.
@@ -1,6 +1,6 @@
1
- # LivePilot v1.5.0 — Architecture & Tool Reference
1
+ # LivePilot v1.6.0 — Architecture & Tool Reference
2
2
 
3
- LivePilot is an agentic production system for Ableton Live 12. It combines 127 MCP tools with a device knowledge corpus, real-time audio analysis, and persistent technique memory.
3
+ LivePilot is an agentic production system for Ableton Live 12. It combines 135 MCP tools with a device knowledge corpus, real-time audio analysis, automation intelligence, and persistent technique memory.
4
4
 
5
5
  ## Architecture
6
6
 
@@ -32,7 +32,7 @@ A flat tool list lets the AI press buttons. LivePilot's three layers give it con
32
32
 
33
33
  This turns "set EQ band 3 to -4 dB" into "cut 400 Hz by 4 dB, then read the spectrum to confirm the mud is actually reduced."
34
34
 
35
- ## The 127 Tools — What Each One Does
35
+ ## The 135 Tools — What Each One Does
36
36
 
37
37
  ### Transport (12) — Playback, tempo, global state, diagnostics
38
38
 
@@ -227,6 +227,23 @@ This turns "set EQ band 3 to -4 dB" into "cut 400 Hz by 4 dB, then read the spec
227
227
  | `stop_scrub` | Stop preview | `track_index`, `clip_index` |
228
228
  | `get_display_values` | Human-readable parameter values ("440 Hz", "-6 dB") | `track_index`, `device_index` |
229
229
 
230
+ ### Automation (8) — Clip automation CRUD + intelligent curve generation
231
+
232
+ | Tool | What it does | Key params |
233
+ |------|-------------|------------|
234
+ | `get_clip_automation` | Lists all automation envelopes on a session clip | `track_index`, `clip_index` |
235
+ | `set_clip_automation` | Writes automation points to a clip envelope | `track_index`, `clip_index`, `parameter_type`, `points` |
236
+ | `clear_clip_automation` | Clears automation envelopes (specific or all) | `track_index`, `clip_index`, `parameter_type` (optional) |
237
+ | `apply_automation_shape` | Generates and applies a curve to a clip in one call | `track_index`, `clip_index`, `parameter_type`, `curve_type`, `duration`, `density` |
238
+ | `apply_automation_recipe` | Applies a named recipe (filter_sweep_up, dub_throw, etc.) | `track_index`, `clip_index`, `parameter_type`, `recipe`, `duration` |
239
+ | `get_automation_recipes` | Lists all 15 recipes with descriptions and targets | — |
240
+ | `generate_automation_curve` | Previews curve points without writing them | `curve_type`, `duration`, `density`, curve-specific params |
241
+ | `analyze_for_automation` | Spectral analysis + device-aware automation suggestions | `track_index` |
242
+
243
+ **16 curve types:** linear, exponential, logarithmic, s_curve, sine, sawtooth, spike, square, steps, perlin, brownian, spring, bezier, easing, euclidean, stochastic
244
+
245
+ **15 recipes:** filter_sweep_up, filter_sweep_down, dub_throw, tape_stop, build_rise, sidechain_pump, fade_in, fade_out, tremolo, auto_pan, stutter, breathing, washout, vinyl_crackle, stereo_narrow
246
+
230
247
  ## Units & Ranges Quick Reference
231
248
 
232
249
  | Concept | Unit/Range | Notes |
@@ -0,0 +1,101 @@
1
+ ---
2
+ name: livepilot-release
3
+ description: Release checklist for LivePilot — run before ANY push, publish, or "update everything" request. Covers every file, channel, and artifact that references version numbers, tool counts, or project state.
4
+ ---
5
+
6
+ # LivePilot Release Checklist
7
+
8
+ Run this checklist EVERY time the user says "update everything", "push", "release", "make sure everything is current", or similar.
9
+
10
+ ## 1. Version Strings (must ALL match)
11
+
12
+ - [ ] `package.json` → `"version"`
13
+ - [ ] `package-lock.json` → `"version"` (run `npm install --package-lock-only` if stale)
14
+ - [ ] `server.json` → `"version"` (TWO locations: top-level and package)
15
+ - [ ] `plugin/plugin.json` → `"version"`
16
+ - [ ] `mcp_server/__init__.py` → `__version__`
17
+ - [ ] `remote_script/LivePilot/__init__.py` → version in log message
18
+ - [ ] `m4l_device/livepilot_bridge.js` → version in ping response
19
+ - [ ] `CHANGELOG.md` → latest version header
20
+ - [ ] `docs/social-banner.html` → version display
21
+
22
+ **How to check:** `grep -rn "1\.[0-9]\.[0-9]" package.json server.json plugin/plugin.json mcp_server/__init__.py remote_script/LivePilot/__init__.py m4l_device/livepilot_bridge.js CHANGELOG.md docs/social-banner.html`
23
+
24
+ ## 2. Tool Count (must ALL match)
25
+
26
+ - [ ] `README.md` — every occurrence
27
+ - [ ] `package.json` → `"description"`
28
+ - [ ] `server.json` → `"description"`
29
+ - [ ] `plugin/plugin.json` → `"description"`
30
+ - [ ] `CLAUDE.md`
31
+ - [ ] `plugin/skills/livepilot-core/SKILL.md` — header and domain breakdown
32
+ - [ ] `plugin/skills/livepilot-core/references/overview.md`
33
+ - [ ] `docs/manual/index.md`
34
+ - [ ] `docs/manual/tool-reference.md`
35
+ - [ ] `docs/TOOL_REFERENCE.md`
36
+ - [ ] `docs/M4L_BRIDGE.md`
37
+ - [ ] `docs/social-banner.html`
38
+ - [ ] `mcp_server/tools/analyzer.py` → module docstring
39
+ - [ ] `tests/test_tools_contract.py` → expected count
40
+
41
+ **How to check:** `grep -rn "127\|104\|113" --include="*.md" --include="*.json" --include="*.py" --include="*.html" --include="*.js" . | grep -v node_modules | grep -v .git | grep -v __pycache__`
42
+
43
+ ## 3. Domain Count
44
+
45
+ - [ ] All files above that mention "10 domains" should say "11 domains"
46
+ - [ ] Domain lists should include: transport, tracks, clips, notes, devices, scenes, mixing, browser, arrangement, memory, analyzer
47
+
48
+ ## 4. npm Registry
49
+
50
+ - [ ] `npm view livepilot version` matches local version
51
+ - [ ] If not: `npm publish`
52
+ - [ ] Verify badge will update: badge URL in README.md points to shields.io/npm/v/livepilot
53
+
54
+ ## 5. GitHub
55
+
56
+ - [ ] Repo description matches current tool count and features
57
+ - [ ] Topics are current (should include: ai, mcp, ableton, livepilot, max-for-live, audio-analysis)
58
+ - [ ] Latest release matches current version (`gh release list`)
59
+ - [ ] Release notes are current
60
+ - [ ] Old stale releases cleaned up
61
+ - [ ] Git tags: only relevant versions exist (`git tag -l`)
62
+ - [ ] No co-author or unwanted metadata in commit messages
63
+
64
+ ## 6. Plugin Cache
65
+
66
+ - [ ] `~/.claude/plugins/cache/dreamrec-LivePilot/livepilot/` has current version directory
67
+ - [ ] Old version directories removed
68
+
69
+ ## 7. Social/Promotional Assets
70
+
71
+ - [ ] `docs/social-banner.html` — tool count, version, domain list
72
+ - [ ] `docs/social-banner.png` — regenerated from HTML (must match)
73
+ - [ ] GitHub repo social preview image (Settings > Social preview)
74
+
75
+ ## 8. Documentation Content
76
+
77
+ - [ ] `README.md` — features match current capabilities
78
+ - [ ] `docs/manual/getting-started.md` — install instructions current, mentions M4L Analyzer
79
+ - [ ] `docs/manual/tool-reference.md` — all tools listed
80
+ - [ ] `docs/M4L_BRIDGE.md` — architecture accurate
81
+ - [ ] `docs/TOOL_REFERENCE.md` — all tools listed with correct params
82
+
83
+ ## 9. Derived Artifacts
84
+
85
+ - [ ] `m4l_device/LivePilot_Analyzer.amxd` — frozen JS matches source? All commands present?
86
+ - [ ] Distributable zip on Desktop — rebuilt with latest?
87
+ - [ ] Private backup repo — synced and pushed?
88
+ - [ ] `LivePilot-v*.INSTALL.txt` — updated?
89
+
90
+ ## 10. Code Consistency
91
+
92
+ - [ ] `@mcp.tool()` count matches documented tool count: `grep -r "@mcp.tool" mcp_server/tools/ | wc -l`
93
+ - [ ] No dead imports or unused code in recently changed files
94
+ - [ ] Remote script version matches MCP server version
95
+
96
+ ## Quick Verify Command
97
+
98
+ Run this one-liner to catch most issues:
99
+ ```bash
100
+ echo "=== Versions ===" && grep -h '"version"' package.json server.json plugin/plugin.json | head -5 && grep __version__ mcp_server/__init__.py && echo "=== Tool count ===" && grep -rc "@mcp.tool" mcp_server/tools/ | tail -1 && echo "=== npm ===" && npm view livepilot version 2>/dev/null && echo "=== GitHub release ===" && gh release list --limit 1 && echo "=== Tags ===" && git tag -l
101
+ ```
@@ -16,7 +16,8 @@ from . import scenes # noqa: F401 — registers scene handlers
16
16
  from . import mixing # noqa: F401 — registers mixing handlers
17
17
  from . import browser # noqa: F401 — registers browser handlers
18
18
  from . import arrangement # noqa: F401 — registers arrangement handlers
19
- from . import diagnostics # noqa: F401 — registers diagnostics handler
19
+ from . import diagnostics # noqa: F401 — registers diagnostics handler
20
+ from . import clip_automation # noqa: F401 — registers clip automation handlers
20
21
 
21
22
 
22
23
  def create_instance(c_instance):
@@ -31,7 +32,7 @@ class LivePilot(ControlSurface):
31
32
  ControlSurface.__init__(self, c_instance)
32
33
  self._server = LivePilotServer(self)
33
34
  self._server.start()
34
- self.log_message("LivePilot v1.5.0 initialized")
35
+ self.log_message("LivePilot v1.6.1 initialized")
35
36
  self.show_message("LivePilot: Listening on port 9878")
36
37
 
37
38
  def disconnect(self):
@@ -0,0 +1,220 @@
1
+ """Clip automation envelope handlers.
2
+
3
+ Provides CRUD access to session clip automation envelopes.
4
+ Uses the same LOM API as arrangement automation (AutomationEnvelope)
5
+ but targets session clips via track.clip_slots[i].clip.
6
+ """
7
+
8
+ from .router import register
9
+ from .utils import get_track
10
+
11
+
12
+ @register("get_clip_automation")
13
+ def get_clip_automation(song, params):
14
+ """List automation envelopes on a session clip."""
15
+ track_index = params["track_index"]
16
+ clip_index = params["clip_index"]
17
+
18
+ track = get_track(song, track_index)
19
+ clip_slot = list(track.clip_slots)[clip_index]
20
+ if not clip_slot.has_clip:
21
+ return {"error": {"code": "NOT_FOUND",
22
+ "message": "No clip at slot %d" % clip_index}}
23
+
24
+ clip = clip_slot.clip
25
+ envelopes = []
26
+
27
+ # Check mixer parameters: volume, panning, sends
28
+ mixer = track.mixer_device
29
+ for param_name, param in [
30
+ ("Volume", mixer.volume),
31
+ ("Pan", mixer.panning),
32
+ ]:
33
+ env = clip.automation_envelope(param)
34
+ if env is not None:
35
+ envelopes.append({
36
+ "parameter_name": param_name,
37
+ "parameter_type": "mixer",
38
+ "has_envelope": True,
39
+ })
40
+
41
+ # Check send parameters
42
+ sends = list(mixer.sends)
43
+ for i, send in enumerate(sends):
44
+ env = clip.automation_envelope(send)
45
+ if env is not None:
46
+ envelopes.append({
47
+ "parameter_name": "Send %s" % chr(65 + i),
48
+ "parameter_type": "send",
49
+ "send_index": i,
50
+ "has_envelope": True,
51
+ })
52
+
53
+ # Check device parameters
54
+ devices = list(track.devices)
55
+ for di, device in enumerate(devices):
56
+ dev_params = list(device.parameters)
57
+ for pi, param in enumerate(dev_params):
58
+ try:
59
+ env = clip.automation_envelope(param)
60
+ if env is not None:
61
+ envelopes.append({
62
+ "parameter_name": param.name,
63
+ "parameter_type": "device",
64
+ "device_index": di,
65
+ "device_name": device.name,
66
+ "parameter_index": pi,
67
+ "has_envelope": True,
68
+ })
69
+ except Exception:
70
+ pass
71
+
72
+ return {
73
+ "track_index": track_index,
74
+ "clip_index": clip_index,
75
+ "clip_name": clip.name,
76
+ "envelope_count": len(envelopes),
77
+ "envelopes": envelopes,
78
+ }
79
+
80
+
81
+ @register("set_clip_automation")
82
+ def set_clip_automation(song, params):
83
+ """Write automation points to a session clip envelope.
84
+
85
+ parameter_type: "device", "volume", "panning", "send"
86
+ points: [{time, value, duration?}] — time relative to clip start
87
+ """
88
+ track_index = params["track_index"]
89
+ clip_index = params["clip_index"]
90
+ parameter_type = params["parameter_type"]
91
+ points = params["points"]
92
+ device_index = params.get("device_index")
93
+ parameter_index = params.get("parameter_index")
94
+ send_index = params.get("send_index")
95
+
96
+ track = get_track(song, track_index)
97
+ clip_slot = list(track.clip_slots)[clip_index]
98
+ if not clip_slot.has_clip:
99
+ return {"error": {"code": "NOT_FOUND",
100
+ "message": "No clip at slot %d" % clip_index}}
101
+
102
+ clip = clip_slot.clip
103
+
104
+ # Resolve the target parameter
105
+ if parameter_type == "volume":
106
+ parameter = track.mixer_device.volume
107
+ elif parameter_type == "panning":
108
+ parameter = track.mixer_device.panning
109
+ elif parameter_type == "send":
110
+ if send_index is None:
111
+ return {"error": {"code": "INVALID_PARAM",
112
+ "message": "send_index required for send automation"}}
113
+ sends = list(track.mixer_device.sends)
114
+ if send_index >= len(sends):
115
+ return {"error": {"code": "INDEX_ERROR",
116
+ "message": "send_index %d out of range" % send_index}}
117
+ parameter = sends[send_index]
118
+ elif parameter_type == "device":
119
+ if device_index is None or parameter_index is None:
120
+ return {"error": {"code": "INVALID_PARAM",
121
+ "message": "device_index and parameter_index required"}}
122
+ devices = list(track.devices)
123
+ if device_index >= len(devices):
124
+ return {"error": {"code": "INDEX_ERROR",
125
+ "message": "device_index %d out of range" % device_index}}
126
+ dev_params = list(devices[device_index].parameters)
127
+ if parameter_index >= len(dev_params):
128
+ return {"error": {"code": "INDEX_ERROR",
129
+ "message": "parameter_index %d out of range" % parameter_index}}
130
+ parameter = dev_params[parameter_index]
131
+ else:
132
+ return {"error": {"code": "INVALID_PARAM",
133
+ "message": "parameter_type must be device/volume/panning/send"}}
134
+
135
+ # Get or create envelope
136
+ song.begin_undo_step()
137
+ try:
138
+ envelope = clip.automation_envelope(parameter)
139
+ if envelope is None:
140
+ envelope = clip.create_automation_envelope(parameter)
141
+
142
+ # Write points
143
+ written = 0
144
+ for pt in points:
145
+ time = float(pt["time"])
146
+ value = float(pt["value"])
147
+ duration = float(pt.get("duration", 0.125))
148
+ # Clamp value to parameter range
149
+ value = max(parameter.min, min(parameter.max, value))
150
+ envelope.insert_step(time, duration, value)
151
+ written += 1
152
+ finally:
153
+ song.end_undo_step()
154
+
155
+ return {
156
+ "track_index": track_index,
157
+ "clip_index": clip_index,
158
+ "parameter_name": parameter.name,
159
+ "parameter_type": parameter_type,
160
+ "points_written": written,
161
+ }
162
+
163
+
164
+ @register("clear_clip_automation")
165
+ def clear_clip_automation(song, params):
166
+ """Clear automation envelopes from a session clip.
167
+
168
+ If parameter_type is provided, clears only that parameter's envelope.
169
+ If omitted, clears ALL envelopes on the clip.
170
+ """
171
+ track_index = params["track_index"]
172
+ clip_index = params["clip_index"]
173
+ parameter_type = params.get("parameter_type")
174
+
175
+ track = get_track(song, track_index)
176
+ clip_slot = list(track.clip_slots)[clip_index]
177
+ if not clip_slot.has_clip:
178
+ return {"error": {"code": "NOT_FOUND",
179
+ "message": "No clip at slot %d" % clip_index}}
180
+
181
+ clip = clip_slot.clip
182
+
183
+ song.begin_undo_step()
184
+ try:
185
+ if parameter_type is None:
186
+ # Clear all envelopes
187
+ clip.clear_all_envelopes()
188
+ return {
189
+ "track_index": track_index,
190
+ "clip_index": clip_index,
191
+ "cleared": "all",
192
+ }
193
+
194
+ # Clear specific parameter
195
+ if parameter_type == "volume":
196
+ parameter = track.mixer_device.volume
197
+ elif parameter_type == "panning":
198
+ parameter = track.mixer_device.panning
199
+ elif parameter_type == "send":
200
+ send_index = params.get("send_index", 0)
201
+ parameter = list(track.mixer_device.sends)[send_index]
202
+ elif parameter_type == "device":
203
+ device_index = params.get("device_index", 0)
204
+ parameter_index = params.get("parameter_index", 0)
205
+ device = list(track.devices)[device_index]
206
+ parameter = list(device.parameters)[parameter_index]
207
+ else:
208
+ return {"error": {"code": "INVALID_PARAM",
209
+ "message": "Unknown parameter_type"}}
210
+
211
+ clip.clear_envelope(parameter)
212
+ finally:
213
+ song.end_undo_step()
214
+
215
+ return {
216
+ "track_index": track_index,
217
+ "clip_index": clip_index,
218
+ "cleared": parameter_type,
219
+ "parameter_name": parameter.name,
220
+ }
@@ -52,6 +52,9 @@ WRITE_COMMANDS = frozenset([
52
52
  "modify_arrangement_notes", "duplicate_arrangement_notes",
53
53
  "transpose_arrangement_notes", "set_arrangement_automation",
54
54
  "set_arrangement_clip_name",
55
+ # clip automation
56
+ "set_clip_automation",
57
+ "clear_clip_automation",
55
58
  ])
56
59
 
57
60