本文主要介绍WebRTC的APM。

现在主要介绍一下audio_processing.h。

首先插入了几个类,这些都是audio_processing的核心模块。

class AudioFrame;  
class EchoCancellation;  
class EchoControlMobile;  
class GainControl;  
class HighPassFilter;  
class LevelEstimator;  
class NoiseSuppression;  
class VoiceDetection;  

AudioFrame:主要记录了通道基本信息,数据,VAD标志时间戳,采样频率,信道数等。

EchoCancellation:回声消除模块(AEC),在使用外置扬声器的时候应该使用,有些使用耳麦通讯的情况也会存在回声(因为麦克风与扬声器有空间或者电的弱耦合),如果影响了通话也应该开启。

EchoControlMobile:回声抑制模块(AES),这个模块和回声消除模块功能相似,但是实现方法不一样。运算量远远小于回声消除模块。非常适合移动平台使用。但是对语音损伤大。

GainControl:增益控制模块(AGC),这个模块使用了语音的特征对系统硬件音量和输出的信号大小进行调节。硬件上可以控制输入音量。软件上只能调节原来信号的幅度,如果对原来就已经破音的信号,或者本来输入就比较小的信号就无能为力了。

HighPassFilter:高通滤波器,抑制不需要的低频信号。可以根据需要修改参数选择相应的截止频率。对于某些有工频干扰的设备需要使用高通滤波器。

LevelEstimator:估计信号的能量值。

NoiseSuppression:噪声抑制模块(NS/SE),该模块一般应用在有环境噪声的情况,或者是麦克风采集到的数据有明显噪声的情况。

VoiceDetection:语音激活检测模块(VAD),该模块用于检测语音是否出现。用于编解码以及后续相关处理。

APM分为两个流,一个近端流,一个远端流。近端(Near-end)流是指从麦克风进入的数据;远端(Far-end)流是指接收到的数据。现在分别介绍一下,这部分代码在audio_processing_impl.cc里。

far_end流代码:

int AudioProcessingImpl::AnalyzeReverseStreamLocked() {
  AudioBuffer* ra = render_audio_.get();  // For brevity.
  if (rev_proc_format_.rate() == kSampleRate32kHz) {
    for (int i = 0; i < rev_proc_format_.num_channels(); i++) {
      // Split into low and high band.
      WebRtcSpl_AnalysisQMF(ra->data(i),
                            ra->samples_per_channel(),
                            ra->low_pass_split_data(i),
                            ra->high_pass_split_data(i),
                            ra->filter_states(i)->analysis_filter_state1,
                            ra->filter_states(i)->analysis_filter_state2);
    }
  }
  RETURN_ON_ERR(echo_cancellation_->ProcessRenderAudio(ra));
  RETURN_ON_ERR(echo_control_mobile_->ProcessRenderAudio(ra));
  RETURN_ON_ERR(gain_control_->ProcessRenderAudio(ra));
  return kNoError;
}

上述代码可以看出far-end获得数据后主要有4个步骤的处理。

1、判断是否是32k信号,采取相应的分频策略;

2、AEC流程,记录AEC中的far-end及其相关运算;

3、AES流程,记录AES中的far-end及其相关运算;

4、AGC流程,计算far-end及其相关特征。

near-end流代码:

