22s: offline law-fit infra (full replay sim, 2s/corpus) — NO single affine (A,S) exists even on pure tones; family slopes 0.85-2.19, global LSQ resid 0.60 dB structured by rms/fc-dist; scalar law saturated ~1.87-1.93, canon stays HEAD
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env python3
|
||||
"""pairfit22q.py — collect (model lvl_raw, real cut_dB) pairs across corpus."""
|
||||
import sys, os, subprocess, numpy as np
|
||||
sys.path.insert(0, "/home/m/re-tools/scripts")
|
||||
sys.path.insert(0, "/home/m/re-tools/handoff")
|
||||
import corpus
|
||||
from render_parity import load, FS
|
||||
|
||||
RB = "/home/m/re-tools/dsp/build/render48k"
|
||||
ENVBASE = {"RT_LUT_OFF": "1", "RT_IIR12": "0", "RT_NOWARP": "1",
|
||||
"RT_NOBLEND": "1", "RT_NOIIR3": "1", "RT_POOL": "0",
|
||||
"RT_SCALE_M": "1.0", "RT_FLOOR": "0",
|
||||
"RT_DUMP_BIN": "/tmp/opencode/tract_pair.txt",
|
||||
"RT_DUMP_FRAME": "120"}
|
||||
|
||||
def amp(p, f):
|
||||
a = load(p)
|
||||
seg = np.mean(a[:min(len(a), int(3.5*FS))][-int(0.75*FS):], axis=1)
|
||||
n = len(seg); tt = np.arange(n)/FS
|
||||
return 2*np.abs(np.dot(seg, np.exp(-2j*np.pi*f*tt)))/n
|
||||
|
||||
def bin_of(f):
|
||||
return int(round(f/48000*4096))
|
||||
|
||||
def main():
|
||||
e = {**os.environ, **ENVBASE}
|
||||
pairs = []
|
||||
seen_render = {}
|
||||
cases = corpus.build_cases()
|
||||
# unique renders first (input,args): dump tract once, remember lvl per freq
|
||||
uniq = {}
|
||||
for name, inp, args, ref, f in cases:
|
||||
key = (inp, ",".join(args) if isinstance(args, list) else args)
|
||||
uniq.setdefault(key, []).append((name, ref, f))
|
||||
n_done = 0
|
||||
for (inp, argstr), items in sorted(uniq.items()):
|
||||
out_wav = "/tmp/opencode/pf_model.wav"
|
||||
env = {**e}
|
||||
bands = argstr.split(",") if "," in argstr else [argstr]
|
||||
nbands = max(1, len(bands)//3)
|
||||
env["RT_DUMP_FRAME"] = str(120 * nbands - 1) # land mid-frame of last band
|
||||
r = subprocess.run([RB, inp, out_wav] + ([argstr] if "," in argstr else [" ".join([])]),
|
||||
capture_output=True, text=True, env=env, cwd="/home/m/re-tools")
|
||||
if not os.path.exists("/tmp/opencode/tract_pair.txt"):
|
||||
print("no tract for", argstr); continue
|
||||
try:
|
||||
d = np.loadtxt("/tmp/opencode/tract_pair.txt")
|
||||
except Exception as ex:
|
||||
print("load fail", ex); continue
|
||||
if d.ndim == 1: continue
|
||||
k, am, res, lvl = d.T[0], d.T[1], d.T[2], d.T[3]
|
||||
for name, ref, f in items:
|
||||
b = bin_of(f)
|
||||
if b >= len(lvl): continue
|
||||
lv = float(lvl[b])
|
||||
i_db = 20*np.log10(max(amp(inp, f), 1e-12))
|
||||
r_db = 20*np.log10(max(amp(ref, f), 1e-12))
|
||||
cut = i_db - r_db
|
||||
pairs.append((name, f, lv, cut))
|
||||
n_done += 1
|
||||
os.remove("/tmp/opencode/tract_pair.txt")
|
||||
print("collected %d pairs from %d renders" % (len(pairs), n_done))
|
||||
with open("/tmp/opencode/pairs.csv", "w") as fh:
|
||||
fh.write("case,freq,lvl,cut_db\n")
|
||||
for nm, f, lv, c in pairs:
|
||||
fh.write("%s,%.1f,%.4f,%.3f\n" % (nm, f, lv, c))
|
||||
arr = np.array([(lv, c) for _, _, lv, c in pairs])
|
||||
o = np.argsort(arr[:, 0]); s = arr[o]
|
||||
print("lvl -> cut samples:")
|
||||
step = max(1, len(s)//18)
|
||||
for i in range(0, len(s), step):
|
||||
print(" %8.3f -> %+8.2f dB" % tuple(s[i]))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user