res_power breakthrough: 500Hz residual solved (q0.1 err +0.00), decomp inventory, FUN_180563440 decoded
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys, numpy as np
|
||||
sys.path.insert(0, '/home/m/re-tools')
|
||||
from render_parity import load
|
||||
from scipy.optimize import minimize
|
||||
import wave
|
||||
|
||||
BT='/home/m/soothe-bt/'; FS=44100.0; GAIN=4.132
|
||||
|
||||
def bandres(f, fc, Q):
|
||||
w0 = fc*2*np.pi/FS; c,s=np.cos(w0),np.sin(w0)
|
||||
p=(s*0.5)/Q; a,a2=p*GAIN,p/GAIN
|
||||
A=[a+1,-2*c,1-a]; B=[a2+1,-2*c,1-a2]
|
||||
w=2*np.pi*np.asarray(f)/FS; z=np.exp(-1j*w)
|
||||
return np.abs(2.0*(B[0]+B[1]*z+B[2]*z*z)/(A[0]+A[1]*z+A[2]*z*z))
|
||||
|
||||
def warp(f): x=np.asarray(f)/2000.0; return 0.87*7.942*x/(7.942+x)
|
||||
|
||||
def lut_powerlaw(xv, C, A=0.0, B=1.0):
|
||||
"""FUN_180563440 power-law mode: centered=2*x-1, result=sign*10^(log10|/C)"""
|
||||
x = np.clip(xv, A, B)
|
||||
centered = 2.0 * (x - A) / (B - A) - 1.0 # map [A,B] → [-1,1]
|
||||
abs_c = np.abs(centered)
|
||||
result = np.where(abs_c > 1e-9,
|
||||
np.sign(centered) * np.power(10.0, np.log10(np.maximum(abs_c, 1e-9)) / C),
|
||||
0.0)
|
||||
return (result + 1.0) / 2.0 # remap [-1,1] → [0,1]
|
||||
|
||||
def lut_linear(xv, A=0.483, B=0.717):
|
||||
"""Linear mode: (B-A)*x+A"""
|
||||
return np.clip(A + (B - A) * np.clip(xv, 0, 1), 0, 1)
|
||||
|
||||
def frames(x, fc, Q, G_, W_, A_, C_lut=10.0, mode='powerlaw', NS=2048, hop=512, tatt=0.011, trel=0.08):
|
||||
win=np.sqrt(np.hanning(NS)); wsum=win.sum(); n=len(x)
|
||||
nfr=max(1,int(np.ceil((n-NS)/hop))+1)
|
||||
X=np.empty((nfr,NS//2+1),dtype=complex); freqs=np.fft.rfftfreq(NS,1/FS)
|
||||
res=bandres(freqs,fc,Q)
|
||||
att=np.exp(-hop/(tatt*FS)); rel=np.exp(-hop/(trel*FS))
|
||||
am=np.zeros(freqs.size); G=np.empty(X.shape)
|
||||
for m in range(nfr):
|
||||
s=m*hop; seg=np.zeros(NS); kk=min(NS,n-s); seg[:kk]=x[s:s+kk]
|
||||
F=np.fft.rfft(win*seg); X[m]=F; ac=2*np.abs(F)/wsum
|
||||
am=np.where(ac>am, att*am+(1-att)*ac, rel*am+(1-rel)*ac)
|
||||
xv=np.log10(np.maximum(am/np.maximum(res,1e-12),1e-9))
|
||||
if mode=='powerlaw':
|
||||
L = lut_powerlaw(xv, C_lut)
|
||||
else:
|
||||
L = lut_linear(xv)
|
||||
C = G_*L + W_*warp(freqs)**A_
|
||||
G[m] = np.maximum(1-np.minimum(C,0.95),1e-9)
|
||||
return X,G,win,hop,n
|
||||
|
||||
def synthe(X,G,win,hop,n):
|
||||
out=np.zeros(n); acc=np.zeros(n); NS=len(win)
|
||||
for m in range(X.shape[0]):
|
||||
seg=np.fft.irfft(X[m]*G[m])*win; s=m*hop; lay=min(NS,n-s)
|
||||
out[s:s+lay]+=seg[:lay]; acc[s:s+lay]+=(win*win)[:lay]
|
||||
return out/np.maximum(acc,1e-12)
|
||||
|
||||
def tone_cmp(x,f,seglen=0.75*FS):
|
||||
x=np.asarray(x)[-int(seglen):]; n=len(x); t=np.arange(n)/FS; w=2*np.pi*f
|
||||
return np.hypot(2*np.sum(x*np.cos(w*t))/n,2*np.sum(x*np.sin(w*t))/n)
|
||||
|
||||
def dB(v): return 20*np.log10(np.clip(v,1e-9,None))
|
||||
|
||||
x=np.mean(load(BT+'dual.wav'),axis=1)
|
||||
refs=[(q,f'dual_b1q_{q}.wav',f) for q in [0.1,1.0,10.0] for f in (500,2000)]
|
||||
tone_ref={(r,f):dB(tone_cmp(np.mean(load(BT+r),axis=1),f)) for _,r,f in refs}
|
||||
|
||||
def score(GWA, C_lut, mode):
|
||||
G_,W_,A_=GWA; tot=[]
|
||||
for q,ref,f in refs:
|
||||
X,G,win,hop,n=frames(x,500.0,q,G_,W_,A_,C_lut,mode)
|
||||
y=synthe(X,G,win,hop,n); tot.append(dB(tone_cmp(y,f))-tone_ref[(ref,f)])
|
||||
return np.array(tot)
|
||||
|
||||
# Test power-law mode with different C values
|
||||
print("=== POWER-LAW MODE ===")
|
||||
for C in [2.0, 3.0, 5.0, 8.0, 10.0, 15.0, 20.0]:
|
||||
def obj(p):
|
||||
return np.mean(np.abs(score(p, C, 'powerlaw')))
|
||||
r = minimize(obj, [1.0, 0.3, 1.0], method='Nelder-Mead', options=dict(maxiter=500))
|
||||
err = score(r.x, C, 'powerlaw')
|
||||
print(f' C={C:5.1f} G={r.x[0]:.3f} W={r.x[1]:.3f} A={r.x[2]:.3f} mean={np.mean(np.abs(err)):.3f} '
|
||||
f'errs=[{",".join(f"{e:+.2f}" for e in err)}]')
|
||||
|
||||
# Also test linear mode
|
||||
print("=== LINEAR MODE ===")
|
||||
def obj_lin(p):
|
||||
return np.mean(np.abs(score(p, 0, 'linear')))
|
||||
r = minimize(obj_lin, [1.0, 0.3, 1.0], method='Nelder-Mead', options=dict(maxiter=500))
|
||||
err = score(r.x, 0, 'linear')
|
||||
print(f' G={r.x[0]:.3f} W={r.x[1]:.3f} A={r.x[2]:.3f} mean={np.mean(np.abs(err)):.3f} '
|
||||
f'errs=[{",".join(f"{e:+.2f}" for e in err)}]')
|
||||
Reference in New Issue
Block a user