- Инфраструктура: 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 порядок).
92 lines
2.9 KiB
Python
92 lines
2.9 KiB
Python
#!/usr/bin/env python3
|
|
import os, glob, sys, time, subprocess
|
|
|
|
REAPER = "/usr/bin/reaper"
|
|
RPP = "/home/m/soothe-bt/render_rt.rpp"
|
|
OUTDIR = "/home/m/re-tools/regions_rt"
|
|
|
|
os.makedirs(OUTDIR, exist_ok=True)
|
|
|
|
def find_soothe2_pid():
|
|
for p in glob.glob('/proc/[0-9]*'):
|
|
try:
|
|
pid = int(os.path.basename(p))
|
|
cmd = open(p + '/cmdline','rb').read().replace(b'\0',b' ').decode('utf8','replace')
|
|
if 'yabridge' not in cmd and 'wine' not in cmd:
|
|
continue
|
|
maps = open(f'/proc/{pid}/maps').read()
|
|
if 'soothe2' in maps:
|
|
st = open(p + '/stat','rb').read().decode('utf8','replace')
|
|
state = st.split(')')[-1].split()[0]
|
|
return pid, state
|
|
except Exception:
|
|
pass
|
|
return None, None
|
|
|
|
def init_ok(pid, base=0x180000000):
|
|
try:
|
|
maps = open(f'/proc/{pid}/maps').read()
|
|
except Exception:
|
|
return False
|
|
seen = 0
|
|
for line in maps.splitlines():
|
|
parts = line.split()
|
|
if len(parts) < 6: continue
|
|
lo = int(parts[0].split('-')[0],16)
|
|
if 'r' in parts[1] and base <= lo < base+0x7000000:
|
|
seen += 1
|
|
return seen >= 8
|
|
|
|
def dump_module(pid, base=0x180000000, extent=0x7000000):
|
|
maps = open(f'/proc/{pid}/maps').read()
|
|
readable = []
|
|
for line in maps.splitlines():
|
|
parts = line.split()
|
|
if len(parts) < 6: continue
|
|
lo, hi = (int(x,16) for x in parts[0].split('-'))
|
|
if 'r' in parts[1] and (base <= lo < base+extent):
|
|
readable.append((lo, hi, parts[1]))
|
|
print(f' pid {pid}: {len(readable)} regions')
|
|
mem = os.open(f'/proc/{pid}/mem', os.O_RDONLY)
|
|
total = 0
|
|
for lo, hi, perms in readable:
|
|
try:
|
|
data = os.pread(mem, hi-lo, lo)
|
|
total += len(data)
|
|
fn = os.path.join(OUTDIR, f'pid{pid}_{lo:08x}_{hi:08x}.bin')
|
|
with open(fn,'wb') as f: f.write(data)
|
|
except Exception as e:
|
|
print(' fail', lo, e)
|
|
os.close(mem)
|
|
print(f' dumped total {total} bytes in {time.time()-T0:.1f}s')
|
|
|
|
T0 = time.time()
|
|
print("spawning reaper (realtime render)...")
|
|
proc = subprocess.Popen(
|
|
[REAPER, "-nosplash", "-renderproject", RPP],
|
|
stdout=open('/tmp/harness_fast.log','w'), stderr=subprocess.STDOUT,
|
|
env={**os.environ})
|
|
print("reaper pid", proc.pid)
|
|
|
|
deadline = time.time() + 200
|
|
dumped = False
|
|
while time.time() < deadline and proc.poll() is None:
|
|
pid, state = find_soothe2_pid()
|
|
if pid and init_ok(pid):
|
|
print(f' found pid {pid} state {state}, dumping...')
|
|
try:
|
|
dump_module(pid)
|
|
except Exception as e:
|
|
print(' dump error:', e)
|
|
dumped = True
|
|
break
|
|
time.sleep(0.05)
|
|
|
|
if not dumped:
|
|
print("no live soothe2 captured")
|
|
print("waiting for render to finish...")
|
|
try:
|
|
proc.wait(timeout=240)
|
|
except Exception:
|
|
proc.kill()
|
|
print(f"done in {time.time()-T0:.0f}s, exit {proc.returncode}") |