详解低延时高音质:回声消除与降噪篇
在实时音频互动场景中,除了我们上一篇讲到的编解码会影响音质与体验,在端上,降噪、回声消除、自动增益模块同样起着重要作用。在本篇内容中我们将主要围绕回声消除和降噪模块,讲讲实时互动场景下的技术挑战,以及我们的解决思路与实践。
回声消除的三大算法模块优化
在语音通信系统中,回声消除(Echo Cancellation)一直扮演着核心算法的角色。一般来说,回声消除的效果受诸多因素的影响,包括:
声学环境,包括反射,混响等;
通话设备本身声学设计,包括音腔设计以及器件的非线性失真等;
系统性能,处理器的计算能力以及操作系统线程调度的能力。
声网回声消除算法在设计之初,就将算法性能、鲁棒性和普适性作为最终的优化目标,这一点对于一个优秀的音视频 SDK 来说至关重要。
首先,回声是怎么产生的?简单来讲,就是你的声音从对方的扬声器发出,这个声音又被他的麦克风给收录了进去,这个被麦克风收录的声音又传回到你这一端,你就听到了回声。为了消除回声,我们就要设计一个算法将这个声音信号从麦克风信号中去除掉。
那么声学回声消除模块(AEC, Acoustic Echo Cancellation)是如何消除回声的呢?具体的步骤见如下简图所示:
第一步需要找到参考信号/扬声器信号(蓝色折线)跟麦克风信号(红色折线)之间的延迟,也就是图中的 delay=T。
第二步根据参考信号估计出麦克风信号中的线性回声成分,并将其从麦克风信号中减去,得到残差信号(黑色折线)。
第三步通过非线性的处理将残差信号中的残余回声给彻底抑制掉。
与以上的三个步骤相对应,回声消除也由三个大的算法模块组成:
延迟估计(Delay Estimation)
线性自适应滤波器(Linear Adaptive Filter)
非线性处理(Nonlinear Processing)
其中「延迟估计」决定了 AEC 的下限,「线性自适应滤波器」决定了 AEC 的上限,「非线性处理」决定了最终的通话体验,特别是回声抑制跟双讲之间的平衡。
注:双讲是指在交互场景中,互动双方或多方同时讲话,其中一方的声音会受到抑制,从而出现断断续续的情况。这是由于回声消除算法“矫枉过正”,消除了部分不该去除的音频信号。
接下来,我们先围绕这三个算法模块,分别讲讲其中的技术挑战与优化思路。
一、延迟估计
受具体系统实现的影响,当把参考信号与麦克风信号分别送入 AEC 模块进行处理之时,它们所存入的数据 buffer 之间存在一个时间上的延迟,即我们在上图中看到的“delay=T”。假设这个产生回声的设备是一部手机,那么声音从它的扬声器发出后,一部分会经过设备内部传导到麦克风,也可能会经过外部环境传回到麦克风中。所以这个延迟就包含了设备采集播放 buffer 的长度,声音在空气中传输的时间,也包含了播放线程与采集线程开始工作的时间差。**正是由于影响延迟的因素很多,因此这个延迟的值在不同系统,不同设备,不同 SDK 底层实现上都各不相同。**它在通话过程中也许是一个定值,也有可能会中途变化(所谓的 overrun 和 underrun)。这也是为什么一个 AEC 算法在设备 A 上可能起作用,但换到另一个设备上可能效果会变差。延迟估计的精确性是 AEC 能够工作的先决条件,过大的估计偏差会导致 AEC 的性能急剧下降,甚至无法工作,而无法快速跟踪时延变化是出现偶现回声的重要因素。
增强延迟估计算法鲁棒性
传统算法通常通过计算参考信号跟麦克风信号之间的相关性来决定延迟。相关性的计算可以放在频域上,典型的就是 Binary Spectrum 的方法,通过计算单频点上的信号能量是否超过一定门限值,实际将参考信号跟麦克风信号映射成了两维的 0/1 数组,然后通过不断移动数组偏移来找到延迟。最新的 WebRTC AEC3 算法通过并行的多个 NLMS 线性滤波器来寻找延迟,这个方法在检测速度及鲁棒性方面都取得了不错的效果,但是计算量非常大。当在时域上计算两个信号的互相关时,一个明显的问题是语音信号包含大量的谐波成分并且具有时变特性,它的相关信号常常呈现出多峰值的特征,有的峰值并不代表真正的延迟,并且算法容易受到噪声干扰。
声网延迟估计算法通过降低信号之间的相关性(de-correlate),能够有效抑制 local maxima 的值以大大增强算法的鲁棒性。如下图所示,左边是原始信号之间的互相关,右边是声网 SDK 处理后的互相关,可见信号的预处理大大增强了延迟估计的鲁棒性:
算法自适应,降低计算量
通常延迟估计算法为了降低计算量的需要,会预先假设回声信号出现在某个较低的频段内,这样就可以将信号做完下采样之后再送入延迟估计模块,降低算法的计算复杂度。然而面对市面上数以万计的设备及各种路由,以上的假设往往并不成立。下图是 VivoX20 在耳机模式下麦克风信号的频谱图,可见回声都集中在 4kHz 以上的频段内,传统的算法针对这些 case 都会导致回声消除模块的失效。声网延迟估计算法会在全频段内搜索回声出现的区域,并自适应地选择该区域计算延迟,确保算法在任何设备,路由下都有精确的延迟估计输出。
图:VivoX20 接入耳机后的麦克风信号
动态更新音频算法库,提升设备覆盖率
为了确保算法的持续迭代改进,声网维护了一个音频算法的数据库。我们使用大量不同测试设备,在不同声学环境下采集到了各种参考信号和麦克风信号的组合,而它们之间的延迟全部通过离线处理的方式进行标定。除了真实采集的数据,数据库中也包含了大量仿真的数据,包括不同的说话人,不同的混响强度,不同的底噪水平,以及不同的非线性失真类型。为了衡量延迟估计算法的性能,参考信号与麦克风信号之间的延迟可以随机的变化,用以观察算法对突发延迟变化的响应。
所以判断一个延迟估计算法的优劣,还需要考察:
1.适应尽量多的设备、声学环境,且在尽量短的时间内根据设备、声学环境的因素匹配合适的算法;
2.在突发随机的延迟变化后,能及时动态调整算法策略。
以下是声网 SDK 与友商之间的延迟估计性能对比,总共使用了数据库中 8640 组测试数据。从图中数据可以看出,声网 SDK 可以在更短的时间内找到大多数测试数据的初始时延。在 96% 的测试数据中,声网 SDK 能在 1s 之内找到它们正确的延迟,而友商这一比例为 89%。
第二个测试的是在通话过程中出现随机的延迟抖动,测试延迟估计算法要在尽量短的时间内找到精确的延迟值。如图中所示,声网 SDK 在 71% 的测试数据中能在 3s 之内重新找到变化后的精确延迟值,而友商这个比例为 44%。
二、线性自适应滤波器
对于线性滤波器,已有大量的文献介绍其原理及实践。当应用于回声消除的应用场景,主要考虑的指标包含收敛速度(convergence rate),稳态失调(steady-state misalignment)及跟踪性能(tracking capability)。这些指标之间往往也有冲突,譬如较大的步长可以改善收敛速度,但是会导致较大的失调。这个就是自适应滤波器中的没有免费的午餐定理(No Free Lunch Theorem)。
对于自适应滤波器的类型,除了最为常用的 NLMS 滤波器(Model Independent),还可以使用 RLS 滤波器(Least Squares Model)或 Kalman 滤波器(State-Space Model)。除了各自理论推导中的各种假设,近似,优化,这些滤波器的性能最终都归结到如何计算最佳的步长因子(在卡尔曼滤波器里面步长因子合并到 Kalman Gain 的计算里面)。在滤波器尚未收敛或是环境传输函数突变的情形下,步长因子需要足够大以跟踪环境变化,当滤波器收敛及环境传递函数变化缓慢的时间段,步长因子应尽量减小以达到尽可能小的稳态失调。对于步长因子的计算,需要考虑自适应滤波器后残余回声跟残差信号间的能量比值,建模为系统的 leakage coefficients。这个变量常常等价于求滤波器系数跟真实传递函数之间的差(Kalman 滤波器里面称为状态空间状态向量误差),这也是整个估计算法中的难点。除此之外,双讲阶段的滤波器发散问题也是一个需要考虑的点,一般来说这个问题可以通过调整滤波器结构,使用 two echo path models 来解决。
声网自适应滤波器算法并不使用单一的滤波器类型,而是兼顾了不同滤波器之间的优点,同时搭配自适应算法计算最优的步长因子。此外,算法通过线性滤波器系数实时估计环境的传递函数,自动修正滤波器长度,以覆盖通信设备连接 HDMI 外设等高混响,强回声的场景。如下是一个例子,在声网办公室的一个中型会议室中(面积约 20m2,三面玻璃墙),使用 Macbook Pro 通过 HDMI 连接小米电视,图中是线性滤波器时域信号的变化趋势,算法能自动计算并匹配实际环境传递函数的长度(在第 1400 帧左右自动检测出了强混响环境),以优化线性滤波器的性能。
同样的,我们也使用数据库中大量测试数据进行声网 SDK 与友商之间的性能对比,对比的指标包括稳态失调(滤波器收敛之后对回声的抑制程度)以及收敛速度(滤波器达到收敛状态需要的时长)。第一张图代表自适应滤波器的稳态失调,声网 SDK 在 47%的测试数据中能达到超过 20dB 的回声抑制,而友商的比例为 39%。
下图显示的是自适应滤波器的收敛速度,声网 SDK 在 51% 的测试样本中能在通话前 3s 之内收敛到稳态,而友商的比例为 13%。
三、非线性处理
非线性处理旨在抑制线性滤波器所没有预测出的回声成分,通常通过计算参考信号,麦克风信号,线性回声以及残差信号间的相关性,或是将相关性直接映射到抑制增益上,或是通过相关性估计出残留回声的功率谱,进一步通过维纳滤波器等传统降噪的算法抑制残留回声。
作为回声消除算法的最后一个模块,除了抑制残留回声之外,非线性处理单元也肩负着监控整个系统是否正常工作的重任,譬如线性滤波器是否因为延迟抖动而无法正常工作?在声网 SDK 回声消除之前是否存在硬件回声消除未能处理的残余回声?
下面是一个简单的例子,通过自适应滤波器估计出的回声能量等内部参数,能够更快的发现延迟变化的现象,并且提示 NLP 采取相应的动作:
随着声网 SDK 覆盖的场景越来越广,针对音乐信号的传输成为了一个重要的场景。声网 SDK 针对音乐信号的回声消除体验做了大量的优化,一个典型场景是舒适噪声的估计算法改进。传统算法使用基于 Minimum Statistics 的算法原理对信号中的底噪进行估计,当把这个算法应用于音乐信号时,因为音乐信号比之语音信号更为平稳,因此会过高的估计噪声功率,反映到回声消除中会导致处理后有回声时段与无回声时段间的底噪(背景噪声)不平稳,体验极差。声网 SDK 通过信号分类以及模块融合的方式,彻底解决了 CNG 估计导致的底噪起伏现象。
除此之外,声网 SDK 还针对所有可能碰到的极端情况,包括非因果系统,设备频率偏移,采集信号溢出,声卡包含系统信号处理等等,都进行了大量的优化,确保算法能够工作在所有的通信场景中。
音质优先的降噪策略
降噪对信号音质的影响大于回声消除模块,这一点源自于在降噪算法的设计之初,我们先验的假设底噪都是平稳信号(至少是短时平稳的),而根据这个假设,音乐跟底噪的区分度明显弱于语音跟底噪的区分度。
声网 SDK 在降噪模块的前端预置了信号分类模块,能够精确的检测出信号的类型,并根据信号的类型调整降噪算法的类型及参数,常见的信号类型包括一般语音、清唱、音乐信号等。下图所示是两个降噪算法处理的信号片段,其中第一个是语音与音乐的混合信号,前 15 秒为含噪的语音信号,之后是 40s 是音乐信号,再之后是 10s 的含噪语音,语谱图从上到下分别是原始信号、友商处理结果、声网 SDK 处理结果。结果显示在语音段信号降噪性能差不多的前提下,竞品处理过信号中的音乐部分受到了严重的损伤,而声网 SDK 的处理并没有降低音乐的音质。
在第二个例子中,使用的音频是歌手的清唱,清唱中歌手反复发出“啊”的声音。在下图的语谱图中,从上到下分别是原始信号、友商的处理结果、声网 SDK 处理结果。结果显示,友商的降噪处理严重的损伤了原语音的频谱成分,而声网 SDK 完整的保留了原语音的谐波成分,保证了歌手清唱时候的音质体验。
结语
自 1967 年贝尔实验室的 M. M. Sondhi 开创性的提出以自适应滤波器的方法来消除回声为开端,无数的研究和实践都投入到了这个语音通信的最基本问题上。要完美的解决回声问题,除了要有强大的算法作为基础,也需要在工程优化领域做很多优化。声网会持续不断的改进回声消除在各个不同应用场景下的体验。
在本系列的下一篇内容中,我们将随着音频信号,从设备端进入现实的网络环境,一边实地环行上海,一边聊聊音频互动场景下的延时、抖动,以及丢包对抗背后的优化策略。(以一张图来简单剧透,敬请期待)
评论