livepilot 1.9.24 → 1.10.1
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 +3 -3
- package/CHANGELOG.md +223 -0
- package/CONTRIBUTING.md +2 -2
- package/LICENSE +62 -21
- package/README.md +291 -276
- package/bin/livepilot.js +87 -0
- package/installer/codex.js +147 -0
- package/livepilot/.Codex-plugin/plugin.json +2 -2
- package/livepilot/.claude-plugin/plugin.json +2 -2
- package/livepilot/skills/livepilot-arrangement/SKILL.md +18 -1
- package/livepilot/skills/livepilot-core/SKILL.md +22 -5
- package/livepilot/skills/livepilot-core/references/device-knowledge/00-index.md +34 -0
- package/livepilot/skills/livepilot-core/references/device-knowledge/automation-as-music.md +204 -0
- package/livepilot/skills/livepilot-core/references/device-knowledge/chains-genre.md +173 -0
- package/livepilot/skills/livepilot-core/references/device-knowledge/creative-thinking.md +211 -0
- package/livepilot/skills/livepilot-core/references/device-knowledge/effects-distortion.md +188 -0
- package/livepilot/skills/livepilot-core/references/device-knowledge/effects-space.md +162 -0
- package/livepilot/skills/livepilot-core/references/device-knowledge/effects-spectral.md +229 -0
- package/livepilot/skills/livepilot-core/references/device-knowledge/instruments-synths.md +243 -0
- package/livepilot/skills/livepilot-core/references/overview.md +13 -9
- package/livepilot/skills/livepilot-core/references/sample-manipulation.md +724 -0
- package/livepilot/skills/livepilot-core/references/sound-design-deep.md +140 -0
- package/livepilot/skills/livepilot-devices/SKILL.md +39 -4
- package/livepilot/skills/livepilot-evaluation/references/capability-modes.md +1 -1
- package/livepilot/skills/livepilot-release/SKILL.md +23 -19
- package/livepilot/skills/livepilot-sample-engine/SKILL.md +105 -0
- package/livepilot/skills/livepilot-sample-engine/references/sample-critics.md +87 -0
- package/livepilot/skills/livepilot-sample-engine/references/sample-philosophy.md +51 -0
- package/livepilot/skills/livepilot-sample-engine/references/sample-techniques.md +131 -0
- package/livepilot/skills/livepilot-sound-design-engine/SKILL.md +45 -0
- package/livepilot/skills/livepilot-wonder/SKILL.md +17 -0
- package/livepilot.mcpb +0 -0
- package/m4l_device/livepilot_bridge.js +1 -1
- package/manifest.json +4 -4
- package/mcp_server/__init__.py +1 -1
- package/mcp_server/atlas/__init__.py +357 -0
- package/mcp_server/atlas/device_atlas.json +44067 -0
- package/mcp_server/atlas/enrichments/__init__.py +111 -0
- package/mcp_server/atlas/enrichments/audio_effects/auto_filter.yaml +162 -0
- package/mcp_server/atlas/enrichments/audio_effects/beat_repeat.yaml +183 -0
- package/mcp_server/atlas/enrichments/audio_effects/channel_eq.yaml +126 -0
- package/mcp_server/atlas/enrichments/audio_effects/chorus_ensemble.yaml +149 -0
- package/mcp_server/atlas/enrichments/audio_effects/color_limiter.yaml +109 -0
- package/mcp_server/atlas/enrichments/audio_effects/compressor.yaml +159 -0
- package/mcp_server/atlas/enrichments/audio_effects/convolution_reverb.yaml +143 -0
- package/mcp_server/atlas/enrichments/audio_effects/convolution_reverb_pro.yaml +178 -0
- package/mcp_server/atlas/enrichments/audio_effects/delay.yaml +151 -0
- package/mcp_server/atlas/enrichments/audio_effects/drum_buss.yaml +142 -0
- package/mcp_server/atlas/enrichments/audio_effects/dynamic_tube.yaml +147 -0
- package/mcp_server/atlas/enrichments/audio_effects/echo.yaml +167 -0
- package/mcp_server/atlas/enrichments/audio_effects/eq_eight.yaml +148 -0
- package/mcp_server/atlas/enrichments/audio_effects/eq_three.yaml +121 -0
- package/mcp_server/atlas/enrichments/audio_effects/erosion.yaml +103 -0
- package/mcp_server/atlas/enrichments/audio_effects/filter_delay.yaml +173 -0
- package/mcp_server/atlas/enrichments/audio_effects/gate.yaml +130 -0
- package/mcp_server/atlas/enrichments/audio_effects/gated_delay.yaml +133 -0
- package/mcp_server/atlas/enrichments/audio_effects/glue_compressor.yaml +142 -0
- package/mcp_server/atlas/enrichments/audio_effects/grain_delay.yaml +141 -0
- package/mcp_server/atlas/enrichments/audio_effects/hybrid_reverb.yaml +160 -0
- package/mcp_server/atlas/enrichments/audio_effects/limiter.yaml +97 -0
- package/mcp_server/atlas/enrichments/audio_effects/multiband_dynamics.yaml +174 -0
- package/mcp_server/atlas/enrichments/audio_effects/overdrive.yaml +119 -0
- package/mcp_server/atlas/enrichments/audio_effects/pedal.yaml +145 -0
- package/mcp_server/atlas/enrichments/audio_effects/phaser_flanger.yaml +161 -0
- package/mcp_server/atlas/enrichments/audio_effects/redux.yaml +114 -0
- package/mcp_server/atlas/enrichments/audio_effects/reverb.yaml +190 -0
- package/mcp_server/atlas/enrichments/audio_effects/roar.yaml +159 -0
- package/mcp_server/atlas/enrichments/audio_effects/saturator.yaml +146 -0
- package/mcp_server/atlas/enrichments/audio_effects/shifter.yaml +154 -0
- package/mcp_server/atlas/enrichments/audio_effects/spectral_resonator.yaml +141 -0
- package/mcp_server/atlas/enrichments/audio_effects/spectral_time.yaml +164 -0
- package/mcp_server/atlas/enrichments/audio_effects/vector_delay.yaml +140 -0
- package/mcp_server/atlas/enrichments/audio_effects/vinyl_distortion.yaml +141 -0
- package/mcp_server/atlas/enrichments/instruments/analog.yaml +222 -0
- package/mcp_server/atlas/enrichments/instruments/bass.yaml +202 -0
- package/mcp_server/atlas/enrichments/instruments/collision.yaml +150 -0
- package/mcp_server/atlas/enrichments/instruments/drift.yaml +167 -0
- package/mcp_server/atlas/enrichments/instruments/electric.yaml +137 -0
- package/mcp_server/atlas/enrichments/instruments/emit.yaml +163 -0
- package/mcp_server/atlas/enrichments/instruments/meld.yaml +164 -0
- package/mcp_server/atlas/enrichments/instruments/operator.yaml +197 -0
- package/mcp_server/atlas/enrichments/instruments/poli.yaml +192 -0
- package/mcp_server/atlas/enrichments/instruments/sampler.yaml +218 -0
- package/mcp_server/atlas/enrichments/instruments/simpler.yaml +217 -0
- package/mcp_server/atlas/enrichments/instruments/tension.yaml +156 -0
- package/mcp_server/atlas/enrichments/instruments/tree_tone.yaml +162 -0
- package/mcp_server/atlas/enrichments/instruments/vector_fm.yaml +165 -0
- package/mcp_server/atlas/enrichments/instruments/vector_grain.yaml +166 -0
- package/mcp_server/atlas/enrichments/instruments/wavetable.yaml +162 -0
- package/mcp_server/atlas/enrichments/midi_effects/arpeggiator.yaml +156 -0
- package/mcp_server/atlas/enrichments/midi_effects/bouncy_notes.yaml +93 -0
- package/mcp_server/atlas/enrichments/midi_effects/chord.yaml +147 -0
- package/mcp_server/atlas/enrichments/midi_effects/melodic_steps.yaml +97 -0
- package/mcp_server/atlas/enrichments/midi_effects/note_echo.yaml +108 -0
- package/mcp_server/atlas/enrichments/midi_effects/note_length.yaml +97 -0
- package/mcp_server/atlas/enrichments/midi_effects/pitch.yaml +76 -0
- package/mcp_server/atlas/enrichments/midi_effects/random.yaml +117 -0
- package/mcp_server/atlas/enrichments/midi_effects/rhythmic_steps.yaml +103 -0
- package/mcp_server/atlas/enrichments/midi_effects/scale.yaml +83 -0
- package/mcp_server/atlas/enrichments/midi_effects/step_arp.yaml +112 -0
- package/mcp_server/atlas/enrichments/midi_effects/velocity.yaml +119 -0
- package/mcp_server/atlas/enrichments/utility/amp.yaml +159 -0
- package/mcp_server/atlas/enrichments/utility/cabinet.yaml +109 -0
- package/mcp_server/atlas/enrichments/utility/corpus.yaml +150 -0
- package/mcp_server/atlas/enrichments/utility/resonators.yaml +131 -0
- package/mcp_server/atlas/enrichments/utility/spectrum.yaml +63 -0
- package/mcp_server/atlas/enrichments/utility/tuner.yaml +51 -0
- package/mcp_server/atlas/enrichments/utility/utility.yaml +136 -0
- package/mcp_server/atlas/enrichments/utility/vocoder.yaml +160 -0
- package/mcp_server/atlas/scanner.py +236 -0
- package/mcp_server/atlas/tools.py +224 -0
- package/mcp_server/composer/__init__.py +1 -0
- package/mcp_server/composer/engine.py +532 -0
- package/mcp_server/composer/layer_planner.py +427 -0
- package/mcp_server/composer/prompt_parser.py +329 -0
- package/mcp_server/composer/sample_resolver.py +153 -0
- package/mcp_server/composer/tools.py +211 -0
- package/mcp_server/connection.py +53 -8
- package/mcp_server/corpus/__init__.py +377 -0
- package/mcp_server/device_forge/__init__.py +1 -0
- package/mcp_server/device_forge/builder.py +377 -0
- package/mcp_server/device_forge/models.py +142 -0
- package/mcp_server/device_forge/templates.py +483 -0
- package/mcp_server/device_forge/tools.py +162 -0
- package/mcp_server/m4l_bridge.py +1 -0
- package/mcp_server/memory/taste_accessors.py +47 -0
- package/mcp_server/preview_studio/engine.py +9 -2
- package/mcp_server/preview_studio/tools.py +78 -35
- package/mcp_server/project_brain/tools.py +34 -0
- package/mcp_server/runtime/capability_probe.py +21 -2
- package/mcp_server/runtime/execution_router.py +184 -38
- package/mcp_server/runtime/live_version.py +102 -0
- package/mcp_server/runtime/mcp_dispatch.py +46 -0
- package/mcp_server/runtime/remote_commands.py +13 -5
- package/mcp_server/runtime/tools.py +66 -29
- package/mcp_server/sample_engine/__init__.py +1 -0
- package/mcp_server/sample_engine/analyzer.py +216 -0
- package/mcp_server/sample_engine/critics.py +390 -0
- package/mcp_server/sample_engine/models.py +193 -0
- package/mcp_server/sample_engine/moves.py +127 -0
- package/mcp_server/sample_engine/planner.py +186 -0
- package/mcp_server/sample_engine/slice_workflow.py +190 -0
- package/mcp_server/sample_engine/sources.py +540 -0
- package/mcp_server/sample_engine/techniques.py +908 -0
- package/mcp_server/sample_engine/tools.py +545 -0
- package/mcp_server/semantic_moves/__init__.py +3 -0
- package/mcp_server/semantic_moves/device_creation_moves.py +237 -0
- package/mcp_server/semantic_moves/mix_moves.py +8 -8
- package/mcp_server/semantic_moves/models.py +7 -7
- package/mcp_server/semantic_moves/performance_moves.py +4 -4
- package/mcp_server/semantic_moves/sample_compilers.py +377 -0
- package/mcp_server/semantic_moves/sound_design_moves.py +4 -4
- package/mcp_server/semantic_moves/tools.py +63 -10
- package/mcp_server/semantic_moves/transition_moves.py +4 -4
- package/mcp_server/server.py +71 -1
- package/mcp_server/session_continuity/tracker.py +4 -1
- package/mcp_server/sound_design/critics.py +89 -1
- package/mcp_server/splice_client/__init__.py +1 -0
- package/mcp_server/splice_client/client.py +347 -0
- package/mcp_server/splice_client/models.py +96 -0
- package/mcp_server/splice_client/protos/__init__.py +1 -0
- package/mcp_server/splice_client/protos/app_pb2.py +319 -0
- package/mcp_server/splice_client/protos/app_pb2.pyi +1153 -0
- package/mcp_server/splice_client/protos/app_pb2_grpc.py +1946 -0
- package/mcp_server/tools/_conductor.py +16 -0
- package/mcp_server/tools/_planner_engine.py +24 -0
- package/mcp_server/tools/analyzer.py +2 -0
- package/mcp_server/tools/arrangement.py +69 -0
- package/mcp_server/tools/automation.py +15 -2
- package/mcp_server/tools/devices.py +117 -6
- package/mcp_server/tools/notes.py +37 -4
- package/mcp_server/tools/planner.py +3 -0
- package/mcp_server/wonder_mode/diagnosis.py +5 -0
- package/mcp_server/wonder_mode/engine.py +144 -14
- package/mcp_server/wonder_mode/tools.py +33 -1
- package/package.json +14 -4
- package/remote_script/LivePilot/__init__.py +8 -1
- package/remote_script/LivePilot/arrangement.py +114 -0
- package/remote_script/LivePilot/browser.py +56 -1
- package/remote_script/LivePilot/devices.py +246 -6
- package/remote_script/LivePilot/mixing.py +8 -3
- package/remote_script/LivePilot/server.py +5 -1
- package/remote_script/LivePilot/transport.py +3 -0
- package/remote_script/LivePilot/version_detect.py +78 -0
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
"""Compilers for sample-domain semantic moves.
|
|
2
|
+
|
|
3
|
+
These compile sample manipulation intents into concrete tool call sequences
|
|
4
|
+
using the session kernel to find appropriate tracks and devices.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from .compiler import CompiledPlan, CompiledStep, register_compiler
|
|
10
|
+
from .models import SemanticMove
|
|
11
|
+
from . import resolvers
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _resolve_sample_path(kernel: dict) -> str:
|
|
15
|
+
"""Get the sample file path from kernel, or return placeholder."""
|
|
16
|
+
return kernel.get("sample_file_path", "{sample_file_path}")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _compile_sample_chop_rhythm(move: SemanticMove, kernel: dict) -> CompiledPlan:
|
|
20
|
+
"""Compile 'sample_chop_rhythm': load, slice, and chop a sample for rhythm."""
|
|
21
|
+
steps = []
|
|
22
|
+
descriptions = []
|
|
23
|
+
warnings = []
|
|
24
|
+
|
|
25
|
+
# Find drum/percussion tracks to layer alongside
|
|
26
|
+
drums = resolvers.find_tracks_by_role(kernel, ["drums", "percussion"])
|
|
27
|
+
|
|
28
|
+
# Create a new track for the chopped sample
|
|
29
|
+
steps.append(CompiledStep(
|
|
30
|
+
tool="create_midi_track",
|
|
31
|
+
params={"name": "Chop"},
|
|
32
|
+
description="Create track for chopped sample",
|
|
33
|
+
))
|
|
34
|
+
descriptions.append("Create chop track")
|
|
35
|
+
|
|
36
|
+
# Load into Simpler — track index will be last + 1
|
|
37
|
+
tracks = kernel.get("session_info", {}).get("tracks", [])
|
|
38
|
+
new_idx = len(tracks)
|
|
39
|
+
|
|
40
|
+
steps.append(CompiledStep(
|
|
41
|
+
tool="load_sample_to_simpler",
|
|
42
|
+
params={"track_index": new_idx, "file_path": _resolve_sample_path(kernel)},
|
|
43
|
+
description="Load sample into Simpler for slicing",
|
|
44
|
+
))
|
|
45
|
+
|
|
46
|
+
steps.append(CompiledStep(
|
|
47
|
+
tool="set_simpler_playback_mode",
|
|
48
|
+
params={"track_index": new_idx, "device_index": 0, "playback_mode": 2},
|
|
49
|
+
description="Switch to slice mode for rhythmic chopping",
|
|
50
|
+
))
|
|
51
|
+
descriptions.append("Slice sample")
|
|
52
|
+
|
|
53
|
+
# Balance against existing drums
|
|
54
|
+
if drums:
|
|
55
|
+
steps.append(CompiledStep(
|
|
56
|
+
tool="set_track_volume",
|
|
57
|
+
params={"track_index": new_idx, "volume": 0.55},
|
|
58
|
+
description="Set chop volume below main drums",
|
|
59
|
+
))
|
|
60
|
+
else:
|
|
61
|
+
warnings.append("No drum tracks found — chop will be the primary rhythm")
|
|
62
|
+
|
|
63
|
+
steps.append(CompiledStep(
|
|
64
|
+
tool="get_track_meters",
|
|
65
|
+
params={"include_stereo": True},
|
|
66
|
+
description="Verify chopped sample producing audio",
|
|
67
|
+
))
|
|
68
|
+
|
|
69
|
+
return CompiledPlan(
|
|
70
|
+
move_id=move.move_id,
|
|
71
|
+
intent=move.intent,
|
|
72
|
+
steps=steps,
|
|
73
|
+
risk_level="medium",
|
|
74
|
+
summary="; ".join(descriptions) if descriptions else "Chop sample for rhythm",
|
|
75
|
+
requires_approval=True,
|
|
76
|
+
warnings=warnings,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _compile_sample_texture_layer(move: SemanticMove, kernel: dict) -> CompiledPlan:
|
|
81
|
+
"""Compile 'sample_texture_layer': load and filter a sample as background texture."""
|
|
82
|
+
steps = []
|
|
83
|
+
descriptions = []
|
|
84
|
+
|
|
85
|
+
tracks = kernel.get("session_info", {}).get("tracks", [])
|
|
86
|
+
new_idx = len(tracks)
|
|
87
|
+
|
|
88
|
+
steps.append(CompiledStep(
|
|
89
|
+
tool="create_midi_track",
|
|
90
|
+
params={"name": "Texture"},
|
|
91
|
+
description="Create track for texture layer",
|
|
92
|
+
))
|
|
93
|
+
|
|
94
|
+
steps.append(CompiledStep(
|
|
95
|
+
tool="load_sample_to_simpler",
|
|
96
|
+
params={"track_index": new_idx, "file_path": _resolve_sample_path(kernel)},
|
|
97
|
+
description="Load textural sample into Simpler",
|
|
98
|
+
))
|
|
99
|
+
descriptions.append("Load texture sample")
|
|
100
|
+
|
|
101
|
+
# Low volume for background placement
|
|
102
|
+
steps.append(CompiledStep(
|
|
103
|
+
tool="set_track_volume",
|
|
104
|
+
params={"track_index": new_idx, "volume": 0.35},
|
|
105
|
+
description="Set texture low in mix for background presence",
|
|
106
|
+
))
|
|
107
|
+
descriptions.append("Set background level")
|
|
108
|
+
|
|
109
|
+
# Add reverb send for spatial depth
|
|
110
|
+
steps.append(CompiledStep(
|
|
111
|
+
tool="set_track_send",
|
|
112
|
+
params={"track_index": new_idx, "send_index": 0, "value": 0.40},
|
|
113
|
+
description="Heavy reverb for spatial depth on texture",
|
|
114
|
+
))
|
|
115
|
+
descriptions.append("Add reverb depth")
|
|
116
|
+
|
|
117
|
+
steps.append(CompiledStep(
|
|
118
|
+
tool="get_track_meters",
|
|
119
|
+
params={"include_stereo": True},
|
|
120
|
+
description="Verify texture layer producing audio at low level",
|
|
121
|
+
))
|
|
122
|
+
|
|
123
|
+
return CompiledPlan(
|
|
124
|
+
move_id=move.move_id,
|
|
125
|
+
intent=move.intent,
|
|
126
|
+
steps=steps,
|
|
127
|
+
risk_level="low",
|
|
128
|
+
summary="; ".join(descriptions),
|
|
129
|
+
requires_approval=(kernel.get("mode", "improve") != "explore"),
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _compile_sample_vocal_ghost(move: SemanticMove, kernel: dict) -> CompiledPlan:
|
|
134
|
+
"""Compile 'sample_vocal_ghost': reverse, pitch, and wash a vocal sample."""
|
|
135
|
+
steps = []
|
|
136
|
+
descriptions = []
|
|
137
|
+
|
|
138
|
+
tracks = kernel.get("session_info", {}).get("tracks", [])
|
|
139
|
+
new_idx = len(tracks)
|
|
140
|
+
|
|
141
|
+
steps.append(CompiledStep(
|
|
142
|
+
tool="create_midi_track",
|
|
143
|
+
params={"name": "Ghost Vox"},
|
|
144
|
+
description="Create track for ghost vocal",
|
|
145
|
+
))
|
|
146
|
+
|
|
147
|
+
steps.append(CompiledStep(
|
|
148
|
+
tool="load_sample_to_simpler",
|
|
149
|
+
params={"track_index": new_idx, "file_path": _resolve_sample_path(kernel)},
|
|
150
|
+
description="Load vocal sample into Simpler",
|
|
151
|
+
))
|
|
152
|
+
|
|
153
|
+
steps.append(CompiledStep(
|
|
154
|
+
tool="reverse_simpler",
|
|
155
|
+
params={"track_index": new_idx},
|
|
156
|
+
description="Reverse vocal for ghostly character",
|
|
157
|
+
))
|
|
158
|
+
descriptions.append("Reverse vocal")
|
|
159
|
+
|
|
160
|
+
# Heavy reverb wash
|
|
161
|
+
steps.append(CompiledStep(
|
|
162
|
+
tool="set_track_send",
|
|
163
|
+
params={"track_index": new_idx, "send_index": 0, "value": 0.55},
|
|
164
|
+
description="Heavy reverb wash for ghostly depth",
|
|
165
|
+
))
|
|
166
|
+
descriptions.append("Reverb wash")
|
|
167
|
+
|
|
168
|
+
# Low volume — ghosts live in the background
|
|
169
|
+
steps.append(CompiledStep(
|
|
170
|
+
tool="set_track_volume",
|
|
171
|
+
params={"track_index": new_idx, "volume": 0.30},
|
|
172
|
+
description="Set ghost vocal low in mix",
|
|
173
|
+
))
|
|
174
|
+
descriptions.append("Background level")
|
|
175
|
+
|
|
176
|
+
steps.append(CompiledStep(
|
|
177
|
+
tool="get_track_meters",
|
|
178
|
+
params={"include_stereo": True},
|
|
179
|
+
description="Verify ghost vocal producing audio with reverb tail",
|
|
180
|
+
))
|
|
181
|
+
|
|
182
|
+
return CompiledPlan(
|
|
183
|
+
move_id=move.move_id,
|
|
184
|
+
intent=move.intent,
|
|
185
|
+
steps=steps,
|
|
186
|
+
risk_level="medium",
|
|
187
|
+
summary="; ".join(descriptions),
|
|
188
|
+
requires_approval=True,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _compile_sample_break_layer(move: SemanticMove, kernel: dict) -> CompiledPlan:
|
|
193
|
+
"""Compile 'sample_break_layer': slice a break and layer over existing drums."""
|
|
194
|
+
steps = []
|
|
195
|
+
descriptions = []
|
|
196
|
+
warnings = []
|
|
197
|
+
|
|
198
|
+
drums = resolvers.find_tracks_by_role(kernel, ["drums", "percussion"])
|
|
199
|
+
if not drums:
|
|
200
|
+
warnings.append("No existing drum tracks — break will be the primary rhythm")
|
|
201
|
+
|
|
202
|
+
tracks = kernel.get("session_info", {}).get("tracks", [])
|
|
203
|
+
new_idx = len(tracks)
|
|
204
|
+
|
|
205
|
+
steps.append(CompiledStep(
|
|
206
|
+
tool="create_midi_track",
|
|
207
|
+
params={"name": "Break"},
|
|
208
|
+
description="Create track for breakbeat layer",
|
|
209
|
+
))
|
|
210
|
+
|
|
211
|
+
steps.append(CompiledStep(
|
|
212
|
+
tool="load_sample_to_simpler",
|
|
213
|
+
params={"track_index": new_idx, "file_path": _resolve_sample_path(kernel)},
|
|
214
|
+
description="Load breakbeat into Simpler",
|
|
215
|
+
))
|
|
216
|
+
|
|
217
|
+
steps.append(CompiledStep(
|
|
218
|
+
tool="set_simpler_playback_mode",
|
|
219
|
+
params={"track_index": new_idx, "device_index": 0, "playback_mode": 2},
|
|
220
|
+
description="Slice break by transients for individual hits",
|
|
221
|
+
))
|
|
222
|
+
descriptions.append("Slice break")
|
|
223
|
+
|
|
224
|
+
# Sit below main drums
|
|
225
|
+
steps.append(CompiledStep(
|
|
226
|
+
tool="set_track_volume",
|
|
227
|
+
params={"track_index": new_idx, "volume": 0.45},
|
|
228
|
+
description="Set break layer below main drums",
|
|
229
|
+
))
|
|
230
|
+
descriptions.append("Balance break level")
|
|
231
|
+
|
|
232
|
+
steps.append(CompiledStep(
|
|
233
|
+
tool="get_track_meters",
|
|
234
|
+
params={"include_stereo": True},
|
|
235
|
+
description="Verify break layer producing audio alongside drums",
|
|
236
|
+
))
|
|
237
|
+
|
|
238
|
+
return CompiledPlan(
|
|
239
|
+
move_id=move.move_id,
|
|
240
|
+
intent=move.intent,
|
|
241
|
+
steps=steps,
|
|
242
|
+
risk_level="medium",
|
|
243
|
+
summary="; ".join(descriptions) if descriptions else "Layer breakbeat",
|
|
244
|
+
requires_approval=True,
|
|
245
|
+
warnings=warnings,
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def _compile_sample_resample_destroy(move: SemanticMove, kernel: dict) -> CompiledPlan:
|
|
250
|
+
"""Compile 'sample_resample_destroy': warp and mangle a sample destructively.
|
|
251
|
+
|
|
252
|
+
SAFETY: This is a high-risk move — always requires approval.
|
|
253
|
+
Only adjusts device params when a known device is confirmed present.
|
|
254
|
+
"""
|
|
255
|
+
steps = []
|
|
256
|
+
descriptions = []
|
|
257
|
+
warnings = ["High-risk: destructive processing — consider duplicating track first"]
|
|
258
|
+
|
|
259
|
+
tracks = kernel.get("session_info", {}).get("tracks", [])
|
|
260
|
+
new_idx = len(tracks)
|
|
261
|
+
|
|
262
|
+
steps.append(CompiledStep(
|
|
263
|
+
tool="create_midi_track",
|
|
264
|
+
params={"name": "Destroy"},
|
|
265
|
+
description="Create track for destructive resampling",
|
|
266
|
+
))
|
|
267
|
+
|
|
268
|
+
steps.append(CompiledStep(
|
|
269
|
+
tool="load_sample_to_simpler",
|
|
270
|
+
params={"track_index": new_idx, "file_path": _resolve_sample_path(kernel)},
|
|
271
|
+
description="Load sample for destruction",
|
|
272
|
+
))
|
|
273
|
+
descriptions.append("Load source")
|
|
274
|
+
|
|
275
|
+
steps.append(CompiledStep(
|
|
276
|
+
tool="warp_simpler",
|
|
277
|
+
params={"track_index": new_idx},
|
|
278
|
+
description="Apply extreme warp for time-stretch artifacts",
|
|
279
|
+
))
|
|
280
|
+
descriptions.append("Warp for artifacts")
|
|
281
|
+
|
|
282
|
+
# Use volume + send instead of blindly setting device params
|
|
283
|
+
steps.append(CompiledStep(
|
|
284
|
+
tool="set_track_send",
|
|
285
|
+
params={"track_index": new_idx, "send_index": 0, "value": 0.30},
|
|
286
|
+
description="Add reverb send for destroyed texture depth",
|
|
287
|
+
))
|
|
288
|
+
|
|
289
|
+
steps.append(CompiledStep(
|
|
290
|
+
tool="set_track_volume",
|
|
291
|
+
params={"track_index": new_idx, "volume": 0.50},
|
|
292
|
+
description="Set destroyed sample at moderate level",
|
|
293
|
+
))
|
|
294
|
+
descriptions.append("Set level")
|
|
295
|
+
|
|
296
|
+
steps.append(CompiledStep(
|
|
297
|
+
tool="get_track_meters",
|
|
298
|
+
params={"include_stereo": True},
|
|
299
|
+
description="Verify destroyed sample producing audio",
|
|
300
|
+
))
|
|
301
|
+
|
|
302
|
+
return CompiledPlan(
|
|
303
|
+
move_id=move.move_id,
|
|
304
|
+
intent=move.intent,
|
|
305
|
+
steps=steps,
|
|
306
|
+
risk_level="high",
|
|
307
|
+
summary="; ".join(descriptions),
|
|
308
|
+
requires_approval=True,
|
|
309
|
+
warnings=warnings,
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def _compile_sample_one_shot_accent(move: SemanticMove, kernel: dict) -> CompiledPlan:
|
|
314
|
+
"""Compile 'sample_one_shot_accent': load a one-shot for rhythmic punctuation."""
|
|
315
|
+
steps = []
|
|
316
|
+
descriptions = []
|
|
317
|
+
|
|
318
|
+
tracks = kernel.get("session_info", {}).get("tracks", [])
|
|
319
|
+
new_idx = len(tracks)
|
|
320
|
+
|
|
321
|
+
steps.append(CompiledStep(
|
|
322
|
+
tool="create_midi_track",
|
|
323
|
+
params={"name": "Accent"},
|
|
324
|
+
description="Create track for one-shot accent",
|
|
325
|
+
))
|
|
326
|
+
|
|
327
|
+
steps.append(CompiledStep(
|
|
328
|
+
tool="load_sample_to_simpler",
|
|
329
|
+
params={"track_index": new_idx, "file_path": _resolve_sample_path(kernel)},
|
|
330
|
+
description="Load one-shot into Simpler",
|
|
331
|
+
))
|
|
332
|
+
|
|
333
|
+
steps.append(CompiledStep(
|
|
334
|
+
tool="set_simpler_playback_mode",
|
|
335
|
+
params={"track_index": new_idx, "device_index": 0, "playback_mode": 1},
|
|
336
|
+
description="One-shot mode for trigger playback",
|
|
337
|
+
))
|
|
338
|
+
descriptions.append("One-shot mode")
|
|
339
|
+
|
|
340
|
+
steps.append(CompiledStep(
|
|
341
|
+
tool="crop_simpler",
|
|
342
|
+
params={"track_index": new_idx},
|
|
343
|
+
description="Tight crop around the transient",
|
|
344
|
+
))
|
|
345
|
+
descriptions.append("Crop to transient")
|
|
346
|
+
|
|
347
|
+
# Accent should be punchy but not dominating
|
|
348
|
+
steps.append(CompiledStep(
|
|
349
|
+
tool="set_track_volume",
|
|
350
|
+
params={"track_index": new_idx, "volume": 0.60},
|
|
351
|
+
description="Set accent at punchy but balanced level",
|
|
352
|
+
))
|
|
353
|
+
|
|
354
|
+
steps.append(CompiledStep(
|
|
355
|
+
tool="get_track_meters",
|
|
356
|
+
params={"include_stereo": True},
|
|
357
|
+
description="Verify one-shot accent triggers cleanly",
|
|
358
|
+
))
|
|
359
|
+
|
|
360
|
+
return CompiledPlan(
|
|
361
|
+
move_id=move.move_id,
|
|
362
|
+
intent=move.intent,
|
|
363
|
+
steps=steps,
|
|
364
|
+
risk_level="low",
|
|
365
|
+
summary="; ".join(descriptions) if descriptions else "One-shot accent",
|
|
366
|
+
requires_approval=(kernel.get("mode", "improve") != "explore"),
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
# ── Register ────────────────────────────────────────────────────────────────
|
|
371
|
+
|
|
372
|
+
register_compiler("sample_chop_rhythm", _compile_sample_chop_rhythm)
|
|
373
|
+
register_compiler("sample_texture_layer", _compile_sample_texture_layer)
|
|
374
|
+
register_compiler("sample_vocal_ghost", _compile_sample_vocal_ghost)
|
|
375
|
+
register_compiler("sample_break_layer", _compile_sample_break_layer)
|
|
376
|
+
register_compiler("sample_resample_destroy", _compile_sample_resample_destroy)
|
|
377
|
+
register_compiler("sample_one_shot_accent", _compile_sample_one_shot_accent)
|
|
@@ -13,7 +13,7 @@ ADD_WARMTH = SemanticMove(
|
|
|
13
13
|
targets={"warmth": 0.5, "depth": 0.3, "cohesion": 0.2},
|
|
14
14
|
protect={"clarity": 0.6, "punch": 0.5},
|
|
15
15
|
risk_level="low",
|
|
16
|
-
|
|
16
|
+
plan_template=[
|
|
17
17
|
{"tool": "set_device_parameter", "params": {"description": "Add Saturator drive +2-4dB for harmonic warmth"}, "description": "Add saturation", "backend": "remote_command"},
|
|
18
18
|
{"tool": "set_device_parameter", "params": {"description": "Boost EQ low-mid shelf +1-2dB"}, "description": "Low-mid warmth", "backend": "remote_command"},
|
|
19
19
|
],
|
|
@@ -30,7 +30,7 @@ ADD_TEXTURE = SemanticMove(
|
|
|
30
30
|
targets={"motion": 0.4, "novelty": 0.3, "depth": 0.3},
|
|
31
31
|
protect={"clarity": 0.6},
|
|
32
32
|
risk_level="medium",
|
|
33
|
-
|
|
33
|
+
plan_template=[
|
|
34
34
|
{"tool": "apply_automation_shape", "params": {"curve_type": "perlin", "description": "Perlin noise on filter cutoff for organic texture"}, "description": "Organic filter motion", "backend": "mcp_tool"},
|
|
35
35
|
{"tool": "set_track_send", "params": {"description": "Increase delay send for spatial texture"}, "description": "Spatial texture via delay", "backend": "remote_command"},
|
|
36
36
|
],
|
|
@@ -46,7 +46,7 @@ SHAPE_TRANSIENTS = SemanticMove(
|
|
|
46
46
|
targets={"punch": 0.5, "clarity": 0.3, "groove": 0.2},
|
|
47
47
|
protect={"warmth": 0.5},
|
|
48
48
|
risk_level="low",
|
|
49
|
-
|
|
49
|
+
plan_template=[
|
|
50
50
|
{"tool": "set_device_parameter", "params": {"description": "Adjust Compressor attack time (faster = sharper transients, slower = rounder)"}, "description": "Shape attack", "backend": "remote_command"},
|
|
51
51
|
{"tool": "set_device_parameter", "params": {"description": "Adjust Compressor release for rhythmic pumping"}, "description": "Shape release", "backend": "remote_command"},
|
|
52
52
|
],
|
|
@@ -62,7 +62,7 @@ ADD_SPACE = SemanticMove(
|
|
|
62
62
|
targets={"depth": 0.5, "width": 0.3, "clarity": 0.2},
|
|
63
63
|
protect={"punch": 0.6, "clarity": 0.5},
|
|
64
64
|
risk_level="low",
|
|
65
|
-
|
|
65
|
+
plan_template=[
|
|
66
66
|
{"tool": "set_track_send", "params": {"description": "Increase reverb send to 25-35%"}, "description": "Add reverb depth", "backend": "remote_command"},
|
|
67
67
|
{"tool": "set_track_send", "params": {"description": "Add subtle delay send 10-15%"}, "description": "Add delay texture", "backend": "remote_command"},
|
|
68
68
|
{"tool": "set_track_pan", "params": {"description": "Widen pan slightly for spatial presence"}, "description": "Widen spatial field", "backend": "remote_command"},
|
|
@@ -27,13 +27,15 @@ def list_semantic_moves(
|
|
|
27
27
|
Semantic moves express WHAT to achieve musically, not HOW parametrically.
|
|
28
28
|
Each move compiles into a sequence of existing deterministic tools.
|
|
29
29
|
|
|
30
|
-
domain: filter by family (mix, arrangement, transition, sound_design, performance)
|
|
30
|
+
domain: filter by family (e.g. mix, arrangement, transition, sound_design, sample, performance)
|
|
31
31
|
style: filter by genre/style (reserved for future use)
|
|
32
32
|
|
|
33
33
|
Returns: list of moves with move_id, family, intent, targets, risk_level.
|
|
34
34
|
"""
|
|
35
35
|
moves = registry.list_moves(domain=domain, style=style)
|
|
36
|
-
|
|
36
|
+
all_moves = registry.list_moves()
|
|
37
|
+
domains = sorted({m.get("family", "") for m in all_moves if m.get("family")})
|
|
38
|
+
return {"moves": moves, "count": len(moves), "available_domains": domains}
|
|
37
39
|
|
|
38
40
|
|
|
39
41
|
@mcp.tool()
|
|
@@ -43,9 +45,13 @@ def preview_semantic_move(
|
|
|
43
45
|
) -> dict:
|
|
44
46
|
"""Preview what a semantic move will do before applying it.
|
|
45
47
|
|
|
46
|
-
Returns the
|
|
47
|
-
|
|
48
|
-
the
|
|
48
|
+
Returns the static plan_template + verification_plans, PLUS an additive
|
|
49
|
+
compiled_plan field built by compiling the move against a lightweight
|
|
50
|
+
kernel of the current session. Use compiled_plan to inspect the concrete
|
|
51
|
+
tool calls the move would emit right now; use plan_template to understand
|
|
52
|
+
the move's shape independent of session state.
|
|
53
|
+
|
|
54
|
+
Existing callers reading plan_template are unaffected by the addition.
|
|
49
55
|
"""
|
|
50
56
|
move = registry.get_move(move_id)
|
|
51
57
|
if not move:
|
|
@@ -55,7 +61,46 @@ def preview_semantic_move(
|
|
|
55
61
|
"available_moves": available,
|
|
56
62
|
}
|
|
57
63
|
|
|
58
|
-
|
|
64
|
+
result = move.to_full_dict()
|
|
65
|
+
|
|
66
|
+
# Additive: compile against a lightweight kernel so callers get an
|
|
67
|
+
# executable representation alongside the static plan_template.
|
|
68
|
+
try:
|
|
69
|
+
from ..runtime.session_kernel import build_session_kernel
|
|
70
|
+
from ..runtime.capability_state import build_capability_state
|
|
71
|
+
from . import compiler as move_compiler
|
|
72
|
+
|
|
73
|
+
ableton = None
|
|
74
|
+
if hasattr(ctx, "lifespan_context"):
|
|
75
|
+
ableton = ctx.lifespan_context.get("ableton")
|
|
76
|
+
|
|
77
|
+
session_info: dict = {}
|
|
78
|
+
if ableton is not None:
|
|
79
|
+
try:
|
|
80
|
+
info = ableton.send_command("get_session_info")
|
|
81
|
+
if isinstance(info, dict):
|
|
82
|
+
session_info = info
|
|
83
|
+
except Exception:
|
|
84
|
+
session_info = {}
|
|
85
|
+
|
|
86
|
+
state = build_capability_state(
|
|
87
|
+
session_ok=bool(session_info),
|
|
88
|
+
analyzer_ok=False,
|
|
89
|
+
memory_ok=True,
|
|
90
|
+
)
|
|
91
|
+
kernel = build_session_kernel(
|
|
92
|
+
session_info=session_info,
|
|
93
|
+
capability_state=state.to_dict(),
|
|
94
|
+
)
|
|
95
|
+
plan = move_compiler.compile(move, kernel.to_dict())
|
|
96
|
+
result["compiled_plan"] = plan.to_dict()
|
|
97
|
+
result["compiled_plan_executable"] = bool(plan.executable)
|
|
98
|
+
except Exception as e:
|
|
99
|
+
result["compiled_plan"] = None
|
|
100
|
+
result["compiled_plan_executable"] = False
|
|
101
|
+
result["compiled_plan_error"] = str(e)
|
|
102
|
+
|
|
103
|
+
return result
|
|
59
104
|
|
|
60
105
|
|
|
61
106
|
@mcp.tool()
|
|
@@ -124,7 +169,7 @@ def propose_next_best_move(
|
|
|
124
169
|
|
|
125
170
|
|
|
126
171
|
@mcp.tool()
|
|
127
|
-
def apply_semantic_move(
|
|
172
|
+
async def apply_semantic_move(
|
|
128
173
|
ctx: Context,
|
|
129
174
|
move_id: str,
|
|
130
175
|
mode: str = "improve",
|
|
@@ -177,14 +222,22 @@ def apply_semantic_move(
|
|
|
177
222
|
result["note"] = "Awaiting approval — present the plan to the user, then execute steps individually"
|
|
178
223
|
return result
|
|
179
224
|
|
|
180
|
-
# explore mode — execute through
|
|
181
|
-
from ..runtime.execution_router import
|
|
225
|
+
# explore mode — execute through the async router
|
|
226
|
+
from ..runtime.execution_router import execute_plan_steps_async
|
|
182
227
|
|
|
183
228
|
step_dicts = [
|
|
184
229
|
{"tool": step.tool, "params": step.params, "description": step.description}
|
|
185
230
|
for step in plan.steps
|
|
186
231
|
]
|
|
187
|
-
|
|
232
|
+
bridge = ctx.lifespan_context.get("m4l")
|
|
233
|
+
mcp_registry = ctx.lifespan_context.get("mcp_dispatch", {})
|
|
234
|
+
exec_results = await execute_plan_steps_async(
|
|
235
|
+
step_dicts,
|
|
236
|
+
ableton=ableton,
|
|
237
|
+
bridge=bridge,
|
|
238
|
+
mcp_registry=mcp_registry,
|
|
239
|
+
ctx=ctx,
|
|
240
|
+
)
|
|
188
241
|
|
|
189
242
|
executed_steps = []
|
|
190
243
|
for i, er in enumerate(exec_results):
|
|
@@ -10,7 +10,7 @@ INCREASE_FORWARD_MOTION = SemanticMove(
|
|
|
10
10
|
targets={"motion": 0.5, "energy": 0.3, "tension": 0.2},
|
|
11
11
|
protect={"clarity": 0.6},
|
|
12
12
|
risk_level="low",
|
|
13
|
-
|
|
13
|
+
plan_template=[
|
|
14
14
|
{"tool": "apply_automation_shape", "params": {"curve_type": "exponential", "description": "Rising filter cutoff over 4 bars"}, "description": "Rising filter sweep", "backend": "mcp_tool"},
|
|
15
15
|
{"tool": "set_track_volume", "params": {"description": "Push rhythm elements +5-8%"}, "description": "Push rhythm forward", "backend": "remote_command"},
|
|
16
16
|
{"tool": "apply_automation_shape", "params": {"curve_type": "linear", "description": "Rising reverb send for anticipation"}, "description": "Build reverb wash", "backend": "mcp_tool"},
|
|
@@ -27,7 +27,7 @@ OPEN_CHORUS = SemanticMove(
|
|
|
27
27
|
targets={"energy": 0.4, "width": 0.3, "contrast": 0.3},
|
|
28
28
|
protect={"clarity": 0.6, "cohesion": 0.5},
|
|
29
29
|
risk_level="medium",
|
|
30
|
-
|
|
30
|
+
plan_template=[
|
|
31
31
|
{"tool": "set_track_volume", "params": {"description": "Push all melodic tracks +10-15%"}, "description": "Push chorus energy", "backend": "remote_command"},
|
|
32
32
|
{"tool": "set_track_pan", "params": {"description": "Widen stereo field on chords/pads"}, "description": "Widen stereo", "backend": "remote_command"},
|
|
33
33
|
{"tool": "set_track_send", "params": {"description": "Increase reverb/delay sends for spaciousness"}, "description": "Add space", "backend": "remote_command"},
|
|
@@ -45,7 +45,7 @@ CREATE_BREAKDOWN = SemanticMove(
|
|
|
45
45
|
targets={"contrast": 0.5, "depth": 0.3, "clarity": 0.2},
|
|
46
46
|
protect={"cohesion": 0.5},
|
|
47
47
|
risk_level="medium",
|
|
48
|
-
|
|
48
|
+
plan_template=[
|
|
49
49
|
{"tool": "set_track_volume", "params": {"description": "Pull drums to 20-30%"}, "description": "Strip drums", "backend": "remote_command"},
|
|
50
50
|
{"tool": "set_track_volume", "params": {"description": "Pull bass to 30-40%"}, "description": "Reduce bass", "backend": "remote_command"},
|
|
51
51
|
{"tool": "set_track_send", "params": {"description": "Increase reverb send on remaining elements"}, "description": "Add reverb depth", "backend": "remote_command"},
|
|
@@ -62,7 +62,7 @@ BRIDGE_SECTIONS = SemanticMove(
|
|
|
62
62
|
targets={"motion": 0.4, "contrast": 0.3, "cohesion": 0.3},
|
|
63
63
|
protect={"clarity": 0.6},
|
|
64
64
|
risk_level="low",
|
|
65
|
-
|
|
65
|
+
plan_template=[
|
|
66
66
|
{"tool": "apply_automation_shape", "params": {"curve_type": "cosine", "description": "Gentle filter sweep across bridge"}, "description": "Bridge filter motion", "backend": "mcp_tool"},
|
|
67
67
|
{"tool": "set_track_volume", "params": {"description": "Gentle volume crossfade between section elements"}, "description": "Crossfade elements", "backend": "remote_command"},
|
|
68
68
|
],
|