3. 单通道维纳滤波混响抑制

单通道维纳滤波混响抑制 学习卡片

任务说明

本案例实现单通道维纳滤波混响抑制。输入为带混响的语音信号(单声道,16kHz采样),目标是估计每个时频点的维纳增益,对混响语音进行谱修正,输出增强后的语音。实现采用分帧加窗、短时傅里叶变换(STFT)、噪声/混响功率谱估计、维纳增益计算与逆变换(ISTFT)的完整链路,不依赖外部音频库,仅使用NumPy完成核心处理。

算法与实现

维纳滤波在频域逐帧进行:对带混响信号分帧加窗后做FFT,得到复数谱X(k,l)。维纳增益由先验信噪比ξ(k,l)决定:

G(k,l) = ξ(k,l) / (1 + ξ(k,l))

其中先验信噪比采用判决引导法(Decision-Directed)递归估计:

ξ(k,l) = α·|G(k,l-1)·X(k,l-1)|²/P(k,l) + (1-α)·max(γ(k,l)-1, 0)

γ(k,l)为后验信噪比,P(k,l)为噪声/混响功率谱,通过语音活动检测(VAD)在无声段递归更新。增强谱为Y(k,l)=G(k,l)·X(k,l),经ISTFT和重叠相加得到时域输出。

import numpy as np

def wiener_filter_enhance(x, fs=16000, frame_len=512, hop=128, alpha=0.98, beta=0.98):
    """
    单通道维纳滤波混响抑制
    x: 一维float数组,带混响语音信号
    返回: 增强后的一维float数组(长度与x相同)
    """
    n_frames = 1 + (len(x) - frame_len) // hop
    # 预分配帧矩阵
    frames = np.zeros((n_frames, frame_len))
    for i in range(n_frames):
        start = i * hop
        frames[i, :] = x[start:start + frame_len]

    # 汉宁窗
    win = np.hanning(frame_len)
    frames = frames * win

    # STFT
    X = np.fft.rfft(frames, axis=1)  # (n_frames, frame_len//2+1)
    n_freq = X.shape[1]

    # 功率谱平滑
    P = np.mean(np.abs(X[:10, :])**2, axis=0)  # 初始噪声估计(前10帧)
    G_prev = np.ones(n_freq)
    Y = np.zeros_like(X, dtype=complex)

    for i in range(n_frames):
        mag2 = np.abs(X[i, :])**2

        # 后验信噪比
        gamma = mag2 / (P + 1e-10)

        # 先验信噪比(判决引导)
        xi = alpha * (G_prev**2 * mag2 / (P + 1e-10)) + (1 - alpha) * np.maximum(gamma - 1, 0)

        # 维纳增益
        G = xi / (1 + xi)
        G = np.clip(G, 0.01, 1.0)

        # 增强谱
        Y[i, :] = G * X[i, :]

        # VAD:若当前帧能量高于噪声底限,则更新噪声估计
        frame_energy = np.sum(mag2) / n_freq
        if frame_energy < 1.5 * np.mean(P):
            P = beta * P + (1 - beta) * mag2

        G_prev = G

    # ISTFT
    y_frames = np.fft.irfft(Y, n=frame_len, axis=1) * win
    y_out = np.zeros(len(x))
    overlap_sum = np.zeros(len(x))
    for i in range(n_frames):
        start = i * hop
        y_out[start:start + frame_len] += y_frames[i, :]
        overlap_sum[start:start + frame_len] += win**2

    # 归一化(消除重叠相加增益)
    eps = 1e-10
    y_out = y_out / (overlap_sum + eps)
    return y_out

# 示例:生成带混响的测试信号(仅用于验证流程)
if __name__ == "__main__":
    fs = 16000
    t = np.arange(fs * 1.0) / fs
    clean = 0.5 * np.sin(2 * np.pi * 440 * t) * (t < 0.5)  # 0.5秒440Hz音
    # 简单混响模拟:叠加延迟衰减副本
    reverb = clean.copy()
    for delay, atten in [(800, 0.6), (1600, 0.4), (2400, 0.3)]:
        if delay < len(clean):
            reverb[delay:] += atten * clean[:-delay]
    enhanced = wiener_filter_enhance(reverb)
    # 输出长度校验
    assert len(enhanced) == len(reverb)
    print("处理完成,输出长度:", len(enhanced))

落地与验证

依赖与运行:仅需NumPy(pip install numpy)。将代码保存为wiener_denoise.py,运行python wiener_denoise.py。程序会生成一个440Hz正弦测试信号并叠加延迟衰减副本模拟混响,经维纳滤波后输出长度与输入一致,控制台打印处理完成信息。

验证方法:将clean、reverb、enhanced分别写入WAV文件(可用scipy.io.wavfile.write),通过以下判定标准评估:

  • 波形对比:增强信号的前0.5秒(发声段)幅度应接近原始clean信号,而混响尾音(0.5秒后)幅度应显著低于reverb信号;
  • 信噪比改善:计算增强后与clean的均方误差(MSE),应小于reverb与clean的MSE;
  • 频谱观察:对增强信号做STFT,高频段混响拖尾的能量应被压制。

判定标准:若增强信号在混响段的能量较输入降低至少3dB,且发声段波形无明显畸变,则视为通过。上述测试信号仅用于验证算法链路正确性,实际语音处理需替换为真实带混响语音文件。

单通道维纳滤波混响抑制处理链路 带混响语音 分帧+汉宁窗 512点/128hop STFT 维纳增益 G=ξ/(1+ξ) 增强语音 判决引导先验SNR估计 噪声/混响功率 谱P(k,l)更新 VAD控制更新 帧长512(32ms@16kHz) | 帧移128(8ms) | α=0.98 | 频率分辨率31.25Hz 输出经ISTFT与重叠相加(OLA)重建时域信号