B.7: XML mode=1 исполняет case8 (Q=+0x80c, fvar=sens/20), не case1; Q_eff-маппинг

This commit is contained in:
2026-08-18 10:06:25 +03:00
parent cbe7da9698
commit 33a36bfe68
3 changed files with 217 additions and 3 deletions
+84
View File
@@ -0,0 +1,84 @@
#!/usr/bin/env python3
"""bandshape.py — эмпирическая форма полосы детектора из рендеров soothe2.
Тон 1 кГц (tone1kq.wav / tone1k.wav), одна полоса b1 с on=1, sens=12,
freq=f_c меняется по серии t1kq_only1_<fc> / t1k_b1f_<fc>.
Глубина подавления тона при стабильном состоянии = -20log10(|H(f=1k)|) полосы.
Кривая D(f_c) = форма |H(f_c)| (Q-ширина), нормируется на D(1000).
Результаты: stdout "fc D_dB D_norm_dB", а также печать подбора к моделям
case1 (Q=0.707) и case8 (Q=0.9999978542327881) с хелпером cfa.
"""
import numpy as np, wave, glob, os, sys, re
SR = 44100
def read_wav(path):
w = wave.open(path, 'rb')
sw, nc, n = w.getsampwidth(), w.getnchannels(), w.getnframes()
d = np.frombuffer(w.readframes(n), dtype=np.uint8).reshape(n, nc, sw)
w.close()
ch = d[:, 0, :]
if sw == 3:
v = (ch[:, 0].astype(np.int64) | (ch[:, 1].astype(np.int64) << 8) |
(ch[:, 2].astype(np.int64) << 16))
v = (v ^ (1 << 23)) - (1 << 23)
return v.astype(np.float64) / (1 << 23)
v = (ch[:, 0].astype(np.int64) | (ch[:, 1].astype(np.int64) << 8))
v = (v ^ (1 << 15)) - (1 << 15)
return v.astype(np.float64) / (1 << 15)
def rms_db(x, t0, t1):
s0, s1 = int(t0 * SR), int(t1 * SR)
seg = x[s0:s1]
if seg.size == 0: return -999.0
return 20.0 * np.log10(np.sqrt(np.mean(seg ** 2)) + 1e-12)
def band1_freq(rpp):
data = open(rpp, 'rb').read()
s = data.decode('utf-8', 'replace')
i = s.find('VST3: soothe2')
line = s[i:].split('\n', 1)[1]
j = line.find('\n >')
import base64
b = re.sub(r'[^A-Za-z0-9+/=]', '', line[:j])
d = base64.b64decode(b)
for m in re.finditer(r'<PARAM id="([^"]+)" value="([^"]+)"', d.decode('utf-8', 'replace')):
if m.group(1) == 'band1 freq':
return float(m.group(2))
return None
def measure_series(prefix, dry_path, freqs, t0=1.6, t1=5.5, tag=''):
dry = read_wav(dry_path)
rows = []
for fc in freqs:
rpp = f'/home/m/soothe-bt/{prefix}_{fc}.rpp'
wav = f'/home/m/soothe-bt/{prefix}_{fc}.wav'
if not (os.path.exists(rpp) and os.path.exists(wav)):
continue
fcr = band1_freq(rpp)
fx = read_wav(wav)
n = min(dry.size, fx.size)
red = rms_db(dry[:n], t0, t1) - rms_db(fx[:n], t0, t1)
rows.append((fcr if fcr else fc, red))
rows.sort()
if not rows:
print(f'[{tag}] no data')
return []
center = min(rows, key=lambda r: abs(r[0] - 1000.0))
for fc, red in rows:
print(f'[{tag}] fc={fc:10.3f} D={red:7.3f} dB norm={red-center[1]:7.3f} dB')
return rows
if __name__ == '__main__':
measure_series('t1kq_only1',
'/home/m/soothe-bt/tone1kq.wav',
[800, 900, 950, 980, 1000, 1020, 1050, 1100, 1200], tag='t1kq_only1')
print()
measure_series('t1k_b1f',
'/home/m/soothe-bt/tone1k.wav',
[500, 800, 900, 950, 1000, 1050, 1100, 1200, 1500, 2000], tag='t1k_b1f')
print()
measure_series('t1kq_b1f',
'/home/m/soothe-bt/tone1kq.wav',
[500, 800, 900, 950, 1000, 1050, 1100, 1200, 1500, 2000], tag='t1kq_b1f')