bench: official param bridge (setparam/dump_params.lua) + rpp_setparam format tool; 22c — XML params decorative, depth=+-18dB, trim=+-24dB, reduction curves measured

This commit is contained in:
2026-08-22 18:28:54 +03:00
parent 7fac8b2a4f
commit f5ef81667c
4 changed files with 176 additions and 0 deletions
+23
View File
@@ -0,0 +1,23 @@
-- dump_params.lua : enumerate soothe2 FX params (name, raw, formatted) to file
local out = io.open("/tmp/opencode/fxparams.txt", "w")
local tr = reaper.GetTrack(0, 0)
if tr == nil then
out:write("NO TRACK\n"); out:close(); return
end
local nfx = reaper.TrackFX_GetCount(tr)
out:write(string.format("nfx=%d\n", nfx))
for fxi = 0, nfx - 1 do
local rv, fxname = reaper.TrackFX_GetFXName(tr, fxi, "")
out:write(string.format("FX %d: %s\n", fxi, fxname))
local np = reaper.TrackFX_GetNumParams(tr, fxi)
for p = 0, np - 1 do
local _, pname = reaper.TrackFX_GetParamName(tr, fxi, p, "")
local val, minv, maxv = reaper.TrackFX_GetParam(tr, fxi, p)
local _, fmt = reaper.TrackFX_GetFormattedParamValue(tr, fxi, p, "")
out:write(string.format("%d\t%s\traw=%.6f\t[%.3f..%.3f]\tfmt=%s\n", p, pname, val, minv, maxv, fmt))
end
end
out:close()
local t0 = reaper.time_precise()
while reaper.time_precise() - t0 < 2 do reaper.defer(function() end) end
reaper.Main_OnCommand(40004, 0) -- File: Quit REAPER
+46
View File
@@ -1435,3 +1435,49 @@ FUN_180529fe0 (Soothe2Module<float>::vtbl) НЕ ссылается на {+0x168,
канон не меняем (корпус держит), но источник правды теперь внутри аудио-модуля. канон не меняем (корпус держит), но источник правды теперь внутри аудио-модуля.
3. Новый приоритет №1: полный разбор аудио-тела FUN_180529fe0 (215 строк декомпа, 3. Новый приоритет №1: полный разбор аудио-тела FUN_180529fe0 (215 строк декомпа,
/tmp/consumers_out.txt) + combine-consumer hunt — насыщение C_max≈0.70 сидит там. /tmp/consumers_out.txt) + combine-consumer hunt — насыщение C_max≈0.70 сидит там.
## ============ UPDATE 2026-08-22c: PARAM BRIDGE + RENDER-BENCH CALIBRATION ============
Инфраструктура управления плагином через ОФИЦИАЛЬНЫЙ параметр-мост REAPER:
- `dump_params.lua` — дамп всех FX-параметров (имя, raw 0..1, formatted UI) в /tmp/opencode/fxparams.txt.
- `setparam.lua` — установка по имени через TrackFX_SetParam + SaveProjectEx (env S2_SET="name=val;...", S2_OUT).
- `scripts/rpp_setparam.py` — редактор XML-стейта в RPP с корректным апдейтом length-полей
(A=buflen16, B=len(xml), хвост JUCEPrivateData сохраняется).
### КЛЮЧЕВОЕ ОТКРЫТИЕ: XML `<PARAM>` секция НЕ источник VST3-стейта
Живой дамп: depth raw=0.524 ↔ fmt=0.9; в XML при этом "0.8639736175537109" (=UI-единицы!).
Плагин восстанавливает параметры из БИНАРНОЙ части чанка; XML-список — декоративная
копия для GUI-восстановления. Правки только XML → рассинхрон → плагин откатывается в
дефолт (объясняет «магию» идентичных рендеров −31.76 у любых depth-правок; контроль
same-value rewrite давал байт-в-байт реф ✓). ВНИМАНИЕ к rpp_setparam.py: менять можно,
но аудио это НЕ меняет.
### Семантика параметров (через мост)
- `depth`: raw 0..1 → UI **±18 линейно** (ui=36·raw−18). Рендеры подтверждают монотонность:
raw=0 → юнити; raw=1.0 (+18) → 37.6 dB @tone.
- `input trim`: raw → **±24 dB**.
- band sens: fmt в dB; stereo mode mid|side; balance 100%|57%.
### WAV-грабли (живое подтверждение класса багов 71644ff)
REAPER пишет bext(602B)+junk(28B) ПЕРЕД data; наивный парсер (readframes+reshape без
каноничного loader) даёт фантомный широкополосный шум и фальшивый клиппинг
(corr(L,R)≈0 артефакты). Каноничный render_parity.load корректен; санити bypass:
corr(L,R)=1.0000, rms=out=in — РЕНДЕР-ПАЙПЛАЙН ЧИСТЫЙ.
### Измеренные кривые редукции (t1kq-пресет, тон@1000, Goertzel 0.75s)
- Depth-свип: монотонный, raw .5→−5.5 dB; .75→−21 dB; 1.0→−37.6 dB @tone.
- Input-trim свип (холодный тон −18 dBFS): R_eff растёт 1.4→15.7 dB на всём ±24 —
ПОТОЛКА НЕТ в этом диапазоне (против гипотезы C_max≈0.70 как свойства плагина).
- Горячий тон (+6 dBFS): out@1000 ЗАМИРАЕТ на −26.74 dB при trim ≥+12 (R_eff cap ≈20.7),
плато воспроизведено дважды.
- НО: при одинаковом относительном драйве холодный/горячий дают РАЗНЫЙ gain
(15.7 vs 20.7 @L≈+6) ⇒ отклик детектора не чистая f(level) — контент/нормализация
(согласуется с KEY NEGATIVE RESULT Phase B).
### Следствия для приоритета №1
1. «Насыщение C_max≈0.70» старой модели — артефакт placeholder MULT=4.2/Pchip cap 0.667,
а не свойство плагина в рабочем диапазоне.
2. Реальный потолок проявляется как FLOOR выходного уровня (−26.74 dB @hot) — искать
механизм floor/clamp в level-path декомпе с конкретной целью.
3. Инструмент готов для систематического картирования R(L, sens, depth): скрипты +
мост позволяют прогонять десятки конфигов без ручного GUI.
+63
View File
@@ -0,0 +1,63 @@
#!/usr/bin/env python3
"""rpp_setparam.py — rewrite PARAM values inside a REAPER .rpp soothe2 state chunk.
!!! CAVEAT (NOTES_LEVEL 22c): the XML <PARAM> list is a decorative UI-restore copy,
NOT the VST3 state source. Editing values here does NOT change plugin audio behaviour
(plugin falls back to defaults on any length mismatch). For real param changes use
setparam.lua (TrackFX_SetParam bridge). This tool is kept for format surgery only.
Block layout (joined from N consecutive base64 lines):
[u32 A=len(rest)][u32 ver=1]['VC2!'][u32 B=len(xml)][xml][tail: JUCEPrivateData...]
Both length fields MUST be updated when xml size changes, otherwise the plugin
silently rejects the state and falls back to defaults.
Usage:
python3 scripts/rpp_setparam.py in.rpp out.rpp depth=1.0 release=0.5
"""
import re, base64, struct, sys
def find_block(lines):
idx = [i for i, ln in enumerate(lines) if re.fullmatch(r"[A-Za-z0-9+/=]{40,}", ln.strip())]
start = None
for i in idx:
if base64.b64decode(lines[i].strip())[:1] == b"\x95":
start = i; break
if start is None:
raise SystemExit("no state block found")
end = start
while end + 1 < len(lines) and re.fullmatch(r"[A-Za-z0-9+/=]{40,}", lines[end + 1].strip()):
end += 1
return start, end
def main():
inp, outp = sys.argv[1], sys.argv[2]
sets = dict(kv.split("=", 1) for kv in sys.argv[3:])
lines = open(inp).read().splitlines()
start, end = find_block(lines)
buf = b"".join(base64.b64decode(lines[k].strip()) for k in range(start, end + 1))
a, ver, magic, blen = struct.unpack_from("<II4sI", buf, 0)
i = buf.find(b"<?xml")
head, xml, tail = buf[:i], buf[i:i + blen], buf[i + blen:]
for k, v in sets.items():
pat = f'<PARAM id="{k}" value="'
j = xml.find(pat.encode())
assert j >= 0, f"param {k} not found"
v0 = j + len(pat)
e = xml.index(b'"/>', v0)
print(f" {k}: {xml[v0:e].decode()} -> {v}")
xml = xml[:v0] + v.encode() + xml[e:]
new_blen = len(xml)
new_buf = struct.pack("<II4sI", a - blen + new_blen, ver, magic, new_blen) + xml + tail
ind = re.match(r"\s*", lines[start]).group(0)
width = max(len(lines[k]) - len(ind) for k in range(start, end + 1))
enc = base64.b64encode(new_buf).decode("ascii")
wrapped = [ind + enc[j:j + width] for j in range(0, len(enc), width)]
lines[start:end + 1] = wrapped
open(outp, "w").write("\n".join(lines) + "\n")
print(f"wrote {outp} (block {end-start+1}->{len(wrapped)} lines, xml {blen}->{new_blen})")
if __name__ == "__main__":
main()
+44
View File
@@ -0,0 +1,44 @@
-- setparam.lua : set soothe2 FX params by name (normalized 0..1), save-as, quit.
-- Env: S2_SET "name=value;name=value" (value normalized 0..1)
-- S2_OUT full path to save the modified project copy to
local spec = os.getenv("S2_SET") or ""
local outpath = os.getenv("S2_OUT")
local log = io.open("/tmp/opencode/setparam.log", "w")
log:setvbuf("line")
local tr = reaper.GetTrack(0, 0)
if tr == nil then
log:write("NO TRACK\n") ; log:close() ; return
end
local function find_idx(name)
local np = reaper.TrackFX_GetNumParams(tr, 0)
for p = 0, np - 1 do
local _, pn = reaper.TrackFX_GetParamName(tr, 0, p, "")
if pn:lower() == name:lower() then return p end
end
return nil
end
for pair in spec:gmatch("[^;]+") do
local name, val = pair:match("^(.-)=(.-)$")
val = tonumber(val)
local idx = find_idx(name)
if idx == nil then
log:write(string.format("param %-16s NOT FOUND\n", name))
else
reaper.TrackFX_SetParam(tr, 0, idx, val)
local rv = reaper.TrackFX_GetParam(tr, 0, idx)
local _, fmt = reaper.TrackFX_GetFormattedParamValue(tr, 0, idx, "")
log:write(string.format("set %-16s -> raw=%.6f fmt=%s\n", name, rv, fmt))
end
end
if outpath and outpath ~= "" then
reaper.Main_SaveProjectEx(0, outpath, 0)
log:write("saved " .. outpath .. "\n")
end
log:close()
local t0 = reaper.time_precise()
while reaper.time_precise() - t0 < 2 do reaper.defer(function() end) end
reaper.Main_OnCommand(40004, 0) -- File: Quit REAPER