SailorHub

iOS-音视频(十四) : Speex manul中文版

VOIP的音频算法中,回音处理已经成为一个关系通话质量的主要问题。

 

回声的产生在IP网络主要有两种:

1.声学回声

2.电路回声

 

声学回声主要又分成以下几种:

a ) 直接回声:由扬声器产生的声音未经任何反射直接进入麦克风

b ) 间接回声: 由扬声器发出的声音经过多次反射后,再进入Mic

对于第二种回声,拥有多路径,时变性的特点.是比较难处理的.

 

由于IP网络下的传输的延迟较大,而一般情况下,对于人耳,如果声音延迟达到了10ms以上的话,那么回声就会越来越明显.

一般来讲,VOIP中的声音延迟主要来自于几个方面:

1. 编码延迟: 一般情况下编码算法在声音压缩时都会产生延迟,就我们采用的Speex来讲,延迟大概在20ms左右

2. 处理延迟, 封装时延, 缓冲时延等

3. IP网络中数据的传输过程也会照成延时.这由当前的网络状况决定.

 

回声消除的模型:

a) 建立远端声音模型,进行回声估计, 从采集的值中减去估计值

b) 声学模型

 

Speex manul(手册)中文版

Speex是一套专门用于压缩声音的库,由于其专门针对声音,所以压缩声音的性能非常高.Speex由于其压缩性能,0.80版后的跨平台的性能,所以在网络声音的传输中有很大的价值.但是需要注意的是speex只能对声音进行压缩,不支持音乐的压缩,如果你需要音乐的压缩你或许需要用vorbis.

 

但是的speex资料像其它大都数专用库一样,并没有大多的中文资料.所以在这里我决定将里面最核心的编程技术翻译出来.一来是练习练习自己翻译资料的能力,二来是方便一些英语水平较差的朋友.由于本人能力有限,有些感觉有出入或难理解的地方可以去speex的官方网站www.speex.org找到英文原版的说明.

 

翻译的一些说明:

 

1,对于一些专有名词如speex,api不过行翻译

 

2,对于一些新概念翻译,以及其它的翻译过来也许会产生歧义的文字,用中/英两种方式标出:

 

:对话(speech),位采集(bit_packing)

 

3,基本做到和英文原行对译.及英文原文一行,中文翻译过来也是一行,使翻译后的文章和原文基本行行对照.

 

4,源代码不翻译,SpeexBits bits;

 

5,语言中的关健字不译,float

 

6,一此不是关健字但英文通常出现的词第一次以中/英文格式给出,之后按具体情况给出英文或中文,:frame(),

 

7,对一些有自己翻译起来感觉有歧义的地方,加斜线作标记

 

 

1,speex的介绍()

 

2特征描述

 

这个章节展示了speex的主要特征,以衣一些关于对话(speech)编码的一个概念,以便

 

帮助我们更好的了解下一章节.

 

取样率(Sampling rate)

 

Speex主要是设计了三种不同的取样率:8kHz,16kHz,32kHz.这些分别代表了窄宽(narrowband),

 

多频率,超声.

 

质量

 

Speex编码大都数时间是被一个范围为010的质量参数来控制  .在一个比特率为常量(CBR)的操作中,质量参数是一个整数,而对于变动的比特率(VBR)参数是一个float;

 

复杂性(变量)

 

speex,你可以将编码设置成允许的复杂度.这由一个范围为110的整数来控制完成,就像你用选项-1-9来控制gzipbzip2的压缩质量.在通常的运用中,噪声级别的复杂度1是在12dB之间,比复杂度10要高,但是CPU需要复杂度10大概5倍高行复杂度1.在实践中,最好的是设置在24之间,尽管更高的设定通常有用,当编码一个非对话声音(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)这种包含着最大的比特率的,必须把比特率设为足够低以适应

     

     传输通道.

     

     r

     

     平均比特率(ABR)

     

     平均比特率通过动态地调节VBR质量去得到一个确定的目标的比特率,从而解决了VBR中的一个问题..因为质量/比特率被即时的调整了,整体质量将会稍稍低于由VBR对一个

     

     设置得和目标平均比特率非常接近的质量数编码得到的结果.

     

     

     

     声音生动性检测(VAD)

     

     声音生动性检测将会发觉音频正在被编码成对话,静音,或背景噪音.VAD总在用VBR进行编码时暗中起作用,因此选项仅仅对一个不是VBR的操作起作用.对于不是VBR的操作来说,speex察觉出一个不属于对话的周期,然后对它用足够的字节重新生成为背景噪音.不这叫做舒适的噪音生成(CNG).

     

     不连续传输(DTX)

     

     不连续传输是VAD/VBR操作的一个额外选项,当背景噪音一定时,它可以完整地传输.因为在基于文件的操作中,我们不能停止对文件进行写入,所以只有5字节被这种帧所运用.(250bps通信)

     

     Perceptual enhancement

     

     知觉增强

     

     知觉增加是解码的一部分,它在打开的时候用来减少由编码解码所产生的噪音.在大都数

     

     情况下,知觉增强在客观上使声音离原始值更远(如果用SNR),但是在最后它仍然听起来更好(主观上的改进)

     

     Algorithmic delay

     

     迟时算法

     

     每一个声音编码导致了在传输上的延时.对于speex,这种延时等于frame的大小加上一些

     

     数量的需要对每一帧进行的前瞻(”look-adhead”).

     

     在窄宽操作中(8kHz),迟时是30ms,而对于多频率(2-44Hz),迟时是34ms.这些值

     

     不包括CPU编码,解码帧的时间.

     

     speex编程(the libspeex api)

     

     这个章节出要讲述了如何用speex api进行编程.例子的源代友你也可以在附录B中找到

     

     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.

     

     

     这就是关于编码的方面.

     

     Speex manul中文版三

     

     附源代码的翻译:

     

     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;

    

    /*保存字节因此他们可以被speex常规读写*/

    

    

    

    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模块,提高声音质量?

Speexwww.speex.org)的最新版本中,开始集成了回音消除的模块,而回音消除一直是Voip之中亟待解决的主要问题。

很多朋友和我说speexaec模块的效能并不好,我们先来看一下speexaecapi调用方式。

 

/*

 *创建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 来控制

 

问题的关键是 处理inputecho 之间的关系,

也就是说在捕捉到的信号和播放的信号之间的延迟必须足够的小,才可以提高效率.

 

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执行过程中的同步问题。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注