Speex手册(六)——语音处理API 2 与格式和标准
6.3 抖动缓冲器
启用抖动缓冲器,需要包含头文件:
#include <speex/speex_jitter.h>
然后初始化一个新的抖动缓冲器:
JitterBuffer *state = jitter_buffer_init(step);
其中step参数是默认时间步长(单位为时间戳的单位),用来调整延时和做隐蔽,合适的值为1,有时更大的值更好。例如,如果你能在20ms帧上做隐蔽,则抖动缓冲器中的点无需一个一个做。另一个例子是针对视频,对少于一帧的延时进行调整没有意义,后面可以随时改变这个值。
抖动缓冲器API基于JitterBufferPacket类型,定义如下:
typedef struct {
char *data; /*数据包中的字节数据*/
spx_uint32_t len; /*数据包长度,单位字节*/
spx_unit32_t timestamp; /*数据包时间戳*/
spx_uint32_t span; /*数据包覆盖的时间(时间戳为单位)*/
} JitterBufferPacket;
例如,对于音频,timestamp就是RTP时间戳域,span是数据包中编码的样本数。对于Speex窄带,若数据包仅包含一帧,则span为160。
当一个包到达时,将被插入到抖动缓冲器中:
JitterBufferPacket packet;
/*填充数据包结构中每一项*/
jitter_buffer_put(state, &packet);
当解码器准备解码一个包时,被解码的包可这样获取:
int start_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是要求的质量设置(0到10)。质量参数能控制质量、复杂度和等待时间之间的折中。高的质量参数意味着更少的噪声和混叠,但有更高的复杂度和更长的等待时间。通常质量参数为3对大多数桌面应用都可接受,质量参数为10在专业音响中最推荐。质量参数为0通常有不错的声音(当然比线性插值重采样好),但能听出人为迹象。
实际重采样通过如下调用执行:
err = speex_resampler_process_int(resampler, channelID, in, &in_length, out, &out_length);
其中channelID是被处理的信道ID,对于单声道流,为0。in指针指向被选信道输入buffer的第一个样本;out指针指向输出的第一个样本。输入输出buffer的大小分别由in_length和out_length指定。完成后,这些值被替代为重采样器读取和写入的样本数。除非发生错误,否则所有输入样本将被读取或/和所有输出样本将被写入。对于浮点型样本,函数speex_resampler_process_float()有相似功能。也可以一次处理多个通道。
未完待续...
6.5 环形缓冲区
待补充...
7 格式与标准
Speex既能窄带编码语音也能宽带编码语音,并且支持不同比特率。然而,某一个应用或设备无需支持所有特征。为了满足"Speex兼容“,一个应用最少需满足一些基本特征。
最起码,所有的窄带模型操作必须被解码器支持,这包括通过窄带解码器解码宽带比特流(宽带比特流包含一个能单独接解码的嵌入的窄带比特流)。如果存在,宽带解码器必须能解码窄带比特流,也许能解码所有宽带模式或能解码所有模式的嵌入的窄带部分(包含忽略高代比特)。
对于编码器,最少一个窄带或宽带模式必须被支持。所有的编码模式不是必须支持的主要原因是一些平台可能能处理某些模式下的复杂编码。
7.1 RTP有效载荷格式
RTP有效载荷草案见附录C,最新版本可在网站http://www.speex.org/drafts/latest上获得。草案已于2003年2月26日发送到英特网工程任务组(Internet Engineering Task Force,IETF)并将于3月18日在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]获取更多信息。开始流标识(beginning ofstream,b_o_s)在头文件中置为1,包头有packetno=0和granulepos=0。
第二个包包含Speex注释头。使用的Vorbis注释格式在网站中描述:http://www.xiph.org/ogg/vorbis/doc/v-comment.html。这个包有packetno=1和granulepos=0。
第三个之后的包每个都包含一个或多个(头文件中写的数量)Speex帧。这些包的packetno由2开始,granulepos为包中编码的最后样本数。这些包的最后结束流(end of stream,e_o_s)标识置为1。
表7.1 Ogg/Speex头包