int AudioProcessingImpl::ProcessStreamLocked() {
#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP
  if (debug_file_->Open()) {
    audioproc::Stream* msg = event_msg_->mutable_stream();
    msg->set_delay(stream_delay_ms_);
    msg->set_drift(echo_cancellation_->stream_drift_samples());
    msg->set_level(gain_control_->stream_analog_level());
    msg->set_keypress(key_pressed_);
  }
#endif
  AudioBuffer* ca = capture_audio_.get();  // For brevity.
  bool data_processed = is_data_processed();
  if (analysis_needed(data_processed)) {
    for (int i = 0; i < fwd_proc_format_.num_channels(); i++) {
      // Split into a low and high band.
      WebRtcSpl_AnalysisQMF(ca->data(i),
                            ca->samples_per_channel(),
                            ca->low_pass_split_data(i),
                            ca->high_pass_split_data(i),
                            ca->filter_states(i)->analysis_filter_state1,
                            ca->filter_states(i)->analysis_filter_state2);
    }
  }
  RETURN_ON_ERR(high_pass_filter_->ProcessCaptureAudio(ca));
  RETURN_ON_ERR(gain_control_->AnalyzeCaptureAudio(ca));
  RETURN_ON_ERR(echo_cancellation_->ProcessCaptureAudio(ca));
  if (echo_control_mobile_->is_enabled() && noise_suppression_->is_enabled()) {
    ca->CopyLowPassToReference();
  }
  RETURN_ON_ERR(noise_suppression_->ProcessCaptureAudio(ca));
  RETURN_ON_ERR(echo_control_mobile_->ProcessCaptureAudio(ca));
  RETURN_ON_ERR(voice_detection_->ProcessCaptureAudio(ca));
  RETURN_ON_ERR(gain_control_->ProcessCaptureAudio(ca));
  if (synthesis_needed(data_processed)) {
    for (int i = 0; i < fwd_proc_format_.num_channels(); i++) {
      // Recombine low and high bands.
      WebRtcSpl_SynthesisQMF(ca->low_pass_split_data(i),
                             ca->high_pass_split_data(i),
                             ca->samples_per_split_channel(),
                             ca->data(i),
                             ca->filter_states(i)->synthesis_filter_state1,
                             ca->filter_states(i)->synthesis_filter_state2);
    }
  }
  // The level estimator operates on the recombined data.
  RETURN_ON_ERR(level_estimator_->ProcessStream(ca));
  was_stream_delay_set_ = false;
  return kNoError;
}

其中包括七个步骤:1、分频;2、高通滤波;3、硬件音量控制;4、AEC;5、NS;6、AES;7、VAD;8、AGC;9、综合。

可见near-end的处理全面,流程清晰。可以根据实际需要打开不同的模块,适应不同场景的需要,对于一般通讯系统来说具有正面的改善效果。但是在实际工作中也发现了一些流程上隐患。另外就是该结构的各个模块处理相对独立耦合低,本来应该是一个优良的特性,然而在复杂情况的信号处理难以到达目标效果。由于低耦合造成的运算量浪费更加是无法避免的。

WebRTC APM音频处理流程概述的更多相关文章

  1. webrtc的音频处理模块apm( audio processing)下载与编译出libwebrtc_audio_preprocessing.so

    webrtc代码在android和chromium项目中都有.但是android中的那个带有Android.mk,稍微修改下就能用ndk-build编译出libwebrtc_audio_preproc ...

  2. 单独编译使用WebRTC的音频处理模块

    块,每块个点,(12*64=768采样)即AEC-PC仅能处理48ms的单声道16kHz延迟的数据,而 - 加载编译好的NS模块动态库 接下来只需要按照 此文 的描述在 android 的JAVA代码 ...

  3. 【单独编译使用WebRTC的音频处理模块 - android】

    更新 [2015年2月15日] Bill 这段时间没有再关注 WebRTC 以及音频处理的相关信息,且我个人早已不再推荐单独编译 WebRTC 中的各个模块出来使用.实际上本文的参考价值已经很小了,甚 ...

  4. 2019 WebRtc AudioMixer混音流程

    本文简要说明最新版WebRtc AudioMixer混音流程. 本程序使用4个16KHz 单声道时长均大于10秒的Wav文件作为混音源,只合成前10秒的音频,输出也是16KHz单声道音频. 输入和输出 ...

  5. Java虚拟机JVM学习01 流程概述

    Java虚拟机JVM学习01 流程概述 Java虚拟机与程序的生命周期 一个运行时的Java虚拟机(JVM)负责运行一个Java程序. 当启动一个Java程序时,一个虚拟机实例诞生:当程序关闭退出,这 ...

  6. 异步tcp通信——APM.Core 服务端概述

    为什么使用异步 异步线程是由线程池负责管理,而多线程,我们可以自己控制,当然在多线程中我们也可以使用线程池.就拿网络扒虫而言,如果使用异步模式去实现,它使用线程池进行管理.异步操作执行时,会将操作丢给 ...

  7. ETL流程概述及常用实现方法

    ETL流程概述及常用实现方法 http://blog.csdn.net/btkuangxp/article/details/48224187 目录(?)[-] 1抽取作业 1手工开发抽取作业时候的常用 ...

  8. Linux音频驱动-ALSA概述

    概述 ALSA(Advanced Linux Sound Architecture)是linux上主流的音频结构,在没有出现ALSA架构之前,一直使用的是OSS(Open Sound System)音 ...

  9. iOS音频开发系列-概述篇

    概述 iOS中对于音频的处理,苹果提供了两个库. AVFoundation AudioToolbox 在iOS系统中apple对上述的流程进行了封装并提供了不同层次的接口

随机推荐

  1. 防DDOS攻击SHELL脚本

    最近一段时间服务器频繁遭到DDOS攻击,目前只能通过封IP来源来暂时解决.IP不源变化多端,光靠手工来添加简直是恶梦,想了个方法,用SHELL来做. 比较简单,但很实用:) 以下内容根据作者原文进行适 ...

  2. [C#] 将 List 转 DataTable

    /// <summary> /// Convert a List{T} to a DataTable. /// </summary> private DataTable ToD ...

  3. BZOJ3489: A simple rmq problem

    设$i$的前驱为$p_i$,后继为$q_i$,把询问看成点$(L,R)$,有贡献的$i$满足$L\in(p_i,i]$且$R\in[i,q_i)$,询问的就是覆盖这个点的矩形的最大值.那么可以用可持久 ...

  4. 谷歌地图地理解析和反解析geocode.geocoder详解

    地址解析就是将地址(如:贵州省贵阳市)转换为地理坐标(如经度:106.71,纬度:26.57)的过程. 地理反解析和上面的过程相反是将地理坐标(如纬度:26.57,经度:106.71)转换为地址(中国 ...

  5. Java排序算法——希尔排序

    package sort; //================================================= // File Name : ShellSort //------- ...

  6. Zabbix3 agent端安装(二)

    1.基础环境准备 安装zabbix的yum源,这里有必要提一点,阿里的yum源已经提供了zabbix3.0 1.1.yum源配置 rpm -ihv http://mirrors.aliyun.com/ ...

  7. 简述block

    block传值也适用于从后往前传值 先介绍block的基本知识 /** * 1.如何定义一个Block变量 2.怎样给定义的Block变量赋初值 3.如何冲定义Block类型 4.如何使用Block实 ...

  8. 深入理解javascript原型和闭包(8)——简述【执行上下文】上

    什么是“执行上下文”(也叫做“执行上下文环境”)?暂且不下定义,先看一段代码: 第一句报错,a未定义,很正常.第二句.第三句输出都是undefined,说明浏览器在执行console.log(a)时, ...

  9. ThinkPHP3.2.3整合smarty模板(二)

    前言:继ThinkPHP3.2.3整合smarty模板(一)之后,继续来探讨一下tp框架整合smarty模板,看到有人在群上问到怎么使用自定义的常量,今天就具体来谈谈: 一.开发一个项目,必不可少会用 ...

  10. tyvj1198 最优矩阵连乘

    描述    一个n*m矩阵由n行m列共n*m个数排列而成.两个矩阵A和B可以相乘当且仅当A的列数等于B的行数.一个N*M的矩阵乘以一个M*P的矩阵等于一个N*P的矩阵,运算量为nmp.   矩阵乘法满 ...