WebRTC中的Wiener滤波降噪算法#
在实时语音通信系统中,背景噪声的抑制对于提升通话清晰度至关重要。Google 的 WebRTC 项目在其开源语音引擎中实现了高质量的语音降噪模块,其中 Wiener 滤波器作为核心组成部分,结合语音概率估计、多特征建模、噪声谱跟踪等模块构建了一个高度实用的增强框架。
本文将从 Wiener 滤波理论入手,逐步解析 WebRTC 中该算法的工程实现,其中噪声估计模块和语音概率估计模块已经在之前介绍了,有需要的可以翻阅。
一、Wiener滤波基础#
Wiener 滤波器是一种最小均方误差(MMSE)估计器,在频域降噪中用于构造一个频点增益函数,以最大程度还原语音信号。
其基本形式为:
$$
G(f) = \frac{\xi(f)}{\xi(f) + 1}
$$
其中:
• $\xi(f)$:先验信噪比(prior SNR)
• $G(f)$:频率点 f 的 Wiener 增益
此增益用于对输入频谱进行缩放,从而滤除噪声成分。
二、WebRTC中的Wiener滤波结构#
在 WebRTC 降噪模块中,Wiener 滤波不是孤立运行,而是集成在一套完整的噪声估计与语音概率建模框架中:
1
2
3
4
5
6
7
8
9
| graph TD
A[输入音频帧] --> B[STFT 分帧]
B --> C[信号谱 + 噪声谱]
C --> D[SNR估计(后验+先验)]
C --> E[LRT / Flatness / Diff 特征提取]
D & E --> F[Wiener 滤波增益计算]
F --> G[基于语音概率微调增益]
G --> H[频谱乘以增益后逆变换]
H --> I[输出增强语音]
|
三、核心增益计算逻辑#
- 后验SNR与先验SNR估计
• 后验SNR:使用当前帧信号谱与噪声谱之比
• 先验SNR:
$$
\xi_t(f) = \alpha \cdot \frac{|S_{t-1}(f)|^2}{N_{t-1}(f)} + (1 - \alpha) \cdot \max(\gamma_t(f) - 1, 0)
$$
1
2
3
| // Previous estimate based on previous frame with gain filter.
float prev_tsa = spectrum_prev_process_[i] /
(prev_noise_spectrum[i] + 0.0001f) * filter_[i];
|
spectrum_prev_process_[i] 为前一帧信号的幅度谱,filter_[i]为前帧的Wiener滤波器增益,两者相乘后就是估计的纯净语音信号,因此prev_tsa就是前一帧的TSA (time-smoothed a priori SNR)
1
2
3
4
5
6
| // Post SNR.
if (signal_spectrum[i] > noise_spectrum[i]) {
post_snr[i] = signal_spectrum[i] / (noise_spectrum[i] + 0.0001f) - 1.f;
} else {
post_snr[i] = 0.f;
}
|
只有当信号幅度大于噪声幅度时,才认为有语音信号存在,避免负增益
1
| prior_snr[i] = 0.98f * prev_estimate + (1.f - 0.98f) * post_snr[i];
|
0.98 是平滑因子,表示以历史先验估计为主,防止突变
- 基本增益计算
1
2
3
4
5
| // 参变维纳滤波器,参考《语音增强--理论与实践》,通过over_subtraction_factor来控制降噪力度
filter_[i] =
snr_prior / (suppression_params_.over_subtraction_factor + snr_prior);
filter_[i] = std::max(std::min(filter_[i], 1.f),
suppression_params_.minimum_attenuating_gain);
|

