單獨編譯使用WebRTC的音頻處理模塊


不推薦單獨編譯  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次唧唧聲。二是通話過程中聲音會忽大忽小,目前我是懷疑由AECMdouble 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項目對應目錄中找到需要的文件。

  

前言

   最近一直在搗騰如何在androidiOS上使用GoogleWebRTC——一個無疑大力推動了互聯網即時通信以及VoIP發展的開源項目。(elesos注:連谷歌都訪問不了的國家,活該落后!)

   WebRTC提供一套音頻處理引擎VOE,但VOE在 android 和 iOS 上的整體編譯一直是一個比較繁瑣且惱火的問題,於是單獨提取了VOE中的NS(Noise Suppression 噪聲抑制)、VADVoice Activity Detection 靜音檢測)、AECMAcoustic Echo Canceller for Mobile 聲學回聲消除)以及 AGCAuto Gain Control 自動增益控制)等模塊進行編譯並搗鼓其使用方法。


   經過自己兩月有余的搗騰和測試,終於在 android 和 iOS 上成功編譯出各模塊並在項目中使用了NS/VAD/AECM三大模塊,效果比較不錯。

   回過頭來看看,這幾大模塊的編譯其實非常簡單,不過兩月前的自己也着實為這個花了一番力氣。



   由於幾大模塊的編譯方式相同,故本文僅以 NS 模塊為例,其余模塊請讀者自行摸索和實驗。



Step 1 - 下載 google WebRTC 源碼

   WebRTC目前的開發版主線版本已經到了 r4152 - 3.32,但這幾大模塊並未有大的修改,故本文依舊按bill當時的版本 3.31 進行講解,請自行使用SVN同步以下目錄(至於同步的方法,請自行google):

http://webrtc.googlecode.com/svn/branches/3.31/      可訪問



Step 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源碼外,如果要在androidJava代碼中使用,還需自行編寫JNI包裝文件:

ns_jni_wrapper.c(此為自定義的 jni 包裝文件,詳情請見 此文


ADDED(billhoo - 2013-6-14) 

鑒於有朋友詢問JNI Wrapper的編寫,下面提供NS模塊create以及initialize函數(這兩個函數足以說明問題)的wrapper源碼及注釋,希望對大家有所幫助。更詳細的編寫步驟請參考 Oracle官方文檔 或 此文或 此文



WebRtcNs_Create 包裝函數及注釋

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/***
  * 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".
  * Input:
  *        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 2013-1-29
  */
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 包裝函數及注釋

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
  * 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.
  *
  * Input:
  *        - nsHandler   - Handler of NS instance that should be
  *                        initialized.
  *        - sf          - sampling frequency, only 8000, 16000, 32000
  *                        are available.
  * Output:
  *         nsHandler  - the handler of initialized instance.
  * Return value:
  *         0                - OK
  *         -1               - Error
  *
  * @author billhoo
  * @version 1.0 2013-1-29
  */
JNIEXPORT jint JNICALL
Java_你的類限定名_initiateNSInstance(JNIEnv *env,
         jclass jclazz, jint nsHandler, jlong sf) {
     NsHandle *hNS = (NsHandle*) nsHandler;
     return  WebRtcNs_Init(hNS, sf);
}



[END OF ADDED]





Step 3 - 編譯WebRTC - NS模塊

   此步請參照 bill之前的文章將剛才提取的NS代碼添加進eclipse工程進行編譯即可。以下為NS模塊的Android.mk文件:



1
2
3
4
5
6
7
8
9
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 動態庫拷貝出來以備后續使用。



Step 4 - 加載編譯好的NS模塊動態庫

   接下來只需要按照 此文 的描述在 android JAVA代碼中使用剛才編譯好的 webrtc_ns.so 動態庫便大功告成。



Step 5 - 幾大模塊的使用及注意事項

   前四步已經完成了幾大音頻處理模塊在android上的單獨編譯過程,並分別生成了 webrtc_ns.so、webrtc_vad.so、webrtc_aecm.so 以及 webrtc_agc.so 四個動態庫,下面bill簡要介紹NS、VAD以及AECM三個庫的函數使用方法及注意事項:



5.1 - NS庫函數的使用及注意事項

   這個很簡單,參照 noise_suppression.h 頭文件中對各API的描述即可,首先使用 WebRtcNs_Create 創建NS實體,然后 WebRtcNs_Init 初始化該實體,WebRtcNs_set_policy 設置噪聲抑制的級別bill使用的是最高級別 2,效果比較理想),設置完成后便可調用 WebRtcNs_Process循環10ms8000Hz、16000Hz音頻幀進行NS處理,注意最后別忘了調用 WebRtcNs_Free 將NS實體銷毀。



5.2 - VAD庫函數的使用及注意事項

VAD的使用和NS區別不大,唯一需要注意的是VAD僅僅只是檢測,返回結果1表示VAD檢測此幀為活動幀,0表示此幀為靜音幀,至於判斷為靜音后該進行何種處理,就和你自己的項目相關了。



5.3 - AECM庫函數的使用及注意事項

AECM實體的創建、初始化和銷毀工作與上述相同,之后需要在遠端和近端分別調用WebRtcAecm_BufferFarend   (注:farend遠端)以及 WebRtcAecm_Process,對於AECM的使用,需要注意的重點在於Process函數的參數msInSndCardBuf,該參數在audio_procession.h頭文件中以名為delay的變量呈現,該延遲的計算確為一難點(對於單獨使用AECM模塊來說),不過只要嚴格按照delay的描述進行操作即可。



附:

   其他幾大模塊單獨編譯時需要的源文件列表(所有依賴頭文件略,請自行根據報錯添加):

WebRTC - 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



WebRTC - 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



WebRTC - 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認為靜音的

音頻全部置零然后發送。


出處http://billhoo.blog.51cto.com/2337751/1213801



[3樓]      wanglimingyin  回復
2013-06-14 17:18:33
回復 Bill_Hoo: [2樓]



謝謝你,bill。主要是我對c不熟悉,好久沒用了。現在有個需求就是需要拿到pcm音頻數據然后進行降噪,可是不知到怎么使用里面的方法


[4樓]      [匿名]WebRtc_Aecm  回復
2013-06-26 19:54:24
BillHoo你好, 我是從StackOverflow追到這里來的。有一個問題不知道樓主有沒有遇到過: WebRtcAecm_Process在去掉回聲的同時把與回聲重疊的那部分的原聲也去掉了。
還有一個問題啊,麥克風采集的PCM的buffer是作為WebRtcAecm_Process的第二個參數嗎?不勝感激。


[5樓]樓主      Bill_Hoo  回復
2013-06-26 21:07:14
回復 WebRtc_Aecm: [4樓]



你好,很高興你能追到這里來 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  回復
2013-06-27 09:54:30
回復 Bill_Hoo: [5樓]



多謝你的回復!

回聲和原聲重疊的意思是回聲和原聲同一時刻進入到麥克風,我將它稱之為“重疊”

回環路徑測試是怎樣的場景和配置呢?

我現在的測試環境是這樣搭的: 從手機SD卡中以80字節為單位順序讀取一個pcm文件,送入到揚聲器,同時調用WebRtcAecm_BufferFarend(), 然后讀取麥克風采集到的數據的80字節,調用Process。再將out寫入到aec.pcm文件。分析這個aec.pcm文件時,發現回聲是被消除了,但是與回聲處於同一時刻的原聲也被消除了。

我這種測試思路有問題嗎?

還有一個問題,降噪與否對回聲抑制的效果影響大嗎?

多謝 :)


[7樓]樓主      Bill_Hoo  回復
2013-06-27 11:47:35
回復 WebRtc_Aecm: [6樓]



1.首先,我在stackoverflow里看到你調用時傳的是8000Hz的采樣率,也就是說你每次應該傳入80個采樣點,(編者注:10ms數據需要傳80,1000ms即1s為8000,所以10ms對應80*2=160字節)一個采樣點是2個字節,你應該采集160字節/次才對。
注:  http://stackoverflow.com/questions/17319574/audio-is-also-be-cancelled-which-overlap-with-acoustic-echo-when-using-webrtc-ae/17333797#17333797

2.對於AECM的測試,首先可以使用同一個PCM文件,分別放入FAREND和PROCESS,如果出來的結果接近全零,則驗證你提取的AECM模塊工作正常。

3.在驗證AECM模塊能夠正常工作的前提下,你需要兩台設備,每台設備兩個線程,線程一用來采集和PROCESS,線程二用來播放和FAREND。

4.降噪與否對回聲消除的效果:我的實驗結果為:先消回聲再降噪效果比先降噪再消回聲好。


[8樓]樓主      Bill_Hoo  回復
2013-06-27 12:47:50
回復 WebRtc_Aecm: [6樓]



還有個問題看掉了,我說的回環路徑就是 127.0.0.1 的本機測試。


[9樓]      [匿名]WebRtcAecm  回復
2013-06-27 13:17:45
回復 Bill_Hoo: [8樓]



對於第1點,我也測試過傳160字節,效果和80字節是差不多的。

對於第2點,結果會接近全零,但是在本地也有聲音進入麥克風的情況下,與回聲重疊的那部分原聲也會被消掉(預期應該是不會被消掉的,這和你的回環路徑測試不一樣,因為進入麥克風的"Normal Voice"沒有再次進入揚聲器,也就算不上是回聲,而我猜你的回環路徑測試是會再次被揚聲器播放出來吧?)。

這就是我遇到的最主要問題。AECM是可以工作的,但是結果沒有達到預期。我看了你的StackOverflow的回復,這一點應該是和時延沒有關系的。


[10樓]樓主      Bill_Hoo  回復
2013-06-27 14:05:17
回復 WebRtcAecm: [9樓]



你好,我仔細看了下你的思路



“從手機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  回復
2013-06-27 14:40:17
回復 Bill_Hoo: [10樓]



是啊是啊,就是你說的這個意思。唯一不同的是我放音樂了,也就是說“B”里面有音樂,現在這段音樂與A重疊的部分就被消掉了 :(


[12樓]樓主      Bill_Hoo  回復
2013-06-27 14:54:04
回復 WebRtcAecm: [11樓]



也就是說你的問題不在AECM這個模塊上,而是你的測試思路。

你的問題就在於 —— 你的測試方法測出來本來就應該是這個結果,而你的預期卻錯誤地認為會是另外一個結果。

搞清什么是“原聲”,什么是“回聲”就OK了。
Elesos.com注:他的音樂其實還是 送到揚聲器的聲音A吧,是回聲。是需要消除的。


[13樓]      [匿名]WebRtcAecm  回復
2013-06-27 14:59:41
回復 Bill_Hoo: [12樓]



預期:"AECM的處理結果就是從“A+B”中找到被揚聲器播放出去的“A”並進行消除,留下了“B”"

現在的結果是: B沒有被完全留下,有一部分被消掉了。你假設我沒有說話,實際我放了音樂並且被麥克風采集到了啊,這個音樂不是從揚聲器出來的,這不算回聲嗎?我覺得這個音樂應該是被保留的吧?現在被部分消掉了。


[14樓]樓主      Bill_Hoo  回復
2013-06-27 15:11:10
音樂是連續的音源,和人聲是有很大區別的,連續樂音中很可能某一段的數據被認為是和A中的數據匹配從而導致被消除,如果你非要這樣測,應該在運行時說話,而不是放音樂。


[15樓]樓主      Bill_Hoo  回復
2013-06-27 15:14:19
回復 WebRtcAecm: [13樓]



可以參考一下  http://www.net130.com/netbass/voip/20040008009.htm
回復 Bill_Hoo: [15樓]

注: http://stackoverflow.com/questions/15302339/webrtc-aec-algorithm/16182942#16182942

多謝你的回復 :)
最后一個問題,那個時延是怎么計算的? 我看了你在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)

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().

1). 你是根據這個公式來計算的嗎?
2). 這4個值都是什么意思啊?這段話我看了很多次了,總是看不懂。

2013-06-27 22:13:30
回復 WebRtcAecm: [16樓]

你好,是根據這個來計算,而且需要嚴格根據這個的描述來
t_analyze 表示你對音頻幀A調用 farend 的時刻,t_render 表示[硬件]真正播放出幀A的時刻。
t_capture 表示[硬件]采集到音頻幀B(注意跟A沒關系了)的時刻,t_pull 表示幀B被傳入 Process的時刻。
其實就是計算系統硬件buffer + 軟件 buffer 的總延遲。
這個延遲在android系統中比較大,有100~200左右不等,需要你根據自己的buffer進行計算。計算方法就跟你android上層的buffer設計相關了。



2013-07-01 14:30:12
問下樓主,
我參照樓主的做法,第二步中AECM已經可以了。
2.對於AECM的測試,首先可以使用同一個PCM文件,分別放入FAREND和PROCESS,如果出來的結果接近全零,則驗證你提取的AECM模塊工作正常。

現在我的項目遇到的問題是,本機測試是可以的,但是雙機通訊下,會有非常大的噪聲,是不是AECM的msInSndCardBuf的參數錯了。這個到底該怎么計算呢?
順便問個小白的問題,在雙機通信中,我認為從網絡上接收的音頻為遠端音頻,用WebRtcAecm_BufferFarend,本地錄音的音頻為近端,用process。這個沒錯吧。還有,我並沒有用NS,AGC和其他模塊,自用了AECM。這樣可以嗎?請樓主點播下,多謝。我的QQ:553270954

2013-07-01 18:08:35
回復 cstriker1407: [21樓]

你好:
1.非常大的噪聲:你確定那是噪聲而不是嘯叫么? 我這邊AECM局域網通信沒有你說的很大的噪聲,只是之前由於delay計算錯誤會產生很大的嘯叫。delay的計算公式在audio_procession.h里面說的很清楚,那個延遲你只能根據自己的buffer實現進行計算。
2.遠端數據就是網絡傳過來的數據,近端就是本機采集到的數據。
3.只用AECM模塊是可以的。

