一、噪声问题分析
1.1 噪声类型
| 类型 | 特点 | 示例 | 处理难度 |
|---|---|---|---|
| 稳态噪声 | 频谱稳定 | 风扇、空调、白噪声 | 低 |
| 周期噪声 | 周期重复 | 50Hz电源、机械振动 | 中 |
| 瞬态噪声 | 突发短暂 | 敲键盘、关门、咳嗽 | 高 |
| 非稳态噪声 | 频谱变化 | 街道、餐厅、电视 | 高 |
| 混响 | 多次反射 | 空旷房间 | 很高 |
1.2 噪声对通话的影响
信噪比(SNR):
ini
SNR = 10 * log10(语音能量 / 噪声能量)
| SNR | 语音质量 | 用户感受 |
|---|---|---|
| >20dB | 优秀 | 清晰 |
| 15-20dB | 良好 | 基本清晰 |
| 10-15dB | 一般 | 轻微嘈杂 |
| 5-10dB | 较差 | 明显嘈杂 |
| <5dB | 差 | 难以听清 |
1.3 噪声抑制目标
- 降低噪声:提高SNR
- 保持语音:避免语音失真
- 自然感:处理后的语音自然
- 低延迟:实时处理
二、频域噪声抑制
2.1 基本原理

2.2 噪声估计
统计方法:
cpp
class NoiseEstimator {
std::vector<float> noise_power_;
float alpha_; // 平滑因子
void Update(const std::vector<float>& signal_power, bool voice_present) {
for (int k = 0; k < signal_power.size(); k++) {
if (!voice_present) {
// 非语音段,更新噪声估计
noise_power_[k] = alpha_ * noise_power_[k] +
(1 - alpha_) * signal_power[k];
}
}
}
};
最小值跟踪:
cpp
// 跟踪频谱最小值作为噪声估计
void MinTracker(const std::vector<float>& signal_power,
std::vector<float>& noise_power,
std::vector<float>& min_power,
std::deque<std::vector<float>>& history) {
history.push_back(signal_power);
if (history.size() > kWindowSize) {
history.pop_front();
}
// 计算每个频点的最小值
for (int k = 0; k < signal_power.size(); k++) {
min_power[k] = INF;
for (const auto& frame : history) {
min_power[k] = std::min(min_power[k], frame[k]);
}
// 偏置补偿
noise_power[k] = min_power[k] * kBias;
}
}
2.3 增益计算
谱减法:
cpp
void SpectralSubtraction(const std::vector<float>& signal_power,
const std::vector<float>& noise_power,
std::vector<float>& gain) {
for (int k = 0; k < signal_power.size(); k++) {
// 谱减
float clean_power = signal_power[k] - noise_power[k];
clean_power = std::max(clean_power, 0.0f);
// 计算增益
gain[k] = sqrt(clean_power / signal_power[k]);
}
}
Wiener滤波:
cpp
void WienerFilter(const std::vector<float>& signal_power,
const std::vector<float>& noise_power,
std::vector<float>& gain) {
for (int k = 0; k < signal_power.size(); k++) {
// Wiener增益
// H = max(1 - noise/signal, 0)
float snr = signal_power[k] / (noise_power[k] + 1e-6);
gain[k] = std::max(1.0f - 1.0f / snr, 0.0f);
}
}
MMSE-STSA:
cpp
// 最小均方误差短时谱幅度估计
void MMSE_STSA(const std::vector<float>& signal_power,
const std::vector<float>& noise_power,
std::vector<float>& gain) {
for (int k = 0; k < signal_power.size(); k++) {
// 先验SNR
float snr_prior = (signal_power[k] - noise_power[k]) /
(noise_power[k] + 1e-6);
snr_prior = std::max(snr_prior, 0.0f);
// 后验SNR
float snr_post = signal_power[k] / (noise_power[k] + 1e-6);
// MMSE增益(简化版)
float v = snr_prior * snr_post / (1.0f + snr_prior);
gain[k] = ComputeBesselRatio(v) * sqrt(snr_prior) / (1.0f + snr_prior);
}
}
2.4 增益平滑
避免音乐噪声:
cpp
void SmoothGain(std::vector<float>& gain,
const std::vector<float>& prev_gain) {
// 时间平滑
for (int k = 0; k < gain.size(); k++) {
gain[k] = kTimeSmooth * prev_gain[k] +
(1 - kTimeSmooth) * gain[k];
}
// 频率平滑
std::vector<float> smoothed(gain.size());
for (int k = 1; k < gain.size() - 1; k++) {
smoothed[k] = 0.25f * gain[k-1] + 0.5f * gain[k] + 0.25f * gain[k+1];
}
gain = smoothed;
}
三、WebRTC噪声抑制
3.1 WebRTC NS架构

