解密回声消除技术之一(理论篇)

http://hulong988.blog.51cto.com

解密回声消除技术之一(理论篇) 2009-06-11 22:24:58

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://silversand.blog.51cto.com/820613/166095
一、前言
因为工作的关系,笔者从2004年开始接触回声消除(Echo Cancellation)技术,而后一直在某大型通讯企业从事与回声消除技术相关的工作,对回声消除这个看似神秘、高端和难以理解的技术领域可谓知之甚详。
要了解回声消除技术的来龙去脉,不得不提及作为现代通讯技术的理论基础——数字信号处理理论。首先,数字信号处理理论里面有一门重要的分支,叫做自适应信号处理。而在经典的教材里面,回声消除问题从来都是作为一个经典的自适应信号处理案例来讨论的。既然回声消除在教科书上都作为一种经典的具体的应用,也就是说在理论角度是没有什么神秘和新鲜的,那么回声消除的难度在哪里?为什么提供回声消除技术(不管是芯片还是算法)的公司都是来自国外?回声消除技术的神秘性在哪里?
 
二、回声消除原理
从通讯回音产生的原因看,可以分为声学回音(Acoustic Echo)和线路回音(Line Echo),相应的回声消除技术就叫声学回声消除(Acoustic Echo Cancellation,AEC)和线路回声消除(Line Echo Cancellation, LEC)。声学回音是由于在免提或者会议应用中,扬声器的声音多次反馈到麦克风引起的(比较好理解);线路回音是由于物理电子线路的二四线匹配耦合引起的(比较难理解)。
回音的产生主要有两种原因:
1.  由于空间声学反射产生的声学回音(见下图):
 
图中的男子说话,语音信号(speech1)传到女士所在的房间,由于空间的反射,形成回音speech1(Echo)重新从麦克风输入,同时叠加了女士的语音信号(speech2)。此时男子将会听到女士的声音叠加了自己的声音,影响了正常的通话质量。此时在女士所在房间应用回音抵消模块,可以抵消掉男子的回音,让男子只听到女士的声音。
2.  由于2-4线转换引入的线路回音(见下图):
 
 
在ADSL Modem和交换机上都存在2-4线转换的电路,由于电路存在不匹配的问题,会有一部分的信号被反馈回来,形成了回音。如果在交换机侧不加回音抵消功能,打电话的人就会自己听到自己的声音。
不管产生的原因如何,对语音通讯终端或者语音中继交换机需要做的事情都一样:在发送时,把不需要的回音从语音流中间去掉。
试想一下,对一个至少混合了两个声音的语音流,要把它们分开,然后去掉其中一个,难度何其之大。就像一瓶蓝墨水和一瓶红墨水倒在一起,然后需要把红墨水提取出来,这恐怕不可能了。所以回声消除被认为是神秘和难以理解的技术也就不奇怪了。诚然,如果仅仅单独拿来一段混合了回音的语音信号,要去掉回音也是不可能的(就算是最先进的盲信号分离技术也做不到)。但是,实际上,除了这个混合信号,我们是可以得到产生回音的原始信号的,虽然不同于回音信号。
我们看下面的AEC声学回声消除框图(本图片转载)。
 
 
Figure Acoustic Echo Cancellation in a voice communication terminal
 
其中,我们可以得到两个信号:一个是蓝色和红色混合的信号1,也就是实际需要发送的speech和实际不需要的echo混合而成的语音流;另一个就是虚线的信号2,也就是原始的引起回音的语音。那大家会说,哦,原来回声消除这么简单,直接从混合信号1里面把把这个虚线的2减掉不就行了?请注意,拿到的这个虚线信号2和回音echo是有差异的,直接相减会使语音面目全非。我们把混合信号1叫做近端信号ne,虚线信号2叫做远端参考信号fe,如果没有fe这个信号,回声消除就是不可能完成的任务,就像“巧妇难为无米之炊”。
虽然参考信号fe和echo不完全一样,存在差异,但是二者是高度相关的,这也是echo称之为回音的原因。至少,回音的语义和参考信号是一样的,也还听得懂,但是如果你说一句,马上又听到自己的话回来一句,那是比较难受的。既然fe和echo高度相关,echo又是fe引起的,我们可以把echo表示为fe的数学函数:echo=F(fe)。函数F被称之为回音路径。在声学回声消除里面,函数F表示声音在墙壁,天花板等表面多次反射的物理过程;在线路回声消除里面,函数F表示电子线路的二四线匹配耦合过程。很显然,我们下面要做的工作就是求解函数F。得到函数F就可以从fe计算得到echo,然后从混合信号1里面减掉echo就实现了回声消除。
 