2013-07-03 14:31:59
Bill_Hoo:
你好:
  我現在在做回音消除這塊,但是不能准備計算delay的時間,雖然audio_precession.h頭文件中的公式說得很清楚,但是對於android平台的java層始終不能很好的計算出delay的時間,所以想請教一下你在android上層設計buffer並計算出delay時間的解決辦法。

2013-07-03 14:39:21
回復 Bill_Hoo: [22樓]

多謝樓主回復,問題(注: 雙機通訊下,會有非常大的噪聲)我已經發現並解決了。不是webrtc的問題。

2013-07-03 21:08:27
回復 51CTO游客: [23樓]

你好,如果可能,盡量不要在java層做AECM,java到android底層中間還夾着jni,據說還有一個AudioFlinger的延遲,這個我也沒有深入,所以不敢亂講。如果你的項目不是必須在java層做音頻的采集和播放,那么AECM還是放在底層做的好。
如果非要在java層做,delay的計算只有根據你自己的buffer設計來,這個我沒辦法幫上忙,總體思路就是系統底層硬件延遲 + 你的buffer延遲。

2013-08-01 14:25:30
您好 我也在做webrtc的AEC 請問AudioRecord和AudioTrack如何的阻塞時間如何計算

long timestamp = System.nanoTime();
mAudioTrack.write(data, 0, data.length);
long renderTime = System.nanoTime() - timestamp;
native_set_audioRenderBlockTime(renderTime);

請問這樣做能得到准確的阻塞時間嘛

2013-08-02 15:14:00
請問樓上,AECM、VAD、AGC三個模塊使用的順序是怎樣?如果VAD檢測出是靜音模塊,那么AECM該如何調用啊?

2013-08-02 15:33:47
回復 hsfhzkd: [27樓]

VAD檢測出是靜音,你可以采取以下兩種方式:1.不發送這一段靜音的音頻 2.將這一段VAD認為靜音的音頻全部置零然后發送。

2013-08-02 15:34:46
回復 w33222885: [26樓]

你好,你這樣做可以得到 write 的阻塞時間,但這個時間並不是 renderTime(如果你的這個變量表示渲染延遲的話)。

2013-08-05 10:01:54
回復 Bill_Hoo: [29樓]

您好 請問渲染時間是一個固定的延遲嗎,是否跟audioflinger的lantency有關。

2013-08-05 13:28:25
回復 w33222885: [30樓]

據我的實驗結果,該延遲並不是固定的,需要周期性更新。我當時看源碼時,AudioFlinger層的latency沒有辦法在上層獲取,所以沒有考慮這個。不過不知道現在4.1、4.2的源碼發生了怎樣的變化,是否有類似iOS的直接獲取底層硬件延遲的接口我也不清楚。

2013-08-05 13:47:52
回復 Bill_Hoo: [31樓]

現在aecm已經可以工作,但是最后的效果始終有嘶嘶的雜音,不知道您最終的效果如何~

2013-08-06 13:14:27
回復 w33222885: [32樓]

單獨提取的AECM模塊達不到完美的效果,但也不會始終存在嘶嘶聲。也許你可以配合NS和VAD模塊一起使用。

2013-08-21 02:21:29
你好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 是什么?? 多謝了。

2013-08-21 10:15:18
回復 51CTO游客: [34樓]

可以參考下 http://what-when-how.com/voip/wideband-voice-voip/。
以及維基 http://en.wikipedia.org/wiki/H_band
       http://en.wikipedia.org/wiki/L_band
L band  is the 1 to 2  GHz  range of the  radio spectrum .


2013-08-21 22:51:03
回復 Bill_Hoo: [35樓]

多謝,樓主。我的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?

2013-08-22 08:59:28
回復 51CTO游客: [36樓]

早上好, 8KHz sample 直接入 L band 就OK了,我目前最高用到 16KHz,均只入的L band,至於 H band 對於話音的噪聲消除個人覺得應該沒有用處。 不過對於 32000 Hz 的sample就說不好了,一般單聲道的應用可能都不會觸及到 Hband,如果你發現在某個環境下會用到 H Band,還請回來告知一聲哦。

2013-08-22 17:23:51
Bill_Hoo你好,我又來了。之前一個月在做voip的其他功能,現在又開始做AEC了。現在遇到的問題是怎么計算系統硬件buffer的延遲,這個Android有什么接口嗎?

2013-08-22 18:05:12
回復 51CTO游客: [38樓]

你好,iOS有API直接提供硬件層的采集和播放延遲,但據我的實踐和了解,android目前並沒有提供直接的硬件延遲。因此你需要根據自己的上層工作buffer對底層buffer進行估算,從而得到一個較為精確的硬件延遲。但該延遲始終不精確,因為經過了幾處buffer的緩沖。 如果要更精確,可以采用OpenSE等JNI層的采集庫,而不是android上層的AudioRecord以及AudioTrack。不知道這樣回答是否解決了你的問題。

2013-08-23 02:10:14
回復 Bill_Hoo: [37樓]

恩, 謝拉。 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*) nsHandler;

     // 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);

     return result;
}

2013-08-23 22:34:41
回復 Bill_Hoo: [37樓]

原來是WebRtcNs_Process的input放錯了。 問題已解決。

2013-08-23 22:37:50
回復 51CTO游客: [40樓]

你好,如果按照我的調用方式,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呢。

2013-08-23 22:49:02
回復 51CTO游客: [41樓]

哦哈哈,看來我在打字的時候你也在打字..................... 恭喜、

2013-09-02 15:22:24
回復 Bill_Hoo: [17樓]

"
你好,是根據這個來計算,而且需要嚴格根據這個的描述來
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困擾挺久了。不勝感謝~

2013-09-02 19:07:20
Hi,Bill!
我最近在做一個Android的通過藍牙傳遞語音數據的項目,用了WebRTC的NS后聲音變得沙啞了。
我的實現過程很簡單,先用AudioRecord錄音,取出pcm數據,然后傳給ns_process,最好編碼后發出去,我為了排除其它影響,特地在ns_process后將數據寫入文件播放,跟傳送到遠端后類似,都是變沙啞了。
另外,不加NS,整個通話聲音都是正常的,加上AECM也沒有問題。
你有沒有碰到類似的問題,NS需要什么特殊設置嗎,還是我這個使用過程有問題?
麻煩你有空幫忙看看,謝謝!



2013-09-03 11:32:59
樓上可以試下nsx, ns是浮點運算, nsx是定點運算。我用 nsx效果挺好的。

2013-09-03 16:03:50
回復 51CTO游客: [46樓]

謝謝!找到原因了,不過不是因為浮點的原因。
我的參數:
16 bit encoding 
8000 Hz sample rate
Mono (1 channel)
20ms一幀,故每幀為160字節,之前將160字節直接傳給NS,而NS每次處理10ms的數據,現在我分成兩次,每次傳80字節就沒有問題了。

2013-09-03 17:50:04
回復 51CTO游客: [44樓]

你好:
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, int, int)

希望以上闡述能解答你的疑惑。


2013-09-03 17:53:05
回復 51CTO游客: [46樓]

這段時間都沒接觸WebRTC音頻部分了,NS模塊都有定點了 - -+


2013-09-22 11:33:59
回復 Bill_Hoo: [17樓]

你好博主,請教一下您在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同理,您是怎么處理這兩個時間點的呢?

2013-09-23 14:04:58
回復 emuman: [53樓]

你好emuman,
A1:四個時間點均在調用 之前得到即可。
A2:我們確實不能直接得到第一個采樣點的時間,但你可以根據自己上層的 buffer 大小以及底層的硬件 buffer 大小估算出緩沖區中總共緩沖了多少幀,這樣一來便可計算出第一個采樣點的時間。

2013-09-25 15:02:09
回復 Bill_Hoo: [54樓]

首先多謝博主答復,然后繼續咨詢一下。
這幾天我在android上試驗,用jni+opensles來錄音、放音,發現理論和實際情況大有不同。
參數是8khz、mono、16bit錄音和放音,每個語音幀80個sample(10ms)。
1、首先單個延遲很小,t_render - t_analyze大約在25-55ms區間內,t_pull - t_capture對我來說約等於0,因為我采集后馬上就進行process了;和博主說的大約100--200ms差距比較大。此外我是不斷重新計算延遲值的,博主您是計算出一個值就固定使用還是也不斷計算?
2、opensles的錄音放音情況是:某個時間段內得到m個錄音的語音幀,然后下一個時間段播放n個語音幀,如此反復。所以如果不變通,就會變成調用m次WebRtcAecm_BufferFarend()后再調用n次WebRtcAecm_Process,4個時間點怎么確定就很曖昧了:因為延遲值其實是給錄音后的process用的,所以屬於錄音的t_capture和t_pull的時間點是明確的,但是放音的t_analyze和t_render用m個語音幀里的哪個呢?第一個、最后一個、平均值?我試驗了最后一個(距離錄音動作最近的上個播放延遲),發現出來的結果不對;直覺上第一個、平均值也應該不對。 
最后,從多次統計結果發現,m和n是同一個數量級,差距不大(這是顯然的)。
3、從我這段時間的測試數據看,android機系統有自己的opensles緩沖區,比如錄音,我設置錄音緩沖區是80個sample,系統會先錄制一段時間,數據充滿它自己的緩沖區(系統緩沖區)后再調用回調函數拷貝到我設置的緩沖區(用戶緩沖區),放音也同理(提供數據足夠快的情況下),這就解釋了上面第二點的情況:先提供m個錄音語音幀,再消費n個放音語音幀。
這里有個問題,我翻遍開發文檔發現系統沒提供接口來得到系統內置緩沖區大小,我甚至在google play上找到個audio buffer size的app,它用自己的算法來計算系統opensles的內置緩沖區和采樣率大小,但我的測試機算出來的結果和我的測試數據又沒法對上。java層的AudioRecord.getMinBufferSize()以及api17后倒有個相關的AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER,這兩個試驗過發現也不對。
4、針對2和3的情況,我設想過能不能把m個錄音幀當一個大幀、n個放音幀當一個大幀來處理。WebRtcAecm_BufferFarend()好像可以累積數據,只要長度不超過它的緩沖區最大值就好;但WebRtcAecm_Process()只能傳進80或160個sample,這就矛盾了。
5、好像蘋果機的機制是錄一個幀再放一個幀,非常符合webrtc aecm的理論,甚至它都提供內置的回音處理了。android在4.0后雖說也提供內置的回音處理,但效果很多時候等於沒有。這點是我的牢騷了,android還有很多地方要完善。

對opensles的這種情況,博主有沒有什么經驗?甚至提供個思考的方向都可以,我現在束手無策了

2013-09-26 10:02:34
回復 emuman: [55樓]

emuman你好:
A1:100~200ms延遲是在 Java 層使用AudioRecord/AudioTrack得到的,你使用的opensles在JNI層了,延遲在25-55ms正常。t_pull - t_capture不一定為0,你雖然采集之后馬上就調用 process,但是t_captrue是硬件采集到某幀的時刻,該幀從硬件進入底層buffer,然后你的 opensles 從底層buffer讀取是有延遲的,這個延遲要算,在JNI層可能很小,但是也要算。最后延遲值的確需要不斷更新。WebRTC源碼里我看了下好像是每一秒更新一次,我自己的實驗結果1秒太久了,我是每發送一幀就更新延遲值。

A2:當初選型的時候我沒用OpenslES,所以我不清楚他的工作流程。從你對他的描述中發現問題在於其錄音、放音流程導致四個時間點不好捕捉,他自己緩存了錄音幀和播放幀,但我們又沒辦法求得緩存大小以最終求得緩存延遲。個人暫時沒想到好的直接可行的解決辦法,因為如果我們沒辦法得知他緩存了多少數據,那如何求得對應的延遲?不過我隱約有個思路是我們需要自己設計buffer來統一緩存數據,這樣便可計算,但實現方案我沒有思路。

A3:據你的描述,“我設置錄音緩沖區是80個sample,系統會先錄制一段時間,數據充滿它自己的緩沖區(系統緩沖區)后再調用回調函數拷貝到我設置的緩沖區(用戶緩沖區),放音也同理”。既然你可以設置緩沖區大小,為何不能計算出延遲呢?這點需要你再加以闡釋。其次,AudioRecord.getMinBufferSize()所獲得的大小是系統最小錄制緩沖區大小(如果你沒有其他設置的話),這個值你需要在構造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 市場了,跑題了- -、

希望以上回答能幫助你打開思路,我還有哪里沒講清楚的請留言。

2013-09-26 10:36:29
你好博主,感謝你的回復。
關於3,“既然你可以設置緩沖區大小,為何不能計算出延遲呢”,這里沒講清楚,opensles里初始化錄音器和播放器時必須指定緩沖區大小以及個數,自己設置的緩沖區指的是這個用戶緩沖區,它的延遲是可以計算的,但系統還有個緩沖區,那個現在看來無法直接取到它的大小(也就是延遲),問題是在這里。
opensles的工作方式里可以設置成給錄音/放音提供多個一樣大小的緩沖區(估計是為了數據處理連續性),這個緩沖區只是用戶緩沖區而非系統緩沖區,然后指定錄音/放音回調函數,每次回調就提供/消費一個用戶緩沖區數據。
昨晚我有了新思路,這兩天繼續試驗,有什么進展再過來討論。

2013-09-26 19:46:52
你好,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中,為什么?服務器端指的是什么?多點通信中負責混音的類似H.323中的MCU嗎?超級節點?
4.AEC可以修改為44.1K采樣率嗎?Echo Tail Length想要修改的話要動哪里?
不好意思,一口氣問了這么多,還望博主給予指點。
多謝!

2013-09-29 15:01:55
回復 emuman: [57樓]

好的,祝你成功。

2013-09-29 17:09:05
回復 Suitjune: [58樓]

你好 Suitjune, 我最近很忙,所以沒辦法詳細回復你,抱歉。


