当前位置: 首页 > news >正文

webrtc降噪-NoiseSuppressor类源码分析与算法原理

         WebRTC中的NoiseSuppressor类是实现实时音频噪声抑制的核心模块。它采用基于维纳滤波的频域处理算法,通过分析-处理两阶段工作流程:分析阶段进行噪声估计、语音概率检测和SNR计算,构建噪声模型;处理阶段应用自适应滤波器在频域抑制噪声,并通过重叠相加恢复时域信号。该模块支持多通道处理,采用保守的抑制策略确保噪声充分消除,同时通过零帧检测防止无声段影响噪声统计。其智能内存管理和频域时域结合的设计,在保持语音质量的前提下有效抑制背景噪声,显著提升实时音视频通信的听觉体验。

1. 核心功能

NoiseSuppressor 是 WebRTC 中的噪声抑制模块,主要功能:

  • 实时音频噪声抑制

  • 多通道音频处理

  • 频域噪声估计和抑制

  • 语音概率估计

  • 自适应滤波器设计

2. 核心算法原理

2.1 维纳滤波算法

数学公式:

H(ω) = P_s(ω) / [P_s(ω) + P_n(ω)]

其中:

  • H(ω) 是维纳滤波器频域响应

  • P_s(ω) 是语音功率谱

  • P_n(ω) 是噪声功率谱

// 计算先验和后验SNR
void ComputeSnr(rtc::ArrayView<const float, kFftSizeBy2Plus1> filter,rtc::ArrayView<const float> prev_signal_spectrum,rtc::ArrayView<const float> signal_spectrum,rtc::ArrayView<const float> prev_noise_spectrum,rtc::ArrayView<const float> noise_spectrum,rtc::ArrayView<float> prior_snr,rtc::ArrayView<float> post_snr) {for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) {// 先前帧的后验SNR估计,基于前一帧的增益滤波器float prev_estimate = prev_signal_spectrum[i] / (prev_noise_spectrum[i] + 0.0001f) * filter[i];// 当前后验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;  // 避免负值}// 基于决策导向的先验SNR估计,结合当前和先前估计prior_snr[i] = 0.98f * prev_estimate + (1.f - 0.98f) * post_snr[i];}
}

2.2 语音概率估计

// 更新语音概率估计
ch_p->speech_probability_estimator.Update(num_analyzed_frames_, prior_snr, post_snr,ch_p->noise_estimator.get_conservative_noise_spectrum(),signal_spectrum, signal_spectral_sum, signal_energy);

2.3 噪声估计

// 噪声估计器预处理
ch_p->noise_estimator.PreUpdate(num_analyzed_frames_, signal_spectrum,signal_spectral_sum);// 噪声估计器后处理,基于语音概率更新噪声谱
ch_p->noise_estimator.PostUpdate(ch_p->speech_probability_estimator.get_probability(), signal_spectrum);

3. 关键数据结构

3.1 ChannelState - 通道状态

struct ChannelState {ChannelState(const SuppressionParams& suppression_params, size_t num_bands);SpeechProbabilityEstimator speech_probability_estimator;  // 语音概率估计器WienerFilter wiener_filter;                               // 维纳滤波器NoiseEstimator noise_estimator;                           // 噪声估计器std::array<float, kFftSizeBy2Plus1> prev_analysis_signal_spectrum;  // 先前分析信号频谱std::array<float, kFftSize - kNsFrameSize> analyze_analysis_memory; // 分析内存std::array<float, kOverlapSize> process_analysis_memory;  // 处理分析内存std::array<float, kOverlapSize> process_synthesis_memory; // 处理合成内存std::vector<std::array<float, kOverlapSize>> process_delay_memory; // 延迟内存
};

3.2 FilterBankState - 滤波器组状态

struct FilterBankState {std::array<float, kFftSize> real;        // FFT实部std::array<float, kFftSize> imag;        // FFT虚部  std::array<float, kFftSize> extended_frame; // 扩展帧
};

4. 核心方法详解

4.1 Analyze - 分析阶段

void NoiseSuppressor::Analyze(const AudioBuffer& audio) {// 准备噪声估计器进行分析阶段for (size_t ch = 0; ch < num_channels_; ++ch) {channels_[ch]->noise_estimator.PrepareAnalysis();}// 零帧检测:避免在无声时更新统计信息bool zero_frame = true;for (size_t ch = 0; ch < num_channels_; ++ch) {rtc::ArrayView<const float, kNsFrameSize> y_band0(&audio.split_bands_const(ch)[0][0], kNsFrameSize);float energy = ComputeEnergyOfExtendedFrame(y_band0, channels_[ch]->analyze_analysis_memory);if (energy > 0.f) {zero_frame = false;break;}}if (zero_frame) return;  // 跳过零帧处理// 分析计数器更新if (++num_analyzed_frames_ < 0) {num_analyzed_frames_ = 0;}// 多通道分析处理for (size_t ch = 0; ch < num_channels_; ++ch) {// 形成扩展帧并应用滤波器组窗std::array<float, kFftSize> extended_frame;FormExtendedFrame(y_band0, ch_p->analyze_analysis_memory, extended_frame);ApplyFilterBankWindow(extended_frame);// FFT变换和幅度谱计算std::array<float, kFftSize> real;std::array<float, kFftSize> imag;fft_.Fft(extended_frame, real, imag);std::array<float, kFftSizeBy2Plus1> signal_spectrum;ComputeMagnitudeSpectrum(real, imag, signal_spectrum);// 噪声和语音概率估计ch_p->noise_estimator.PreUpdate(num_analyzed_frames_, signal_spectrum,signal_spectral_sum);// SNR计算和语音概率更新ComputeSnr(ch_p->wiener_filter.get_filter(),ch_p->prev_analysis_signal_spectrum, signal_spectrum,ch_p->noise_estimator.get_prev_noise_spectrum(),ch_p->noise_estimator.get_noise_spectrum(), prior_snr, post_snr);ch_p->noise_estimator.PostUpdate(ch_p->speech_probability_estimator.get_probability(), signal_spectrum);// 存储当前幅度谱供处理阶段使用std::copy(signal_spectrum.begin(), signal_spectrum.end(),ch_p->prev_analysis_signal_spectrum.begin());}
}

4.2 Process - 处理阶段

void NoiseSuppressor::Process(AudioBuffer* audio) {// 内存分配策略:小通道数用栈,大通道数用堆std::array<FilterBankState, kMaxNumChannelsOnStack> filter_bank_states_stack;// ... 其他栈数组if (NumChannelsOnHeap(num_channels_) > 0) {// 使用堆内存filter_bank_states = rtc::ArrayView<FilterBankState>(filter_bank_states_heap_.data(), num_channels_);// ... 其他堆数组}// 计算所有通道的抑制滤波器for (size_t ch = 0; ch < num_channels_; ++ch) {// 扩展帧形成和窗函数应用FormExtendedFrame(y_band0, channels_[ch]->process_analysis_memory,filter_bank_states[ch].extended_frame);ApplyFilterBankWindow(filter_bank_states[ch].extended_frame);// FFT分析和幅度谱计算fft_.Fft(filter_bank_states[ch].extended_frame, filter_bank_states[ch].real, filter_bank_states[ch].imag);// 维纳滤波器更新channels_[ch]->wiener_filter.Update(num_analyzed_frames_,channels_[ch]->noise_estimator.get_noise_spectrum(),channels_[ch]->noise_estimator.get_prev_noise_spectrum(),channels_[ch]->noise_estimator.get_parametric_noise_spectrum(),signal_spectrum);// 高频带增益计算(多频带情况)if (num_bands_ > 1) {upper_band_gains[ch] = ComputeUpperBandsGain(suppression_params_.minimum_attenuating_gain,channels_[ch]->wiener_filter.get_filter(),channels_[ch]->speech_probability_estimator.get_probability(),channels_[ch]->prev_analysis_signal_spectrum, signal_spectrum);}}// 聚合多通道维纳滤波器(取最小值策略)std::array<float, kFftSizeBy2Plus1> filter_data;if (num_channels_ == 1) {filter = channels_[0]->wiener_filter.get_filter();} else {AggregateWienerFilters(filter_data);  // 多通道时取各通道滤波器的最小值}// 应用滤波器到频域数据for (size_t ch = 0; ch < num_channels_; ++ch) {for (size_t i = 0; i < kFftSizeBy2Plus1; ++i) {filter_bank_states[ch].real[i] *= filter[i];filter_bank_states[ch].imag[i] *= filter[i];}}// IFFT合成回时域for (size_t ch = 0; ch < num_channels_; ++ch) {fft_.Ifft(filter_bank_states[ch].real, filter_bank_states[ch].imag,filter_bank_states[ch].extended_frame);}// 重叠相加输出for (size_t ch = 0; ch < num_channels_; ++ch) {OverlapAndAdd(filter_bank_states[ch].extended_frame,channels_[ch]->process_synthesis_memory, y_band0);}// 高频带处理(对齐延迟和增益应用)if (num_bands_ > 1) {// 选择最小高频增益float upper_band_gain = upper_band_gains[0];for (size_t ch = 1; ch < num_channels_; ++ch) {upper_band_gain = std::min(upper_band_gain, upper_band_gains[ch]);}// 高频带延迟对齐和增益应用for (size_t ch = 0; ch < num_channels_; ++ch) {for (size_t b = 1; b < num_bands_; ++b) {DelaySignal(y_band, channels_[ch]->process_delay_memory[b - 1],delayed_frame);// 应用时域噪声衰减增益for (size_t j = 0; j < kNsFrameSize; j++) {y_band[j] = upper_band_gain * delayed_frame[j];}}}}
}

5. 设计亮点

5.1 内存优化策略

// 栈堆混合内存分配:小通道数用栈,大通道数用堆
constexpr size_t kMaxNumChannelsOnStack = 2;
size_t NumChannelsOnHeap(size_t num_channels) {return num_channels > kMaxNumChannelsOnStack ? num_channels : 0;
}

5.2 多通道聚合策略

// 多通道滤波器聚合:取各通道最小值,确保保守的噪声抑制
void AggregateWienerFilters(rtc::ArrayView<float, kFftSizeBy2Plus1> filter) const {std::copy(filter0.begin(), filter0.end(), filter.begin());for (size_t ch = 1; ch < num_channels_; ++ch) {for (size_t k = 0; k < kFftSizeBy2Plus1; ++k) {filter[k] = std::min(filter[k], filter_ch[k]);  // 最小值聚合}}
}

5.3 零帧检测机制

// 避免在无声帧更新统计信息,防止阈值漂移
if (zero_frame) {// 在零信号情况下更新统计信息会导致阈值向零信号情况移动// 一旦信号"开启",所有内容都将被视为语音,没有噪声抑制效果return;
}

6. 典型工作流程

6.1 时序图

6.2 处理流程图

6.3 关键处理步骤说明

分析阶段关键点:

  • 零帧检测:防止无声帧影响噪声统计,避免阈值漂移

  • 决策导向SNR估计:结合当前和先前帧信息,提高估计稳定性

  • 保守噪声估计:在语音概率低时更新噪声模型

处理阶段关键点:

  • 多通道保守策略:取各通道滤波器最小值,确保噪声充分抑制

  • 频域时域结合:低频带频域处理 + 高频带时域增益

  • 延迟对齐:高频带延迟处理以匹配低频带处理延迟

  • 输出保护:限制输出在有效范围内,防止溢出

这个噪声抑制器采用了经典的维纳滤波框架,结合了先进的噪声估计和语音概率检测技术,在保持语音质量的同时有效抑制背景噪声。

http://www.dtcms.com/a/585593.html

相关文章:

  • openEuler容器化实践:从Docker入门到生产部署
  • Spring Security实战代码详解
  • ES6 Promise:告别回调地狱的异步编程革命
  • 企业网站备案教程免费建设网站抽取佣金
  • seo网站诊断流程公司网站建设费用会计处理
  • 与Figma AI对话的对话框在哪里?
  • 【科研绘图系列】R语言绘制微生物箱线图(box plot)
  • 禅城区网站建设管理网站模板下载免费下载
  • 前端微服务化
  • Linux 软件安装 “命令密码本”:yum/apt/brew 一网打尽
  • 做网站框架显示不出来中国最大的软件公司
  • 轻量级云原生体验:在OpenEuler 25.09上快速部署单节点K3s
  • 程序员 给老婆做网站网站建设 海南
  • 解释 StringRedisTemplate 类和对象的作用与关系
  • MATLAB遗传算法优化RVFL神经网络回归预测(随机函数链接神经网络)
  • 建设网站的知识竞赛国家建设网站
  • ROS2 Humble 笔记(七)标准与自定义 Interface 接口
  • 深入探索序列学习:循环神经网络(RNN)及其变体(LSTM、GRU)的详尽解析
  • 永川区做网站临沂网站建设方案报价
  • B哩B哩车机版专为汽车端或大屏设备开发
  • 一种基于视网膜图像的深度学习系统 DeepRETStroke
  • 2025汽车零部件行业数字化转型落地方案
  • 前后端分离和传统非分离(后端渲染)方案的核心差异
  • 【ZeroRange WebRTC】在自有 AWS 环境实现与 Amazon KVS 等效的 WebRTC 安全方案(落地指南)
  • 用wordpress建站要不要用宝塔网页制作基础教程免费
  • 深入解析红黑树:高效平衡的艺术
  • 李云飞:比亚迪将坚持磷酸铁锂电池路线
  • 制作网站赚钱不建设门户网站的请示
  • 张祥前统一场论动量公式P=m(C-V)误解解答
  • 建设工程施工员考试在哪个网站网页设计欣赏熊出没