四、增益微调:语音概率辅助因子#
虽然 Wiener 滤波器理论上是最优的,但其在以下情况下仍存在问题:
• SNR 估计不准确
• 缺乏上下文判断
• 语音尾音易被吞掉
因此 WebRTC 引入了语音概率估计模块,通过以下方式微调 Wiener 增益:
scale = prior_prob * scale1 + (1 - prior_prob) * scale2;
其中:
• scale1: 当前是语音 → 增强增益
• scale2: 当前是噪声 → 抑制增益
• prior_prob: 通过 LRT、Spectral Flatness、Spectral Diff 等特征综合估计
五、高带处理#
在WebRTC的噪声抑制算法中,高频带(8kHz以上)没有直接使用频域Wiener滤波处理,而是在时域统一增益处理,这里即是因为语音大部分是在低带部分,同时也是为了效率减少计算量。接下我们来分析WebRTC的噪声抑制算法是如何通过低带的信息计算高频带增益的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
| // Computes the attenuating gain for the noise suppression of the upper bands.
float ComputeUpperBandsGain(
float minimum_attenuating_gain,
rtc::ArrayView<const float, kFftSizeBy2Plus1> filter,
rtc::ArrayView<const float> speech_probability,
rtc::ArrayView<const float, kFftSizeBy2Plus1> prev_analysis_signal_spectrum,
rtc::ArrayView<const float, kFftSizeBy2Plus1> signal_spectrum) {
// Average speech prob and filter gain for the end of the lowest band.
constexpr int kNumAvgBins = 32;
constexpr float kOneByNumAvgBins = 1.f / kNumAvgBins;
// 计算低带中最后的的kNumAvgBins个频点(靠近8kHz附近)的平均语音概率和平均增益
float avg_prob_speech = 0.f;
float avg_filter_gain = 0.f;
for (size_t i = kFftSizeBy2Plus1 - kNumAvgBins - 1; i < kFftSizeBy2Plus1 - 1;
i++) {
avg_prob_speech += speech_probability[i];
avg_filter_gain += filter[i];
}
avg_prob_speech = avg_prob_speech * kOneByNumAvgBins;
avg_filter_gain = avg_filter_gain * kOneByNumAvgBins;
// If the speech was suppressed by a component between Analyze and Process, an
// example being by an AEC, it should not be considered speech for the purpose
// of high band suppression. To that end, the speech probability is scaled
// accordingly.
float sum_analysis_spectrum = 0.f;
float sum_processing_spectrum = 0.f;
for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) {
sum_analysis_spectrum += prev_analysis_signal_spectrum[i];
sum_processing_spectrum += signal_spectrum[i];
}
// The magnitude spectrum computation enforces the spectrum to be strictly
// positive.
RTC_DCHECK_GT(sum_analysis_spectrum, 0.f);
avg_prob_speech *= sum_processing_spectrum / sum_analysis_spectrum;
// Compute gain based on speech probability.
float gain =
0.5f * (1.f + static_cast<float>(tanh(2.f * avg_prob_speech - 1.f)));
// Combine gain with low band gain.
if (avg_prob_speech >= 0.5f) {
gain = 0.25f * gain + 0.75f * avg_filter_gain;
} else {
gain = 0.5f * gain + 0.5f * avg_filter_gain;
}
// Make sure gain is within flooring range.
return std::min(std::max(gain, minimum_attenuating_gain), 1.f);
}
|
- 选取低带频谱中最后32个bin(即靠近8kHz的部分),计算平均语音概率和平均Wiener滤波器增益
1
2
| avg_prob_speech:语音存在的可能性(0~1)
avg_filter_gain:低带最后段的增益(代表频谱高频变化)
|
- 处理前后谱能量比修正语音概率
1
| avg_prob_speech *= sum_processing_spectrum / sum_analysis_spectrum;
|
- sum_analysis_spectrum:分析阶段的谱(原始 noisy 语音)
- sum_processing_spectrum:处理阶段的谱(可能被 AEC、AGC 等处理)
⚠️ 如果语音信号被其他模块(比如回声消除)削弱了,而估计器没注意到,就会过度抑制高带,这里通过能量比进行修正,防止“假静音”影响高频压制判断。
- 使用 tanh 平滑映射为增益因子
1
| gain = 0.5f * (1.f + tanh(2.f * avg_prob_speech - 1.f));
|
- tanh 函数做了一个S型映射:使得 01 的 avg_prob_speech 映射为 01 的增益,但中心更敏感,平滑、可导。
- 当语音概率高于 0.5 时增益快速上升;低于 0.5 时快速下降。
- 将该增益和低带平均滤波器增益融合
1
2
3
4
| if (avg_prob_speech >= 0.5f)
gain = 0.25f * gain + 0.75f * avg_filter_gain;
else
gain = 0.5f * gain + 0.5f * avg_filter_gain;
|
- 当语音概率较高,以低带滤波器为主(强调保留语音的高频特征)
- 当语音概率较低,用概率增益和滤波增益各一半权重
- 高带延时处理
1
| DelaySignal(y_band, channels_[ch]->process_delay_memory[b - 1], delayed_frame);
|
低带由于滤波器组分析→合成,会产生系统延时,因此在低带和高带合成前,需要将高带信号进行延时操作,与低带信号进行对齐。
六、小结与启发#
WebRTC 的 Wiener 降噪模块体现了“理论 + 工程”的完美结合:
| 模块 | 功能 | 工程优化 |
|---|
| Wiener 增益 | 降噪核心 | 加入先验平滑与概率调节 |
| SNR估计 | 支持增益计算 | TSA平滑,避免突变 |
| 语音概率 | 增益微调 | 三特征融合 + Sigmoid 映射 |
| 噪声谱估计 | 保证准确性 | 分位数估计 + 启动模型 |
这种设计保证了算法在实时语音场景中的鲁棒性、稳定性与可听感提升效果。
