看到網上很多希望使用speex aec的, 似乎找不到win32下的例子, 我這裡嘗試做了一個, 效果還行, 使用
上面是三路即時音頻錄下後, 在audacity中顯示, 第一路為本地mic採集, 第二路為本地回放, 第三路為經過speex_echo_cancellation() 處理後的, 這個測試中, 為間斷的讀"1, 2, 3...", 圖中選中的是 "2". capture到playback之間,大約差了1百多毫秒.
下面開始敘述實現過程, 並附上源碼.
其實aec中最困難的問題是如何同步capture和playback, 一開始我也嘗試用 speex_echo_playback()/speex_echo_capture() 但最後還是放棄了, 因為真正的問題在於win32下的即時性太差了, 兩個及時最高優先順序的背景工作執行緒, 也很難對齊.
這個例子中使用dsound進行capture/playback(開始用waveIn/waveOut, 簡直無法忍受, 感興趣的, 關閉代碼中的 USING_DSOUND試試), capture和playback使用 event, 通知獨立的背景工作執行緒, 進行aec.
speex aec中, 希望20ms一個frames, 但是win32下似乎設定20ms一個通知點時, 就亂套了, 至少在我的x61(win2003)是不行的, 所以設定40ms.
通知點:
static HANDLE _evt_notify[2] = { CreateEvent(0, 0, 0, 0), CreateEvent(0, 0, 0, 0), };
建立兩個event對象, 分別對應 capture和playback中使用的 notify point
fifo緩衝 :
實現為環形緩衝, 分別用於 _cbuf_recv: 接收網路資料, _cbuf_input: 儲存capture資料, _cbuf_output: 儲存 playback資料
背景工作執行緒: 實施aec
while true {
WaitForMultipleObjects(_evt_notify);
if (capture notify) {
save data info _cbuf_input; // _cbuf_input 包含回聲需要消除的聲音
}
else {
get data from _cbuf_recv; // _cbuf_recv 儲存來自網路的資料
save data info _cbuf_output // _cbuf_output 作為參考
playback data
}
while (_cbuf_input.data > FRAMESIZE, _cbuf_output.data > FRAMESIZE) {
speex_echo_cancellation();
send_pcm();
}
}
PCM格式
WAVEFORMATEX:
.wFormatTag = WAVE_FORMAT_PCM;
.nChannels = 1;
.nSamplesPerSec = 8000;
.wBitsPerSample = 16;
.nBlockAlign = 2;
.nAvgBytesPerSec = 16000;
開啟playback裝置
DirectSoundCreate8();
SetCooperativeLevel( DSSCL_EXCLUSIVE)
CreateSoundBuffer()
QueryInterface (IID_IDirectSoundNotify8 ..)
SetNotificationPositions( _evt_notify[1]);
Play(0, 0, DSBPLAY_LOOPING);
開啟capature裝置
DirectSoundCaptureCreate8();
CreateCaptureBuffer();
QueryInterface (IID_IDirectSoundNotify8 ..)
SetNotificationPositions( _evt_notify[0]);
Start(DSCBBSTART_LOOPING);
其實xp開始有個 DirectSoundFullDuplexCreate8() 的函數, 可以一次調用同時建立 capture buffer和playback buffer, 源碼中有.
這裡下載 http://download.csdn.net/source/3290182