相比头部信息的解码,MP3帧主数据(main data)的结构就不那么直观了。前面我们介绍过Mad中对应于MP3帧的结构struct mad_frame:
//file:libmad-1.05b/frame.h;line:67~74 struct mad_frame { struct mad_header header; /* MPEG audio header */ int options; /* decoding options (from stream) */ mad_fixed_t sbsample[2][36][32]; /* synthesis subband filter samples */ mad_fixed_t (*overlap)[2][32][18]; /* Layer III block overlap data */ };
其中包括该帧对应的帧头,该帧头由上一篇介绍的mad_header_decode()函数从MP3数据中解析并填充。
除了帧头,struct mad_frame当然要包含更重要的MP3帧采样信息。关于采样的知识各位看官可以参考信号与系统教材或者DSP相关的教材,本文后续章节也会对此进行简要介绍。
采样信息包括MP-1、MP-2和MP-3中都要使用的子带过滤器采样数据(subband filter sample)和只有MP-3才拥有的MDCT频谱因子数据(overlap data)。MP3标准中把采样分成32个等宽的频带,每个频带大约有625Hz的带宽,称作子频带(subband)。而这32个子带中的采样又分成3组,每组12个采样。MPEG Audio Layer 1只使用第一组,而Layer2和Layer3要使用全部的3组,共3*12=36个采样。等等,有点乱,咱再缕缕:
MP3将人耳能感知的20Hz~20KHz的总频带划分到32个子频带中(事实上数字音频的总频带不只是20KHz,而更常见的是44.1KHz甚至48HKz,至于为什么是这个数字请见后续章节)每个子带拥有3*12=36个采样,一个单声道MP3帧总共包含32*3*12=1152个采样!而双声道或立体声MP3帧包含的采样数就是1152的2倍。
从mad_frame结构中我们可以很容易地看出这样的事实:
sbsample 包含2个声道,每个子带36个采样,32个子带
在MPEG Audio Layer 3编码中由于种种原因,在这32各子带被组合之后往往与压缩前的原信号有较大差别。为了矫正这些差别,MP3引入了MDCT(Modified Discrete Cosine Transform,改进离散余弦转换)。具体的算法本文不做介绍,但是MP3标准中规定了两种频谱因子序列——一种是6个因子的短序列,另外一种是18个因子的长序列。长序列的音质较好但占空间较大。
从mad_frame结构中我们可以很容易地看出上述事实:
overlap 包含2个声道,32个子带,每个子带18个频谱因子
Mad函数mad_frame_decode()负责填充上述的struct mad_frame结构。前面我介绍header回调函数时曾提起过如果用户初始化该函数指针为0或NULL,mad_header_decode就推迟到主数据解码前执行,反之,mad_header_decode会被首先调用,解析并填充一个mad_header结构,把这个结构作为参数传递给用户定义的header回调函数,又用户根据header信息进行一些额外的处理工作。注意header回调函数中只能使用一个const修饰的mad_header,即不能对MP3头部信息做任何更改,因MP3头部信息为跟主数据一样重要,最终还是要被填充到帧数据中:
//file:libmad-1.05b/frame.c;line:438~455 int mad_frame_decode(struct mad_frame *frame, struct mad_stream *stream) { frame->options = stream->options; /* header() */ /* error_check() */ if (!(frame->header.flags & MAD_FLAG_INCOMPLETE) && mad_header_decode(&frame->header, stream) == -1) goto fail; /* audio_data() */ frame->header.flags &= ~MAD_FLAG_INCOMPLETE; if (decoder_table[frame->header.layer - 1](stream, frame) == -1) { if (!MAD_RECOVERABLE(stream->error)) stream->next_frame = stream->this_frame;
mad_frame_decode从stream中定位frame,首先解码header数据,再根据header中的Layer信息选择对应于Layer1、2或3的解码函数进行真数据的解码并填充mad_frame结构。
帧解码过程中可能产生错误,比如CRC校验失败等,全部已定义的错误列表在mad.h和stream.h中:
//file:libmad-1.05b/stream.h;line:30~57 enum mad_error { MAD_ERROR_NONE = 0x0000, /* no error */ MAD_ERROR_BUFLEN = 0x0001, /* input buffer too small (or EOF) */ MAD_ERROR_BUFPTR = 0x0002, /* invalid (null) buffer pointer */ MAD_ERROR_NOMEM = 0x0031, /* not enough memory */ MAD_ERROR_LOSTSYNC = 0x0101, /* lost synchronization */ ...
解码过程中产生的最后一个错误会保存在struct mad_stream结构中的error枚举变量中。通过检查error的枚举值检测解码过程是否有错误发生。Mad提供了一个error回调函数供我们重写以处理错误,同时也提供了一个默认的错误处理函数error_default:
//file:libmad-1.05b/decoder.c;line:329~336 if (decoder->error_func) { error_func = decoder->error_func; error_data = decoder->cb_data; } else { error_func = error_default; error_data = &bad_last_frame; } 如果用户没有提供自己的error回调函数,那么madlib就使用自己的默认错误处理函数error_default: //file:libmad-1.05b/decoder.c;line:296~313 enum mad_flow error_default(void *data, struct mad_stream *stream, struct mad_frame *frame) { int *bad_last_frame = data; switch (stream->error) { case MAD_ERROR_BADCRC: if (*bad_last_frame) mad_frame_mute(frame); else *bad_last_frame = 1; return MAD_FLOW_IGNORE; default: return MAD_FLOW_CONTINUE; } }
error_default为我们提供了一个良好的错误处理回调函数范例,只是在编写自己的error_default版本时注意参数data的实际指向类型。
Error_default函数的参数想函数内部提供了包含了枚举变量error的stream结构指针,同时还包括可能发生错误的frame结构指针。
Mad的错误类型分为两类:可恢复错误(recoverable error)和不可恢复错误(unrecoverable error),区别就在于错误枚举值的高16位:
//file:stream.h;line:59
# define MAD_RECOVERABLE(error) ((error) & 0xff00)
当decode过程中出现一个可回复错误,那经过重新同步->丢弃该->重新同步够帧解码仍会继续;当decode过程中出现一个不可恢复错误,解码器直接退出。
如果error枚举值的高16为不位0x00xx,那么MAD_RECOVERABLE宏不为0x0000,则该错误可恢复。从error枚举值的定义可见,前4种错误为不可恢复错误,包括NO_ERROR!可以说这是个小小的不足,我们在编写自己的错误处理回调函数时一定要注意这个问题,先判断是否有错误再用MAD_RECOVERABLE()宏判断是否为可恢复错误:
if(stream->error) if(!MAD_RECOVERABLE(stream->error))
到目前位置,解码过程比较清晰,各回调函数和解码函数的调用顺序为
Input回调函数-> ->mad_header_decode头解码->header回调函数(如果存在)->mad_frame_decode-> error回调函数(包括默认的error_default)。