尽管回声消除是非常复杂的技术,但我们可以简单的描述这种处理方法:
  1、房间A的音频会议系统接收到房间B中的声音
  2、声音被采样,这一采样被称为回声消除参考
  3、随后声音被送到房间A的音箱和声学回声消除器中
  4、房间B的声音和房间A的声音一起被房间A的话筒拾取
  5、声音被送到声学回声消除器中,与原始的采样进行比较,移除房间B的声音
 
求解回音路径函数F的过程恐怕就是比较难以表达的数学公式了。鉴于通俗表达数学公式的难度比发现数学公式还难,笔者就不费力解释了。下面这段表达了利用自适应滤波器原理求解函数F的过程。(以下可以跳过
 
自适应滤波器
自适应滤波器是以输入和输出信号的统计特性的估计为依据,采取特定算法自动地调整滤波器系数,使其达到最佳滤波特性的一种算法或装置。自适应滤波器可以是连续域的或是离散域的。离散域自适应滤波器由一组抽头延迟线、可变加权系数和自动调整系数的机构组成。附图表示一个离散域自适应滤波器用于模拟未知离散系统的信号流图。自适应滤波器对输入信号序列x(n)的每一个样值,按特定的算法,更新、调整加权系数,使输出信号序列y(n)与期望输出信号序列d(n)相比较的均方误差为最小,即输出信号序列y(n)逼近期望信号序列d(n)。
 
 
以最小均方误差为准则设计的自适应滤波器的系数可以由维纳-霍甫夫方程解得。
B.维德罗提出的一种方法,能实时求解自适应滤波器系数,其结果接近维纳-霍甫夫方程近似解。这种算法称为最小均方算法或简称 LMS法。这一算法利用最陡下降法,由均方误差的梯度估计从现时刻滤波器系数向量迭代计算下一个时刻的系数向量
   
式中ks为一负数,它的取值决定算法的收敛性, Vε2(n)】为均方误差梯度估计,
 
自适应滤波器应用于通信领域的自动均衡、回声消除、天线阵波束形成,以及其他有关领域信号处理的参数识别、噪声消除、谱估计等方面。对于不同的应用,只是所加输入信号和期望信号不同,基本原理则是相同的。(以上部分可以跳过
 
上面这段话表明,需要求解的回音路径函数F就是一个自适应滤波器W(n)收敛的过程。所加输入信号x(n)是fe,期望信号是echo,自适应滤波器收敛后的W(n)就是回音路径函数F。 收敛之后,当实际回音发生,我们把fe通过函数W(n),就可以得到一个很准确的echo,把混合信号直接减去echo,得到实际需要发送的语音speech,完成回声消除任务。
值得注意的两点:
1、           自适应滤波器收敛阶段,期望信号是完全的echo,不能混杂有speech。因为speech和fe是没有关系的,会扰乱W(n)的收敛过程。也就是说要求回声消除算法开始运转后收敛要非常快,最好对方还来不及说话,你一说就收敛好了;收敛好之后,如果对方开始说话,也就是有speech混合过来,这个W(n)系数就不要变化了,需要稳定下来。
2、            回音路径可能是变化的,一旦出现变化,回声消除算法要能判断出来,因为自适应滤波器学习要重新开始,也就是W(n)需要一个新的收敛过程,以逼近新的回音路径函数F。
基本上来说,上面这两点是两难的,一个需要自适应滤波器收敛后保持系数稳定,以保证不受speech说话干扰,另一个需要自适应滤波器随时保持更新状态,以保证能够追踪变化的回音路径。这样一来,仅从数学算法层面,回声消除已经是难上加难!简单地说,回声消除自适应滤波器的设计具有两个互相矛盾的特性,也就是快速收敛和高度的稳定性,如何同时实现这两项特性,正是设计上的主要挑战。
经过上面的分析,相信大家对回声消除的原理和技术有了深刻的理解,这是一门即容易理解又难以实现的技术。

本文出自 “碧海银沙” 博客,请务必保留此出处http://silversand.blog.51cto.com/820613/166095

标题:        作者:zhaohl  时间:2006-09-26 16:22
网上可查到的用于回音消除的源码很少。
但对于想做SOFTPHONE的人来说,这些东西也是必不可少的。
在DIRECTSOUND中带有AEC功能,但必须是XP以上系统支持。
[[i] 本帖最后由 zhaohl 于 2006-9-26 16:23 编辑 [/i]]
 
标题:谢谢楼主        作者:yangziyi  时间:2006-10-18 11:30
你的东西对我太有用了,希望以后能多提供  
标题:        作者:y123456  时间:2006-10-21 16:21
bucuo  
标题:        作者:share2000  时间:2006-10-31 11:27
好东西。楼主是做什么方面的工作的。我是做汽车音箱方面的。有机会多交流。 我的email- [email]wxh_0423@163.com[/email]  
标题:        作者:taijilin  时间:2006-11-14 01:03
好东西,顶一下  
标题:        作者:alpha6  时间:2006-11-20 14:45
好东西啊  
标题:回复 #1 zhaohl 的帖子        作者:xbwang020  时间:2006-12-13 09:30
好像晚上这方面的资料太少了 ^_^  
标题:这个软件怎么测试呀?        作者:11uu  时间:2006-12-13 17:37
请教各位DX,请问这个软件怎么测试呀,直接执行echo.exe吗?没有效果呀。  
标题:顶        作者:11uu  时间:2006-12-13 17:41
请教搂主和各位DX  
标题:搂主        作者:11uu  时间:2006-12-13 17:43
你在哪里呀?  
标题:        作者:wmh_hong  时间:2006-12-14 23:50
不错~  
标题:[转]一个开源的声学回声消除器(Acoustic Echo Cancellation)        作者:zero100  时间:2006-12-18 23:45
前段时间,搞了一阵声学回声消除,非常郁闷,因为没有成功,但可以说学到一点东西吧,至少理论上懂了一点。 为什么需要声学回声消除呢?在一般的VOIP软件或视频会议系统中,假设我们只有A和B两个人在通话,首先,A的声音传给B,B然后用喇叭放出来,而这时B的MIC呢则会采集到喇叭放出来的声音,然后传回给A,如果这个传输的过程中时延足够大,A就会听到一个和自己刚才说过的话一样的声音,这就是回声,声学回声消除器的作用就是在B端对B采集到的声音进行处理,把采集到声音包含的A的声音去掉再传给A,这样,A就不会听到自己说过的话了。 声学回声消除的原理我就不说了,这在网上有很多文档,网上缺少的是实现,所以,我在这把一个开源的声学回声消除器介绍一下,希望对有些有人用,如果有人知道怎么把这消除器用的基于实时流的VOIP软件中,希望能一起分享一下。 这个声学回声消除器是一个著名的音频编解码器speex中的一部分,1.1.9版本后的回声消除器才起作用,以前版本的都不行,我用的也是这个版本,测试表明,用同一个模拟文件,它有效果比INTEL IPP库4.1版中的声学回声消除器的还要好。 先说编译。首先,从[url]www.speex.org[/url]上下载speex1.1.9的源代码,解压,打开speex\win32\libspeex中的libspeex.dsw,这个工作区里有两个工程,一个是libspeex,另一个是libspeex_dynamic。然后,将libspeex中的mdf.c文件添加到工程libspeex中,编译即可。 以下是我根据文档封装的一个类,里面有一个测试程序: //file name: speexEC.h #ifndef SPEEX_EC_H #define SPEEX_EC_H #include <stdio.h> #include <stdlib.h> #include "speex/speex_echo.h" #include "speex/speex_preprocess.h" class CSpeexEC { public: CSpeexEC(); ~CSpeexEC(); void Init(int frame_size=160, int filter_length=1280, int sampling_rate=8000); void DoAEC(short *mic, short *ref, short *out);
protected: void Reset();
private: bool m_bHasInit; SpeexEchoState* m_pState; SpeexPreprocessState* m_pPreprocessorState; int m_nFrameSize; int m_nFilterLen; int m_nSampleRate; float* m_pfNoise; };
#endif
//fine name:speexEC.cpp #include "SpeexEC.h"
CSpeexEC::CSpeexEC() { m_bHasInit = false; m_pState = NULL; m_pPreprocessorState = NULL; m_nFrameSize = 160; m_nFilterLen = 160*8; m_nSampleRate = 8000; m_pfNoise = NULL; }
CSpeexEC::~CSpeexEC() { Reset(); }
void CSpeexEC::Init(int frame_size, int filter_length, int sampling_rate) { Reset();
if (frame_size<=0 || filter_length<=0 || sampling_rate<=0) { m_nFrameSize =160; m_nFilterLen = 160*8; m_nSampleRate = 8000; } else { m_nFrameSize =frame_size; m_nFilterLen = filter_length; m_nSampleRate = sampling_rate; }
m_pState = speex_echo_state_init(m_nFrameSize, m_nFilterLen); m_pPreprocessorState = speex_preprocess_state_init(m_nFrameSize, m_nSampleRate); m_pfNoise = new float[m_nFrameSize+1]; m_bHasInit = true; }
void CSpeexEC::Reset() { if (m_pState != NULL) { speex_echo_state_destroy(m_pState); m_pState = NULL; } if (m_pPreprocessorState != NULL) { speex_preprocess_state_destroy(m_pPreprocessorState); m_pPreprocessorState = NULL; } if (m_pfNoise != NULL) { delete []m_pfNoise; m_pfNoise = NULL; } m_bHasInit = false; }
void CSpeexEC::DoAEC(short* mic, short* ref, short* out) { if (!m_bHasInit) return;
speex_echo_cancel(m_pState, mic, ref, out, m_pfNoise); speex_preprocess(m_pPreprocessorState, (__int16 *)out, m_pfNoise);
}
可以看出,这个回声消除器类很简单,只要初始化一下就可以调用了。但是,要注意的是,传给回声消除器的两个声音信号,必须同步得非常的好,就是说,在B端,接收到A说的话以后,要把这些话音数据传给回声消除器做参考,然后再传给声卡,声卡再放出来,这有一段延时,这时,B再采集,然后传给回声消除器,与那个参考数据比较,从采集到的数据中把频域和参考数据相同的部分消除掉。如果传给消除器的两个信号同步得不好,即两个信号找不到频域相同的部分,就没有办法进行消除了。 测试程序:
#define NN 160 void main() { FILE* ref_fd, *mic_fd, *out_fd; short ref[NN], mic[NN], out[NN]; ref_fd = fopen ("ref.pcm", "rb"); //打开参考文件,即要消除的声音 mic_fd = fopen ("mic.pcm", "rb");//打开mic采集到的声音文件,包含回声在里面 out_fd = fopen ("echo.pcm", "wb");//消除了回声以后的文件
CSpeexEC ec; ec.Init();
while (fread(mic, 1, NN*2, mic_fd)) { fread(ref, 1, NN*2, ref_fd); ec.DoAEC(mic, ref, out); fwrite(out, 1, NN*2, out_fd); }
fclose(ref_fd); fclose(mic_fd); fclose(out_fd); }
  以上的程序是用文件来模拟回声和MIC,但在实时流中是大不一样的,在一般的VOIP软件中,接收对方的声音并传到声卡中播放是在一个线程中进行的,而采集本地的声音并传送到对方又是在另一个线程中进行的,而声学回声消除器在对采集到的声音进行回声消除的同时,还需要播放线程中的数据作为参考,而要同步这两个线程中的数据是非常困难的,因为稍稍有些不同步,声学回声消除器中的自适应滤波器就会发散,不但消除不了回声,还会破坏原始采集到的声音,使被破坏的声音难以分辨。我做过好多尝试,始终无法用软件来实现对这两个线程中的数据进行同步,导致实现失败,希望有经验的网友们一起分享一下这方面的经验。
原文地址:https://www.cnblogs.com/baiyu/p/2798243.html