2013-10-17 11:29:47
能再分析下neteq模塊嗎,貌似對音頻質量有一定的提高

2013-10-18 08:52:14
回復 it206: [62樓]

it206 你好,neteq模塊我還沒有涉及,如果有可能涉及到,我會分享在這里的。謝謝你的建議 :)

2013-10-20 15:22:04
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,那輸出也是嗡嗡的;
這可能是什么問題導致的呢?謝謝!

2013-10-20 16:23:42
上面的問題應該是我自己弄粗了。

2013-10-21 21:33:39
樓主,您好,最近項目中要用到AECM,我在按照樓主提供的AECM模塊源文件進行編譯的時候(頭文件都已添加),出現函數缺失的錯誤,具體的錯誤提示為“undefined reference to 'WebRtcSpl_MaxAbsValueW16'”...“undefined reference to 'WebRtcSpl_RealForwardFFT'“等等錯誤。我的解決方法是找到這些函數所在的源文件並添加到工程中,生成的庫之后,編寫Demo按照樓主方法進行檢測,讀取相同音頻文件時依然有原聲。樓主能否給個生成AECM庫的完整文件,QQ 893077446,萬分感謝。

2013-10-23 23:35:04
謝謝您的帖子!我正在使用webRTC中的AEC模塊、我想問一次WebRtcAec_BufferFarend和WebRtcAec_Process中的音頻信號的格式(就是nearend.pcm和farend.pam的格式):我現在是用:
8000HZ採樣、Signed-16 PCM、LITTLE_ENDIAN
我用audacity錄的farend.pcm、然後(僅為測試)我把farend.pcm直接拷貝生成nearend.pcm、這樣我希望AEC處理後生成的output.pcm應該不含有聲音信號(全部過濾)、但是我得到的output.pcm是很大的噪聲、所以我懷疑是我爸音頻格式設錯了。

請問:
輸入的farend.pcm和nearend.pcm的格式是8000HZ採樣、Signed-16 PCM、LITTLE_ENDIAN嗎?
AEC處理後輸出的output.pcm的格式也是8000HZ採樣、Signed-16 PCM、LITTLE_ENDIAN嗎?

另外附上我的測試代碼(從sample中拷貝的)、麻煩您幫我看一下問題在哪裡、謝謝!
。。。
     void * haec = NULL;       

     if( 0!=WebRtcAec_Create(&haec) ) 
           printf("create error\n"); 

     if( 0!=WebRtcAec_Init(haec,8000,8000) )
           printf("init error\n");

     short farend[160],nearend[160],output[160];

     FILE *ffar, *fnear,*foutput;
     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);
。。。

2013-10-24 02:34:44
問題已經找到了(粗心大意忘記用2進制來讀寫測試文件了)。現在把farend.pcm中的語音以100ms延遲混入nearend.pcm中,再用WebRtcAec_Process(haec,nearend,NULL,output,NULL,160,100,0)來處理,基本可以去掉原來farend.pcm中的語音(迴聲),再次感謝您的帖子!


2013-10-24 22:16:42
謝謝!我還想問您一個問題:在昨天的測試中,我剛開始使用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,16000, 16000)
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都報錯(返回值不為0),我想問一下:是否這2個函數能夠處理的sample數目最大就是160?您在處理16000HZ/Signed-16數據的時候、每次處理的是10ms(160)還是20ms(320),請問這個sample數目和消除迴聲的效果有關係嗎?謝謝!

2013-10-25 03:21:13
剛才看見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)、謝謝!

2013-10-25 08:53:35
回復 米牛牛: [71樓]

您好米牛牛,16000Hz,10ms就是 160 個 sample,您最後是算對了的。

2013-10-29 21:07:14
hi, bill
最近也在研究webRTC中的AEC模塊,有個問題請教下
WebRtcAec_BufferFarend和WebRtcAec_Process中的參數farend和nearend是不是分別表示揚聲器要發出的原始音頻數據和麥克風錄制的原始音頻數據(這個有可能包括揚聲器的回聲)?另外參數 nrOfSamples是不是表示這兩個函數一次最大處理的數據塊?即只有80和160這兩個

2013-10-29 21:57:08
您好!我還想問一下關於drift compensation的問題:WebRtcAec_Process最後的那個skew參數、我現在一般都設為0、請問這個參數需要根據硬件的情況進行調整嗎?還是說都設為0?謝謝!

2013-10-29 22:12:10
這裡有一個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?謝謝!


2013-10-30 09:28:28
回復 米牛牛: [75樓]

這個應該是對於不同平台的時鍾補償吧,因為不同平台的時鍾不一樣,會導致同頻率有差異

2013-10-30 17:40:27
回復 icebery425: [73樓]

您好icebery,
farend是遠端數據,即VoIP通信中接收到的對端的、即將被本地揚聲器播放的音頻A。
nearend是本地數據,即剛剛被本地麥克風采集到的音頻B,而音頻B可能包含上述音頻A(麥克風采集到了揚聲器播放的A),於是 A和B 一起發送給遠端,致使遠端又聽到了自己剛才發送的A,也就產生了所謂的回聲。
nrOfSamples 可以這么理解,具體的含義WebRTC源碼里有說明的 :)

2013-10-30 17:43:46
回復 米牛牛: [74樓]

您好米牛牛,該參數在理想情況下的確為0,但硬件不同,對音頻進行渲染和播放的延遲就有所差異,這個差異在我之前的android設備上達到了100~200ms不等,如果始終保持為0,僅根據我的實踐結果,將得不到好的回聲消除效果。以我個人的經驗來說,該參數是需要實時更新的。 :)
注:博主用繁體回復,真是很好的一個人啊

2013-10-30 19:56:18
hi, bill
最近我在研究一個消除超聲波回聲的項目,用WebRTC中的AEC時,發現在調用WebRtcAec_Init時,參數有一個需要傳遞采樣數據頻率只有8000, 16000,32000hz這幾種頻率,我這邊需要用到的采樣數據頻率為44100,當傳這個值是就出錯,應該是表示不能傳其它頻率的值,或更高的值,想問下,你有沒碰到過這樣的問題,或者類似的情況,需要消除超聲波的回聲?

2013-10-30 23:19:41
回復 Bill_Hoo: [78樓]

謝謝您的回復!您說的要實時更新的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參數的步驟嗎?謝謝!

2013-10-31 09:08:55
回復 米牛牛: [80樓]

hi, 米牛牛,你有沒償試做過對44.1KHz采樣數據的消回聲事情?
見96樓

2013-10-31 09:16:50
回復 米牛牛: [80樓]

您好米牛牛:
我很抱歉的發現我們討論的不是一個模塊。我說的是手機上進行AEC處理的模塊“AECM”,而你使用的是PC端的“AEC”模塊。手機上的AECM模塊是沒有skew參數的,我想這也是手機CPU瓶頸所致。而PC端的接口會有skew參數。這個參數我沒有研究。只是當初在手機上使用PC端模塊AEC時,該參數被我設置為0。之後就沒有對這個參數做任何學習了。


2013-11-01 02:03:13
回復 米牛牛: [84樓]


但是我發現一個情況:就是當我把AEC初始化為16K的時候、依然可以處理8K的數據(這時每次處理的數據依然是160個samples、即變為20ms的8K數據)、不知是否對您的44K項目有用

2013-11-01 10:59:40
Bill_Hoo,請教你一下,我在android上應用了那個aecm模塊,回音消除的效果不錯,但是,如果一個語音過長的話,比如說一次說七八個字,經過回音消除后,出現了語音丟失的情況,即第四五個字會丟失或者無法聽清楚,不知道這個是什么原因呢,是和參數的設置有關嗎?我的msInSndCardBuff設為240左右?這個問題困擾了好久了,最近一直在看源代碼也沒啥頭緒。

2013-11-01 11:21:14
回復 lzg9099: [87樓]

您好lzg9099:
我在想,你是一直使用240這個定值么?再者,回聲消除的級別是否開到了最大?降一個級別試試。如果你能提供更明確的信息,也許我們可以找到問題所在。

2013-11-01 11:50:49
回復 lzg9099: [87樓]

還有一個問題我突然想到,你是不是在做本機的回環(127.0.0.1)測試? 如果是的話,吞字是正常的。

2013-11-01 13:23:24
回復 Bill_Hoo: [89樓]

感謝你的回復:
1,我是一直使用的240這個定值,我換了幾個手機測試時都是用的240,效果都很好,要是將該值設為其他的話,聲音要么有雜音,要么就聽不到聲音了;2,關於回音消除的級別,我沒有去設置,那應該是默認的級別吧,這個級別我也沒弄太清楚;3,關於測試,我只是寫了一個demo,通過將AudioRecord采集的聲音,傳給aecProcess()方法進行回音消除,然后利用AudioTrack將處理過后的聲音播放出來,播放以后調用aecmBufferFarend()方法將處理后的聲音處理一下。這樣測試時,播放出來的聲音是沒有回音的,但是出現了吞字狀況。
2013-11-02 07:43:28
Bill_Hoo 高手,你好,我按照你的思想把aecm抽取出來編譯成so,通過jni給Android調用,回音消除效果如下:如果是前置喇叭(聽筒)效果完美,如果是后置喇叭(播放音樂用的)效果不如前置喇叭,總體來說還不錯。但是現在出現一個問題就是 當選擇后置喇叭作為輸出時,如果在一個很安靜的房間,靜靜的幾分鍾不說話,就會產生嘯叫,如果出現嘯叫后就說話,這嘯叫就立刻消失。想請教一下,這個嘯叫怎么產生的,還有怎么樣可以解決,你的軟件會產生這樣情況嗎? 恭候QQ上討論 扣扣 1281200395

2013-11-02 21:06:13
回復 AudioAEC: [91樓]

你好,首先,聽筒放音是不會存在聲學回聲的。聲學回聲之所以產生,是因為揚聲器播放出來的聲音又被麥克風采集回去。而聽筒播放的聲音麥克風是采不到的。
其次,嘯叫的問題需要借助 VAD 來進行靜音檢測,在靜音的環境下就不需要向揚聲器傳遞音頻進行播放了。


2013-11-04 19:20:42
您好,我的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中。

2013-11-05 16:34:38
回復 icebery425: [81樓]

您好 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
* states.

你所說的44.1應該會被處理掉的,具體的我還在看。

2013-11-06 22:40:50
想問下,vad模塊的使用,為什么我的WebRtcVad_Process總是返回1,即使不說話時也是這樣

2013-11-06 22:58:36
回復 Bill_Hoo: [96樓]

您好,delay的計算方法在之前的討論中已經討論過了。我看了下你的計算,應該還沒有計算底層 buffer 緩沖的數據。最近我在看 APM 了,建議您參考下 audio_device_jni_android.cc 中的實現。

2013-11-06 22:59:42
回復 it206: [97樓]

您好,您給的條件好像少了點,我沒辦法判斷返回值異常的原因。也許你的JNI實現有bug?也許VAD模式開到了最高級別?級別越高,返回1的概率越大。

