Speex 一个双声道回声消除的小demo
2018-04-20 本文已影响18人
FlyingPenguin
Speex中的回声消除可以对双声道(aec stereo)或多声道的数据进行直接处理。
以下以双声道进行举例:
主要注意事项有三点:
1. 用speex_echo_state_init_mc来进行初始化
echo_state = speex_echo_state_init_mc(frame_size, filter_length, 2, 2);
2. frame_size的大小
在Speex 双声道回声消除中frame_size的含义中已经详细说明,frame_size就是单声道每次处理的采样点的个数。
以48k,取10ms为例:
int sampleRate = 48000;
int nFrameSizeInMS = 10;
int frame_size = (nFrameSizeInMS * sampleRate * 1.0) / 1000;
计算结果为480.
3. 每次读取多少数据给speex_echo_cancellation
通过对源码进行分析,如果声道数为N,则应该读取N*frame_size的数据给回声消除的模块。
比如双声道,则每次近端数据和远端数据应该读取2 * frame_size
个采样点的数据给回声消除。
双声道回声消除的小demo如下:
https://github.com/ZhaoliangGuo/speex/blob/master/testecho_stereo/testecho_stereo.cpp
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "targetver.h"
#include <stdio.h>
#include <tchar.h>
#include "arch.h"
#ifdef __cplusplus
extern "C" {
#endif
#include "speex/speex_echo.h"
#include "speex/speex_preprocess.h"
#ifdef __cplusplus
}
#endif
#include <windows.h>
int main(int argc, char **argv)
{
int sampleRate = 48000;
int nFrameSizeInMS = 10;
int nFilterLenInMS = 120;
SpeexEchoState *echo_state = NULL;
SpeexPreprocessState *preprocess_state = NULL;
SYSTEMTIME st;
::GetLocalTime(&st);
int frame_size = (nFrameSizeInMS * sampleRate * 1.0) / 1000;
int filter_length = (nFilterLenInMS * sampleRate * 1.0) / 1000;
char szMicFileName[] = ".\\near48_stereo.pcm";
char szSpeakerFileName[] = ".\\far48_stereo.pcm";
char szOutputFileName[512];
sprintf(szOutputFileName, ".\\output_stereo_samplerate_%d_NN_%4d_TAIL_%4d_%04d%02d%02d_%02d%02d%02d.pcm",
sampleRate,
frame_size, filter_length,
st.wYear, st.wMonth, st.wDay,
st.wHour, st.wMinute, st.wSecond);
printf("%s\n", szOutputFileName);
LARGE_INTEGER timeStartCount;
LARGE_INTEGER timeEndCount;
LARGE_INTEGER timeFreq;
QueryPerformanceFrequency(&timeFreq);
QueryPerformanceCounter(&timeStartCount);
FILE *echo_fd, *ref_fd, *e_fd;
ref_fd = fopen(szMicFileName, "rb");
echo_fd = fopen(szSpeakerFileName, "rb");
e_fd = fopen(szOutputFileName, "wb");
echo_state = speex_echo_state_init_mc(frame_size, filter_length, 2, 2); // for stereo
frame_size *= 2; // length read each time
preprocess_state = speex_preprocess_state_init(frame_size, sampleRate);
speex_echo_ctl(echo_state, SPEEX_ECHO_SET_SAMPLING_RATE, &sampleRate);
speex_preprocess_ctl(preprocess_state, SPEEX_PREPROCESS_SET_ECHO_STATE, echo_state);
short *echo_buf;
short *ref_buf;
short *e_buf;
echo_buf = new short[frame_size];
ref_buf = new short[frame_size];
e_buf = new short[frame_size];
while (!feof(ref_fd) && !feof(echo_fd))
{
int nLen = fread(ref_buf, sizeof(short), frame_size, ref_fd);
if (nLen < 0)
{
break;
}
nLen = fread(echo_buf, sizeof(short), frame_size, echo_fd);
if (nLen < 0)
{
break;
}
speex_echo_cancellation(echo_state, ref_buf, echo_buf, e_buf);
speex_preprocess_run(preprocess_state, e_buf);
fwrite(e_buf, sizeof(short), frame_size, e_fd);
}
// Destroys an echo canceller state
speex_echo_state_destroy(echo_state);
speex_preprocess_state_destroy(preprocess_state);
fclose(e_fd);
fclose(echo_fd);
fclose(ref_fd);
if (echo_buf)
{
delete [] echo_buf;
echo_buf = NULL;
}
if (ref_buf)
{
delete [] ref_buf;
ref_buf = NULL;
}
if (e_buf)
{
delete [] e_buf;
e_buf = NULL;
}
QueryPerformanceCounter(&timeEndCount);
double elapsed = (((double)(timeEndCount.QuadPart - timeStartCount.QuadPart) * 1000/ timeFreq.QuadPart));
printf("AEC Done. TimeDuration: %.2f ms\n", elapsed);
getchar();
return 0;
}