@mthines/reaper-mcp 0.14.0 → 0.14.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/package.json
CHANGED
package/reaper/mcp_bridge.lua
CHANGED
|
@@ -1000,8 +1000,25 @@ function handlers.snapshot_restore(params)
|
|
|
1000
1000
|
|
|
1001
1001
|
local restored = 0
|
|
1002
1002
|
|
|
1003
|
-
|
|
1004
|
-
|
|
1003
|
+
-- Decode tracks array if the fallback JSON parser left it as a raw string
|
|
1004
|
+
local tracks = state.tracks
|
|
1005
|
+
if type(tracks) == "string" then
|
|
1006
|
+
tracks = json_decode_array(tracks)
|
|
1007
|
+
end
|
|
1008
|
+
|
|
1009
|
+
if tracks then
|
|
1010
|
+
for _, track_state in ipairs(tracks) do
|
|
1011
|
+
-- Decode nested arrays that may also be raw strings from fallback parser
|
|
1012
|
+
if type(track_state.fx) == "string" then
|
|
1013
|
+
track_state.fx = json_decode_array(track_state.fx)
|
|
1014
|
+
end
|
|
1015
|
+
if type(track_state.sends) == "string" then
|
|
1016
|
+
track_state.sends = json_decode_array(track_state.sends)
|
|
1017
|
+
end
|
|
1018
|
+
if type(track_state.fxEnabled) == "string" then
|
|
1019
|
+
track_state.fxEnabled = json_decode_array(track_state.fxEnabled)
|
|
1020
|
+
end
|
|
1021
|
+
|
|
1005
1022
|
local track = reaper.GetTrack(0, track_state.index)
|
|
1006
1023
|
if track then
|
|
1007
1024
|
-- Basic mixer state (all versions)
|
|
@@ -1240,11 +1257,14 @@ function handlers.read_track_lufs(params)
|
|
|
1240
1257
|
local fx_idx, err = ensure_jsfx_on_track(track, MCP_LUFS_METER_FX_NAME)
|
|
1241
1258
|
if not fx_idx then return nil, err end
|
|
1242
1259
|
|
|
1243
|
-
-- Set the track_slot parameter
|
|
1244
|
-
|
|
1245
|
-
reaper.
|
|
1260
|
+
-- Set the track_slot parameter so this instance writes to a unique gmem offset
|
|
1261
|
+
local desired_slot = idx / 127
|
|
1262
|
+
local current_slot = reaper.TrackFX_GetParam(track, fx_idx, 1)
|
|
1263
|
+
if math.abs(current_slot - desired_slot) > 0.001 then
|
|
1264
|
+
reaper.TrackFX_SetParam(track, fx_idx, 1, desired_slot)
|
|
1265
|
+
end
|
|
1246
1266
|
|
|
1247
|
-
--
|
|
1267
|
+
-- Read from gmem (JSFX writes here from @sample)
|
|
1248
1268
|
reaper.gmem_attach("MCPLufsMeter")
|
|
1249
1269
|
|
|
1250
1270
|
local base = idx * 8
|
|
@@ -1277,6 +1297,7 @@ function handlers.read_track_correlation(params)
|
|
|
1277
1297
|
local fx_idx, err = ensure_jsfx_on_track(track, MCP_CORRELATION_METER_FX_NAME)
|
|
1278
1298
|
if not fx_idx then return nil, err end
|
|
1279
1299
|
|
|
1300
|
+
-- Read from gmem (JSFX writes here from @sample)
|
|
1280
1301
|
reaper.gmem_attach("MCPCorrelationMeter")
|
|
1281
1302
|
|
|
1282
1303
|
local correlation = reaper.gmem_read(0)
|
|
@@ -1303,6 +1324,7 @@ function handlers.read_track_crest(params)
|
|
|
1303
1324
|
local fx_idx, err = ensure_jsfx_on_track(track, MCP_CREST_FACTOR_FX_NAME)
|
|
1304
1325
|
if not fx_idx then return nil, err end
|
|
1305
1326
|
|
|
1327
|
+
-- Read from gmem (JSFX writes here from @sample)
|
|
1306
1328
|
reaper.gmem_attach("MCPCrestFactor")
|
|
1307
1329
|
|
|
1308
1330
|
local crest_factor = reaper.gmem_read(0)
|
|
@@ -2861,8 +2883,9 @@ function handlers.create_track_envelope(params)
|
|
|
2861
2883
|
return nil, "Unknown envelope name: " .. params.envelopeName .. ". Use Volume, Pan, Mute, Width, or Trim Volume"
|
|
2862
2884
|
end
|
|
2863
2885
|
-- Get track chunk, insert envelope chunk if missing
|
|
2886
|
+
-- Use anchored pattern "<VOLENV\n" to avoid matching VOLENV2 (Trim Volume)
|
|
2864
2887
|
local _, chunk = reaper.GetTrackStateChunk(track, "", false)
|
|
2865
|
-
if not chunk:find(chunk_key) then
|
|
2888
|
+
if not chunk:find("<" .. chunk_key .. "\n") then
|
|
2866
2889
|
-- Insert a minimal envelope chunk before the closing >
|
|
2867
2890
|
local env_chunk = "\n<" .. chunk_key .. "\nACT 1 -1\nVIS 1 1 1\nLANEHEIGHT 0 0\nARM 0\nDEFSHAPE 0 -1 -1\n>\n"
|
|
2868
2891
|
-- Use position capture to find the last ">" (closing the <TRACK block).
|
|
@@ -2874,8 +2897,8 @@ function handlers.create_track_envelope(params)
|
|
|
2874
2897
|
reaper.SetTrackStateChunk(track, chunk, false)
|
|
2875
2898
|
else
|
|
2876
2899
|
-- Envelope exists in chunk but may be hidden; make it visible
|
|
2877
|
-
chunk = chunk:gsub("(" .. chunk_key .. "[^\n]*\n)ACT 0", "%1ACT 1")
|
|
2878
|
-
chunk = chunk:gsub("(" .. chunk_key .. "[^\n]*\n[^\n]*\n)VIS 0", "%1VIS 1")
|
|
2900
|
+
chunk = chunk:gsub("(<" .. chunk_key .. "[^\n]*\n)ACT 0", "%1ACT 1")
|
|
2901
|
+
chunk = chunk:gsub("(<" .. chunk_key .. "[^\n]*\n[^\n]*\n)VIS 0", "%1VIS 1")
|
|
2879
2902
|
reaper.SetTrackStateChunk(track, chunk, false)
|
|
2880
2903
|
end
|
|
2881
2904
|
env = reaper.GetTrackEnvelopeByName(track, params.envelopeName)
|
|
@@ -58,7 +58,6 @@ slider1:window_ms=300<50,2000,10>Window (ms)
|
|
|
58
58
|
gmem[3] = -150;
|
|
59
59
|
);
|
|
60
60
|
|
|
61
|
-
local(l, r, m, s);
|
|
62
61
|
l = spl0;
|
|
63
62
|
r = spl1;
|
|
64
63
|
|
|
@@ -66,7 +65,6 @@ slider1:window_ms=300<50,2000,10>Window (ms)
|
|
|
66
65
|
m = (l + r) * 0.5;
|
|
67
66
|
s = (l - r) * 0.5;
|
|
68
67
|
|
|
69
|
-
local(ll, rr, lr, mm, ss);
|
|
70
68
|
ll = l * l;
|
|
71
69
|
rr = r * r;
|
|
72
70
|
lr = l * r;
|
|
@@ -99,8 +97,6 @@ slider1:window_ms=300<50,2000,10>Window (ms)
|
|
|
99
97
|
|
|
100
98
|
// Update gmem every 512 samples (~86 Hz at 44.1kHz)
|
|
101
99
|
(buf_pos % 512) == 0 ? (
|
|
102
|
-
local(denom, corr, mid_rms, side_rms, mid_db, side_db, width);
|
|
103
|
-
|
|
104
100
|
// Correlation: sum(L*R) / sqrt(sum(L*L) * sum(R*R))
|
|
105
101
|
denom = sqrt(sum_ll * sum_rr);
|
|
106
102
|
denom > 0.0000001 ? (
|
|
@@ -49,7 +49,6 @@ slider2:peak_hold_ms=1000<100,10000,100>Peak Hold (ms)
|
|
|
49
49
|
);
|
|
50
50
|
|
|
51
51
|
// Mono mix for metering
|
|
52
|
-
local(mono, sq, peak_abs);
|
|
53
52
|
mono = (spl0 + spl1) * 0.5;
|
|
54
53
|
sq = mono * mono;
|
|
55
54
|
peak_abs = abs(mono);
|
|
@@ -76,8 +75,6 @@ slider2:peak_hold_ms=1000<100,10000,100>Peak Hold (ms)
|
|
|
76
75
|
|
|
77
76
|
// Update gmem every 512 samples
|
|
78
77
|
(buf_pos % 512) == 0 ? (
|
|
79
|
-
local(rms_val, rms_db, peak_db, crest_db);
|
|
80
|
-
|
|
81
78
|
rms_val = sqrt(sum_sq / buf_size);
|
|
82
79
|
|
|
83
80
|
rms_val > 0.000001 ? (
|
|
@@ -21,29 +21,18 @@ slider2:track_slot=0<0,127,1>Track Slot
|
|
|
21
21
|
// K-weighting filter state (two biquad stages per channel)
|
|
22
22
|
// Stage 1: High-shelf pre-filter (+4 dB at 1681 Hz)
|
|
23
23
|
// Stage 2: High-pass RLB filter (100 Hz, -12 dB/oct)
|
|
24
|
-
|
|
25
|
-
// Biquad state: [b0, b1, b2, a1, a2, x1, x2, y1, y2] per stage per channel
|
|
26
|
-
// We store state variables in flat arrays
|
|
27
24
|
hs_x1l = hs_x2l = hs_y1l = hs_y2l = 0;
|
|
28
25
|
hs_x1r = hs_x2r = hs_y1r = hs_y2r = 0;
|
|
29
26
|
hp_x1l = hp_x2l = hp_y1l = hp_y2l = 0;
|
|
30
27
|
hp_x1r = hp_x2r = hp_y1r = hp_y2r = 0;
|
|
31
28
|
|
|
32
|
-
// High-shelf filter coefficients (
|
|
33
|
-
// Using bilinear transform of analog prototype per BS.1770-4 Annex 1
|
|
29
|
+
// High-shelf filter coefficients (recomputed on init and sample rate change)
|
|
34
30
|
hs_b0 = hs_b1 = hs_b2 = hs_a1 = hs_a2 = 0;
|
|
35
31
|
hp_b0 = hp_b1 = hp_b2 = hp_a1 = hp_a2 = 0;
|
|
36
32
|
|
|
37
|
-
//
|
|
38
|
-
// 400ms
|
|
39
|
-
|
|
40
|
-
// We approximate with per-sample accumulation into sliding windows
|
|
41
|
-
|
|
42
|
-
// Ring buffer for per-sample squared weighted values
|
|
43
|
-
// momentary: 400ms window
|
|
44
|
-
// short-term: 3s window
|
|
45
|
-
// We store up to 3s of samples in a circular buffer
|
|
46
|
-
buf_size = 0; // computed at runtime from srate
|
|
33
|
+
// Ring buffer for per-sample squared weighted values (up to 3s)
|
|
34
|
+
// momentary: 400ms window, short-term: 3s window
|
|
35
|
+
buf_size = 0;
|
|
47
36
|
buf_l = 65536; // start address for left channel ring buffer
|
|
48
37
|
buf_r = 131072; // start address for right channel ring buffer
|
|
49
38
|
buf_pos = 0;
|
|
@@ -63,13 +52,11 @@ slider2:track_slot=0<0,127,1>Track Slot
|
|
|
63
52
|
// Measurement duration
|
|
64
53
|
sample_count = 0;
|
|
65
54
|
|
|
66
|
-
// Flags
|
|
67
55
|
needs_init = 1;
|
|
68
56
|
|
|
69
57
|
function compute_kweight_filters() (
|
|
70
58
|
// High-shelf pre-filter: +4 dB shelf at f0 = 1681.974 Hz
|
|
71
59
|
// From BS.1770-4, Annex 1, Table 1
|
|
72
|
-
local(db, K, Vh, Vb, a0);
|
|
73
60
|
db = 3.99984385397; // ~4 dB
|
|
74
61
|
K = tan($pi * 1681.974 / srate);
|
|
75
62
|
Vh = exp(db / 20 * log(10));
|
|
@@ -82,7 +69,6 @@ slider2:track_slot=0<0,127,1>Track Slot
|
|
|
82
69
|
hs_a2 = (1 - K/0.7071067811865476 + K*K) / a0;
|
|
83
70
|
|
|
84
71
|
// High-pass filter: 38.13547 Hz, Q = 0.5003270373238773
|
|
85
|
-
local(K2, a0);
|
|
86
72
|
K2 = tan($pi * 38.13547 / srate);
|
|
87
73
|
a0 = K2*K2 + K2/0.5003270373238773 + 1;
|
|
88
74
|
hp_b0 = 1 / a0;
|
|
@@ -93,7 +79,6 @@ slider2:track_slot=0<0,127,1>Track Slot
|
|
|
93
79
|
);
|
|
94
80
|
|
|
95
81
|
function reset_measurement() (
|
|
96
|
-
local(base);
|
|
97
82
|
base = floor(slider2 + 0.5) * 8;
|
|
98
83
|
buf_pos = 0;
|
|
99
84
|
momentary_sum_l = 0;
|
|
@@ -121,7 +106,7 @@ slider2:track_slot=0<0,127,1>Track Slot
|
|
|
121
106
|
);
|
|
122
107
|
|
|
123
108
|
@slider
|
|
124
|
-
// Re-compute filter coefficients (
|
|
109
|
+
// Re-compute filter coefficients (handles sample rate changes)
|
|
125
110
|
needs_init = 1;
|
|
126
111
|
|
|
127
112
|
// Handle reset button
|
|
@@ -142,7 +127,6 @@ slider2:track_slot=0<0,127,1>Track Slot
|
|
|
142
127
|
);
|
|
143
128
|
|
|
144
129
|
// --- K-weighting: stage 1 (high-shelf) ---
|
|
145
|
-
local(wl, wr, kl, kr);
|
|
146
130
|
wl = spl0;
|
|
147
131
|
wr = spl1;
|
|
148
132
|
|
|
@@ -155,7 +139,6 @@ slider2:track_slot=0<0,127,1>Track Slot
|
|
|
155
139
|
hs_x2r = hs_x1r; hs_x1r = wr; hs_y2r = hs_y1r; hs_y1r = kr;
|
|
156
140
|
|
|
157
141
|
// --- K-weighting: stage 2 (high-pass) ---
|
|
158
|
-
local(fl, fr);
|
|
159
142
|
fl = hp_b0 * kl + hp_b1 * hp_x1l + hp_b2 * hp_x2l - hp_a1 * hp_y1l - hp_a2 * hp_y2l;
|
|
160
143
|
hp_x2l = hp_x1l; hp_x1l = kl; hp_y2l = hp_y1l; hp_y1l = fl;
|
|
161
144
|
|
|
@@ -163,13 +146,10 @@ slider2:track_slot=0<0,127,1>Track Slot
|
|
|
163
146
|
hp_x2r = hp_x1r; hp_x1r = kr; hp_y2r = hp_y1r; hp_y1r = fr;
|
|
164
147
|
|
|
165
148
|
// Squared weighted samples
|
|
166
|
-
local(sq_l, sq_r);
|
|
167
149
|
sq_l = fl * fl;
|
|
168
150
|
sq_r = fr * fr;
|
|
169
151
|
|
|
170
152
|
// --- Ring buffer update ---
|
|
171
|
-
// Subtract outgoing samples from running sums
|
|
172
|
-
local(old_l, old_r);
|
|
173
153
|
old_l = buf_l[buf_pos];
|
|
174
154
|
old_r = buf_r[buf_pos];
|
|
175
155
|
|
|
@@ -177,12 +157,7 @@ slider2:track_slot=0<0,127,1>Track Slot
|
|
|
177
157
|
shortterm_sum_l -= old_l;
|
|
178
158
|
shortterm_sum_r -= old_r;
|
|
179
159
|
|
|
180
|
-
// Momentary window: 400ms
|
|
181
|
-
// We subtract from momentary only when the sample leaving is within 400ms window
|
|
182
|
-
// Simplified: maintain separate momentary accumulators via separate pointers
|
|
183
|
-
// For efficiency, use a single ring buffer and recompute momentary from partial sum
|
|
184
|
-
// Here we track momentary with a second set of pointers (400ms lag)
|
|
185
|
-
local(mom_pos);
|
|
160
|
+
// Momentary window: 400ms lag pointer
|
|
186
161
|
mom_pos = buf_pos - floor(srate * 0.4);
|
|
187
162
|
mom_pos < 0 ? mom_pos += buf_size;
|
|
188
163
|
momentary_sum_l -= buf_l[mom_pos];
|
|
@@ -203,18 +178,12 @@ slider2:track_slot=0<0,127,1>Track Slot
|
|
|
203
178
|
buf_pos >= buf_size ? buf_pos = 0;
|
|
204
179
|
|
|
205
180
|
// --- Integrated loudness (gated per BS.1770) ---
|
|
206
|
-
//
|
|
207
|
-
// Simplified: accumulate ungated for speed, apply gate check periodically
|
|
181
|
+
// Simplified: accumulate ungated, apply gate check periodically
|
|
208
182
|
integrated_sum += sq_l + sq_r;
|
|
209
183
|
integrated_count += 1;
|
|
210
184
|
|
|
211
185
|
// --- True peak: 4x linear interpolation oversampling ---
|
|
212
|
-
//
|
|
213
|
-
local(prev_l, prev_r, frac, tp_l, tp_r);
|
|
214
|
-
|
|
215
|
-
// We use the previous sample stored in a local state var
|
|
216
|
-
// Stored in memory block above buf_r to avoid conflict
|
|
217
|
-
// prev_l is at buf_r + buf_size, prev_r is at buf_r + buf_size + 1
|
|
186
|
+
// Previous sample stored above buf_r to avoid conflict
|
|
218
187
|
prev_l = buf_r[buf_size];
|
|
219
188
|
prev_r = buf_r[buf_size + 1];
|
|
220
189
|
|
|
@@ -224,7 +193,6 @@ slider2:track_slot=0<0,127,1>Track Slot
|
|
|
224
193
|
// Check interpolated peaks at 1/4, 2/4, 3/4 offsets
|
|
225
194
|
frac = 0.25;
|
|
226
195
|
loop(3,
|
|
227
|
-
local(interp_l, interp_r);
|
|
228
196
|
interp_l = abs(prev_l + frac * (spl0 - prev_l));
|
|
229
197
|
interp_r = abs(prev_r + frac * (spl1 - prev_r));
|
|
230
198
|
interp_l > tp_l ? tp_l = interp_l;
|
|
@@ -243,9 +211,6 @@ slider2:track_slot=0<0,127,1>Track Slot
|
|
|
243
211
|
|
|
244
212
|
// --- Update gmem every 4096 samples (approx 10x/second at 44.1kHz) ---
|
|
245
213
|
(sample_count % 4096) == 0 ? (
|
|
246
|
-
local(shortterm_mean, momentary_mean, integrated_lufs, shortterm_lufs, momentary_lufs);
|
|
247
|
-
local(mom_samples, base);
|
|
248
|
-
|
|
249
214
|
base = floor(slider2 + 0.5) * 8;
|
|
250
215
|
|
|
251
216
|
mom_samples = floor(srate * 0.4);
|
|
@@ -255,8 +220,7 @@ slider2:track_slot=0<0,127,1>Track Slot
|
|
|
255
220
|
shortterm_mean = (shortterm_sum_l + shortterm_sum_r) / (buf_size * 2);
|
|
256
221
|
momentary_mean = (momentary_sum_l + momentary_sum_r) / (mom_samples * 2);
|
|
257
222
|
|
|
258
|
-
// Integrated mean square (two channels
|
|
259
|
-
local(integrated_mean);
|
|
223
|
+
// Integrated mean square (two channels)
|
|
260
224
|
integrated_mean = integrated_count > 0 ? (integrated_sum / (integrated_count * 2)) : 0;
|
|
261
225
|
|
|
262
226
|
// LUFS = -0.691 + 10 * log10(mean_square)
|
|
@@ -274,7 +238,6 @@ slider2:track_slot=0<0,127,1>Track Slot
|
|
|
274
238
|
|
|
275
239
|
integrated_mean > 0 ? (
|
|
276
240
|
// Apply absolute gate at -70 LUFS
|
|
277
|
-
local(ungated_lufs);
|
|
278
241
|
ungated_lufs = -0.691 + 10 * log10(integrated_mean);
|
|
279
242
|
ungated_lufs > -70 ? (
|
|
280
243
|
integrated_lufs = ungated_lufs;
|