实践windows下 speex 1.2rc1 中的回声消除功能(转)

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

原文地址:https://www.cnblogs.com/myitm/p/2112843.html