WEBRTC音频处理与3A算法

134 阅读39分钟

前言

随着互联网的蓬勃发展和数字化通信的普及,实时音频通信成为人们日常生活和工作中不可或缺的一部分。在这个领域,WEBRTC(Web Real-Time Communication)技术和3A回声处理算法的出现与发展,为音频通信带来了前所未有的便利和创新。WEBRTC作为一个开源项目,为网络浏览器和移动应用提供了实时通信的技术标准和软件库,其中的音频处理功能为用户提供了高质量的通话体验。而3A回声处理算法:声学回音消除(AEC)、自适应噪声抑制(ANS)和自动增益控制(AGC)等功能为用户提供了清晰的通话体验。3A 回声处理算法具有自适应、自学习、自优化等特点,能够根据环境和用户需求动态调整音频处理参数,提供更加个性化、高效的音频处理效果。

本文将从理论原理、应用场景和未来发展等方面对 WEBRTC 音频处理技术和 3A 回声处理算法进行深入探讨,旨在为读者提供对实时音频通信技术的理解和科普。

关键字:WEBRTC;音频处理;3A算法;回声消除;

1、概述

1.1、项目背景

自2022年,智慧医疗项目-医护对讲系统的研发开始,使用的RK设备安卓终端一体机,由于rk主板没有设计类似手机芯片自带dsp芯片,也没有在软件层上匹配回声处理器算法,这给应用开发工程师的回声处理跟开发很大的算法压力,回声处理需要工程师在软件上实现与集成。另外我们也测试过RK给的回声消除的补丁Patch文件,合并到HAL层之后,实践测试效果并不理想。

本人从2022年,开始研究与学习3A回声处理算法,在项目上,从继承aecm(基于手机中底片芯片计算能力的回声消除器)初步解决对讲回声,再到2023年上半年集成新的回声消除器(3A算法),获得了比较好的单向回声对讲效果,双工对讲仍无法完整解决,2024年重新出发,安排时间进行aec3的调研,目前正在进行中。

由于3A算法是比较比较大的算法体系与知识模块,也未必是个人学习的长项以及学习深度,本文也是基于学习的经验进行技术分享;

2、技术导论与描述

2.1、什么是回声?回声的分类?回声消除的基础导论是什么?

2.1.1、什么是回声?

回声就是声音信号经过一系列反射之后,又听到了自己讲话的声音,这就是回声。而在VoIP(Voice over Internet Protocol,基于IP的语音传输 )实时语音通话中,近端通话者的声音被自己的麦克风拾取后通过网络传到远端,远端扬声器播放出来的声音被麦克风拾取后通过网络又重新发回近端,再加上网络和数据处理等各种延迟的影响,使得近端通话者能够从扬声器中听到自己的刚才所说的话,就产生了对讲回声;如图所示:

2.1.2、回声的分类

在通信系统中,回声主要分为两类:电路回声和声学回声(声学回声有2种:线性回声和非线性回声)。

1、电路回声: 通常产生于有线通话中,由于电路回声信号是线性且稳定的,所以比较容易将其消除,用一个简单的线性叠加器就可以实现,不属于本文讨论方向。(其原理是:在有线电话传输种,为了降低电话中心局与电话用户之间电话线的价格,用户间线的连接采用两线制;而电话中心局之间连接采用四线制(上面两条线路用于发送给用户端信号,下面两条线路用于接收用户端信号)。问题就出来了,造成电路回声的根本原因是转换混合器的二线-四线阻抗不能完全匹配(使用的不同型号的电线或者负载线圈没有被使用的原因),导致混合器 接收线路 上的语音信号流失到了 发送线路 ,产生了回声信号,使得另一端的用户在接收信号的同时听到了自己的声音)

现如今的数字通信网络中,转换混合器与数模转换器融为一体,但无论是模拟电子线路还是数字电子线路,二-四线的转换都会造成阻抗不匹配问题,从而导致其产生电路回声,影响现代通信质量。由于电路回声的线性以及稳定性,用一个简单的线性叠加器就可以实现电路回声消除。

2、声学回声:近端扬声器将语音信号播放出来后,被近端麦克风直接采集后得到的回声,这与扬声器到麦克风的距离及位置、麦克风腔体、扬声器腔体等多种硬件结构都有很大的关系,我们可以称为硬件的声学结构设计。

具体详细为:远端讲话者的输入声音,被远端麦克风进行采集,并传入通信设备,经过无线或有线传输之后到达近端的通讯设备,近端扬声器播放声音,而后这个声音被近端麦克风捕获采集形成声学回声,经传输后回到远端设备,被扬声器进行播放,这样,远端就能听到自己的回声;因为网络是有延时的,所以这个回声更加容易的被自己辨认到。

2.1.3、回声消除的基础导论是什么?

我们先理解一下几个名词:

远端参考信号被远端麦克风采集后的信号(被认定为已经过处理后的信号:软件算法处理、dsp芯片算法处理等等),等于近端扬声器播放的信号,称为“参考信号”,是回声消除过程中一个重要的指标。
近端语音信号被近端麦克风采集后的信号(被认定为已经过处理后的信号:软件算法处理、dsp芯片算法处理等等),等于要发送发送到远端设备扬声器播放的信号,换个角度来说也是远端的参考信号,这个概念是相互的。
近端混合回声信号近端说话人的语音信号经过麦克风采集之后的信号,通常该信号=人说话的信号+采集到扬声器的回声信号;
远端混合回声信号在整个对话过程中,近端麦克风接受的信号有近端说话人语音信号和近端扬声器器播放的远端说话的信号几个,这样叠加起来的语音信号通过电话线传输到对端之后,经过扬声器播放导致远端人听到了自己的说话信号,即是所谓的回声。

