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:
@@ -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
|
||||
@@ -1435,3 +1435,49 @@ FUN_180529fe0 (Soothe2Module<float>::vtbl) НЕ ссылается на {+0x168,
|
||||
канон не меняем (корпус держит), но источник правды теперь внутри аудио-модуля.
|
||||
3. Новый приоритет №1: полный разбор аудио-тела FUN_180529fe0 (215 строк декомпа,
|
||||
/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=buflen−16, 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.
|
||||
|
||||
@@ -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()
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user