UPMCU工作室UPMCU工作室淘宝店:工作室编写,若要转载请注明出处2012-8-12UPMCU工作室UPMCU工作室淘宝店:前言目前,网络上绝大都数关于MP3播放器的例子,大都采用vs1003这颗硬件解码芯片或者使用AT89C51SND1C这颗含有内部音频解码电路的NB单片机,软件音频解码的例子少之又少。UPMCU工作室的相关人员花了些时间、精力,研究了目前linux领域很常用的一个开源音频解码库——libmad的解码流程,并将其成功移植到裸奔的stm32平台上。本着资源共享的奉献精神,本工作室将移植过程整理成手册,发布于网络,希望对大家有用。本手册移植工作所对应的软硬件平台如下:操作系统:windowsXP开发环境:MDKV4.23STM固件库版本号:V3.5.0主芯片:STM32F103RET6(512Kflash64KRAM)运行于72MDA芯片:PCM1770PW由于本人水平有限,文中若有不对的地方,欢迎拍砖。拍砖地址447926737wangkai@163.com1.Libmad简介LIBMAD是一个高质量的音频解码库,MAD的全称是MPEGAudioDecoder。LIBMAD目前支持MPEG-1、低采样率的MPEG-2和MPEG2.5格式的LayerⅠ、LayerⅡ、LayerⅢ(即MP3)的解码。MAD具有如下特性:●高精度的24-BITPCM输出;●100%使用定点运算;●完全基于ISO/IEC标准;●支持GNUGPL协议。MAD完全采用C语言编写(里面的IMDCT部分和乘法运算可根据平台不同使用汇编来加快运算速度),它对MP3解码算法做了很多优化,非常适合在没有浮点支持的嵌入式环境下使用。利用MAD提供的API,我们可以很容易的实现音频解码。MAD的源码我们可以在下载到。UPMCU工作室UPMCU工作室淘宝店:有关于libmad的介绍和其他相关的资料,其中madlld这个项目是一个libmad低层API的demon,我的解码工作就是移植libmad和利用madlld搭好的框架解析MP3数据得到PCM格式的数据。2.Libmad简单分析下载libmad-0.15.1b解压后除了msvc++这个文件夹,其他的都是单个文件。其中所有的.C、.H、.dat文件都是我们所需要的。minimad.c这个文件是对libmad高层API的使用示例,从代码中可以看到它是基于类unix平台的。大致瞅瞅,我们可以看到使用libmad,解码真的很简单!下图大致展示了MP3解码的流程:其中同步及差错检查包括了头解码模块,在主控模块开始运行后,主控模块将比特流的数据缓冲区交给同步及差错检查模块,此模块包含两个功能,即头信息解码及帧边信息解码,根据它们的信息进行尺度因子解码及哈夫曼解码,得出的结果经过逆量化,立体声解码,混淆缩减,IMDCT,频率反转,合成多相滤波这几个模块之后,得出左右声道的PCM码流,再由主控模块将其放入输出缓冲区输出到声音播放设备。下面我们主要分析一下libmad里面用到的一些重要数据结构,这些都定义在mad.h文件中。structmad_stream{unsignedcharconst*buffer;/*inputbitstreambuffer*/unsignedcharconst*bufend;/*endofbuffer*/unsignedlongskiplen;/*bytestoskipbeforenextframe*/intsync;/*streamsyncfound*/unsignedlongfreerate;/*freebitrate(fixed)*/unsignedcharconst*this_frame;/*startofcurrentframe*/unsignedcharconst*next_frame;/*startofnextframe*/structmad_bitptrptr;/*currentprocessingbitpointer*/UPMCU工作室UPMCU工作室淘宝店:*ancillarybitspointer*/unsignedintanc_bitlen;/*numberofancillarybits*/unsignedchar(*main_data)[MAD_BUFFER_MDLEN];/*LayerIIImain_data()*/unsignedintmd_len;/*bytesinmain_data*/intoptions;/*decodingoptions(seebelow)*/enummad_errorerror;/*errorcode(seeabove)*/};此数据结构存放解码前的位流数据。structmad_synth{mad_fixed_tfilter[2][2][2][16][8];/*polyphasefilterbankoutputs*//*[ch][eo][peo][s][v]*/unsignedintphase;/*currentprocessingphase*/structmad_pcmpcm;/*PCMoutput*/};此数据结构存放解码合成滤波后的PCM数据,pcm域比较重要:structmad_pcm{unsignedintsamplerate;/*samplingfrequency(Hz)*/unsignedshortchannels;/*numberofchannels*/unsignedshortlength;/*numberofsamplesperchannel*/mad_fixed_tsamples[2][1152];/*PCMoutputsamples[ch][sample]*/};它定义了音频的采样率,声道个数和PCM采样数据,我们用这里面的信息来初始化音频设备。libmad是以桢(frame)为单位对MP3进行解码的,当正确的解码完一帧数据可以得到(每声道)1152个PCM数据。那么一帧数据量到底有多少呢?——这个值是变化的,我们可以用下面的公式来计算:FrameSize=(((MpegVersion==MPEG1?144:72)*Bitrate)/SamplingRate)+PaddingBit例如:Bitrate=128000,aSamplingRate=44100,andPaddingBit=1FrameSize=(144*128000)/44100+1=417bytes也就是说,想解码一个比特率为128K,采样率为44.1K的MP3文件,最少一次读入内存417bytes以准备解码,通常我们需要读入的字节数要比一帧的数据量多一些。structmad_frame{structmad_headerheader;/*MPEGaudioheader*/intoptions;/*decodingoptions(fromstream)*/mad_fixed_tsbsample[2][36][32];/*synthesissubbandfiltersamples*/mad_fixed_t(*overlap)[2][32][18];/*LayerIIIblockoverlapdata*/};上面数据结构记录MPEG帧解码后PCM数据的数据结构,其中mad_header这个结构体比较重要,它的内容如下:structmad_header{enummad_layerlayer;/*audiolayer(1,2,or3)*/enummad_modemode;/*channelmode(seeabove)*/intmode_extension;/*additionalmodeinfo*/enummad_emphasisemphasis;/*de-emphasistouse(seeabove)*/unsignedlongbitrate;/*streambitrate(bps)*/UPMCU工作室UPMCU工作室淘宝店:*samplingfrequency(Hz)*/unsignedshortcrc_check;/*frameCRCaccumulator*/unsignedshortcrc_target;/*finaltargetCRCchecksum*/intflags;/*flags(seebelow)*/intprivate_bits;/*privatebits(seebelow)*/mad_timer_tduration;/*audioplayingtimeofframe*/};我们可以在layer这个域中得到音频数据所采的层,在mode域中得到音频数据的声道个数,在birrate和samplerete中得到音频数据的位率(128kbps、384kbps等等)和采样率(22KHZ、44.1KHZ、48KHZ等等)Libmad解码分同步方式和异步方式两种,所谓同步方式是指解码函数在解码完一帧后才返回并带回出错信息,异步方式是指解码函数在调用后立即返回,通过消息传递解码状态信息。我们采用裸奔的方式,当然只能使用同步解码。3.Libmad移植从前面的解码框图中我们知道,使用libmad解码,我们只需要把音频文件流读入给libmad,然后把解码得到的PCM数据进行播放就可以。移植前,我们需要准备好已经能进行文件系统读写的工程模板,相信对文件系统的应用,大家都不陌生,我采用的是FatfsR0.09,从SD卡中读入MP3数据给libmad解码,最后使用PCM1770进行DA转换。如果有对文件系统移植不熟悉的童鞋,可以参考我例程中的文件系统移植模板的源码。Ok,废话少说,下面我们真正的开始移植u添加源码到工程解压源码包内libmad-0.15.1b.rar,然后将libmad-0.15.1b文件夹复制到工程目录,把里面所有.c文件都加入到MDK工程,并设置好包含路径,防止.h和.bat文件在编译时被提示找不到,加入后如下图:UPMCU工作室UPMCU工作室淘宝店:解开编译警告接下来我们先编译一下,看有哪些错误。这里说明下,为得到最彻底的优化,我的编译选项的优化等级选择了等级3,同时waring选择了allwarings,如下图:首次编译后,我们会看到3种“致命”编译警告:1、找不到unistd.h2、abort函数声明问题3、FPM没选择先说第一个,前面讲过minimad.c这个文件是对libmad的高层API在unix下的应用范例,我们直接把minimad.c这个文件从工程中删除或者注释掉里面所有内容即可解决掉第一个警告。第二个警告是断言语句声明问题,我们直接在global.h的最后那段里把其声明为空就行:#if!defined(HAVE_ASSERT_H)#ifdefined(NDEBUG)#defineassert(x)/*nothing*/#else#defineassert(x)/*nothing*///do{if(!(x))abort();}while(0)#endif#endif剩下的那个警告需要对libmad进行合理的配置。我们知道,让移植性很强的源码在某个确定的平台上运行,都需要针对这个平台进行配置(如fa