谁能帮我给一段手机录音降噪处理软件下,并把声音放大清晰点?很重要,万分感谢!

6433人阅读
不推荐单独编译&WebRTC&中的各个模块出来使用。
昨天有幸在&Google&论坛里询问到&AECM&模块的延迟计算一事,Project
member&说捣腾这个延迟实际上对&AECM&的效果没有帮助,这个延迟值仅在&AECM&启动时加快内置延迟估算器的收敛,如果更新的延迟有误,甚至会使&AECM&内置的延迟估算器出现错误的偏移,他建议我使用一个理论上的定值,Chrome&中他们用了&100ms。我之前也在&AECM&里看到了它对内置的&delay_estimator&二进制延迟估算器有很大的依赖,近端与远端数据匹配与否,几乎全部仰仗这个延迟估算器的结果。因此,对于&AECM&的使用,是否还需要花时间去计算这个系统延迟,bill&不再置评,个中效果大家自己实践和把握。
& &其次,AECM&产生的唧唧声&Project
member&澄清这不是&bug,而是&AECM&算法本来就有此表现,属正常现象。
& &本文已有一年之久,随着自己在学习过程中认识的加深,以及期间和各位在文后的讨论,担心后来者照着这一年前的文章走弯路,bill&觉得有必要对文章做一个更新,点出自己走的弯路,以免误导后来者。
& &1. 自己最开始是把&AECM、NS、VAD、AGC&各个模块单独提取出来使用,现在看来实属麻烦,且效果也不甚理想。如果大家的项目没有特殊的要求,大可将整个语音引擎&VoiceEngine&编译出来使用。就我个人而言,目前的解决方案是独立编译使用音频处理单元&AudioProcessingModule,因为&APM&是一个纯净的音频处理单元,其接口仅与音频处理有关,APM的使用加上上层代码的优化,可以保证基本的通话效果(离完美还很远),回声基本是没有的。主要会存在两个问题,一是AECM出来的效果会有唧唧声,这个声音可以通过对延迟计算的不断优化而得到改善,最终可以做到说几句话之后有1~2次唧唧声。二是通话过程中声音会忽大忽小,目前我是怀疑由AECM的double
talk处理引起的,具体的还要自己去倒腾。
& &2. 关于回声消除滤波器延迟的计算,之前自己一直认为只要这个延迟计算准确,就能得到理想的回声消除效果,现在发现这个想法太幼稚,一是AECM算法本身有一定局限性,二是Android上的采集延迟没有系统API支持,很难计算准确,而播放端的API又不能保证其准确性。目前我的能力只能做到尽量优化上层的延迟计算,尽量减少由Android音频API对延迟造成的影响。
& &3. 在&Android&上层优化计算系统音频延迟的代码达到一定瓶颈后,可以将优化目标转向&1)AECM&算法。&2)优化AEC(PC)(使其能在手机上正常运行,目前AEC-PC默认滤波器长度为12块,每块64个点,(12*64=768采样)即AEC-PC仅能处理48ms的单声道16kHz延迟的数据,而Android的音频系统延迟大多在100ms以上,因此既要增加AEC-PC滤波器长度又要保证其运行效率是优化的重点)&3)其他模块的优化(比如抖动缓冲区等)。
& &4. 文后的源码列表已经过时,由于我目前不再支持单独编译这些模块,恕我不再更新该列表,如确有独立编译需求的,可自行在WebRTC项目对应目录中找到需要的文件。
&最近一直在捣腾如何在android和iOS上使用Google的——一个无疑大力推动了互联网即时通信以及VoIP发展的开源项目。(elesos注:连谷歌都访问不了的国家,活该落后!)
&WebRTC提供一套音频处理引擎VOE,但VOE在&android&和&iOS&上的整体编译一直是一个比较繁琐且恼火的问题,于是单独提取了VOE中的NS(Noise
Suppression&噪声抑制)、VAD(Voice
Activity Detection&静音检测)、AECM(Acoustic
Echo Canceller for Mobile&声学回声消除)以及&AGC(Auto
Gain Control&自动增益控制)等模块进行编译并捣鼓其使用方法。
&经过自己两月有余的捣腾和测试,终于在&android&和&iOS&上成功编译出各模块并在项目中使用了NS/VAD/AECM三大模块,效果比较不错。
& &回过头来看看,这几大模块的编译其实非常简单,不过两月前的自己也着实为这个花了一番力气。
& &由于几大模块的编译方式相同,故本文仅以&NS&模块为例,其余模块请读者自行摸索和实验。
1 - 下载&google
WebRTC&源码
& &WebRTC目前的开发版主线版本已经到了&r4152
- 3.32,但这几大模块并未有大的修改,故本文依旧按bill当时的版本&3.31&进行讲解,请自行使用SVN同步以下目录(至于同步的方法,请自行google):
& & 可访问
2&- 提取WebRTC - NS模块代码
& &同步源码后,进入目录&\webrtc\modules\audio_processing\ns&,将NS模块的源码拷贝出来,下面是单独编译NS时的参考源码列表(部分头文件在WebRTC项目其他目录下,请自行搜索提取):
& & & & & & & & & & & & & & & & & & & &defines.h
& & & & & & & & & & & & & & & & & & & &signal_procession_library.h
& & & & & & & & & & & & & & & & & & & &spl_inl.h
& & & & & & & & & & & & & & & & & & & &typdefs.h
& & & & & & & & & & & & & & & & & & & &windows_private.h
& & & & & & & & & & & & & & & & & & & &fft4g.h / fft4g.c
& & & & & & & & & & & & & & & & & & & &noise_suppression.h / noise_suppression/c
& & & & & & & & & & & & & & & & & & & &ns_core.h / ns_core.c
&除了上述WebRTC源码外,如果要在android的Java代码中使用,还需自行编写JNI包装文件:
ns_jni_wrapper.c(此为自定义的&jni&包装文件,详情请见&)
ADDED(billhoo - )&
鉴于有朋友询问JNI
Wrapper的编写,下面提供NS模块create以及initialize函数(这两个函数足以说明问题)的wrapper源码及注释,希望对大家有所帮助。更详细的编写步骤请参考
Oracle官方文档&或&或&。
WebRtcNs_Create&包装函数及注释
&*&Summary
&*&types:
&*&&&NSinst_t&:&the&type&of&noise&suppression&instance&structure.
&*&&&NsHandle&:&actually&the&same&type&of&NSinst_t,&defined&in
&*&&&&&&&&&&&&&&&noise_suppression.h&&as&a&empty&struct&type&named
&*&&&&&&&&&&&&&&&NsHandleT&.
&*&&&Note:
&*&&&&1.You&have&no&need&to&pass&env&and&jclazz&to&these&functions,
&*&&&&&&cus'&JVM&will&does&it&for&you.
&*&&&&2.We&only&support&10ms&frames,&that&means&you&can&only&input&320
&*&&&&&&Bytes&a&time.
&*&This&function&wraps&the&&WebRtcNs_Create&&function&in&&noise_suppression.c&.
&*&&&&&&&&none.
&*&Output:
&*&&&&&&&&the&handler&of&created&noise&suppression&instance.
&*&Return&value:
&*&&&&&&&&-1&:&error&occurs.
&*&&&&&&&&other&value&:&available&handler&of&created&NS&instance.
&*&@author&billhoo
&*&@version&1.0&
JNIEXPORT&jint&JNICALL
Java_你的类限定名_createNSInstance(JNIEnv&*env,
&&&&&&&&jclass&jclazz)&{
&&&&NsHandle&*hNS&=&NULL;&//create&a&pointer&to&NsHandle&on&native&stack.
&&&&if&(WebRtcNs_Create(&hNS)&==&-1)&{&//allocate&dynamic&memory&on&native&heap&for&NS&instance&pointed&by&hNS.
&&&&&&&&return&-1;&&//error&occurs
&&&&}&else&{
&&&&&&&&return&((int)&(NSinst_t&*)&hNS);&//returns&the&address&of&NS&instance&on&native&heap.
WebRtcNs_Initiate&包装函数及注释
&*&This&function&wraps&the&&WebRtcNs_Init&&function&in
&*&&noise_suppression.c&.
&*&Initializes&a&NS&instance&and&has&to&be&called&before&any&other
&*&processing&is&made.
&*&&&&&&&&-&nsHandler&&&-&Handler&of&NS&instance&that&should&be
&*&&&&&&&&&&&&&&&&&&&&&&&&initialized.
&*&&&&&&&&-&sf&&&&&&&&&&-&sampling&frequency,&only&,&32000
&*&&&&&&&&&&&&&&&&&&&&&&&&are&available.
&*&Output:
&*&&&&&&&&&nsHandler&&-&the&handler&of&initialized&instance.
&*&Return&value:
&*&&&&&&&&&0&&&&&&&&&&&&&&&&-&OK
&*&&&&&&&&&-1&&&&&&&&&&&&&&&-&Error
&*&@author&billhoo
&*&@version&1.0&
JNIEXPORT&jint&JNICALL
Java_你的类限定名_initiateNSInstance(JNIEnv&*env,
&&&&&&&&jclass&jclazz,&jint&nsHandler,&jlong&sf)&{
&&&&NsHandle&*hNS&=&(NsHandle*)&nsH
&&&&return&WebRtcNs_Init(hNS,&sf);
3&- 编译WebRTC - NS模块
& &此步请参照&将刚才提取的NS代码添加进eclipse工程进行编译即可。以下为NS模块的Android.mk文件:
LOCAL_PATH&:=&$(call&my-dir)
include&$(CLEAR_VARS)
LOCAL_MODULE&&&&:=&webrtc_ns
LOCAL_SRC_FILES&:=&\
&&&&&&&&noise_suppression.c&\
&&&&&&&&ns_core.c&\
&&&&&&&&fft4g.c&\
&&&&&&&&ns_jni_wrapper.c
include&$(BUILD_SHARED_LIBRARY)
&编译完成后,将项目中的&webrtc_ns.so&动态库拷贝出来以备后续使用。
加载编译好的NS模块动态库
& &接下来只需要按照&&的描述在&android&的JAVA代码中使用刚才编译好的&webrtc_ns.so&动态库便大功告成。
5&- 几大模块的使用及注意事项
& &前四步已经完成了几大音频处理模块在android上的单独编译过程,并分别生成了&webrtc_ns.so、webrtc_vad.so、webrtc_aecm.so&以及&webrtc_agc.so&四个动态库,下面bill简要介绍对NS、VAD以及AECM三个库的函数使用方法及注意事项:
-&NS库函数的使用及注意事项
& &这个很简单,参照&noise_suppression.h&头文件中对各API的描述即可,首先使用&WebRtcNs_Create&创建NS实体,然后&WebRtcNs_Init&初始化该实体,WebRtcNs_set_policy&设置噪声抑制的级别(bill使用的是最高级别
2,效果比较理想),设置完成后便可调用&WebRtcNs_Process循环对10ms(8000Hz、16000Hz)音频帧进行NS处理,注意最后别忘了调用&WebRtcNs_Free&将NS实体销毁。
-&VAD库函数的使用及注意事项
VAD的使用和NS区别不大,唯一需要注意的是VAD仅仅只是检测,返回结果1表示VAD检测此帧为活动帧,0表示此帧为静音帧,至于判断为静音后该进行何种处理,就和你自己的项目相关了。
-&AECM库函数的使用及注意事项
AECM实体的创建、初始化和销毁工作与上述相同,之后需要在远端和近端分别调用WebRtcAecm_BufferFarend
& (注:farend远端)以及&WebRtcAecm_Process,对于AECM的使用,需要注意的重点在于Process函数的参数msInSndCardBuf,该参数在audio_procession.h头文件中以名为delay的变量呈现,该延迟的计算确为一难点(对于单独使用AECM模块来说),不过只要严格按照delay的描述进行操作即可。
& &其他几大模块单独编译时需要的源文件列表(所有依赖头文件略,请自行根据报错添加):
- VAD&模块源文件列表
& & & &注意:VAD的编译需要宏&WEBRTC_POSIX&的支持,而该宏是否有实现,由&WEBRTC_ANDROID&等宏是否被定义决定,若你在编译时提示&once&函数未定义等错误,
请自行添加对&WEBRTC_ANDROID宏的定义。
& & & &webrtc_vad.c
& & & &vad_core.c
& & & &vad_filterbank.c
& & & &vad_gmm.c
& & & &vad_sp.c
& & & &real_fft.c
& & & &division_operations.c
& & & &complex_bit_reverse.c
& & & &cross_correlation.c
& & & &complex_fft.c
& & & &downsample_fast.c
& & & &vector_scaling_operations.c
& & & &get_scaling_square.c
& & & &energy.c
& & & &min_max_operations.c
& & & &spl_init.c
- AECM&模块源文件列表
& & & &randomization_functions.c
& & & &spl_sqrt_floor.c
& & & &division_operations.c
& & & &min_max_operations.c
& & & &ring_buffer.c
& & & &delay_estimator.c
& & & &delay_estimator_wrapper.c
& & & &complex_bit_reverse.c
& & & &complex_fft.c
& & & &aecm_core.c
& & & &echo_control_mobile.c
- AGC&模块源文件列表
& & & &spl_sqrt.c
& & & &copy_set_operations.c
& & & &division_operations.c
& & & &dot_product_with_scale.c
& & & &resample_by_2.c
& & & &analog_agc.c
& & & &digital_agc.c
下面是elesos从评论中摘抄的部分信息:
WebRtcAecm_Process
nearendNoisy 表示带有噪声的buf
nearendClean 表示经过降噪处理的 buf
建议首先使用NS将采集到的buf降噪,然后将原buf传给nearendNoisy ,降噪后的buf传给
nearendClean,如果不降噪,直接将buf传给nearendNoisy ,nearendClean置为NULL即可。
NS每次处理10ms的数据
都處理10ms數據(8000HZ的sample是80, 16000HZ的sample是160)、謝謝!
先消回声再降噪效果比先降噪再消回声好。建议参考WebRTC AudioPreprocessing 模块里面的 ProcessStream 的实现顺序。
WebRtcAecm_Init( &aecm , 8000 );
While ( aecProcessing )
& & WebRtcAecm_BufferFarend( speakerBuffer );
& & WebRtcAecm_Process( aecm , micBuffer , NULL , aecBuffer , 160 , 200 );
上面的200ms最好不要用常量,需要this delay is always changes, you should estimate it every& &
1 second or shorter.
在audio_processing.h中有描述
& // Sets the |delay| in ms between AnalyzeReverseStream() receiving a far-end
& // frame and ProcessStream() receiving a near-end frame containing the
& // corresponding echo. On the client-side this can be expressed as
& // & delay = (t_render - t_analyze) + (t_process - t_capture)
& // where,
& // & - t_analyze is the time a frame is passed to AnalyzeReverseStream() and
& // & & t_render is the time the first sample of the same frame is rendered by
& // & & the audio hardware.
& // & - t_capture is the time the first sample of a frame is captured by the
& // & & audio hardware and&t_pull&is the time the same frame is passed to
& // & & ProcessStream().
& virtual int set_stream_delay_ms(int delay) = 0;
延迟你只能根据自己的buffer实现进行计算。
四个时间点均在调用之前得到即可。
的第一个采样被播放的时间,总感觉取不到,我们能取到的只有播放完整个语音帧的时间点吧:我们确
实不能直接得到第一个采样点的时间,但你可以根据自己上层的 buffer 大小以及底层的硬件 buffer
大小估算出缓冲区中总共缓冲了多少帧,这样一来便可计算出第一个采样点的时间。
请教一下你在android上层设计buffer并计算出delay时间的解决办法。
尽量不要在java层做AECM,如果非要在java层做,delay的计算只有根据你自己的buffer设计来,总体
思路就是系统底层硬件延迟 + 你的buffer延迟。
每发送一帧就更新延迟值。
可以采用OpenSE等JNI层的采集库,而不是android上层的AudioRecord以及AudioTrack。
目前采用一半底层buffer大小作为采集的固定延迟。
T1(系统的播放延迟) = 帧A被硬件播放出来的时刻 - 帧A被放进 farend 的时刻;
T2(系统的采集延迟) = 帧B被放进 process 的时刻 - 帧B被硬件采集的时刻;
total delay = T1 + T2;
这个延迟的计算方法可以参考WebRTC主线版本目录 webrtc\modules\audio_device\android\java\src\org\webrtc\voiceengine\
下的WebRTCAudioTrack和WebRTCAudioRecord
对于AECM的测试,首先可以使用同一个PCM文件,分别放入FAREND和PROCESS,如果出来的结果接近全零
,则验证你提取的AECM模块工作正常。
在验证AECM模块能够正常工作的前提下,你需要两台设备,每台设备两个线程,线程一用来采集和
PROCESS,线程二用来播放和FAREND。
“从手机SD卡中读取一个pcm文件,送入到扬声器,同时调用WebRtcAecm_BufferFarend(), 然后读取麦
克风采集到的数据,调用Process。再将out写入到aec.pcm文件。分析这个aec.pcm文件。”
远端数据就是网络传过来的数据,近端就是本机采集到的数据。
farend是远端数据,即VoIP通信中接收到的对端的、即将被本地扬声器播放的音频A。
nearend是本地数据,即刚刚被本地麦克风采集到的音频B,而音频B可能包含上述音频A(麦克风采集到了扬声器播放的A),于是 A和B 一起发送给远端,致使远端又听到了自己刚才发送的A,也就产生了所谓的回声。
发现你这里面根本不存在“原声”,何来原声被消除呢?
你使用固定文件作为声音来源,我们假设输入PCM数据为“A”,那么你送到扬声器的声音就为“A”,
而麦克风采集到的声音为扬声器的“A”再加上背景噪声“B”(现在的B才是实际意义上的“原声”,
假设你没说话),AECM的处理结果就是从“A+B”中找到被扬声器播放出去的“A”并进行消除,留下
了“B”,而背景噪声也许比较小,结果也就仍然接近全零了,绕了一圈,你做的这个流程和我用同一
个PCM文件分别放入farend和process是一个道理。
应该在运行时说话,而不是放音乐。
VAD检测出是静音,你可以采取以下两种方式:1.不发送这一段静音的音频 2.将这一段VAD认为静音的
音频全部置零然后发送。
[3楼]&&&&&&&
回复 Bill_Hoo:
谢谢你,bill。主要是我对c不熟悉,好久没用了。现在有个需求就是需要拿到pcm音频数据然后进行降噪,可是不知到怎么使用里面的方法
[4楼]&&&&&&[匿名]WebRtc_Aecm&
BillHoo你好, 我是从StackOverflow追到这里来的。有一个问题不知道楼主有没有遇到过: WebRtcAecm_Process在去掉回声的同时把与回声重叠的那部分的原声也去掉了。
还有一个问题啊,麦克风采集的PCM的buffer是作为WebRtcAecm_Process的第二个参数吗?不胜感激。
[5楼]&&&&&&&
回复 WebRtc_Aecm:
你好,很高兴你能追到这里来 1)我没有弄懂你说的回声和原声重叠是什么意思。消回声把原声消掉了,这种情况我仅在回环路径测试时遇到过,两台设备进行测试不会有此问题。&
2)process函数原型如下 int32_t WebRtcAecm_Process(void* aecmInst,
& & & & & & & & & const int16_t* nearendNoisy,
& & & & & & & & & const int16_t* nearendClean,
& & & & & & & & & int16_t* out,
& & & & & & & & & int16_t nrOfSamples,
& & & & & & & & & int16_t msInSndCardBuf);
其中 nearendNoisy 表示带有噪声的buf,nearendClean 表示经过降噪处理的 buf,建议首先使用NS将采集到的buf降噪,然后将原buf传给nearendNoisy&,降噪后的buf传给
nearendClean,如果不降噪,直接将buf传给nearendNoisy ,nearendClean置为NULL即可。
[6楼]&&&&&&[匿名]WebRtc_Aecm&
回复 Bill_Hoo:
多谢你的回复!
回声和原声重叠的意思是回声和原声同一时刻进入到麦克风,我将它称之为“重叠”
回环路径测试是怎样的场景和配置呢?
我现在的测试环境是这样搭的: 从手机SD卡中以80字节为单位顺序读取一个pcm文件,送入到扬声器,同时调用WebRtcAecm_BufferFarend(), 然后读取麦克风采集到的数据的80字节,调用Process。再将out写入到aec.pcm文件。分析这个aec.pcm文件时,发现回声是被消除了,但是与回声处于同一时刻的原声也被消除了。
我这种测试思路有问题吗?
还有一个问题,降噪与否对回声抑制的效果影响大吗?
[7楼]&&&&&&&
回复 WebRtc_Aecm:
1.首先,我在stackoverflow里看到你调用时传的是8000Hz的采样率,也就是说你每次应该传入80个采样点,(编者注:10ms数据需要传80,1000ms即1s为8000,所以10ms对应80*2=160字节)一个采样点是2个字节,你应该采集160字节/次才对。
2.对于AECM的测试,首先可以使用同一个PCM文件,分别放入FAREND和PROCESS,如果出来的结果接近全零,则验证你提取的AECM模块工作正常。
3.在验证AECM模块能够正常工作的前提下,你需要两台设备,每台设备两个线程,线程一用来采集和PROCESS,线程二用来播放和FAREND。
4.降噪与否对回声消除的效果:我的实验结果为:先消回声再降噪效果比先降噪再消回声好。
[8楼]&&&&&&&
回复 WebRtc_Aecm:
还有个问题看掉了,我说的回环路径就是 127.0.0.1 的本机测试。
[9楼]&&&&&&[匿名]WebRtcAecm&
回复 Bill_Hoo:
对于第1点,我也测试过传160字节,效果和80字节是差不多的。
对于第2点,结果会接近全零,但是在本地也有声音进入麦克风的情况下,与回声重叠的那部分原声也会被消掉(预期应该是不会被消掉的,这和你的回环路径测试不一样,因为进入麦克风的&Normal Voice&没有再次进入扬声器,也就算不上是回声,而我猜你的回环路径测试是会再次被扬声器播放出来吧?)。
这就是我遇到的最主要问题。AECM是可以工作的,但是结果没有达到预期。我看了你的StackOverflow的回复,这一点应该是和时延没有关系的。
[10楼]&&&&&&&
回复 WebRtcAecm:
你好,我仔细看了下你的思路
“从手机SD卡中读取一个pcm文件,送入到扬声器,同时调用WebRtcAecm_BufferFarend(), 然后读取麦克风采集到的数据,调用Process。再将out写入到aec.pcm文件。分析这个aec.pcm文件。”
发现你这里面根本不存在“原声”,何来原声被消除呢?
你使用固定文件作为声音来源,我们假设输入PCM数据为“A”,那么你送到扬声器的声音就为“A”,而麦克风采集到的声音为扬声器的“A”再加上背景噪声“B”(现在的B才是实际意义上的“原声”,假设你没说话),AECM的处理结果就是从“A+B”中找到被扬声器播放出去的“A”并进行消除,留下了“B”,而背景噪声也许比较小,结果也就仍然接近全零了,绕了一圈,你做的这个流程和我用同一个PCM文件分别放入farend和process是一个道理。
如果需要做回声测试,你需要两台设备,如我上一个回复所说。
希望对你有所帮助。
[11楼]&&&&&&[匿名]WebRtcAecm&
回复 Bill_Hoo:
是啊是啊,就是你说的这个意思。唯一不同的是我放音乐了,也就是说“B”里面有音乐,现在这段音乐与A重叠的部分就被消掉了 :(
[12楼]&&&&&&&
回复 WebRtcAecm:
也就是说你的问题不在AECM这个模块上,而是你的测试思路。
你的问题就在于 —— 你的测试方法测出来本来就应该是这个结果,而你的预期却错误地认为会是另外一个结果。
搞清什么是“原声”,什么是“回声”就OK了。
Elesos.com注:他的音乐其实还是送到扬声器的声音A吧,是回声。是需要消除的。
[13楼]&&&&&&[匿名]WebRtcAecm&
回复 Bill_Hoo:
预期:&AECM的处理结果就是从“A+B”中找到被扬声器播放出去的“A”并进行消除,留下了“B”&
现在的结果是: B没有被完全留下,有一部分被消掉了。你假设我没有说话,实际我放了音乐并且被麦克风采集到了啊,这个音乐不是从扬声器出来的,这不算回声吗?我觉得这个音乐应该是被保留的吧?现在被部分消掉了。
[14楼]&&&&&&&
音乐是连续的音源,和人声是有很大区别的,连续乐音中很可能某一段的数据被认为是和A中的数据匹配从而导致被消除,如果你非要这样测,应该在运行时说话,而不是放音乐。
[15楼]&&&&&&&
回复 WebRtcAecm:
可以参考一下&
回复 Bill_Hoo:
多谢你的回复 :)
最后一个问题,那个时延是怎么计算的? 我看了你在StackOverflow上的发言,根据AudioProcessing.h的描述来计算,但是我的英文真是烂,那段话看了好久都没有看懂...
Sets the |delay| in ms between AnalyzeReverseStream() receiving a far-end frame and ProcessStream() receiving a near-end frame containing the corresponding echo. On the client-side this can be expressed as delay = (t_render - t_analyze) + (t_process - t_capture)
- t_analyze is the time a frame is passed to AnalyzeReverseStream() and
& t_render is the time the first sample of the same frame is rendered by
& the audio hardware.
- t_capture is the time the first sample of a frame is captured by the
& audio hardware and t_pull is the time the same frame is passed to
& ProcessStream().
1). 你是根据这个公式来计算的吗?
2). 这4个值都是什么意思啊?这段话我看了很多次了,总是看不懂。
[17楼]&&&&&&&
回复 WebRtcAecm:
你好,是根据这个来计算,而且需要严格根据这个的描述来
t_analyze 表示你对音频帧A调用 farend 的时刻,t_render 表示[硬件]真正播放出帧A的时刻。
t_capture 表示[硬件]采集到音频帧B(注意跟A没关系了)的时刻,t_pull 表示帧B被传入 Process的时刻。
其实就是计算系统硬件buffer + 软件 buffer 的总延迟。
这个延迟在android系统中比较大,有100~200左右不等,需要你根据自己的buffer进行计算。计算方法就跟你android上层的buffer设计相关了。
[21楼]&&&&&&[匿名]cstriker1407&
问下楼主,
我参照楼主的做法,第二步中AECM已经可以了。
2.对于AECM的测试,首先可以使用同一个PCM文件,分别放入FAREND和PROCESS,如果出来的结果接近全零,则验证你提取的AECM模块工作正常。
现在我的项目遇到的问题是,本机测试是可以的,但是双机通讯下,会有非常大的噪声,是不是AECM的msInSndCardBuf的参数错了。这个到底该怎么计算呢?
顺便问个小白的问题,在双机通信中,我认为从网络上接收的音频为远端音频,用WebRtcAecm_BufferFarend,本地录音的音频为近端,用process。这个没错吧。还有,我并没有用NS,AGC和其他模块,自用了AECM。这样可以吗?请楼主点播下,多谢。我的QQ:
[22楼]&&&&&&&
回复 cstriker1407:
1.非常大的噪声:你确定那是噪声而不是啸叫么? 我这边AECM局域网通信没有你说的很大的噪声,只是之前由于delay计算错误会产生很大的啸叫。delay的计算公式在audio_procession.h里面说的很清楚,那个延迟你只能根据自己的buffer实现进行计算。
2.远端数据就是网络传过来的数据,近端就是本机采集到的数据。
3.只用AECM模块是可以的。
[23楼]&&&&&&[匿名]51CTO游客&
& 我现在在做回音消除这块,但是不能准备计算delay的时间,虽然audio_precession.h头文件中的公式说得很清楚,但是对于android平台的java层始终不能很好的计算出delay的时间,所以想请教一下你在android上层设计buffer并计算出delay时间的解决办法。
[24楼]&&&&&&[匿名]cstriker1407&
回复 Bill_Hoo:
多谢楼主回复,问题(注:双机通讯下,会有非常大的噪声)我已经发现并解决了。不是webrtc的问题。
[25楼]&&&&&&&
回复 51CTO游客:
你好,如果可能,尽量不要在java层做AECM,java到android底层中间还夹着jni,据说还有一个AudioFlinger的延迟,这个我也没有深入,所以不敢乱讲。如果你的项目不是必须在java层做音频的采集和播放,那么AECM还是放在底层做的好。
如果非要在java层做,delay的计算只有根据你自己的buffer设计来,这个我没办法帮上忙,总体思路就是系统底层硬件延迟 + 你的buffer延迟。
[26楼]&&&&&&&
您好 我也在做webrtc的AEC 请问AudioRecord和AudioTrack如何的阻塞时间如何计算
long timestamp = System.nanoTime();
mAudioTrack.write(data, 0, data.length);
long renderTime = System.nanoTime() -
native_set_audioRenderBlockTime(renderTime);
请问这样做能得到准确的阻塞时间嘛
[27楼]&&&&&&&
请问楼上,AECM、VAD、AGC三个模块使用的顺序是怎样?如果VAD检测出是静音模块,那么AECM该如何调用啊?
[28楼]&&&&&&&
回复 hsfhzkd:
VAD检测出是静音,你可以采取以下两种方式:1.不发送这一段静音的音频 2.将这一段VAD认为静音的音频全部置零然后发送。
[29楼]&&&&&&&
你好,你这样做可以得到 write 的阻塞时间,但这个时间并不是 renderTime(如果你的这个变量表示渲染延迟的话)。
[30楼]&&&&&&&
回复 Bill_Hoo:
您好 请问渲染时间是一个固定的延迟吗,是否跟audioflinger的lantency有关。
[31楼]&&&&&&&
据我的实验结果,该延迟并不是固定的,需要周期性更新。我当时看源码时,AudioFlinger层的latency没有办法在上层获取,所以没有考虑这个。不过不知道现在4.1、4.2的源码发生了怎样的变化,是否有类似iOS的直接获取底层硬件延迟的接口我也不清楚。
[32楼]&&&&&&&
回复 Bill_Hoo:
现在aecm已经可以工作,但是最后的效果始终有嘶嘶的杂音,不知道您最终的效果如何~
[33楼]&&&&&&&
单独提取的AECM模块达不到完美的效果,但也不会始终存在嘶嘶声。也许你可以配合NS和VAD模块一起使用。
[34楼]&&&&&&[匿名]51CTO游客&
你好Bill Hoo,我对NS 模块的 WebRtcNs_Process 这个函数有点疑问。int&WebRtcNs_Process(NsHandle*
NS_inst, short* spframe, short* spframe_H,short* outframe, short* outframe_H)。 最后面四个参数 是指什么?我不太懂. noise_suppresion.h 里面说 buffer for L band, buffer for H band。&L band 和 H band 是什么??
[35楼]&&&&&&&
回复 51CTO游客:
可以参考下 http://what-when-how.com/voip/wideband-voice-voip/。
以及维基 http://en.wikipedia.org/wiki/H_band
L band,&&is
the 1 to 2&&range
[36楼]&&&&&&[匿名]51CTO游客&
回复 Bill_Hoo:
多谢,楼主。我的audio input format 是
PCM signed (wav file format)
16 bit encoding&
8000 Hz sample rate
Mono (1 channel)
Big endian format
但是参考资料都是说把16khz 的audio&用qmf 分成2个8khz的audio。 那我要怎么去用 WebRtcNs_Process 这个函数呢??只把audio
放进spframe, buffer for L band 么,不管buffer for H band?
[37楼]&&&&&&&
回复 51CTO游客:
早上好, 8KHz sample 直接入 L band 就OK了,我目前最高用到 16KHz,均只入的L band,至于 H band&对于话音的噪声消除个人觉得应该没有用处。 不过对于 32000 Hz 的sample就说不好了,一般单声道的应用可能都不会触及到
Hband,如果你发现在某个环境下会用到 H Band,还请回来告知一声哦。
[38楼]&&&&&&[匿名]51CTO游客&
Bill_Hoo你好,我又来了。之前一个月在做voip的其他功能,现在又开始做AEC了。现在遇到的问题是怎么计算系统硬件buffer的延迟,这个Android有什么接口吗?
[39楼]&&&&&&&
回复 51CTO游客:
你好,iOS有API直接提供硬件层的采集和播放延迟,但据我的实践和了解,android目前并没有提供直接的硬件延迟。因此你需要根据自己的上层工作buffer对底层buffer进行估算,从而得到一个较为精确的硬件延迟。但该延迟始终不精确,因为经过了几处buffer的缓冲。
如果要更精确,可以采用OpenSE等JNI层的采集库,而不是android上层的AudioRecord以及AudioTrack。不知道这样回答是否解决了你的问题。
[40楼]&&&&&&[匿名]51CTO游客&
回复 Bill_Hoo:
恩, 谢拉。 L band 和 H band 估计跟坏境噪音的频率有关吧。 我想问问你是怎么wrap WebRtcNs_Process? 我成功把 .so build出来。 然后放到另一个app应用的时候出现了这个错误 Fatal signal 11 (SIGSEGV) at 0x1e90001d (code = 1). 我估计是我wrapper 写错了。 我的是这样的
JNIEXPORT jint JNICALL
Java_research_webrtc_NoiseSuppression_process(JNIEnv *env, jclass jclazz,
& & & & & &jint nsHandler,&jshortArray&spframe, jshortArray spframe_H,
& & & & & &jshortArray outframe, jshortArray outframe_H) {
& & &NsHandle *hNS = (NsHandle*) nsH
& & &// Step 1: Convert the incoming JNI jintarray to C's jint[]
& & &jshort *inCArrayIn = (*env)-&GetShortArrayElements(env, spframe, NULL );
& & &jshort *inCArrayOut = (*env)-&GetShortArrayElements(env, outframe, NULL );
& & &//dummy variable for buffer for H band, contains no content
& & &jshort *inCArrayIn_H = (*env)-&GetShortArrayElements(env, spframe_H, NULL );
& & &jshort *inCArrayOut_H = (*env)-&GetShortArrayElements(env, outframe_H,
& & & & & & & & &NULL );
& & &if (NULL == inCArrayIn || NULL == inCArrayOut || NULL == inCArrayIn_H
& & & & & & & & &|| NULL == inCArrayOut_H)
& & & & & &return -1;
& & &jsize length = (*env)-&GetArrayLength(env, spframe);
& & &// Step 2: Perform its intended operations
& & &jint result = 0;
& & &result = WebRtcNs_Process(hNS, spframe, spframe_H, outframe, outframe_H);
& & &// Step 3: Copy the content of C's Native jshort[] to JNI jshortarray, and release resources
& & &(*env)-&ReleaseShortArrayElements(env, spframe, inCArrayIn, 0);
& & &(*env)-&ReleaseShortArrayElements(env, outframe, inCArrayOut, 0);
& & &(*env)-&ReleaseShortArrayElements(env, spframe_H, inCArrayIn_H, 0);
& & &(*env)-&ReleaseShortArrayElements(env, outframe_H, inCArrayOut_H, 0);
[41楼]&&&&&&[匿名]51CTO游客&
回复 Bill_Hoo:
原来是WebRtcNs_Process的input放错了。 问题已解决。
[42楼]&&&&&&&
回复 51CTO游客:
你好,如果按照我的调用方式,H band的两个参数我都会传递 NULL 指针,而你的
if (NULL == inCArrayIn || NULL == inCArrayOut || NULL == inCArrayIn_H
& & & & & || NULL == inCArrayOut_H)
就直接 return -1了;
再者,题外话,如果没有这句if判断,那么错误会发生在&
(*env)-&ReleaseShortArrayElements(env, spframe_H, inCArrayIn_H, 0);
Release要求传递进去的指针不能为NULL,对NULL指针进行内存释放是非法操作。
不知有没有找到你的bug呢。
[43楼]&&&&&&&
回复 51CTO游客:
哦哈哈,看来我在打字的时候你也在打字..................... 恭喜、
[44楼]&&&&&&[匿名]51CTO游客&
回复 Bill_Hoo:
你好,是根据这个来计算,而且需要严格根据这个的描述来
t_analyze 表示你对音频帧A调用 farend 的时刻,t_render 表示[硬件]真正播放出帧A的时刻。
t_capture 表示[硬件]采集到音频帧B(注意跟A没关系了)的时刻,t_pull 表示帧B被传入 Process的时刻。
其实就是计算系统硬件buffer + 软件 buffer 的总延迟。
这个延迟在android系统中比较大,有100~200左右不等,需要你根据自己的buffer进行计算。计算方法就跟你android上层的buffer设计相关了。
Bill, 对于这个回复我还是有几个问题弄不明白:
1. 帧B里面是不是包含帧A的声音( 帧A从speaker出来进入mic时就是帧B )?
2. 假如t_render & t_analyze呢? 我的理解是WebRtcAecm_BufFarend( Frame A )只要在WebRtcAecm_Process( Frame B )之前调用进行了。这种理解对吗?
3. 怎么能够找到帧A和帧B的对应关系呢(我感觉这是最困难的)?
3. 我又仔细读了你在StackOverflow的发言, 对于这两点:
& 2.AudioRecord.read() and AudioTrack.write() sometimes block(due to minimized buffer size), so when you calc the delay, don't forget adding blocking time to it.
& 3.the buffer of AudioRecord and AudioTrack also increases the total delay. so add it.
你是怎么知道blocking time和buffer of AudioRecord and AudioTrack的大小的呢?
问题有点多, 被AEC困扰挺久了。不胜感谢~
[45楼]&&&&&&&
我最近在做一个Android的通过蓝牙传递语音数据的项目,用了WebRTC的NS后声音变得沙哑了。
我的实现过程很简单,先用AudioRecord录音,取出pcm数据,然后传给ns_process,最好编码后发出去,我为了排除其它影响,特地在ns_process后将数据写入文件播放,跟传送到远端后类似,都是变沙哑了。
另外,不加NS,整个通话声音都是正常的,加上AECM也没有问题。
你有没有碰到类似的问题,NS需要什么特殊设置吗,还是我这个使用过程有问题?
麻烦你有空帮忙看看,谢谢!
[46楼]&&&&&&[匿名]51CTO游客&
楼上可以试下nsx, ns是浮点运算, nsx是定点运算。我用nsx效果挺好的。
[47楼]&&&&&&&
回复 51CTO游客:
谢谢!找到原因了,不过不是因为浮点的原因。
我的参数:
16 bit encoding&
8000 Hz sample rate
Mono (1 channel)
20ms一帧,故每帧为160字节,之前将160字节直接传给NS,而NS每次处理10ms的数据,现在我分成两次,每次传80字节就没有问题了。
[48楼]&&&&&&&
回复 51CTO游客:
A1、帧A和帧B没有直接联系,我们只需要知道总延迟,至于这两帧是否为相互包含的音频没有关系。
A2、根据你的描述,感觉你的思路是这样的:记录帧A放进farend buffer的时刻,然后要让该时刻去匹配帧B传入 process 的时刻。如果你是这样想的,是因为你觉得帧A和帧B是有关联的。实际上这两帧并没有直接联系。如A1所述,我们需要知道的仅仅是两个时间:&
T1(系统的播放延迟) = 帧A被硬件播放出来的时刻 - 帧A被放进 farend 的时刻;
T2(系统的采集延迟) = 帧B被放进 process 的时刻 - 帧B被硬件采集的时刻;
total delay =&T1 + T2;
A3、AudioRecord.rad() and AudioTrack.write() 会阻塞,阻塞时间可以在调用前和调用后粗略计时即可。 AudioRecord buffer的最小大小可使用其提供的API获取。详情请参见 http://developer.android.com/reference/android/media/AudioRecord.html#getMinBufferSize(int,
希望以上阐述能解答你的疑惑。
[50楼]&&&&&&&
回复 51CTO游客:
这段时间都没接触WebRTC音频部分了,NS模块都有定点了 - -+
[53楼]&&&&&&&
回复 Bill_Hoo:
你好博主,请教一下您在17楼关于aecm延迟的计算说明:
“t_analyze 表示你对音频帧A调用 farend 的时刻,t_render 表示[硬件]真正播放出帧A的时刻。
t_capture 表示[硬件]采集到音频帧B(注意跟A没关系了)的时刻,t_pull 表示帧B被传入 Process的时刻。”
我看了echo_control_mobile.h里的方法,关于四个时刻的点怎么确定不是很清楚,比如t_analyze的时间,应该是在调用WebRtcAecm_BufferFarend()前计时还是调用后计时呢?看英文原意四个时刻应该是之后吧?可是如果t_pull是在调用WebRtcAecm_Process()之后才计时的话,WebRtcAecm_Process()它本身就需要使用延迟参数(也就是说调用它时这参数还没计算出来呢),感觉又矛盾了,请博主指点迷津。
而关于render的时间,“t_render is the time the first sample of the same frame is rendered”,一个语音帧(80或160个采样)的第一个采样被播放的时间,总感觉取不到,我们能取到的只有播放完整个语音帧的时间点吧,t_capture同理,您是怎么处理这两个时间点的呢?
[54楼]&&&&&&&
回复 emuman:
你好emuman,
A1:四个时间点均在调用之前得到即可。
A2:我们确实不能直接得到第一个采样点的时间,但你可以根据自己上层的 buffer 大小以及底层的硬件 buffer 大小估算出缓冲区中总共缓冲了多少帧,这样一来便可计算出第一个采样点的时间。
[55楼]&&&&&&&
回复 Bill_Hoo:
首先多谢博主答复,然后继续咨询一下。
这几天我在android上试验,用jni+opensles来录音、放音,发现理论和实际情况大有不同。
参数是8khz、mono、16bit录音和放音,每个语音帧80个sample(10ms)。
<span style="color:#、首先单个延迟很小,t_render - t_analyze大约在25-55ms区间内,t_pull - t_capture对我来说约等于0,因为我采集后马上就进行process了;和博主说的大约100--200ms差距比较大。此外我是不断重新计算延迟&#20540;的,博主您是计算出一个&#20540;就固定使用还是也不断计算?
<span style="color:#、opensles的录音放音情况是:某个时间段内得到m个录音的语音帧,然后下一个时间段播放n个语音帧,如此反复。所以如果不变通,就会变成调用m次WebRtcAecm_BufferFarend()后再调用n次WebRtcAecm_Process,4个时间点怎么确定就很暧昧了:因为延迟&#20540;其实是给录音后的process用的,所以属于录音的t_capture和t_pull的时间点是明确的,但是放音的t_analyze和t_render用m个语音帧里的哪个呢?第一个、最后一个、平均&#20540;?我试验了最后一个(距离录音动作最近的上个播放延迟),发现出来的结果不对;直觉上第一个、平均&#20540;也应该不对。&
最后,从多次统计结果发现,m和n是同一个数量级,差距不大(这是显然的)。
<span style="color:#、从我这段时间的测试数据看,android机系统有自己的opensles缓冲区,比如录音,我设置录音缓冲区是80个sample,系统会先录制一段时间,数据充满它自己的缓冲区(系统缓冲区)后再调用回调函数拷贝到我设置的缓冲区(用户缓冲区),放音也同理(提供数据足够快的情况下),这就解释了上面第二点的情况:先提供m个录音语音帧,再消费n个放音语音帧。
这里有个问题,我翻遍开发文档发现系统没提供接口来得到系统内置缓冲区大小,我甚至在google play上找到个audio buffer size的app,它用自己的算法来计算系统opensles的内置缓冲区和采样率大小,但我的测试机算出来的结果和我的测试数据又没法对上。java层的AudioRecord.getMinBufferSize()以及api17后倒有个相关的AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER,这两个试验过发现也不对。
<span style="color:#、针对2和3的情况,我设想过能不能把m个录音帧当一个大帧、n个放音帧当一个大帧来处理。WebRtcAecm_BufferFarend()好像可以累积数据,只要长度不超过它的缓冲区最大&#20540;就好;但WebRtcAecm_Process()只能传进80或160个sample,这就矛盾了。
<span style="color:#、好像苹果机的机制是录一个帧再放一个帧,非常符合webrtc aecm的理论,甚至它都提供内置的回音处理了。android在4.0后虽说也提供内置的回音处理,但效果很多时候等于没有。这点是我的牢骚了,android还有很多地方要完善。
对opensles的这种情况,博主有没有什么经验?甚至提供个思考的方向都可以,我现在束手无策了
[56楼]&&&&&&&
回复 emuman:
emuman你好:
A1:<span style="color:#ff0ms延迟是在 Java 层使用AudioRecord/AudioTrack得到的,你使用的opensles在JNI层了,延迟在25-55ms正常。t_pull
- t_capture不一定为0,你虽然采集之后马上就调用 process,但是t_captrue是硬件采集到某帧的时刻,该帧从硬件进入底层buffer,然后你的 opensles 从底层buffer读取是有延迟的,这个延迟要算,在JNI层可能很小,但是也要算。最后延迟&#20540;的确需要不断更新。WebRTC源码里我看了下好像是每一秒更新一次,我自己的实验结果1秒太久了,我是每发送一帧就更新延迟&#20540;。
A2:当初选型的时候我没用OpenslES,所以我不清楚他的工作流程。从你对他的描述中发现问题在于其录音、放音流程导致四个时间点不好捕捉,他自己缓存了录音帧和播放帧,但我们又没办法求得缓存大小以最终求得缓存延迟。个人暂时没想到好的直接可行的解决办法,因为如果我们没办法得知他缓存了多少数据,那如何求得对应的延迟?不过我隐约有个思路是我们需要自己设计buffer来统一缓存数据,这样便可计算,但实现方案我没有思路。
A3:据你的描述,“我设置录音缓冲区是80个sample,系统会先录制一段时间,数据充满它自己的缓冲区(系统缓冲区)后再调用回调函数拷贝到我设置的缓冲区(用户缓冲区),放音也同理”。既然你可以设置缓冲区大小,为何不能计算出延迟呢?这点需要你再加以阐释。其次,AudioRecord.getMinBufferSize()所获得的大小是系统最小录制缓冲区大小(如果你没有其他设置的话),这个&#20540;你需要在构造AudioRecord实例时传入,这样该实例对应的底层buffer才会是这么大,之后根据这个大小是可以估算出底层buffer所造成的延迟的。AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER我没有研究过,不敢乱讲。
A4:“当成一个大帧”这种思路其实和我第二点提到的思路异曲同工了,我们的核心思想都是希望将小的、不可知的opensles底层buffer延迟中和起来,得到一个整体的、可计算的延迟,我直觉可行,但需要实践。 WebRtcAecm_BufferFarend()的确有自己的buffer,好像是用来做匹配算法的,回声处理的算法图可以在 CSDN 里找到。但该buffer大小不足以容纳太多的数据。
A5:iOS音频系统本身的延迟就很小,并且其具有直接获取硬件延迟的API,WebRTC源码里就是直接调用的该API。 VoiceProcessionUnit 也自带有回声消除选项,不过我没有使用过。至于 android 的 ES,我用过,效果不堪。android的的确确需要继续完善,尤其是音频API,不过 android
毕竟开源,个人觉得其生命力和活力都很不错,而且 Google 也在统一 android 市场了,跑题了- -、
希望以上回答能帮助你打开思路,我还有哪里没讲清楚的请留言。
[57楼]&&&&&&&
你好博主,感谢你的回复。
关于3,“既然你可以设置缓冲区大小,为何不能计算出延迟呢”,这里没讲清楚,opensles里初始化录音器和播放器时必须指定缓冲区大小以及个数,自己设置的缓冲区指的是这个用户缓冲区,它的延迟是可以计算的,但系统还有个缓冲区,那个现在看来无法直接取到它的大小(也就是延迟),问题是在这里。
opensles的工作方式里可以设置成给录音/放音提供多个一样大小的缓冲区(估计是为了数据处理连续性),这个缓冲区只是用户缓冲区而非系统缓冲区,然后指定录音/放音回调函数,每次回调就提供/消费一个用户缓冲区数据。
昨晚我有了新思路,这两天继续试验,有什么进展再过来讨论。
[58楼]&&&&&&&
你好,BillHoo!
我从StackOverflow追到这里,我最近在做基于ARM Linux板子的媒体通信项目,碰到回声问题,想用WebRtc的AEC,但有些问题看代码和网上资料没看懂,想跟你请教以下几个问题:
1.WebRtcAec_BufferFarend函数前面有注释:
Only Buffer L band for farend
是什么意思?只能放L Band数据还是放了W Band他也转换为L Band?我看了里面代码,就是时域转频域,然后写缓存;
另外,L Band和W Band是指的采样率的高低吗?
2.WebRtcAecc_Process函数中的最后一个参数skew是干什么用的?怎么设置?我没看明白。
3.AEC和AECM都有注释Not recommended to be enabled on the server-side.在aec_processing.h中,为什么?服务器端指的是什么?多点通信中负责混音的类&#20284;H.323中的MCU吗?超级节点?
4.AEC可以修改为44.1K采样率吗?Echo Tail Length想要修改的话要动哪里?
不好意思,一口气问了这么多,还望博主给予指点。
[59楼]&&&&&&&
回复 emuman:
好的,祝你成功。
[60楼]&&&&&&&
回复 Suitjune:
你好 Suitjune, 我最近很忙,所以没办法详细回复你,抱歉。
[62楼]&&&&&&&
能再分析下neteq模块吗,貌&#20284;对音频质量有一定的提高
[63楼]&&&&&&&
回复 it206:
it206 你好,neteq模块我还没有涉及,如果有可能涉及到,我会分享在这里的。谢谢你的建议 :)
[64楼]&&&&&&&
Bill Ho,还是要请教你相关问题:
1.我用WebRtc的AEC提取出来,在ARM-Linux的板子上运行,现在我做测试:
1.32K单声道的人声文件A,1次160采样点做BufferFarend,同时将这个文件去做Process,Output出来的文件几乎没有声音,都去掉了;
2.如果我是A去做BufferFarend,同时A+B,两个人声去做Process,则输出声音劣化严重,声音嗡嗡的;
3.如果我录制了一段很安静的声音,几乎没有声音的文件C去做BufferFarend,然后A+B人声做Process,那输出也是嗡嗡的;
这可能是什么问题导致的呢?谢谢!
[65楼]&&&&&&&
上面的问题应该是我自己弄粗了。
[66楼]&&&&&&&
楼主,您好,最近项目中要用到AECM,我在按照楼主提供的AECM模块源文件进行编译的时候(头文件都已添加),出现函数缺失的错误,具体的错误提示为“undefined reference to 'WebRtcSpl_MaxAbsValueW16'”...“undefined reference to 'WebRtcSpl_RealForwardFFT'“等等错误。我的解决方法是找到这些函数所在的源文件并添加到工程中,生成的库之后,编写Demo按照楼主方法进行检测,读取相同音频文件时依然有原声。楼主能否给个生成AECM库的完整文件,QQ
,万分感谢。
[67楼]&&&&&&&
謝謝您的帖子!我正在使用webRTC中的AEC模塊、我想問一次WebRtcAec_BufferFarend和WebRtcAec_Process中的音頻信號的&#26684;式(就是nearend.pcm和farend.pam的&#26684;式):我現在是用:
8000HZ採樣、Signed-16 PCM、LITTLE_ENDIAN
我用audacity錄的farend.pcm、然後(僅為測試)我把farend.pcm直接拷貝生成nearend.pcm、這樣我希望AEC處理後生成的output.pcm應該不含有聲音信號(全部過濾)、但是我得到的output.pcm是很大的噪聲、所以我懷疑是我爸音頻&#26684;式設錯了。
輸入的farend.pcm和nearend.pcm的&#26684;式是8000HZ採樣、Signed-16 PCM、LITTLE_ENDIAN嗎?
AEC處理後輸出的output.pcm的&#26684;式也是8000HZ採樣、Signed-16 PCM、LITTLE_ENDIAN嗎?
另外附上我的測試代碼(從sample中拷貝的)、麻煩您幫我看一下問題在哪裡、謝謝!
& & &void * haec = NULL; & & &&
& & &if( 0!=WebRtcAec_Create(&haec) )&
& & & & & &printf(&create error\n&);&
& & &if( 0!=WebRtcAec_Init(haec,) )
& & & & & &printf(&init error\n&);
& & &short&farend[160],nearend[160],output[160];
& & &FILE *ffar, *fnear,*
& & &ffar = fopen(&farend.pcm&,&r&);
& & &fnear = fopen(&nearend.pcm&,&r&);
& & &foutput = fopen(&realoutput.pcm&,&w&);
& & &for(int i=0;i&500;i++)
& & & & & &fread(farend,2,160,ffar);
& & & & & &fread(nearend,2,160,fnear);
& & & & & &if( 0!=WebRtcAec_BufferFarend(haec,farend,160) )
& & & & & & & & &printf(&bufferfarend error\n&);
& & & & & &if( 0!=WebRtcAec_Process(haec,nearend,NULL,output,NULL,160,0,0) )
& & & & & & & & &printf(&process error\n&);
& & & & & &fwrite(output,2,160,foutput);
& & &if( 0!=WebRtcAec_Free(haec) )
& & & & & &printf(&free error\n&);
& & &fclose(ffar);
& & &fclose(fnear);
& & &fclose(foutput);
[68楼]&&&&&&&
問題已經找到了(粗心大意忘記用2進制來讀寫測試文件了)。現在把farend.pcm中的語音以100ms延遲混入nearend.pcm中,再用WebRtcAec_Process(haec,nearend,NULL,output,NULL,160,100,0)來處理,基本可以去掉原來farend.pcm中的語音(迴聲),再次感謝您的帖子!
[70楼]&&&&&&&
謝謝!我還想問您一個問題:在昨天的測試中,我剛開始使用8000HZ/Signed-16-PCM/LITTLE_Endian的語音數據、當延遲100ms的時候,迴聲基本可以消除;但是後來改為我們現在實際使用的16000HZ/Signed-16-PCM/LITTLE_Endian的語音數據時,發現迴聲殘留明顯比8000HZ的時候大很多。我在處理8000HZ數據的時候調用WebRtcAec_Process的參數是這樣的:
WebRtcAec_Process(haec, nearend, NULL, output, NULL, 160, 100, 0)
我覺的160應該是20ms的8000HZ/Signed-16數據,所以當處理16000HZ/Signed-16數據的時候,應該改成320,即這樣調用:
WebRtcAec_Init(haec,1)
WebRtcAec_BufferFarend(haec, farend, 320)//20ms的16000HZ/Signed-16數據
WebRtcAec_Process(haec, nearend, NULL, output, NULL, 320, 100, 0)//100ms延時
但是當改成320的時候、WebRtcAec_BufferFarend和WebRtcAec_Process都報錯(返回&#20540;不為0),我想問一下:是否這2個函數能夠處理的sample數目最大就是160?您在處理16000HZ/Signed-16數據的時候、每次處理的是10ms(160)還是20ms(320),請問這個sample數目和消除迴聲的效果有關係嗎?謝謝!
[71楼]&&&&&&&
剛才看見WebRtcAec_Process的源碼(echo_cancellation.c)中:
& // number of samples == 160 for SWB input
&&if (nrOfSamples != 80 && nrOfSamples != 160) {
& & aecpc-&lastError = AEC_BAD_PARAMETER_ERROR;
& & return -1;
所以可能是都處理10ms數據(8000HZ的sample是80, 16000HZ的sample是160)、謝謝!
[72楼]&&&&&&&
回复 米牛牛:
您好米牛牛,16000Hz,10ms就是 160 個 sample,您最後是算對了的。
[73楼]&&&&&&&
最近也在研究webRTC中的AEC模块,有个问题请教下
WebRtcAec_BufferFarend和WebRtcAec_Process中的参数farend和nearend是不是分别表示扬声器要发出的原始音频数据和麦克风录制的原始音频数据(这个有可能包括扬声器的回声)?另外参数 nrOfSamples是不是表示这两个函数一次最大处理的数据块?即只有80和160这两个
[74楼]&&&&&&&
您好!我還想問一下關於drift compensation的問題:WebRtcAec_Process最後的那個skew參數、我現在一般都設為0、請問這個參數需要根據硬件的情況進行調整嗎?還是說都設為0?謝謝!
[75楼]&&&&&&&
這裡有一個skew參數的討論:
https://groups.google.com/forum/#!searchin/discuss-webrtc/skew|sort:relevance/discuss-webrtc/T8j0CT_NBvs/aLmJ3YwEiYAJ
其中有一段:
。。。but we're really just compensating for a mismatch
between 44.1 and 44 kHz sampling rates.。。。
這是不是說對於8000HZ和16000HZ來說、skew參數都可以設為0?還是說依然要設法計算當前硬件的drift?謝謝!
[76楼]&&&&&&&
回复 米牛牛:
这个应该是对于不同平台的时钟补偿吧,因为不同平台的时钟不一样,会导致同频率有差异
[77楼]&&&&&&&
回复 icebery425:
您好icebery,
farend是远端数据,即VoIP通信中接收到的对端的、即将被本地扬声器播放的音频A。
nearend是本地数据,即刚刚被本地麦克风采集到的音频B,而音频B可能包含上述音频A(麦克风采集到了扬声器播放的A),于是 A和B 一起发送给远端,致使远端又听到了自己刚才发送的A,也就产生了所谓的回声。
nrOfSamples 可以这么理解,具体的含义WebRTC源码里有说明的 :)
[78楼]&&&&&&&
回复 米牛牛:
您好米牛牛,該參數在理想情況下的確為0,但硬件不同,對音頻進行渲染和播放的延遲就有所差異,這個差異在我之前的android設備上達到了100~200ms不等,如果始終保持為0,僅根據我的實踐結果,將得不到好的回聲消除效果。以我個人的經驗來說,該參數是需要實時更新的。&:)
注:博主用繁体回复,真是很好的一个人啊
[79楼]&&&&&&&
最近我在研究一个消除超声波回声的项目,用WebRTC中的AEC时,发现在调用WebRtcAec_Init时,参数有一个需要传递采样数据频率只有8000, 1hz这几种频率,我这边需要用到的采样数据频率为44100,当传这个&#20540;是就出错,应该是表示不能传其它频率的&#20540;,或更高的&#20540;,想问下,你有没碰到过这样的问题,或者类&#20284;的情况,需要消除超声波的回声?
[80楼]&&&&&&&
回复 Bill_Hoo:
謝謝您的回复!您說的要實時更新的100~200ms的參數是下面的哪一個?是msInSndCardBuf還是skew?
WebRtcAec_Process(void *aecInst,&
const WebRtc_Word16 *nearend,
const WebRtc_Word16 *nearendH,&
WebRtc_Word16 *out,&
WebRtc_Word16 *outH,
WebRtc_Word16 nrOfSamples,&
WebRtc_Word16 msInSndCardBuf,&
WebRtc_Word32 skew)
msInSndCardBuf是用來適應延遲的、您說的要在100~200ms內實時更新的是指msInSndCardBuf嗎?請問是否skew也需要實時更新?是否skew目前只用於對於44100hz的漂移補償?能介紹一下如何實時更新skew參數的步驟嗎?謝謝!
[81楼]&&&&&&&
回复 米牛牛:
hi, 米牛牛,你有没偿试做过对44.1KHz采样数据的消回声事情?
[82楼]&&&&&&&
回复 米牛牛:
您好米牛牛:
我很抱歉的發現我們討論的不是一個模塊。我說的是手機上進行AEC處理的模塊“AECM”,而你使用的是PC端的“AEC”模塊。手機上的AECM模塊是沒有skew參數的,我想這也是手機CPU瓶頸所致。而PC端的接口會有skew參數。這個參數我沒有研究。只是當初在手機上使用PC端模塊AEC時,該參數被我設置為0。之後就沒有對這個參數做任何學習了。
[86楼]&&&&&&&
回复 米牛牛:
但是我發現一個情況:就是當我把AEC初始化為16K的時候、依然可以處理8K的數據(這時每次處理的數據依然是160個samples、即變為20ms的8K數據)、不知是否對您的44K項目有用
[87楼]&&&&&&&
Bill_Hoo,请教你一下,我在android上应用了那个aecm模块,回音消除的效果不错,但是,如果一个语音过长的话,比如说一次说七八个字,经过回音消除后,出现了语音丢失的情况,即第四五个字会丢失或者无法听清楚,不知道这个是什么原因呢,是和参数的设置有关吗?我的msInSndCardBuff设为240左右?这个问题困扰了好久了,最近一直在看源代码也没啥头绪。
[88楼]&&&&&&&
回复 lzg9099:
您好lzg9099:
我在想,你是一直使用240这个定&#20540;么?再者,回声消除的级别是否开到了最大?降一个级别试试。如果你能提供更明确的信息,也许我们可以找到问题所在。
[89楼]&&&&&&&
回复 lzg9099:
还有一个问题我突然想到,你是不是在做本机的回环(127.0.0.1)测试? 如果是的话,吞字是正常的。
[90楼]&&&&&&&
回复 Bill_Hoo:
感谢你的回复:
1,我是一直使用的240这个定&#20540;,我换了几个手机测试时都是用的240,效果都很好,要是将该&#20540;设为其他的话,声音要么有杂音,要么就听不到声音了;2,关于回音消除的级别,我没有去设置,那应该是默认的级别吧,这个级别我也没弄太清楚;3,关于测试,我只是写了一个demo,通过将AudioRecord采集的声音,传给aecProcess()方法进行回音消除,然后利用AudioTrack将处理过后的声音播放出来,播放以后调用aecmBufferFarend()方法将处理后的声音处理一下。这样测试时,播放出来的声音是没有回音的,但是出现了吞字状况。
[91楼]&&&&&&&
Bill_Hoo 高手,你好,我按照你的思想把aecm抽取出来编译成so,通过jni给Android调用,回音消除效果如下:如果是前置喇叭(听筒)效果完美,如果是后置喇叭(播放音乐用的)效果不如前置喇叭,总体来说还不错。但是现在出现一个问题就是 当选择后置喇叭作为输出时,如果在一个很安静的房间,静静的几分钟不说话,就会产生啸叫,如果出现啸叫后就说话,这啸叫就立刻消失。想请教一下,这个啸叫怎么产生的,还有怎么样可以解决,你的软件会产生这样情况吗?
恭候QQ上讨论 扣扣 
[92楼]&&&&&&&
回复 AudioAEC:
你好,首先,听筒放音是不会存在声学回声的。声学回声之所以产生,是因为扬声器播放出来的声音又被麦克风采集回去。而听筒播放的声音麦克风是采不到的。
其次,啸叫的问题需要借助 VAD 来进行静音检测,在静音的环境下就不需要向扬声器传递音频进行播放了。
[95楼]&&&&&&&
您好,我的AECM模块读文件测试正常。在开双线程测试时,一个线程用AudioTrack读文件播放,并调用bufferFarend;另一个线程中,用AudioRecord录取音频数据,并调用process将录取的数据进行处理,处理后的数据存入到pcm文件中。用Audition播放处理后的音频数据,为能完全消除掉回声,但是出现吞音的现象,且经常出现。用setConfig方法设置回音消除等级后,吞音现象微有减弱,但依然存在。
调试了很多,依然无法解决上面的问题,我怀疑是延时计算有问题,我的延时计算为:
硬件延时t1: bufferSize1 = AudioRecord.getMinBufferSize(...)
& & & &&t1 = bufferSize1 / frameLength * 10 ms
读取数据阻塞延时t2:
& & & & & & & startTime = System.currentTimeMillis()
& & & & & & & audioRecord.read(...)
& & & & & & & t2 = System.currentTimeMillis() - startTime
硬件延时t3: bufferSize2 = AudioTrack.getMinBufferSize(...)
& & & & t3 = bufferSize2 / frameLength * 10 ms
读取数据阻塞延时t4 :&
& & & & & & startTime = System.currentTimeMillis()
& & & & & & inStream.read(...)
& & & & & & t4 = System.currentTimeMillis() - startTime&
总延时是上述四个延时之和,不知道上述延时计算是否存在问题,请多指教。
我是从pcm文件读取数据送到AudioTrack播放,同时录音获取的数据直接送到process中。
[96楼]&&&&&&&
回复 icebery425:
您好 icebery425:
我这段时间在往WebRTC音频的上边一层看,也就是APM(AudioProcessingModule),发现APM使用的抽象类型 AudioFrame 最高仅支持双声道 60ms 的 32KHz音频。
/* This class holds up to 60 ms of super-wideband (32 kHz) stereo audio. It
* allows for adding and subtracting frames while keeping track of the resulting
你所说的44.1应该会被处理掉的,具体的我还在看。
[97楼]&&&&&&&
想问下,vad模块的使用,为什么我的WebRtcVad_Process总是返回1,即使不说话时也是这样
[98楼]&&&&&&&
回复 Bill_Hoo:
您好,delay的计算方法在之前的讨论中已经讨论过了。我看了下你的计算,应该还没有计算底层 buffer 缓冲的数据。最近我在看 APM 了,建议您参考下&audio_device_jni_android.cc
中的实现。
[99楼]&&&&&&&
回复 it206:
您好,您给的条件好像少了点,我没办法判断返回&#20540;异常的原因。也许你的JNI实现有bug?也许VAD模式开到了最高级别?级别越高,返回1的概率越大。
[100楼]&&&&&&&
我是8000采样,pcm16,每次处理320 byte数据,局域网自环或者2台手机udp互通,情况一样,WebRtcVad_Create,VadApi_Init 都正常返回0,vad模式设置0或者最高都试过,在编码之前做静音检测,如下
& & for(int i = 0; i & 2; ++i)
& & & result = vad.VadApiValidRateAndFrameLength();
& & & System.out.println(&VadApiValidRateAndFrameLength result = & + result);
& & & System.arraycopy(bufferSpeech, i * 160, intmp, 0, 160);
& & & result = vad.VadApiProcess(8000, intmp, 160);
& & & System.out.println(&VadApiProcess result = & + result);
VadApiValidRateAndFrameLength 返回0
VadApiProcess每次都返回1
感觉这样使用没有问题,但是无法检测静音
[101楼]&&&&&&&
我還有個問題想問一下:我在實驗中想處理2種迴聲:
第一種是別人的聲音進來後我不把它發出去(由我的麥克風造成的)、這種迴聲延時較短
第二種是我的聲音發出去之後又被別人echo回來、我不想聽到自己的聲音、這種迴聲延時較長
我現在可以消除第一種迴聲,但是第二種基本無法消除:請問你們有用webRTC-GIPS的這個API可以成功消除第二種迴聲的嗎?謝謝!
我現在的思路是:只要我能夠正確估算出延時、我就可以先buffer一段數據、然後在合適的時候再調用WebRtcAec_BufferFarend、如果這個思路可行的話、延時可能可以用某種算法(比如卡爾曼濾波)來自適應跟踪、不知這種思路是否可行?謝謝!
[102楼]&&&&&&&
另外請問一下:這篇論文討論的是不是我在前面說的第二種迴聲(就是我發出去的然後又返回來)?謝謝!
http://wenku.baidu.com/view/403b6739376baf1ffc4fadfc.html
[103楼]&&&&&&&
回复 米牛牛:
您好米牛牛:
不知道你的實驗是基於PC的還是手機的?
首先我們討論的是VoIP中的聲學回聲的消除。WEBRTC的API足以保證聲學回聲的消除(只是如果延遲處理的不好,會有部份正反饋嘯叫)。
就我目前的知識水平來說,我認為你說的第一種情況不會產生、至少不會影響到正常通話的聲學回聲。麥克風造成的應該是硬件回聲吧?這跟WebRTC討論的聲學回聲應該不是一個範疇。
第二種情況,是別人開了揚聲器外放,如果沒有做回聲消除,外放的聲音會被他的麥克風再次採集并發送給我們,這是聲學回聲的範疇,WebRTC是可以很好處理的。
如果你是基於PC的實驗,WebRTC上Windows端的回聲消除做得還是不錯的,他的延遲可以根據系統API直接計算出來,這個過程WebRTC - win 的Demo中已經有了,可以直接參考著做。
如果你是基於手機的VoIP應用,本文提供的方法可以消除90%的回聲,剩餘的10%是因為delay計算的不准確導致的正反饋嘯叫。這一點我現在也正在努力,目前的思路是上升一個級別的API,使用WebRTC - APM 并使用底層的OpenSLES進行音頻的播放和採集,這樣最大限度的減小延遲的誤算。
[104楼]&&&&&&&
Bill_Hoo,你好。
我现在也在做Aecm这块,我这边编译后,在PC上测试,一点效果都没有,用PCM文件测试,声音都变得很糟。是不是编译的时候需要打开什么宏啊?另外,你能提供一个Demo吗?
[105楼]&&&&&&&
楼主你好,请问如何利用 web rtc的AEC模块在本地采集音频并播放,从而验证消除回声的功能。我在使用windows 下的directshow 音频采集和 播放,在 Audio Capture 和 DSound renderer之间加入了自己写的AEC filter(内部利用Webrtc AEC来实现), 在AEC Filter内部 设置缓冲区存放之前的几个声音buffer, 同时将新来的buffer和老的buffer做回声消除。效果很差,仍然会出现回声啸叫。请问楼主这种方案可行么?问题出在什么地方?谢谢~
[106楼]&&&&&&&
楼主你好,请问使用webrtc 的回声消除功能,只需要使用它的 AEC 部分,编译成库文件即可么?还是需要整个了解设置 webrtc 的 Audio Processing 模块来使用(我看网上好多这么写的,但都没有实例)?希望楼主有时间解答一下,谢谢~
[107楼]&&&&&&&
楼主 你好!一直在关注你的回复,我现在在linux平台上测试webrtc的aecm模块,现在苦于延时的计算,我用的是alsa的接口aplay和arecord来进行放音和录音,很难计算出第一个帧从硬件放出来的时间和第一个帧被硬件采集到的时间,系统貌&#20284;没有提供合适的函数来计算这些时间,请问楼主能否给一些建议,非常感谢!
[108楼]&&&&&&&
回复 LuoZH:
Bill_Hoo您好,
我在測試中發現2種迴聲都是存在的、具體如下:
第一種迴聲是我自己的設備產生的:即別人的聲音進到我的揚聲器後、又被我的麥克風捕獲後發給別人、這種迴聲延時較短。這時AEC的目的是不想讓別人(因為我的設別)而聽到他自己的聲音、這時farend是我收到的語音、nearend是我即將要發出的語音
第二種迴聲是別人的設備產生的:即我的聲音進到別人的揚聲器後、又被別人的麥克風捕獲後發給我、這種迴聲延時較長。這時AEC的目的是我自己不想因為別人的設備而聽到我自己的聲音、這時farend是我即將要發出的語音、nearend是我收到的語音
我發現因為第一種情況延時較短所以AEC效果較好;而第二種情況延時較短所以AEC效果相對較差;請問您處理的是哪一種情況?您計算出來的延時大概是多少毫秒?能否告訴我一下您通常處理的這個延時數?謝謝!
[109楼]&&&&&&&
回复 米牛牛:
您好米牛牛:
我懂到您的意思了,您對farend和nearend的理解和我的理解不太一樣。
首先說說我的理解:針對同一台設備,只存在一個 farend 和一個 nearend,同一台設備上,從對方設備接收到的音頻數據為 farend(也即即將被本機播放的音頻);從本機採集到的音頻為
nearend (也即即將發送給對方的音頻)。而每一台設備僅針對自己設備上的farend和nearend進行AEC處理而無需考慮對方(因為對方也會在他的設備上做同樣的處理)。
綜上所述,所以我認為不存在您所說的具備兩種回聲。
不知道我這樣的理解是否有誤?您有什麽見解我們再一起討論。
其次,延遲參數在不同的設備上是不一樣的,這裡我僅有手機處理的經驗,所以就不談PC了。手機上,比如三星i9100延遲計算出來達到了150~230ms的樣子,而魅族MX延遲僅在80~120ms左右。當然i9100用的是android 2.3.1,而魅族MX是4.1.3,不過CPU處理能力也占一部份因素。延遲爲什麽會差距這麼大,就我目前的知識水品無法說清楚。
[110楼]&&&&&&&
回复 linux_aecm:
linux上的ALSA接口我沒有测试过,我这段时间在看APM,好像针对ALSA有单独的编译选项,具体的我没有深究。这里仅说一下android上延迟的计算。android上也没有直接获取延迟的API,不过可以通过得到底层缓冲区的大小和当前播放的位置从而估算出缓冲区造成的延迟,这一计算的具体细节我昨天刚在WebRTC源码的android_test中找到。不知道linux上的接口是否有提供关于底层缓冲buffer大小的信息?
[111楼]&&&&&&&
回复 Sea_biscuit:
您好Sea_biscuit:
PC上的AEC我没有涉足,不敢乱讲。不过PC上好像不用单独把AEC提取出来,因为没有CPU的限制,PC上是可以直接把WebRTC-WIN编译出来跑的。
至于您的第二个问题,在手机上,AECM模块是可以单独使用的。如果延迟计算的好,效果是可以接受的、不会产生反感情绪,但不够完美。因此近段时间我也一直在倒腾APM,现已经完成了APM整体模块的编译和测试,只是如何调优我还在倒腾中,所以也不敢乱讲。
[112楼]&&&&&&&
回复 it206:
您好,初步看了下代码应该是没有问题的。建议您进行底层的调试以求找到问题所在。调试方法博客里有写。希望对您有所帮助。
[113楼]&&&&&&&
回复 LuoZH:
您好,AECM是用于手机等移动设备的回声消除模块。PC您应该使用AEC才对。至于编译选项,不知道您是在PC环境下(WIN32宏)还是在linux环境下(WEBRTC_ANDROID等宏)编译的?
[114楼]&&&&&&&
回复 Bill_Hoo:
楼主你好,谢谢你的回复。还有一些问题,就是你说的 PC上可以直接将 WebRTc-WIN编译出来跑是什么意思,是整个P2p的聊天系统么? 还有就是楼主能否讲一下如何将整个APM 编译使用么,是做成库还是啥?谢谢~
[115楼]&&&&&&&
回复 Sea_biscuit:
您好,PC上WebRTC是有完整的DEMO的,只要编译出来就可以跑,然后你可以根据自己的需要进行提取。具体的我没有亲自实验过所以就不乱讲了。APM是可以直接编译成库进行使用的。
[116楼]&&&&&&&
回复 Bill_Hoo:
楼主你好,谢谢你的回复。现在还有个问题麻烦你一下下,就是我编译出来 audioprocessing.lib 在程序中结合 audio_processing.h 使用。其中 static AudioProcessing* AudioProcessing::Create(int id);函数显示未定义还是啥
: error LNK2019: unresolved external symbol &public: static class webrtc::AudioProcessing * __stdcall webrtc::AudioProcessing::Create(int)& (?Create@AudioProcessing@webrtc@@SGPAV12@H@Z) referenced in function &public: __thiscall APM::APM(void)& (??0APM@@QAE@XZ)
就是这个。
请问楼主问题出在什么地方? 是使用方法不对还是什么,谢谢~
[117楼]&&&&&&&
回复 Sea_biscuit:
您好,我没有编译PC上的APM,不过这个问题您可以借助GOOGLE进行分析解决,直接GOOGLE:error LNK2019:http://www.cppblog.com/longshen/archive//111418.html
[118楼]&&&&&&&
回复 Bill_Hoo:
谢谢楼主,您真是太耐心了~谢谢,问题我自己解决了。使用APM模块不光需要 audio_processing.lib还需要其他几个lib.再次感谢!
需要以下的几个lib
#pragma comment(lib, &webrtc\\audio_processing.lib&)
#pragma comment(lib, &webrtc\\audio_processing_sse2.lib&)
#pragma comment(lib, &webrtc\\system_wrappers.lib&)
#pragma comment(lib, &webrtc\\protobuf_lite.lib&)
#pragma comment(lib, &webrtc\\common_audio.lib&)
#pragma comment(lib, &webrtc\\audioproc_debug_proto.lib&)
[119楼]&&&&&&&
回复 Bill_Hoo:
Bill_Hoo您好!是的一般都是處理第一種迴聲(我自己的設備產生的)、並且如果每一個設備都把自己的迴聲消除之後、就不會存在第二種迴聲了。只有當通話對方(或視頻會議中某一個或某幾個與會者)的設備沒有消除自己的迴聲時、別人才會聽到自己的迴聲(第二種)、正如您所說的這種情況比較少見、並且延時也較長不太容易處理(視不同的網絡情況、延時可能會超過1000ms)。
再次感謝您提供的手機延時數據!
[121楼]&&&&&&&
回复 米牛牛:
您好米牛牛,您所說的第二種回聲是我沒有考慮到的。我一直假設的是每個設備都能自己把回聲處理掉,也就是說我假設參與會畫的雙方或多方都已經開啟了AEC模塊。感謝您提出的新思路。預祝您的試驗成功。
[122楼]&&&&&&&
回复 Bill_Hoo:
Bill_Hoo你好!
你这篇文章帮我了大忙,我是在Linux下编译的,按你的方法编译了。之前播放和录音是在两个线程,然后没有正确的计算延时,所以效果很差,现在播放和录音放在一个线程。将buffer调整到尽量小,现在,消除效果很好。下下一步是在安卓和winCE上使用。
很感谢你们这些愿意分享知识的人,如果Lz后续能够继续完善后续的研究结果,一定会帮助许多人的。
谢谢Bill_Hoo.
[123楼]&&&&&&&
回复 LuoZH:
您好,感谢您的反馈。如果您愿意,也可以将您在Linux上的经验写成博文分享给大家 :) 比如您提到的播放和录音在同一个线程里进行。我现在在android上仍然是两个线程,并且为计算延迟也花了不少精力,如果您的方法更有效,我想这对大家后续的开发都有好处。
[124楼]&&&&&&&
回复 Bill_Hoo:
Bill_Hoo你好!
我这个因为场景特殊,所以才能在一个线程内播放和录音。
编译的AECM,使用了Linux的OSS接口,可以使用ioctl直接修改buffer(这个网上资料很丰富)。我是使用一个线程进行接收和发送。接收到数据后直接将数据放入一个队列,然后到另一个队列去取数据来发送。另一个线程处理录音和播放,线程首先去接收队列取数据,放入WebRtcAecm_Farend,并播放,然后立即录音,将录到的数据放入WebRtcAecm_Process进行处理,处理之后放入发送队列。你提到adnroid_test里面有对延迟的计算,但webRTC类太多层了,我找了很久也没找到这个计算,不知你能否对这部分计算的相关源码进行更多的讲解。
[125楼]&&&&&&&
回复 LuoZH:
您好,android_test 中的延迟仍然使用的是2012年的版本,录制延迟仅仅采用了一个粗略的固定&#20540;,播放延迟的计算方法我比较赞同,也是我自己使用的方法,就是通过android AudioTrack的API估算出底层buffer缓冲的数据延迟。总的来说,仍然是尽可能严&#26684;地按照前面所说的那个延迟计算公式进行计算,计算得越严&#26684;,效果越好。
[126楼]&&&&&&&
回复 Bill_Hoo:
你好,可否把你计算的这部分代码发给我?我的Email是
[127楼]&&&&&&&
回复 LuoZH:
您好,本文仅讨论实现的思路,代码已经涉及实现细节,有所不便,都是技术人员,请谅解。 :)
[128楼]&&&&&&&
您好,看了您的博文很有启发,所以自己也弄了几个模块,编译成了.so文件。aec我也使用起来了,但现在我被两个问题所困扰:
1,我是在linux下的alsa环境下作的,所以测延时用了snd_pcm_delay函数。计算出来总共有60多毫秒,去回声效果还行,但是我把其中一台的delay设成0之后去回声效果反而还更好了些。这个就有点纳闷了,难道delay计算有错?
2,另一个问题是我在通话的过程当中偶尔会有声音忽然变小的现象出现,甚至小到听不到。然后又会恢复。我感觉有点像是回声消除的太多,把我说话的原音也消除了些。但是我把回音消除的级别降低后现象反而还更不好(我用的是aec,不是aecm,即对nlpMode调节)。
[129楼]&&&&&&&
回复 huangqizhen:
您好 huangqizhen:
这段时间很忙没能及时回复。
A1:linux上的接口我没有使用过,不过根据 http://mailman.alsa-project.org/pipermail/alsa-devel/2008-June/008421.html 中的回复所说 —— In the driver implementation level, snd_pcm_delay() simply returns the
difference between appl_ptr and hw_ptr. It means how many samples are
ahead on the buffer from the point currently being played. 看样子 snd_pcm_delay 能够得到播放缓冲的延迟,但还差采集缓冲的所造成的延迟。至于你把delay置为0之后进行测试效果更好,我觉得这个属于硬件个别现象,固定的delay不能达到稳定的回声消除效果。
A2:忽然变小甚至小到听不到,但是声音仍然存在?如果声音存在,只是变小声了,是否是你使用了 AGC 且参数设置除了问题。
[130楼]&&&&&&&
回复 huangqizhen:
这里的一些零星信息也许可以帮到你&
http://www.wavecn.com/content.php?id=198
[131楼]&&&&&&&
回复 huangqizhen:
上文中提到“不要使用 snd_pcm_delay() 来查询播放缓冲区的填充情况。它只能用来作同步用途。”。也许这个才是你延迟计算的罪魁祸首。对于该API我没有话语权,你可以试着在网络上查找更多的信息。
[132楼]&&&&&&&
回复 Bill_Hoo:
您好,Bill
我现在是直接使用WebRtcAec_GetDelayMetrics来获取当前延时偏差。并根据这个偏差来进行微调,效果在声音不大的时候还是比较稳定的。但是,声音一旦想要大起来就出现回音啸叫了(PS:使用电视与高灵敏度麦克风)。现在也没有使用snd_pcm_delay了。webrtc的最佳效果不知是否是这样的,我不太相信只是这样。我想是我没调节好。但是我现在也感觉没什么想法与路子了。
请教请教啊,呵呵。
[133楼]&&&&&&&
声音忽大忽小的现象现在倒是没那么明显了,我是把ns放到aec前面了。之前是ns放在aec的后面。
[134楼]&&&&&&&
另外我在audio_device_alsa_linux.cc中找到这个:
// calculate delay
& & & & _playoutDelay = 0;
& & & & _recordingDelay = 0;
& & & & if (_handlePlayout)
& & & & & err = LATE(snd_pcm_delay)(_handlePlayout,
& & & & & & &_playoutDelay); // returned delay in frames
& & & & & if (err & 0)
& & & & & {
& & & & & & // TODO(xians): Shall we call ErrorRecovery() here?
& & & & & & _playoutDelay = 0;
& & & & & & WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
& & & & & & & & & & & &playout snd_pcm_delay: %s&,
& & & & & & & & & & & LATE(snd_strerror)(err));
& & & & & }
& & & & err = LATE(snd_pcm_delay)(_handleRecord,
& & & & & &_recordingDelay); // returned delay in frames
& & & & if (err & 0)
& & & & & // TODO(xians): Shall we call ErrorRecovery() here?
& & & & & _recordingDelay = 0;
& & & & & WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
& & & & & & & & & &capture snd_pcm_delay: %s&,
& & & & & & & & & LATE(snd_strerror)(err));
所以说snd_pcm_delay也可以对录音计算延时才对的啊?
[137楼]&&&&&&&
你好,我的本科毕业设计是android webrtc的实时视频通讯系统,您能给我点思路吗,我现在不知道要先做什么
[138楼]&&&&&&&
回复 习惯在这里:
您好,不清楚你们的毕设要求是什么,如果只是视频通讯,你只需要把WebRTC在android上整个儿编译出来就OK了。WebRTC的Demo本身就是一个视频通讯系统。已经编译好的Demo你可以在网上搜索得到。
[139楼]&&&&&&&
回复 Bill_Hoo:
bill您好!
现在有个比较急的问题,就是那个声音有的时候会变小,甚至有点失真的现象。我没有使用AGC模块。现在一直找不到问题所在,基本上20个字有1个字声音会变小。很影响体验。求教大侠啊!!!
[140楼]&&&&&&&
回复 huangqizhen:
您好,这几天没有关注博客,您提到的声音有时候会变小,是不是AEC的等级过高导致自己的声音被当做回声一并消掉了?
我最近也遇到一个问题,为了适配各种手机,我找了一个折中的算法进行延迟的计算,但最后的效果就是折中了,双方通话时却能听到令人很不爽的唧唧声,不知你遇到这种问题没有,如何解决的呢?
[141楼]&&&&&&&
您好,bill,年前去做别的事情了,呵呵,所以没能及时关注。
1、声音变小应该是能量过低,比如说站在4米外讲话,造成语音状态检测的误判(然而检测阀&#20540;又不能太低),把较低能量的语音给消除掉了。现在经过调整好很多了,用手机的话应该是没有我这个问题的。
2、您说的唧唧声音是否身处的环境有各种机器的叫声?比如打印机,或着是在工厂里?如果是的话,我也不知道,这是一个难题,目前我也在为这个苦恼。使用低通滤波器,比如4K频率以下的通过,可以降低唧唧叫的几率。(我的采样率是16000)。
如果环境不是的话,那么我觉得还是延时的计算不对了。建议使用WebRtcAec_GetDelayMetrics去估计延时偏差。
[142楼]&&&&&&&
回复 huangqizhen:
您好huangqizhen,我这边声音忽然变小的问题应该和能量没有关系,我都是距离手机十五公分左右测试的,这个距离比正常的视频、语音通话距离还近。这个问题我还要继续观察。
至于唧唧声,我的测试环境除了环境噪声、周围人声就没有别的异常声音了,这个唧唧声应该是AECM算法收敛失败造成的。
我会继续关注这几个问题的。希望大家都能早日解决。
[143楼]&&&&&&&
Bill_Hoo大神你好
我在做linphone上移植NetEQ功能,我想问您一下NetEQ是包括了解码麽?
[144楼]&&&&&&&
回复 lee_fat:
您好lee_fat
由于项目的安排我没能接触到NetEQ模块,所以不敢妄言。
[145楼]&&&&&&&
看了你的文章,也想玩一玩WebRTC,不过死活卡在第一步下载源码...我这里网络环境不稳定,用gclient同步两天了总是会中断。你能帮忙把android上的所有源码打包发给我一下么?我的邮箱是ys.,如果太大的话网盘什么的也可以,多谢了
[146楼]&&&&&&&
回复 nicholashz:
您好 nicholashz,直接 svn 同步 http://webrtc.googlecode.com/svn/trunk/ 主线即可。我没有花时间去同步整 android 的整体工程。
[147楼]&&&&&&&
& 您好,还是关于msInSndCardBuff这个参数的计算。
请问,您在Android中是采取何种方式去获取到音频硬件采集到帧A的时间以及播放出帧B的时间的。Android中好像没有这样的API呢,之前您说在WebRtc 源码中的android-test有那样的处理方法,恕我愚昧,找了很久也就理出个头绪来,故来向您取经求教,谢谢。
[148楼]&&&&&&&
回复 lzg9099:
您好,这个延迟的计算方法可以参考WebRTC主线版本目录&webrtc\modules\audio_device\android\java\src\org\webrtc\voiceengine\
下的WebRTCAudioTrack和WebRTCAudioRecord
具体如何在程序中体现,还需要你自己进行程序结构的调优和测试。
[149楼]&&&&&&&
hello,Bill,我认真的看了前面的所有评论,还是有几个问题想要请教您
1.采集的时候audioRecord.read(tempBuf, 0, buffer_frame_size),tempBuf用short类型和byte类型有区别吗?80HZ 采集,如果用byte类型,buffer_frame_size是传递160吧?如果是short类型,是传递320?
因为我看到前面的很多人都是用short传递到process中的,所以有点蒙,不懂该怎么做才是合适的
2.jni层,aecm process函数像下面这样写有错吗?AudioRecode采集的时候是用byte类型存储的,我直接传递给nearendNoisy,WebRtcAecm_Process需要传递的类型是short*,所以我把jbyte*类型转换成short*,不知道有没有错?还有nrOfSamples传递的是160(80HZ的)?
jint....Process(JNIEnv *env,
& & & & & &jclass jclazz, jint aecmInst,jbyteArray nearendNoisy,jbyteArray nearendClean,jbyteArray out,jint nrOfSamples,jint msInSndCardBuf) {
& & &void* handle = (void*) aecmI
& & &jbyte* nn = (*env)-&GetByteArrayElements(env, nearendNoisy, NULL);
& & &jbyte* nc = (*env)-&GetByteArrayElements(env, nearendClean, NULL);
& & &jbyte* sout = (*env)-&GetByteArrayElements(env, out, NULL);
& & &jint result = 0;
& & &if(nearendClean==NULL)
& & & & & &result= WebRtcAecm_Process(handle,(short *)nn,NULL,(short *)sout,nrOfSamples,msInSndCardBuf);
& & & & & &result= WebRtcAecm_Process(handle,(short *)nn,(short *)nc,(short *)sout,nrOfSamples,msInSndCardBuf);
& & &if (nn != NULL)
& & & & & &(*env)-&ReleaseByteArrayElements(env, nearendNoisy, nn, 0);
& & &if (nc != NULL)
& & & & & & & & &(*env)-&ReleaseByteArrayElements(env, nearendClean, nc, 0);
& & &if (sout != NULL)
& & & & & & & & &(*env)-&ReleaseByteArrayElements(env, out , sout, 0);
3.关于降噪,如果是80HZ,160字节采集的话是要分成两次降噪吗,每次传递80byte?
4.目前我播放和采集都在一个线程中执行,发送和接收在另一个线程,播放的时候去接收队列中取出数据,执行bufferFarend,然后采集,降噪,process,再静音检测,不知道这样的流程是否

我要回帖

更多关于 手机录音降噪处理软件 的文章

 

随机推荐