3.2 使用方法
cpp
// 创建NS实例
NsHandle* ns = WebRtcNs_Create();
WebRtcNs_Init(ns, 48000); // 采样率
// 设置模式
WebRtcNs_set_policy(ns, kNsModerate);
// 处理
float input_frame[480]; // 10ms @ 48kHz
float output_frame[480];
WebRtcNs_Process(ns, input_frame, NULL, output_frame);
// 销毁
WebRtcNs_Free(ns);
3.3 模式选择
| 模式 | 抑制量 | 语音失真 | 适用场景 |
|---|---|---|---|
| VeryLow | 轻 | 几乎无 | 低噪声环境 |
| Low | 低 | 很小 | 一般环境 |
| Moderate | 中 | 小 | 常用推荐 |
| High | 高 | 中 | 高噪声环境 |
| VeryHigh | 很高 | 较大 | 极高噪声 |
3.4 高级配置
cpp
struct NsConfig {
// 抑制级别
int suppression_level; // 0-4
// 噪声估计参数
float noise_est_alpha; // 噪声更新速度
float noise_est_beta; // 噪声更新偏置
// 增益限制
float min_gain; // 最小增益(避免完全静音)
float max_gain; // 最大增益
};
四、深度学习噪声抑制
4.1 为什么用深度学习
传统方法局限:
- 对非稳态噪声效果差
- 难以处理复杂场景
- 需要手工调参
深度学习优势:
- 自动学习噪声特征
- 适应各种噪声
- 端到端优化
4.2 网络结构
RNNoise:

DCCRN: 
4.3 实现示例
python
import torch
import torch.nn as nn
class RNNoise(nn.Module):
def __init__(self):
super().__init__()
self.gru1 = nn.GRU(42, 256, batch_first=True)
self.gru2 = nn.GRU(256, 256, batch_first=True)
self.gru3 = nn.GRU(256, 256, batch_first=True)
self.fc = nn.Linear(256, 1) # 输出增益
def forward(self, x):
# x: (batch, time, features)
x, _ = self.gru1(x)
x, _ = self.gru2(x)
x, _ = self.gru3(x)
gain = torch.sigmoid(self.fc(x)) # 0-1
return gain
4.4 性能对比
| 方法 | 稳态噪声 | 非稳态噪声 | 计算量 | 延迟 |
|---|---|---|---|---|
| 谱减法 | 好 | 差 | 低 | 低 |
| Wiener | 好 | 一般 | 低 | 低 |
| WebRTC NS | 好 | 一般 | 中 | 低 |
| RNNoise | 好 | 好 | 中 | 低 |
| DCCRN | 很好 | 很好 | 高 | 中 |
五、瞬态噪声处理
5.1 瞬态噪声特点
- 突发、短暂(<100ms)
- 能量集中
- 如:键盘、敲击、咳嗽
5.2 检测方法
cpp
bool DetectTransient(const std::vector<float>& frame,
const std::vector<float>& noise_power) {
// 计算帧能量
float energy = 0;
for (float s : frame) energy += s * s;
// 计算噪声能量
float noise_energy = 0;
for (float n : noise_power) noise_energy += n;
// 突发检测
float ratio = energy / noise_energy;
return ratio > kTransientThreshold; // 如10倍
}
5.3 处理策略
方法1:衰减
cpp
void HandleTransient(std::vector<float>& frame, float attenuation) {
// 直接衰减瞬态帧
for (float& s : frame) {
s *= attenuation; // 如0.3
}
}
方法2:替换
cpp
void HandleTransient(std::vector<float>& frame,
const std::vector<float>& prev_frame) {
// 用前一帧替代
frame = prev_frame;
}
方法3:插值
cpp
void HandleTransient(std::vector<float>& frame,
const std::vector<float>& prev_frame,
const std::vector<float>& next_frame) {
// 前后帧插值
for (int i = 0; i < frame.size(); i++) {
frame[i] = 0.5 * (prev_frame[i] + next_frame[i]);
}
}
六、多麦克风噪声抑制
6.1 空间滤波
原理: 利用麦克风阵列的空间信息
目标方向:增强 其他方向:抑制
波束成形:
cpp
void Beamforming(const std::vector<std::vector<float>>& mic_inputs,
std::vector<float>& output,
const std::vector<float>& weights) {
int num_mics = mic_inputs.size();
int frame_size = mic_inputs[0].size();
output.resize(frame_size, 0);
for (int m = 0; m < num_mics; m++) {
for (int i = 0; i < frame_size; i++) {
output[i] += weights[m] * mic_inputs[m][i];
}
}
}
6.2 自适应波束成形
MVDR:
cpp
// 最小方差无失真响应波束成形
// min w^T R w s.t. w^T d = 1
// w = R^-1 d / (d^T R^-1 d)
void MVDR(const std::vector<std::vector<float>>& R, // 噪声协方差
const std::vector<float>& d, // 方向向量
std::vector<float>& weights) {
// 求解MVDR权重
auto R_inv = Inverse(R);
auto R_inv_d = Multiply(R_inv, d);
float denom = InnerProduct(d, R_inv_d);
for (int i = 0; i < weights.size(); i++) {
weights[i] = R_inv_d[i] / denom;
}
}
6.3 后置滤波
cpp
// 波束成形后的残余噪声抑制
void PostFilter(std::vector<float>& beamformed,
const std::vector<float>& noise_estimate) {
// 频域噪声抑制
auto spectrum = FFT(beamformed);
for (int k = 0; k < spectrum.size(); k++) {
float gain = ComputeWienerGain(|spectrum[k]|², noise_estimate[k]);
spectrum[k] *= gain;
}
beamformed = IFFT(spectrum);
}
七、噪声抑制效果评估
7.1 客观指标
SNR改善:
bash
SNR改善 = SNR处理后 - SNR处理前
PESQ:
bash
# 使用PESQ评估
pesq +16000 original.wav processed.wav
STOI(短时客观可懂度):
bash
# 使用STOI评估
stoi original.wav processed.wav 16000
7.2 主观测试
测试方法:
- 准备干净语音和噪声
- 混合生成带噪语音
- 噪声抑制处理
- 主观评分(MOS)
测试场景:
| 场景 | 噪声类型 | SNR范围 |
|---|---|---|
| 办公室 | 稳态+瞬态 | 10-20dB |
| 街道 | 非稳态 | 0-10dB |
| 餐厅 | 混响+人声 | 5-15dB |
| 车内 | 低频+引擎 | 0-10dB |
八、实践建议
8.1 场景选择
cpp
enum NoiseScenario {
kQuiet, // 安静:低抑制
kOffice, // 办公室:中等抑制
kStreet, // 街道:高抑制+瞬态处理
kRestaurant, // 餐厅:高抑制+多麦克风
kVehicle // 车内:高抑制+低频处理
};
void ConfigureNS(NoiseScenario scenario, NsConfig& config) {
switch (scenario) {
case kQuiet:
config.suppression_level = kNsLow;
break;
case kOffice:
config.suppression_level = kNsModerate;
config.transient_detection = true;
break;
case kStreet:
config.suppression_level = kNsHigh;
config.transient_detection = true;
break;
// ...
}
}
8.2 参数调优
cpp
// 根据实际效果调整
struct TuningParams {
// 噪声估计速度
float noise_update_rate = 0.9; // 慢更新,适合稳态噪声
// 快更新,适合非稳态噪声
// 抑制强度
float suppression_strength = 0.9; // 强抑制,高噪声场景
// 弱抑制,避免语音失真
// 最小增益
float min_gain = 0.01; // 避免完全静音
};
8.3 监控指标
cpp
struct NsStats {
float input_snr; // 输入SNR
float output_snr; // 输出SNR
float snr_improvement; // SNR改善
float noise_reduction; // 噪声降低量(dB)
int transient_count; // 瞬态噪声次数
};
九、本章小结
噪声抑制是提升语音清晰度的关键技术。本章我们探讨了:
- 噪声类型:稳态、非稳态、瞬态
- 频域方法:谱减、Wiener、MMSE
- WebRTC NS:架构、使用、配置
- 深度学习:RNNoise、DCCRN
- 瞬态处理:检测、衰减、替换
- 多麦克风:波束成形、MVDR
- 效果评估:SNR、PESQ、STOI
- 实践建议:场景选择、参数调优
下一章,我们将探讨增益控制与音量管理,学习如何保持音量稳定和舒适。