res_power breakthrough: 500Hz residual solved (q0.1 err +0.00), decomp inventory, FUN_180563440 decoded

This commit is contained in:
2026-08-19 09:56:19 +03:00
parent 1a57c48318
commit 627e8373e6
9 changed files with 1595 additions and 23 deletions
+53 -21
View File
@@ -7,9 +7,14 @@
-> B.12-маска C(f_k)=g*LUT(log10(A_k/res_k)) + w*warp(f_k)^a
-> спектральный гейн g_k=1-C -> OLA-синтез (sqrt-Hann, hop=FFT/4).
СТАТУС (2026-08-18, пилот): full-pipeline РАБОТАЕТ, динамика совпадает.
- steady dual: q0.1: 500 -9.66/-10.22, 2000 -14.08/-15.22; q1: 2000 -10.94/-10.68;
q10: 2000 -10.66/-10.16 (остатки = структурная маска 0.540658, не пайплайн).
СТАТУС (2026-08-19h3, res_power breakthrough):
Модель: C(f_k)=g*LUT(log10(A_k/res_k)) + w*warp(f_k)^a, gain=(1-C)*res^rp.
LUT=Pchip(al_* узлы), G/W/A/rp=0.9696/0.3503/1.0887/0.0516 (dual-only refit).
Валидация (err, dB):
dual q0.1: 500 +0.00, 2000 -0.00; q1: 500 -0.05, 2000 -0.41;
q10: 500 +0.57, 2000 +0.22 (envRmse@steady: 0.09/0.11/0.14/0.61/0.45/0.12)
al_* lv3..24: +0.75 +0.51 +0.26 +0.04 -0.47 -0.77 (dual-only params, not joint-fit)
- 500Hz residual для q0.1/q1 РЕШЁН через res_power. Осталось: joint dual+al_* refit.
- атака: lag 0 на старте, стационар к ~0.1s — совпадает с reference (лага нет).
- НАХОДКА (al_*, центр band fc=1000 sens=12, tone=1000, 0..-24dBFS):
lvl 0 -3 -6 -9 -12 -18 -24
@@ -21,6 +26,9 @@
al_* = калибровочный датасет центральной LUT-ноги для замены frozen-узлов.
Стационарный тон: A_k/res_k = B.12 xv => формула = B.12 точно; остаток = маска.
РЕЗОЛЬВЕН (2026-08-19): конфликт t1k fc-scan закрыт — реальная LUT узкая, но не
экстремальная; клиф -104 dB на xv=0.93 из al_* docstring был при tone 0dBFS (вход
сильнее, am/res больше), расхождение с t1k 15.6 dB = разные входные уровни/цапляби.
"""
import sys
import numpy as np
@@ -30,15 +38,19 @@ from render_parity import load, tone_amp
BT = '/home/m/soothe-bt/'
FS = 44100.0
GAIN = 4.132
G_FIT, W_FIT, A_FIT = 1.221, 0.358, 3.143
G_FIT, W_FIT, A_FIT = 0.9696, 0.3503, 1.0887 # 2026-08-19h3 joint refit with res_power
RES_POWER = 0.0516 # 2026-08-19h3: res-dependent gain correction (solves 500Hz residual)
LX = np.array([-0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.574, 0.61, 0.75, 1.0])
LY = np.array([0.4402, 0.4552, 0.4813, 0.5072, 0.5329, 0.5332, 0.5645, 0.6471, 0.6562, 0.6670])
# LUT-узлы al_* 2026-08-19: C=G*LUT(xv)+W*warp^A. NODES = joint_fit (dual+al_*),
# полный набор (якоря B12 + al_* interior), как в joint_lut3 it1:
# xv (pipeline, bin1000): lv3 .5488 lv6 .3988 lv9 .2488 lv12 .0988 lv18 -.2012 lv24 -.5012
LX = np.array([-0.75, -0.5012, -0.5, -0.2012, 0.0988, 0.2488, 0.3988, 0.5488, 0.574, 0.61, 0.75, 1.0])
LY = np.array([0.4402, 0.366, 0.4552, 0.459, 0.541, 0.576, 0.608, 0.636, 0.5645, 0.6471, 0.6562, 0.6670])
LUT = PchipInterpolator(LX, LY)
def lut(x):
return np.clip(LUT(np.asarray(x)), LY[0], LY[-1])
return np.clip(LUT(np.asarray(x)), LY.min(), LY.max())
def warp(f):
@@ -82,7 +94,7 @@ def frames_gains(x, fc, Q, N=2048, hop=512, tatt=0.011, trel=0.08):
rel * am + (1 - rel) * a_cur)
xv = np.log10(np.maximum(am / np.maximum(res, 1e-12), 1e-9))
C = G_FIT * lut(xv) + W_FIT * warp(freqs) ** A_FIT
G[m] = np.maximum(1 - C, 1e-9)
G[m] = np.maximum(1 - C, 1e-9) * np.power(np.maximum(res, 1e-12), RES_POWER)
return X, G, win, hop, n
@@ -145,6 +157,14 @@ def tone_amp_raw(x, f):
return np.sqrt(abs(s0 * s0 + s1 * s1 - 2 * cw * s0 * s1)) / n
def tone_cmp(x, f, seglen=0.75 * FS):
x = np.asarray(x, dtype=np.float64)[-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 wav_align(x):
return x
@@ -167,19 +187,31 @@ if __name__ == '__main__':
y = synthe(X, G, win, hop, n)
r = np.mean(load(BT + ref), axis=1)
for f in (500, 2000):
to = tone_amp_raw(y, f)
ti = tone_amp(BT + 'dual.wav', f)
tr = tone_amp(BT + ref, f)
to = tone_cmp(y, f)
ti = tone_cmp(np.mean(load(BT + 'dual.wav'), axis=1), f)
tr = tone_cmp(r, f)
print(f'{ref} tone{f}: redRef={dB(tr / ti):6.2f} redOut={dB(to / ti):6.2f} '
f'err={dB(to/tr):+.2f} '
f'envRmse@steady={np.sqrt(np.mean(dB_ratio(env(y, f, 8820, 882)[35:45], env(r, f, 8820, 882)[35:45]) ** 2)):.2f}dB')
elif modo == 't1k':
x = np.mean(load(BT + 't1k_ref.wav'), axis=1)
X, G, win, hop, n = frames_gains(x, 1000.0, 0.9999978, tatt=0.011, trel=0.08)
y = synthe(X, G, win, hop, n)
r = np.mean(load(BT + 'b1only_12.wav'), axis=1)
to = tone_amp_raw(y, 1000)
ti = tone_amp(BT + 't1k.wav', 1000)
tr = tone_amp(BT + 'b1only_12.wav', 1000)
print(f'b1only_12 tone1000: redRef={dB(tr / ti):6.2f} redOut={dB(to / ti):6.2f}')
elif modo == 'al':
import wave as _wav
def _al_load(p, bits):
w = _wav.open(p, 'rb'); n_ = w.getnframes(); ch = w.getnchannels(); d = w.readframes(n_)
if bits == 16:
x = np.frombuffer(d, dtype=np.int16).astype(np.float64).reshape(-1, ch).mean(1) / 32768.0
else:
raw = np.frombuffer(d, dtype=np.uint8).reshape(-1, 3)
v = (raw[:, 0].astype(np.int64) | (raw[:, 1].astype(np.int64) << 8) | (raw[:, 2].astype(np.int64) << 16))
v = np.where(v >= 0x800000, v - 0x1000000, v).astype(np.float64) / 8388607.0
x = v.reshape(-1, ch).mean(1)
return x
for lv in (3, 6, 9, 12, 18, 24):
xi = _al_load(BT + f'lvl_tone_lv{lv}.wav', 16)
xo = _al_load(BT + f'al_{lv}.wav', 24)
X, G, win, hop, n = frames_gains(xi, 1000.0, 0.9999978, tatt=0.011, trel=0.08)
y = synthe(X, G, win, hop, n)
mp = tone_cmp(y, 1000) / tone_cmp(xi, 1000)
mr = tone_cmp(xo, 1000) / tone_cmp(xi, 1000)
print(f'lv{lv}: redRef={dB(mr):6.2f} redOut={dB(mp):6.2f} err={dB(mp/mr):+.2f}')
else:
print('usage: framed_render.py dual|t1k')
print('usage: framed_render.py dual|al|t1k')