您的位置:首页 > 其它

speex回音消除

2017-10-18 09:56 671 查看
传给回声消除器的两个声音信号,必须同步得非常的好。具体过程如下:

1、在B端,接收到A说的话以后,要把这些话音数据传给回声消除器做参考,然后再传给声卡,声卡再通过扬声器放出来,这有一段延时。

2、MIC采集B说话的声音和扬声器的声音,然后传给回声消除器,与A传过来的数据比较,从采集到的数据中把频域和参考数据相同的部分消除掉。

如果传给消除器的两个信号同步得不好,即两个信号找不到频域相同的部分,就没有办法进行消除了。

下面的例子采集的声音都在本地,所以延时非常小,主要是实现从一段声音里面消除掉一部分声音,这部分声音相当于回音,从而模拟VOIP语音通信

的消回音场景。

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "speex/speex_echo.h"
#include "speex/speex_preprocess.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define NN 128
#define TAIL 1024
int main(int argc, char **argv)
{
FILE *echo_fd, *ref_fd, *e_fd;
short echo_buf[NN], ref_buf[NN], e_buf[NN];
SpeexEchoState *st;
SpeexPreprocessState *den;
int sampleRate = 8000;

if (argc != 4)
{
fprintf(stderr, "testecho mic_signal.sw speaker_signal.sw output.sw\n");
exit(1);
}
echo_fd = fopen(argv[2], "rb");//打开参考文件,即要消除的声音
ref_fd  = fopen(argv[1],  "rb");//打开mic采集到的声音文件,包含回声在里面
e_fd    = fopen(argv[3], "wb");//消除了回声以后的文件

/** Creates a new echo canceller state
* @param frame_size Number of samples to process at one time (should correspond to 10-20 ms)
* @param filter_length Number of samples of echo to cancel (should generally correspond to 100-500 ms)
* @return Newly-created echo canceller state
*/
//
st = speex_echo_state_init(NN, TAIL);

/** Creates a new preprocessing state. You MUST create one state per channel processed.
* @param frame_size Number of samples to process at one time (should correspond to 10-20 ms). Must be
* the same value as that used for the echo canceller for residual echo cancellation to work.
* @param sampling_rate Sampling rate used for the input.
* @return Newly created preprocessor state
*/
den = speex_preprocess_state_init(NN, sampleRate);

/** Used like the ioctl function to control the echo canceller parameters
*
* @param st Echo canceller state
* @param request ioctl-type request (one of the SPEEX_ECHO_* macros)
* @param ptr Data exchanged to-from function
* @return 0 if no error, -1 if request in unknown
*/
speex_echo_ctl(st, SPEEX_ECHO_SET_SAMPLING_RATE, &sampleRate);

/** Used like the ioctl function to control the preprocessor parameters
* @param st Preprocessor state
* @param request ioctl-type request (one of the SPEEX_PREPROCESS_* macros)
* @param ptr Data exchanged to-from function
* @return 0 if no error, -1 if request in unknown
*/
speex_preprocess_ctl(den, SPEEX_PREPROCESS_SET_ECHO_STATE, st);

while (!feof(ref_fd) && !feof(echo_fd))
{
fread(ref_buf, sizeof(short), NN, ref_fd);
fread(echo_buf, sizeof(short), NN, echo_fd);

/** Performs echo cancellation a frame, based on the audio sent to the speaker (no delay is added
* to playback in this form)
*
* @param st Echo canceller state
* @param rec Signal from the microphone (near end + far end echo)
* @param play Signal played to the speaker (received from far end)
* @param out Returns near-end signal with echo removed
*/
speex_echo_cancellation(st, ref_buf, echo_buf, e_buf);

/** Preprocess a frame
* @param st Preprocessor state
* @param x Audio sample vector (in and out). Must be same size as specified in speex_preprocess_state_init().
* @return Bool value for voice activity (1 for speech, 0 for noise/silence), ONLY if VAD turned on.
*/
speex_preprocess_run(den, e_buf);
fwrite(e_buf, sizeof(short), NN, e_fd);
}
/** Destroys an echo canceller state
* @param st Echo canceller state
*/
speex_echo_state_destroy(st);

/** Destroys a preprocessor state
* @param st Preprocessor state to destroy
*/
speex_preprocess_state_destroy(den);
fclose(e_fd);
fclose(echo_fd);
fclose(ref_fd);
return 0;
}


在局域网内实现实时语音,传输层协议使用UDP协议,如果直接使用AudioRecord进行录制音频流并发送到另一端进行播放,音质会非常差,而且断断续续,原因如下:

采样频率: samplerate = 44.1KHz

采样精度:16bit

