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

不推薦單獨編譯  WebRTC  中的各個模塊出來使用。

   

昨天有幸在 Google 論壇裏詢問到 AECM 模塊的延遲計算一事,Project member 說搗騰這個延遲實際上對 AECM 的效果沒有幫助,這個延遲值僅在 AECM 啓動時加快內置延遲估算器的收斂,若是更新的延遲有誤,甚至會使 AECM 內置的延遲估算器出現錯誤的偏移,他建議我使用一個理論上的定值,Chrome 中他們用了 100ms。我以前也在 AECM 裏看到了它對內置的 delay_estimator 二進制延遲估算器有很大的依賴,近端與遠端數據匹配與否,幾乎所有仰仗這個延遲估算器的結果。所以,對於 AECM 的使用,是否還須要花時間去計算這個系統延遲,bill 再也不置評,箇中效果你們本身實踐和把握。

php

   其次,AECM 產生的唧唧聲 Project member 澄清這不是 bug,而是 AECM 算法原本就有此表現,屬正常現象。html


   本文已有一年之久,隨着本身在學習過程當中認識的加深,以及期間和各位在文後的討論,擔憂後來者照着這一年前的文章走彎路,bill 以爲有必要對文章作一個更新,點出本身走的彎路,以避免誤導後來者。java

   1. 本身最開始是把 AECM、NS、VAD、AGC 各個模塊單獨提取出來使用,如今看來實屬麻煩,且效果也不甚理想。若是你們的項目沒有特殊的要求,大可將整個語音引擎 VoiceEngine 編譯出來使用。就我我的而言,目前的解決方案是獨立編譯使用音頻處理單元 AudioProcessingModule,由於 APM 是一個純淨的音頻處理單元,其接口僅與音頻處理有關,APM的使用加上上層代碼的優化,能夠保證基本的通話效果(離完美還很遠),回聲基本是沒有的。主要會存在兩個問題,一是AECM出來的效果會有唧唧聲,這個聲音能夠經過對延遲計算的不斷優化而獲得改善,最終能夠作到說幾句話以後有1~2次唧唧聲。二是通話過程當中聲音會忽大忽小,目前我是懷疑由AECMdouble talk處理引發的,具體的還要本身去倒騰。

linux

   2. 關於回聲消除濾波器延遲的計算,以前本身一直認爲只要這個延遲計算準確,就能獲得理想的回聲消除效果,如今發現這個想法太幼稚,一是AECM算法自己有必定侷限性,二是Android上的採集延遲沒有系統API支持,很難計算準確,而播放端的API又不能保證其準確性。目前個人能力只能作到儘可能優化上層的延遲計算,儘可能減小由Android音頻API對延遲形成的影響。android

   3. 在 Android 上層優化計算系統音頻延遲的代碼達到必定瓶頸後,能夠將優化目標轉向 1)AECM 算法。 2)優化AEC(PC)(使其能在手機上正常運行,目前AEC-PC默認濾波器長度爲12塊,每塊64個點,(12*64=768採樣)即AEC-PC僅能處理48ms的單聲道16kHz延遲的數據,而Android的音頻系統延遲大多在100ms以上,所以既要增長AEC-PC濾波器長度又要保證其運行效率是優化的重點) 3)其餘模塊的優化(好比抖動緩衝區等)。

git

   4. 文後的源碼列表已通過時,因爲我目前再也不支持單獨編譯這些模塊,恕我再也不更新該列表,如確有獨立編譯需求的,可自行在WebRTC項目對應目錄中找到須要的文件。github

  

前言web

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

算法

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


   通過本身兩月有餘的搗騰和測試,終於在 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.一、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樓]