2013-11-07 10:52:45
我是8000采樣,pcm16,每次處理320 byte數據,局域網自環或者2台手機udp互通,情況一樣,WebRtcVad_Create,VadApi_Init 都正常返回0,vad模式設置0或者最高都試過,在編碼之前做靜音檢測,如下
    for(int i = 0; i < 2; ++i)
    {
      result = vad.VadApiValidRateAndFrameLength(8000, 160);
      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

感覺這樣使用沒有問題,但是無法檢測靜音

2013-11-08 23:40:35
我還有個問題想問一下:我在實驗中想處理2種迴聲:
第一種是別人的聲音進來後我不把它發出去(由我的麥克風造成的)、這種迴聲延時較短
第二種是我的聲音發出去之後又被別人echo回來、我不想聽到自己的聲音、這種迴聲延時較長

我現在可以消除第一種迴聲,但是第二種基本無法消除:請問你們有用webRTC-GIPS的這個API可以成功消除第二種迴聲的嗎?謝謝!

我現在的思路是:只要我能夠正確估算出延時、我就可以先buffer一段數據、然後在合適的時候再調用WebRtcAec_BufferFarend、如果這個思路可行的話、延時可能可以用某種算法(比如卡爾曼濾波)來自適應跟蹤、不知這種思路是否可行?謝謝!

2013-11-09 03:09:53
另外請問一下:這篇論文討論的是不是我在前面說的第二種迴聲(就是我發出去的然後又返回來)?謝謝!
http://wenku.baidu.com/view/403b6739376baf1ffc4fadfc.html

2013-11-09 11:06:49
回復 米牛牛: [101樓]

您好米牛牛:
不知道你的實驗是基於PC的還是手機的?
首先我們討論的是VoIP中的聲學回聲的消除。WEBRTC的API足以保證聲學回聲的消除(只是如果延遲處理的不好,會有部份正反饋嘯叫)。
就我目前的知識水平來說,我認為你說的第一種情況不會產生、至少不會影響到正常通話的聲學回聲。麥克風造成的應該是硬件回聲吧?這跟WebRTC討論的聲學回聲應該不是一個範疇。
第二種情況,是別人開了揚聲器外放,如果沒有做回聲消除,外放的聲音會被他的麥克風再次採集並發送給我們,這是聲學回聲的範疇,WebRTC是可以很好處理的。
如果你是基於PC的實驗,WebRTC上Windows端的回聲消除做得還是不錯的,他的延遲可以根據系統API直接計算出來,這個過程WebRTC - win 的Demo中已經有了,可以直接參考著做。
如果你是基於手機的VoIP應用,本文提供的方法可以消除90%的回聲,剩餘的10%是因為delay計算的不准確導致的正反饋嘯叫。這一點我現在也正在努力,目前的思路是上升一個級別的API,使用WebRTC - APM 並使用底層的OpenSLES進行音頻的播放和採集,這樣最大限度的減小延遲的誤算。

2013-11-10 16:08:07
Bill_Hoo,你好。
我現在也在做Aecm這塊,我這邊編譯后,在PC上測試,一點效果都沒有,用PCM文件測試,聲音都變得很糟。是不是編譯的時候需要打開什么宏啊?另外,你能提供一個Demo嗎?
謝謝。

2013-11-11 00:41:41
樓主你好,請問如何利用 web rtc的AEC模塊在本地采集音頻並播放,從而驗證消除回聲的功能。我在使用windows 下的directshow 音頻采集和 播放,在 Audio Capture 和 DSound renderer之間加入了自己寫的AEC filter(內部利用Webrtc AEC來實現), 在AEC Filter內部 設置緩沖區存放之前的幾個聲音buffer, 同時將新來的buffer和老的buffer做回聲消除。效果很差,仍然會出現回聲嘯叫。請問樓主這種方案可行么?問題出在什么地方?謝謝~
2013-11-11 00:44:58
樓主你好,請問使用webrtc 的回聲消除功能,只需要使用它的 AEC 部分,編譯成庫文件即可么?還是需要整個了解設置 webrtc 的 Audio Processing 模塊來使用(我看網上好多這么寫的,但都沒有實例)?希望樓主有時間解答一下,謝謝~

2013-11-11 12:03:08
樓主 你好!一直在關注你的回復,我現在在linux平台上測試webrtc的aecm模塊,現在苦於延時的計算,我用的是alsa的接口aplay和arecord來進行放音和錄音,很難計算出第一個幀從硬件放出來的時間和第一個幀被硬件采集到的時間,系統貌似沒有提供合適的函數來計算這些時間,請問樓主能否給一些建議,非常感謝!

2013-11-12 02:27:53
回復 LuoZH: [104樓]


Bill_Hoo您好,

我在測試中發現2種迴聲都是存在的、具體如下:
第一種迴聲是我自己的設備產生的:即別人的聲音進到我的揚聲器後、又被我的麥克風捕獲後發給別人、這種迴聲延時較短。這時AEC的目的是不想讓別人(因為我的設別)而聽到他自己的聲音、這時farend是我收到的語音、nearend是我即將要發出的語音
第二種迴聲是別人的設備產生的:即我的聲音進到別人的揚聲器後、又被別人的麥克風捕獲後發給我、這種迴聲延時較長。這時AEC的目的是我自己不想因為別人的設備而聽到我自己的聲音、這時farend是我即將要發出的語音、nearend是我收到的語音

我發現因為第一種情況延時較短所以AEC效果較好;而第二種情況延時較短所以AEC效果相對較差;請問您處理的是哪一種情況?您計算出來的延時大概是多少毫秒?能否告訴我一下您通常處理的這個延時數?謝謝!

2013-11-12 09:27:26
回復 米牛牛: [108樓]

您好米牛牛:
我懂到您的意思了,您對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處理能力也占一部份因素。延遲爲什麽會差距這麼大,就我目前的知識水品無法說清楚。

2013-11-12 09:33:52
回復 linux_aecm: [107樓]

您好:
linux上的ALSA接口我沒有測試過,我這段時間在看APM,好像針對ALSA有單獨的編譯選項,具體的我沒有深究。這里僅說一下android上延遲的計算。android上也沒有直接獲取延遲的API,不過可以通過得到底層緩沖區的大小和當前播放的位置從而估算出緩沖區造成的延遲,這一計算的具體細節我昨天剛在WebRTC源碼的android_test中找到。不知道linux上的接口是否有提供關於底層緩沖buffer大小的信息?

2013-11-12 09:37:57
回復 Sea_biscuit: [105樓]

您好Sea_biscuit:
PC上的AEC我沒有涉足,不敢亂講。不過PC上好像不用單獨把AEC提取出來,因為沒有CPU的限制,PC上是可以直接把WebRTC-WIN編譯出來跑的。
至於您的第二個問題,在手機上,AECM模塊是可以單獨使用的。如果延遲計算的好,效果是可以接受的、不會產生反感情緒,但不夠完美。因此近段時間我也一直在倒騰APM,現已經完成了APM整體模塊的編譯和測試,只是如何調優我還在倒騰中,所以也不敢亂講。

2013-11-12 09:42:25
回復 it206: [100樓]

您好,初步看了下代碼應該是沒有問題的。建議您進行底層的調試以求找到問題所在。調試方法博客里有寫。希望對您有所幫助。

2013-11-12 09:44:14
回復 LuoZH: [104樓]

您好,AECM是用於手機等移動設備的回聲消除模塊。PC您應該使用AEC才對。至於編譯選項,不知道您是在PC環境下(WIN32宏)還是在linux環境下(WEBRTC_ANDROID等宏)編譯的?

2013-11-12 12:07:10
回復 Bill_Hoo: [111樓]

樓主你好,謝謝你的回復。還有一些問題,就是你說的 PC上可以直接將 WebRTc-WIN編譯出來跑是什么意思,是整個P2p的聊天系統么? 還有就是樓主能否講一下如何將整個APM 編譯使用么,是做成庫還是啥?謝謝~

2013-11-12 18:18:24
回復 Sea_biscuit: [114樓]

您好,PC上WebRTC是有完整的DEMO的,只要編譯出來就可以跑,然后你可以根據自己的需要進行提取。具體的我沒有親自實驗過所以就不亂講了。APM是可以直接編譯成庫進行使用的。

2013-11-12 20:36:11
回復 Bill_Hoo: [115樓]

樓主你好,謝謝你的回復。現在還有個問題麻煩你一下下,就是我編譯出來 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)
就是這個。
請問樓主問題出在什么地方? 是使用方法不對還是什么,謝謝~

2013-11-12 21:20:07
回復 Sea_biscuit: [116樓]

您好,我沒有編譯PC上的APM,不過這個問題您可以借助GOOGLE進行分析解決,直接GOOGLE:error LNK2019:http://www.cppblog.com/longshen/archive/2010/04/02/111418.html

2013-11-12 22:46:59
回復 Bill_Hoo: [117樓]

謝謝樓主,您真是太耐心了~謝謝,問題我自己解決了。使用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")

2013-11-12 23:02:27
回復 Bill_Hoo: [110樓]

Bill_Hoo您好!是的一般都是處理第一種迴聲(我自己的設備產生的)、並且如果每一個設備都把自己的迴聲消除之後、就不會存在第二種迴聲了。只有當通話對方(或視頻會議中某一個或某幾個與會者)的設備沒有消除自己的迴聲時、別人才會聽到自己的迴聲(第二種)、正如您所說的這種情況比較少見、並且延時也較長不太容易處理(視不同的網絡情況、延時可能會超過1000ms)。

再次感謝您提供的手機延時數據!

2013-11-13 09:10:15
回復 米牛牛: [119樓]

您好米牛牛,您所說的第二種回聲是我沒有考慮到的。我一直假設的是每個設備都能自己把回聲處理掉,也就是說我假設參與會畫的雙方或多方都已經開啟了AEC模塊。感謝您提出的新思路。預祝您的試驗成功。

2013-11-14 10:49:31
回復 Bill_Hoo: [113樓]

Bill_Hoo你好!
你這篇文章幫我了大忙,我是在Linux下編譯的,按你的方法編譯了。之前播放和錄音是在兩個線程,然后沒有正確的計算延時,所以效果很差,現在播放和錄音放在一個線程。將buffer調整到盡量小,現在,消除效果很好。下下一步是在安卓和winCE上使用。
很感謝你們這些願意分享知識的人,如果Lz后續能夠繼續完善后續的研究結果,一定會幫助許多人的。
謝謝Bill_Hoo.

2013-11-14 12:51:34
回復 LuoZH: [122樓]

您好,感謝您的反饋。如果您願意,也可以將您在Linux上的經驗寫成博文分享給大家 :) 比如您提到的播放和錄音在同一個線程里進行。我現在在android上仍然是兩個線程,並且為計算延遲也花了不少精力,如果您的方法更有效,我想這對大家后續的開發都有好處。

2013-11-14 15:51:06
回復 Bill_Hoo: [123樓]

Bill_Hoo你好!
我這個因為場景特殊,所以才能在一個線程內播放和錄音。
編譯的AECM,使用了Linux的OSS接口,可以使用ioctl直接修改buffer(這個網上資料很豐富)。我是使用一個線程進行接收和發送。接收到數據后直接將數據放入一個隊列,然后到另一個隊列去取數據來發送。另一個線程處理錄音和播放,線程首先去接收隊列取數據,放入WebRtcAecm_Farend,並播放,然后立即錄音,將錄到的數據放入WebRtcAecm_Process進行處理,處理之后放入發送隊列。你提到adnroid_test里面有對延遲的計算,但webRTC類太多層了,我找了很久也沒找到這個計算,不知你能否對這部分計算的相關源碼進行更多的講解。

2013-11-14 16:20:46
回復 LuoZH: [124樓]

您好,android_test 中的延遲仍然使用的是2012年的版本,錄制延遲僅僅采用了一個粗略的固定值,播放延遲的計算方法我比較贊同,也是我自己使用的方法,就是通過android AudioTrack的API估算出底層buffer緩沖的數據延遲。總的來說,仍然是盡可能嚴格地按照前面所說的那個延遲計算公式進行計算,計算得越嚴格,效果越好。

2013-11-14 18:00:48
回復 Bill_Hoo: [125樓]

你好,可否把你計算的這部分代碼發給我?我的Email是LuoZhongYao@gmail.com

2013-11-15 08:53:48
回復 LuoZH: [126樓]

您好,本文僅討論實現的思路,代碼已經涉及實現細節,有所不便,都是技術人員,請諒解。 :)

2013-11-26 09:43:27
您好,看了您的博文很有啟發,所以自己也弄了幾個模塊,編譯成了.so文件。aec我也使用起來了,但現在我被兩個問題所困擾:
1,我是在linux下的alsa環境下作的,所以測延時用了snd_pcm_delay函數。計算出來總共有60多毫秒,去回聲效果還行,但是我把其中一台的delay設成0之后去回聲效果反而還更好了些。這個就有點納悶了,難道delay計算有錯?
2,另一個問題是我在通話的過程當中偶爾會有聲音忽然變小的現象出現,甚至小到聽不到。然后又會恢復。我感覺有點像是回聲消除的太多,把我說話的原音也消除了些。但是我把回音消除的級別降低后現象反而還更不好(我用的是aec,不是aecm,即對nlpMode調節)。

2013-12-04 20:15:39
回復 huangqizhen: [128樓]

您好 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 且參數設置除了問題。

2013-12-04 20:18:05
回復 huangqizhen: [128樓]

這里的一些零星信息也許可以幫到你 
http://www.wavecn.com/content.php?id=198

2013-12-04 20:21:29
回復 huangqizhen: [128樓]

上文中提到“不要使用 snd_pcm_delay() 來查詢播放緩沖區的填充情況。它只能用來作同步用途。”。也許這個才是你延遲計算的罪魁禍首。對於該API我沒有話語權,你可以試着在網絡上查找更多的信息。

2013-12-05 10:14:52
回復 Bill_Hoo: [130樓]

您好,Bill
我現在是直接使用WebRtcAec_GetDelayMetrics來獲取當前延時偏差。並根據這個偏差來進行微調,效果在聲音不大的時候還是比較穩定的。但是,聲音一旦想要大起來就出現回音嘯叫了(PS:使用電視與高靈敏度麥克風)。現在也沒有使用snd_pcm_delay了。webrtc的最佳效果不知是否是這樣的,我不太相信只是這樣。我想是我沒調節好。但是我現在也感覺沒什么想法與路子了。
請教請教啊,呵呵。

2013-12-05 11:09:03
聲音忽大忽小的現象現在倒是沒那么明顯了,我是把ns放到aec前面了。之前是ns放在aec的后面。

2013-12-05 11:12:02
另外我在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也可以對錄音計算延時才對的啊?
2013-12-19 19:19:11
你好,我的本科畢業設計是android webrtc的實時視頻通訊系統,您能給我點思路嗎,我現在不知道要先做什么

2013-12-19 21:04:07
回復 習慣在這里: [137樓]

您好,不清楚你們的畢設要求是什么,如果只是視頻通訊,你只需要把WebRTC在android上整個兒編譯出來就OK了。WebRTC的Demo本身就是一個視頻通訊系統。已經編譯好的Demo你可以在網上搜索得到。

2013-12-24 17:10:59
回復 Bill_Hoo: [129樓]

bill您好!
現在有個比較急的問題,就是那個聲音有的時候會變小,甚至有點失真的現象。我沒有使用AGC模塊。現在一直找不到問題所在,基本上20個字有1個字聲音會變小。很影響體驗。求教大俠啊!!!

2014-01-09 11:24:24
回復 huangqizhen: [139樓]

您好,這幾天沒有關注博客,您提到的聲音有時候會變小,是不是AEC的等級過高導致自己的聲音被當做回聲一並消掉了?
我最近也遇到一個問題,為了適配各種手機,我找了一個折中的算法進行延遲的計算,但最后的效果就是折中了,雙方通話時卻能聽到令人很不爽的唧唧聲,不知你遇到這種問題沒有,如何解決的呢?

2014-02-08 18:06:14
您好,bill,年前去做別的事情了,呵呵,所以沒能及時關注。
1、聲音變小應該是能量過低,比如說站在4米外講話,造成語音狀態檢測的誤判(然而檢測閥值又不能太低),把較低能量的語音給消除掉了。現在經過調整好很多了,用手機的話應該是沒有我這個問題的。
2、您說的唧唧聲音是否身處的環境有各種機器的叫聲?比如打印機,或着是在工廠里?如果是的話,我也不知道,這是一個難題,目前我也在為這個苦惱。使用低通濾波器,比如4K頻率以下的通過,可以降低唧唧叫的幾率。(我的采樣率是16000)。
如果環境不是的話,那么我覺得還是延時的計算不對了。建議使用WebRtcAec_GetDelayMetrics去估計延時偏差。

2014-02-09 20:07:18
回復 huangqizhen: [141樓]

