修回声: 房间 IR 含 30ms 传播延迟(声源到麦克风 10.3m 的声程)且前两次反射与直达声同量级(30ms 处 0dB/40ms 处 -1.6dB), 与零延迟的干声相加 -> 湿声晚 30ms 还很响 = 回声; 处理 IR(裁前 22ms 静音 + 前 55ms 余弦渐入, 直达声段 0 -> -23.6dB)再把 gain 重标 0.126->0.4 补回混响量; 脉冲取证: 修前 120ms 处 -26dBFS 孤立峰(+12dB), 修后包络单调衰减无孤立峰; 新增 测试/test_echo.py 与 工具/重做混响IR.py, 原 IR 备份 .orig.wav
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
"""重做房间混响 IR: 剪掉直达声, 只留混响尾。
|
||||
|
||||
原 IR 前 20ms 是静音、主峰在 30ms(= 10.3m 的传播延迟), 前两次反射(30/40ms)与直达声
|
||||
同量级。它和零延迟的干声相加时, 湿声整体晚 30ms 且很响 -> 听感就是明显的回声。
|
||||
|
||||
处理:
|
||||
1. 从主峰前一点点开始裁(去掉纯静音段)
|
||||
2. 前 55ms 余弦渐入 —— 压掉直达声和过强的早期反射
|
||||
3. 之后原样保留(混响尾)
|
||||
|
||||
产出 reverb/房间混响IR-96k.wav(原文件备份为 .orig.wav), 并打印标定用的增益。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
from scipy.io import wavfile
|
||||
|
||||
IR = Path("reverb/房间混响IR-96k.wav")
|
||||
BAK = Path("reverb/房间混响IR-96k.orig.wav")
|
||||
FADE_MS = 55.0 # 渐入长度
|
||||
TRIM_MS = 22.0 # 裁掉的纯静音段
|
||||
TARGET_PEAK = 0.1493 # 原 IR 峰值(保持同量级, 免得到时候电平对不上)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""处理 IR。"""
|
||||
rate, data = wavfile.read(IR)
|
||||
x = data.astype(np.float64)
|
||||
if x.ndim > 1:
|
||||
x = x[:, 0]
|
||||
n0 = len(x)
|
||||
peak0 = float(np.abs(x).max())
|
||||
rms0 = float(np.sqrt((x ** 2).mean()))
|
||||
|
||||
if not BAK.exists():
|
||||
shutil.copy2(IR, BAK)
|
||||
print(f"已备份原 IR -> {BAK}")
|
||||
|
||||
trim = int(rate * TRIM_MS / 1000)
|
||||
y = x[trim:].copy()
|
||||
|
||||
# 前 FADE_MS 余弦渐入(升余弦的前半段)
|
||||
fade = int(rate * FADE_MS / 1000)
|
||||
if fade < len(y):
|
||||
t = np.arange(fade) / fade
|
||||
y[:fade] *= 0.5 * (1 - np.cos(np.pi * t))
|
||||
|
||||
peak1 = float(np.abs(y).max())
|
||||
rms1 = float(np.sqrt((y ** 2).mean()))
|
||||
print(f"原 IR: {n0} 样, 峰值 {peak0:.4f}, RMS {rms0:.6f}")
|
||||
print(f"新 IR: {len(y)} 样, 峰值 {peak1:.4f}, RMS {rms1:.6f}")
|
||||
print(f"峰值比 {peak1 / peak0:.4f} ({20 * np.log10(peak1 / peak0):+.2f} dB)")
|
||||
print(f"RMS 比 {rms1 / rms0:.4f} ({20 * np.log10(rms1 / rms0):+.2f} dB)")
|
||||
|
||||
# 保持峰值量级与原版一致(后面 gain 仍按 0.126 用, 电平不会跑)
|
||||
y *= TARGET_PEAK / peak1
|
||||
wavfile.write(IR, rate, y.astype(np.float32))
|
||||
print(f"已写入 {IR}")
|
||||
|
||||
print("\n新 IR 每 10ms 包络:")
|
||||
z = np.abs(y)
|
||||
for k in range(0, 14):
|
||||
a, b = int(rate * 0.01 * k), int(rate * 0.01 * (k + 1))
|
||||
if b > len(z):
|
||||
break
|
||||
print(f" {k * 10:4d} ms {20 * np.log10(max(z[a:b].max(), 1e-7)):7.1f} dB")
|
||||
|
||||
|
||||
main()
|
||||
Reference in New Issue
Block a user