声道配置:2(双声道)

那么,码率 V = 44.1K * 16 *2 = 1411.2 Kbps = 176.4KBps,即每秒传输速率大概176.4KB,

若音频帧时间为20ms,每个音频数据包大小为
b46a
size = 176.4KBps * 0.02s = 3.528KB,

一般情况下,我们每次读取一个音频帧的数据,可以取整为3600Byte,

所以 每秒大概发送 176.4/3.6=49 个数据包,每个数据包大小为3.6KB。

如果再考虑到数据报头,实测每秒发送约45个数据包,每秒传输速率大概180KB。

由于一般都是使用手机连接Wifi,这就要求网络质量和硬件设备必须很好,而且信道干扰较弱,并且链接的设备不能过多。只要稍微信号不好,就会导致丢包率特别高,而且延时十分大,根本无法满足通信的需要。在这种情况下,我们就需要进行语音压缩、降噪等处理。

采样率:

实际中,人发出的声音信号为模拟信号,想要在实际中处理必须为数字信号,即采用采样、量化、编码的处理方案。处理的第一步为采样,即模数转换。简单地说就是通过波形采样的方法记录1秒钟长度的声音,需要多少个数据。根据奈魁斯特(NYQUIST)采样定理,用两倍于一个正弦波的频繁率进行采样就能完全真实地还原该波形。所以,对于声音信号而言,要想对离散信号进行还原,必须将抽样频率定为40KHz以上。实际中,一般定为44.1KHz。44.1KHz采样率的声音就是要花费44100个数据来描述1秒钟的声音波形。原则上采样率越高,声音的质量越好,采样频率一般共分为22.05KHz、44.1KHz、48KHz三个等级。22.05 KHz只能达到FM广播的声音品质,44.1KHz则是理论上的CD音质界限,48KHz则已达到DVD音质了。

码率:

对于音频信号而言,必须进行编码。在这里,编码指信源编码,即数据压缩。如果,未经过数据压缩,直接量化进行传输则被称为PCM(脉冲编码调制)。要算一个PCM音频流的码率是一件很轻松的事情,采样率值×采样大小值×声道数 bps。一个采样率为44.1KHz,采样大小为16bit,双声道的PCM编码的WAV文件,它的数据速率则为 44.1K×16×2 =1411.2 Kbps。我们常说128K的MP3,对应的WAV的参数,就是这个1411.2 Kbps,这个参数也被称为数据带宽,它和ADSL中的带宽是一个概念。将码率除以8,就可以得到这个WAV的数据速率,即176.4KB/s。这表示存储一秒钟采样率为44.1KHz,采样大小为16bit,双声道的PCM编码的音频信号,需要176.4KB的空间,1分钟则约为10.34M,这对大部分用户是不可接受的,尤其是喜欢在电脑上听音乐的朋友,要降低磁盘占用,只有2种方法,降低采样指标或者压缩。降低采样指标是不可取的,因此专家们研发了各种压缩方案。最原始的有DPCM、ADPCM,其中最出名的为MP3。所以,采用了数据压缩以后的码率远小于原始码。

音频帧:

音频的帧的概念没有视频帧那么清晰,几乎所有视频编码格式都可以简单的认为一帧就是编码后的一副图像。但音频帧跟编码格式相关,它是各个编码标准自己实现的。

如果以PCM(未经编码的音频数据)来说,它根本就不需要帧的概念,根据采样率和采样精度就可以播放了。比如采样率为44.1HZ,采样精度为16位的音频,你可以算出bitrate(比特率)是44100*16kbps,每秒的音频数据是固定的44100*16/8 字节。

mp3帧较为复杂一点,包含了更多的信息,比如采样率,比特率,等各种参数。具体如下:

音频数据帧个数由文件大小和帧长决定,每个FRAME的长度可能不固定,也可能固定,由位率bitrate决定,每个FRAME又分为帧头和数据实体两部分,帧头记录了mp3的位率,采样率,版本等信息,每个帧之间相互独立。

每帧持续时间(秒) = 每帧采样数 / 采样频率(HZ)

可以这么理解:每帧采用数就是要采取的总数,采样率就是采取的速度,相除就得到时间。

函数名称 speex_echo_state_init

头文件 #include “speex_echo.h”

函数功能 初始化Speex声学回音消除器句柄。

Speex声学回音消除器句柄用于对16位有符号整型单声道原始音频数据进行回音消除。

函数声明 SpeexEchoState * speex_echo_state_init (int frame_size, int filter_length);

函数参数

frame_size,[输入]:存放一帧16位有符号整型单声道原始音频数据的采样数量,单位个,一般为10毫秒到20毫秒。