回声消除技术主要用于在免提电话,VoIP的双工通话当中,它的原理可以理解为:通过远端参考信号,在近端的混合回声信号中将远端的信号进行删减,得到近端的语音信号,即只有近端说话人说出的声音。

举个例子:现在远端参考信号为:“广播的梦想公司”,近端说话人的语音是“安卓开发”,那么经过麦克风采集之后,近端混合的回声信号为<广播的梦想公司 + 安卓开发>的混音。 那么回声消除的导论就是,在上述婚姻内,将远端参考信号进行删减,得到最后的电信号为<安卓开发>,再通过网络或有线发送到远程进行播放。这便是一次回声消除的基本流程。

Android回声消除方案调研表

WebRTC与Speex对比表

技术对比分析

3、WEBRTC 3A算法(AEC + ANS + AGC)

音频处理技术3A算法主要集中在上行的发送端对发送信号依次进行回声消除、降噪以及音量均衡,包含:

声学回声消除:AEC(Acoustic echo cancellation)

背景噪声抑制:ANS(Adaptive noise suppression)

自动增益控制:AGC(Automatic gain control)

如图所示:在源码的Process方法中,该图显示的是 AEC 的处理流程;若采用的是AECM 的处理流程的话 ANS 会前置,先处理ANS再经过AECM,而AGC除了使用增益对音频进行处理之外,也可作为压限器作用在接收端对即将播放的音频信号进行限幅,防止音频溢出。

3.1:AEC回声消除算法原理

在二线制传输的两个方向上同时间、同频谱的占用线路,线路的两个方向的音频数据信号完全混合在一起(混音),近端发出的信号的回波成为了近端的干扰信号,在AEC中,可以采用线性自适应滤波器抵消回波以达到较好的接收信号质量,即实现了回声消除。他的根本原理是:利用接受的音频数据跟本地采集数据进行对比,并对干扰信号进行消减。

回声流程也主要由以下的三个大模块构成:

  1. 时延估计(Time Delay Estimation, TDE) 模块
  2. (线性)回声消除(Linear Acoustic Echo Cancellation, AEC) 模块
    核心包括:(线性)自适应滤波器(Linear Adaptive Filter)
  3. 双讲检测(Double-Talk Detect, DTD)模块
  4. 非线性残余声学回声抑制(Residual Acoustic Echo Suppression, RAES) 模块
    核心包括:非线性处理(Nonlinear Processing)

可以简单来说:时延估计决定了AEC的下限,线性回声消除决定了AEC的上限,非线性声学回声抑制,决定了最终的通话体验,特别是回声抑制跟双讲之间的平衡。

1. 时延估计(Time Delay Estimation, TDE) 模块

从软件上来说,因各种原因(延时,信号传播速度,声音音波在空气介质中的传播速度等),会导致麦克风收到的信号与网络传输的远端信号不是对齐的,这就导致在实时性上没有办法精准的识别到拿一些声音是干扰信号,那就无法精准的估计回声了。当近端信号和远端信号的延迟较大时,或者对不齐时,就不得不使用较长的线性滤波器来处理,或者需要通过自适应滤波器动态更新系数来重新对齐,这也无疑增加了计算量。如果我们能将近端信号和远端信号对齐,那么就可以减少滤波器的系数从而减少算法开销。

从T0、T1、T2的时间来分析:

T0:代表着声音从扬声器出声到麦克风采集的时间,这个时间基本可以忽略,因为一般说话话筒和扬声器的距离并不会太远,并且考虑声音在空气中传播速度 340米/秒,这个时间不会超过1ms。

T1:代表远端传到近端的声音,这个声音被传递到参考信号接口(WebrtcAec_BufferFarend)到播放出来的时间,一般来说接收到音频数据的时候,播放声音数据的时候,那也是程序把音频数据给接口的时候,近似理解成音频数据开始放入播放队列,并到播放出来的时间。

T2:代表一段声音被麦克风采集到,并送如近端回声计算函数(WebrtcAec_Process)的时间,由于声音被捕获到之后,上层AudioRecoder反馈音频数据后,马上进入到算法之后,获取到最终结果的时间。

那么整个delay = T0 + T1 +T2;

一般来说,一个设备能找到一个合适的delay,那么这个设备的回声消除基本可以满足能够在信号中找到正确的参考回声位置,这个需要对于不同的设备上进行调试与测试,以至于找到一个合适的值。

如果delay对不齐的条件下,会使用时延估计算法进行滤波器的系数更新,在滤波器系数更新过程中,回声计算可能效果不佳,导致出现回声等问题,常见的时延估计算法有:

1)gips首席科学家Bastiaan的算法:

设1表示有说话音,0表示无说话音(静音或者很弱的声音);参考端(远端)信号x(t)和接收端(近端)信号y(t)可能的组合方式有以下几种:(0,0),(0,1),(1,0),(1,1);

