- Инфраструктура: RTTI-дампы (rtti_dsp/full.json), декомпиляции DSP-классов (decomp_*.txt), Ghidra-скрипты (Dump*.java, ImportRtti*.java, Diag.java), depthcurve/curve_fits LUT. - Поведенческая модель sim_v5.py + sim.py (RMSE ~0.3dB), утилиты (measure, tt_sweep, patchparam, sweep, harness_*, verify_sim). - summary.md + roadmap.md (контракт из manual M1-M12, топология пайплайна из OCR, фазы A-C). - pipeline_ocr.txt: OCR диаграммы Appendix A (mid/side ручка, trim/mix/bypass порядок).
63 lines
2.2 KiB
Python
63 lines
2.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Patch a soothe2 PARAM value inside an RPP's base64 state block.
|
|
Usage: patchparam.py <in.rpp> <out.rpp> param=value [param=value...]
|
|
Value formatting: %16 params (depth, band freq/q/sens/balance, offset etc)
|
|
need %.16f; short params (selectivity, mix, mode, oversample, resolution,
|
|
attack, band on/balance?) use 'X.0'. Append '!' to force %.16f, e.g.
|
|
depth=0.0! to guarantee full precision.
|
|
"""
|
|
import re, sys, base64
|
|
|
|
def fmt(val: float, force16: bool):
|
|
if force16:
|
|
return '%.16f' % val
|
|
s = repr(val)
|
|
if '.' not in s:
|
|
s += '.0'
|
|
if '.' in s and float(s) == int(float(s)) and len(s.rstrip('0').rstrip('.')) <= 4:
|
|
return s.rstrip('0').rstrip('.') if '.' in s else s
|
|
return '%.16f' % val
|
|
|
|
def main():
|
|
src, out = sys.argv[1], sys.argv[2]
|
|
params = {}
|
|
for a in sys.argv[3:]:
|
|
if a in ('-f16',):
|
|
continue
|
|
k, v, f16 = a.split('=', 1)[0], a.split('=', 1)[1], '!' in a
|
|
params[k] = (float(v.rstrip('!')), f16)
|
|
s = open(src, encoding='utf8', errors='replace').read()
|
|
i = s.find('PpeX')
|
|
if i < 0:
|
|
i = s.find('VkMy')
|
|
if i < 0:
|
|
sys.exit('no state block found')
|
|
j = s.find('>', i)
|
|
if j < 0:
|
|
sys.exit('malformed state block')
|
|
block = s[i:j]
|
|
raw = bytearray(base64.b64decode(block))
|
|
txt = raw.decode('latin-1')
|
|
n = 0
|
|
for pm in re.finditer(r'(<PARAM id="([^"]*)" value=")[^"]*(")/>', txt):
|
|
did = pm.group(2)
|
|
if did in params:
|
|
val, f16 = params[did]
|
|
txt = txt[:pm.start()] + pm.group(1) + fmt(val, f16) + pm.group(3) + '/>' + txt[pm.end():]
|
|
n += 1
|
|
if n != len(params):
|
|
print(f'warning: patched {n}/{len(params)} params')
|
|
raw = txt.encode('latin-1')
|
|
enc = base64.b64encode(raw).decode('ascii')
|
|
line_start = s.rfind('\n', 0, i) + 1
|
|
indent = s[line_start:i]
|
|
wrapped = '\n'.join(indent + enc[i:i+128] for i in range(0, len(enc), 128))
|
|
jline = s.rfind('\n', 0, j) + 1
|
|
gt_indent = s[jline:j]
|
|
s = s[:line_start] + wrapped + '\n' + gt_indent + s[j:]
|
|
open(out, 'w', encoding='utf8').write(s)
|
|
print(f'{out}: patched {n} params')
|
|
|
|
if __name__ == '__main__':
|
|
main()
|