• Speex编解码手册

    2018-05-23 14:44:15
    前言 目前公司的项目需要改善音频效果,最近也一直在研究声学回声消除,接触到了Speex,用到其中回声消除API,多次用到Speex,本文是官方手册(The Speex Codec Manual Version 1.2 Beta 3)翻译版,加深些理解。...

    Speex编解码器手册 1.2Beta3

        The Speex CodecManual Version 1.2 Beta 3

    1  Speex介绍

            Speex编解码器(http://www.speex.org/)的存在是因为需要一款开源且免软件专利使用费的语音编解码器,这是任何开源软件可用的必要条件。本质上来说,Speex相对于语音正如Vorbis(注:免费音乐格式)相对于音频/音乐。不像许多其他语音编解码器,Speex不是为移动电话而设计,而是为分封网络(packet network)和网络电话(VoIP)而设计的。所以当然支持基于文件的压缩。

            Speex设计灵活,支持多种不同的语音质量和比特率。对高质量语音的支持也就意味着Speex不仅能编码窄带语音(电话语音质量,8kHz采样率),也能编码宽带语音(16kHz采样率)。

            VoIP而不是移动电话而设计意味着Speex对丢失的数据包鲁棒,但对损坏的数据包不鲁棒。这基于在VoIP中数据包要么完整到达要么不能到达这一假设的。因为Speex针对于多种设备,所以它复杂性适度(可调节)并且占用较少内存。

            考虑这些设计目标,我们选用CELP作为编码技术。其中一个主要原因是CELP很早就被证明在低比特率(4.8kbpsDoD CELP)和高比特率(16kbpsG.728)都能稳定可靠工作。

    1.1  获取帮助

            正如许多开源项目一样,有许多方法可以获取Speex的帮助,包括:

                1)此文档

                2Speex网站(http://www.speex.org/)上的其他文档

                3)邮件列表:在speex-dev@xiph.org上于Speex相关主题的讨论(不仅仅针对开发者)

                4IRC:在irc.freenode.net上的主频道是#speex。注意由于时间的不同,可能需要一些时间联系某人,所以请耐心等待。

                5)私下给作者邮箱jean-marc.valin@usherbrooke.ca发邮件,但仅限于你不想被公开讨论的私人的或精妙的主题。

            在寻求帮助之前(邮件列表或IRC),请最好先阅读本手册(好吧,如果你看到这了,那么这是一个好迹象)。在邮件中讨论已经在手册中具体说明的问题一般是很不礼貌的。另一方面,如果是询问在手册中没有说清楚的问题则是很好地(鼓励)。本手册没有包含Speex的方方面面,所以鼓励每个人问问题,发评论,要求增加的新特性,或简单的让我们知道Speex是如何被使用的。

            再说些附加的关于邮件列表的指引。在你向列表中报告Speex中的bug之前,强烈建议(如果可能)先测试下这些bug在使用speexencspeexdec命令行工具(见第四章)下是否可复现。基于第三方代码(3rd party code)发布的bug不仅难发现,而且经常是由于与Speex无关的错误引起的。

    1.2  关于此文档

            此文档分为如下几章。第二章描述不同的Speex特征并定义此文档中使用的许多术语,第四章描述Speex提供的标准命令行工具,第五章包括用libspeex API编程的具体说明,第七章有Speex相关的信息和标准。

            最后三章描述Speex中所使用的算法。阅读这部分需要信号处理相关知识,但对于仅仅使用Speex则不需要。这部分是为那些对于想了解Speex如何工作或想基于Speex研究的人而准备。第八章解释了CELP后的一般思想,第九和第十章是Speex的一些特性。

    2  编解码器描述

            这张具体描述Speex和它的特征。

    2.1 概念

            在介绍Speex特征之前,先介绍在语音编码中的一些概念,帮助读者更好地理解手册的剩余部分。尽管其中一些事语音/音频处理中的一般概念,但有些也是Speex中特有的。

            采样率

            采样率是每秒钟采集到的信号样本数,单位是HertzHz)。如果采样率是Fs kHz,则所采样信号的最高频率为Fs/2 kHzFs/2被称为奈奎斯特频率)。这是信号处理中的一个基本性质,由采样定理描述。Speex为三种不同的采样率而设计:8kHz16kHz32kHz,分别对应窄带,宽带和超宽带。

            比特率

            在对语音信号编码时,比特率定义为单位时间内的比特数,单位是比特每秒(bps)或通常的千比特每秒(kbps)。注意千比特每秒(kbps)和千字节每秒(kBps的区别。

            质量(可变)

            Speex是有损的编解码器,意味着压缩率以输入语音信号的保真度为代价。不像一些其他的语音编解码器,Speex可以控制质量和比特率之间的折中。Speex编码处理大多数时间由一个范围在010之间的质量参数控制。在不变比特率(CBR)中,质量参数是一个整数,在可变比特率(VBR)中,质量参数是一个浮点数。

            复杂度(可变)

            Speex允许编码器拥有可变的复杂度。这通过一个范围在110之间的整数控制搜索的执行来实现,类似于gzipbzip2压缩工具的-1-9选项。正常使用情况下,复杂度为1的噪声等级比复杂度为10的噪声等级高12dB,但复杂度为10CPU要求比复杂度为1的高5倍。实际应用中,最好的折中是复杂度24,但在编码非语音声音如DTMF声调时更高的复杂度经常被用到。

            可变比特率(VBR

            可变比特率(VBR)允许编解码器自适应的根据待编码音频的难度动态地改变比特率。在Speex例子中,像元音和高能瞬态变化的声音需要高比特率以获得好的质量,但是摩擦音(如sf)用低比特率就能充分编码。由于这一原因,VBR在相同的质量下能获得更低的比特率,或在不变比特率下获得更好的质量。除了这些优点,VBR有两个主要缺点:第一,在指定质量情况下,无法保证最终的平均比特率;第二,在一些如网络电话(VoIP)这样的实时应用中,依赖于最大比特率,这在通信信道中必须足够低。

            平均比特率(ABR

            平均比特率解决了VBR中的第一个问题,它动态地调整VBR质量以获得指定的比特率。因为质量和比特率是实时调整的(开环的),ABR的全局质量比正好达到目标平均比特率的VBR编码质量稍微差些。

            声音活动检测(VAD

            当被激活时,VAD检测待编码的音频是语音还是无声/背景噪声。VADVBR编码中默认激活,所以这一选项仅对非VBR编码有效。在这种情况下,Speex检测出非语言段并仅使用足够复现背景噪声的比特率进行编码,这叫柔化噪音生成CNG)。

            断续传输(DTX

            断续传输室VAD/VBR的附加操作,当背景噪声平稳时会完全停止传输。在基于文件的操作中,既然我们无法停止写文件,则对于平稳背景噪声帧仅用5比特(对应250bps)。

            知觉增强

            知觉增强是解码器的一部分,当被启用时,能减少编解码过程中产生的噪声或失真的知觉。在大多数情况下,知觉增强会带来声音客观上的偏离(如仅考虑SNR),但最后仍听起来更好(主管增强)。

            等待时间和算法延时

            每一个语音编解码器在传输中都会引入延时。对于Speex,延时等于帧长加上处理每一帧需要前几帧的数量。在窄带操作中(8kHz),延时为30ms;在宽带操作中(16kHz)延时为34ms,这不包括编解码帧时的CPU时间。

    2.2  编解码器

            Speex的主要特性有:

               1)免费软件/开源,免专利费和版税

               2)利用嵌入比特流集成了窄带和宽带

               3)大范围可用比特率(从2.15kbps44kbps

               4)动态比特率转换(AMR)和可变比特率操作(VBR

               5)声音活动检测(VAD,与VBR集成)和断续传输(DTX

               6)可变复杂度

               7)嵌入宽带结构(可伸缩采样率)

               832kHz超宽带采样率

               9)强度立体声编码选项

               10)定点实现

    2.3  预处理器

            这是在1.1.x版本中已经介绍的预处理器模块。预处理器在对音频编码前对音频进行预处理,有三个主要功能:

               1)噪声抑制

               2)自动增益控制·AGC

               3)声音活动检测(VAD

            解码器能减少输入信号的背景噪声。预处理能得到高质量语音,而不管降噪后的信号是否通过Speex编码。然而,先降噪再进行编解码是有好处的,因为Speex编解码器通常会对噪声输入同样进行编解码,这将会扩大噪声,而降噪能大大减少这一影响。

            自动增益控制(AGC)是为了处理录音音量在不同设置里有很大差别这一问题,AGC将会调整信号音量到参考音量大小。这对网络电话时非常有用的,因为这样就不需要手工调节麦克风增益了。第二个好处是若将麦克风增益设置为较低的等级,更容易避免削波。

            预处理器提供的声音活动检测(VAD)比编解码器中直接提供的VAD更先进。

    2.4  自适应抖动缓冲器

            当通过UDPUser DatagramProtocal,用户数据报协议)或RTPReal TimeProtocal,实时传输协议)传输声音(或其他任何内容)时,数据包可能丢失,不同延时到达,甚至乱序。抖动缓冲器的作用是对数据包进行重排序并保存在足够长的buffer(但有一定限度)里,然后将数据包发送去解码。

    2.5  声学回声消除器

            在任何免提式通信系统中(图2.1),远端的语音在本地扬声器播放时,经过在房间里传播后又会被麦克风录音。如果将麦克风录音直接又发送到远端,则远端的用户将会听到他自己的回声。声学回声消除器就是为了在将录音发送到远端前消除声学回声。注意回声消除器意味着提高了远端接收的语音质量。

                     

                                                                 2.1  声学回声模型

    2.6  重采样器

            在一些情况下,转换音频的采样率是很有用的,原因有很多,如能混合不同采样率流,能支持声卡不支持的采样率,能转码等。这也是为什么现在重采样器也是Speex项目的一部分的原因。重采样器能在任意采样率间进行转换(采样率必须是有理数),能控制质量和复杂度的折中。

    3  编译和端口

            UNIX/Linux或任何其他支持autoconf的平台(如Win32/cygwin)下编译Speex很容易,只需输入

                   % ./configure [options]

                   % make

                   % make install

            Speex配置脚本支持的选项有:

                   -prefix=<path>  指定安装Speex的基本路径(如/usr)

                   -enable-shared/-disable-shared  是否编译共享库

                   -enable-static/-disable-static  是否编译静态库

                   -disable-wideband  禁用Speex的宽带部分(典型地为了节省空间)

                   -enable-valgrind  为调试启用valgrind(一款用于内存调试内存泄漏检测以及性能分析的软件开发工具)额外的匹配记录(默认不启用)

                   -enable-sse  启用SSE(StreamingSimd Extensions,指令集)指令(仅支持x86浮点数)

                   -enable-fixed-point  为没有浮点单元(FPU)的处理器编译Speex

                   -enable-arm4-asm  启用ARMv4结构配置特性(仅支持gcc

                   -enable-arm5e-asm  启用ARMv5E结构配置特性(仅支持gcc

                   -enable-fixed-poin-debug  仅调试定点代码(非常慢)

                   -enable-epic-48k  启用特殊(不兼容)的4.8kbps窄带模型(在1.1.x1.2beta版本中已损坏)

                   -enable-ti-c55x  启用对TI C5x系列的支持

                   -enable-balckfin-asm  启用Blackfin DSP结构配置特性(仅支持gcc

                   -enable-vorbis-psycho  使编码器使用Vorbis心理音响学模型,这处于试验阶段,将来可能取消

    3.1  平台

            Speex可在包括浮点单元和定点单元的许多平台下编译和工作。通常,任何能计算两个16位带符号数乘法(结果是32位)和能在充分地时钟频率下运行(依赖结构)的平台都能运行Speex。已知的Speex的工作平台有(可能还有其他):

            1x86 & x86-64    2Power   3SPARC    4ARM   5Blackfin    6Coldfire68k系列)   7TI C54xx & C55xx    8TI C6xxx    9TriMedia(试验)

              已知的Speex工作的操作系统有(可能还有其他):

             1Linux    2uClinux    3MacOS X    4BSD    5)其他UNIX/POSIX变体    6Symbian

             源代码目录中README.xxx文件包含在某个平台或操作系统下编译的额外信息。

    3.2  端口和优化

            以下是在新平台或目前已有平台下配置端口或优化Speex要考虑的一些注意事项。

    3.2.1  CPU优化

            最影响SpeexCPU使用的是浮点编译还是定点编译。如果你的CPU/DSP没有浮点单元FPU,则定点编译将更快。如果有FPU,则需要测试以下浮点编译和定点编译哪个快。在x86平台中,浮点编译一般更快。为了对Speex定点编译,你需要输入-fixed-point来配置脚本或为编译器定义FIXED-POINT宏。对于1.2beta3版本,现在能禁用浮点兼容的API,这意味着你的代码无需浮点仿真库即可链接,为了达到这一目的,可以通过-disable-float-api配置或定义DISABLE_FLOAT_API宏。等到VBR特性移植到定点,你将需要通过-disable-vbr配置或定义DISABLE_VBR宏。

            在一些DSP平台中需要核实的一些事有:1)确定高速缓存被设置为回写式模型;2)如果芯片用SRAM代替高速缓存,确认大量代码处于SRAM而不是RAM中。

            如果你要编写汇编,以下函数通常是你必须最先优化的:1filter_mem16()    2iir_mem16()    3vq_nbest()    4pitch_xcorr()    5interp_pitch()。滤波器函数filter_mem16()iir_mem16()应用在转置直接II型(DF2T)。然而对基于乘法累计(MAC)的平台,DF2T需要频繁重装蓄电池,这将使代码非常慢。对于这些平台(如BlackfinColdfire),更好的方法是应用这些函数为直接I型(DF1),这在MAC时更容易再装。然而如果这么做,需要确认DF1实现仍能得到像原始DF2T实现一样的滤波器值,这很必要,因为滤波器是时变的并且必须在任何编码器或解码器上直接计算相同的值(不算机器舍入)。

    3.2.2  内存优化

            内存优化主要是在一些小的嵌入式平台中需要考虑。对于PCSpeex已经足够小,无需内存优化。有几种方法的减少Speex的内存使用,包括代码大小和数据大小。对于优化代码大小,关键是移除你不需要的特性。以下是禁用一些你不需要的特性的例子:1)宽带支持(-disable-wideband);2)立体声支持(移除stereo.c);3VBR支持(-disable-vbrDISABLE_VBR)4)你所使用的比特率不要静态码本(*_table.c文件)

            Speex也有一些配置临时数组的方法。当使用适当支持C99的编译器时(到2007年,微软编译器不支持,gcc支持),最好定义VAR_ARRAYS,以利用C99的可变大小数组这一特性。下一个最好做的是定义USE_ALLOCA以使Speex能用函数alloca()分配临时数组。注意在许多系统中,alloca()可能无效。如果没有定义VAR_ARRAYSUSE_ALLOCA,则Speex将利用它的内部分配分配一大块可用空间,这样的主要缺点是浪费。需要为最坏情况(最坏比特率,最高复杂度设置...)分配足够的栈,默认的,在多个编码器/解码器状态之间内存不共享。最后,如果只能手动分配,也有一些方法可以提高。通过复写os_support.h中的speex_alloc_scratch(),可以使所有状态总是返回同一块内存(此时必须特别注意线程)。除此之外,通过重新定义NB_ENC_STACKNB_DEC_STACKA(宽带类似),可以为提前知道的情况分配内存,此时,需要衡量使用的采样率、比特率和复杂度级别所需的内存数量。    

    4  命令行编码器/解码器

            Speex最基本的是命令行编码器(speexenc)和解码器(speexdec)。这些工具产生并读取封装在Ogg容器内的Speex文件。尽管能在任意容器内封装Speex,但Ogg是文件命令行容器。这一部分介绍如何在Ogg中为Speex文件使用命令行工具。

    4.1  speexenc

            speexenc的作用是从未加工的PCM文件或wave文件生成Speex文件。可通过如下调用:

                    speexenc[options] input_file output_file

            input_fileoutput_file‘-’值分别对应标准输入和标准输出。有效的选项有:

                    -narrowband(-n)   告诉Speex默认将输入视为窄带(8kHz

                    -wideband(-n)  告诉Speex将输入视为宽带(16kHz

                    -ultra-wideband(-u) 告诉Speex将输入视为超宽带(32kHz

                    -quality n  设置编码质量(0-10),默认为8

                    -bitrate n  编码比特率(比特率将小于等于n

                    -vbr n  启用VBR(可变比特率),默认禁用

                    -abr n  启用ABR(平均比特率)为n kbps,默认禁用

                    -vad  启用VAD(声音活动检测),默认禁用

                    -dtx  启用DTX(断续传输),默认禁用

                    -nrames n  每个Ogg数据包包含n帧(这在低比特率时能节省空间)

                    -comp n  设置编码速度和质量的折中,n值越大,编码速度越慢(默认为3

                    -V    详细的操作,输出所使用的比特率

                    -help(-h)  输出帮助

                    -version(-v) 输出版本信息

           Speex注释

                    -comment  增加给定的字符串为额外的注释,这可能花费几倍时间

                    -author  声道作者

                    -title  声道标题

            原始输入选项

                    -rate n  原始输入的采样率

                    -stereo  将原始输入视为立体声

                    -le  原始输入为小头(little-endian,高位字节放在高地址单元)

                    -be  原始输入为大头(big-endian,高位字节放在低地址单元)

                    -8bit  原始输入为8位无符号数

                    -16bit  原始输入为16位有符号数

    4.2  speexdec 

            speexdec用于解码Speex文件,可通过如下调用

                    speexdec[options] speex_file [output_file]

            input_fileoutput_file的值‘-’分别对应标准输入和标准输出。若没有指定output_file,则文件通过声卡播放。有效的选项有:

                    -enh  启用后置滤波(默认)

                    -no-enh  禁用后置滤波

                    -force-nb  强制窄带解码

                    -force-wb  强制宽带解码

                    -force-uwb  强制超宽带解码

                    -mono 强制单声道解码

                    -stereo  强制立体声解码

                    -rate n  强制解码采样率为n Hz

                    -packet-loss n  模拟n%随机包丢失

                    -V  详细操作,输出使用的比特率

                    -help(-h)  输出帮助

                   -version(-v)  输出版本信息

    5  Speex编解码器API(libspeex)

            libspeex库包含所有使用Speex编解码器对语音编码和解码的函数。若在UNIX系统中链接,需要在编译器命令行中加入-lspeex-lm。注意libspeex的调用时可重入的,但不是线程安全的。这意味着可以在多个线程中条用,但多个线程间对同一状态的调用需要互斥锁保护。代码示例参见附录A,完整的API文档可在Speex网站的文档部分找到(http://www.speex.org/)。

    5.1 编码

            在用Speex编码语音之前,首先需要包括头文件:

                    #include<speex/speex.h>

            然后在代码中,必须声明一个Speex位封装结构和一个Speex编码状态:

                   SpeexBits bits;

                    void *enc_state;

           然后需要两个初始化:

                   speex_bits_init(&bits);

                   enc_state = speex_encoder_init(&speex_nb_mode);

    对于宽带编码,speex_nb_mode替换为speex_wb_mode。大多数情况下,你需要知道所使用采样率的帧长,可通过变量frame_size获得(单位是样本数,而不是字节):

                    speex_encoder_ctl(enc_state,SPEEX_GET_FRAME_SIZE,&frame_size);

    实际应用中,当用8,1632kHz采样率是frame_size通常为20msSpeex编码器可设置许多参数,但最有用的是控制质量和比特率折中的质量参数:

                   speex_encoder_ctl(enc_state,SPEEX_SET_QUALITY,&quality);

    其中quality是一个010之间的整数(包含10)。图9.2描述了窄带时质量和比特率之间的关系。

            初始化之后1,对于每一输入帧:

                   speex_bits_reset(&bits);

                   speex_encode_int(enc_state,input_frame,&bits);

                    nbBytes= speex_bits_write(&bits,byte_ptr,MAX_NB_BYTES);

    其中input_fram是一个short *指针,只想一个语音帧的开始位置;byte_ptr是一个char *指针,指向将被写的已编码帧;MAX_NB_BYTES是在无溢出情况下能写到byte_ptr的最大字节数;nbBytes是实际写到byte_pte的字节数(已经编码的字节数)。在调用speex_bits_wrtie之前,可通过调用speex_bits_nbytes(&bits)获得需要被写的字节数,返回这一字节数。

            也可以使用speex_encode()函数,返回一个指向音频的float *指针。然而这将使得没有FPU的平台(如ARM)最后端口更难懂。在内部,speex_encode()speex_encode_int()是以相同的方式处理的。编码器是否使用定点版本仅由编译时间标志决定,而不是API级别。

           在完成编码之后,释放资源:

                   speex_bits_destroy(&bits);

                   speex_encode_deatroy(enc_state);

            以上是编码器部分。

    5.2  解码器

            在用Speex解码语音之前,首先需要头文件:

                    #include <speex/speex.h>

            你也需要声明一个Speex位封装结构和一个Speex解码状态:

                   SpeexBitsbits;

                    void * dec_state;

            然后进行初始化:

                   speex_bits_init(&bits);

                   dec_state = speex_decoder_int(&speex_nb_mode);

    对于宽带解码,speex_nb_mode替换为speex_wb_mode。若需要获得解码器将使用的帧大小,可通过变量frame_size(单位是样本数,而不是字节数)获得:

                   speex_decoder_ctl(dec_state,SPEEX_GET_FRAME_SIZE,&frame_size);

             解码器同样有一个参数可以设置:是否启用知觉增强:

                   speex_decoder_ctl(dec_state,SPEEX_SET_ENH,&enh);

    其中enh初始化为0则禁用知觉增强,值为1则启用。对于1.2-beta1版本,默认启用知觉增强。

             解码初始化后,对每一输入帧:

                    speex_bits_read_from(&bits,input_bytes,nbBytes);

                    speex_decode_int(dec_state,&bits,output_frame);

    其中input_bytes是一个包含一帧接收到的比特流数据的char*指针;nbBytes是比特流的大小(字节);output_frame是一个short*型指针,指向解码语音将被写入的区域。若第二个参数值为NULL,则表明当前帧没有比特。当有一帧丢失时,Speex解码器将尽量猜测正确的信号。

             跟编码器一样,speex_decode()函数也可以使用,返回一个指向输出音频的float*型指针。在完成解码后,要释放资源:

                     speex_bits_destroy(&bits);

                     speex_decoder_destrpy(dec_state);

    5.3  编解码器选项(speex_*_ctl)

            Speex编码器和解码器支持许多选项,可通过speex_encoder_ctlspeex_decoder_ctl函数设置。这些函数类似于输入输出系统函数调用,他们的原型是:

                    void speex_encoder_ctl(void *encoder,int request,void *ptr);

                    void speex_decoder_ctl(void *decoder,int request,void *ptr);

            尽管有许多设置函数,但是对于许多应用默认设置一般足够,仅当使用者理解了这些设置并需要时才需设置选项。一个常见错误是设置了许多没必要的设置。

            以下是一个允许请求设置值得列表,其中一些仅适用于编码器或解码器。由于最后一个参数是void*型的,所以_ctl()函数不是类型安全的,需要小心使用。类型spx_int32_t同等于C99中的类型int32_t

                    SPEEX_SET_ENH:设置知觉增强为开(1)或关(2)(spx_int32_t型,默认为开,仅用于解码器)

                    SPEEX_GET_ENH:获取知觉增强状态(spx_int32_t型,仅用于解码器)

                    SPEEX_GET_FRAME_SIZE:获取当前模式下每帧的样本数(spx_int32_t型)

                    SPEEX_SET_QUALITY:设置编码器语音质量(spx_int32_t型,从010,默认为8,仅用于编码器)

                    SPEEX_GET_QUALITY:获取当前编码器语音质量(spz_int32_t型,从010,仅用于编码器)

                    SPEEX_SET_MODE:设置模型数字,同RTP规格里指定(spx_int32_t型,仅用于编码器)

                    SPEEX_GET_MODE:获取当前模型数字,同RTP规格里指定(spx_int32_t型,仅用于编码器)

                    SPEEX_SET_VBR:设置变比特率(VBR)为开(1)或关(0)(spx_int32_t型,默认为关,仅用于编码器)

                    SPEEX_GET_VBR:获取变比特率(VBR)状态(spx_int32_t型,仅用于编码器)

                    SPEEX_SET_VBR_QUALITY:设置编码器VBR语音质量(浮点型,从0.010.0,默认为8.0,仅用于编码器)

                    SPEEX_GET_VBR_QUALITY:获取当前编码器VBR语音质量(浮点型,从0.010.0,仅用于编码器)

                    SPEEX_SET_COMPLEXITY:设置分配给编码器的CPU资源(spx_int32_t型,从110,默认为2,仅用于编码器)

                    SPEEX_GEY_COMPLEXITY:获取分配给编码器的CPU资源(spx_int32_t型,从110,默认为2,仅用于编码器)

                    SPEEX_SET_BITRATE:设置比特率为不超过参数的最近整数值(SPX_INT32_t型,单位比特每秒,仅用于编码器)

                    SPEEX_GET_BITRATE:获取当前比特率(spx_int32_t型,单位比特每秒,仅用于编码器)

                    SPEEX_SET_SAMPLING_RATE:设置实际采样率(spx_int32_t型,单位Hz

                    SPEEX_GET_SAMPLING_RATE:获取实际采样率(spx_int32_t型,单位Hz

                    SPEEX_RESET_STATE:重置编码器/解码器状态为初始状态,清空所有内存(不推荐)

                    SPEEX_SET_VAD:设置声音活动检测(VAD)为开(1)或关(0)(spx_int32_t型,默认为关,仅用于编码器)

                    SPEEX_GET_VAD:获取声音活动检测(VAD)状态(spx_int32_t型,仅用于编码器)

                    SPEEX_SET_DTX:设置断续传输(DTX)为开(1)或关(0)(spx_int32_t型,默认为关,仅用于编码器)

                    SPEEX_GET_DTX:获取断续传输(DTX)状态(spx_int32_t型,仅用于编码器)

                    SPEEX_SET_ABR:设置平均比特率(ABR)为nspx_int32_t型,单位比特每秒,仅用于编码器)

                    SPEEX_GET_ABR:获取平均比特率(ABR)设置(spx_int32_t型,单位比特每秒,仅用于编码器)

                    SPEEX_SET_PLC_TUNING:告诉编码器对一个包丢失百分比进行优化编码(spx_int32_t型,百分数,仅用于编码器)

                    SPEEX_GET_PLC_TUNING:获取PLC编码的当前调谐(spx_int32_t型,百分数,仅用于编码器)

                    SPEEX_SET_VBR_MAX_BITRATE:设置VBR操作中允许的最大比特率(spx_int32_t型,单位比特每秒,仅用于编码器)

                    SPEEX_GET_VBR_MAX_BITRATE:获取当前VBR操作中允许的最大比特率(spx_int32_t型,单位比特每秒,仅用于编码器)

                    SPEEX_SET_HIGHPASS:设置高通滤波器为开(1)或关(0)(spx_int32_t型,默认为开)

                    SPEEX_GET_HIGHPASS:获取当前高通滤波器状态(spx_int32_t)

    5.4  模式查询

            Speex模式查询类似于speex_encoder_ctlspeex_decoder_ctl函数调用。因为模式是只读的,所以仅能获取一个特别模式的信息。函数调用如下:

                    voidspeex_mode_query(Speexmode *mode, int request, void *ptr);

            其中request可允许的值有(除非特别声明,这些值通过指针ptr返回):

                    SPEEX_MODE_FRAME_SIZE 获取模式的帧大小(样本数)

                    SPEEX_SUBMODE_BITRATE 通过ptr获取子模式比特率(整数,单位bps

    5.5  封包和带内信号

            有时期望每个数据包(或其他基本存储单元)不止包含一帧,此时可在调用speex_bits_write写数据流之前调用speex_encode N次来实现。若每个包的帧数不是由带外机制决定,则可以包含一个终止码。终止码用5位对代码15(十进制)编码,如表9.2所示。注意对于1.0.2版本,调用speex_bits_write自动插入终止码来填充最后一字节,这样没有任何其他费用并保证包里没有更多帧时Speex能准确检测到。

            可以发送带内信息到另一端。这些信息之前加入一个包含4位信息类型代码的模式14伪帧。表5.1列出了可能的代码,包含他们的意义和信息大小。大多数信息是要发送到另一端编码器或解码器的请求,可以自由接受或无视这些请求。默认的,无视所有带内信息。

                   

    1   带内信号代码

            最后,应用可以用代码13定义常用带内信息,信息字节大小用5位编码,解码器若不知如何解码则可以跳过它。

    6  语音处理APIlibspeexdsp

            对于1.2beta3版本,Speex中的非编解码部分单独分离成libspeexdsp库,包括预处理器,声学回声消除器,抖动缓冲器和重采样器。在UNIX环境下,可通过在编译器命令行中添加-lspeexdsp -lm链接到你的程序中。正如libspeexlibspeexdsp调用时可重入的,但不是线程安全的,这意味着可以在多线程中使用,但多线程中对同一状态的调用必须由互斥锁保护。

    6.1 预处理器

            调用Speex预处理器之前,首先需要头文件

                    #include<speex/speex_preprocess.h>

            然后,创建预处理器状态:

                   SpeexPreprocessState *preprocess_state =speex_preprocess_state_init(frame_size,sampling_rate);

    推荐frame_size的大小同编码器中的一样,为20ms

            对于每一输入帧,需要调用:

                   speex_preprocess_run(preprocess_state, audio_frame);

    其中audio_frame可为输入或输出。若不需要输出处理后的数据,可调用以下函数替代:

                   speex_preprocess_eatimate_update(preprocess_state, audio_frame);

    这一函数会更新预处理器内部状态变量而无需计算输出音频,节省CPU周期。

            预处理器参数可以改变,通过调用:

                   speex_preprocess_ctl(preprocess_state, request, ptr);

    调用方法同编码器和译码器中的一样,选项在6.1.1中列出。

            最后,销毁预处理器状态:

                   speex_preprocess_state_destroy(preprocess_state);

    6.1.1  预处理器选项

            同编解码器一样,预处理选项可通过函数控制。可用选项有:

                    SPEEX_PREPROCESS_SET_DENOISE 开启(1)或关闭(2)降噪(spx_int32_t型)

                   SPEEX_PREPROCESS_GET_DENOISE  获取降噪状态(spx_int32_t型)

                   SPEEX_PREPROCESS_SET_AGC  开启(1)或关闭(2)自动增益控制(AGC)spx_int32_t型)

                    SPEEX_PREPROCESS_GET_AGC 获取AGC状态(spx_int32_t型)

                   SPEEX_PREPROCESS_SET_VAD  开启(1)或关闭(2)声音活动检测(VAD)(spx_int32_t型)

                    SPEEX_PREPRECESS_GET_VAD 获取VAD状态(spx_int32_t型)

                    SPEEX_PREPROCESS_SET_AGC_LEVEL

                    SPEEX_PREPROCESS_GET_AGC_LEVEL

                    SPEEX_PREPROCESS_SET_DEREVERB 开启(1)或关闭(2)混响消除(spx_int32_t型)

                    SPEEX_PREPROCESS_GET_DEREVERB 获取混响消除状态(spx_int32_t型)

                   SPEEX_PREPROCESS_SET_DEREVERB_LEVEL  暂时不可用

                    SPEEX_PREPROCESS_GET_DEREVERB_LEVEL 暂时不可用

                    SPEEX_PREPROCESS_SET_DEREVERB_DECAY 暂时不可用

                    SPEEX_PREPROCESS_GET_DEREVERB_DECAY 暂时不可用

                    SPEEX_PREPROCESS_SET_PROB_START

                    SPEEX_PREPROCESS_GET_PROB_START

                    SPEEX_PREPROCESS_SET_PROB_CONTINUE

                    SPEEX_PREPROCESS_GET_PROB_CONTINUE

                    SPEEX_PREPROCESS_SET_NOISE_SUPPRESS 设置最大噪声衰减,单位dB(负spx_int32_t型)

                    SPEEX_PREPROCESS_GET_NOISE_SUPPRESS 获取最大噪声衰减,单位dB(负spx_int32_t型)

                    SPEEX_PREPROCESS_SET_ECHO_SUPPRESS 设置最大残余回声衰减,单位dB(负spx_int32_t型)

                    SPEEX_PREPROCESS_GET_ECHO_SUPPRESS 获取最带残余回声衰减,单位dB(负spx_int32_t型)

                    SPEEX_PREPROCESS_SET_ECHO_SUPPRESS_ACTIVE 近端活动时设置最大残余回声衰减,单位dB(负spx_int32_t型)

                    SPEEX_PREPROCESS_GET_ECHO_SUPPRESS_ACTIVE 近端活动时获取最大残余回声衰减,单位dB(负spx_int32_t型)

                    SPEEX_PREPROCESS_SET_ECHO_STATE 为残余回声一直设置相关回声消除器(若没有残余回声消除,为指针或NULL

                    SPEEX_PREPROCESS_GET_ECHO_STATE 获取相关回声消除器(指针)

    6.2  回声消除

            Speex库现在包含一个可用的声学回声消除算法。使用回声消除器之前,需要包含头文件:

                    #include<speex/speex_echo.h>

            然后,创建回声消除器状态:

                   SpeexEchoState *echo_state = speex_echo_state_int(frame_size. filter_length);

    其中frame_size是你一次想处理的数据大小(样本数),filter_length是你想使用的回声消除滤波器长度(样本数,也叫尾长)。推荐使用20ms帧长(或等于编解码器帧长),确保容易执行FFT2的指数大小最佳)。推荐尾长近似为房间混响时间的三分之一,例如,在小房间中,混响时间是300ms,则尾长最好为100ms(采样率8000Hz对应800样本数)。

            创建回声消除器状态后,处理音频:

                   speex_echo_cancellation(echo_state, input_frame, echo_frame,output_frame);

    其中input_frame是麦克风采集到的音频数据(近端),echo_frame是扬声器中播放的音频(远端,待消除),output_frame是回声消除后的数据。

            需要重点考虑的是input_frameecho_frame之间的关系,在任何时候,近端中存在的回声必须发送到echo_frame作为参考信号。换言之,回声消除器不能消除没有参考信号的回声。另一方面,输入信号和参考回声信号的延时必须足够小,否则回声消除器效果不好甚至无效(译者注:具体参考NLMS算法,这是声学回声消除的一个关键问题,就是近端和远端的同步问题,因为不同步算法收敛慢甚至不收敛。微软的回声消除效果很好,但也对系统版本和硬件有要求,因为我们设置的采样率跟硬件实际采样率还是有一点差别的,具体参见微软相关论文)。在完全同步时,代码可以像这样:

                   write_to_soundcard(echo_frame, frame_size);

                   read_from_soundcard(input_frame, frame_size);

                   speex_echo_cancellation(echo_state, input_frame, echo_frame, output_frame);

            若想更好的回声消除效果,可在预处理器后设置回声消除器(见6.1),即初始化时调用:

                   speex_preprocess_ctl(preprocess_state, SPEEX_PREPROCESS_SET_ECHO_STATE,echo_state);

            对于1.2-beta2版本,可用另一种方法代替speex_echo_cancellation()函数。当录音和播放需要同步处理时(如不同线程或使用poll()select()系统调用),很难确定input_frame对应哪一echo_frame(译者注:即同步问题)。作为替代,回放线程可简单调用:

                   speex_echo_playback(echo_state,echo_frame);

    每次播放一帧,就调用录音线程处理录音的一帧:

                   speex_echo_capture(echo_state, input_frame, output_frame);

    在内部,speex_echo_playback()函数简单的将播放帧放在一个buffer中,然后在调用speex_echo_cancel()时由speex_can_capture()调用buffer中数据。使用这种API调用方式的一个副作用是回放帧需要延迟两帧,这是声卡引起的正常延迟。若回放和录音已经完全同步,speex_echo_cancellation()函数能更好地控制回声消除效果。

            最后,回声消除状态需要销毁:

                   speex_echo_state_destroy(echo_state);

    也可以重置回声消除器,这样就可以重新利用回声消除器而无需再创建一个新的:

                   speex_echo_state_reset(echo_state);

    6.2.1  故障查找

            回声消除器正常工作可能存在几个问题。其中之一是代码中存在bug(或效果是次优的),其他需要考虑的问题有:

                    1)当换一块声卡来录音和播放时,原来的回声消除器无效,除非两块声卡在同样的时钟脉冲源上有相同的采样时钟锁定,否则,它们的时钟总会有一些偏移,这将使得回声消除器不收敛(译者注:这就是软件设置的采样率与硬件实际的采样率有些许差别的问题)。

                    2)录音和回放之间的延时必须尽可能小。任何信号都是先回放(远端),然后被近端采样录音,过多的延时意味着部分滤波器长度的浪费。最坏情况下,延时比滤波器长度还大,此时不能消除任何回声。

                    3)回声尾长(滤波器长度)并不是越大越好。实际上,尾长越长,滤波器收敛越慢。当然,尾长太短也不能消除回声,但更常见的问题是许多人设置了一个很长的尾长然后奇怪为什么没有消除回声。

                    4)非线性失真不能被回声消除所使用的线性自适应滤波器建模,因此不能消除非线性失真。可使用好的音频设备并且避免饱和与削波。

            具体可参考Alexey Frunze的《Echo Cancellation Demystified》(http://www.embeddedstar.com/articles/2003/7/article20030720-1.html),解释了回声消除的基本原理。具体算法在不同文章中的描述不同,但回声消除的通用方法就是自适应滤波器。

            1.2beta2版本后,源代码中包含了一个新的echo_diagnostic.m工具。第一步是在建立是定义DUMP_ECHO_CANCEL_DATA,这将会使回声消除自动保存近端、远端和处理后的信号到文件中(aec_rec.swsec_play.swaec_out.sw),这正是AEC的输入和输出。使用这一工具时,需要开启倍频程:

                   echo_diagnostic('aec_rec.sw', 'aec_play', 'aec_diagnostoc', 1024);

    1024是滤波器长度,可以改变。将会输出一些有用的信息,回声消除音频将保存到aec_diagnostic.sw。如果输出无效(回声没有消除),可能是播放或录音的问题。

    6.3  抖动缓冲器

            启用抖动缓冲器,需要包含头文件:

                    #include<speex/speex_jitter.h>

    然后初始化一个新的抖动缓冲器:

                   JitterBuffer *state = jitter_buffer_init(step);

    其中step参数是默认时间步长(单位为时间戳的单位),用来调整延时和做隐蔽,合适的值为1,有时更大的值更好。例如,如果你能在20ms帧上做隐蔽,则抖动缓冲器中的点无需一个一个做。另一个例子是针对视频,对少于一帧的延时进行调整没有意义,后面可以随时改变这个值。

            抖动缓冲器API基于JitterBufferPacket类型,定义如下:

                    typedefstruct {

                           char             *data;                                                   /*数据包中的字节数据*/

                           spx_uint32_t  len;                                                   /*数据包长度,单位字节*/

                           spx_unit32_t  timestamp;                                     /*数据包时间戳*/

                           spx_uint32_t  span;                                               /*数据包覆盖的时间(时间戳为单位)*/

                     } JitterBufferPacket;

            例如,对于音频,timestamp就是RTP时间戳域,span是数据包中编码的样本数。对于Speex窄带,若数据包仅包含一帧,则span160

            当一个包到达时,将被插入到抖动缓冲器中:

                    JitterBufferPacketpacket;

                    /*填充数据包结构中每一项*/

                   jitter_buffer_put(state, &packet);

            当解码器准备解码一个包时,被解码的包可这样获取:

                    intstart_offset;

                    err =jitter_buffer_get(state, &packet, desired_span, &start_offset);

            jitter_buffer_put()jitter_buffer_get()在不同的线程中调用,则需要用互斥锁保护抖动缓冲器的状态。

            因为抖动缓冲器不用一个明确的计时器,所以需要告诉程序精确的时间,通过如下调用实现:

                  jitter_buffer_tick(state);

    这需要在播放线程中定期调用,这是播放线程休眠之前的最后一个抖动缓冲器调用(直到更多数据被回放)。在一些例子中,如下调用会更好:

                   jitter_buffer_remaining_span(state,remaining);

    第二个参数用指定还没被写入回放设备的仍保持在buffer中的数据,比如,若声卡需要256个样本数(由deaired_span指定),但jitter_buffer_get()返回320个样本数,则remaining=64

    6.4  重采样器

            Speex包含一个重采样器模型,要使用它,需要包含头文件:

                   #include<speex/speex_resampler.h>

            对于每一个要重采样的流,需要创建一个重采样器状态:

                  SpeexResamplerState *resampler;

                   resampler = speex_resampler_init(nb_channels, input_rate, output_rate, quality,&err);

    其中nb_channels是将被使用的信道数(交错的或非交错的);input_rate是输入流的采样率;output_rate是输出流的采样率;quality是要求的质量设置(010)。质量参数能控制质量、复杂度和等待时间之间的折中。高的质量参数意味着更少的噪声和混叠,但有更高的复杂度和更长的等待时间。通常质量参数为3对大多数桌面应用都可接受,质量参数为10在专业音响中最推荐。质量参数为0通常有不错的声音(当然比线性插值重采样好),但能听出人为迹象。

            实际重采样通过如下调用执行:

                    err =speex_resampler_process_int(resampler, channelID, in, &in_length, out,&out_length);

    其中channelID是被处理的信道ID,对于单声道流,为0in指针指向被选信道输入buffer的第一个样本;out指针指向输出的第一个样本。输入输出buffer的大小分别由in_lengthout_length指定。完成后,这些值被替代为重采样器读取和写入的样本数。除非发生错误,否则所有输入样本将被读取或/和所有输出样本将被写入。对于浮点型样本,函数speex_resampler_process_float()有相似功能。也可以一次处理多个通道。

            未完待续...

    6.5  环形缓冲区

            待补充...(英文版文档中是这样写的)

    7  格式与标准

            Speex既能窄带编码语音也能宽带编码语音,并且支持不同比特率。然而,某一个应用或设备无需支持所有特征。为了满足"Speex兼容,一个应用最少需满足一些基本特征。

            最起码,所有的窄带模型操作必须被解码器支持,这包括通过窄带解码器解码宽带比特流(宽带比特流包含一个能单独接解码的嵌入的窄带比特流)。如果存在,宽带解码器必须能解码窄带比特流,也许能解码所有宽带模式或能解码所有模式的嵌入的窄带部分(包含忽略高代比特)。

            对于编码器,最少一个窄带或宽带模式必须被支持。所有的编码模式不是必须支持的主要原因是一些平台可能能处理某些模式下的复杂编码。

    7.1  RTP有效载荷格式

            RTP有效载荷草案见附录C,最新版本可在网站http://www.speex.org/drafts/latest上获得。草案已于2003226日发送到英特网工程任务组(InternetEngineering Task Force,IETF)并将于318日在San Francisco举办的会议上讨论。

    7.2  MIME类型

            现在,你可以对Speex-in-Ogg使用MIME类型audio/x-speex。我们将在不久的将来提供类型audio/speex

    7.3  Ogg文件格式

            Speex比特流能存储在Ogg文件中。在这一情况下,Ogg文件的第一个包包含表7.1描述的Speex头文件,头文件中的所有整数字段以小头存储(高位字节放在高地址单元)。speex_string字段必须包含”Speex   "(尾部3个空格),标识比特流。下一个字段,speex_version包含编码文件的Speex版本。现在可以参见speex_header.[ch]获取更多信息。开始流标识(beginningofstream,b_o_s)在头文件中置为1,包头有packetno=0granulepos=0

            第二个包包含Speex注释头。使用的Vorbis注释格式在网站中描述:http://www.xiph.org/ogg/vorbis/doc/v-comment.html。这个包有packetno=1granulepos=0

            第三个之后的包每个都包含一个或多个(头文件中写的数量)Speex帧。这些包的packetno2开始,granulepos为包中编码的最后样本数。这些包的最后结束流(end ofstream,e_o_s)标识置为1

    7.1 Ogg/Speex头包

    8  CELP编码介绍

            Speex基于CELP,即码激励线性预测(Code Excited Linear Prediction)。本章介绍CELP基本原理,所以如果你已经对CELP很熟悉,可以跳到第9章。CELP技术基于以下3点思想:

                    1)利用一个线性预测(LP)模型模拟声道

                    2)使用(自适应的和固定的)密码本条目作为LP模型的输入(激励)

                    3)在感知加权域执行闭合搜索

            本章介绍CELP的基本思想,其原理也在不断发展中。

    8.1  语音产生的声源滤波器模型

            产生语音的声源滤波器模型假设声带是声音的起源(激励信号),并且声道作为一个滤波器对各种语音声音调整谱形状。虽然这只是一个近似,却因为其简单性使得这一模型广泛用于语音编码中。这一模型也是为什么大多数语音编解码器(包括Speex)对音乐信号效果很差的原因。在这一模型中,不同的因素能根据激励(源)和谱形(滤波器)得以区分。浊音(如元音)是一个周期性激励信号,并能用一个时域脉冲序列近似或频域固定间隔的谐波近似。另一方面,摩擦声(如“s”“sh"”f“)是一种类似于高斯白噪声的激励信号,所谓的浊音摩擦音(如”z“”v“)是一个由谐波部分和噪声部分组成的激励信号。

            声源滤波器模型通常结合线性预测使用。CELP模型基于声源滤波器模型,如图8.1CELP解码器所示。

    8.1  语音合成(解码)的CELP模型

    8.2  线性预测(LPC

            线性预测是包括CELP在内的许多语音编码技术的基础,其思想是用x[n]之前的N个样本的线性组合来预测x[n]

            

    其中y[n]就是x[n]的线性预测。预测误差为:

            

            线性预测的目的就是寻找使得误差平方和函数最小的最优预测系数ai

            

    可通过对ai求导,并使其等于0求得:

            

             对于一个N阶滤波器,滤波器系数ai通过求解一个N*N线性系统Ra=r求得,其中

    Rm)为信号x[n]的自相关:

            

            因为RHermitian Toeplitz矩阵,所以可以利用Levinson-Durbin算法,使得复杂度由O(N^3)降低为O(N^2)。而且,可以证明A(z)的所有根都在单位圆内,这意味着1/A(z)总是稳定的。实际应用中由于精确度有限,有两个常用技术可以保证滤波器稳定:第一,将R(0)乘以一个比1稍微大一点的数(如1.0001),等价于对信号添加噪声;第二,也可以对自相关加窗,等价于在频域滤波,减少尖锐的共振。

    8.3  音高预测

            在浊音段,语音信号是周期的,所以可以通过对过去激励乘以一个增益来近似当前激励信号e[n]

            

    其中T为音高周期,B为音高增益。我们称其为长期预测,因为激励是由e[n-T]预测而来,而T>>N

    8.4  创新码本

            最后的激励e[n]是预测音高来自固定码本信号c[n]之和,即码激励线性预测编码。最后的激励为:

            

    c[n]量化在CELP编解码器分配的大多数位中,意味着不能由线性预测或音高预测获得信息。在z域最终信号X(Z)可表示为:

            

    8.5  噪声加权

            大多数(即便不是全部)现代音频编解码器都会形成噪声,它大多出现在耳朵不能检测到的频域。例如,耳朵更能忍受噪声谱中较大声部分,反之亦然。为了获得最佳语音质量,CELP编解码器在感知加权域最小化噪声均方误差,所以可以在编码器中对误差信号应用一个感知噪声加权滤波器Wz)。在大多数CELP编解码器中,Wz)是一个由线性预测系数(LPC)派生而来零极点加权滤波器,一般用带宽阐述。令合成滤波器1/A(z)代表频谱包络,CELP编解码器典型地导出噪声加权滤波器为:

            

    Speex参考实现中y1=0.9y2=0.6。如果滤波器Az)在z平面有(复)极点pi,滤波器Az/y)将有一个pi‘=ypi的极点。

            加权滤波器应用于误差信号,用来通过analysis-by-synthesisAbS)最优化码本搜索,这导致趋向于1/W(z)的噪声谱形。虽然这一简单模型是CELP很有效的重要原因,但Wz)却是感知最优噪声加权函数的粗糙近似。图8.2阐释了由图8.1得来的噪声谱形。在本手册中,Wz)指的是噪声加权滤波器而1/Wz)是噪声整形滤波器(或曲线)。

    8.2  CELP中标准噪声整形,任意y轴偏移

    8.6  为分析合成技术

            CELP中的一个主要原理叫Analysis-by-Synthesis(AbS),即编码(分析)是通过闭环感知最优解码(合成)信号实现的。理论上,最佳的CELP流应通过尝试所有可能的位组合并选择其中产生最佳听觉解码信号的那个组合产生。实际中这明显是不可能的,有两个原因:所需的复杂度超出目前所有可用的硬件,并且最佳听觉选择标准是人的听力,机器做不到。

            为了利用有限的计算资源进行实时编码,CELP最优化利用感知加权函数分解成更小的、更易管理的、顺序的检索。

    9  Speex窄带模式

            这章介绍Speex如何在窄带(8kHz采样率)操作。这一模式下帧大小为20ms,对应160个采样点。每一帧又分成4个子帧,每个子帧40个采样点。

            许多设计决策基于如下的初衷和假设:

                    1)使从先前帧提取的信息数量最小化(为了对丢包具有鲁棒性)

                    2)动态可选择的码本(LSP,音高和创新)

                    3)子向量固定码本(创新)

    9.1  Whole-Frame分析

            窄带模式下,Speex帧长为20ms160个采样点),每帧又分成4个帧长5ms40个采样点)的子帧。对于大多数窄带比特率(8kbps),在帧级接口的编码参数仅有线谱对子(Line SpectralPairsLSP)和一个全局激励增益gframe,如图9.1所示。其他编码参数都在子帧接口中。

    9.1   帧开环分析

            每帧都会应用一个不对称的汉明窗,这个窗中心在第4个子帧,此时执行就会执行线性预测分析。因为线性预测系数(LPC)对量化不鲁棒,所以先将LPC转化为线谱对子(LSP)。LSP与第4个子帧有关并且与前3个子帧相关的LPC用当前和先前LSP系数线性内插获得。LSP系数然后转换回LPC滤波器A^(z)。非量化内插滤波器表示为A(z),能用做加权滤波器Wz),因为它对解码器是没必要的。

            为了使Speex对丢包更鲁棒,在LSP系数量化之前不进行预测。LSP用向量量化(VQ)编码,其中高质量模式用30位,低质量模式用18位。

    9.2  Sub-Frame Analysis-by-Synthesis

            Analysis-by-Synthesis(AbS)编码器如图9.2所示。Speex与其他大多数CELP编解码器主要有3个方面的不同。第一,目前大多数CELP编解码器使用一个单一增益的分数基音估计算法,Speex用一个整数来编码基音周期,但使用一个3抽头预测器(3增益)。因而自适应码本ea[n]可表示为:

                    

    其中g0g1g2是共同量化的音高增益,e[n]是编解码器激励存储器。值得注意的是若基音周期少于子帧大小,我们在周期T重复此激励。例如,当n-T+1>=0,我们用n-2T+1代替。大多数模式下,基音周期用范围为[17,144]7位编码,Bi系数用向量量化,其中高比特率(15kbps窄带及以上)用7位,低比特率(11kbps窄带及以下)用5位。

    9.2  子帧Analysis-by-synthesis闭环优化

            目前许多CELP编解码器用移动平均(MA)预测来编码固定码本增益,这能获得稍微好点的编码,但依赖于先前已编码的帧。第二个不同点是Speex将固定码本增益编码为全局激励增益gframe与子帧增益修正系数gsubf的积。这样通过消除帧间依赖提高了丢包鲁棒性。子帧增益修正系数在搜索固定码本之前被编码(不是闭环优化)且每个子帧用03位,这依赖于比特率。

            第三个不同是Speex用固定码本信号的子向量优化代替代数码本。每一个子帧分成520个采样点的子向量,每一个子向量从依赖于比特率的码本中选择且所有子向量串联形成一个子帧。例如,3.95kbps模式使用码本中32条目(5位)的20采样点子向量,意味着码本以每子帧10位编码或2000bps。另一方面,18.2kbps模式使用码本条目2568位)的5采样点子向量,所以码本以每帧64位编码,或12800bps

    9.3  位分配

            Speex定义了7种不同的窄带比特率,范围从250bps24.6kbps,尽管不推荐使用5.9kbps以下的模式。每个模式的位分配如表9.1所示。每帧以模式ID开始,它以4位编码,允许范围从015,但是仅仅使用了前7个值(剩余值保留)。表中的参数以它们在比特流中的打包顺序列出,所有帧参数在子帧参数之前打包。某一子帧参数在下一子帧打包之前全部打包。注意参数描述中的“OL”表示参数是整帧的开环(open loop)估计。

    9.1  窄带模式位分配

            目前为止,Speex没有进行平均意见得分(MOSMean Opinion Score)主观评测。为了获得理想的质量,表9.2列出了作者的主观意见。值得注意的是不同的人对语音质量的感觉是不同的,设计编解码器的人对主观意见可能有些偏见。最后,值得注意的是大多数编解码器(包括Speex)的编码质量因输入而异的。注意复杂度只是个近似值(精度0.5mflops,使用最低复杂度设置)。大多数模式下解码需要约0.5mflops(有感知增强为1mflops)。

    9.2  不同比特率的质量

    9.4  感知增强

            这部分在版本1.1.12之前有效。1.2-beta1版本(及之后)没包含,因为新的感知增强还没写好文档。

            编解码器的这部分仅应用于解码器,甚至可以在不影响内部操作下改变。因为这个原因,这里提供和描述的实现仅作为参考。增强系统分为两部分。首先,合成滤波器S(z)=1/A(z)替换为增强滤波器:

                                        

    其中a1a2依赖于所用的模式,a3=1/r(1-(1-ra1)/(1-ra2))r=0.9。增强的第二部分包括在激励域使用梳形滤波器增强音高。

    10  Speex宽带模式(子带CELP

            对于宽带,Speex用一个正交镜像滤波器(QMF)将带宽一分为二。16kHz信号因此分成两个8kHz信号,一个代表低宽带(0-4kHz),另一个代表高宽带(4-8kHz)。低宽带用第9章描述的窄带模式编码,用这种方式嵌入式窄带比特流也能用窄带解码器解码。因为低宽带编码已经描述,这章仅描述高宽带编码。

    10.1  线性预测

            高宽带的线性预测部分类似于窄带的,唯一的区别是我们仅使用12位编码高宽带LSP,并用一个多级向量量化(MSVQ)。先用6位量化10个系数,然后用6位量化误差。

    10.2  音高预测

            这部分很简单:高宽带没有音高预测。这有两个原因,第一,通常在这一带宽内只有少量谐波结构(4kHz以上);第二,很难实现,因为QMF折叠4-8kHz4-0kHz(反转频率轴),意味着谐波的位置已经不处于基频的倍数上。

    10.3  激励量化

            高宽带激励编码方式与窄带相同。

    10.4  位分配

            对于宽带模式,整个窄带帧在高宽带编码前打包,比特流的窄带部分定义在表9.1中,高宽带如表10.1所描述。对于宽带,模式IDSpeex质量设置相同。这意味着可能通过窄带解码器正确解码宽带帧,唯一的警告是如果同一个包内大于一帧,解码器需要跳过高宽带部分,以同步比特流。

    10.1  宽带模式下高带宽位分配

    10.2  宽带解码器不同比特率的解码质量

    A 代码示例     

             这部分展示运用Speex API编码和解码语音的代码示例,可通过调用如下指令编码和解码一个文件:

                    % sampleenc  in_file.sw  |  sampledec  out_file.sw

    其中的文件都是每个样本16比特编码(机器自然字节顺序)的原始文件(无文件头)。

    A.1  sampleenc.c

            sampleenc读取一个原始的16位采样的文件,进行编码并输出Speex流到stdout。注意所用的数据包与speexenc/speexdec是不兼容的。

     

    /*代码清单A.1:sampleenc源代码*/

    #include

    #include

     

    /*The framesize in hardcoded for this sample code but it doesn't have to be*/

    #defineFRAME_SIZE 160               

    intmain(int argc, char **argv)

    {

        char *inFile;

        FILE *fin;

        short in[FRAME_SIZE];

        float input[FRAME_SIZE];

        char cbits[200];

        int nbBytes;

        /*Holds the state of the encoder*/

        void *state;                     

        /*Holds bits so they can be read andwritten to by the Speex routines*/

        SpeexBits bits;                  

        int i, tmp;

       

        /*Create a new encoder state in narrowbandmode*/

        state =speex_encoder_init(&speex_nb_mode);

       

        /*Set the quality to 8 (15 kbps)*/

        tmp = 8;

        speex_encoder_ctl(state, SPEEX_SET_QUALITY,&tmp);

       

        inFile = argv[1];

        fin = fopen(inFile, "r");

       

        /*Initialization of the structure thatholds the bits*/

        speex_bits_init(&bits);

        while(1)

        {

            /*Read a 16 bits/sample audio frame*/

            fread(in, sizeof(short), FRAME_SIZE,fin);

            if (feof(fin))

                break;

            /*Copy the 16 bits values to float soSpeex can work on them*/

            for (i = 0; i < FRAME_SIZE, i++)

                input[i] = in[i];

           

            /*Flusf all the bits in the struct sowe can encode a new frame*/

            speex_bits_reset(&bits);

           

            /*Encode the frame*/

            speex_encode(state, input, &bits);

            /*Copy the bits to an array of charthat can be written*/

            nbBytes = speex_bits_write(&bits,cbits, 200);

           

            /*Write the size of the frame first.This is what sampledec expects but

            it's likely to be different in your ownapplication*/

            fwrite(&nbBytes, sizeof(int), 1,stdout);

            /*Write the compressed data*/

            fwrite(cbits, 1, nbBytes, stdout);

        }

       

        /*Destroy the encoder state*/

        speex_encoder_destroy(state);

        /*Destroy the bit-packing struct*/

        speex_bits_destroy(&bits);

        fclose(fin);

        return 0;

    }

     

    A.2  sampledec.c

            sampledecstdin中读取Speex流,解码并输出到一个原始16位采样文件。注意所用的数据包与speexenc/speexdec是不兼容的。

     

    /*清单A.2sampledec源代码*/

    #include

    #include

     

    /*The framesize in hardcoded for this sample code but it doesn't have to be*/

    #defineFRAME_SIZE 160

    intmain(int argc, char **argv)

    {

        char *outFile;

        FILE *fout;

        /*Holds the audio that will be written tofile (16 bits per sample)*/

        short out[FRAME_SIZE];

        /*Speex handle samples as float,so we needan array of floats*/

        float output[FRAME_SIZE]

        char cbits[200];

        int nbBytes;

        /*Holds the state of the decoder*/

        void * state;

        /*Holds bits so they can be read andwritten to by Speex routines*/

        SpeexBits bits;

        int i, tmp;

       

        /*Create a new decoder state in narrowbandmode*/

        state =speex_decoder_init(&speex_nb_mode);

        /*Set the perceptual enhancement on*/

        tmp = 1;

        speex_decoder_ctl(state, SPEEX_SET_ENH,&tmp);

       

        outFile = argv[1];

        fout = fopen(outFile, "w");

       

        /*Initialization of the structure thatholds the bits*/

        speex_bits_init(&bits);

        while(1)

        {

            /*Read the size encoded by sampleenc,this part will likely be

              different in your application*/

            fread(&nbBytes, sizeof(int), 1,stdin);

            fprintf(stderr, "nbBytes:%d\n", nbBytes);

            if (feof(stdin))

                break;

           

            /*Read the "packet" encodedby sampleenc*/

            fread(cbits, 1, nbBytes, stdin);

            /*Copy the data into the bit-streamstruct*/

            speex_bits_read_from(&bits, cbits,nbBytes);

           

            /*Decode the data*/

            speex_decode(state, &bits, output);

           

            /*Copy from float to short (16 bits)for output*/

            for (i = 0; i < FRAME_SIZE; i++)

                out[i] = output[i];

           

            /*Write the decoded audio to file*/

            fwrite(out, sizeof(short), FRAME_SIZE,fout);

        }

       

        /*Destroy the decoder state*/

        speex_decoder_destroy(state);

        /*Destroy the bit-stream truct*/

        speex_bits_destroy(&bits);

        fclose(fout);

        return 0;

    }

    B  Speex抖动缓冲器示例代码

    /*清单B.1:Speex数据包应用抖动缓冲器代码示例*/

    #include

    #include "speex_jitter_buffer.h"

     

    #ifndef NULL

    #define NULL 0

    #endif

     

    void speex_jitter_init(SpeexJitter *jitter,void *decoder, int sampling_rate)

    {

       jitter->dec = decoder;

       speex_decoder_ctl(decoder, SPEEX_GET_FRAME_SIZE,&jitter->frame_size);

       

       jitter->packets = jitter_buffer_init(jitter->frame_size);

       

       speex_bits_init(&jitter->current_packet);

       jitter->valid_bits = 0;

    }

     

    void speex_jitter_destroy(SpeexJitter*jitter)

    {

       jitter_buffer_destroy(jitter->packets);

       speex_bits_destroy(&jitter->current_packet);

    }

     

    void speex_jitter_put(SpeexJitter *jitter,char *packet, int len, int timestamp)

    {

       JitterBufferPacket p;

       p.data = packet;

       p.len = len;

       p.timestamp = timestamp;

       p.span = jitter->frame_size;

       jitter_buffer_put(jitter->packet, &p);

    }

     

    void speex_jitter_get(SpeexJitter *jitter,spx_int16_t *out, int *current_timestamp)

    {

       int i;

       int ret;

       spx_int32_t activity;

       char data[2048];

       JitterBufferPacket packet;

       packet.data = data;

       

       if(jitter->valid_bits)

        {

           /*Try decoding last received packet*/

           ret = speex_decode_init(jitter->dec, &jitter->current_packet,out);

           if(ret == 0)

           {

               jitter_buffer_tick(jitter->packets);

               return;

           }

           else

           {

               jitter->valid_bits = 0;

           }

        }

       

       ret = jitter_buffer_get(jitter->packet, &packet,jitter->frame_size, NULL);

       

       if (ret != JITTER_BUFFER_OK)

        {

           /*Packet is late or lost*/

           speex_decode_int(jitter->dec, NULL, out);

        }

       else

        {

           speex_bits_read_from(&jitter->current_packet, packet.data,packet.len);

           /*Decode packet*/

           ret = speex_decode_int(jitter->dec, &jitter->current_packet,out);

           if(ret == 0)

           {

               jitter->valid_bits = 1;

           }

           else

           {

               /*Error while decoding*/

               for (i = 0; i < jitter->frame_size; i++)

                    out[i] = 0;

           }

        }

       

       speex_decoder_ctl(jitter->dec, SPEEX_GET_ACTIVITY, &activity);

       if (activity < 30)

           jitter_buffer_update_delay(jitter->packets, &packet, NULL);

       jitter_buffer_tick(jitter->packets);

    }

     

    intspeex_jitter_get_pointer_timestamp(SpeexJitter *jitter)

    {

       return jitter_buffer_get_pointer_timestamp(jitter->packets);

    }

    展开全文
  • Speex 音频编解码

    2010-08-27 10:24:00
    // demo.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include #include #include #define FRAME_SIZE 160 int main(int argc, char **argv) ... short in[FRAME

    展开全文
  • Speex编/解码API手册

    2014-06-04 14:06:23
    5.3 编解码选项(speex_*_ctl) 5.4 模式查询 5.5 封包和带内信令 补充 后记   The libspeex library contains all the functions for encoding and decoding speech with the Speex codec. When linking on a...
    5.1 编码
    

    5.2 解码

    5.3 编解码选项(speex_*_ctl)

    5.4 模式查询

    5.5 封包和带内信令

    后记 

     

    The libspeex library contains all the functions for encoding and decoding speech with the Speex codec. When linking on a UNIX system, one must add -lspeex -lm to the compiler command line. One important thing to know is that libspeex calls are reentrant, but not thread-safe. That means that it is fine to use calls from many threads, but calls using the same state from multiple threads must be protected by mutexes. Examples of code can also be found in Appendix A and the complete API documentation is included in the Documentation section of the Speex website (http://www.speex.org/).

    Speex编解码器的libspeex包囊括了所有的语音编码和解码函数。在Linux系统中连接时,必须在编译器命令行中加入-lspeex –lm。需要知道的是,虽然libspeex的函数调用是可重入的,但不是线程安全的,所以在多线程调用时,如果使用共享资源需要进行互斥保护。附录A中有代码实例,在Speex站点(http://www.speex.org/ )的文档部分能下到完整的API文档。

     

     

    5.1编码

    In order to encode speech using Speex, one first needs to:
    #include <speex/speex.h>
    Then in the code, a Speex bit-packing struct must be declared, along with a Speex encoder state:
    SpeexBits bits;

    void *enc_state;
    The two are initialized by:
    speex_bits_init(&bits);
    enc_state = speex_encoder_init(&speex_nb_mode);
    For wideband coding, speex_nb_mode will be replaced by speex_wb_mode. In most cases, you will need to know the frame size used at the sampling rate you are using. You can get that value in the frame_size variable (expressed in samples, not
    bytes) with:
    speex_encoder_ctl(enc_state,SPEEX_GET_FRAME_SIZE,&frame_size);
    In practice, frame_size will correspond to 20 ms when using 8, 16, or 32 kHz sampling rate. There are many parameters that can be set for the Speex encoder, but the most useful one is the quality parameter that controls the quality vs bit-rate tradeoff.
    This is set by:
    speex_encoder_ctl(enc_state,SPEEX_SET_QUALITY,&quality);
    where quality is an integer value ranging from 0 to 10 (inclusively). The mapping between quality and bit-rate is described in Fig. 9.2 for narrowband.
    Once the initialization is done, for every input frame:
    speex_bits_reset(&bits);
    speex_encode_int(enc_state, input_frame, &bits);
    nbBytes = speex_bits_write(&bits, byte_ptr, MAX_NB_BYTES);
    where input_frame is a (short *) pointing to the beginning of a speech frame, byte_ptr is a (char *) where the encoded frame will be written,MAX_NB_BYTES is the maximumnumber of bytes that can be written to byte_ptr without causing an overflow and nbBytes is the number of bytes actually written to byte_ptr (the encoded size in bytes). Before calling speex_bits_write, it is possible to find the number of bytes that need to be written by calling speex_bits_nbytes(&bits), which returns a number of bytes.
    It is still possible to use the speex_encode() function, which takes a (float *) for the audio. However, this would make an eventual port to an FPU-less platform (like ARM) more complicated. Internally, speex_encode() and speex_encode_int() are processed in the same way. Whether the encoder uses the fixed-point version is only decided by the compile-time flags, not at the API level.
    After you’re done with the encoding, free all resources with:
    speex_bits_destroy(&bits);
    speex_encoder_destroy(enc_state);
    That’s about it for the encoder.

    使用Speex进行语音编码,首先要:

    #include < speex/speex.h >

    在代码中,需要声明Speex比特包结构体,同时设置Speex编码器状态:

    SpeexBits bits;

    void * enc_state;

    初始化两变量:

    speex_bits_init( &bits );

    enc_state = speex_encoder_init( &speex_nb_mode );

    speex_wb_mode代替为speex_nb_mode,即可转换为宽带编码。很多时候,你在使用采样率的需要知道帧的大小,可以通过变量frame_size(用样本中的单位表示,不以字节为单位)获得,调用下面函数:

    speex_encoder_ctl( enc_state, SPEEX_GET_FRAME_SIZE, &frame_size );

    实践表明,在采用81632kHz采样率的时候,frame_size大约对应于20msSpeex编码器还有很多参数可以设置,其中最有用的一个是质量参数,控制着比特率(bit-rate)交换的质量,通过下面函数设置:
    speex_encoder_ctl( enc_state, SPEEX_SET_QUALITY, &quality );

    quality是一个010(包含10)范围内的整数,窄带(narrowband)的质量和比特率(bit-rate)的对应关系如图9.2所示。

    初始化成功后,对于每帧的输入:

    speex_bits_reset( &bits );

    speex_encode_int( enc_state, input_frame, &bits );

    nbBytes = speex_bits_write( &bits, byte_ptr, MAX_NB_BYTES );

    其中,input_frame是指向每个Speex帧开始的short型指针,byte_ptr是将写入已被编码的帧的char型指针,MAX_NB_BYTESbyte_ptr在不导致溢出时可被写入的最大字节数,nbBytesbyte_ptr实际被写入的字节数(编码大小以字节为单位)。在调用speex_bits_write之前,可能会通过speex_bits_nbytes(&bits)返回的字节数获得需要被写入的字节数,也可能使用speex_encode()函数,它接受一个携带音频数据的float*型参数。不过这将使缺少浮点运算单元(FPU)的平台(如ARM)变的更为复杂。实际上,speex_encodespeex_encode_int()用同样的方法处理,编码器是否使用定点数取决于编译期的标志位,不由API来控制。

    完成编码后,释放所有资源:
    speex_bits_destroy( &bits );

    speex_encoder_destroy( enc_state );

    这是关于编码的部分。

      

    5.2解码

    In order to decode speech using Speex, you first need to:
    #include <speex/speex.h>
    You also need to declare a Speex bit-packing struct
    SpeexBits bits;
    and a Speex decoder state
    void *dec_state;
    The two are initialized by:
    speex_bits_init(&bits);
    dec_state = speex_decoder_init(&speex_nb_mode);
    For wideband decoding, speex_nb_mode will be replaced by speex_wb_mode. If you need to obtain the size of the frames that will be used by the decoder, you can get that value in the frame_size variable (expressed in samples, not bytes) with:
    speex_decoder_ctl(dec_state, SPEEX_GET_FRAME_SIZE, &frame_size);
    There is also a parameter that can be set for the decoder: whether or not to use a perceptual enhancer. This can be set by:
    speex_decoder_ctl(dec_state, SPEEX_SET_ENH, &enh);
    where enh is an int with value 0 to have the enhancer disabled and 1 to have it enabled. As of 1.2-beta1, the default is now to enable the enhancer.
    Again, once the decoder initialization is done, for every input frame:
    speex_bits_read_from(&bits, input_bytes, nbBytes);
    speex_decode_int(dec_state, &bits, output_frame);
    where input_bytes is a (char *) containing the bit-stream data received for a frame, nbBytes is the size (in bytes) of that bit-stream, and output_frame is a (short *) and points to the area where the decoded speech frame will be written. A NULL value as the second argument indicates that we don’t have the bits for the current frame. When a frame is lost, the Speex decoder will do its best to "guess" the correct signal.
    As for the encoder, the speex_decode() function can still be used, with a (float *) as the output for the audio. After you’re done with the decoding, free all resources with:
    speex_bits_destroy(&bits);
    speex_decoder_destroy(dec_state);

    使用Speex解码语音,首先要包含speex.h头文件。

    #include < speex/speex.h>

    需要声明Speex比特包的结构体和Speex解码器的状态

    SpeexBits bits;

    void* dec_state;

    进行初始化
    speex_bits_init( &bits );

    dec_state = speex_decoder_init( &speex_nb_mode );

    speex_wb_mode代替speex_nb_mode,可转换为宽带(windband)解码。可能过变量frame_size来获得解码的帧大小

    speex_decoder_ctl( dec_state, SPEEX_GET_FRAME_SIZE, &frame_size );

    还可以能过下面函数设置是否使用“知觉增强”功能

    speex_decoder_ctl( dec_state, SPEEX_SET_ENH, &enh );

    如果enh0则表是不启用,1则表示启用。在1.2-beta1中,默认是开启的。

    做完初始化工作后,则可对每个输入帧进行如下操作:

    speex_bits_read_from( &bits, input_bytes, nbBytes );

    speex_decode_int( dec_state, &bits, output_frame );

    其中,input_byteschar型指针,包含了一帧的比特流数据,nbBytes是那帧比特流数据的大小(以字节为单位),output_frameshort型指针,指向一块内存区域,存储对语音帧的解码。第二个参数为空值(NULL)意味着没有获得到正确的比特(bit)数据,出现丢帧,Speex解码器会尽可能猜测最为准确的语音信号。

    和编码器类似,可以用speex_decode()函数的一个float*型参数获得音频输出。

    完成解码后,释放掉所有资源:

    speex_bits_destory( &bits );

    speex_decoder_destory( dec_state );

     

    5.3编解码选项(speex_*_ctl)

    The Speex encoder and decoder support many options and requests that can be accessed through the speex_encoder_ctl and
    speex_decoder_ctl functions. These functions are similar to the ioctl system call and their prototypes are:
    void speex_encoder_ctl(void *encoder, int request, void *ptr);
    void speex_decoder_ctl(void *encoder, int request, void *ptr);
    Despite those functions, the defaults are usually good for many applications and optional settings should only be used when one understands them and knows that they are needed. A common error is to attempt to set many unnecessary settings.
    Here is a list of the values allowed for the requests. Some only apply to the encoder or the decoder. Because the last argument is of type void *, the _ctl() functions are not type safe, and shoud thus be used with care. The type spx_int32_t is the same as the C99 int32_t type.
    SPEEX_SET_ENH‡ Set perceptual enhancer to on (1) or off (0) (spx_int32_t, default is on)
    SPEEX_GET_ENH‡ Get perceptual enhancer status (spx_int32_t)
    SPEEX_GET_FRAME_SIZE Get the number of samples per frame for the current mode (spx_int32_t)
    SPEEX_SET_QUALITY† Set the encoder speech quality (spx_int32_t from 0 to 10, default is 8)
    SPEEX_GET_QUALITY† Get the current encoder speech quality (spx_int32_t from 0 to 10)
    SPEEX_SET_MODE† Set the mode number, as specified in the RTP spec (spx_int32_t)
    SPEEX_GET_MODE† Get the current mode number, as specified in the RTP spec (spx_int32_t)
    SPEEX_SET_VBR† Set variable bit-rate (VBR) to on (1) or off (0) (spx_int32_t, default is off)
    SPEEX_GET_VBR† Get variable bit-rate (VBR) status (spx_int32_t)
    SPEEX_SET_VBR_QUALITY† Set the encoder VBR speech quality (float 0.0 to 10.0, default is 8.0)
    SPEEX_GET_VBR_QUALITY† Get the current encoder VBR speech quality (float 0 to 10)
    SPEEX_SET_COMPLEXITY† Set the CPU resources allowed for the encoder (spx_int32_t from 1 to 10, default is 2)
    SPEEX_GET_COMPLEXITY† Get the CPU resources allowed for the encoder (spx_int32_t from 1 to 10, default is 2)
    SPEEX_SET_BITRATE† Set the bit-rate to use the closest value not exceeding the parameter (spx_int32_t in bits per second)
    SPEEX_GET_BITRATE Get the current bit-rate in use (spx_int32_t in bits per second)
    SPEEX_SET_SAMPLING_RATE Set real sampling rate (spx_int32_t in Hz)
    SPEEX_GET_SAMPLING_RATE Get real sampling rate (spx_int32_t in Hz)
    SPEEX_RESET_STATE Reset the encoder/decoder state to its original state, clearing all memories (no argument)
    SPEEX_SET_VAD† Set voice activity detection (VAD) to on (1) or off (0) (spx_int32_t, default is off)
    SPEEX_GET_VAD† Get voice activity detection (VAD) status (spx_int32_t)
    SPEEX_SET_DTX† Set discontinuous transmission (DTX) to on (1) or off (0) (spx_int32_t, default is off)
    SPEEX_GET_DTX† Get discontinuous transmission (DTX) status (spx_int32_t)
    SPEEX_SET_ABR† Set average bit-rate (ABR) to a value n in bits per second (spx_int32_t in bits per second)
    SPEEX_GET_ABR† Get average bit-rate (ABR) setting (spx_int32_t in bits per second)
    SPEEX_SET_PLC_TUNING† Tell the encoder to optimize encoding for a certain percentage of packet loss (spx_int32_t in percent)
    SPEEX_GET_PLC_TUNING† Get the current tuning of the encoder for PLC (spx_int32_t in percent)
    SPEEX_SET_VBR_MAX_BITRATE† Set the maximum bit-rate allowed in VBR operation (spx_int32_t in bits per second)
    SPEEX_GET_VBR_MAX_BITRATE† Get the current maximum bit-rate allowed in VBR operation (spx_int32_t in bits per second)
    SPEEX_SET_HIGHPASS Set the high-pass filter on (1) or off (0) (spx_int32_t, default is on)
    SPEEX_GET_HIGHPASS Get the current high-pass filter status (spx_int32_t)
    † applies only to the encoder
    ‡ applies only to the decoder

    Speex编码器和解码器可以通过访问speex_encoder_ctlspeex_decoder_ctl函数来设置更多选项,类似于系统函数ioctl。它们的原型是:

    void speex_encoder_ctl( void* encoder, int request, void* ptr );

    void speex_decoder_ctl( void* decoder, int request, void* ptr );

    尽管拥有这些函数,但一般的应用程序在默认情况下就足够,如果要设置则需了解并知道为什么需要它们,勿随变设置。

    下面列出了各种需求的允许值,其中一些只能应用于编码器或解码器。因为最后一个参数是void指针,所以_ctl()函数不是类型安全的,应小心使用。spx_int32_t类型同C99中的int32_t

    SPEEX_SET_ENH:设置知觉增强,1开启,0关闭(spx_int32_t,默认开启)

    SPEEX_GET_ENH:获得知觉增强状态( spx_int32_t)

    SPEEX_SET_QUALITY:设置编码质量(spx_int32_t010,默认为8

    SPEEX_GET_QUALITY:获得当前语音编码质量(spx_int32_t010

    SPEEX_SET_MODE:设置模式,指明RTP协议规格(spx_int32_t

    SPEEX_GET_MODE:获得当前模式,指明的RTP协议规格(spx_int32_t

    SPEEX_SET_VBR:设置变比特率(VBR),1开启,0关闭(spx_int32_t默认关闭)

    SPEEX_GET_VBR获得变比特率功能当前是否开启(spx_int32_t

    SPEEX_SET_VBR_QUALITY:设置变比特率语音的编码质量(浮点数从0.010.0,默认8.0

    SPEEX_GET_VBR_QUALITY:获得当前变比特率语音的编码质量(浮点数从0.010.0

    SPEEX_SET_COMPLEXITY:设置编码器的可用CPU资源(spx_int32_t110,默认为2

    SPEEX_GET_COMPLEXITY:获取编码器的可用CPU资源(spx_int32_t110,默认为2

    SPEEX_SET_BITRATE:设置不超过参数设置的最佳比特值(spx_int32_t单位bits/s )

    SPEEX_GET_BITRATE:获取当前使用的比特率(spx_int32_t单位 bits/s

    SPEEX_SET_SAMPLING_RATE:设置实时采样率(spx_int32_t单位Hz

    SPEEX_GET_SAMPLING_RATE:获取实时采样率(spx_int32_t单位Hz

    SPEEX_RESET_STATE:重置编/解码器到原始状态,并清除所有记忆(无参数)

    SPEEX_SET_VAD:设置静音检测特性(VAD),1为打开,0为关闭(spx_int32_t,默认为关闭)

    SPEEX_GET_VAD:获取静音检测是否打开(spx_int32_t

    SPEEX_SET_DTX:设计非连续性传输(DTX),1为打开,0为关闭(spx_int32_t,默认为关闭)

    SPEEX_GET_DTX:获取非连续性传输(DTX)是否打开(spx_int32_t

    SPEEX_SET_ABR:设置平均比特率(ABR)值,单位bits/s(spx_int32_t,单位 bits/s )

    SPEEX_GET_ABR:获得平均比特率设置(spx_int32_t,单位bits/s

    SPEEX_SET_PLC_TUNING:让编码器对一定的失包率开启最优化编码(spx_int32_t,单位%)

    SPEEX_GET_PLC_TUNING获取编码器为PLC的当前调整(spx_int32_t,单位%)

    SPEEX_SET_VBR_MAX_BITRATE:设置允许变比特率(VBR)使用的最大比特率(spx_int32_t,单位bits/s

    SPEEX_GET_VBR_MAX_BITRATE:获取允许变比特率(VBR)使用的最大比特率(spx_int32_t,单位bits/s

    SPEEX_SET_HIGHPASS:设置高通滤波器,1为打开,0为关闭(spx_int32_t,默认为打开)

    SPEEX_GET_HIGHPASS:获取高通滤波器状态(spx_int32_t

    仅用于编/解码器。

     

    5.4模式查询

    Speex modes have a query system similar to the speex_encoder_ctl and speex_decoder_ctl calls. Since modes are read-only,it is only possible to get information about a particular mode. The function used to do that is:
    void speex_mode_query(SpeexMode *mode, int request, void *ptr);

    类似于调用speex_encoder_ctlspeex_decoder_ctlSpeex有模式查询系统。因为模式是只读的,所以只能获得模式的详细信息。使用如下函数:

    void speex_mode_query( SpeexMode* mode, int request, void* ptr );

     

    The admissible values for request are (unless otherwise note, the values are returned through ptr):
    SPEEX_MODE_FRAME_SIZE Get the frame size (in samples) for the mode
    SPEEX_SUBMODE_BITRATE Get the bit-rate for a submode number specified through ptr (integer in bps).

    受理的请求值(除非另有说明,要不返回值都是通过ptr):

    SPEEX_MODE_FRAME_SIZE获得模式的帧大小(样本中)

    SPEEX_SUBMODE_BITRATE:获取通过ptr指定的子模式数量的比特率(以bps为单位的整数)

     

    5.5封包和带内信令

    Sometimes it is desirable to pack more than one frame per packet (or other basic unit of storage). The proper way to do it is to call speex_encode N times before writing the stream with speex_bits_write. In cases where the number of frames is not determined by an out-of-band mechanism, it is possible to include a terminator code. That terminator consists of the code 15 (decimal) encoded with 5 bits, as shown in Table 9.2. Note that as of version 1.0.2, calling speex_bits_write automatically inserts the terminator so as to fill the last byte. This doesn’t involves any overhead and makes sure Speex can always detect when there is no more frame in a packet.

    有时我们打包的数据不只一帧(或其他基本存储单元),正确做法是在用speex_bits_write写入流数据之前调用Nspeex_encode。这种情况下的帧数不是由带外机制决定的,它会包含一个终结码。如表9.2所示,这个终结码是由用5bits编码的Mode 15组成。如果是1.0.2版本需注意,调用speex_bits_write时,为了填充最后字节,它会自动添加终结码。这不会增加开销,并能确保Speex一直检测到包中没有更多帧为止。

     

    It is also possible to send in-band “messages” to the other side. All these messages are encoded as “pseudo-frames” of mode 14 which contain a 4-bit message type code, followed by the message. Table 5.1 lists the available codes, their meaning and the size of the message that follows. Most of these messages are requests that are sent to the encoder or decoder on the other end, which is free to comply or ignore them. By default, all in-band messages are ignored.

    当然也可以通过带内“消息”的方法,所有这些消息是作为Mode14的“伪帧”编码的,Mode14包含4bit的消息类型代码。表5.1列出了可用代码的说明和大小,发送给编/解码器的的消息大部分都可随意的被接受或被忽略。默认情况下,所有带内消息都被忽略掉了。

    In-band signalling codes

     

    5.1带内信号代码

    Finally, applications may define custom in-band messages using mode 13. The size of the message in bytes is encoded with 5 bits, so that the decoder can skip it if it doesn’t know how to interpret it.

    最后,一些应用会使用Mode 13自定义带内消息,消息的字节大小是用5bits编码的,所以如果编码器不知道如何解析它就会跳过。

     

    补充:

    本是第9章--Speex窄带模式中的图和表格,但本章中需要参考,贴上来

    Analysis-by-synthesis closed-loop optimization on a sub-frame

    Figure 9.2: Analysis-by-synthesis closed-loop optimization on a sub-frame.

     

     Quality versus bit rate

    Table 9.2: Quality versus bit-rate

    展开全文
  • Android中录音使用AudioRecord录音后的格式为pcm,要想播放需要转换格式,可以加入44字节的头转换为wav格式然后播放,但是我们要在网络上传输还是要把音频压缩一下,压缩为speex文件方便传输,节省流量,下面讲解...

    Android中录音使用AudioRecord录音后的格式为pcm,要想播放需要转换格式,可以加入44字节的头转换为wav格式然后播放,但是我们要在网络上传输还是要把音频压缩一下,压缩为speex文件方便传输,节省流量,下面讲解怎样打speex的so库,和怎样使用speex在wav和spx文件之间相互转换。

    1.在speex官网上下载speex源码 https://www.speex.org/downloads/

    这里写图片描述

    2.得到speex-1.2rc2.tar.gz ,然后解压缩得到speex文件夹内容如图

    这里写图片描述

    3.创建一个Android项目,命名为Speex,项目中新建jni目录,并把speex源码目录下的libspeex和include目录及其子目录文件全部拷贝到$project/jni目录下,拷贝完成如下图:

    这里写图片描述

    4.创建几个文件

    • 在jni目录下新增Android.mk文件,复制如下内容(意思是编译speex_jni.cpp和libspeex下所有的c文件,有三个测试文件不用编译testenc_uwb.c、testenc_wb.c 、testenc.c )
    LOCAL_PATH := $(call my-dir)
    
    include $(CLEAR_VARS)
    
    LOCAL_MODULE    := libspeex
    LOCAL_CFLAGS = -DFIXED_POINT -DUSE_KISS_FFT -DEXPORT="" -UHAVE_CONFIG_H
    LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
    
    #LOCAL_SRC_FILES :=  
    LOCAL_SRC_FILES := speex_jni.cpp \
            ./libspeex/bits.c \
            ./libspeex/cb_search.c \
            ./libspeex/exc_10_16_table.c \
            ./libspeex/exc_10_32_table.c \
            ./libspeex/exc_20_32_table.c \
            ./libspeex/exc_5_256_table.c \
            ./libspeex/exc_5_64_table.c \
            ./libspeex/exc_8_128_table.c \
            ./libspeex/filters.c \
            ./libspeex/gain_table_lbr.c \
            ./libspeex/gain_table.c \
            ./libspeex/hexc_10_32_table.c \
            ./libspeex/hexc_table.c \
            ./libspeex/high_lsp_tables.c \
            ./libspeex/kiss_fft.c \
            ./libspeex/kiss_fftr.c \
            ./libspeex/lpc.c \
            ./libspeex/lsp_tables_nb.c \
            ./libspeex/lsp.c \
            ./libspeex/ltp.c \
            ./libspeex/modes_wb.c \
            ./libspeex/modes.c \
            ./libspeex/nb_celp.c \
            ./libspeex/quant_lsp.c \
            ./libspeex/sb_celp.c \
            ./libspeex/smallft.c \
            ./libspeex/speex_callbacks.c \
            ./libspeex/speex_header.c \
            ./libspeex/speex.c \
            ./libspeex/stereo.c \
            ./libspeex/vbr.c \
            ./libspeex/vorbis_psy.c \
            ./libspeex/vq.c \
            ./libspeex/window.c \
    
    include $(BUILD_SHARED_LIBRARY)
    
    
    • 在jni目录下增加Application.mk,复制内容如下(意思是编译所有平台下的so文件):
    APP_ABI := all
    • 在jni/include/speex/目录下新增speex_config_types.h文件,复制内容如下:
    #ifndef __SPEEX_TYPES_H__
    #define __SPEEX_TYPES_H__
    
    typedef short spx_int16_t;
    typedef unsigned short spx_uint16_t;
    typedef int spx_int32_t;
    typedef unsigned int spx_uint32_t;
    
    #endif
    
    
    • 在包com.speex.util中新建SpeexUtil类,复制以下内容(主要定义native方法)
    package com.speex.util;
    
    /**
     * 
     * @author jxn
     * 2016年11月15日有更新,更改默认压缩比,并增加单例模式,重新打jar包
     */
    public class SpeexUtil {
    
        /*
         * quality 1 : 4kbps (very noticeable artifacts, usually intelligible) 2 :
         * 6kbps (very noticeable artifacts, good intelligibility) 4 : 8kbps
         * (noticeable artifacts sometimes) 6 : 11kpbs (artifacts usually only
         * noticeable with headphones) 8 : 15kbps (artifacts not usually noticeable)
         */
        //设置为4时压缩比为1/16(与编解码密切相关)
        private static final int DEFAULT_COMPRESSION = 4;
    
        static {
            try {
                System.loadLibrary("speex");
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    
        private static SpeexUtil speexUtil = null;
    
        SpeexUtil() {
            open(DEFAULT_COMPRESSION);
        }
    
        public static SpeexUtil getInstance(){
            if (speexUtil == null) {    
                synchronized (SpeexUtil.class) {    
                   if (speexUtil == null) {    
                       speexUtil = new SpeexUtil();   
                   }    
                }    
            }    
            return speexUtil;
        }
    
        public native int open(int compression);
    
        public native int getFrameSize();
    
        public native int decode(byte encoded[], short lin[], int size);
    
        public native int encode(short lin[], int offset, byte encoded[], int size);
    
        public native void close();
    
    
    }
    
    • 在jni根目录下新建speex_jni.cpp实现刚才的native方法,复制内容:
    #include <jni.h>
    
    #include <string.h>
    #include <unistd.h>
    
    #include <speex/speex.h>
    
    static int codec_open = 0;
    
    static int dec_frame_size;
    static int enc_frame_size;
    
    static SpeexBits ebits, dbits;
    void *enc_state;
    void *dec_state;
    
    static JavaVM *gJavaVM;
    
    extern "C"
    JNIEXPORT jint JNICALL Java_com_speex_util_SpeexUtil_open
      (JNIEnv *env, jobject obj, jint compression) {
        int tmp;
    
        if (codec_open++ != 0)
            return (jint)0;
    
        speex_bits_init(&ebits);
        speex_bits_init(&dbits);
    
        enc_state = speex_encoder_init(&speex_nb_mode); 
        dec_state = speex_decoder_init(&speex_nb_mode); 
        tmp = compression;
        speex_encoder_ctl(enc_state, SPEEX_SET_QUALITY, &tmp);
        speex_encoder_ctl(enc_state, SPEEX_GET_FRAME_SIZE, &enc_frame_size);
        speex_decoder_ctl(dec_state, SPEEX_GET_FRAME_SIZE, &dec_frame_size);
    
        return (jint)0;
    }
    
    extern "C"
    JNIEXPORT jint JNICALL Java_com_speex_util_SpeexUtil_encode
        (JNIEnv *env, jobject obj, jshortArray lin, jint offset, jbyteArray encoded, jint size) {
    
            jshort buffer[enc_frame_size];
            jbyte output_buffer[enc_frame_size];
        int nsamples = (size-1)/enc_frame_size + 1;
        int i, tot_bytes = 0;
    
        if (!codec_open)
            return 0;
    
        speex_bits_reset(&ebits);
    
        for (i = 0; i < nsamples; i++) {
            env->GetShortArrayRegion(lin, offset + i*enc_frame_size, enc_frame_size, buffer);
            speex_encode_int(enc_state, buffer, &ebits);
        }
    
        tot_bytes = speex_bits_write(&ebits, (char *)output_buffer,
                         enc_frame_size);
        env->SetByteArrayRegion(encoded, 0, tot_bytes,
                    output_buffer);
    
            return (jint)tot_bytes;
    }
    
    extern "C"
    JNIEXPORT jint JNICALL Java_com_speex_util_SpeexUtil_decode
        (JNIEnv *env, jobject obj, jbyteArray encoded, jshortArray lin, jint size) {
    
            jbyte buffer[dec_frame_size];
            jshort output_buffer[dec_frame_size];
            jsize encoded_length = size;
    
        if (!codec_open)
            return 0;
    
        env->GetByteArrayRegion(encoded, 0, encoded_length, buffer);
        speex_bits_read_from(&dbits, (char *)buffer, encoded_length);
        speex_decode_int(dec_state, &dbits, output_buffer);
        env->SetShortArrayRegion(lin, 0, dec_frame_size,
                     output_buffer);
    
        return (jint)dec_frame_size;
    }
    
    extern "C"
    JNIEXPORT jint JNICALL Java_com_speex_util_SpeexUtil_getFrameSize
        (JNIEnv *env, jobject obj) {
    
        if (!codec_open)
            return 0;
        return (jint)enc_frame_size;
    
    }
    
    extern "C"
    JNIEXPORT void JNICALL Java_com_speex_util_SpeexUtil_close
        (JNIEnv *env, jobject obj) {
    
        if (--codec_open != 0)
            return;
    
        speex_bits_destroy(&ebits);
        speex_bits_destroy(&dbits);
        speex_decoder_destroy(dec_state); 
        speex_encoder_destroy(enc_state); 
    }
    

    新增完成后目录结构如下(共增加了5个文件):

    这里写图片描述

    5.下面开始生成so库

    使用ndk-build生成so文件,生成方法参考本文:HelloJni导入到Eclipse及命令行编译

    编译完成,如图:

    这里写图片描述

    刷新项目后,生成了obj文件夹:

    这里写图片描述

    6.将Speex提供为jar包和so的形式供外部项目使用,使用时拷贝jar包到lib中,so拷贝到lib中对应的平台下

    打开cmd,运行下图中命令将SpeexUtil.class打入jar包内

    这里写图片描述

    最后附上源码和打包的jar和so库下载地址:

    http://download.csdn.net/detail/jianiuqi/9683657

    下一篇文件中将介绍怎样使用生成的jar完成wav转speex和speex转wav,并提供了使用示例源码下载(包括AS和Eclipse版本):
    Android语音——speex编码解码(二)

    展开全文
  • 以前在应用中使用到了Speex编解码,近来总结了一下Speex在android上的实现。Speex是一套主要针对语音的开源免费,无专利保护的音频压缩格式。Speex工程着力于通过提供一个可以替代高性能语音编解码来降低语音应用...

    此文的原创地址:


    上一篇文章已经对speex的工作原理做介绍。这里是android端的编译方法。
    以前在应用中使用到了Speex编解码,近来总结了一下Speex在android上的实现。Speex是一套主要针对语音的开源免费,无专利保护的音频压缩格式。Speex工程着力于通过提供一个可以替代高性能语音编解码来降低语音应用输入门槛 。另外,相对于其它编解码,Speex也很适合网络应用,在网络应用上有着自己独特的优势。同时,Speex还是GNU工程的一部分,在改版的BSD协议中得到了很好的支持。Speex是基于CELP并且专门为码率在2-44kbps的语音压缩而设计的。Speex源码是基于c语音实现的(也有java实现,效率相对较低)。

    1. 去Speex官网下载最新Speex源码。
    2. 创建新的android工程,并创建jni文件夹。
    3. 把speex源码目录下的libspeex和include目录及其子目录文件全部拷贝到$project/jni目录下。
    4. 在jni目录下新增Android.mk文件,编辑内容如下:
    LOCAL_PATH := $(call my-dir)
    
    include $(CLEAR_VARS)
    
    LOCAL_MODULE:= libspeex
    LOCAL_CFLAGS = -DFIXED_POINT -DUSE_KISS_FFT -DEXPORT="" -UHAVE_CONFIG_H
    LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
    
    LOCAL_SRC_FILES :=\
    libspeex/bits.c \
    libspeex/buffer.c \
    libspeex/cb_search.c \
    libspeex/exc_10_16_table.c \
    libspeex/exc_10_32_table.c \
    libspeex/exc_20_32_table.c \
    libspeex/exc_5_256_table.c \
    libspeex/exc_5_64_table.c \
    libspeex/exc_8_128_table.c \
    libspeex/fftwrap.c \
    libspeex/filterbank.c \
    libspeex/filters.c \
    libspeex/gain_table.c \
    libspeex/gain_table_lbr.c \
    libspeex/hexc_10_32_table.c \
    libspeex/hexc_table.c \
    libspeex/high_lsp_tables.c \
    libspeex/jitter.c \
    libspeex/kiss_fft.c \
    libspeex/kiss_fftr.c \
    libspeex/lpc.c \
    libspeex/lsp.c \
    libspeex/lsp_tables_nb.c \
    libspeex/ltp.c \
    libspeex/mdf.c \
    libspeex/modes.c \
    libspeex/modes_wb.c \
    libspeex/nb_celp.c \
    libspeex/preprocess.c \
    libspeex/quant_lsp.c \
    libspeex/resample.c \
    libspeex/sb_celp.c \
    libspeex/scal.c \
    libspeex/smallft.c \
    libspeex/speex.c \
    libspeex/speex_callbacks.c \
    libspeex/speex_header.c \
    libspeex/stereo.c \
    libspeex/vbr.c \
    libspeex/vq.c \
    libspeex/window.c \
    speex_jni.cpp \
    
    
    include $(BUILD_SHARED_LIBRARY)

    在jni目录下新增Application.mk文件,编辑内容如下

    APP_ABI := armeabi armeabi-v7a  

    在$project/jni/include/speex/目录下新增speex_config_types.h文件,编辑内容如下

    #ifndef __SPEEX_TYPES_H__  
    #define __SPEEX_TYPES_H__  
    typedef short spx_int16_t;  
    typedef unsigned short spx_uint16_t;  
    typedef int spx_int32_t;  
    typedef unsigned int spx_uint32_t;  
    #endif  

    创建JNI包装类speex_jni.cpp,用来调用Speex中的C代码函数,编辑内容如下

    #include <jni.h>
    
    #include <string.h>
    #include <unistd.h>
    
    #include <speex/speex.h>
    
    static int codec_open = 0;
    
    static int dec_frame_size;
    static int enc_frame_size;
    
    static SpeexBits ebits, dbits;
    void *enc_state;
    void *dec_state;
    
    static JavaVM *gJavaVM;
    
    extern "C"
    JNIEXPORT jint JNICALL Java_com_trunkbow_speextest_Speex_open
      (JNIEnv *env, jobject obj, jint compression) {
        int tmp;
    
        if (codec_open++ != 0)
            return (jint)0;
    
        speex_bits_init(&ebits);
        speex_bits_init(&dbits);
    
        enc_state = speex_encoder_init(&speex_nb_mode);
        dec_state = speex_decoder_init(&speex_nb_mode);
        tmp = compression;
        speex_encoder_ctl(enc_state, SPEEX_SET_QUALITY, &tmp);
        speex_encoder_ctl(enc_state, SPEEX_GET_FRAME_SIZE, &enc_frame_size);
        speex_decoder_ctl(dec_state, SPEEX_GET_FRAME_SIZE, &dec_frame_size);
    
        return (jint)0;
    }
    
    extern "C"
    JNIEXPORT jint Java_com_trunkbow_speextest_Speex_encode
        (JNIEnv *env, jobject obj, jshortArray lin, jint offset, jbyteArray encoded, jint size) {
    
            jshort buffer[enc_frame_size];
            jbyte output_buffer[enc_frame_size];
        int nsamples = (size-1)/enc_frame_size + 1;
        int i, tot_bytes = 0;
    
        if (!codec_open)
            return 0;
    
        speex_bits_reset(&ebits);
    
        for (i = 0; i < nsamples; i++) {
            env->GetShortArrayRegion(lin, offset + i*enc_frame_size, enc_frame_size, buffer);
            speex_encode_int(enc_state, buffer, &ebits);
        }
        //env->GetShortArrayRegion(lin, offset, enc_frame_size, buffer);
        //speex_encode_int(enc_state, buffer, &ebits);
    
        tot_bytes = speex_bits_write(&ebits, (char *)output_buffer,
                         enc_frame_size);
        env->SetByteArrayRegion(encoded, 0, tot_bytes,
                    output_buffer);
    
            return (jint)tot_bytes;
    }
    
    extern "C"
    JNIEXPORT jint JNICALL Java_com_trunkbow_speextest_Speex_decode
        (JNIEnv *env, jobject obj, jbyteArray encoded, jshortArray lin, jint size) {
    
            jbyte buffer[dec_frame_size];
            jshort output_buffer[dec_frame_size];
            jsize encoded_length = size;
    
        if (!codec_open)
            return 0;
    
        env->GetByteArrayRegion(encoded, 0, encoded_length, buffer);
        speex_bits_read_from(&dbits, (char *)buffer, encoded_length);
        speex_decode_int(dec_state, &dbits, output_buffer);
        env->SetShortArrayRegion(lin, 0, dec_frame_size,
                     output_buffer);
    
        return (jint)dec_frame_size;
    }
    
    extern "C"
    JNIEXPORT jint JNICALL Java_com_trunkbow_speextest_Speex_getFrameSize
        (JNIEnv *env, jobject obj) {
    
        if (!codec_open)
            return 0;
        return (jint)enc_frame_size;
    
    }
    
    extern "C"
    JNIEXPORT void JNICALL Java_com_trunkbow_speextest_Speex_close
        (JNIEnv *env, jobject obj) {
    
        if (--codec_open != 0)
            return;
    
        speex_bits_destroy(&ebits);
        speex_bits_destroy(&dbits);
        speex_decoder_destroy(dec_state);
        speex_encoder_destroy(enc_state);
    }
    

    在Java层创建Speex工具类,内容如下:

    package com.trunkbow.speextest;
    
    public class Speex {
         /* quality 
         * 1 : 4kbps (very noticeable artifacts, usually intelligible) 
         * 2 : 6kbps (very noticeable artifacts, good intelligibility) 
         * 4 : 8kbps (noticeable artifacts sometimes) 
         * 6 : 11kpbs (artifacts usually only noticeable with headphones) 
         * 8 : 15kbps (artifacts not usually noticeable) 
         */  
        private static final int DEFAULT_COMPRESSION = 8;   
    
        Speex() {  
        }  
    
        public void init() {  
            load();   
            open(DEFAULT_COMPRESSION);   
        }  
    
        private void load() {  
            try {  
                System.loadLibrary("speex");  
            } catch (Throwable e) {  
                e.printStackTrace();  
            }  
    
        }  
    
        public native int open(int compression);  
        public native int getFrameSize();  
        public native int decode(byte encoded[], short lin[], int size);  
        public native int encode(short lin[], int offset, byte encoded[], int size);  
        public native void close();  
    }
    
    展开全文
  • 以前在应用中使用到了Speex编解码,近来总结了一下Speex在android上的实现。Speex是一套主要针对语音的开源免费,无专利保护的音频压缩格式。Speex工程着力于通过提供一个可以替代高性能语音编解码来降低语音应用...
  • Speex编解码器手册 1.2Beta3版 The Speex Codec Manual Version 1.2 Beta 3 Speex编解码器(http://www.speex.org/)的存在是因为需要一款开源且免软件专利使用费的语音编解码器,这是任何开源软件可用的必要条件。...
  • 以前在应用中使用到了Speex编解码,近来总结了一下Speex在Android上的实现。Speex是一套主要针对语音的开源免费,无专利保护的音频压缩格式。Speex工程着力于通过提供一个可以替代高性能语音编解码来降低语音应用...
  • Speex编解码

    2013-03-12 11:56:01
    以前在应用中使用到了Speex编解码,近来总结了一下Speex在android上的实现。Speex是一套主要针对语音的开源免费,无专利保护的音频压缩格式。Speex工程着力于通过提供一个可以替代高性能语音编解码来降低语音应用...
  • 以前在应用中使用到了Speex编解码,近来总结了一下Speex在android上的实现。Speex是一套主要针对语音的开源免费,无专利保护的音频压缩格式。Speex工程着力于通过提供一个可以替代高性能语音编解码来降低语音应用...
  • Speex编解码

    2019-06-13 05:34:48
    为什么80%的码农都做不了架构师?>>> ...
  • 因为当时大概只有G.711 等极少的语音编解码器可以自由使用,这种情形大大限制了VOIP在Linux以及其它自由操作系统上的发展,所以Speex Codec应运而生,可见它是针对VOIP应用而开发的,当然也可以用于一般语音
1 2 3 4 5 ... 20
收藏数 3,100
精华内容 1,240
关键字:

speex 编解码