以下是我根据文档封装的一个类,里面有一个测试程序: //file name: speexEC.h
#ifndef SPEEX_EC_H #define SPEEX_EC_H #include <stdio.h> #include <stdlib.h> #include "speex/speex_echo.h" #include "speex/speex_preprocess.h" class CSpeexEC { public: CSpeexEC(); ~CSpeexEC(); void Init(int frame_size=160, int filter_length=1280, int sampling_rate=8000); void DoAEC(short *mic, short *ref, short *out); protected: void Reset(); private: bool m_bHasInit; SpeexEchoState* m_pState; SpeexPreprocessState* m_pPreprocessorState; int m_nFrameSize; int m_nFilterLen; int m_nSampleRate; float* m_pfNoise; }; #endif
//fine name:speexEC.cpp #include "SpeexEC.h" CSpeexEC::CSpeexEC() { m_bHasInit = false; m_pState = NULL; m_pPreprocessorState = NULL; m_nFrameSize = 160; m_nFilterLen = 160*8; m_nSampleRate = 8000; m_pfNoise = NULL; } CSpeexEC::~CSpeexEC() { Reset(); } void CSpeexEC::Init(int frame_size, int filter_length, int sampling_rate) { Reset(); if (frame_size<=0 || filter_length<=0 || sampling_rate<=0) { m_nFrameSize =160; m_nFilterLen = 160*8; m_nSampleRate = 8000; } else { m_nFrameSize =frame_size; m_nFilterLen = filter_length; m_nSampleRate = sampling_rate; } m_pState = speex_echo_state_init(m_nFrameSize, m_nFilterLen); m_pPreprocessorState = speex_preprocess_state_init(m_nFrameSize, m_nSampleRate); m_pfNoise = new float[m_nFrameSize+1]; m_bHasInit = true; } void CSpeexEC::Reset() { if (m_pState != NULL) { speex_echo_state_destroy(m_pState); m_pState = NULL; } if (m_pPreprocessorState != NULL) { speex_preprocess_state_destroy(m_pPreprocessorState); m_pPreprocessorState = NULL; } if (m_pfNoise != NULL) { delete []m_pfNoise; m_pfNoise = NULL; } m_bHasInit = false; } void CSpeexEC::DoAEC(short* mic, short* ref, short* out) { if (!m_bHasInit) return; speex_echo_cancel(m_pState, (const spx_int16_t *)mic, (const spx_int16_t *)ref, (spx_int16_t *)out, (spx_int32_t *)m_pfNoise); speex_preprocess(m_pPreprocessorState, (__int16 *)out, (spx_int32_t *)m_pfNoise); }
#define NN 160 void main() { FILE* ref_fd, *mic_fd, *out_fd; short ref[NN], mic[NN], out[NN]; ref_fd = fopen ("ref.pcm", "rb"); //打开参考文件,即要消除的声音 mic_fd = fopen ("mic.pcm", "rb");//打开mic采集到的声音文件,包含回声在里面 out_fd = fopen ("echo.pcm", "wb");//消除了回声以后的文件 CSpeexEC ec; ec.Init(); while (fread(mic, 1, NN*2, mic_fd)) { fread(ref, 1, NN*2, ref_fd); ec.DoAEC(mic, ref, out); fwrite(out, 1, NN*2, out_fd); } fclose(ref_fd); fclose(mic_fd); fclose(out_fd); }
示例代码:
Sample code
This section shows sample code for encoding and decoding speech using the Speex API. The commands can be used to encode and decode a file by calling:
% sampleenc in_file.sw | sampledec out_file.sw
where both files are raw (no header) files encoded at 16 bits per sample (in the machine natural endianness).
sampleenc.c
sampleenc takes a raw 16 bits/sample file, encodes it and outputs a Speex stream to stdout. Note that the packing used is NOT compatible with that of speexenc/speexdec.
#include <speex/speex.h>
#include <stdio.h>
#define FRAME_SIZE 160
int main(int argc, char **argv)
{
char *inFile;
FILE *fin;
short in[FRAME_SIZE];
float input[FRAME_SIZE];
char cbits[200];
int nbBytes;
void *state;
SpeexBits bits;
int i, tmp;
state = speex_encoder_init(&speex_nb_mode);
tmp=8;
speex_encoder_ctl(state, SPEEX_SET_QUALITY, &tmp);
inFile = argv[1];
fin = fopen(inFile, "r");
speex_bits_init(&bits);
while (1)
{
fread(in, sizeof(short), FRAME_SIZE, fin);
if (feof(fin))
break;
for (i=0;i<FRAME_SIZE;i++)
input[i]=in[i];
speex_bits_reset(&bits);
speex_encode(state, input, &bits);
nbBytes = speex_bits_write(&bits, cbits, 200);
fwrite(&nbBytes, sizeof(int), 1, stdout);
fwrite(cbits, 1, nbBytes, stdout);
}
speex_encoder_destroy(state);
speex_bits_destroy(&bits);
fclose(fin);
return 0;
}
sampledec.c
sampledec reads a Speex stream from stdin, decodes it and outputs it to a raw 16 bits/sample file. Note that the packing used is NOT compatible with that of speexenc/speexdec.
#include <speex/speex.h>
#include <stdio.h>
#define FRAME_SIZE 160
int main(int argc, char **argv)
{
char *outFile;
FILE *fout;
short out[FRAME_SIZE];
float output[FRAME_SIZE];
char cbits[200];
int nbBytes;
void *state;
SpeexBits bits;
int i, tmp;
state = speex_decoder_init(&speex_nb_mode);
tmp=1;
speex_decoder_ctl(state, SPEEX_SET_ENH, &tmp);
outFile = argv[1];
fout = fopen(outFile, "w");
speex_bits_init(&bits);
while (1)
{
fread(&nbBytes, sizeof(int), 1, stdin);
fprintf (stderr, "nbBytes: %d\n", nbBytes);
if (feof(stdin))
break;
fread(cbits, 1, nbBytes, stdin);
speex_bits_read_from(&bits, cbits, nbBytes);
speex_decode(state, &bits, output);
for (i=0;i<FRAME_SIZE;i++)
out[i]=output[i];
fwrite(out, sizeof(short), FRAME_SIZE, fout);
}
speex_decoder_destroy(state);
speex_bits_destroy(&bits);
fclose(fout);
return 0;
}
在Speex(www.speex.org)的最新版本中,开始集成了回音消除的模块,而回音消除一直是Voip之中亟待解决的主要问题。
很多朋友和我说speex的aec模块的效能并不好,我们先来看一下speex的aec的api调用方式。
/*
*创建AEC对象
*/
SpeexEchoState *echo_state = speex_echo_state_init(frame_size, filter_length);
frame_size 的取值最好是一个编码的frame大小, 在低带宽条件下,一般延迟20ms,而大小为160
filter_length,最好是房间内反射时间的1/3
如: 一个房间的反射时延为300ms
那么这个filter_length就最好是100ms(这个长度又被称为tail length).
而其中filter_length的设定是一个关键。
/*
*执行AEC
*/
speex_echo_cancel(echo_state, input_frame, echo_frame, output_frame, residue);
其中:
input_frame: 就是被声卡捕捉到的声音
echo_frame: 是由扬声器播放出的声音,这个声音是需要从 input_frame中抵消的声音.
output_frame 是处理完以后输出的声音
residue是一个可选参数,如果不使用可以将之设置为NULL, 也可以通过preprocessor 来控制
问题的关键是 处理input和echo 之间的关系,
也就是说在捕捉到的信号和播放的信号之间的延迟必须足够的小,才可以提高效率.
writetosndcard(echo_frame, frame_size)
readfromsndcard(input_frame, frame_size)
speex_echo_cancel(echo_state, input_frame, echo_frame, output_frame, residue)
如果你想要尽可能的减小信号中的回音,那么可以将residue这个参数设置为噪音参数.
我相信在大多数情况下,都是因为声音捕捉和声音播放之间的同步问题没有处理好,导致的音频质量下降。
/*
*销毁和复位
*/
speex_echo_state_destroy(echo_state);
speex_echo_state_reset(echo_state);
不再复述了!
说明:
据说在Speex的最新的1.2beta版本上,Speex提供了可选择的,简化的API,来提高echo执行过程中的同步问题。
speex的窄带编码算法是基于 8k 16bit 单声道,每帧数据160个采样
speex内部将160个采样点,分成了4个子帧,每个子帧40个(…)