幸运的是,我们并非没有任何办法可以找到远端回声和近端声音的边界。
远端的声音信号和回声是相关的。也许有朋友会一拍脑袋焕然大悟地说:那就直接把远端声音从采集到的声音中减掉就可以了。然而事情并没有那么简单。
远端的声音信号并非等同于回声。远端的声音从扬声器播放出来,到被采集端采集,经历过扬声器 - 房间 - 麦克风(Loudspeaker-Room-Microphone,LRM)这样的回声馈路。在 LRM 回声馈路中传播的时候,远端声音一方面经过多次反射,另外一方面经过多次叠加,最后变得和远端声音信号有差别了。我们把这个差别用一个函数来表示:
- fe = f(fs)
其中,
fs=far-end signal(远端信号);
fe=far-end echo (远端回声);
如果能够对这个函数求解,那么就可以根据远端声音信号和远端回声之间的相关性进行建模。这个模型是对回声馈路 LRM 的模拟,会高度逼近回声馈路 LRM。
等到这个模型稳定时,输入远端声音信号 fs,就可以输出高度接近远端回声的信号 fe。通过滤波器生成反相的信号,和采集到的声音信号进行叠加,就可以把回声信号消除掉。这就是回声消除 AEC 的基本原理。
这个函数求出来的解不大可能和远端回声完全一致,只能高度逼近。该函数求出来的解和远端回声越逼近,回声消除的效果就越好。
虽然实时语音的通话是双工模式,但是可以分为不同的情形:静音、单讲和双讲。针对不同的情形要采取不同的回声消除策略。
即没有人说话的情形。
在语音段才需要做回声消除,在非语音段不会有回声,不需要做回声消除,甚至不需要发送语音信息,从而可以降低码率,节约带宽成本。
因此,准确探测语音活动十分重要。语音的探测算法叫 VAD(Voice Activity Detection)。不同的厂商有不同的 VAD 实现方法。我们是利用基音周期实现 VAD,有效地提高 VAD 判断的准确性,避免将非语音段误判为语音段。
即只有远端说话的情形。
由于只有远端说话,从麦克风采集进来的语音信号只包含远端的回声,而不包含近端的语音。单讲情形下的回声消除相对比较容易处理,可以采取比较激进的处理策略。
如果确定单讲是高概率事件的情况下,可以直接地把所有语音信号都干掉,然后适当地填充舒适噪音。一般来说,在单讲情形下,用线性自适应滤波器跟踪回声馈路,可以很好地消除回声,大概能够抑制掉 18dB 的回声。
有多方同时说话的情形。
由于有多方同时说话,从麦克风采集进来的语音信号就包含了远端的回声和近端的语音,两者混合在一起。双讲情形下的回声消除就十分困难:一方面要保护近端的语音信号不被损伤,另外一方面还要尽量地把回声消除干净。
这里不但有 "红墨水蓝墨水分离" 的难题,而且还有 "投鼠忌器" 的困境。一般来说,在远端回声比近端语音要高大概 6dB~8dB 的情况下,如果要把远端回声消除干净,那么肯定会或多或少地损伤到近端语音。
另外,如果远端回声比近端语音要高出 18dB 以上,比如说扬声器离麦克风太近,远端回声把近端语音完全掩盖掉了,那么回声消除的效果肯定是不好的。这种情形下,可以采取比较激进的策略,把远端回声和近端语音一起干掉,然后适当地填充舒适噪音。
因此,回声消除模块要有能力区分这三种情形,才能针对各种情形采取不同的算法。通过 VAD 可以区分非语音段和语音段。如何区分单讲和双讲的方法将在下面讨论。
回声消除主要包含两个步骤:线性自适应滤波和非线性处理。
线性自适应滤波就是对 fe=f(fs) 求解,建立远端回声的语音模型,进行第一轮回声消除。
非线性处理又分为两个步骤:残留回声处理和非线性剪切处理。残留回声处理进行第二轮回声消除,处理残留回声;非线性剪切处理就是对衰减量达到阈值的语音信号进行比较激进的剪切处理。
线性自适应滤波和非线性剪切处理在学术论文和开源项目中能找到东西学习。残留回声处理就很难,一般都是要靠团队自行摸索、积累和创新。正是因为如此,语音技术的门槛才如此的高。
(点击放大图像)
回声消除的原理与实现
以远端声音信号和远端回声的相关性为基础,建立远端回声的语音模型,利用它对远端回声进行估计,目的是获得对远端回声尽量逼近的估计。我们可以把回声馈路 LRM 看作一个 "环境滤波器"。
经过它的处理,远端声音信号被变成远端回声。回声消除就是要构建一个 "算法滤波器",基于对远端回声的语音模型,不断地调整该滤波器的系数,使得估计值更加逼近真实的回声。估计值越逼近真实回声,回声消除效果就越好。
自适应滤波器收敛后得到的就是需要求解的回声馈路函数 fe=f(fs)。当滤波器收敛稳定之后,输入远端声音信号 fs,就可以输出相对准确的对远端回声信号的估计值 fe。把采集到的信号减去远端回声信号的估计值 fe,就得到实际要发送的语音信号。
实现线性自适应滤波器有两个难点:
这两个难点是一对矛盾的特征,要求自适应滤波器一方面要能够快速收敛后保持系数高度稳定,另外一方面要能够随时保持更新状态跟踪回声馈路的变化。
通过自适应滤波器来消除回声,并不能百分之一百把回声消除干净,还需要进一步消除残留的回声。
一般来说,残留回声消除的策略是利用自适应滤波器处理后的残留回声与远端参考语音信号的相关性,进一步消除残留回声。相关性越大,说明残留回声越多,需要对残留回声进一步消除的程度越大;反之,相关性越小,说明残留回声较少,需要对残留回声进一步消除的程度越小。
因此,首先要通过计算残留回声与参考信号的相关矩阵,得到一个反映消除程度的衰减因子;然后将残留回声乘以衰减因子,从而进一步消除残留回声。
在线性自适应滤波做完了以后,可以利用残留回声和麦克风采集到的远端回声信号的相关性来检测是处于单讲还是双讲状态。根据单讲还是双讲状态,可以进一步调整衰减因子。
如果处于远端单讲状态,因为近端没有声音信号(没人说话),可以尽量多地抑制回声,让衰减因子尽量地小;如果处于双讲状态,因为线性自适应滤波器是在尽量不损伤近端语音音质的前提下消除回声,回声抑制量不会太大,所以衰减因子相对较大。
消除残留回声的算法难度甚高。在论文或者开源项目中甚少有可参考的东西,各家厂商都是通过私有的算法来实现的,甚至很多厂商都选择不实现。
在完成了上述处理以后,其实剩下的回声一般都比较小了,但不排除仍有一些残留的可以感知的小回声。为了进一步消除这些小回声,要根据前面处理得到的衰减量来做进一步的抑制处理。
在这里要为衰减量设定一个阈值。一般来说,这个衰减量阈值要设定得比较保守(比较高)。
如果衰减量达到或者超过设定的阈值,就表明回声消除量比较大,采集进来的语音信号很可能全部都是回声信号,那么就直接将语音信号全部消除掉,并填充舒适噪声,防止声音听感起伏。能达到那么大的衰减量,一般是处于远端单讲状态,或者远端回声信号要远远大于近端语音信号的双讲状态。
正常的双讲状态下,为了保护近端语音的音质,自适应滤波器是不会做大幅的回声消除的。因此,只要衰减量达到或者超过设定阈值,把采集到的语音信号全部消除掉是不会影响正常听音效果的。
如果衰减量没有超过设定的阈值,那么就不要进一步做回声消除了。这种情形可能是双讲状态,要保护本地语音的音质,避免本地语音被当成回声误杀。
业界一般有两种做法:一种是允许对近端声音有些许损伤也要把远端回声消除干净,另外一种是允许保留些许远端回声也不要对近端声音造成损伤。如果过分消除回声,就会造成断续的听音感觉。回声消除就是要在这两种做法之间找平衡点。
笔者在工作中的实践表明,在音视频社交行业,回声消除是客户高度关注的一个技术特征。与此同时,回声消除也是音视频社交中最有难度的技术,没有之一。即使是王者荣耀这种顶级的游戏,也十分重视回声消除的效果。在以用户体验为生命线的游戏行业,特别在手游做得越来越重的今天,回声消除技术做得好不好,往往决定了用户是否继续玩你的游戏。
冼牛,即构科技 Zego 技术专家,北京邮电大学计算机硕士,香港大学工商管理硕士,多年从事语音视频云服务技术研究,专注互动直播技术和实时游戏语音。
来源: http://www.infoq.com/cn/articles/how-does-echo-cancellation-technology-work-in-av