@mthines/reaper-mcp 0.9.1-beta.10.1 → 0.10.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/package.json +1 -1
- package/reaper/mcp_bridge.lua +82 -8
package/package.json
CHANGED
package/reaper/mcp_bridge.lua
CHANGED
|
@@ -413,6 +413,8 @@ function handlers.set_track_property(params)
|
|
|
413
413
|
local track = reaper.GetTrack(0, idx)
|
|
414
414
|
if not track then return nil, "Track " .. idx .. " not found" end
|
|
415
415
|
|
|
416
|
+
reaper.Undo_BeginBlock()
|
|
417
|
+
|
|
416
418
|
if prop == "volume" then
|
|
417
419
|
reaper.SetMediaTrackInfo_Value(track, "D_VOL", from_db(value))
|
|
418
420
|
elseif prop == "pan" then
|
|
@@ -422,9 +424,11 @@ function handlers.set_track_property(params)
|
|
|
422
424
|
elseif prop == "solo" then
|
|
423
425
|
reaper.SetMediaTrackInfo_Value(track, "I_SOLO", value)
|
|
424
426
|
else
|
|
427
|
+
reaper.Undo_EndBlock("MCP: Set track property", -1)
|
|
425
428
|
return nil, "Unknown property: " .. tostring(prop)
|
|
426
429
|
end
|
|
427
430
|
|
|
431
|
+
reaper.Undo_EndBlock("MCP: Set track " .. prop, -1)
|
|
428
432
|
return { success = true, trackIndex = idx, property = prop, value = value }
|
|
429
433
|
end
|
|
430
434
|
|
|
@@ -439,12 +443,16 @@ function handlers.add_fx(params)
|
|
|
439
443
|
if not track then return nil, "Track " .. idx .. " not found" end
|
|
440
444
|
|
|
441
445
|
local position = params.position or -1
|
|
446
|
+
|
|
447
|
+
reaper.Undo_BeginBlock()
|
|
442
448
|
local fx_idx = reaper.TrackFX_AddByName(track, fx_name, false, position)
|
|
443
449
|
if fx_idx < 0 then
|
|
450
|
+
reaper.Undo_EndBlock("MCP: Add FX (failed)", -1)
|
|
444
451
|
return nil, "FX not found: " .. fx_name
|
|
445
452
|
end
|
|
446
453
|
|
|
447
454
|
local _, actual_name = reaper.TrackFX_GetFXName(track, fx_idx)
|
|
455
|
+
reaper.Undo_EndBlock("MCP: Add FX '" .. (actual_name or fx_name) .. "' to track " .. idx, -1)
|
|
448
456
|
return {
|
|
449
457
|
fxIndex = fx_idx,
|
|
450
458
|
fxName = actual_name,
|
|
@@ -462,11 +470,14 @@ function handlers.remove_fx(params)
|
|
|
462
470
|
local track = reaper.GetTrack(0, idx)
|
|
463
471
|
if not track then return nil, "Track " .. idx .. " not found" end
|
|
464
472
|
|
|
473
|
+
reaper.Undo_BeginBlock()
|
|
465
474
|
local ok = reaper.TrackFX_Delete(track, fx_idx)
|
|
466
475
|
if not ok then
|
|
476
|
+
reaper.Undo_EndBlock("MCP: Remove FX (failed)", -1)
|
|
467
477
|
return nil, "Failed to remove FX " .. fx_idx .. " from track " .. idx
|
|
468
478
|
end
|
|
469
479
|
|
|
480
|
+
reaper.Undo_EndBlock("MCP: Remove FX " .. fx_idx .. " from track " .. idx, -1)
|
|
470
481
|
return { success = true, trackIndex = idx, fxIndex = fx_idx }
|
|
471
482
|
end
|
|
472
483
|
|
|
@@ -518,11 +529,14 @@ function handlers.set_fx_parameter(params)
|
|
|
518
529
|
local track = reaper.GetTrack(0, idx)
|
|
519
530
|
if not track then return nil, "Track " .. idx .. " not found" end
|
|
520
531
|
|
|
532
|
+
reaper.Undo_BeginBlock()
|
|
521
533
|
local ok = reaper.TrackFX_SetParam(track, fx_idx, param_idx, value)
|
|
522
534
|
if not ok then
|
|
535
|
+
reaper.Undo_EndBlock("MCP: Set FX parameter (failed)", -1)
|
|
523
536
|
return nil, "Failed to set param " .. param_idx .. " on FX " .. fx_idx
|
|
524
537
|
end
|
|
525
538
|
|
|
539
|
+
reaper.Undo_EndBlock("MCP: Set FX parameter", -1)
|
|
526
540
|
return { success = true, trackIndex = idx, fxIndex = fx_idx, paramIndex = param_idx, value = value }
|
|
527
541
|
end
|
|
528
542
|
|
|
@@ -749,8 +763,10 @@ function handlers.get_fx_preset_list(params)
|
|
|
749
763
|
|
|
750
764
|
-- TrackFX_GetPresetIndex returns current index and total count
|
|
751
765
|
-- We iterate by cycling through presets
|
|
766
|
+
-- Wrap in undo block to prevent intermediate preset changes from polluting undo history
|
|
752
767
|
local _, total = reaper.TrackFX_GetPresetIndex(track, fx_idx)
|
|
753
768
|
if total and total > 0 then
|
|
769
|
+
reaper.Undo_BeginBlock()
|
|
754
770
|
for i = 0, total - 1 do
|
|
755
771
|
reaper.TrackFX_SetPresetByIndex(track, fx_idx, i)
|
|
756
772
|
local _, preset_name = reaper.TrackFX_GetPreset(track, fx_idx)
|
|
@@ -758,6 +774,7 @@ function handlers.get_fx_preset_list(params)
|
|
|
758
774
|
end
|
|
759
775
|
-- Restore original preset
|
|
760
776
|
reaper.TrackFX_SetPresetByIndex(track, fx_idx, preset_count)
|
|
777
|
+
reaper.Undo_EndBlock("MCP: Get FX preset list", -1)
|
|
761
778
|
else
|
|
762
779
|
-- Plugin doesn't report preset count; return current preset only
|
|
763
780
|
local _, current_preset = reaper.TrackFX_GetPreset(track, fx_idx)
|
|
@@ -781,12 +798,15 @@ function handlers.set_fx_preset(params)
|
|
|
781
798
|
if not track then return nil, "Track " .. track_idx .. " not found" end
|
|
782
799
|
|
|
783
800
|
-- TrackFX_SetPreset returns true on success
|
|
801
|
+
reaper.Undo_BeginBlock()
|
|
784
802
|
local ok = reaper.TrackFX_SetPreset(track, fx_idx, preset_name)
|
|
785
803
|
if not ok then
|
|
804
|
+
reaper.Undo_EndBlock("MCP: Set FX preset (failed)", -1)
|
|
786
805
|
return nil, "Preset not found: " .. preset_name
|
|
787
806
|
end
|
|
788
807
|
|
|
789
808
|
local _, applied = reaper.TrackFX_GetPreset(track, fx_idx)
|
|
809
|
+
reaper.Undo_EndBlock("MCP: Set FX preset '" .. (applied or preset_name) .. "'", -1)
|
|
790
810
|
return { success = true, trackIndex = track_idx, fxIndex = fx_idx, presetName = applied or preset_name }
|
|
791
811
|
end
|
|
792
812
|
|
|
@@ -884,6 +904,9 @@ function handlers.snapshot_restore(params)
|
|
|
884
904
|
-- Restore each track state
|
|
885
905
|
local restored = 0
|
|
886
906
|
local state = snapshot.mixerState
|
|
907
|
+
|
|
908
|
+
reaper.Undo_BeginBlock()
|
|
909
|
+
|
|
887
910
|
if state.tracks then
|
|
888
911
|
for _, track_state in ipairs(state.tracks) do
|
|
889
912
|
local track = reaper.GetTrack(0, track_state.index)
|
|
@@ -907,6 +930,8 @@ function handlers.snapshot_restore(params)
|
|
|
907
930
|
end
|
|
908
931
|
end
|
|
909
932
|
|
|
933
|
+
reaper.Undo_EndBlock("MCP: Restore snapshot '" .. name .. "'", -1)
|
|
934
|
+
|
|
910
935
|
reaper.TrackList_AdjustWindows(false)
|
|
911
936
|
reaper.UpdateArrange()
|
|
912
937
|
|
|
@@ -2157,7 +2182,9 @@ function handlers.set_fx_enabled(params)
|
|
|
2157
2182
|
if params.fxIndex >= fx_count then
|
|
2158
2183
|
return nil, "FX index " .. params.fxIndex .. " out of range (track has " .. fx_count .. " FX)"
|
|
2159
2184
|
end
|
|
2185
|
+
reaper.Undo_BeginBlock()
|
|
2160
2186
|
reaper.TrackFX_SetEnabled(track, params.fxIndex, params.enabled == 1)
|
|
2187
|
+
reaper.Undo_EndBlock(params.enabled == 1 and "MCP: Enable FX" or "MCP: Disable FX", -1)
|
|
2161
2188
|
return { trackIndex = params.trackIndex, fxIndex = params.fxIndex, enabled = params.enabled == 1 }
|
|
2162
2189
|
end
|
|
2163
2190
|
|
|
@@ -2168,7 +2195,9 @@ function handlers.set_fx_offline(params)
|
|
|
2168
2195
|
if params.fxIndex >= fx_count then
|
|
2169
2196
|
return nil, "FX index " .. params.fxIndex .. " out of range (track has " .. fx_count .. " FX)"
|
|
2170
2197
|
end
|
|
2198
|
+
reaper.Undo_BeginBlock()
|
|
2171
2199
|
reaper.TrackFX_SetOffline(track, params.fxIndex, params.offline == 1)
|
|
2200
|
+
reaper.Undo_EndBlock(params.offline == 1 and "MCP: Set FX offline" or "MCP: Set FX online", -1)
|
|
2172
2201
|
return { trackIndex = params.trackIndex, fxIndex = params.fxIndex, offline = params.offline == 1 }
|
|
2173
2202
|
end
|
|
2174
2203
|
|
|
@@ -2201,7 +2230,9 @@ end
|
|
|
2201
2230
|
function handlers.set_time_selection(params)
|
|
2202
2231
|
if params.start == nil then return nil, "start required" end
|
|
2203
2232
|
if params["end"] == nil then return nil, "end required" end
|
|
2233
|
+
reaper.Undo_BeginBlock()
|
|
2204
2234
|
reaper.GetSet_LoopTimeRange(true, false, params.start, params["end"], false)
|
|
2235
|
+
reaper.Undo_EndBlock("MCP: Set time selection", -1)
|
|
2205
2236
|
return { start = params.start, ["end"] = params["end"] }
|
|
2206
2237
|
end
|
|
2207
2238
|
|
|
@@ -2251,8 +2282,13 @@ function handlers.add_marker(params)
|
|
|
2251
2282
|
if params.position == nil then return nil, "position required" end
|
|
2252
2283
|
local color = params.color or 0
|
|
2253
2284
|
local name = params.name or ""
|
|
2285
|
+
reaper.Undo_BeginBlock()
|
|
2254
2286
|
local idx = reaper.AddProjectMarker2(0, false, params.position, 0, name, -1, color)
|
|
2255
|
-
if idx < 0 then
|
|
2287
|
+
if idx < 0 then
|
|
2288
|
+
reaper.Undo_EndBlock("MCP: Add marker (failed)", -1)
|
|
2289
|
+
return nil, "Failed to add marker"
|
|
2290
|
+
end
|
|
2291
|
+
reaper.Undo_EndBlock("MCP: Add marker", -1)
|
|
2256
2292
|
return { index = idx, position = params.position, name = name }
|
|
2257
2293
|
end
|
|
2258
2294
|
|
|
@@ -2261,23 +2297,38 @@ function handlers.add_region(params)
|
|
|
2261
2297
|
if params["end"] == nil then return nil, "end required" end
|
|
2262
2298
|
local color = params.color or 0
|
|
2263
2299
|
local name = params.name or ""
|
|
2300
|
+
reaper.Undo_BeginBlock()
|
|
2264
2301
|
local idx = reaper.AddProjectMarker2(0, true, params.start, params["end"], name, -1, color)
|
|
2265
|
-
if idx < 0 then
|
|
2302
|
+
if idx < 0 then
|
|
2303
|
+
reaper.Undo_EndBlock("MCP: Add region (failed)", -1)
|
|
2304
|
+
return nil, "Failed to add region"
|
|
2305
|
+
end
|
|
2306
|
+
reaper.Undo_EndBlock("MCP: Add region", -1)
|
|
2266
2307
|
return { index = idx, start = params.start, ["end"] = params["end"], name = name }
|
|
2267
2308
|
end
|
|
2268
2309
|
|
|
2269
2310
|
function handlers.delete_marker(params)
|
|
2270
2311
|
if params.markerIndex == nil then return nil, "markerIndex required" end
|
|
2312
|
+
reaper.Undo_BeginBlock()
|
|
2271
2313
|
-- DeleteProjectMarker takes (proj, isrgn, markrgnindexnumber)
|
|
2272
2314
|
local deleted = reaper.DeleteProjectMarker(0, false, params.markerIndex, false)
|
|
2273
|
-
if not deleted then
|
|
2315
|
+
if not deleted then
|
|
2316
|
+
reaper.Undo_EndBlock("MCP: Delete marker (failed)", -1)
|
|
2317
|
+
return nil, "Marker " .. params.markerIndex .. " not found"
|
|
2318
|
+
end
|
|
2319
|
+
reaper.Undo_EndBlock("MCP: Delete marker", -1)
|
|
2274
2320
|
return { success = true }
|
|
2275
2321
|
end
|
|
2276
2322
|
|
|
2277
2323
|
function handlers.delete_region(params)
|
|
2278
2324
|
if params.regionIndex == nil then return nil, "regionIndex required" end
|
|
2325
|
+
reaper.Undo_BeginBlock()
|
|
2279
2326
|
local deleted = reaper.DeleteProjectMarker(0, true, params.regionIndex, false)
|
|
2280
|
-
if not deleted then
|
|
2327
|
+
if not deleted then
|
|
2328
|
+
reaper.Undo_EndBlock("MCP: Delete region (failed)", -1)
|
|
2329
|
+
return nil, "Region " .. params.regionIndex .. " not found"
|
|
2330
|
+
end
|
|
2331
|
+
reaper.Undo_EndBlock("MCP: Delete region", -1)
|
|
2281
2332
|
return { success = true }
|
|
2282
2333
|
end
|
|
2283
2334
|
|
|
@@ -2391,8 +2442,10 @@ function handlers.insert_envelope_point(params)
|
|
|
2391
2442
|
local env = reaper.GetTrackEnvelope(track, params.envelopeIndex)
|
|
2392
2443
|
local shape = params.shape or 0
|
|
2393
2444
|
local tension = params.tension or 0
|
|
2445
|
+
reaper.Undo_BeginBlock()
|
|
2394
2446
|
reaper.InsertEnvelopePoint(env, params.time, params.value, shape, tension, false, true)
|
|
2395
2447
|
reaper.Envelope_SortPoints(env)
|
|
2448
|
+
reaper.Undo_EndBlock("MCP: Insert envelope point", -1)
|
|
2396
2449
|
local total = reaper.CountEnvelopePoints(env)
|
|
2397
2450
|
return {
|
|
2398
2451
|
trackIndex = params.trackIndex,
|
|
@@ -2417,7 +2470,9 @@ function handlers.delete_envelope_point(params)
|
|
|
2417
2470
|
if params.pointIndex >= total then
|
|
2418
2471
|
return nil, "Point index " .. params.pointIndex .. " out of range (envelope has " .. total .. " points)"
|
|
2419
2472
|
end
|
|
2473
|
+
reaper.Undo_BeginBlock()
|
|
2420
2474
|
reaper.DeleteEnvelopePointRange(env, params.pointIndex, params.pointIndex + 1)
|
|
2475
|
+
reaper.Undo_EndBlock("MCP: Delete envelope point", -1)
|
|
2421
2476
|
return { success = true, totalPoints = reaper.CountEnvelopePoints(env) }
|
|
2422
2477
|
end
|
|
2423
2478
|
|
|
@@ -2425,6 +2480,8 @@ function handlers.create_track_envelope(params)
|
|
|
2425
2480
|
local track = reaper.GetTrack(0, params.trackIndex)
|
|
2426
2481
|
if not track then return nil, "Track " .. params.trackIndex .. " not found" end
|
|
2427
2482
|
|
|
2483
|
+
reaper.Undo_BeginBlock()
|
|
2484
|
+
|
|
2428
2485
|
local env = nil
|
|
2429
2486
|
local env_name = nil
|
|
2430
2487
|
|
|
@@ -2506,6 +2563,8 @@ function handlers.create_track_envelope(params)
|
|
|
2506
2563
|
end
|
|
2507
2564
|
end
|
|
2508
2565
|
|
|
2566
|
+
reaper.Undo_EndBlock("MCP: Create envelope '" .. (env_name or "") .. "'", -1)
|
|
2567
|
+
reaper.MarkProjectDirty(0)
|
|
2509
2568
|
reaper.TrackList_AdjustWindows(false)
|
|
2510
2569
|
reaper.UpdateArrange()
|
|
2511
2570
|
|
|
@@ -2526,6 +2585,8 @@ function handlers.set_envelope_properties(params)
|
|
|
2526
2585
|
end
|
|
2527
2586
|
local env = reaper.GetTrackEnvelope(track, params.envelopeIndex)
|
|
2528
2587
|
|
|
2588
|
+
reaper.Undo_BeginBlock()
|
|
2589
|
+
|
|
2529
2590
|
if reaper.BR_EnvAlloc then
|
|
2530
2591
|
-- SWS extension available: use BR_Env* functions
|
|
2531
2592
|
local br_env = reaper.BR_EnvAlloc(env, false)
|
|
@@ -2553,6 +2614,8 @@ function handlers.set_envelope_properties(params)
|
|
|
2553
2614
|
reaper.SetEnvelopeStateChunk(env, chunk, false)
|
|
2554
2615
|
end
|
|
2555
2616
|
|
|
2617
|
+
reaper.Undo_EndBlock("MCP: Set envelope properties", -1)
|
|
2618
|
+
|
|
2556
2619
|
reaper.TrackList_AdjustWindows(false)
|
|
2557
2620
|
reaper.UpdateArrange()
|
|
2558
2621
|
|
|
@@ -2584,8 +2647,10 @@ function handlers.clear_envelope(params)
|
|
|
2584
2647
|
local env = reaper.GetTrackEnvelope(track, params.envelopeIndex)
|
|
2585
2648
|
local prev_count = reaper.CountEnvelopePoints(env)
|
|
2586
2649
|
-- Delete all points by using a range from -infinity to +infinity
|
|
2650
|
+
reaper.Undo_BeginBlock()
|
|
2587
2651
|
reaper.DeleteEnvelopePointRange(env, -math.huge, math.huge)
|
|
2588
2652
|
reaper.Envelope_SortPoints(env)
|
|
2653
|
+
reaper.Undo_EndBlock("MCP: Clear envelope", -1)
|
|
2589
2654
|
return {
|
|
2590
2655
|
trackIndex = params.trackIndex,
|
|
2591
2656
|
envelopeIndex = params.envelopeIndex,
|
|
@@ -2603,8 +2668,10 @@ function handlers.remove_envelope_points(params)
|
|
|
2603
2668
|
end
|
|
2604
2669
|
local env = reaper.GetTrackEnvelope(track, params.envelopeIndex)
|
|
2605
2670
|
local prev_count = reaper.CountEnvelopePoints(env)
|
|
2671
|
+
reaper.Undo_BeginBlock()
|
|
2606
2672
|
reaper.DeleteEnvelopePointRange(env, params.timeStart, params.timeEnd)
|
|
2607
2673
|
reaper.Envelope_SortPoints(env)
|
|
2674
|
+
reaper.Undo_EndBlock("MCP: Remove envelope points", -1)
|
|
2608
2675
|
local new_count = reaper.CountEnvelopePoints(env)
|
|
2609
2676
|
return {
|
|
2610
2677
|
trackIndex = params.trackIndex,
|
|
@@ -2636,6 +2703,7 @@ function handlers.insert_envelope_points(params)
|
|
|
2636
2703
|
if not points then return nil, "Failed to parse points JSON" end
|
|
2637
2704
|
|
|
2638
2705
|
local inserted = 0
|
|
2706
|
+
reaper.Undo_BeginBlock()
|
|
2639
2707
|
for _, pt in ipairs(points) do
|
|
2640
2708
|
if pt.time and pt.value then
|
|
2641
2709
|
local shape = pt.shape or 0
|
|
@@ -2646,6 +2714,7 @@ function handlers.insert_envelope_points(params)
|
|
|
2646
2714
|
end
|
|
2647
2715
|
|
|
2648
2716
|
reaper.Envelope_SortPoints(env)
|
|
2717
|
+
reaper.Undo_EndBlock("MCP: Insert " .. inserted .. " envelope points", -1)
|
|
2649
2718
|
return {
|
|
2650
2719
|
trackIndex = params.trackIndex,
|
|
2651
2720
|
envelopeIndex = params.envelopeIndex,
|
|
@@ -2721,11 +2790,16 @@ local function process_command(filename)
|
|
|
2721
2790
|
local response = {}
|
|
2722
2791
|
|
|
2723
2792
|
if handler then
|
|
2724
|
-
local
|
|
2725
|
-
if
|
|
2726
|
-
|
|
2793
|
+
local ok, data_or_err, err_msg = pcall(handler, cmd.params or {})
|
|
2794
|
+
if not ok then
|
|
2795
|
+
-- pcall caught a Lua runtime error — handler crashed
|
|
2796
|
+
response = { id = cmd.id, success = false, error = "Handler error: " .. tostring(data_or_err), timestamp = os.time() * 1000 }
|
|
2797
|
+
elseif err_msg then
|
|
2798
|
+
-- Handler returned (nil, errorString)
|
|
2799
|
+
response = { id = cmd.id, success = false, error = err_msg, timestamp = os.time() * 1000 }
|
|
2727
2800
|
else
|
|
2728
|
-
|
|
2801
|
+
-- Handler returned (data, nil) — success
|
|
2802
|
+
response = { id = cmd.id, success = true, data = data_or_err, timestamp = os.time() * 1000 }
|
|
2729
2803
|
end
|
|
2730
2804
|
else
|
|
2731
2805
|
response = {
|