例如:8000Hz采样频率20毫秒本参数就是160。

filter_length,[输入]:存放回音消除过滤器的采样数量,单位个,一般为100毫秒到500毫秒。

如果采样频率是8000Hz,选择300毫秒,本参数就是8000÷1000×300。

本参数具体大小要慢慢调,调的好不好直接影响回音消除的效果。

返回值 Speex声学回音消除器句柄。

其他说明

如果是对一条音频流回音消除,那么回音消除从开始到结束都应该用一个Speex声学回音消除器句柄,中途不要更换Speex声学回音消除器句柄,也不要用一个Speex声学回音消除器句柄给多条音频流回音消除,否则会导致回音消除后的音频数据不正确。当Speex声学回音消除器句柄不再使用时,必须调用speex_echo_state_destroy()函数销毁Speex声学回音消除器句柄,否则会内存泄漏。

函数名称 speex_echo_state_reset

头文件 #include “speex_echo.h”

函数功能 重置Speex声学回音消除器句柄为初始状态。

函数声明 void speex_echo_state_reset (SpeexEchoState * st)

函数参数

st,[输入]:存放Speex声学回音消除器句柄。

返回值 无

函数名称 speex_echo_ctl

头文件 #include “speex_echo.h”

函数功能 控制Speex声学回音消除器句柄的相关参数。

函数声明 int speex_echo_ctl (SpeexEchoState * st, int request, void * ptr);

函数参数

st,[输入]:存放Speex声学回音消除器句柄。

request,[输入]:存放需要控制的参数,可以为(选一至一个):

SPEEX_ECHO_GET_FRAME_SIZE宏(0x0003):获取Speex声学回音消除器句柄处理一帧时的采样数量,ptr参数为int型变量的内存指针。

SPEEX_ECHO_SET_SAMPLING_RATE宏(0x0018):设置Speex声学回音消除器句柄处理一帧时的采样频率,单位Hz,ptr参数为int型变量的内存指针,默认为8000。

SPEEX_ECHO_GET_SAMPLING_RATE宏(0x0019):获取Speex声学回音消除器句柄处理一帧时的采样频率,ptr参数为int型变量的内存指针。

ptr,[输入&输出]:存放控制参数。本参数是根据request参数来定义的。

返回值

0:成功。

-1:request参数无法识别。

speex_echo_cancel(废弃的)

本函数已经废弃,使用无效。

函数名称 speex_echo_cancellation

头文件 #include “speex_echo.h”

函数功能 根据Speex声学回音消除器句柄,对一帧16位有符号整型单声道原始音频数据进行预处理。

函数声明

void speex_echo_cancellation ( SpeexEchoState * st,

const spx_int16_t * rec,

const spx_int16_t * play,

spx_int16_t * out

);

函数参数

st,[输入]:存放Speex声学回音消除器句柄。

rec,[输入]:存放由音频输入设备录音的一帧16位有符号整型单声道原始音频数据,不能为NULL。

play,[输入]:存放由音频输出设备播放的一帧16位有符号整型单声道原始音频数据,不能为NULL。

out,[输出]:存放经过回音消除后的一帧16位有符号整型单声道原始音频数据,不能为NULL。

返回值 无

其他说明

回音消除的原理就是将音频输入数据中所采集到的音频输出数据清除掉,所以要求音频输入数据和音频输出数据必须是同步的,且音频输入数据中所采集到的回音数据肯定在实际的音频输出数据之后出现。目前还没有找到不同步就可以做回音消除的方法。做回音消除后,最好再使用Speex预处理器对音频输入数据进行噪音抑制、混响消除、自动增益控制、残余回音消除等预处理,不要在回音消除前做预处理操作,否则回音消除效果将降低。如果感觉回音消除效果不好,就把rec、play、out这些参数打印日志出来看看,然后调整speex_echo_state_init()函数的filter_length参数,再测试。

已知问题:

当录音里的回音的音量大于播放的音量,则本函数认为这个不是回音,就不会消除掉。这种情况主要在开了麦克风增益、或者喇叭离麦克风特别近时才会产生。

当录音里的回音和播放的声音区别较大时,则本函数认为这个不是回音,就不会消除掉。这种情况主要是麦克风或音响的音质不好造成的,大多出现在台式机,笔记本一般不会。音频流的刚开始几秒钟内产生的回音可能消除不掉。具体原因未知。

函数名称 speex_echo_state_destroy

头文件 #include “speex_echo.h”

函数功能

销毁Speex声学回音消除器句柄。

函数声明 void speex_echo_state_destroy (SpeexEchoState * st);

函数参数

st,[输入]:存放Speex声学回音消除器句柄。

返回值 无
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: