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估计:结合当前和先前帧信息,提高估计稳定性

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

处理阶段关键点:

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

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

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

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

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

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值