在一般的数字楼宇对讲系统应用中,对讲双方需要进行实时的语音交流,而在室内或是楼道门E1都采用外置音箱放音的形式,这势必会产生回声H。21,即通话的一方说话后通过网络传到通话另一方的音箱进行播放,然后播放出来的声音又被另一方的传声器采集到并且通过网络传回自己。这时,回声的产生将会影响对讲的通话质量和用户对于楼宇对讲产品体验,而对于现在新兴的Android数字楼宇对讲系统。更是如此。目前Android数字楼宇对讲系统中的回声消除技术实现有以下3方面的难点和问题:
1)Android本地实时回声消除技术问题Android系统是便携式嵌入式系统,软硬件的计算资源相对有限,很多在Windows系统等PC机平台上应用很成熟的回声消除算法和技术到Android平台上可能都会出现一些适应性问题。
2)回声消除中音频采集、播放的延时问题因为Android不是实时操作系统,造成传声器的录音、音响的放音之间都有一定时延,而且这个时延每个Android设备可能都不一样,这样即便有很好的回声消除方法,由于不能将录音、放音对齐将会使得回声消除没有效果或是消去正常通话声音,这会进一步增加Android回声消除难度。
3)Android回声消除的平台移植性
目前,Android版本很多,如何使Android回声消除技术能应用于多样的终端上是一个现实问题。
为了解决以上问题,采用WebRTC的回声消除模块实现Android本地实时回声消除,并设计了多线程编程技术实现音频采集、播放的同步,解决了Android楼宇对讲系统的回声问题,最后利用JNI(Java Native Interface——JAVA本地调用)技术将Android楼宇对讲系统回声消除程序进行封装,便于不同Android平台的移植。
1 技术基础
目前,主流的实时通信软件中应对回声消除问题基本都采用了这两种回声消除模块:Speex[41和Global IPSound(GIPS)”1。Speex是很多开源即时通信软件都会使用的方案,其最大亮点也是完全开源的,而GlobalIP Sound(GIPS)则是瑞典Global IP Solutions公司所研制的回声消除技术,广泛应用与商业软件中。针对Speex和GIPS这两个回声消除模块,笔者采用了两种通信软件进行了实际网络语音测试:Skype(采用GIPS)、Speex(采用Speex),以证明哪种回声消除的效果更好。结果Skype的通话很好,基本没有回音,双方交流很顺畅,而相对Skype,使用Speex通话一直有残余回音,双方交流困难。由此可见,GIPS回声消除模块有更好的回声消除效果,但是一般GIPS回声消除模块都是商用,且费用不菲。
2010年Google高价收购了Global IP Solutions公司,并将其拥有的WebRTC(Web Real Time Communiea—tion)技术项目开源。WebRTC是于Web浏览器应用的实时音视频的通信技术,其功能非常强大,内容主要包含语音引擎模块(voice engine)、视频引擎模块(videoengine)、传输层模块(Transport)、会话管理模块(sessionmanagement),而在其语音引擎模块(voice engine)中就公开了GIPS回声处理技术(AEC),这为Android数字楼宇对讲系统的回声消除实现提供了很好的基础,并对研究Android数字楼宇对讲系统的回声消除处理问题具有重要意义。
2 Android数字楼宇系统回声消除技术设计
在引言中提到,Android数字楼宇对讲系统中回声消除技术的实现具有3个问题或者说是难点:一是An·droid本地实时回声消除技术问题,二是回声消除中音频采集、播放的延时问题,三是Android回声消除的平台移植性。
针对这三个问题,解决方案如下:
1)使用WebRTC中的GIPS回声处理技术实现Android数字楼宇对讲系统的本地实时回声消除;
2)采用多线程缓存技术,将音频采集、播放进行同步处理,以使GIPS回声处理技术能够应用并取得很好的回声消除效果。
3)利用JNI技术,将WebRTC中的GIPS核心C代码都编译成动态链接库以应用,可保证Android回声消除应用于不同版本的Android平台上。
3 Android数字楼宇系统回声消除技术实现
本部分内容主要针对上文的设计思路进行具体的实现。首先利用WebRTC中的GIPS回声处理技术实现本地回声消除,然后采用多线程态链接库便于使用。
3.1 WebRTC中的GILES本地回声消除
前文中介绍过WebRTC中语音引擎模块(voice en—gine)公开了GIPS回声处理技术(AEC),而另一方面WebRTC功能很强大,包括很多的模块,这意味着代码量也很大,这里能否将GIPS回声处理技术(AEC)提取出来单独使用呢?答案是肯定的。
WebRTC的代码结构布局清晰,在“webrtc\modules\audio_processing\aee”目录下可以找到几个用于回声处理GIPS的AEC源文件。然后主要查找每个AEC源文件所关联的WebRTC代码,就可找出回声处理模块所需要WebRTC相关的源代码文件和头文件,这样就可以将AEC从WebRTC中提取出来单独使用。为方便使用,将需要这些代码分成2个模块,通用音频处理模块webRTC_AUDIO和GIPS-AEC模块。WebRTC—AUDIO模块中包含AEC源文件运行所依赖的WebRTC音频处理相关源文件及头文件,而GIPS—AEC模块则是WebRTC中专门用于回声处理GIPS的AEC源文件。GIPS-AEC模块以WebRTC_AUDIO模块为基础,对回声进行处理。
在GIPS-AEC模块主要使用了5个函数完成回声消除:
1)创建回声消除函数
WebRtc—Word32 WebRtcAec—Create(void * aecInst);
函数会创建一个AEC数据结构,用于存放AEC过程中的变量和状态,并将这个结构的地址存在形参aecInst指向的void指针中。
2)初始化回声消除函数
WebRte_Word32 WebRtcAec_Init(void * aecInst,WebRtc_Word32 sampFreq,WebRtc_Word32 scSampFreq);
该函数可指定采样率来初始化aecInst所指向的AEC数据结构(如采样率sampFreq、scSampFreq可设置为8000)。
3)设置需要消去的参考音频数据函数
WebRtc_Word32 WebRtcAec_BufferFarend(void * aeclnst,
const WebRtc_Wordl6 * farend,
WebRtc—Wordl6 nrOfSamples);
其中,形参farend指向需要消去的参考音频数据块,形参nrOfSamples是需要消去烦人音频数据的采样点数量(一般取值为160或80)。
4)回声消除函数
WebRte_Word32 WebRtcAec—Process(void * aecInst,
const WebRtc_Wordl6 * nearend,
eonst WebRtc_Word 16 * nearendH,
WebRtc_Wordl6 * out,
WebRte Wordl6 * outH,
WebRtc_Wordl6 nrOiSamples,
WebRtc_Wordl6 mslnSndCardBuf,
WebRtc—Word32 skew):
该函数完成具体的回声消除操作。AEC程序回声消除的基本原理就是以音频播放的数据为参照,消除传声器风采集到音频中的已经播放内容。形参nearend就是指向本地所采样的音频数据指针,out是回声消除后输出数据指针;nrOfSamples是输入的nearend音频数据的采样点数量(一般取值为160或80);形参mslnSnd.CardBuf就是声卡实际输入和输出之间的时间差,即本地音频和消去参考音频之间的错位时间。
5)释放AEC回声消除函数
WebRtc_Word32 WebRtcAec_Free(void * aecInst);
该函数释放AEC数据结构。
完成这两个模块后,笔者于单机进行了本地回声消除的验证,具体过程如图1所示,首先准备2个音频文件,一个为带有原声和回声的音频文件,另一个为只有回声参考源的音频文件,然后将2个文件的音频数据分别导入笔者实现的回声消除模块,带有原声和回声的音频数据带入到nearend,只有回声参考源的音频数据带人到farend,通过回声消除处理后,生成的out数据并保存为文件,即为只有原声数据的音频文件。通过该实验验证,得到了非常满意的回声消除效果,原声清晰且无回声。
3.2音频采集、播放的同步
因为Android不是实时操作系统,造成传声器的录音、音响的放音之间都有一定时延,而且这个时延每个Android设备可能都不一样,这样即便有很好的回声消除方法,但有很多人却得不到理想的回声消除效果。
回声消除AEC的基本原理就是以音频播放的数据为参照,消除传声器风采集到音频中的已经播放内容。如果音频播放和音频采集之间的时间差过大,即做回声消除时参照的音频播放数据和音频采集数据之间有过大的错位,那么必然无法正确地消除回声。
如果想要在网络音频通信中成功消除回声,必须实现音频采集、播放的同步。为此采用4个程序线程和3个队列进行具体的实现(如图2所示)。4个线程分别是ReadThread、WriteThread、InThread和AecOutThread。线程之间的数据交换则是使用了3个队列来完成,这3个队列分别是接收队列、采集队列和播放队列。
在Android应用程序中,使用AudioRecord类进行音频采集,使用AudiorTraek进行远端音频播放。为了保证时间上的连续性和时间差,使用了两个单独线程ReadThread和WriteThread来分别进行音频采集和音频播放,且这2个线程同时进行的。
InThread完成网络音频数据的接收。而AecOut-Thread则完成回声消除操作,并将处理后的数据发送给通话另一方。
线程之间的数据交换使用队列来完成,接收队列存储InThread通过网络接收到对方发来的音频数据,WriteThread从接收队列中读出对方发来音频数据并播放。WriteThread播放过的音频数据会放入播放队列存储,等待AecOutThread线程使用。采集队列用于存储ReadThread通过传声器风采集到的音频数据,供AecOutThread使用。AecOutThread同时提取采集队列和播放队列中音频数据进行回声消除处理,并把处理后结果通过发送出去。这样就可以保证音频采集、播放的同步性,并达到满意的回声消除效果。
3.3 WebRTC中的GIPS回声消除代码封装
为了方便使用,通过JNI技术。将GIPS核心C代码封装成动态链接库。本部分的主要工作就是将使用的通用音频处理模块WebRTC_AUDIO和GIPS—AEC模块进行封装,得到Android下的动态链接库文件。具体就是需要编写WebRTC_AUDIO和GIPS-AEC的Android.mk文件以控制.SO文件的编译过程。
通用音频处理模块WebRTC—AUDIO和GIPS—AEC模块在Android下编译基本的步骤是:
1)编写WebRTC_AUDIO的Android.mk文件,将Osip中的所有源代码和头文件按照要求加进去,然后利用NDK(Native Development Kit——本地开发工具包)在Cygwin(linux平台)下直接编译WebRTC_AUDIO源代码和Android.mk生成libWebRTC_AUDIO.SO动态链接库文件。
2)然后完成GIPS-AEC的Android.mk文件,基本过程与上面类似,但是GIPS-AEC是基于WebRTC_AUDIO的,所以必须将生成的WebRTC_AUDIO.SO导入到自己的Android.mk的文件中,最后编译生成libGIPS_AEC.SO文件。
下面以webRTC_AUDIO的Android.mk简单介绍一下.mk的编写,其基本内容如下:
LOCAL_PATH:=$(call my-dir) 指定本地LOCAL_PATH变量位置
include $(CLEAR_VARS) 编译模块开始
LOCAL_SRC_FILES:=...此处加入需要编译的C源文件名称
LOCAL_CFLAGS+= -DWEBRTC_ANDROID\-DWEBRTC_ARCH_ARM\
..... 定义宏
LOCAL—C—INCLUDES:= .... 加入所需要包含的头文件路径
LOCAL_LDLIBS+= -ldl 调用第三方库
LOCAL_MODULE:=WebRTC_audio 指定生成.SO文件名称
include $(BUILD_SHARED—LIBRARY) 指定生成.SO文件,编译模块结束
通过JNI技术将独立出来的通用音频处理模块WebRTC_AUDIO和GIPS—AEC模块核心C代码封装成libWebRTC_AUDIO.SO和libGIPS-AEC.SO动态链接库文件,便于应用于不同的Android版本平台上。
4 总结
Android回声消除技术直接影响着Android数字楼宇对讲系统对讲的通话质量及用户产品体验。本文的目的是实现一种便于使用的Android楼宇对讲的Android回声消除技术。采用WebRTC的回声消除模块实现Android本地实时回声消除,并设计了多线程编程技术实现音频采集、播放的同步,解决了Android楼宇对讲系统的回声问题,最后利用JNI技术将我们的Android楼宇对讲系统回声消除程序进行封装,便于不同Android平台的移植应用。本文中所研究的Android回声消除技术不仅可应用于Android数字楼宇对讲系统,对于其他Android数字通信系统也同样适用。