livepilot 1.8.3 → 1.9.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/.claude-plugin/marketplace.json +3 -3
- package/AGENTS.md +46 -0
- package/CHANGELOG.md +41 -0
- package/README.md +26 -19
- package/bin/livepilot.js +4 -2
- package/livepilot/.claude-plugin/plugin.json +2 -2
- package/livepilot/skills/livepilot-core/SKILL.md +16 -8
- package/livepilot/skills/livepilot-core/references/overview.md +3 -3
- package/livepilot/skills/livepilot-release/SKILL.md +37 -29
- package/m4l_device/LivePilot_Analyzer.amxd +0 -0
- package/m4l_device/livepilot_bridge.js +170 -1
- package/mcp_server/__init__.py +1 -1
- package/mcp_server/m4l_bridge.py +32 -24
- package/mcp_server/memory/technique_store.py +2 -2
- package/mcp_server/server.py +16 -1
- package/mcp_server/tools/_perception_engine.py +3 -2
- package/mcp_server/tools/analyzer.py +8 -2
- package/mcp_server/tools/arrangement.py +12 -1
- package/mcp_server/tools/automation.py +4 -2
- package/mcp_server/tools/devices.py +95 -2
- package/mcp_server/tools/harmony.py +2 -2
- package/mcp_server/tools/midi_io.py +57 -22
- package/mcp_server/tools/notes.py +4 -0
- package/mcp_server/tools/scenes.py +65 -2
- package/mcp_server/tools/tracks.py +45 -2
- package/package.json +2 -2
- package/remote_script/LivePilot/__init__.py +2 -2
- package/remote_script/LivePilot/arrangement.py +42 -0
- package/remote_script/LivePilot/clip_automation.py +18 -30
- package/remote_script/LivePilot/scenes.py +110 -1
- package/remote_script/LivePilot/server.py +15 -2
- package/remote_script/LivePilot/tracks.py +73 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
LivePilot - Track domain handlers (
|
|
2
|
+
LivePilot - Track domain handlers (17 commands).
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
from .router import register
|
|
@@ -101,7 +101,8 @@ def get_track_info(song, params):
|
|
|
101
101
|
result["arm"] = None
|
|
102
102
|
result["has_midi_input"] = None
|
|
103
103
|
result["has_audio_input"] = None
|
|
104
|
-
result["is_return_track"] =
|
|
104
|
+
result["is_return_track"] = track_index != -1000
|
|
105
|
+
result["is_master_track"] = track_index == -1000
|
|
105
106
|
|
|
106
107
|
return result
|
|
107
108
|
|
|
@@ -266,3 +267,73 @@ def set_track_input_monitoring(song, params):
|
|
|
266
267
|
"index": track_index,
|
|
267
268
|
"monitoring_state": track.current_monitoring_state,
|
|
268
269
|
}
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
# ── Freeze / Flatten ────────────────────────────────────────────────────
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
@register("get_freeze_status")
|
|
276
|
+
def get_freeze_status(song, params):
|
|
277
|
+
"""Return freeze state for a track."""
|
|
278
|
+
track_index = int(params["track_index"])
|
|
279
|
+
track = get_track(song, track_index)
|
|
280
|
+
return {
|
|
281
|
+
"track_index": track_index,
|
|
282
|
+
"is_frozen": track.is_frozen,
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
@register("freeze_track")
|
|
287
|
+
def freeze_track(song, params):
|
|
288
|
+
"""Freeze a track — render all devices to audio for CPU savings.
|
|
289
|
+
|
|
290
|
+
Freeze is async in Ableton — this call initiates it and returns
|
|
291
|
+
immediately. Use get_freeze_status to poll for completion.
|
|
292
|
+
"""
|
|
293
|
+
track_index = int(params["track_index"])
|
|
294
|
+
track = get_track(song, track_index)
|
|
295
|
+
if track.is_frozen:
|
|
296
|
+
return {
|
|
297
|
+
"track_index": track_index,
|
|
298
|
+
"is_frozen": True,
|
|
299
|
+
"note": "Track is already frozen",
|
|
300
|
+
}
|
|
301
|
+
# In Live 12, freeze is a track method accessible from ControlSurface
|
|
302
|
+
try:
|
|
303
|
+
track.freeze()
|
|
304
|
+
except AttributeError:
|
|
305
|
+
raise ValueError(
|
|
306
|
+
"freeze() not available on this track type. "
|
|
307
|
+
"Only MIDI and audio tracks with devices can be frozen."
|
|
308
|
+
)
|
|
309
|
+
return {
|
|
310
|
+
"track_index": track_index,
|
|
311
|
+
"freezing": True,
|
|
312
|
+
"note": "Freeze initiated. Poll get_freeze_status to check completion.",
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
@register("flatten_track")
|
|
317
|
+
def flatten_track(song, params):
|
|
318
|
+
"""Flatten a frozen track — commits rendered audio permanently.
|
|
319
|
+
|
|
320
|
+
Destructive operation. The track must already be frozen.
|
|
321
|
+
Wrapped in undo step so it can be reverted.
|
|
322
|
+
"""
|
|
323
|
+
track_index = int(params["track_index"])
|
|
324
|
+
track = get_track(song, track_index)
|
|
325
|
+
if not track.is_frozen:
|
|
326
|
+
raise ValueError(
|
|
327
|
+
"Track %d is not frozen. Freeze it first with freeze_track."
|
|
328
|
+
% track_index
|
|
329
|
+
)
|
|
330
|
+
song.begin_undo_step()
|
|
331
|
+
try:
|
|
332
|
+
# flatten() is a method on the track, not the song
|
|
333
|
+
track.flatten()
|
|
334
|
+
finally:
|
|
335
|
+
song.end_undo_step()
|
|
336
|
+
return {
|
|
337
|
+
"track_index": track_index,
|
|
338
|
+
"flattened": True,
|
|
339
|
+
}
|