Files

85 lines
3.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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')