speex 回聲消除的用法
speex的回聲消息
就是speex_echo_cancellation函數的正確用法
回聲消息的原理:
對參考聲音(解碼的對端原始語音包)做延遲(會有多個延遲,如麥克風直接采集到音箱的聲音,經牆壁反射后再次采集),衰減,
從聲卡里采集到的語音,做一個語音合成。
回聲產生的條件:
通話中,有一方使用音箱(或者雙方都用音箱)。
在實際中如何使用speex_echo_cancellation這個函數呢?錯誤的使用,將導致speex無法快速地收斂回聲濾波器的參數。
使用音箱的那一方,這里我們稱之為"發送方",調用speex_echo_cancellation,
這樣做就繞開了網絡延遲,引起對算法收斂的干撓。
這是第一點要注意的
(也可以在"接收方"調用speex_echo_cancellation,但網絡出現抖動時,就會使算法無法快速收斂,就無法消除回聲了)
這樣,我們的代碼中,大概會是這樣的邏輯:
解碼網絡語音包(記為 play)
寫入聲卡
采集麥克風的聲音(記為rec)
調用speex_echo_cancellation 參play與rec傳給這個函數
回想一下,應用層的程序可能會是這樣(當然您的程序也可能不是這樣,但情形類似):
一個接收線程,收包,放音
一個發送線程,錄音,發包
我們自然會在錄音線程里調用speex_echo_cancellation
但這有一個問題,錄音線程與放音線程因為系統的調度問題,也會造成抖動,導致speex的回聲消除算法無法收斂。
以下的一個程序模形,讀者們可以參考
1 接收線程A,解碼網絡語音包,接語音包推入一個消息隊列A
2 放音錄音線程B,從隊列A中取出語音包,放音,錄音,錄音得到的語音包,通過speex_echo_cancellation處理后,存入隊列B
3 發送線程C,從隊列B中取語音包,編碼,發送
簡單地說,就是用一個線程放音,錄音,然后echo cancel,這樣就不存在線程調度引起的延遲抖動
采用這種方式,就避免了因為線程調度引起的抖動,避免了不確定的延遲對speex算法收斂過程的干撓。
最后一個干撓因素:os提供的錄音放音接口也是異步的。。。
這個干撓因素基本在應用層是無法排除的了。。。可能就是幾毫秒的誤差,但足以干撓回聲消除算法了。
多路語音(會議)
選一個超級節點做合成語音,或者終端對語音進行合成,之后,處理就變成與單對單語音通話類似的情形了
直接上speex_echo_cancellation