在VOIP的音频算法中,回音处理已经成为一个关系通话质量的主要问题。
回声的产生在IP网络主要有两种:
1.声学回声
2.电路回声
声学回声主要又分成以下几种:
a ) 直接回声:由扬声器产生的声音未经任何反射直接进入麦克风
b ) 间接回声: 由扬声器发出的声音经过多次反射后,再进入Mic
对于第二种回声,拥有多路径,时变性的特点.是比较难处理的.
由于IP网络下的传输的延迟较大,而一般情况下,对于人耳,如果声音延迟达到了10ms以上的话,那么回声就会越来越明显.
一般来讲,VOIP中的声音延迟主要来自于几个方面:
1. 编码延迟: 一般情况下编码算法在声音压缩时都会产生延迟,就我们采用的Speex来讲,延迟大概在20ms左右
2. 处理延迟, 封装时延, 缓冲时延等
3. 在IP网络中数据的传输过程也会照成延时.这由当前的网络状况决定.
回声消除的模型:
a) 建立远端声音模型,进行回声估计, 从采集的值中减去估计值
b) 声学模型
但是的speex资料像其它大都数专用库一样,并没有大多的中文资料.所以在这里我决定将里面最核心的编程技术翻译出来.一来是练习练习自己翻译资料的能力,二来是方便一些英语水平较差的朋友.由于本人能力有限,有些感觉有出入或难理解的地方可以去speex的官方网站www.speex.org找到英文原版的说明.
1,speex的介绍(略)
2特征描述
这个章节展示了speex的主要特征,以衣一些关于对话(speech)编码的一个概念,以便帮助我们更好的了解下一章节.
取样率(Sampling rate)
Speex主要是设计了三种不同的取样率:8kHz,16kHz,32kHz.这些分别代表了窄宽(narrowband),多频率,超声.
质量
Speex编码大都数时间是被一个范围为0到10的质量参数来控制 的.在一个比特率为常量(CBR)的操作中,质量参数是一个整数,而对于变动的比特率(VBR)参数是一个float;
复杂性(变量)
用speex,你可以将编码设置成允许的复杂度.这由一个范围为1到10的整数来控制完成,就像你用选项-1到-9来控制gzip和bzip2的压缩质量.在通常的运用中,噪声级别的复杂度1是在1到2dB之间,比复杂度10要高,但是CPU需要复杂度10大概5倍高行复杂度1.在实践中,最好的是设置在2到4之间,尽管更高的设定通常有用,当编码一个非对话声音(non-speech sounds)像DTMF语调(tones).
变波特率(VBR
Variable bit-rate (VBR) allows a codec to change its bit-rate dynamically to adapt to
变波特率(VBR)允许编码动态地改变它的波特率以适应声音编码的”难度”.在speex举例来说,
像元音(vowel)和瞬间高音(high-enenrg transients)需要个高的比特率来取得一个不错的质量,而摩擦音(fricative)可以被充分地用相对较少的字节来进行编码.由于上面这个原因,VBR可以调节到一个低的比特率却达到一个同样的质量,或者用某个比特率达到更好的质量.尽管有上面这些优点,但是VBR也有两个主要的缺点.首先,仅仅靠指定质量值,这里没有一个关于最后平均比特率的保证.(译者注:作者大概是想说没有什么明确的方法知道质量值)此外,对一些即时通信,像IP电话(VoIP)这种包含着最大的比特率的,必须把比特率设为足够低以适应传输通道.
平均比特率(ABR)
平均比特率通过动态地调节VBR质量去得到一个确定的目标的比特率,从而解决了VBR中的一个问题..因为质量/比特率被即时的调整了,整体质量将会稍稍低于由VBR对一个
设置得和目标平均比特率非常接近的质量数编码得到的结果.
声音生动性检测(VAD)
声音生动性检测将会发觉音频正在被编码成对话,静音,或背景噪音.VAD总在用VBR进行编码时暗中起作用,因此选项仅仅对一个不是VBR的操作起作用.对于不是VBR的操作来说,speex察觉出一个不属于对话的周期,然后对它用足够的字节重新生成为背景噪音.不这叫做舒适的噪音生成(CNG).
不连续传输(DTX)
不连续传输是VAD/VBR操作的一个额外选项,当背景噪音一定时,它可以完整地传输.因为在基于文件的操作中,我们不能停止对文件进行写入,所以只有5字节被这种帧所运用.(给250bps通信)
Algorithmic delay
迟时算法
每一个声音编码导致了在传输上的延时.对于speex,这种延时等于frame的大小加上一些数量的需要对每一帧进行的前瞻(”look-adhead”).在窄宽操作中(8kHz),迟时是30ms,而对于多频率(2-44Hz),迟时是34ms.这些值不包括CPU编码,解码帧的时间.
4.1 Encoding
4.1压缩
为了用Speex压缩对话,你首先需要引用头文件:
#include <speex.h>
然后你需要定义一个Speex的位采集(bit-packing)结构
SpeexBits bits;
and a Speex encoder state
以及定义一个speex编码器状态量
void *enc_state;
上面定义的这样被初始化:
The two are initialized by:
speex_bits_init(&bits);
enc_state = speex_encoder_init(&speex_nb_mode);
为了支持多频率的压缩,speex_nb_mode将被sppex_wb_mode取代.在大都数情况下,你需要知道你用的模式(mode)的帧(frame)的大小,你可以得到在frame_size变量里得到这值:speex_encoder_ctl(enc_state,SPEEX_GET_FRAME_SIZE,&frame_size);
一但初始化完毕,对于每一个输入帧:
speex_bits_reset(&bits);
speex_encode(enc_state, input_frame, &bits);
nbBytes = speex_bits_write(&bits, byte_ptr, MAX_NB_BYTES);
上面input_frame是一个指向对话(speech)帧(frame)的float指针(pointing);byte_ptr是指向编码帧开始写的地方的char指针,MAX_NB_BYTES是能写进byte_ptr而不会造成溢出的最大数.nbBytes是一个实际写入btye_ptr的数,即编码的实际大小在调用speex_bits_write前,可能你需要调用speex_bits_nbytes(&bits)得到需要写入(write)的字节大小.
在你已经编码后,释放所有的资源.
speex_bits_destroy(&bits);
speex_encoder_destroy(enc_state);
That’s about it for the encoder.
这就是关于编码的方面.
附源代码的翻译:
B Sample code
B 例程源代码
这个章节演示了一段用speex编码,解码对话(speech)的源代码.
可以如下用api命令来编码并解码一个文件:
译者注:这里说的api命令是指unix的用”|”进行管道写入读出.在windows下这样并不能实现.
% sampleenc in_file.sw | sampledec out_file.sw
这里这两段代码都没有引用其它的头文件,并以16 比特率(bits)进行编码
natural endianness).
B.1 sampleenc.c
Sameleenc 用一个未加工的16比特率(bits)文章,给它编码并产生一个speex 流(steam)给标准输出.注意已压缩的和speexenc/speexdec不和谐!
#include <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; //新建一个新的编码状态在窄宽(narrowband)模式下 state = speex_encoder_init(&speex_nb_mode); //设置质量为8(15kbps) tmp=8; speex_encoder_ctl(state, SPEEX_SET_QUALITY, &tmp); inFile = argv[1]; fin = fopen(inFile, "r"); //初始化结构使他们保存数据 speex_bits_init(&bits); while (1) { //读入一帧16bits的声音 fread(in, sizeof(short), FRAME_SIZE, fin); if (feof(fin)) break; //把16bits的值转化为float,以便speex库可以在上面工作 for (i=0;i<FRAME_SIZE;i++) input[i]=in[i]; //清空这个结构体里所有的字节,以便我们可以编码一个新的帧 speex_bits_reset(&bits); //对帧进行编码 speex_encode(state, input, &bits); //把bits拷贝到一个利用写出的char型数组 nbBytes = speex_bits_write(&bits, cbits, 200); //首先写出帧的大小,这是sampledec文件需要的一个值,但是你的应用程序中可能不一样 fwrite(&nbBytes, sizeof(int), 1, stdout); //写出压缩后的数组 fwrite(cbits, 1, nbBytes, stdout); } //释放编码器状态量 speex_encoder_destroy(state); //释放bit_packing结构 speex_bits_destroy(&bits); fclose(fin); return 0; }
如何使用Speex中的AEC模块,提高声音质量?
在Speex(www.speex.org)的最新版本中,开始集成了回音消除的模块,而回音消除一直是Voip之中亟待解决的主要问题。
很多朋友和我说speex的aec模块的效能并不好,我们先来看一下speex的aec的api调用方式。
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的设定是一个关键。
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执行过程中的同步问题。
采样率
采样率是指从连续信号中每秒钟采集到的采样数量。用Fs kHz来表示,最高频率可表示为Fs/2 kHz(见奈奎斯特Nyquist频率)。Speex主要设计了三种不同的采样率:8kHz,16kHz和32kHz。分别表示了窄带、宽带和超宽带。
采样率是指将声音(模拟信号)转换成mp3(数字信号)时的采样频率,也就是单位时间内采样多少点。一个采样点数据有8(甚至更多)个比特。
采样率越高音质也就越高,文件就越大。
比特率
比特率是指每秒钟传送的比特数,在语音信号编码时,表示语音数据每秒钟需要多少个比特表示,单位为bps(比特/秒)或kbps(千比特/秒)。注意区分kbps和kBps(千字节/秒)。
质量(可变)
Speex是一种有损编解码库,这意味着它的文档压缩方面会导致语音输入信号的失真,和一些语音编解码库不同的是,它尽可能的去控制质量和比特率之间的平衡。大多数时候,是用一个0到10范围内的质量参数来控制Speex的编码,比特率为常量的操作,质量参数是整数,如果是变比特率(VBR),则为浮点数(Float)
复杂度(可变)
在Speex中,编码器可调整复杂度。用1到10的整数来控制如何执行搜索,就像用-1到-9来设置压缩工具gzip或bzip2(博主注:设计压缩的块长度,为100k~900k)。正常情况下,复杂度为1时噪声级会比复杂度为10时高1~2 dB(分贝),而复杂度为10的CPU需求是复杂度为1的5倍。实践证明,最好将复杂度设置在2~4,设置较高则对非语音编码如双音多频(DTMF)音质较为有用。
变比特率(VBR)
变比牲率(VBR)允许编解码器动态调整比特率以适应的音频解码的“难度”,拿Speex来说,像元音和瞬间高音则需较高比特率(Bit-rate)来达到最佳效果,而摩擦音则用较少的比特(bits)即可完成编码。基于这种原因,变比特率(VBR)可以用较低的比特率(bit-rate)达到相同的效果或使用某比特率(bit-rate)质量会更好。尽管它有这些优势,但VBR也有两个主要的缺点:首先,它只是针对质量,却没办法保证最终的平均比特率(ABR); 其次,在一些实时应用如VOIP电话中,尽管拥有高的比特率(bit-rate),为适应通信信道还是需要适当降低。
平均比特率(ABR)
平均比特率(ABR)通过动态调整变比特率(VBR)的质量来获得一个特定目标的比特率,解决了VBR中存在的问题之一。因为平均比特率(ABR)是实时(开环)调整质量/比特率(bit-rate)的,整体质量会略低于通过变比特率(VBR)设置的接近于目标平均比特率进行编码获得的质量。
静音检测(VAD)
静音检测(VAD)将检测被编码的音频数据是语音还是静音或背景噪声。这个特性在用变比特率(VBR)进行编码是总是开启的,所以选项设置只对非变比特率(VBR)起作用。在这种情况下,Speex检测非语音周期并对用足够的比特数重新生成的背景噪声进行编码。这个叫“舒适噪声生成(CNG)”。
比特率是指每秒传送的比特(bit)数。
单位为 bps(Bit Per Second),比特率越高,传送的数据越大。声音中的比特率是指将数字声音由模拟格式转化成数字格式的采样率,采样率越高,还原后的音质就越好。 视频中的比特率(码率)原理与声音中的相同,都是指由模拟信号转换为数字信号的采样率。