#!/usr/bin/env python3 """rendersnap2.py — non-destructive variant of rendersnap.py. Differences (24j): - does NOT kill the host: lets -renderproject finish naturally so the output wav is complete (partial-wav hazard, NOTES 24c); - additionally dumps the ctx SCALAR bank 0x540860..0x5408c0 every phase (goal: wet/FIR scalar [ctx+0x540888], BLOCKMAP 23b line 52b832); - keeps sampling until host exit or phase cap, dedupe by FIR+scalar md5. """ import hashlib import os import signal import struct import subprocess import sys import time import numpy as np SLOTS = [0x540668, 0x540548, 0x540550, 0x540598, 0x540628, 0x5406f8, 0x540678, 0x540688, 0x5406c8, 0x5406e8, 0x540768, 0x540788, 0x5407f8, # 24mm9: ACC-таблица указателей (шаг 10 combine) и WINfreq # (окно FIR-цепи; падающий Hann — контроль формы) 0x5407c8, 0x540658] NARR = 8194 SCAL_OFF = 0x540860 SCAL_N = 24 # floats -> 0x540860..0x5408c0 OUT = '/tmp/opencode/rendersnap2' def find_host(): import glob for p in glob.glob('/proc/[0-9]*'): pid = int(os.path.basename(p)) try: cmd = open(f'/proc/{pid}/cmdline', 'rb').read().replace(b'\0', b' ').decode('utf8', 'replace') maps = open(f'/proc/{pid}/maps').read() except Exception: continue if 'soothe2' in maps and 'reaper' not in cmd: return pid return None def main(): rpp = sys.argv[1] if len(sys.argv) > 1 else '/home/m/soothe-bt/dual_b1q_0.5.rpp' cap = int(sys.argv[2]) if len(sys.argv) > 2 else 200 global OUT OUT = sys.argv[3] if len(sys.argv) > 3 else OUT os.makedirs(OUT, exist_ok=True) subprocess.run("pkill -9 -x reaper; pkill -9 -f '[y]abridge'; " "rm -rf /run/user/1000/yabridge-soothe2_x64-*; sleep 1", shell=True) # wav path = RENDER_FILE from the project (NOT rpp.replace!) — deleting the # wrong file destroyed corpus refs once (24k-3 hazard). wav = None for ln in open(rpp, 'r', errors='replace'): if 'RENDER_FILE' in ln and '"' in ln: wav = ln.split('"')[1] break if wav and os.path.exists(wav): os.remove(wav) proc = subprocess.Popen(['/usr/bin/reaper', '-nosplash', '-ignoreerrors', '-renderproject', rpp], stdout=open('/dev/null', 'w'), stderr=subprocess.STDOUT) t0 = time.time() host = None while time.time() - t0 < 30 and not host: host = find_host() time.sleep(0.001) if not host: print('NO HOST') return 1 print('host %d at %.3fs' % (host, time.time() - t0), flush=True) fd = os.open(f'/proc/{host}/mem', os.O_RDONLY) def rd(a, n): try: return os.pread(fd, n, a) except OSError: return None vt = struct.pack('= 0: cand = a + j sb = rd(cand + 0x540870, 4) if sb and struct.unpack(' 100: return cand j = d.find(vt, j + 1) j = d.find(m48) while j >= 0: cand = a + j - 0x24 sb = rd(cand + 0x540870, 4) if sb and struct.unpack(' 100: return cand j = d.find(m48, j + 1) a += CH return None ctx = None while ctx is None and time.time() - t0 < 25: try: os.kill(host, signal.SIGSTOP) except ProcessLookupError: break try: ctx = scan_ctx() finally: if ctx is None: try: os.kill(host, signal.SIGCONT) except ProcessLookupError: break if ctx is None: time.sleep(0.004) if not ctx: print('NO CTX') return 1 print('ctx %#x' % ctx, flush=True) rng = np.random.default_rng(11) prev = None saved = 0 while True: try: os.kill(host, signal.SIGSTOP) except ProcessLookupError: print('host exited at %.2fs' % (time.time() - t0), flush=True) break try: pb = rd(ctx + 0x540668, 8) if not pb: continue p = struct.unpack(' [r15+0x180]); A=[sub+0] B=[sub+4] G=[sub+0xc] sb = rd(ctx + 0x180, 8) if sb: sub = struct.unpack(' 0x10000: fb4 = rd(sub, 0x18) if fb4: store['lut_A'] = struct.unpack(' buf 2049 f32 each for bi in range(4): q = rd(ctx + off + 8*bi, 8) if not q: continue ptr = struct.unpack('= 0x10000: ab = rd(ptr, NARR * 4) if ab: store[hex(off)] = np.frombuffer(ab, dtype='= cap: break finally: try: os.kill(host, signal.SIGCONT) except ProcessLookupError: pass time.sleep(float(rng.uniform(0.0005, 0.006))) try: os.kill(host, 0) except ProcessLookupError: print('host exited at %.2fs' % (time.time() - t0), flush=True) break # wait for natural finish so wav completes for _ in range(300): if proc.poll() is not None: break time.sleep(0.1) print('saved=%d reaper_rc=%s wav=%s' % (saved, proc.poll(), os.path.getsize(wav) if wav and os.path.exists(wav) else 'NONE')) return 0 if __name__ == '__main__': sys.exit(main())