看到网上很多希望使用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