Version 1.0: VLAW parameterization + detector cascade
- Implemented exact ln/exp2 infrastructure (log2_ln.hpp/cpp) - Parameterized VLAW α/β/c by (fc, q, sens) configuration - Implemented real RFFT for FIR construction - Fixed VLAW parameterization for dual group (3.455 → 0.764 dB) - Added detector cascade 529c60 (Haar smoothing, magnitude, peak processing) - TOTAL error: 0.870 dB (vs bridge baseline 1.594 dB) Results: - t1kq: 0.618 dB (bridge: 0.226 dB) - t1k: 0.938 dB (bridge: 1.801 dB) ✓ better - al: 0.727 dB (bridge: 0.638 dB) - res: 0.284 dB (bridge: 0.628 dB) ✓ better - dual: 0.764 dB (bridge: 0.726 dB) - comb: 3.000 dB (bridge: 10.149 dB) ✓ better
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
#!/usr/bin/env python3
|
||||
"""detector_cascade.py — validated simulator of the soothe2 detector cascade (529c60).
|
||||
|
||||
Decoded from assembly (2026-08-25):
|
||||
Phase 1: |z_i| via 16140 (vrsqrtps+vsqrtps — magnitude, NOT squared)
|
||||
Phase 2: Haar smoothing kernel [0.25, 0.5, 0.25], ctx[0x1b0] iterations
|
||||
Phase 3: peak→sin-mod→max-clamp→ratio→pow→log→FMA-blend→memcpy
|
||||
|
||||
Validated on chain_samples.pkl (2-frame ptrace capture):
|
||||
- op A output matches |z| (max diff 5.4e-6)
|
||||
- 2 Haar iterations + scalar blend: rms=0.30, corr=0.998 vs COUT
|
||||
- ctx[0x1b0]=2 (Haar iterations) — derived from best-fit
|
||||
|
||||
Unknowns (require live capture):
|
||||
- ctx[0x54087c] — sin modulation parameter (controls sin_peak clamp)
|
||||
- ctx[0x24], ctx[0x1a0], ctx[0x1ac] — ratio parameters for w computation
|
||||
- w is currently fitted empirically (≈0.015 for this test signal)
|
||||
"""
|
||||
import numpy as np
|
||||
|
||||
N = 2049 # FFT bins (NFRAME/2 + 1)
|
||||
|
||||
|
||||
def haar_one_pass(b):
|
||||
"""One Haar smoothing pass (kernel [0.25, 0.5, 0.25]).
|
||||
|
||||
Decoded from 529c60 Haar loop (lines 35-74):
|
||||
Step 1: b[i] += b[i+1] (prefix sum, 10e40)
|
||||
Step 2: b[i] *= 0.5 (scalar mul, ffe0)
|
||||
Step 3: scratch[i] = b[i+1] + b[i] (3-op add, 11580)
|
||||
Step 4: b[i+1] = 0.5 * scratch[i] (scalar mul+store, 4720)
|
||||
"""
|
||||
n = len(b)
|
||||
if n < 2:
|
||||
return b
|
||||
# Steps 1+2 combined: b[i] = 0.5 * (b[i] + b[i+1]) for i < n-1
|
||||
# Note: b[n-1] is unchanged by steps 1+2
|
||||
b[:-1] = 0.5 * (b[:-1] + b[1:])
|
||||
# Steps 3+4: b[i+1] = 0.5 * (b[i] + b[i+1]) using UPDATED b
|
||||
# Need original b[i] values for step 3
|
||||
# Actually: step 3 reads AFTER steps 1+2, so uses modified b
|
||||
# scratch[i] = b[i+1] + b[i] (both modified)
|
||||
# b[i+1] = 0.5 * scratch[i]
|
||||
# This means: b_new[i+1] = 0.5 * (b_modified[i+1] + b_modified[i])
|
||||
b6f8 = b[1:] + b[:-1]
|
||||
b[1:] = 0.5 * b6f8
|
||||
return b
|
||||
|
||||
|
||||
def haar_smooth(magnitudes, n_iters):
|
||||
"""Haar smoothing: iterate Haar passes.
|
||||
|
||||
Args:
|
||||
magnitudes: |z_i| array (N floats)
|
||||
n_iters: number of Haar iterations (ctx[0x1b0])
|
||||
Returns:
|
||||
smoothed array
|
||||
"""
|
||||
b = magnitudes.copy()
|
||||
for _ in range(n_iters):
|
||||
haar_one_pass(b)
|
||||
return b
|
||||
|
||||
|
||||
def cascade_detect(complex_state, n_iters=2, w=0.015, sin_peak_floor=0.0):
|
||||
"""Full detector cascade (529c60) simulation.
|
||||
|
||||
Args:
|
||||
complex_state: interleaved re/im array (2N floats)
|
||||
n_iters: Haar iteration count
|
||||
w: blend weight (scalar, ~0.015 for typical settings)
|
||||
sin_peak_floor: minimum from sin modulation (0 = disabled)
|
||||
Returns:
|
||||
bands_output: smoothed detector curve (N floats)
|
||||
"""
|
||||
n = len(complex_state) // 2
|
||||
re = complex_state[0::2]
|
||||
im = complex_state[1::2]
|
||||
|
||||
# Phase 1: magnitudes via 16140
|
||||
magnitudes = np.sqrt(re**2 + im**2)
|
||||
|
||||
# Phase 2: Haar smoothing
|
||||
curve = haar_smooth(magnitudes, n_iters)
|
||||
|
||||
# Phase 3 (partial — unknown ctx params):
|
||||
# peak = max(curve) [4d56b0]
|
||||
# sin_peak = sin(ctx[0x54087c]*30 - 90) * 0.115129 * peak [1a14cac]
|
||||
# curve[i] = max(curve[i], sin_peak) [52d8a0→10860]
|
||||
if sin_peak_floor > 0:
|
||||
np.maximum(curve, sin_peak_floor, out=curve)
|
||||
|
||||
# Blend: output = curve * (1-w) + accumulator * w
|
||||
# 5407a8 (accumulator) = 0 in steady state → output = curve * (1-w)
|
||||
# The blend chain:
|
||||
# 52d920: 5407a8[i] *= w (array scalar mul)
|
||||
# 52dae0: 5407a8[i] += curve[i] * (1-w) (FMA)
|
||||
# 52dbc0: memcpy 5407a8 → 540678
|
||||
bands_output = curve * (1.0 - w)
|
||||
|
||||
return bands_output
|
||||
|
||||
|
||||
def validate():
|
||||
"""Validate against ptrace capture (chain_samples.pkl)."""
|
||||
import pickle
|
||||
path = '/tmp/opencode/winetrace_casc/chain_samples.pkl'
|
||||
with open(path, 'rb') as f:
|
||||
data = pickle.load(f)
|
||||
|
||||
s = data['samples']
|
||||
cin = s[0]
|
||||
cout = s[3]
|
||||
|
||||
trk = np.array(cin['trk'], dtype=np.float64)
|
||||
b0_cout = np.array(cout['bands0'], dtype=np.float64)
|
||||
|
||||
# Fit w and n_iters
|
||||
best_rms = 1e10
|
||||
best_params = None
|
||||
|
||||
for n_iters in range(1, 11):
|
||||
magnitudes = np.zeros(len(trk) // 2)
|
||||
re = trk[0::2]; im = trk[1::2]
|
||||
magnitudes = np.sqrt(re**2 + im**2)
|
||||
|
||||
curve = haar_smooth(magnitudes, n_iters)
|
||||
|
||||
sig = (curve > 0.5) & (b0_cout > 0.5)
|
||||
if sig.sum() < 10:
|
||||
continue
|
||||
|
||||
w_vals = 1.0 - b0_cout[sig] / curve[sig]
|
||||
w = float(np.median(w_vals))
|
||||
|
||||
predicted = curve * (1.0 - w)
|
||||
rms = float(np.sqrt(np.mean((predicted - b0_cout) ** 2)))
|
||||
corr = float(np.corrcoef(curve[sig], b0_cout[sig])[0, 1])
|
||||
|
||||
if rms < best_rms:
|
||||
best_rms = rms
|
||||
best_params = (n_iters, w, corr)
|
||||
|
||||
print(f' iters={n_iters:2d}: w={w:.6f}, rms={rms:.4f}, corr={corr:.6f}')
|
||||
|
||||
n_iters, w, corr = best_params
|
||||
print(f'\nBest: iters={n_iters}, w={w:.6f}, rms={best_rms:.4f}, corr={corr:.6f}')
|
||||
return n_iters, w
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
if '--validate' in sys.argv:
|
||||
validate()
|
||||
else:
|
||||
print('Usage: detector_cascade.py --validate')
|
||||
@@ -0,0 +1,210 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
fit_vlaw_by_group.py — Fit VLAW parameters (α, β, c, Δ) per configuration group.
|
||||
|
||||
VLAW model (framed_model.cpp:205-208):
|
||||
cs = α * log1p(lvl / β) + c + (delta ? Δ : 0)
|
||||
applied_gain = 10^(-cs / 20) [gamma0=1 already absorbed into α,c,Δ]
|
||||
|
||||
Need to fit these for each (fc, q, sens) configuration group:
|
||||
t1kq: fc=800..1200, q=1.0, sens=12 (input tone1kq)
|
||||
t1k: fc=500..2000, q=1.0, sens=12 (input tone1k)
|
||||
al: fc=1000, q=1.0, sens=3..24 (input lvl_tone_lvX)
|
||||
res: fc=300..700, q=1.0, sens=12 (input resonant)
|
||||
dual: fc=500, q=0.1..10.0, sens=12 (input dual)
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import json
|
||||
|
||||
sys.path.insert(0, '/home/m/re-tools/scripts')
|
||||
import corpus
|
||||
|
||||
corpus.RB = '/home/m/re-tools/dsp/build/render48k'
|
||||
|
||||
# Reference errors from baseline_bridge.json (target)
|
||||
with open('scripts/baseline_bridge.json') as f:
|
||||
REF_ERRORS = json.load(f)
|
||||
|
||||
def structural_cases():
|
||||
out = []
|
||||
for name, inp, args, ref, f in corpus.build_cases():
|
||||
joined = [','.join(args)] if len(args) == 3 else args
|
||||
out.append((name, inp, joined, ref, f))
|
||||
return out
|
||||
|
||||
def run_vlaw(inp, args, alpha, beta, c, delta):
|
||||
"""Run render48k with VLAW parameters and return output path."""
|
||||
out = f'/tmp/vlaw_fit_{alpha}_{beta}_{c}_{delta}_{os.path.basename(inp)}.wav'
|
||||
env = {
|
||||
**os.environ,
|
||||
'RT_VLAW': '1',
|
||||
'RT_VLAW_ALPHA': str(alpha),
|
||||
'RT_VLAW_BETA': str(beta),
|
||||
'RT_VLAW_C': str(c),
|
||||
'RT_VLAW_DELTA': str(delta),
|
||||
'RT_SYN': '1',
|
||||
'RT_NOWARP': '1',
|
||||
'RT_NOIIR3': '1',
|
||||
'RT_IIR12': '0',
|
||||
}
|
||||
subprocess.run(
|
||||
[corpus.RB, inp, out] + args,
|
||||
capture_output=True, text=True, env=env,
|
||||
cwd='/home/m/re-tools'
|
||||
)
|
||||
return out
|
||||
|
||||
def eval_error(out, ref, f):
|
||||
"""Evaluate error in dB between output and reference at frequency f."""
|
||||
if not os.path.exists(out) or os.path.getsize(out) == 0:
|
||||
return None
|
||||
ref_sig = corpus.load_mono(ref)
|
||||
out_sig = corpus.load_mono(out)
|
||||
min_len = min(len(ref_sig), len(out_sig))
|
||||
ref_sig = ref_sig[-min_len:]
|
||||
out_sig = out_sig[-min_len:]
|
||||
ref_ta = corpus.ta(ref_sig, f)
|
||||
out_ta = corpus.ta(out_sig, f)
|
||||
return corpus.db(out_ta / ref_ta)
|
||||
|
||||
def group_key(name):
|
||||
return name.split('_')[0]
|
||||
|
||||
def evaluate_params(alpha, beta, c, delta, cases_subset=None):
|
||||
"""Evaluate VLAW params on all cases, return per-group mean abs error."""
|
||||
all_cases = structural_cases()
|
||||
if cases_subset:
|
||||
all_cases = [c for c in all_cases if group_key(c[0]) in cases_subset]
|
||||
|
||||
errs = {}
|
||||
|
||||
for name, inp, args, ref, f in all_cases:
|
||||
out = run_vlaw(inp, args, alpha, beta, c, delta)
|
||||
err = eval_error(out, ref, f)
|
||||
if err is not None:
|
||||
errs[name] = err
|
||||
|
||||
# Group stats
|
||||
groups = {}
|
||||
for k, v in errs.items():
|
||||
g = group_key(k)
|
||||
groups.setdefault(g, []).append(v)
|
||||
|
||||
out_stats = {g: float(np.mean(np.abs(v))) for g, v in groups.items()}
|
||||
out_stats['TOTAL'] = float(np.mean(np.abs(list(errs.values()))))
|
||||
return out_stats, errs
|
||||
|
||||
def fit_single_case(name, inp, args, ref, f, init_params):
|
||||
"""Grid search for best params on a single case."""
|
||||
alpha0, beta0, c0, delta0 = init_params
|
||||
best = None
|
||||
best_err = float('inf')
|
||||
|
||||
# Search around initial params
|
||||
alphas = np.linspace(max(0.5, alpha0-1), alpha0+1, 9)
|
||||
betas = np.linspace(max(0.1, beta0-0.2), beta0+0.2, 9)
|
||||
cs = np.linspace(max(0.0, c0-0.5), c0+0.5, 9)
|
||||
deltas = np.linspace(max(0.0, delta0-2), delta0+2, 9)
|
||||
|
||||
for alpha in alphas:
|
||||
for beta in betas:
|
||||
for c in cs:
|
||||
for delta in deltas:
|
||||
out = run_vlaw(inp, args, alpha, beta, c, delta)
|
||||
err = eval_error(out, ref, f)
|
||||
if err is not None and abs(err) < best_err:
|
||||
best_err = abs(err)
|
||||
best = (alpha, beta, c, delta, err)
|
||||
print(f' {name}: new best α={alpha:.3f}, β={beta:.3f}, c={c:.3f}, Δ={delta:.3f} => err={err:.3f} dB')
|
||||
|
||||
return best
|
||||
|
||||
def main():
|
||||
# Build case map by group
|
||||
all_cases = structural_cases()
|
||||
groups = {}
|
||||
for name, inp, args, ref, f in all_cases:
|
||||
g = group_key(name)
|
||||
groups.setdefault(g, []).append((name, inp, args, ref, f))
|
||||
|
||||
print("Available groups:", list(groups.keys()))
|
||||
for g, cases in groups.items():
|
||||
print(f" {g}: {len(cases)} cases")
|
||||
|
||||
# Current calibrated params for dual(q=0.5)
|
||||
dual_params = (3.2193, 0.4927, 0.5423, 6.9177)
|
||||
|
||||
# Test current params on all groups
|
||||
print("\n=== Testing current dual params on all groups ===")
|
||||
stats, _ = evaluate_params(*dual_params)
|
||||
for g in ['t1kq', 't1k', 'al', 'res', 'dual', 'comb']:
|
||||
if g in stats:
|
||||
print(f' {g}: {stats[g]:.3f} dB')
|
||||
|
||||
# For each group, pick a representative case and fit
|
||||
print("\n=== Fitting per group (representative case) ===")
|
||||
results = {}
|
||||
|
||||
# For dual, use q=0.5 as reference (already calibrated)
|
||||
if 'dual' in groups:
|
||||
# Find q=0.5 case
|
||||
for name, inp, args, ref, f in groups['dual']:
|
||||
if '0.5' in name:
|
||||
best = fit_single_case(name, inp, args, ref, f, dual_params)
|
||||
if best:
|
||||
results['dual'] = best[:4]
|
||||
break
|
||||
|
||||
# For t1kq, use fc=1000
|
||||
if 't1kq' in groups:
|
||||
for name, inp, args, ref, f in groups['t1kq']:
|
||||
if '1000' in name:
|
||||
best = fit_single_case(name, inp, args, ref, f, dual_params)
|
||||
if best:
|
||||
results['t1kq'] = best[:4]
|
||||
break
|
||||
|
||||
# For t1k, use fc=1000
|
||||
if 't1k' in groups:
|
||||
for name, inp, args, ref, f in groups['t1k']:
|
||||
if '1000' in name:
|
||||
best = fit_single_case(name, inp, args, ref, f, dual_params)
|
||||
if best:
|
||||
results['t1k'] = best[:4]
|
||||
break
|
||||
|
||||
# For al, use sens=12
|
||||
if 'al' in groups:
|
||||
for name, inp, args, ref, f in groups['al']:
|
||||
if '12' in name:
|
||||
best = fit_single_case(name, inp, args, ref, f, dual_params)
|
||||
if best:
|
||||
results['al'] = best[:4]
|
||||
break
|
||||
|
||||
# For res, use fc=500
|
||||
if 'res' in groups:
|
||||
for name, inp, args, ref, f in groups['res']:
|
||||
if '500' in name:
|
||||
best = fit_single_case(name, inp, args, ref, f, dual_params)
|
||||
if best:
|
||||
results['res'] = best[:4]
|
||||
break
|
||||
|
||||
# Print results
|
||||
print("\n=== FITTED VLAW PARAMETERS BY GROUP ===")
|
||||
for g, (alpha, beta, c, delta) in results.items():
|
||||
print(f'{g}: α={alpha:.4f}, β={beta:.4f}, c={c:.4f}, Δ={delta:.4f}')
|
||||
|
||||
# Save to JSON
|
||||
with open('/tmp/opencode/vlaw_params.json', 'w') as f:
|
||||
json.dump({g: {'alpha': a, 'beta': b, 'c': c, 'delta': d}
|
||||
for g, (a, b, c, d) in results.items()}, f, indent=2)
|
||||
print('\nSaved to /tmp/opencode/vlaw_params.json')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,158 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
fit_vlaw_params.py — Fit VLAW parameters (α, β, c, Δ, γ₀) per configuration group.
|
||||
|
||||
VLAW model (framed_model.cpp:198-200):
|
||||
cs = α * log1p(lvl / β) + c + (delta ? Δ : 0)
|
||||
applied_gain = 10^(-γ₀ * cs / 20)
|
||||
|
||||
Currently hardcoded for dual(q=0.5): α=3.2193, β=0.4927, c=0.5423, Δ=7.46-0.5423, γ₀=1.79
|
||||
|
||||
Need to fit these for each (fc, q, sens) configuration group:
|
||||
t1kq: fc=800..1200, q=1.0, sens=12
|
||||
t1k: fc=500..2000, q=1.0, sens=12
|
||||
al: fc=1000, q=1.0, sens=3..24
|
||||
res: fc=300..700, q=1.0, sens=12
|
||||
dual: fc=500, q=0.1..10.0, sens=12
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
sys.path.insert(0, '/home/m/re-tools/scripts')
|
||||
import corpus
|
||||
|
||||
corpus.RB = '/home/m/re-tools/dsp/build/render48k'
|
||||
|
||||
def structural_cases():
|
||||
out = []
|
||||
for name, inp, args, ref, f in corpus.build_cases():
|
||||
joined = [','.join(args)] if len(args) == 3 else args
|
||||
out.append((name, inp, joined, ref, f))
|
||||
return out
|
||||
|
||||
def group_key(name):
|
||||
return name.split('_')[0]
|
||||
|
||||
def load_ref_errors():
|
||||
"""Load baseline_bridge.json for target errors."""
|
||||
with open('scripts/baseline_bridge.json') as f:
|
||||
return json.load(f)
|
||||
|
||||
def render_vlaw(inp, out, args, alpha, beta, c, delta, gamma0, extra_env=None):
|
||||
"""Run render48k with VLAW parameters."""
|
||||
env = {
|
||||
**os.environ,
|
||||
'RT_VLAW': '1',
|
||||
'RT_VLAW_ALPHA': str(alpha),
|
||||
'RT_VLAW_BETA': str(beta),
|
||||
'RT_VLAW_C': str(c),
|
||||
'RT_VLAW_DELTA': str(delta),
|
||||
'RT_VLAW_GAMMA0': str(gamma0),
|
||||
'RT_SYN': '1',
|
||||
'RT_NOWARP': '1',
|
||||
'RT_NOIIR3': '1',
|
||||
'RT_IIR12': '0',
|
||||
}
|
||||
if extra_env:
|
||||
env.update(extra_env)
|
||||
subprocess.run(
|
||||
[corpus.RB, inp, out] + args,
|
||||
capture_output=True, text=True, env=env,
|
||||
cwd='/home/m/re-tools'
|
||||
)
|
||||
|
||||
def eval_config(alpha, beta, c, delta, gamma0, cases_subset=None):
|
||||
"""Evaluate VLAW params on cases, return per-group mean abs error."""
|
||||
all_cases = structural_cases()
|
||||
if cases_subset:
|
||||
all_cases = [c for c in all_cases if group_key(c[0]) in cases_subset]
|
||||
|
||||
refs = load_ref_errors()
|
||||
errs = {}
|
||||
|
||||
for name, inp, args, ref, f in all_cases:
|
||||
out = f'/tmp/vlaw_fit_{name}.wav'
|
||||
render_vlaw(inp, out, args, alpha, beta, c, delta, gamma0)
|
||||
|
||||
if not os.path.exists(out) or os.path.getsize(out) == 0:
|
||||
errs[name] = 999.0
|
||||
continue
|
||||
|
||||
try:
|
||||
ref_sig = corpus.load_mono(ref)
|
||||
out_sig = corpus.load_mono(out)
|
||||
min_len = min(len(ref_sig), len(out_sig))
|
||||
ref_sig = ref_sig[-min_len:]
|
||||
out_sig = out_sig[-min_len:]
|
||||
|
||||
ref_ta = corpus.ta(ref_sig, f)
|
||||
out_ta = corpus.ta(out_sig, f)
|
||||
err_db = corpus.db(out_ta / ref_ta)
|
||||
errs[name] = err_db
|
||||
except Exception as e:
|
||||
print(f"Error on {name}: {e}")
|
||||
errs[name] = 999.0
|
||||
|
||||
# Group stats
|
||||
groups = {}
|
||||
for k, v in errs.items():
|
||||
g = group_key(k)
|
||||
groups.setdefault(g, []).append(v)
|
||||
|
||||
out = {g: float(np.mean(np.abs(v))) for g, v in groups.items()}
|
||||
out['TOTAL'] = float(np.mean(np.abs(list(errs.values()))))
|
||||
return out, errs
|
||||
|
||||
def fit_alpha_beta_c(cases_to_fit):
|
||||
"""Coordinate descent on (α, β, c) for a specific case group."""
|
||||
# For now, grid search
|
||||
best = None
|
||||
best_err = float('inf')
|
||||
|
||||
# Search ranges around current dual(q=0.5) values
|
||||
for alpha in np.linspace(2.5, 4.0, 8):
|
||||
for beta in np.linspace(0.3, 0.7, 8):
|
||||
for c in np.linspace(0.2, 1.0, 8):
|
||||
stats, _ = eval_config(alpha, beta, c, 6.9, 1.79, cases_to_fit)
|
||||
total = stats['TOTAL']
|
||||
if total < best_err:
|
||||
best_err = total
|
||||
best = (alpha, beta, c, stats)
|
||||
print(f" New best: α={alpha:.4f}, β={beta:.4f}, c={c:.4f}, TOTAL={total:.4f}")
|
||||
|
||||
return best
|
||||
|
||||
def main():
|
||||
# Build case map by group
|
||||
all_cases = structural_cases()
|
||||
groups = {}
|
||||
for name, inp, args, ref, f in all_cases:
|
||||
g = group_key(name)
|
||||
groups.setdefault(g, []).append(name)
|
||||
|
||||
print("Available groups:", list(groups.keys()))
|
||||
for g, names in groups.items():
|
||||
print(f" {g}: {len(names)} cases")
|
||||
|
||||
# Start with dual group (already calibrated)
|
||||
print("\n=== Testing dual(q=0.5) baseline ===")
|
||||
stats, errs = eval_config(3.2193, 0.4927, 0.5423, 6.9177, 1.79, ['dual'])
|
||||
print(f"Dual stats: {stats}")
|
||||
|
||||
# Now fit for each group
|
||||
for g in ['t1kq', 't1k', 'al', 'res', 'dual']:
|
||||
if g not in groups:
|
||||
continue
|
||||
print(f"\n=== Fitting {g} ===")
|
||||
best = fit_alpha_beta_c([g])
|
||||
if best:
|
||||
alpha, beta, c, stats = best
|
||||
print(f" {g} best: α={alpha:.4f}, β={beta:.4f}, c={c:.4f}")
|
||||
print(f" Stats: {stats}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Quick VLAW parameter grid search - test fewer combos per case.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import json
|
||||
|
||||
sys.path.insert(0, '/home/m/re-tools/scripts')
|
||||
import corpus
|
||||
|
||||
corpus.RB = '/home/m/re-tools/dsp/build/render48k'
|
||||
|
||||
with open('scripts/baseline_bridge.json') as f:
|
||||
REF_ERRORS = json.load(f)
|
||||
|
||||
def structural_cases():
|
||||
out = []
|
||||
for name, inp, args, ref, f in corpus.build_cases():
|
||||
joined = [','.join(args)] if len(args) == 3 else args
|
||||
out.append((name, inp, joined, ref, f))
|
||||
return out
|
||||
|
||||
def run_vlaw(inp, args, alpha, beta, c, delta):
|
||||
out = f'/tmp/vlaw_{alpha}_{beta}_{c}_{delta}_{os.path.basename(inp)}.wav'
|
||||
env = {
|
||||
**os.environ,
|
||||
'RT_VLAW': '1',
|
||||
'RT_VLAW_ALPHA': str(alpha),
|
||||
'RT_VLAW_BETA': str(beta),
|
||||
'RT_VLAW_C': str(c),
|
||||
'RT_VLAW_DELTA': str(delta),
|
||||
'RT_SYN': '1', 'RT_NOWARP': '1', 'RT_NOIIR3': '1', 'RT_IIR12': '0',
|
||||
}
|
||||
subprocess.run([corpus.RB, inp, out] + args, capture_output=True, env=env, cwd='/home/m/re-tools')
|
||||
return out
|
||||
|
||||
def eval_error(out, ref, f):
|
||||
if not os.path.exists(out) or os.path.getsize(out) == 0:
|
||||
return None
|
||||
ref_sig = corpus.load_mono(ref)
|
||||
out_sig = corpus.load_mono(out)
|
||||
min_len = min(len(ref_sig), len(out_sig))
|
||||
ref_sig = ref_sig[-min_len:]
|
||||
out_sig = out_sig[-min_len:]
|
||||
ref_ta = corpus.ta(ref_sig, f)
|
||||
out_ta = corpus.ta(out_sig, f)
|
||||
return corpus.db(out_ta / ref_ta)
|
||||
|
||||
def group_key(name):
|
||||
return name.split('_')[0]
|
||||
|
||||
all_cases = structural_cases()
|
||||
groups = {}
|
||||
for name, inp, args, ref, f in all_cases:
|
||||
g = group_key(name)
|
||||
groups.setdefault(g, []).append((name, inp, args, ref, f))
|
||||
|
||||
# Pick one case per group
|
||||
rep_cases = {}
|
||||
for g in ['t1kq', 't1k', 'al', 'res', 'dual']:
|
||||
if g in groups:
|
||||
# Pick middle-ish case
|
||||
cases = groups[g]
|
||||
rep_cases[g] = cases[len(cases)//2]
|
||||
|
||||
print("Representative cases:")
|
||||
for g, (name, inp, args, ref, f) in rep_cases.items():
|
||||
print(f" {g}: {name}")
|
||||
|
||||
# Test a small grid around dual params
|
||||
dual_params = (3.2193, 0.4927, 0.5423, 6.9177)
|
||||
|
||||
print("\n=== Grid search per group ===")
|
||||
results = {}
|
||||
|
||||
for g, (name, inp, args, ref, f) in rep_cases.items():
|
||||
print(f"\n--- {g} ({name}) ---")
|
||||
best = None
|
||||
best_err = float('inf')
|
||||
|
||||
# Coarse grid
|
||||
alphas = np.linspace(1.0, 5.0, 5)
|
||||
betas = np.linspace(0.2, 0.8, 5)
|
||||
cs = np.linspace(-0.5, 2.0, 5)
|
||||
deltas = np.linspace(0.0, 12.0, 5)
|
||||
|
||||
for alpha in alphas:
|
||||
for beta in betas:
|
||||
for c in cs:
|
||||
for delta in deltas:
|
||||
out = run_vlaw(inp, args, alpha, beta, c, delta)
|
||||
err = eval_error(out, ref, f)
|
||||
if err is not None and abs(err) < best_err:
|
||||
best_err = abs(err)
|
||||
best = (alpha, beta, c, delta, err)
|
||||
print(f' {name}: α={alpha:.3f}, β={beta:.3f}, c={c:.3f}, Δ={delta:.3f} => {err:.3f} dB')
|
||||
|
||||
if best:
|
||||
results[g] = best[:4]
|
||||
print(f' BEST {g}: α={best[0]:.4f}, β={best[1]:.4f}, c={best[2]:.4f}, Δ={best[3]:.4f} => {best[4]:.3f} dB')
|
||||
|
||||
print("\n=== SUMMARY ===")
|
||||
for g, (a, b, c, d) in results.items():
|
||||
print(f'{g}: α={a:.4f}, β={b:.4f}, c={c:.4f}, Δ={d:.4f}')
|
||||
|
||||
with open('/tmp/opencode/vlaw_params.json', 'w') as f:
|
||||
json.dump({g: {'alpha': a, 'beta': b, 'c': c, 'delta': d}
|
||||
for g, (a, b, c, d) in results.items()}, f, indent=2)
|
||||
print('\nSaved to /tmp/opencode/vlaw_params.json')
|
||||
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Quick VLAW parameter test - just evaluate a few configs per group.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
sys.path.insert(0, '/home/m/re-tools/scripts')
|
||||
import corpus
|
||||
|
||||
corpus.RB = '/home/m/re-tools/dsp/build/render48k'
|
||||
|
||||
def run_one(inp, args, alpha, beta, c, delta, gamma0=1.79):
|
||||
out = f'/tmp/vlaw_test_{alpha}_{beta}_{c}.wav'
|
||||
env = {
|
||||
**os.environ,
|
||||
'RT_VLAW': '1',
|
||||
'RT_VLAW_ALPHA': str(alpha),
|
||||
'RT_VLAW_BETA': str(beta),
|
||||
'RT_VLAW_C': str(c),
|
||||
'RT_VLAW_DELTA': str(delta),
|
||||
'RT_SYN': '1',
|
||||
'RT_NOWARP': '1',
|
||||
'RT_NOIIR3': '1',
|
||||
'RT_IIR12': '0',
|
||||
}
|
||||
subprocess.run(
|
||||
[corpus.RB, inp, out] + args,
|
||||
capture_output=True, text=True, env=env,
|
||||
cwd='/home/m/re-tools'
|
||||
)
|
||||
return out
|
||||
|
||||
def eval_one(name, inp, args, ref, f, alpha, beta, c, delta):
|
||||
out = run_one(inp, args, alpha, beta, c, delta)
|
||||
if not os.path.exists(out) or os.path.getsize(out) == 0:
|
||||
return None
|
||||
ref_sig = corpus.load_mono(ref)
|
||||
out_sig = corpus.load_mono(out)
|
||||
min_len = min(len(ref_sig), len(out_sig))
|
||||
ref_sig = ref_sig[-min_len:]
|
||||
out_sig = out_sig[-min_len:]
|
||||
ref_ta = corpus.ta(ref_sig, f)
|
||||
out_ta = corpus.ta(out_sig, f)
|
||||
return corpus.db(out_ta / ref_ta)
|
||||
|
||||
# Test current VLAW params on different groups
|
||||
test_params = (3.2193, 0.4927, 0.5423, 6.9177)
|
||||
|
||||
all_cases = []
|
||||
for name, inp, args, ref, f in corpus.build_cases():
|
||||
joined = [','.join(args)] if len(args) == 3 else args
|
||||
all_cases.append((name, inp, joined, ref, f))
|
||||
|
||||
# Pick one representative case per group
|
||||
groups = {}
|
||||
for name, inp, args, ref, f in all_cases:
|
||||
g = name.split('_')[0]
|
||||
if g not in groups:
|
||||
groups[g] = (name, inp, args, ref, f)
|
||||
|
||||
print("Testing VLAW params (3.2193, 0.4927, 0.5423, 6.9177) on each group:")
|
||||
for g, (name, inp, args, ref, f) in groups.items():
|
||||
err = eval_one(name, inp, args, ref, f, *test_params)
|
||||
if err is not None:
|
||||
print(f" {name} ({g}): {err:.3f} dB")
|
||||
else:
|
||||
print(f" {name} ({g}): FAILED")
|
||||
Reference in New Issue
Block a user