diff --git a/handoff/NOTES_LEVEL.md b/handoff/NOTES_LEVEL.md index 0113ff1..621343e 100644 --- a/handoff/NOTES_LEVEL.md +++ b/handoff/NOTES_LEVEL.md @@ -2804,3 +2804,72 @@ Our application: mask × audio via complex multiply (WITH 1/N from IFFT) 3. Проверить hannfall-мультипликацию: fit exp(0.984·log(bands))·w против захваченного fir_im≠0 (фаза!) — complex ops A-D дают мнимость. 4. Починить wav-path баг rendersnap2 (проверяет rpp.wav вместо RENDER_FILE). + +## ============ UPDATE 2026-08-24k (24k): ЗАКОН ПРИМЕНЕНИЯ НАЙДЕН ТОЧНО — A = 1.019·V^1.8345; «×1.805» = ПРОИЗВЕДЕНИЕ ЭКСПОНЕНТ 0.984×1.8345 ============ + +### Инструменты раунда (все рабочие, закоммичены) +- `scripts/rendersnap2.py` v3: мягкий снаппер (не убивает reaper), t_snap в npz, + слоты {540668 FIR, 540678 bands, 540688, 540788 R, 5407f8 R} + скалярный банк + 0x540860..c0. OUT = argv[3] (иначе каталог перезаписывается!). +- `scripts/hunt2.py`: полный чанковый перебор ВСЕХ ctx-кандидатов в рендере. + РЕЗУЛЬТАТ: инстанс ОДИН (0x2370040) — GUI/DSP-пара (24c) НЕ ПОДТВЕРЖДЕНА, + гипотеза закрыта. +- Зондовый мультитон: 2 главных тона (= амплитуды dual.wav 0.4248) + зонды + −30 дБ на фикс. сетке; Гёрцель по рефу даёт применённый отклик на всех + зондовых частотах при том же адаптивном состоянии детектора. + +### Ключевые отрицательные результаты (не возвращаться) +- [ctx+0x540888] и весь банк 0x540874..88c = 1.000000 весь рендер; 0x540870 = + 440.955. Скаляров-множителей в применении НЕТ. +- R-слоты 0x540788/0x5407f8 ИДЕНТИЧНЫ во всех проектах (r43=4.0285/3.9807) — + статические twin-шаблоны; с формой V НЕ коррелируют (rms 0.47). +- Слот 0x540678 = спектр кадра (HF→0), не кривая маски; 0x540688 = большие + значения (~183) — сырые аккумуляторы. +- Свип для Y/X-отношения непригоден (энергия только на гребне) — только + мультитон/мультиуровень. +- Динамическое выравнивание V(t)↔g(t) на рампе НЕ сходится (инерция детектора + + STOP-семплирование) — пары брать только из стационарных прогонов. +- Каталоги снапов перезаписываются друг другом — всегда отдельный argv[3]. + +### ГЛАВНЫЙ ЗАКОН (8 уровней драйва −0..−30 дБ, q=0.5, fc=500) +Стационарная пара (V = устоявшийся видимый FIR@540668 «most-common complete +state», A = применённый отклик по Гёрцелю рефа): +``` +cutA[дБ] = 1.8345 · cutV[дБ] + 0.1615 дБ (@500 Гц, центр нотча) +rms = 0.0025 дБ (!), max 0.005 дБ — 8 точек, cutV от 1.0 до 5.55 дБ +``` +На скирте (@2000): та же форма, γ≈1.812, offset≈0.18, rms 0.087 дБ. +В линейном домене: A ≈ 1.019 · V^1.8345. +Погрешность выбора V (медиана most-common вместо истинного кадра) ~0.05-0.1 дБ +на скирте объяснима; закон устойчив по всем уровням. + +### РАЗГАДКА «×1.805» (23e/24i окончательно закрыты) +``` +0.984 × 1.8345 = 1.80515 +``` +23e эмпирическая константа «свёртка ×1.805» — это ПРОИЗВЕДЕНИЕ экспонент: +видимый кернел строится как exp(0.984·log(bands)) (23d ✓), а ПРИМЕНЯЕМЫЙ +kerнел = bands^1.805 (в эквиваленте). Никакой свёртки/OLA/скаляра нет: +применение = побиновный комплекс-multiply (th_b3c0 ✓ 24e) на кернел +A = 1.019·V^1.8345. Старые phase*.npz (m43=0.588) — init-кернелы, всё что +строилось на них — артефакты. + +### Двойные аттракторы видимого буфера +V_steady@43 = 0.524±0.002 ∀q (0.1..10); редкие флэши D@43=0.315 ∀q; +ln(D)/ln(V)=1.85 ≈ γ ⇒ флэш = применяемый кернел A, пойманный в буфере +(перестройка каждый кадр: rebuild→A→consume→запись V-стадии). + +### Что осталось до транскрипции применения +1. Формула V(content,params): ширина провала vs q, глубина vs drive/sens, + позиция vs fc. Зондовая методика + rn_q*/rn_lv* каталоги = данные уже есть. +2. Источник 1.8345 в коде: искать вторую экспоненту в ops A-D / bigkernel + 140b30 (exp2-кернел?) — кандидат: двойное применение стадии exp или + exp(0.984·log)²·коррекция окна (0.984·2=1.968≠1.805 — нет; искать точно). +3. RT_FIRCONV=3: маска = 1.019·M^1.8345, где M = выход нашей detector-цепи, + после того как M приведён к форме V (единичный passband, провалы у контента). +4. Корпусный гейт --vs-bridge после каждого шага. + +### Контрольные числа уровня −0 дБ (dual-эквивалент) +V43=0.5279 → cutV=5.550 дБ; cutA=10.34 дБ (реф 10.32 ✓); cutA@2000=11.85 (реф +11.82 ✓). Уровневая серия: см. /tmp/opencode/{lv*,rn_lv*}, pairs в +level_pairs.npz. diff --git a/scripts/hunt2.py b/scripts/hunt2.py new file mode 100644 index 0000000..a08d483 --- /dev/null +++ b/scripts/hunt2.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +"""hunt2.py — enumerate ALL soothe2 module instances during offline render. + +Chunk-correct full-heap scan (rendersnap-style) collecting EVERY ctx candidate +(vtable 0x1824AC210 or m48 marker), not just the first. Per candidate: sens, +fir43/fir171 (via ctx+0x540668 ptr), scalar bank snapshot. Goal: find the +GUI/DSP pair (24c/24j): visible instance holds shallow kernel while audio is +processed by another instance with the deep one. +""" +import hashlib +import os +import signal +import struct +import subprocess +import sys +import time + +import numpy as np + +VT = struct.pack(' 1 else '/tmp/opencode/multi.rpp' + subprocess.run("pkill -9 -x reaper; pkill -9 -f '[y]abridge'; " + "rm -rf /run/user/1000/yabridge-soothe2_x64-*; sleep 1", shell=True) + 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 + 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 + + def scan_all(): + found = {} + for line in open(f'/proc/{host}/maps'): + parts = line.split() + if 'rw' not in parts[1]: + continue + lo, hi = (int(x, 16) for x in parts[0].split('-')) + CH = 16 * 1024 * 1024 + a = lo + while a < hi: + d = rd(a, min(CH + 4096, hi - a)) + if not d: + break + for pat, off in ((VT, 0), (M48, -0x24)): + j = d.find(pat) + while j >= 0: + cand = a + j + off + if cand not in found: + sb = rd(cand + 0x540870, 4) + if sb and struct.unpack(' 100: + found[cand] = True + j = d.find(pat, j + 1) + a += CH + return list(found) + + known = {} + rounds = 0 + while time.time() - t0 < 25: + rounds += 1 + try: + os.kill(host, signal.SIGSTOP) + except ProcessLookupError: + break + try: + cands = scan_all() + new = [c for c in cands if c not in known] + for c in new: + known[c] = rounds + pb = rd(c + 0x540668, 8) + m43 = m171 = -1 + if pb: + p = struct.unpack(' 0x10000: + fb = rd(p, 2049 * 8) + if fb: + arr = np.frombuffer(fb[:2049 * 8], dtype=' 0x10000: + fb = rd(p, 2049 * 8) + if fb: + arr = np.frombuffer(fb[:2049 * 8], dtype=' 0x540860..0x5408c0 @@ -42,6 +42,8 @@ def find_host(): 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) @@ -148,9 +150,20 @@ def main(): arr = np.frombuffer(fb[:2049 * 8], dtype='