你好:
A一、幀A和幀B沒有直接聯繫,咱們只須要知道總延遲,至於這兩幀是否爲相互包含的音頻沒有關係。
A二、根據你的描述,感受你的思路是這樣的:記錄幀A放進farend buffer的時刻,而後要讓該時刻去匹配幀B傳入 process 的時刻。若是你是這樣想的,是由於你以爲幀A和幀B是有關聯的。實際上這兩幀並無直接聯繫。如A1所述,咱們須要知道的僅僅是兩個時間: 
T1(系統的播放延遲) = 幀A被硬件播放出來的時刻 - 幀A被放進 farend 的時刻;
T2(系統的採集延遲) = 幀B被放進 process 的時刻 - 幀B被硬件採集的時刻;
total delay = T1 + T2;

A三、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)。
一、首先單個延遲很小,t_render - t_analyze大約在25-55ms區間內,t_pull - t_capture對我來講約等於0,由於我採集後立刻就進行process了;和博主說的大約100--200ms差距比較大。此外我是不斷從新計算延遲值的,博主您是計算出一個值就固定使用仍是也不斷計算?
二、opensles的錄音放音狀況是:某個時間段內獲得m個錄音的語音幀,而後下一個時間段播放n個語音幀,如此反覆。因此若是不變通,就會變成調用m次WebRtcAecm_BufferFarend()後再調用n次WebRtcAecm_Process,4個時間點怎麼肯定就很曖昧了:由於延遲值實際上是給錄音後的process用的,因此屬於錄音的t_capture和t_pull的時間點是明確的,可是放音的t_analyze和t_render用m個語音幀裏的哪一個呢?第一個、最後一個、平均值?我試驗了最後一個(距離錄音動做最近的上個播放延遲),發現出來的結果不對;直覺上第一個、平均值也應該不對。 
最後,從屢次統計結果發現,m和n是同一個數量級,差距不大(這是顯然的)。
三、從我這段時間的測試數據看,android機系統有本身的opensles緩衝區,好比錄音,我設置錄音緩衝區是80個sample,系統會先錄製一段時間,數據充滿它本身的緩衝區(系統緩衝區)後再調用回調函數拷貝到我設置的緩衝區(用戶緩衝區),放音也同理(提供數據足夠快的狀況下),這就解釋了上面第二點的狀況:先提供m個錄音語音幀,再消費n個放音語音幀。
這裏有個問題,我翻遍開發文檔發現系統沒提供接口來獲得系統內置緩衝區大小,我甚至在google play上找到個audio buffer size的app,它用本身的算法來計算系統opensles的內置緩衝區和採樣率大小,但個人測試機算出來的結果和個人測試數據又無法對上。java層的AudioRecord.getMinBufferSize()以及api17後倒有個相關的AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER,這兩個試驗過發現也不對。
四、針對2和3的狀況,我設想過能不能把m個錄音幀當一個大幀、n個放音幀當一個大幀來處理。WebRtcAecm_BufferFarend()好像能夠累積數據,只要長度不超過它的緩衝區最大值就好;但WebRtcAecm_Process()只能傳進80或160個sample,這就矛盾了。
五、好像蘋果機的機制是錄一個幀再放一個幀,很是符合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,年前去作別的事情了,呵呵,因此沒能及時關注。
一、聲音變小應該是能量太低,好比說站在4米外講話,形成語音狀態檢測的誤判(然而檢測閥值又不能過低),把較低能量的語音給消除掉了。如今通過調整好不少了,用手機的話應該是沒有我這個問題的。
二、您說的唧唧聲音是否身處的環境有各類機器的叫聲?好比打印機,或着是在工廠裏?若是是的話,我也不知道,這是一個難題,目前我也在爲這個苦惱。使用低通濾波器,好比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樓朋友的相似問題:
  一、我單獨編譯了aec模塊,並封裝提供了兩個接口:一、添加遠端數據,二、添加近端數據。然而把遠端數據添加進去後作了混音處理後變成了異步數據,我想問下:把混音後的遠端數據添加到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 時怎麼獲得的?貌似上層不能獲取到硬件的播放/錄音時刻吧?
相關文章
相關標籤/搜索