(0,0)表示远端和近端都是比较弱的声音,(1,1)表示远端和近端都是比较强的声音,webrt的c代码默认其它两种情况是不可能发生的。设在时间间隔p上,即p=1,2,…,P, 频带q,q=1,2,…,Q上,输入信号x加窗(如汉宁窗)后的功率谱用Xw(p,q)来表示,对每个频带中的功率谱设定一个门限Xw(p,q)_threshold。
如果 Xw(p,q) >= Xw(p,q)_threshold , 则Xw(p,q) =1;
如果 Xw(p,q) < Xw(p,q)_threshold , 则Xw(p,q) =0;
同理,对于信号y(t),加窗信号功率谱Yw(p,q)和门限Yw(p,q)_threshold,
如果 Yw(p,q) >= Yw(p,q)_threshold , 则Yw(p,q) =1;
如果 Yw(p,q) < Yw(p,q)_threshold , 则Yw(p,q) =0;

考虑到实际处理的方便,在webrtc的c代码中,将经过fft变换后的频域功率谱分为32个子带,这样每个特定子带 Xw(p,q)的值可以用1个比特来表示,总共需要32个比特,只用一个32位数据类型就可以表示了。

webrtc对参考信号定义了75个32位binary_far_history的数组存放历史远端参考信号,定义了16个32位binary_near_history的数组存放历史近端参考信号,最近的值都放在下标为0的数组中,使用binary_near_history[15]的32位bit与binary_far_history数组中75个32位bit分别按位异或,得到75个32位比特数据,32位bit的物理意义是近似地使用功率谱来统计两帧信号的相关性。统计32位结果中的1的个数存于bit_counts中,接下来用对bit_counts进行平滑防止延时突变,得到mean_bit_count,可以看出 mean_bit_count 越小,则表明近端数据与该帧的远端数据越吻合,两者的时延越接近所需要的延时数值,用value_best_candidate表示。剩下的工作是对边界数值进行保护,如果value_best_candidate接近最差延时(预设),则表明数值不可靠,这时不更新延时数据;如果数据可靠,则进一步使用一阶markvo模型,比照上一次时延数据确定本次最终的更新时延last_delay.
Bastiaan的专利本身要比现有的c代码实现更为复杂,比如在异或的时候(0,0),(0,1),(1,0),(1,1)四种组合可以附加代价函数,而c代码相当于默认给(0,0),(1,1)附加权值为1,给(0,1),(1,0)附加权值为0;

2. PBFDAF分段块频域自适应滤波算法

在实际会议场景中,特别是视频会议,回声路径时长可能达到几百毫秒,在16kHz的采样率爱,NLMS算法的计算复杂度会随着滤波器的阶段呈线性增长,为了客服这个问题,PBFDAF是个不错的选择,他李永乐FFT可快速实现卷积运算的特点,在频域进行滤波器的更新, 同时用逐块更新来带代替逐点更新,这样大大的降低了计算的复杂度。

webrtc使用了分段块频域自适应滤波(PBFDAF)算法,这也是自适应滤波器的常用算法。该算法的原理如下:判断远端和近端是否说话的情况,又称为双端检测,需要监测以下四种情况:

  1. 仅远端说话, 此时有回声,要利用这种状态进行自适应滤波器的系数更新,并尽快完成收敛
  2. 仅近端说话, 这种时候是没有回声的,不用考虑
  3. 双端都在说话(Double Talk),此时系数固化,不进行系数更新
  4. 双端都没有说话,这时候需要启用近端VAD

3. NLMS算法(归一化最小均方自适应算法)

假设远端信号为x(n),近段信号为d(n),W(n),则误差信号e(n)=d(n)-w’(n)x(n) (此处‘表示转秩)

NLMS对滤波器的系数更新使用变步长方法,即步长u=u0/(gamma+x’(n) x(n))。其中u0为更新步长因子,gamma是稳定因子,则滤波器系数更新方程为 W(n+1)=W(n)+ue(n)*x(n);

NLMS比传统LMS算法复杂度略高,但收敛速度明显加快。LMS/NLMS性能差于AP和RLS算法。

4. NLP非线性滤波器

由于Webrtc的AEC线性滤波器消除回声的能力有限,NLP模块在整个回声抑制中起着重要作用,其目的是用于抑制线性滤波器后误差信号中残留的回声。在webrtc中,衡量残余回声信号的大小,是利用信号之间的频域相干性c(0<=c<=1)。首先计算近端信号d(n)和误差信号e(n)之间的频域相干性c(de),可以简单的用C(de)表征残留回声在误差信号中的占有比例,残留回声越小,C(de)越接近1。假设线性阶段正产运行。C(de)越接近1,说明近端信号和误差信号相似性越高,越不需要对误差信号做抑制;相反的,C(de)越小,越需要对误差信号进行抑制。

非线性滤波器的原始数据与滤波结果是一种逻辑关系,即用逻辑运算实现,如最大值滤波器、最小值滤波器、中值滤波器等,是通过比较一定邻域内的灰度值大小来实现的,没有固定的模板,因而也就没有特定的转移函数(因为没有模板作傅里叶变换)。