您好huangqizhen,我這邊聲音忽然變小的問題應該和能量沒有關系,我都是距離手機十五公分左右測試的,這個距離比正常的視頻、語音通話距離還近。這個問題我還要繼續觀察。
至於唧唧聲,我的測試環境除了環境噪聲、周圍人聲就沒有別的異常聲音了,這個唧唧聲應該是AECM算法收斂失敗造成的。
我會繼續關注這幾個問題的。希望大家都能早日解決。

2014-02-13 13:10:26
Bill_Hoo大神你好
我在做linphone上移植NetEQ功能,我想問您一下NetEQ是包括了解碼麽?

2014-02-14 20:41:06
回復 lee_fat: [143樓]

您好lee_fat
由於項目的安排我沒能接觸到NetEQ模塊,所以不敢妄言。

2014-02-21 14:54:17
您好!
看了你的文章,也想玩一玩WebRTC,不過死活卡在第一步下載源碼...我這里網絡環境不穩定,用gclient同步兩天了總是會中斷。你能幫忙把android上的所有源碼打包發給我一下么?我的郵箱是ys.2963@gmail.com,如果太大的話網盤什么的也可以,多謝了

2014-02-26 13:13:16
回復 nicholashz: [145樓]

您好 nicholashz,直接 svn 同步 http://webrtc.googlecode.com/svn/trunk/ 主線即可。我沒有花時間去同步整 android 的整體工程。

2014-02-28 10:49:10
Bill,
  您好,還是關於msInSndCardBuff這個參數的計算。
請問,您在Android中是采取何種方式去獲取到音頻硬件采集到幀A的時間以及播放出幀B的時間的。Android中好像沒有這樣的API呢,之前您說在WebRtc 源碼中的android-test有那樣的處理方法,恕我愚昧,找了很久也就理出個頭緒來,故來向您取經求教,謝謝。

2014-03-01 14:37:41
回復 lzg9099: [147樓]

您好,這個延遲的計算方法可以參考WebRTC主線版本目錄 webrtc\modules\audio_device\android\java\src\org\webrtc\voiceengine\
下的WebRTCAudioTrack和WebRTCAudioRecord
具體如何在程序中體現,還需要你自己進行程序結構的調優和測試。

2014-03-18 16:22:09
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*) aecmInst;
     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);
     else
           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);
     return result;
}
3.關於降噪,如果是80HZ,160字節采集的話是要分成兩次降噪嗎,每次傳遞80byte?
4.目前我播放和采集都在一個線程中執行,發送和接收在另一個線程,播放的時候去接收隊列中取出數據,執行bufferFarend,然后采集,降噪,process,再靜音檢測,不知道這樣的流程是否可行?

初次接觸音頻部分研究,還望多指點,感謝

2014-03-18 16:53:30
bill,您好:
關於那個延遲的計算,根據您的提示,我這邊測試了一下,發現達不了預期的效果呢。我是將采集和播放放在一個線程里面來測試的,代碼入下,不知道這樣處理是不是正確的:
       while(iswork){
             long timeRecBegin = System.currentTimeMillis();
             rcdLen = rcdAudio.read(rcdBuff, 0, AUDIO_SAMPLE_LEN);
             long timeRecEnds = System.currentTimeMillis();
             Log.i(TAG, "Read one audio frame " + rcdLen);      
             if(AudioRecord.ERROR_INVALID_OPERATION == rcdLen) {
                   Log.i(TAG, "Read wrong audio data.");
                   continue;
             }
             //對采集到的音頻進行噪音消除。
             m_audioNs.audioNsProcess(nsHandlerAddress, rcdBuff, null, ns_processed_out, null);
             
             //對噪音消除后的音頻進行回音消除
             timeRecDelay = (short) (System.currentTimeMillis() -( timeRecBegin + ((timeRecEnds - timeRecBegin) * bufferedRecSamples)/80));
             processed_out = m_aecm.aecProcess(ns_processed_out, out, nrOfSamples, (short) (timeRecDelay+timePlayDelay));
             Log.i("TEST", "aecmProgress msInSndCardBuff is " + (timeRecDelay + timePlayDelay));
  
             int written = 0;
             long timePlayBegin = System.currentTimeMillis();
             written = trkAudio.write(processed_out, 0, processed_out.length);
             long timePlayEnds = System.currentTimeMillis();
             Log.i(TAG, "write one audio frame.....");
             if (processed_out.length != written){
                   Log.i(TAG, "write wrong audio data.");
                   continue ;
             }
             bufferedPlaySamples = countBufferedPlaySamples(written);
             Log.i("TEST","bufferedPlaySamples is " + bufferedPlaySamples);
             
             timePlayDelay = (short) (System.currentTimeMillis() - (timePlayBegin + ((timePlayEnds - timePlayBegin)* bufferedPlaySamples)/80));
             m_aecm.aecmBufferFarend(processed_out, nrOfSamples);
             Log.i("TEST","timePlayDelay is " + timePlayDelay);
       }
countBufferedPlaySamples這個函數是我用來計算播放時的延遲的:
public int countBufferedPlaySamples(int written){
       bufferedPlaySamples += (written >> 1);
       int pos = trkAudio.getPlaybackHeadPosition();
       if (pos < playPosition){
             playPosition = 0;
       }
       bufferedPlaySamples -= (pos - playPosition);
       playPosition = pos;
       return bufferedPlaySamples;
希望您有空時候,能再給點建議,不勝感激。
2014-03-19 12:35:36
hello,Bill,我現在回聲是消除了,但是聲音開到最大聲有時候還是有噪聲和嘯叫,我是80HZ,160byte采集的,執行順序是:回聲消除-->噪音消除-->靜音檢測(如果是靜音,就不發送),請問這樣的流程對嗎?
代碼實例如下:
//frame_size : 160
short[] nsout=new short[frame_size];
                       short[] aecmout=new short[frame_size];      
                       short[] temp1=new short[frame_size/2];
                       short[] temp2=new short[frame_size/2];
//recData 為接收到的數據,結構short[160]
WebrtcAECM.Process(recData, null, aecmout, frame_size, 200); //回聲消除                                    
                                   
                                   //噪聲消除分成兩段,一次80個字節
                                   System.arraycopy(aecmout, 0, temp1, 0, frame_size/2);                                    
                                   WebrtcNs.NSProcess(temp1, null, temp2, null); //噪音消除                                    
                                   System.arraycopy(temp2, 0, nsout, 0, frame_size/2);
                                   
                                   System.arraycopy(aecmout, frame_size/2, temp1, 0, frame_size/2);      
                                   WebrtcNs.NSProcess(temp1, null, temp2, null); //噪音消除                                    
                                   System.arraycopy(temp2, 0, nsout, frame_size/2, frame_size/2);
                                   
                                   int vadFlag=WebrtcVAD.process(sampleRate, nsout, frame_size); //靜音檢測,如果是靜音就不發送

延遲時間目前暫時用固定值200,會影響到噪聲消除嗎?
感謝~

2014-03-20 11:18:52
回復 lzg9099: [150樓]

您好lzg9099,AudioRecord 以及 AudioTrack 的接口是阻塞式的,很可能影響了延遲的計算。
對於最終的效果,我從發布這篇文章到目前為止基本上都在做優化,雖然相比最初的版本有較大改善,但不論是單獨使用AECM還是使用集成APM以及其他WebRTC組件,均達不到我預期的100%滿意的效果。目前正在等待Google進一步的優化。之前看了下WebRTC的計划,貌似會廢棄AECM模塊,改用其他實現方式。

2014-03-20 11:29:50
回復 appleZYY: [151樓]

您好appleZYY,您說的采樣率應該是8000Hz單聲道吧。對應為160字節是沒錯的。執行順序是可以自己調優的。建議參考WebRTC AudioPreprocessing 模塊里面的 ProcessStream 的實現順序。不過,有時間可以直接把APM用起來。
固定的延遲得不到較好的效果,對於同一台手機,可能由於線程調度、Audio API 阻塞等不可控因素導致延遲發生變化。對於不同手機,延遲基本上是不同的。
關於嘯叫:回聲消除算法未收斂成功,會導致最后一個尾音產生反饋嘯叫。這種嘯叫可以通過優化程序結構和延遲計算算法盡量的壓制,但無法100%消除。

2014-03-20 12:27:19
回復 Bill_Hoo: [153樓]

hello,bill,感謝您的回復
我在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.“

read和write的block time是算調用read前后的時間差嗎?我有測試過,采集160byte, 是0~60ms不等;
the buffer of AudioRecord and AudioTrack 這個我不知道該怎么算,可否指點一二?
我感覺好像都得不到真正的read和write第一幀的時間

2014-03-20 12:35:53
回復 appleZYY: [154樓]

您好,根據目前 android 的音頻API確實得不到精確的底層第一幀的時間,但是AudioTrack有API可以估算底層還有多少數據沒有播放,這個可以參考下樓上lzg9099的代碼。至於AudioRecord,目前只能想辦法少讓它阻塞。
SO上面的回復已經是很早的事情了,該描述現在看來不甚准確,可以忽略。

2014-03-20 14:56:21
回復 Bill_Hoo: [152樓]

Bill,感謝您的回復。
按照我之前的估算延遲方法,我測試的時候有時候會發現延遲竟然是負數,這個問題真心困擾我了,怎么會是負數呢?而關於播放端的bufferedPlaySamples,在小米3上經常是在1千多,而在華為的手機上就只有六七百左右;另外,關於audioRecord的AudioSource參數,我發現如果是設為VOICE_COMMUNICATION的話,有的手機剛開始時根本不用我們這個aecm也能夠消除回音,但也會有嘯叫聲,這個時候是用到了手機自帶的回音消除功能嗎?而當設為MIC的話,我們的這個aecm顯然是起作用了的,但是效果在不同手機上差別很大,而且即使消除了回音的手機上依然會存在唧唧唧唧的聲音。

2014-03-20 16:09:14
回復 lzg9099: [156樓]

您好 lzg9099
A1:關於延遲是負數的情況,問題很有可能出在播放端延遲的計算上,因為你的 bufferedPlaySamples -= (pos - playPosition); 很有可能越界。這只是猜測,因為我之前自己也犯過這個毛病。
A2:bufferedPlaySamples 是隨硬件實現而變的,底層buffer有多大,不同機型有很大差別。我在一台華為上,通過API得到的底層最小buffer盡然達到了7000+字節。
A3:VOICE_COMMUNICATION 是 Android API Level 11 之后才加入的新特性,它要求硬件廠商在API 11之后,對這個采集流實現硬件回聲消除。這幾種模式的詳細情況可以參照 CSipSimple 的開源代碼。更換模式不失為一種有效的解決辦法,但是不同的硬件廠商對該標准的支持很不一樣,尤其是三星系列,很多機型根本沒有對API進行支持,甚至有選擇該模式后直接卡死的情況。
A4:“唧唧”聲也是我目前覺得比較棘手的問題。之前一直在對系統延遲的計算以及程序結構進行優化,能夠不斷減少這種聲音的發生。但仍然無法完全消除。主要有兩點原因:1)要在各種android機型上找到一個普遍適用的系統延遲算法確實比較困難,就WebRTC自身而言,其延遲計算也是估算的。2)即使系統延遲估算准確,手機上的回聲消除模塊本身存在其局限性,最終導致“唧唧”聲的出現。
針對原因1,可以對您自己的算法不斷的調優,對程序結構和線程調度不斷調優,確實可以達到一定效果,甚至和QQ語音相差無幾,但是QQ語音實際上也做得不是很好,相差YY語音很大一截。而我們希望要達到YY語音的效果,光從AECM模塊入手恐怕是不行的,這個願景在目前看來有點牽強。
針對原因2,我目前也卡在這里,時刻關注Google對AECM模塊的優化或者方案變更。AECM算法簡介可以參考 http://blog.csdn.net/u012931018/article/details/17045077

2014-03-20 16:55:07
回復 Bill_Hoo: [157樓]

Bill,太感謝您的寶貴建議了。
再請教您一個問題,關於語音采集那兒一塊兒的延遲,即bufferedRecSamples,您那邊也是按照WebRtcAudioRecord里面的粗略估計值嗎,即sampleRate / 200 ?

2014-03-20 17:00:06
回復 lzg9099: [158樓]

采集延遲這一塊我目前也沒有定下來,之前嘗試了很多方法,始終不盡人意,目前采用一半底層buffer大小作為采集的固定延遲。如果您有新的發現,還請來此反饋。感謝。

2014-03-25 17:22:01
回復 Bill_Hoo: [159樓]

bill,我這邊采集延遲也是用的固定的buffer = SAMPLERATE / 200 ; 看了一下算出來的msInSndCardBuff 在100左右到300左右,偶爾會高出500,大部分是在200 到 300 之間。
測試發現:當說話時,是不會聽到自己的回音了,但是呢,隨着時間的增長有一個“唧唧”聲音,從很小很小變得越來越大,這種現象是每次都會出現的,運行一會兒就出現了,這是因為回音消除算法沒有收斂成功嗎?不知道您那邊遇到過這種問題沒有,如果遇到了,您處理的思路是如何的呢?感謝。

2014-03-25 21:22:24
回復 lzg9099: [160樓]

您好lzg9099,這個延遲算出來基本是穩定的,我沒有遇到過超出500的情況。也許你的結構設計上哪里還有些問題。至於唧唧聲,我這邊也有,目前正在解決。不過,如果延遲計算准確,唧唧聲不會每次都出現。總體來說,在一個准確的延遲范圍內,AECM的效果會趨於平穩,這種狀態下仍然可能出現唧唧聲。這個聲音應該是爆破音,你google一下 pop/clicking sound 應該有所收獲,如何以程序的方式去除這個 clicking sound,還要再仔細研究。

2014-03-26 11:50:47
回復 Bill_Hoo: [161樓]

