在WebRTC的噪声抑制系统中,SignalModelEstimator负责核心的噪声特征建模任务。通过实时分析音频信号的频谱属性,它提取出三个主要特征:频谱平坦度、频谱差异和似然比(LRT),以此来构建动态噪声模型。利用自适应学习机制,SignalModelEstimator定期更新先验模型的参数,从而精确地区分语音和背景噪声。该模块的设计结合了统计学原理和信号处理技术,通过收集特征的直方图,每500帧更新一次模型,实现了计算效率与准确性的平衡。作为WebRTC音频处理流程中的重要部分,SignalModelEstimator为后续的噪声抑制提供了决策支持,极大地提升了语音的质量。
核心功能
作为WebRTC噪声抑制模块的重要组成部分,SignalModelEstimator的主要职责包括:
- 信号特征提取:计算频谱平坦度、频谱差异、LRT(似然比测试)等特征。
- 噪声模型估计:基于统计分析构建噪声的先验模型。
- 自适应更新:根据输入信号动态调整模型参数。
- 特征归一化:确保所有特征值保持在合理的范围之内。
核心算法原理
2.1 频谱差异计算 (ComputeSpectralDiff)
频谱差异的计算公式如下:
spectral_diff = (var(signal) - cov(signal, noise)) / var(noise)
此函数用于衡量信号频谱与噪声模板频谱之间的差异程度。具体实现如下:
float ComputeSpectralDiff(
rtc::ArrayView<const float, kFftSizeBy2Plus1> conservative_noise_spectrum,
rtc::ArrayView<const float, kFftSizeBy2Plus1> signal_spectrum,
float signal_spectral_sum,
float diff_normalization) {
// 计算噪声谱平均值
float noise_average = 0.f;
for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) {
noise_average += conservative_noise_spectrum[i];
}
noise_average *= kOneByFftSizeBy2Plus1;
// 计算信号谱平均值
float signal_average = signal_spectral_sum * kOneByFftSizeBy2Plus1;
// 计算方差和协方差
float covariance = 0.f;
float noise_variance = 0.f;
float signal_variance = 0.f;
for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) {
float signal_diff = signal_spectrum[i] - signal_average;
float noise_diff = conservative_noise_spectrum[i] - noise_average;
covariance += signal_diff * noise_diff;
noise_variance += noise_diff * noise_diff;
signal_variance += signal_diff * signal_diff;
}
// 计算频谱差异并归一化
float spectral_diff = signal_variance - (covariance * covariance) / (noise_variance + 0.0001f);
return spectral_diff / (diff_normalization + 0.0001f);
}
2.2 频谱平坦度计算 (UpdateSpectralFlatness)
频谱平坦度的计算公式如下:
spectral_flatness = geometric_mean / arithmetic_mean log(geometric_mean) = (1/N) * Σ log(signal_spectrum[i]) arithmetic_mean = (1/N) * Σ signal_spectrum[i]
此函数用于更新频谱平坦度,通过计算几何平均值与算术平均值的比值来评估信号的平坦程度。
void UpdateSpectralFlatness(// 具体实现代码省略 }
在处理频谱平坦度时,代码遵循以下步骤:
rtc::ArrayView<const float, kFftSizeBy2Plus1> signal_spectrum,
float signal_spectral_sum,
float* spectral_flatness) {
// 避免出现log(0)的情况,检查是否存在零值
for (size_t i = 1; i < kFftSizeBy2Plus1; ++i) {
if (signal_spectrum[i] == 0.f) {
*spectral_flatness -= 0.3f * (*spectral_flatness); // 应用衰减
return;
}
}
// 跳过直流成分(i=0),计算几何平均的对数值
float avg_spect_flatness_num = 0.f;
for (size_t i = 1; i < kFftSizeBy2Plus1; ++i) {
avg_spect_flatness_num += LogApproximation(signal_spectrum[i]);
}
// 计算算术平均,同样跳过直流成分
float avg_spect_flatness_denom = signal_spectral_sum - signal_spectrum[0];
avg_spect_flatness_denom *= kOneByFftSizeBy2Plus1;
avg_spect_flatness_num *= kOneByFftSizeBy2Plus1;
// 频谱平坦度计算:几何平均除以算术平均
float spectral_tmp = ExpApproximation(avg_spect_flatness_num) / avg_spect_flatness_denom;
// 更新时间平均值:新值 = 旧值 + 0.3 * (新测量值 - 旧值)
*spectral_flatness += 0.3f * (spectral_tmp - *spectral_flatness);
}
2.3 LRT更新 (UpdateSpectralLrt)
根据贝叶斯准则,使用似然比计算方法进行语音与噪声的分类。
void UpdateSpectralLrt(rtc::ArrayView<const float, kFftSizeBy2Plus1> prior_snr,
rtc::ArrayView<const float, kFftSizeBy2Plus1> post_snr,
rtc::ArrayView<float, kFftSizeBy2Plus1> avg_log_lrt,
float* lrt) {
for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) {
// 根据先验信噪比和后验信噪比计算瞬时LRT
float tmp1 = 1.f + 2.f * prior_snr[i];
float tmp2 = 2.f * prior_snr[i] / (tmp1 + 0.0001f);
float bessel_tmp = (post_snr[i] + 1.f) * tmp2;
// 更新各频带的平均对数LRT
avg_log_lrt[i] += 0.5f * (bessel_tmp - LogApproximation(tmp1) - avg_log_lrt[i]);
}
// 计算所有频带的平均LRT
float log_lrt_time_avg_k_sum = 0.f;
for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) {
log_lrt_time_avg_k_sum += avg_log_lrt[i];
}
*lrt = log_lrt_time_avg_k_sum * kOneByFftSizeBy2Plus1;
}
3. 关键数据结构
3.1 SignalModel(信号模型)
此结构体封装了从信号中提取的所有特征信息。
struct SignalModel {
float spectral_flatness; // 频谱平坦度特性
float spectral_diff; // 频谱差异特性
float lrt; // 似然比测试特性
std::array<float, kFftSizeBy2Plus1> avg_log_lrt; // 各频带的平均LRT
};
3.2 Histograms(直方图统计)
此类用于收集特征值的统计分布,并定期更新先验模型。
class Histograms {
public:
3.3 PriorSignalModelEstimator(先验模型估计器)
该类基于直方图统计来估计先验信号模型的参数。
class PriorSignalModelEstimator {
public:
void Update(const Histograms& histograms); // 更新先验模型
const PriorSignalModel& get_prior_model() const;
};
4. 核心方法详解
4.1 Update 方法
此方法用于根据输入的信号和噪声频谱数据更新信号模型估计器中的特征。
void SignalModelEstimator::Update(
rtc::ArrayView<const float, kFftSizeBy2Plus1> prior_snr, // 先验信噪比
rtc::ArrayView<const float, kFftSizeBy2Plus1> post_snr, // 后验信噪比
rtc::ArrayView<const float, kFftSizeBy2Plus1> conservative_noise_spectrum, // 保守噪声谱
rtc::ArrayView<const float, kFftSizeBy2Plus1> signal_spectrum, // 信号频谱
float signal_spectral_sum, // 频谱总和(预计算优化)
float signal_energy) { // 信号能量
// 1. 更新频谱平坦度特征
UpdateSpectralFlatness(signal_spectrum, signal_spectral_sum, &features_.spectral_flatness);
// 2. 计算并更新频谱差异特征
float spectral_diff = ComputeSpectralDiff(conservative_noise_spectrum,
signal_spectrum,
signal_spectral_sum,
diff_normalization_);
features_.spectral_diff += 0.3f * (spectral_diff - features_.spectral_diff);
// 3. 累积信号能量用于后续归一化
signal_energy_sum_ += signal_energy;
// 4. 定期更新直方图和模型参数
if (--histogram_analysis_counter_ > 0) {
histograms_.Update(features_); // 收集统计信息
} else {
// 每500帧更新一次模型参数
prior_model_estimator_.Update(histograms_); // 更新先验模型
histograms_.Clear(); // 清空统计
histogram_analysis_counter_ = kFeatureUpdateWindowSize; // 重置计数器(500)
// 更新归一化参数
signal_energy_sum_ /= kFeatureUpdateWindowSize;
diff_normalization_ = 0.5f * (signal_energy_sum_ + diff_normalization_);
signal_energy_sum_ = 0.f;
}
// 5. 更新LRT特征
UpdateSpectralLrt(prior_snr, post_snr, features_.avg_log_lrt, &features_.lrt);
}
5. 设计亮点
- 多特征融合:集成频谱平坦度、频谱差异、LRT等多种特征,提升噪声检测精度。
- 自适应学习:利用直方图统计动态学习环境噪声的特性。
- 计算优化:
- 预计算频谱以避免重复计算。
- 采用近似函数(如LogApproximation/ExpApproximation)来降低计算复杂度。
- 定期更新以减轻计算负担。
- 鲁棒性设计:
- 保守的噪声估计以避免过度抑制。
- 防止除零错误(添加小常数0.0001f)。
- 异常值处理(例如零值检测)。
6. 典型工作流程
6.1 时序图

6.2 流程图

关键流程说明:
- 特征提取阶段:每帧实时计算频谱平坦度、频谱差异等基本特征。
- 统计收集阶段:持续收集特征统计数据用于模型学习。
- 模型更新阶段:定期根据收集到的数据更新模型参数。
每500帧会根据统计信息来更新先验模型及归一化参数。
自适应调整机制能够依据信号能量的变化,动态地调整特征归一化过程,从而适应不同的输入电平。
此设计方案在确保实时性能的同时,也兼顾了模型的准确性。通过定期批量更新的方式,有效降低了计算复杂度,同时保留了对噪声抑制的灵活性。


雷达卡


京公网安备 11010802022788号