webrtc采用了维纳滤波器。此处只给出传递函数的表达式,设估计的语音信号的功率谱为Ps(w),噪声信号的功率谱为Pn(w),则滤波器的传递函数为H(w)=Ps(w)/(Ps(w)+Pn(w))。维纳滤波器是通过从带噪声幅度谱中减去经过维纳滤波后的噪声分量幅度谱,然后再使用带噪音频谱的相位,经过傅里叶变换,就得到降噪后的语音信号,采用最小军方误差准则(NMSE)迭代估计未拿滤波器的噪声抑制系统数,根据这组系数来除去噪声,该方法的残留噪声类似白噪声,而不是音乐噪声。

此外,由于NLP在抑制回声段的同时也会将噪声一同抑制,而近端语音段的噪声得到保留,这种噪声的时有时无会带来听感上的不舒适,为了降低这种影响,在我们调用的时候会在NLP之后再加上人工生成的白噪声,一般使用cng舒适噪声生成算法,来进行白噪声添加。

5. 滤波器系数更新

在算法当中存放的是全部样本对应的误差信号,这个保存仅仅是为了绘图使用。根据内部的算法 重叠保留法,算法将时域误差信号补64个零,然后再进行FFT变换,得到频域误差Ek。

比如:pn是当前帧远端信号功率谱,Ek是误差信号频谱,Ek2是归一化误差频谱,参考NLMS的公式:

Ek2=Ek./(pn + 0.001); //误差归一化

接着对Ek2进行限幅处理,如果该点的值比门限大,则取门限值;如果小于门限值,则保持不变;这么处理是为了降低因滤波器系数更新过快而导致的发散风险;

absEf = max (abs(Ek2),threshold);
absEd = ones(N+1, 1)*threshold./absEd;
Ek2 = Ek2.*abdEf;

mufb是指步长,当fs=16kHz,步长取0.5;

mEk = mufb.*Ek2;

将参与计算的16块远端信号频谱的共轭分别乘以误差信号频谱,这一项用于计算滤波器系统的调整量,IFPH是频域到时欲的变换值,将IFPH的后64点样值置零,再常识变换到频域得到FPH,这么操作是为了避免线性卷积变成循环卷积。然后更新滤波器系统WFb。

6. CNG舒适噪声comfort noise generator

webrtc采用的舒适噪声生成器比较简单,首先生成在[0,1]上均匀分布的随机噪声矩阵,再用噪声的功率谱开方去调制噪声的幅度,最后将生成的噪声值进行与人声的混音,并进行有必要的归一化处理。

3.2:ANS背景噪声抑制

单通道降噪的方法有很多,比如谱减法、维纳滤波法、基于最搭似然(ML)、最大后延(MAP),最小均方估计(NMSE)的统计模型法、 贝叶斯估计法,以及基于特征值和奇异值分解(SVD/EVD)的子空间法 。

谱减法基于人类语音时/频域的稀疏性,在非语音段估计噪声,在带噪的语音段减去非语音段估计的噪声得到纯净的语音。该方法只有幅度谱受影响,而相位谱并不受影响,这种方法要求晚声谱是静态的或者准静态的,当估计的背景噪声过小时,会有噪声残留,残余的噪声会形成“音乐噪声”,如果估计的背景噪声过大,则会导致语音被消掉。

维纳滤波基于语音和噪声的统计独立性,使用MMSE准则降噪。子空间法基于语音和噪声在各自的子空间是正交的这一假设,实际上是近似正交,该方法受单帧正交性波动影响较大。

如图所示:在维纳波的方法中,,输入信号y(n)经过一个滤波器后产生一个输出信号x(n),希望x(n)尽量逼近期望信号d(n)。这可以通过计算估计误差e(n)并使其最小化来实现,能够最小化这个估计误差的最优滤波器。他通常是现行的,且为一个FIR滤波器,因为FIR滤波器也是稳定的,以及它也是线性的,方便计算。

深度学习方法将降噪模型的建模过程用大量训练数据通过机器学习的方式获取,在有些场景中深度学习方法已经达到商用标准。比如使用开源深度学习降噪项目RNNoise的降噪方法。

在webrtc中,使用ANS模块进行单通道的降噪实现,他对似然比函数进行了改进,将多个语音/噪声分类特征组合在一起,形成了一个多特整改率密度声源分类模型,其鲁棒性何模型的准确性要比单一特征好很多,代码实现上对理论算法做了较多的工程化改进,以增强算法的鲁棒性,该开源算法对平稳背景噪声(比如转速风扇、家用电器等)具有很好的抑制效果。

整个处理过程主要有6步:

1) 把输入的带噪信号从时域转到频域,主要包括分帧、加窗和短时傅里叶变换(STFT)等

2) 做初始噪声估计,基于估计出的噪声算先验信噪比和后验信噪比

3) 计算分类特征,这些特征包括似然比检验(LRT)、频谱平坦度和频谱差异。根据这些特征确定语音/噪声概率,从而判定当前信号是语音还是噪声。

4) 根据算出来的语音/噪声概率去更新噪声估计

5) 基于维纳滤波去噪

6) 把去噪后的信号从频域转换回时域,主要包括短时傅里叶逆变换(ISTFT)、加窗和重叠相加等。

调用代码如图所示:

// 创建NS模块
NsHandle* WebRtcNs_Create();
//释放NS模块
void WebRtcNs_Free(NsHandle* NS_inst);
//初始化ns模块,输入采样率
int WebRtcNs_Init(NsHandle* NS_inst, uint32_t fs);
//设置ns的消除强度
//0: Mild, 1: Medium , 2: Aggressive
int WebRtcNs_set_policy(NsHandle* NS_inst, int mode);
//估算插入的背景噪声强度,只接受10ms数据:80 或者160样本数据
void WebRtcNs_Analyze(NsHandle* NS_inst, const float* spframe);
//计算插入的背景噪声强度,只接受10ms数据:80 或者160样本数据
void WebRtcNs_Process(NsHandle* NS_inst,
                     const float* const* spframe,
                     size_t num_bands,
                     float* const* outframe);

//返回只想每个频率区间的噪声估计值
const float* WebRtcNs_noise_estimate(const NsHandle* handle);

}

3.3:AGC自动增益算法

在音视频通话的现实场景中,不同的参会人说话音量各有不同,参会用户需要频繁的调整播放音量来满足听感的需要,戴耳机的用户随时承受着大音量对耳朵的 “暴击”。因此,对发送端音量的均衡在上述场景中显得尤为重要,优秀的自动增益控制算法能够统一音频音量大小,极大地缓解了由设备采集差异、说话人音量大小、距离远近等因素导致的音量的差异。

所以agc在webrtc中的功能:在发送端作为均衡器和压限器调整推流音量;

在接收端仅作为压限器防止混音之后播放的音频数据爆音,理论上推流端 AGC 做的足够鲁棒之后,拉流端仅作为压限器是足够的,有的厂家为了进一步减小混音之后不同人声的音量差异也会再做一次 AGC。

先科普一下样本点幅度值 Sample 与分贝 dB 之间的关系,以 16bit 量化的音频采样点为例:dB = 20 * log10(Sample / 32768.0),与 Adobe Audition 右侧纵坐标刻度一致。幅度值表示:16bit 采样最小值为 0,最大值绝对值为 32768 (幅度值如下图右边栏纵坐标)。

分贝表示:最大值为 0 分贝(分贝值如下图右边栏纵坐标),一般音量到达 -3dB 已经比较大了,3 也经常设置为 AGC 目标音量。

agc内部设置的核心参数:

typedef struct {
int16_t targetLevelDbfs; // 目标音量
int16_t compressionGaindB; // 增益能力
uint8_t limiterEnable; // 压限器开关
} AgcConfig;

目标音量 - targetLevelDbfs: 表示音量均衡结果的目标值,如设置为 1 表示输出音量的目标值为 - 1dB;

增益能力 - compressionGaindB: 表示音频最大的增益能力,如设置为 12dB,最大可以被提升 12dB;

压限器开关 - limiterEnable: 一般与 targetLevelDbfs 配合使用,compressionGaindB 是调节小音量的增益范围,limiter 则是对超过 targetLevelDbfs 的部分进行限制,避免数据爆音。

AGC 的核心模式:

除了以上三个核心的参数外,针对不同的接入设备 WebRTC AGC 提供了以下三种模式:

enum {
kAgcModeUnchanged,
kAgcModeAdaptiveAnalog, // 自适应模拟模式
kAgcModeAdaptiveDigital, // 自适应数字增益模式
kAgcModeFixedDigital // 固定数字增益模式
};

固定数字增益 - FixedDigital

固定数字增益模式最基础的增益模式也是 AGC 的核心,其他两种模式都是在此基础上扩展得到。主要是对信号进行固定增益的放大,最大增益不超过设置的增益能力 compressionGaindB,结合 limiter 使用的时候上限不超过设置的目标音量 targetLevelDbfs。

固定数字增益模式下仅依靠核心函数 WebRtcAgc_ProcessDigital 对输入信号音量进行均衡,由于没有反馈机制,其信号处理流程也是极其简单;

自适应模拟增益 - AdaptiveAnalog

因目前大多数的设备端,会内置麦克风阵列,并提供麦克风阵列增强算法,降噪的同时还会额外提供 0~10dB 的增益(不同机型范围不同,联想的设备增益高达 36dB);由于控制音量的模块过多,导致 PC 端 AGC 算法更加敏感。线上很多客户设置的默认值并不合理,这会直接影响音视频通话的体验;

但绝大多数用户在察觉到声音异常后并不知道 PC 设备还具备手动调节采集增益的功能,依赖于线上用户(尤其是教育场景很多是小学生)自己去调节模拟增益值几乎不可能,将模拟增益值动态调节的功能做到 AGC 算法内部更可行,配合数字增益部分将近端信号均衡到理想的位置,因此,WebRTC 设计了自适应模拟增益模式,通过反馈机制来调节原始采集音量,目标就是与数字增益模块相互配合,找到最合适的麦克风增益值并反馈给设备层,使得近端数据再经过数字增益之后达到目标增益;

自适应数字增益 - AdaptiveDigital

基于音频视频通信的娱乐、社交、在线教育等领域离不开多种多样的智能手机和平板设备,然而这些移动端并没有类似 PC 端调节模拟增益的接口。声源与设备的距离,声源音量以及硬件采集能力等因素都会影响采集音量,单纯依赖固定数字增益效果十分有限,尤其是多人会议的时候会明显感受到不同说话人的音量并不一致,听感上音量起伏较大。

所以,为了解决这个问题,WebRTC 科学家仿照了 PC 端模拟增益调节的能力,基于模拟增益框架新增了虚拟麦克风调节模块:WebRtcAgc_VirtualMic,利用两个长度为 128 的数组:增益曲线 - kGainTableVirtualMic 和抑制曲线 - kSuppressionTableVirtualMic 来模拟 PC 端模拟增益(增益部分为单调递增的直线,抑制部分为单调递减的凹曲线),前者提供 1.03.0 倍的增益能力,后者提供 1.00.1 的下压能力。

3.4:常用的音频算法名词简称解释

payloadType音频的载荷类型,音频最常用的是PCM,其他还有如G711U、G711A、G729、Speex等
sample rate采样率(也称为采样速度或者采样频率)定义了每秒从连续信号中提取并组成离散信号的采样个数,单位用赫兹(Hz)来表示。
gain输入或输出信号增益
bitwidth采样位深;我们常见的16Bit(16比特),可以记录大概96分贝的动态范围。那么,您可以大概知道,每一个比特大约可以记录6分贝的声音。同理,20Bit可记录的动态范围大概就是120dB;24Bit就大概是144dB。假如,我们定义0dB为峰值,那么声音振幅以向下延伸计算,那么,CD音频可的动态范围就是“-96dB~0dB。”,依次类推,24Bit的HD-Audio高清音频的的动态范围就是“-144dB~0dB。”。由此可见,位深度较高时,有更大的动态范围可利用,可以记录更低电平的细节。
HPF(High Pass Filter)高通滤波器,在音响系统中,有时会有一些极低频的次声波(infrasonic)信号夹杂在全音频信号当中,这些次声波信号人耳听不见,但是这种信号进入音箱,就会导致低音喇叭产生自激,并导致喇叭损坏;
BSS(Blind Source Separation)盲源分离;又称为盲信号分离,是指在信号的理论模型和源信号无法精确获知的情况下,如何从混迭信号(观测信号)中分离出各源信号的过程。
BF(Beamforming)波束形成;是天线技术与数字信号处理技术的结合,目的用于定向信号传输或接收。
chanel通道数;单声道或者多声道,数字代表声道数;
VoiceDetectionVAD 语音活动检测;也叫静音检测技术,主要用于语音检测或者激活;
speechIntelligibilityEnhance语音清晰增强;用于改善语音可理解性,基于语音增强算法,分析和改进音频质量的算法,包括语音识别和声学模型;
aecExtendFilter扩展滤波器;用于调整回声消除器(AEC)的滤波器长度,增加回声处理延迟,因为较短的滤波器长度可能会减少处理延迟,无法支持较大的延迟,但是扩展滤波器会加大计算复杂度,得根据设备情况衡量。
delayAgnostic延时不稳定参数;允许 AEC 在处理延迟时表现得更加鲁棒,即使在延迟变化较大的情况下,也能保持较好的回声消除效果。当他为true时,aec将尝试自适应处理不同的延时情况,以最大限度的减少回声影响,尤其是在网络条件不稳定或延迟波动较大的情况下;但是开启了之后初始化aec的收敛过慢;(至少达5秒的初始化时间)
experimentalNs实验性的噪声抑制器,其功能可能还在开发或测试阶段,并不是在所有环境中都被广泛使用;可能存在不稳定性或性能问题
experimentalAgc实验性的 AGC 功能。这可能意味着该功能还处于开发或测试阶段,并不一定在所有环境中都被广泛使用。实验性的 AGC 可能包含新的算法或功能,旨在进一步改善通话质量
NextGenerationAec下一代 AEC,经过改进或更新的回声消除算法。这些新算法可能包含更高级的信号处理技术,以应对各种复杂的声学环境和网络条件;但新算法可能会导致更高的计算成本,因此可能会影响系统的性能和资源消耗;
时域从时间维度来衡量一段音频;
频域从频率分布维度来衡量一段声音;常用的均衡器都是从频域的角度去调制;

4、归一化混音与音频压限器Limiter

在学习压线器Limiter与归一化之前,需要先了解音频信号的波形,比如一个正弦波的波形如图所示:

音频的电信号有正负值,该波形存在峰值,那是因为声音的信号,是通过振幅来产生的,最终会被转化成快满、强弱运动。在安卓的开发当中,可以使用一个安卓的AudioRecoder去读取音频帧,通过debug打印音频帧,可见每一帧都可能存在正负值,与该波形相符。

另外在PCM的源数据中,数据格式是按照Litter-Endian小端模式进行排列的,PCM的值为0时候表示没有声音(振幅为0)。

4.1:归一化混音

音频的混音原理:量化的音频信号(PCM bytes[] 音频帧)的叠加就等价于空气中的声波的叠加;

反馈到音频帧数据上面,也就是把同一个声道的数值进行简单的相加,但是会存在一个问题,那就是相加的结果可能会有溢出,你可以理解成一个byte字节8位二进制数,超出了0xFF溢出之后,这种情况就需要进行压限与归一化。

算法的内容描述如下:

1)初始化f衰减因子DEFAULT缺省值为1,初始化MAX,MIN;

2)对于某一帧的处理:output[i] = mix[i] * f;步骤2为了得出output[i]的值。
如果output[i] > MAX,更新f = MAX / output[i],output[i] = MAX;

同样的,如果output[i] < MIN,更新f = MIN / output[i],output[i] = MIN;

3)如果f<1,则需要将f归一化,通过一定的策略将f逐步自增为1,计算式 f = f +STEPSIZE;(STEPSIZE为变化步长);然后继续处理下一帧,转步骤二;

其中:mix[]为所有音频流的某一帧线性叠加值,实际实现下图算法代码块;

output[]为算法归一化之后的输出帧,在MIN <= output[] <= MAX 范围;

STEPSIZE为变化步长,通常表达式为(1-f)/value; value可以取8,162,3,64,128,256,可以按照预设值收敛的数据进行估算;STEPSIZE取值较大时,value值小,运算复杂度低,但是语音平滑度变小;STEPSIZE取值较小时,value值高,运算复杂度高,但是语音平滑度比较细腻。

那么为什么要归一化呢?因为针对于溢出的音频数据进行等比衰减之后,比如当前f = 0.5,后面帧的音频在[MIN,MAX]范围内,不溢出,那么衰减完之后要通过步长的形式逐步回复音频帧的能量,达到符合预期的能量设置。

对应算法伪代码如下所示:

//音频混音
static void pcmAudioMix(SInt16 *bufferA, SInt16 *bufferB, UInt32 bufferLength){
sourseFile[0] = (char *)bufferA;
    
sourseFile[1] = (char *)bufferB;

bufferLength *= 2;  //得到buffer在byte下的数量

Mix(sourseFile, 2, (char *)bufferB, bufferLength);
}

// 归一化混音
static void Mix(char ** sourseFile,int number,char *objectFile, UInt32 bufferLength){
    int const MAX = 32767;
    int const MIN = -32768;
    double f = 1;
    int output;

    for(;;) {
        output[i] = (sourseFile[0] + sourseFile[1]) * f;

        if (output[i] > MAX) {
            f = min (f, MAX/ ((sourseFile[0] + sourseFile[1])));
            output[i] = MAX;
        }

        if (output[i] < MIN) {
            f = min (f, MIN/ ((sourseFile[0] + sourseFile[1])));
            output[i] = MIN;
        }

        if(f < 1) { //步长取256,通过计算,f会平滑超过1
            f += double (1-f) / 256;
        } else {
            f =1;
        }
    }    
}    

4.2:压限器Limiter

压线器Limiter是音频处理中一种常用算法,他可以改变音频信号的动态范围,即增大较弱的音频信号能量,减小较强的音频信号能量,使得音频信号的总体能量更加稳定,在音频录制、混音、母带处理等领域广泛运用。他的基本原理就是根据一个阈值来进行判断音频信号的强弱,在信号的强度超过阈值的情况下,对能量进行衰减,使其不超过最大值;

医疗项目也使用了一个压限器,将音频能量控制在+-310mV左右,下面采用代码实现来进行讲解:

/**
     * 处理音频数据压限:通过实际测试,350mV的数据范围在:+-13500
     * +-12000 -> 310mV
     * @param buffer
     * @param bufferLength short长度,点位的长度而不是byte长度
     * return short[]
     */
 public static int pcmAudioLimiter(short [] buffer, int bufferLength) {
        if (buffer.length != bufferLength) {
            return -1;
        }
    for (int i = 0; i < bufferLength; i++) {
            //比例缩减
            short resultBuffer = (short) (buffer[i] * attenuationFactor);
            if (resultBuffer != 0 && resultBuffer > DEFAULT_MAX) {
                attenuationFactor = Math.min(attenuationFactor, ((double) DEFAULT_MAX / (double)buffer[i]) );
                resultBuffer = (short) DEFAULT_MAX;
                mixCount = 0;
                
                if (logStatus) {
                    logStatus = false;
                    recoverStatus = true;
                    Log.e(TAG, "pcmAudioLimiter: " + "buffer[i] > DEFAULT_MAX-> " + buffer[i]);
                }
            }
        if (resultBuffer != 0 && resultBuffer < DEFAULT_MIN) {
                attenuationFactor = Math.min(attenuationFactor, ((double) DEFAULT_MIN / (double)buffer[i]) );
                resultBuffer = (short) DEFAULT_MIN;
                 mixCount = 0;

                if (logStatus) {
                    logStatus = false;
                    recoverStatus = true;

                    Log.e(TAG, "pcmAudioLimiter: " + "buffer[i] < DEFAULT_MIN-> " + buffer[i]);
                }
           
//赋值回buffer
            buffer[i] = resultBuffer;

            //归一化处理:经过音频实验室数据调教 得出来的系数,音频更加平滑过渡
            if (mixCount < 2500) {
                mixCount ++;
            } else {
                mixDelayCount ++;
                if (attenuationFactor < 1 && mixDelayCount > 40) {
                    mixDelayCount = 0;
                    attenuationFactor += (1 - attenuationFactor) / 350.0 + (1.0/10000);
                }
            if (attenuationFactor >= 1) {
                    attenuationFactor = 1;

                    if (recoverStatus) {
                        Log.e(TAG, "attenuationFactor: " + "recover to 1" );
                        recoverStatus = false;
                  }
                }

            }
        }
        return 1;
    }
}

5、VAD语音端点检测

vad是用于判断给定的音频数据中是否存在语音,常用语在语音编解码、降噪、增益控制、波束形成以及语音唤醒识别等场景中;vad检测给定音频数据含有语音的概率,通常包括特征提取和语音/非语音判决两部分,当前使用的语音特征主要有时域和频域两种: 时域特征包括能量波动、过零率、最大能量和最小能量等,频域特征主要有基频、频谱组成、频谱质心、谱差、谱密度、谱衰减等。

用于VAD判决的特征通常可以分为六大类:能量、频域、倒谱、谱差、谐波和长时信息,基于能量的特征计算简单,如能量过零率,基于谱(频谱、倒谱和谱差)在低 SNR 可以获得较好的效果,当SNR为0dB时,基于语音谐波和长时语音特征判决方法的鲁棒性更强。

当前的判决准则可以分为三类:基于门限、基于统计模型和基于深度学习。很多开源的语音算法都有基于统计模型的判决方法,如 WebRTC和 Speex中基于高斯混合模型的VAD检测方法,这类方法对信噪比较高的音源检测效果良好。随着深度学习的兴起,基于深度学习的VAD方法也已经在一些特定场景商用了,最新的 WebRTC集成了基于RNN模型的VAD检测方法,该方法作为WebRTC新一代AGC(Automatic Gain Control,自动增益控制)算法中的一个子部分存在。远场情景下,由于传播路径较远,反射、散射、吸收、衰减的影响变强,这导致语音的SNR和SDR 比近场差很多,如果此时仅用基于统计模型的检测方法并不能得到很好的判决结果,则有两种解决思路,一种是先提高SNR,再进行VAD检测,另一种是直接对低SNR的带嗓语音进行检测,如一些基于深度学习的方法在训练语料中加入噪声,这类方法在数据集充分的前提下,其准确性可以超过基于统计模型的方法。

在WEBRTC的VAD代码中,WebRTC VAD支持8/16/24/32/48kHz采样率,不过都会重采样到8kHz进行计算,每一帧长度可以为80/10ms、160/20ms和240/30ms三种。VAD具有如下的四种模式,分别表示通用模式、低比特率模式、激进模式和非常激进模式,在不同模式下高斯混合模型的参数和判决的门限值有所不同。

    enum Aggressiveness {
        kVadNormal = 0,
        kVadLowBitrate = 1,
        kVadAggressive = 2,
        kVadVeryAggressive = 3
    };

WebRTC采用GMM统计模型对语音进行VAD判决,将04kHz划分为如下的六个频段:80250Hz,250500Hz,5001kHz,1k2kHz,2k3kHz,3k~4kHz,并使用这些频段的子带能量作为GMM相关特征。

VAD的大致处理流程为:

  1. 进行一些基本的检测(WebRtcVad_ValidRateAndFrameLength),检测VAD结构体是否被初始化,语音帧长度是否满足条件。
  2. 对语音进行下采样,采样到8kHz,WebRTC的下采样并不是一步到位。以48kHz下采样到8kHz为例(WebRtcSpl_Resample48khzTo8khz):首先48kHz下采样到24kHz,然后对24kHz的语音数据进行低通滤波(这一步并不改变采样率),后面继续进行下采样24kHz->16kHz,16kHz->8kHz,最终得到8kHz的语音数据。
  3. 得到8kHz的语音数据之后,我们计算语音各个子带的能量作为GMM相关特征(WebRtcVad_CalculateFeatures)。首先将4kHz的数据分为02kHz和2k4kHz,然后将2k4kHz部分划分为2k3kHz和3k4kHz两部分。02kHz中则先分为01kHz和1k2kHz两部分,其中01kHz再分为0250Hz和250500Hz,最后对0250Hz部分进行80Hz高通滤波得到80250Hz的部分,至此得到六个子带:80250Hz,250500Hz,5001kHz,1k2kHz,2k3kHz,3k~4kHz六个子带,分别计算这六个子带的对数能量作为GMM相关特征。除此之外,还计算了一个total_energy,这个参数会在后面计算GMM的时候使用(WebRtcVad_GmmProbability)。
  4. 接下来就是计算GMM的部分的,在计算前会根据语音帧长度选择不同的判决阈值。

a. 首先判断上一步骤计算得到的total_energy是否大于能量的门限值kMinEnergy,如果大于则对当前帧进行处理;否则直接将vad_flag置为0。

b. 计算每个子带对应的高斯概率(WebRtcVad_GaussianProbability)并与子带的权重相乘作为语音/噪声最终的概率,这里WebRTC为了简化计算,假设语音和噪声的高斯模型是不相关的。

c. 计算每个子带的对数似然比(log likelihood ratio, LLR),每个子带的似然比会和阈值进行比较作为一个局部的VAD判决。所有子带的对数加权似然比之和和阈值比较作一个全局的VAD判决。当局部判决或者全局判决结果有一个判决有语音时则认定当前帧是语音帧。

d. 使用hangover对结果进行平滑

附录:

1、参考文档

  1. www.cnblogs.com/VideoCloudT… (深入浅出 WebRTC AEC - 声学回声消除)
  2. www.cnblogs.com/LXP-Never/p… (声学回声消除(Acoustic Echo Cancellation)原理与实现)
  3. www.cnblogs.com/dylancao/p/… (webrtc aecd算法解析一(原理分析))
  4. zhuanlan.zhihu.com/p/414964950(详解 WebRTC 高音质低延时的背后 — AGC(自动增益控制))
  5. www.cnblogs.com/lactry2king…(WebRTC VAD流程解析)