B.7: XML mode=1 исполняет case8 (Q=+0x80c, fvar=sens/20), не case1; Q_eff-маппинг
This commit is contained in:
@@ -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')
|
||||
Reference in New Issue
Block a user