livepilot 1.9.15 → 1.9.16
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 +1 -1
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +40 -0
- package/README.md +1 -1
- package/livepilot/.Codex-plugin/plugin.json +1 -1
- package/livepilot/.claude-plugin/plugin.json +1 -1
- package/livepilot/skills/livepilot-core/references/overview.md +1 -1
- package/m4l_device/livepilot_bridge.js +27 -13
- package/mcp_server/__init__.py +1 -1
- package/mcp_server/connection.py +24 -2
- package/mcp_server/curves.py +3 -3
- package/mcp_server/evaluation/fabric.py +1 -1
- package/mcp_server/m4l_bridge.py +9 -1
- package/mcp_server/memory/technique_store.py +25 -17
- package/mcp_server/mix_engine/critics.py +1 -1
- package/mcp_server/mix_engine/tools.py +14 -8
- package/mcp_server/performance_engine/safety.py +6 -3
- package/mcp_server/project_brain/refresh.py +8 -2
- package/mcp_server/project_brain/tools.py +12 -12
- package/mcp_server/reference_engine/tools.py +16 -15
- package/mcp_server/runtime/action_ledger_models.py +10 -3
- package/mcp_server/runtime/capability_state.py +3 -2
- package/mcp_server/runtime/tools.py +6 -3
- package/mcp_server/tools/agent_os.py +47 -39
- package/mcp_server/tools/composition.py +114 -32
- package/mcp_server/tools/devices.py +15 -1
- package/mcp_server/tools/midi_io.py +3 -1
- package/mcp_server/tools/research.py +31 -31
- package/mcp_server/tools/tracks.py +3 -3
- package/mcp_server/translation_engine/tools.py +50 -16
- package/package.json +1 -1
- package/remote_script/LivePilot/__init__.py +1 -1
- package/remote_script/LivePilot/arrangement.py +9 -1
- package/remote_script/LivePilot/clips.py +22 -6
- package/remote_script/LivePilot/notes.py +9 -1
- package/remote_script/LivePilot/server.py +6 -6
|
@@ -50,6 +50,11 @@ def create_clip(song, params):
|
|
|
50
50
|
raise ValueError("Clip length must be > 0")
|
|
51
51
|
|
|
52
52
|
clip_slot = get_clip_slot(song, track_index, clip_index)
|
|
53
|
+
if clip_slot.has_clip:
|
|
54
|
+
raise ValueError(
|
|
55
|
+
"Clip slot %d on track %d already has a clip. "
|
|
56
|
+
"Delete it first with delete_clip." % (clip_index, track_index)
|
|
57
|
+
)
|
|
53
58
|
clip_slot.create_clip(length)
|
|
54
59
|
clip = clip_slot.clip
|
|
55
60
|
|
|
@@ -147,12 +152,23 @@ def set_clip_loop(song, params):
|
|
|
147
152
|
clip_index = int(params["clip_index"])
|
|
148
153
|
clip = get_clip(song, track_index, clip_index)
|
|
149
154
|
|
|
150
|
-
#
|
|
151
|
-
#
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if "start" in params
|
|
155
|
-
|
|
155
|
+
# Conditional ordering to avoid Live's loop_start < loop_end clamping.
|
|
156
|
+
# When expanding the window, set the expanding edge first.
|
|
157
|
+
# When shrinking, set the contracting edge first.
|
|
158
|
+
new_end = float(params["end"]) if "end" in params else None
|
|
159
|
+
new_start = float(params["start"]) if "start" in params else None
|
|
160
|
+
|
|
161
|
+
if new_end is not None and new_end > clip.loop_end:
|
|
162
|
+
# Expanding right — set end first so start can move freely
|
|
163
|
+
clip.loop_end = new_end
|
|
164
|
+
if new_start is not None:
|
|
165
|
+
clip.loop_start = new_start
|
|
166
|
+
else:
|
|
167
|
+
# Shrinking or only changing start — set start first
|
|
168
|
+
if new_start is not None:
|
|
169
|
+
clip.loop_start = new_start
|
|
170
|
+
if new_end is not None:
|
|
171
|
+
clip.loop_end = new_end
|
|
156
172
|
if "enabled" in params:
|
|
157
173
|
clip.looping = bool(params["enabled"])
|
|
158
174
|
|
|
@@ -172,6 +172,12 @@ def modify_notes(song, params):
|
|
|
172
172
|
note.velocity = float(mod["velocity"])
|
|
173
173
|
if "probability" in mod:
|
|
174
174
|
note.probability = float(mod["probability"])
|
|
175
|
+
if "mute" in mod:
|
|
176
|
+
note.mute = bool(mod["mute"])
|
|
177
|
+
if "velocity_deviation" in mod:
|
|
178
|
+
note.velocity_deviation = float(mod["velocity_deviation"])
|
|
179
|
+
if "release_velocity" in mod:
|
|
180
|
+
note.release_velocity = float(mod["release_velocity"])
|
|
175
181
|
modified_count += 1
|
|
176
182
|
|
|
177
183
|
# Pass the original NoteVector back — Boost.Python requires the C++ type
|
|
@@ -273,7 +279,9 @@ def transpose_notes(song, params):
|
|
|
273
279
|
clip = get_clip(song, track_index, clip_index)
|
|
274
280
|
|
|
275
281
|
from_time = float(params.get("from_time", 0.0))
|
|
276
|
-
|
|
282
|
+
# Default span covers from from_time to end of clip, not the full clip length
|
|
283
|
+
default_span = max(0.0, clip.length - from_time) if clip.length > 0 else 32768.0
|
|
284
|
+
time_span = float(params.get("time_span", default_span))
|
|
277
285
|
|
|
278
286
|
# Get notes — returns C++ NoteVector that must be passed back intact
|
|
279
287
|
all_notes = clip.get_notes_extended(0, 128, from_time, time_span)
|
|
@@ -162,12 +162,12 @@ class LivePilotServer(object):
|
|
|
162
162
|
pass
|
|
163
163
|
continue
|
|
164
164
|
self._client_connected = True
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
165
|
+
self._client_thread = threading.Thread(
|
|
166
|
+
target=self._run_client_session,
|
|
167
|
+
args=(client, addr),
|
|
168
|
+
)
|
|
169
|
+
self._client_thread.daemon = True
|
|
170
|
+
self._client_thread.start()
|
|
171
171
|
except socket.timeout:
|
|
172
172
|
continue
|
|
173
173
|
except OSError:
|