Bill,感謝您的回復,問一下您那邊穩定后的范圍是多少呢,我剛調試了一下,不會出現500以上的了,但是一直在40左右到200左右直接波動。

2014-03-26 18:10:29
回復 Bill_Hoo: [161樓]

Bill,采集延遲如果通過底層buffer大小的一半作為固定延遲的話,可能會有問題,我小米2s和小米3的手機的這個底層buffer是在1000多左右,這個時候算出來的msInSndBuffer會穩定下來,在200左右,但是當我換成一個華為的榮耀3后,這個buffer竟然為7680,如果根據這個來算出采集端的延遲的話,msInSndBuffer穩定在900多左右,而webRtc源碼里面,msInSndCardBuffer只適用於0--500之間,大於了500就無法處理了。我這邊目前先暫時設定了一個值240,當msInSndCardBuffer大於500就直接設為240,但是這個時候的效果就沒動態算出來的效果好。

2014-03-27 10:10:12
回復 lzg9099: [163樓]

您好,我這邊華為底層buffer也是7000+,但是根據你算出來的延遲900多,我猜測你是否使用了雙聲道32KHz的采樣率?如果是單聲道16KHz的話,7000bytes對應的延遲也才200多毫秒。是否哪里計算錯了呢?

2014-03-27 10:35:54
回復 Bill_Hoo: [164樓]

bill,您好,我這邊是使用的8k,單聲道。底層buffer 7000+ 的話,recBuffer 固定為 3500+ 個Samples,我是這樣理解的,8個samples采集需1ms,那么3500 / 8 = 400+ ,這樣的話光是采集就400多的延遲。按照這種算法,我在另外兩個手機上測試的時候,即小米2s和小米3上測試,底層buffer為1200左右,算出來msInSndBuff就穩定在200左右,效果很好,唧唧的聲音很小很小了。不知道您那邊采集端是如何根據這個buffer計算延遲的。
2014-03-31 16:51:43
回復 lzg9099: [165樓]

您好,計算方法都是一樣的。我使用的16KHz數據,所以延遲是你的一半左右。

2014-04-03 17:53:20
回復 Bill_Hoo: [166樓]

bill,你好,不知道你有沒有解決唧唧的聲音,還有音質也不太好

2014-04-10 15:10:00
hi, 請問 windows phone 是否也可以單獨提取出來編譯呢
或者說有沒有試過編譯webrtc給 win phone 使用
剛開始接觸webrtc,發現和win ph 相關的信息太少了

2014-05-08 15:58:34
hi,bill,首先感謝你這片博客。我按照你的提示,單獨提取出來AECM模塊。目前在驗證AECM的正常工作有點問題:
我是在Android上層做錄音播放工作:
    初始化: 
    int aecmHandler;
     aecmHandler = mWebrtc.createAecm();
    int initAecmResult = mWebrtc.initAecm(aecmHandler, sampleRateInHz);
    驗證:
    int frame_size=160;
    int sampleRateInHz=8000;
    private short[] recordedShorts = new short[frame_size];
    shortsRead = audioRecord.read(recordedShorts, 0,frame_size);
     mWebrtc.bufferFarend(aecmHandler, recordedShorts,sampleRateInHz);
     mWebrtc.processAecm(aecmHandler, recordedShorts, null,aecmOutFrame, frame_size, 0);
    audioTrack.write(aecmOutFrame, 0, frame_size);

如果AECM是正常的,這樣驗證,是不是應該沒有聲音?我是采用死循環,可是會出現很大噪音,應該是嘯叫。AECM是沒正常?還是我的驗證有問題?謝謝指教


2014-05-08 19:49:05
回復 controler2013: [168樓]

您好,抱歉很久沒回博客。
WinPhone我沒有接觸過,是說一下我了解的信息吧,WebRTC底層是C/C++的實現,Android上也是要用JNI去調用底層實現的。如果WinPhone沒有這個能力調用C/C++實現,那這個事情可能就沒辦法。

2014-05-08 19:50:47
回復 HulkChen: [169樓]

理論上講,將同一音頻放入Farend和Process,輸出就是全零的。你覺得你最好記錄音頻文件看一下問題出在哪里。

2014-05-09 18:10:08
回復 Bill_Hoo: [171樓]

謝謝bloger的解答,我單獨編譯AEC等幾個模塊,WINCE下面效果非常理想,Android下面我用OpenSLES接口,一直存在偶爾串回音,效果很差,后來我發現Android已經自帶libwebrtc_audio_preprocessing.so,使用這個so,效果非常好,但是OpenSLES的延遲達450ms,十分可怕.

2014-05-10 16:47:20
bill你好,最近我也在搞webrtc的回聲消除,看到你的文章對我的幫助很大。在看代碼的過程中我有一些問題想請教一下你
1.aecm_core.c代碼過程中屢次提到的Q-domain是什么意思?只是一種定點表示規范?
例:// @param aecm       [i/o]   Handle of the AECM instance.
// @param far_spectrum [in]   Pointer to farend spectrum.
// @param far_q     [in]   Q-domain of farend spectrum.
// @param nearEner   [in]   Near end energy for current block in
//                     Q(aecm->dfaQDomain).
// @param echoEst     [out]   Estimated echo in Q(xfa_q+RESOLUTION_CHANNEL16).
//
void WebRtcAecm_CalcEnergies(AecmCore_t * aecm,const WebRtc_UWord16* far_spectrum,const WebRtc_Word16 far_q,const WebRtc_UWord32 nearEner,WebRtc_Word32 * echoEst)
這當中的Pointer to farend spectrum.和 Q-domain of farend spectrum都是與遠端有關的,怎么讀都讀不懂?

2.是不是加上噪聲抑制一定會對噪聲處理產生良好效果,我對此也進行了測試,感覺加在后面效果比在前面好,這是為什么呢?按照aecm中process函數的理解,當中既然為ns后的語音流留了接口,難道不是應該放在前面效果更好嗎?

3.你對完成以后的唧唧聲或嘯叫聲是怎樣理解的呢?從理論上分析,當輸入為小的噪聲時,webrtc本身代碼處理過程有可能自激產生嘯叫嗎?亦或是時延精度對這一現象造成的影響?webrtc中是否只有遠近端對齊時的這一種時延要求,是否里面仍有不同類型的時延計算需求,時延一面看的糊里糊塗的。

問題問的有點多,麻煩你了,最近被這些問題困擾很久,非常感謝!

2014-05-12 09:15:05
回復 LuoZH: [173樓]

你好LuoZH,很高興你能來此反饋,有幾個問題向你請教一下:
Q1 不知道你提到的效果非常好是怎么個好法,還存在唧唧聲嗎?
Q2 libwebrtc_audio_preprocessing.so 是你用WebRTC編譯出來的,還是其他什么地方得來的?
Q3 上述庫的接口有頭文件可以參考么?

2014-05-12 10:10:32
回復 shifu0803: [174樓]

您好shifu0803:
A1:對於 Q-domain 我也沒能很好的理解,不過看 AECM 的頭文件,應該用了一個叫做 DYNAMIC-Q 的優化算法。
A2:這幾個獨立模塊的順序可以參照 APM 里面的實現,如果沒有特別的要求需要單獨編譯各個模塊,其實可以直接把APM模塊編譯出來使用,參數的配置,接口的統一都方便很多。
A3:嘯叫聲應該是延遲沒有計算准確導致的,最開始我也以為是AECM自己有問題,后來不斷優化延遲的計算,嘯叫聲是不存在的。唧唧聲按我現有的知識沒法作出理論上的描述,我只能說一下我的感受,我覺得這個唧唧聲很像本次回聲消除后殘留的一點點聲音,這個聲音沒被AECM抵消掉,被播放了出來。
A4:只是按我的理解,這里的延遲有兩個。我用 sys_delay 和 filter_delay 加以區分。sys_delay 是我們上層計算出來傳給AECM接口的系統延遲,表示音頻在android系統上的延遲值,一般為100~200ms。sys_delay 主要用於AECM里遠端buffer 偏移量的計算,它被AECM拿去以20%的權重(另80%為舊值,以求延遲穩定)重新計算新的遠端歷史buffer偏移量。 filter_delay 用於表示AECM的濾波器偏移量,這個偏移量通過 delay_estimator 得到計算。最后拿去給濾波算法使用的是 filter_delay,但如果我們的 sys_delay 計算失誤,會導致 delay_estimator 始終得不到一個正確的 filter_delay,所以上層計算延遲是需要注意和優化的。
針對AECM延遲的計算,更加詳細的解釋請參考這位博主的文章:
http://blog.csdn.net/u012931018/article/details/17045077
打開源碼對比着理解就很容易了。
我音頻專業知識不足,說錯的地方還希望大家幫我指出來,謝謝。

2014-05-15 18:29:35
回復 Bill_Hoo: [172樓]

  非常感謝Bill的回答。將同一音頻放入Farend和Process,講話或者放歌的聲音都沒有了,但不是完全靜音狀態,還是有平靜的小小的嗡嗡聲,我是單獨提取AECM及NS模塊處理的,這代表AECM模塊工作正常?
  另外想問下Bill,單獨用了AECM,NS模塊,如果嚴格計算延時參數,能達到的效果會是怎樣的?我這邊確實沒有了沒用AECM前,雙向通話一打開,直接嘯叫的現象。但是雙方聽到的聲音都是有失真,斷續的感覺,而且失真有點嚴重。我是一個線程將接收遠的數據存入隊列,另一個線程取隊列數據播放及采集麥克風數據壓縮傳輸。這是延時參數計算不是非常准確引起的還是我在AECM模塊的提取存在問題?
  AGC模塊利用上,對音質效果會有提示?AGC模塊是利用gain_control.h里面的函數?因為對一些類似WebRtcAgc_Process函數參數利用存在一些疑惑。

2014-05-15 20:08:24
回復 HulkChen: [177樓]

您好 HulkChen,
A1:AECM在相同文件作為NearEnd和Farend輸入時,基本全零就可以了,這只是拿文件來驗證你的JNI是否有誤。
A2:我不清楚你說的聲音失真是什么,被削掉了一半,還是忽大忽小。
A3:延遲值計算准確的話,在每一台機型上都該是一個針對該機型的穩定值(上下波動不大),比如我這邊魅族MX延遲穩定在90~100ms,三星9100延遲穩定在134ms上下,你可以記錄你的延遲,如果不穩定,說明是有問題的,具體問題需要結合你的代碼和設計自己查找。更多關於這個延遲的說明,請參見文前更新內容。
A4:斷斷續續的感覺可能跟你的隊列設計有很大關系,你的隊列由於在兩個線程間共享,可能需要上鎖,或者使用自帶同步的隊列數據結構,很可能會導致播放線程不能及時播放。這個需要你自己去確認。
A5:AGC模塊我沒有單獨使用,在寫此文后不久,我就整體編譯APM出來跑了,AGC是在APM里寫好的,所以單獨對AGC的使用我沒有經驗,不敢亂說。

另,建議大家講APM整體編譯出來使用,條件允許的,請直接使用VOE,獨立編譯各個模塊出來使用是我很早之前的做法了,不再推薦。

2014-05-23 13:24:32
回復 Bill_Hoo: [176樓]

Q1,效果已經達到產品級別,沒有唧唧聲,偶爾串一點回音
Q2,so在Android自帶,在/system/lib/目錄下
Q3,這個庫 == audio_process,跟手動編譯的使用是相同接口.頭文件也是使用手動編譯時的

2014-05-23 15:28:22
回復 LuoZH: [179樓]

恭喜恭喜,沒有唧唧聲是很不錯的表現了。
你們測試了哪些機型呢?在沒有開啟android硬件回聲消除的前提下也是這個效果嗎?
2014-05-23 15:36:23
回復 LuoZH: [179樓]

對了LZY,你提及的動態庫是從哪個版本的Android里導出的?   我現在的軟件結構沒辦法直接用這個庫了,不過我想自己建個工程去試試看。

2014-05-26 10:19:48
回復 Bill_Hoo: [181樓]

4.0.2

2014-05-30 17:45:28
解決延時問題,把aecm_defines.h里面的MAX_DELAY調大一點。嘯叫問題嘗試把CNG關掉試試看還有沒有嘯叫。msInSndCardBuf這個參數不要亂設。和AGC配合時特別要注意AGC別把殘余回聲給放大了。

2014-05-30 20:17:11
回復 DarkEnergy: [183樓]

您好DarkEnergy,感謝,
MAX_DELAY 是底層歷史遠端數據的buffer大小,我之前有試着調大,但沒有發現明顯的改善。
CNG我在測試時發現它會出異常,AECM可能出現異常的噪聲,很大聲。所以一直關了的。
msInSndCardBuf 這個參數我嘗試過定值,定值的時候,一旦出現殘余回聲(應該是AECM收斂失敗)就很難再次收斂(或收斂速度慢),但如果我每次都更新一個計算后的值,在出現殘余回聲后,能夠很快的將濾波器偏移修正回來。所以我自己覺得,這個值還是要算。
最后,我很感興趣您提到的AGC將殘余回聲放大的問題。因為這是一個客觀事實,殘余回聲會和正常語音一起被AGC放大。目前我的拙劣的解決辦法是降低AGC的增益級別,從而降低其對殘余回聲的影響。

所以很想請教您,如何避免AGC將殘余回聲放大?調整AGC在APM里面的執行順序么?希望能得到您的指點。

2014-05-31 15:12:10
樓主,你好
最新剛剛一個項目需要,需要做回音消除。
1.目前模塊都已編譯工作,我在同一個線程里先將遠端數據設為全0,結果發現Aecm process后的聲音聽起來有些問題,沒有以前清楚,而且感覺上有變聲?
2.另外雖說是8000HZ的,但我這邊是512 short的長度來發送接收的。現在只有切開80 short長度的。
想問下,在一個線程連續調用WebRtcAecm_BufferFarend之后,再另外一個線程連續調用幾次WebRtcAecm_Process,這樣是不是就沒有效果了?是不是一次BufferFarend,只對下一次的_Process有效?
3.因為在一個線程里做WebRtcAecm_BufferFarend,另外一個線程中做WebRtcAecm_Process,是否需要加什么鎖之類的啊?

2014-06-03 09:53:52
回復 Bill_Hoo: [184樓]

AGC的問題需要修改代碼,最好把AECM里面回聲抵消的VAD判決拿幾個參數出來,加入到你的AGC里面,相當於AGC要判斷遠端是否有聲音,遠端單端有聲音的話就不作放大,但是這一塊還是要和延時結合起來,因為回聲是滯后的。這個也不難弄,因為你近端是有VAD的,而且AECM里面是能夠判斷出雙端的。MAX_DELAY這個參數很重要,AECM是在MAX_DELAY這個長度范圍內去尋找遠端與近端最匹配的位置來做延時估計。要是做移動端的應用,參考和回聲有時延時很大的。比如三星的某些手機可以達到600ms,如果MAX_DELAY小於延時,AECM做延時估計就永遠返回錯誤的值了。AECM非常依賴延時檢測,因為它做回聲抵消的方法是將遠端參考和回聲對齊,然后計算HNL通過維納濾波器抑制回聲。還有一種可能情況是,MAX_DELAY剛好超過延時,但超過得不多,當延時出現抖動時(延時邊長且超過MAX_DELAY),這個時候就沒法再次收斂了,所以你修改msInSndCardBuf是能夠解決問題的,但是是不是解決了根本,還得再研究研究,看是不是這種可能性


2014-06-03 11:00:23
回復 DarkEnergy: [187樓]

嗯,謝謝您花時間來此指點,AGC的問題我會參考您的建議自己下去試驗的。
另,我看了下您說的關於MAX_DELAY和msInSndCardBuf的問題,這個就真得再研究研究了,說明我底層源碼還沒有吃透,還要花時間。

2014-06-03 21:06:17
回復 xubingnihao: [185樓]

您好 xubingnihao:
A1:不好意思我沒有看懂這個問題,還請再組織一下語言。
A2:這兩個API是可以異步調用的,你可以在兩個線程里分別調用兩個API,沒有問題。
A3:鎖只是在有資源競爭的情況下加,我不清楚你的程序結構。光從這兩個API來看,上層沒有加鎖的必要。

2014-06-04 11:21:21
您好Bill_Hoo:
  我也遇到186樓朋友的類似問題:
  1、我單獨編譯了aec模塊,並封裝提供了兩個接口:1、添加遠端數據,2、添加近端數據。然而把遠端數據添加進去后做了混音處理后變成了異步數據,我想問下:把混音后的遠端數據添加到WebRtcAecm_BufferFarend函數的操作是否一定要先於把近端數據添加到WebRtcAecm_Process函數的操作。

2014-06-04 14:31:49
回復 xdwanmei: [190樓]

您好,請參見 #190 的回復。兩API的詳細描述請參見 audio_processing.h 頭文件中的官方說明,兩接口沒有強制的先后順序。
另,本文開頭已不再建議獨立編譯各個子模塊出來使用,請知曉。

2014-06-04 16:45:24
回復 Bill_Hoo: [191樓]

您好Bill_Hoo:
  由於某些原因,暫時只能單獨使用所有,還有些問題想請教下你:
  在使用WebRtcAecm_BufferFarend函數時,遠端數據是否要進行軟件混音(注:遠端的數據是采集到的數據)?

2014-06-04 17:56:23
回復 xdwanmei: [192樓]

您好,farend 數據指你從網絡接收到的對端音頻,nearend 才是指你本地采集到的音頻數據。

2014-06-29 11:52:54
這樣好麻煩啊,有沒有整理出來的aecm模塊?

2014-07-01 17:45:04
回復 huangqizhen: [132樓]

你好,我現在也在linux上用aec,也有你說的那個聲音忽大忽小的問題,很苦惱,請問你是怎么調整的,盼指點,還有aec模塊效率很低,在arm9上處理一幀10ms的音頻需要4ms,不知道你遇到過這個問題沒,是做匯編優化了嗎
2014-07-03 11:22:40
bill你好,我現在在linux上用aec模塊,遇到的問題就是算法效率很低,處理一幀10ms的音頻16k采樣率的話需要將近10ms,不知道你遇到這個問題沒,是否需要做匯編優化或者浮點轉定點?

2014-07-03 19:22:27
回復 wangyaliss: [196樓]

Linux上我沒有使用經驗,不過Android上如果使用原始的AEC(PC),處理效率就是你這個樣子。正常。

2014-07-04 15:48:42
回復 Bill_Hoo: [197樓]

bill謝謝你的回復,另外我還遇到另外一個問題,就是處理后的聲音忽大忽小。像是去回聲的時候過於敏感,把遠端聲音去除的同時把近端聲音也去除了一部分,你遇到過這個問題嗎

2014-07-04 16:15:56
回復 Bill_Hoo: [197樓]

我用aec模塊和aecm模塊都有上面說的那個聲音忽大忽小的問題

2014-07-08 16:42:55
回復 wangyaliss: [198樓]

一般不會存在忽大的問題,僅有忽小的問題。忽小是因為AECM不支持雙邊通話,一旦出現雙邊通話的情況,AECM就會進行強制壓制,導致本地處理出來的聲音變形,減弱,對方聽上去就忽小了。
至於AEC,我沒有在android上使用,PC上不存在這個問題。

2014-07-10 17:07:46
bill你好,現在我在研究安卓的回聲消除部分,不知道你對安卓4.1版本后自帶的回聲消除模塊有沒有研究,就是把webrtc的回聲消除部分加進了安卓系統。
我現在遇到的問題是加載完該模塊后在開始的一段時間回聲消除會起作用,但是在過幾分鍾后感覺回聲消除的功能失效了,回聲變的很清晰,而且無法恢復原有的無回聲狀態,
不知道你對此是否有研究?或者你認為會引起該狀況的原因會是什么呢?
謝謝。

2014-07-14 19:19:35
回復 shifu0803: [201樓]

您好 shifu0803:
我沒有使用自帶的回聲消除器的經驗,僅僅在之前測試時開啟過一次,效果很糟,之后就沒有使用過。
你提到的回聲特別清晰,是否是一個回聲之后就沒有了?因為如果回聲消除不起作用的話,你的通話會產生無限的回聲反饋,到最后除了嘯叫聲,其他是聽不清的。
還有一個,你的測試環境是怎樣的?是否兩台手機在兩個房間(保證本地說話不會被另一方的麥克風直接采集到)?

2014-07-18 17:19:54
回復 Bill_Hoo: [202樓]

bill謝謝你的回復~
我提到的回聲很糟的意思的確是一個回聲之后就沒有了,回聲很清晰,但是並沒有出現無限的回聲反饋這種情況。
主要讓我特別不理解的是開始的一段時間並沒有這種情況,但是打了一會電話后就會出現清晰的回聲。
我的測試環境可以保證一方說話不會被另一方采集到,一個在屋內一個在屋外。
不知道bill你覺得引起這種現象的原因大概會是什么的?
再次感謝~

2014-07-23 08:55:08
回復 shifu0803: [203樓]

如果沒有出現無限的回聲反饋,只是偶然出現一個清晰的回聲,這個現象是可以存在的。
我聽其他朋友說自帶的EC底層也是WebRTC里的AECM算法。AECM出現偶爾的回聲是正常的。

2014-08-14 10:28:47
你好 Bill:
  我想問下aec的那個delay該怎么算,我看了下提供的那個公司,這幾個時間點都是什么時候標記出來,我是用了aec后回聲變小了,是不是因為我delay設置的原因(我給了一個常值50ms)。

2014-08-14 16:12:32
你好 Bill:
我把問題又梳理了下:
A1:我想問下aec的那個delay該怎么算,我看了下提供的那個公式,這幾個時間點都是什么時候標記出來,apm處理近端和遠端數據函數每次只能接收一定的數據,t_pull 和t_analyze是每次調用函數前更新一次嗎?
A2:如果使用了aec,過一會對方就聽不到自己說話了,不使用aec對方就可以聽到自己說話,這中現象有碰到過嗎,有人說是delay的問題(delay我設了一個常值)?

2014-08-15 09:20:45
回復 xdwanmei: [206樓]

您好 xdwanmei:
A1:這幾個時間點的含義源碼注釋已經寫得比較清楚了,評論列表里也有好多網友提及過,你可以再看看。確實需要每次都更新。
A2:我沒有實際測試過AEC(PC),我只使用過AECM,對於過一會就聽不到說話了,那那個時候是什么聲音?持續嘯叫還是完全無聲音?delay在AEC(PC)里面是需要動態更新的,常量值僅適用於AECM。

還有問題我們再討論。

2014-08-15 10:47:58
Bill 你好:
  A1:我看了下你們的討論,但是實際的操作讓我有點困惑,比如:我采集的一幀數據9600個,但是apm每次處理的數據是遠遠小於一幀數據量的,我在采集這幀數據是記錄一個時間,在每次調用process時都記錄一個process時間點,去跟我這一幀采集時間點作比較嗎?注:我看了下webrtc自帶的一套音頻引擎,看了下它里面只設置了播放和采集的delay,沒有計算加入AnalyzeReverseStream 和ProcessStream的時間,是不是采集后或者播放前不做其他處理直接調用函數,這兩個時間可以忽略。
  A2:是沒有任何聲音了,感覺要是把所有聲音都消掉了。
麻煩您了。

2014-09-02 17:46:50
Bill 你好:
  可以郵件溝通下,2802716485@qq.com

2014-09-09 09:22:10
回復 xdwanmei: [208樓]

您好 xdwanmei,
這段時間較忙沒來看博客,不好意思。
A1:如果在 Android 上,不使用OpenSLES的話,一般用API AudioRecord,一次讀取10ms對應采樣率的采樣點數,如8kHz 80個Sample,16kHz 160個sample。由於Android的音頻采集API沒有提供能夠精確計算延遲的信息,因此可參照WebRTC的粗略實現,使用一個固定值作為采集延遲。播放延遲可以通過 AudioTrack 的API getPlaybackHeadPosition[http://developer.android.com/reference/android/media/AudioTrack.html#getPlaybackHeadPosition()] 進行估算,估算當前底層播放緩沖中還有多少采樣點沒有被硬件渲染,這些采樣點就造成了主要的播放延遲。

A2:這個問題你光這么說我也不是很清楚,你可以說一下細節之類的。不過,我看你采集出來的一幀9600,不知道你這個一幀是指什么,10ms?AECM最高支持16kHz的回聲壓制,AEC我不熟悉,好像是48kHZ,超過了都會被重采樣的。
2014-10-09 15:00:50
你好,Bill_Hoo.我有些問題想請教你的。
我在做aecm時,延時時間msInSndCardBuf都是給個固定值的(如100ms,250ms,490ms等等)。效果是可以消除回音了,但是手機一直都有雜音(不知道是不是你說的唧唧音,不說話也一直有,而msInSndCardBuf都從0到490作為固定值一個一個的測還是一直有哪些唧唧音)。
1.msInSndCardBuf是不是一定時間要改變,才會減小唧唧音。但我看你說msInSndCardBuf是一個比較穩定值,那設為一個固定值應該也可以消除部分的唧唧音,但是我做的aecm一直都是開啟就有唧唧音了。

2014-10-09 15:17:02
hi,Bill_Hoo.在185樓說的CNG關掉的CNG是什么來的。是WebRtcAecm_set_config函數參數的AecmConfig的echoMode嗎

2014-10-09 16:48:26
hi,Bill_Hoo.我在做aecm計算的msInSndCardBuf時,做
long time = System.currentTimeMillis();
mHead.BufferFarend(outBytes, AudioRecordThread.SAMPLE);
mAudoiTrack.write(outBytes, 0, outBytes.length);
int playtime = System.currentTimeMillis()-time;
得到的playtime在0到2ms之間,我怎么都覺得不對啊!這播放延時間一般是多少?

2014-10-09 18:06:06
hi,Bill_Hoo.我是做aecm的用了2個線程,一個是播放線程一個是采集線程。這2個線程要不要什么同步的條件。現在這2個線程是播放線程WebRtcAecm_BufferFarend()m個,再到采集線程WebRtcAecm_Process()n個,每次m和n都不同的。這樣是對的嗎

2014-10-10 21:46:59
回復 keke274233971: [215樓]

您好,關於這個延遲值,AECM 模塊使用定值是標准的做法,因為 AECM 內部有自己的延遲估算機制。
如果一定要計算這個 delay 值,其計算方法在 audio_processing.h 頭文件中有詳細的描述,仔細閱讀便可 :)

2014-10-11 15:32:00
回復 Bill_Hoo: [217樓]

感謝Bill_Hoo,我是用了定值做的,只是運行時一直有唧唧聲(音量開得大時)。我只用了噪音處理,沒有用靜音處理。是不是靜音沒有處理產生的?說話時唧唧聲會變小或者消失。請求幫我分析原因,謝謝

2014-10-14 17:44:40
博主你好,謝謝分享。我現在也在用aecm來降噪讀取的buffer大小是320(char數組),在一些手機比如小米2s上面效果很好,但是其他手機比如Nexus5上面效果很差,后來發現錄音的回掉不均勻,不管是用opensles還是java的錄音回掉的周期都不均勻,N5的Native Buffer 大小是960,我現在讀取的是320,返回不均勻,但是小米的Natvie Bufer是320回掉就很均勻。
我看你在之前在googlecode上面也問過類似問題 https://code.google.com/p/android/issues/detail?id=53996
后來你是怎么解決的?
我自己在代碼中加了定時器強制均勻時間但是效果扔沒有改善。
這種情況要如何處理,謝謝!

2014-10-18 01:30:30
Hi, 我看webrtc 源碼, 發現 msInSndCardBuf 似乎與所謂二進制延遲估算器無關, 而是用來計算 acem->knowndelay 用的, 這個 knowndelay 用來從 aecm->farbuf 定位具體字節偏移;具體來說, 就是沒調用一次 process, 則從 farendbuf 緩沖區讀對應的若干幀到 acem->farbuf, 這個不由任何 delay 控制, 就是順序讀;這個 farbuf 的最大大小是 PART_LEN4, 即 4 個 PART_LEN 大小, 或者說 256 字節, 而后, 再從這個 farbuf 里面讀一個一個 part 來做信號處理;

aecm 跟蹤一個名為 farBufReadPos 的變量, 其含義就是上次已經從 farbuf 里面讀到了哪個字節; 經 msInSndCardBuf 計算出來的 knwndelay 即在這里發生作用, 具體來說, 就是 farBufReadPos - kowndelay 作為新的 farBufReadPos;   而二進制延遲估算器是在處理 part 時計算的, 這部分代碼還沒看, 但該 part 是 間接由 msInSndCardBuf 選定的;即msInSndCardBuf決定那部分數據作為參考幀, 選取完畢后進行的一系列計算, 包括二進制延遲估算器, 基本沒msInSndCardBuf什么事情了;但我還沒搞明白那個估算器是干什么用的, 

整體來看, 似乎是如下模型: webrtc 認為硬件足夠可信, 即 8000 采樣率的 8000 個點硬件一定是在 1 秒內播放或者捕獲完畢;因此只要數據始終供應充足, 那么理論上。 若保持 farendbuf 大小正好是 api 頭文件說明的那個值, 則每捕獲一幀數據, 則farend 中的參考幀必然是 farendbuf 中 readpos 指向的那一幀; 即在一切理想的情況下, kowndelay 應該始終為 0; 

但這個函數卻給我莫名其妙的感覺了:
static int WebRtcAecm_EstBufDelay(aecmob_t *aecm, short msInSndCardBuf)
{
  short delayNew, nSampSndCard;
  short nSampFar = (short) WebRtc_available_read(aecm->farendBuf);
  short diff;

  nSampSndCard = msInSndCardBuf * kSampMsNb * aecm->aecmCore->mult;

  delayNew = nSampSndCard - nSampFar;

  if (delayNew < FRAME_LEN)
  {
    WebRtc_MoveReadPtr(aecm->farendBuf, FRAME_LEN);
    delayNew += FRAME_LEN;
  }

  aecm->filtDelay = WEBRTC_SPL_MAX(0, (8 * aecm->filtDelay + 2 * delayNew) / 10);

  diff = aecm->filtDelay - aecm->knownDelay;
  if (diff > 224)
  {
    if (aecm->lastDelayDiff < 96)
    {
        aecm->timeForDelayChange = 0;
    } else
    {
        aecm->timeForDelayChange++;
    }
  } else if (diff < 96 && aecm->knownDelay > 0)
  {
    if (aecm->lastDelayDiff > 224)
    {
        aecm->timeForDelayChange = 0;
    } else
    {
        aecm->timeForDelayChange++;
    }
  } else
  {
    aecm->timeForDelayChange = 0;
  }
  aecm->lastDelayDiff = diff;

  if (aecm->timeForDelayChange > 25)
  {
    aecm->knownDelay = WEBRTC_SPL_MAX((int)aecm->filtDelay - 160, 0);
  }
  return 0;
}

按我理解,   delayNew = nSampSndCard - nSampFar; 若 nSampFar == nSampSndCard, 則是我所說的最理想情況, 此時計算出的 delayNew 為 0, 

若 沒有
  if (delayNew < FRAME_LEN)
  {
    WebRtc_MoveReadPtr(aecm->farendBuf, FRAME_LEN);
    delayNew += FRAME_LEN;
  }
那么, 一切還是很完美, knowndelay 最終計算出 0, 然后把剩下的全交給硬件;

但 這個判斷是什么意思呢, 為何要加上 FRAME_LEN, 若這么搞, 則最終意味者 farBufReadPos 將減去某個值, 而這個值初始化為 0, 減去的結果就是 0 + 256 - kowndelay, 那么, 第一個被分析的 part 的參考則可能根本就不是 farendbuf 中, 而是初始化時 memset 的 0; 

另外, nSampFar 也不等於 nSampSndCard, webrtc 啟動階段在 farendbuf 填充到 nSampSndCard * 3 / 4 時就結束了, 那么, nSampFar 小於 nSampSndCard 的可能性很大;

最終的效果, 就是這個算法弄了一系列無法摸清規律的因子進去, 實在搞不懂那是為什么。

2014-10-20 22:07:52
一直在跟看樓主的帖子,樓主好人。一直回答超過1年了。
我們現在也在用AECM做回音消除,現在發現幾款手機的表現不一致。華為和小米表現較好,三星和Nexus表現不行,對比文件一直有一些尾音消不掉。
請教了一些人,有人說可能是時延和抖動不穩定的問題,好像類似220樓的兄弟問的那樣?
也有說還是參數的配置問題?
樓主和各位兄弟有沒有見過這種現象?多謝了。
急需一條明路。。。

2014-10-28 11:34:11
回復 keke274233971: [218樓]

Re[219]: 你好,近段時間沒有跟進WebRTC的進度了,所以僅根據以前的經驗回答,嘰嘰聲應當是 AECM 算法本身的表現,靜音檢測打開,在靜音時發送全零數據,可以減少些許能量較弱的唧唧聲。

2014-10-28 11:35:46
回復 642759382: [219樓]

您好,據我所知目前 Google 的該音頻回調 API 是存在 bug 的,你不能依靠這個回調去保證你的采集時間。我采用的是 AudioRecord.read() 這個阻塞 API。

2014-10-28 11:38:00
回復 zylthinking: [220樓]

您好,這個函數我也看了很久,最后我以為,它僅僅作為穩定延遲,防止延遲突變的工具函數。僅個人理解。

2014-10-28 11:43:31
回復 阿弱德一號: [221樓]

您好,不同 Android 機型的表現不一是正常現象,這取決於其音頻底層實現。
如果使用的是 AECM 模塊,有些許尾音消不掉(或者出現扭曲的唧唧聲)是正常現象,據我所知 Google 前日已經關閉 [issue 1067][https://code.google.com/p/webrtc/issues/detail?id=1067],表示不對此進行修復。
2014-11-12 14:21:36
可以請問一下AGC的使用方法嗎?
int32_t mic_level_out = 0;
int32_t mic_level_in = 0;
uint8_t sat;
WebRtcAgc_VirtualMic(iAGC, (int16_t*)pcm, NULL, 160, 0, &mic_level_in);
if(WebRtcAgc_Process(iAGC, (const int16_t*)pcm, NULL, 160, (int16_t*)agc_buffer, NULL, mic_level_in, &mic_level_out, 0, &sat) == -1) 
  QLOGE("webRTC_audio::PinIn AGC process ERR");


用這種寫法是正確的嗎?每個frame得到的mic_level_in和mic_level_out都是相同的

2014-11-14 08:58:26
回復 lance7: [226樓]

您好 lance7,AGC我之前單獨提取出來使用過,效果不好,最后使用的是整體的 APM 模塊,它自己已經架設好了這些獨立模塊之間的銜接。本文開頭已經提到過,不再提倡單獨編譯AGC、NS等模塊進行處理。

2014-11-14 10:40:22
回復 Bill_Hoo: [22樓]

你好,就是用webrtc測試時,兩個客戶端嘯叫的很厲害,這個怎么解決?有沒有好的方法

2014-11-18 10:16:13
回復 guowei19862006: [228樓]

你好,兩個客戶端嘯叫的很厲害,首先應當排除距離問題,兩個客戶端是否靠得太近導致雙方均能直接采集到對方發出的聲音?

2014-11-21 19:29:12
Bill,你好。我最近在做手機和網頁視頻通話的項目。在我目前在android手機端的語音模塊用了webrtc,PC端沒有加webrtc。通過實驗,ns去噪現在效果顯著,但是aecm去回聲沒有效果。我在PC端用耳機說話,可以聽到自己的回聲。這個回聲應該是到達手機端,喇叭放出后再通過麥克風錄入傳回來的。我把手機端的喇叭靜音,PC端再講話就聽不到自己聲音了。aecm我嘗試了各種delay值,沒有通過計算。看到不少人在這里評論,寫死數值也應該有一點效果的。webrtc去回音去的是那一部分,是不是我在PC端也要加上webrtc的消回音,這樣我PC端才不會聽到自己講話的回聲?期待你的幫助。

2014-11-25 15:50:51
Bill,你好。
我在github上看到了你共享出來的關於aecm的工程(https://github.com/lhc180/webrtc-based-android-aecm),我把他加入到了我的工程中,但是消回音幾部手機測試下來普遍不理想,回應還在。我測試的方式是PC端講話,通過流媒體發往手機端。如果我在PC端聽不到自己的回聲,那么就是去回聲成功了。以三星S5為例,我采集的是8KHZ單聲道,播放延遲和采集延遲總和是50ms以內,AudioTrack和AudioRecord通過getMinBufferSize()得到的buffer大小之和是1920,算下來延遲是120ms,msInSndCardBuf在120-170之間波動,但是回音還在,看到前面好多人評論說回音消掉了,可我卻沒消掉,希望Bill能幫助一下

2014-11-25 18:13:19
回復 zhu4272: [231樓]

你好,GitHub上的工程我已經移除了,你看到的應該是別人fork的分支,文章開頭已經提到,不再建議單獨使用AECM等模塊,請使用APM或者VOE,它們能使底層的獨立音頻模塊更好的協作。
看到你說回聲消除沒有效果,我看你的測試環境估計沒有問題。AECM的兩大接口是否有用對?在其頭文件中有對接口的詳細說明。

2014-12-08 15:53:38
樓主,您好。我想問你一下,webrtc中的自動增益控制(agc)是用來干什么的?

2014-12-17 16:26:38
回復 Bill_Hoo: [17樓]

t_analyze 表示你對音頻幀A調用 farend 的時刻,t_render 表示[硬件]真正播放出幀A的時刻。
t_capture 表示[硬件]采集到音頻幀B(注意跟A沒關系了)的時刻,t_pull 表示幀B被傳入 Process的時刻。
----------

看了以上討論受益匪淺,有幾點疑惑,希望能指點一下。

t_analyze 表示你對音頻幀A調用 farend 的時刻:
這里是指調用WebRtcAecm_BufferFarend()的時刻嗎,還是請求系統播放(AudioTrack.write)的時刻?
----------

t_render 表示[硬件]真正播放出幀A的時刻:
這里是指硬件開始播放幀A的時刻還是播放完幀A的時刻?如何得到這個時間?
如果是用AudioTrack的話,使用那個setMarkerPosition嗎?
----------

t_capture 表示[硬件]采集到音頻幀B(注意跟A沒關系了)的時刻:
這個應該是AudioRecord.read返回時的時刻吧?還是用這個時刻減去所讀buffer的size對應的時間?
----------

t_pull 表示幀B被傳入 Process的時刻:
此處t_pull是指公式中的t_process吧
----------

另外AECM可以直接用多路進來的聲音進行去回聲嗎 還是必須要先把多路合為一路以作為參考 ?
不勝感激

2015-02-12 19:35:33
樓主,我參照你的方法編譯了agc模塊,但是在調用agc的WebRtcAgc_Process函數時 有點疑問,
int WMWebRtcAgc_Process(void* agcInst,
              const int16_t* const* inNear,
              int16_t num_bands,
              int16_t samples,
              int16_t* const* out,
              int32_t inMicLevel,
              int32_t* outMicLevel,
              int16_t echo,
              uint8_t* saturationWarning);
不知道inNear要傳入怎樣的參數,看源碼似乎要調用AudioBuffer的函數去獲取按頻率分段的一個數組,我現在只有一個buffer,單聲道的,怎么去轉換成這樣一個數組

2015-02-15 13:05:24
回復 fingerplay: [235樓]

您好,前面已經說過,不再建議單獨使用各種模塊去進行音頻處理,本文目前參考價值較小。

2015-04-11 16:20:02
樓主,您好。

非常感謝你的文章。我編譯了單獨的ns, 確實可以去掉一些背景噪音。但是同時說話的聲音也變低了,這個可以理解。但是說話的聲音也模糊不清楚了,還需要什么處理么。

多謝回復。

2015-04-24 21:26:59
Bill_Hoo,你好,我近期把webrtc的音頻模塊單獨拿出來編譯,是在ARM+LINUX平台下,音頻驅動是OSS,揚聲器那端我是接了個耳機, 當我說話的時候 我把耳機放到麥克風旁 產生很強的嘯叫,這是延遲計算錯誤的原因嗎?

2015-04-27 15:09:49
回復 Bill_Hoo: [17樓]

你說的(注意跟A沒關系了)是什么意思?難道是不需要包含音頻a, 可以是播放a之前就錄好的音頻幀嗎?

2015-04-28 20:17:40
回復 Bill_Hoo: [236樓]

我先在回音消除, 發現有時候能夠消除一些,比如連續說話,有些回音是可以消除的。但是不能夠完全消除,大牛,有啥高見沒?
2015-04-29 08:37:26
回復 zengwh513: [238樓]

沒有明白你說的把耳機放到麥克風旁什么意思,讓麥克風再次采集耳機里播放出來的聲音?

2015-04-29 09:15:53
回復 Bill_Hoo: [241樓]

是的,這樣是不是一定會產生嘯叫

2015-07-13 17:23:59
"t_analyze 表示你對音頻幀A調用 farend 的時刻,t_render 表示[硬件]真正播放出幀A的時刻。
t_capture 表示[硬件]采集到音頻幀B(注意跟A沒關系了)的時刻,t_pull 表示幀B被傳入 Process的時刻。"

樓主可否將這4個值進行量化?我是指這幾個值在具體實現時的數學表達式。

ps: 例如Android中 t_render 和t_capture 時怎么得到的?貌似上層不能獲取到硬件的播放/錄音時刻吧?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM