diff --git a/handoff/NOTES_LEVEL.md b/handoff/NOTES_LEVEL.md index 7d7b278..3213a4c 100644 --- a/handoff/NOTES_LEVEL.md +++ b/handoff/NOTES_LEVEL.md @@ -3638,3 +3638,33 @@ bigkernel'ов = ИМПОРТИРОВАННЫЕ функции (UCRT/хост-DL Как только имена получены (ожидаемо expf/powf/log10f класс), семантика bigkernel'ов замыкается, и полный каскадный симулятор шагов 9–19+FIR становится реализуемым без дальнейшей археологии. + +## ============ ДОПОЛНЕНИЕ 24hh2: гейт смены канона = BIT EXACT; точка g_s12; кампания параметризации ============ + +### Решение по гейту (пользователь) +Цель = BIT EXACT (не «лучше канона»): все параметры прослежены до декомпа, +корпус сходится в шумовой пол. Дефолт переключается только после этого. + +### Новая точка фронтенда: fc1000 q1 SENS12 (g_s12) +``` +наш lvl@85=20.139 res@85=0.11718 +deepest scratch: cut_D=7.64 dB +REF goertzel: 7.53 dB ⇒ audio=scratch+const ✓ (−0.11) +``` +Инверсия через dual-константы НЕКОРРЕКТНА (чужое семейство) — для анализа +кламп-гипотезы нужен фит закона внутри fc1000/q1 (серия драйвов по q1). + +### Кампания параметризации (план): +Семейство = (контент, fc, q, sens); для каждого нужна серия драйвов: +``` +✓ dual fc500 q0.5 s12 два-тона : α=3.2193 β=0.4927 c=0.54 +✓ fc1000 q0.5 s12 один тон : α=1.6151 β=0.3645 c=0.48 +✓ fc500 q0.5 s12 один тон : α=1.1530 β=0.4038 c=0.33 +□ fc1000 q1 s12 + драйв-серия : ← следующий +□ q-серия на одном контенте : q∈{0.1..10} × фикс драйв +□ sens-серия аналогично +□ multi-band (comb) : правило суммирования нотчей +``` +Каждая ячейка ≈10 мин (patchparam+рендер+rendersnap2+тракт). +После сетки: зависимость (α,β,c)(q,sens,fc,контент) → формулы или таблицы +с декомп-обоснованием из шагов 9–19. diff --git a/scripts/g_s12_run.py b/scripts/g_s12_run.py new file mode 100644 index 0000000..12ec9b0 --- /dev/null +++ b/scripts/g_s12_run.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +"""g_s12 pipeline: clean render ref + deepest scratch capture + render48k tract. +Then compute implied-res vs our-res for the frontend clamp analysis.""" +import subprocess, os, sys, time, glob +import numpy as np + +def find_host(): + import glob as g + for p in g.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 sh(cmd): + return subprocess.run(cmd,shell=True,capture_output=True,text=True).stdout + +sh("pkill -9 -x reaper; pkill -9 -f '[y]abridge'; " + "rm -rf /run/user/1000/yabridge-soothe2_x64-*; sleep 1") + +# 1. clean ref render +wav='/tmp/opencode/g_s12_ref.wav' +if os.path.exists(wav): os.remove(wav) +pr=subprocess.Popen(['/usr/bin/reaper','-nosplash','-ignoreerrors','-renderproject','/tmp/opencode/g_s12.rpp'], + stdout=subprocess.DEVNULL,stderr=subprocess.STDOUT) +t0=time.time(); host=None +while time.time()-t0<30 and not host: host=find_host(); time.sleep(0.002) +print('host',host) +for _ in range(300): + if pr.poll() is not None: break + time.sleep(0.1) +for _ in range(50): + if pr.poll() is not None: break + time.sleep(0.1) +print('ref done rc=',pr.poll()) + +# 2. deepest scratch capture +sh("pkill -9 -x reaper; sleep 1; rm -rf /tmp/opencode/sc_g_s12") +r=sh('cd /home/m/re-tools && timeout 60 python3 scripts/rendersnap2.py /tmp/opencode/g_s12.rpp 400 /tmp/opencode/sc_g_s12') +print('capture tail:',r.strip().splitlines()[-1] if r.strip() else 'empty') + +# 3. render48k tract (same input tone1k, band fc1000 q1 s12) +tf='/tmp/opencode/tract_g_s12.txt' +if os.path.exists(tf): os.remove(tf) +env=dict(os.environ); env['RT_DUMP_BIN']=tf +subprocess.run(['/home/m/re-tools/dsp/build/render48k','/home/m/soothe-bt/tone1k.wav', + '/tmp/o48_g.wav','1000,1.0,12'],capture_output=True,env=e if False else env) +print('tract done') + +# 4. analysis +import wave +def loadwav(p): + w=wave.open(p,'rb'); n=w.getnframes(); ch=w.getnchannels(); sw=w.getsampwidth() + d=w.readframes(n); w.close() + if sw==2: return np.frombuffer(d,dtype=np.int16).astype(np.float64).reshape(-1,ch).mean(1)/32768 + raw=np.frombuffer(d,dtype=np.uint8).reshape(-1,ch,3) + s=raw[:,:,0].astype(np.int64)|(raw[:,:,1].astype(np.int64)<<8)|(raw[:,:,2].astype(np.int64)<<16) + return np.where(s>=0x800000,s-0x1000000,s).astype(np.float64).reshape(-1,ch).mean(1)/8388608 +def ta(x,f,sr=44100): + x=x[-int(0.75*sr):]; t=np.arange(len(x))/sr; w=2*np.pi*f + return np.hypot(2*np.sum(x*np.cos(w*t))/len(x), 2*np.sum(x*np.sin(w*t))/len(x)) +def db(a): return 20*np.log10(max(a,1e-12)) + +inp=loadwav('/home/m/soothe-bt/tone1k.wav') +ref=loadwav(wav) +rcut=-db(ta(ref,1000)/ta(inp,1000)) + +best=None +for fn in sorted(glob.glob('/tmp/opencode/sc_g_s12/ph*.npz')): + d=np.load(fn) + if '0x540628' not in d.files: continue + s=d['0x540628'].astype(np.float64) + if best is None or s[85] implied res=%.5f' % (li, 0.7307*3.2309/max(li,1e-9))) +print('REF goertzel cut@1000=%.2f dB' % rcut) +print('\ncontext: sens18 impl res=0.15244 (плато); ours@s18=0.0284')