解码后的数据如下:
大家还是尽量下载附件查看。帖子里面好多格式都乱了……我再慢慢改吧。
一、前期准备移植libmad之前必须先调通SD卡硬件驱动,学会使用znFAT的基本读数据功能(参考znFAT实例之“08文件定位读取数据”)。
Libmad原来是在Linux下面使用的,我们需要做一些修改:
1. 删除minimad.c/version.c/version.h等没有用到的文件
2. 由于znfat中有一个config文件,libmad也有一个config文件,我们将libmad的config改为libmad_config.h,避免冲突。然后将libmad中#include config.h 全部改为 #include libmad_config.h
由于我们没有abort这个函数,所以我们需要在global.h中将do { if (!(x)) abort(); } while (0) 这一句注释掉(如上图)
1. 最后我们在config文件中添加一行#define FPM_DEFAULT,防止编译时出现“FPM未选择”的错误。
对于libmad文件的修改比较简单,也并非移植的重点,大家可以直接使用我们修改过的libmad文件。经过修改后的libmad文件如图:
一/MP3格式探索
关于MP3格式的资料很多,但很少有拿具体文件来分析的。我们这里结合手上的资料,使用具体文件来分析分析。这里只进行简单介绍,MP3内容主要分为三个部分:
1) ID3V2为了方便研究MP3格式,我将一个SD卡格式化,并放入一个MP3文件,然后通过WinHex进行查看:
最开头十个字节为ID3标签头,其结构如下:(来自pdf文档《mp3文件格式》)
我们的Size[4]按上面规则转换后为 1111110110=1014(10) 。而后面的数据从0x00408400开始,中间经过0x400=1024(10),说明这个标签部分应该是有1024个字节。所以说:那个计数应该没有包含标签头这十个字节。为了验证我们的猜想,再拿一个文件来试试看。
计算得到246.但是数据一部分为0之后又从0x00408100开始的。距开头0x00408000有256个字节。验证了我们的猜想。
关于ID3V2标签我们就介绍到这里。如需更深入了解,可自己看看《mp3文件格式》
1) 一系列的帧
中间的核心数据就是一个一个帧。每个MP3帧(frame)都有一个帧头(frameheader),长度为4Byte(后文用B代表字节Byte,b代表位bit),帧头后面可能有两个字节的CRC校验位(这两个字节是否存在决定于frameheader第16b。0-无CRC校验位,1-有crc校验位),接着就是frame实体数据了。帧的格式如下:
A-frame sync B-MPEG Audio version C-Layer description D-Protection bit
E-Bitrateindex F-Sampling rate frequency index(Hz) G-padding bit
H-Private bit I-ChannelMode
1111 1111 111110111001 00000000 0000
我们读到的文件帧头为: F F F B 9 0 0 0
Syncword 11111111111 全为1
ID 11 MPEG1
Layer description 01 Layer III (这就是MP3中那个3)
Protection bit 1 没有CRC校验位
Bitrate 1001 128kpbs(这就是我们常说的比特率)
Sampling rate 00 44100 (采样频率)
Pading bit 0 frame没有padding
Channel Mode 00 立体声
解析这些数据我们可以看到一些最基本的信息。我们这个帧是没有CRC校验的,所以接着的数据就直接是所谓的MAIN_DATA。这个MAIN_DATA到底有多大?我们可以通过上面的参数计算得到。
1 无论帧的长度为多长,每帧的播放时约为26ms
2 每帧数据大小:
FrameSize = (((MPEGVersion==MPEG1?144:72)*Bitrate)/SamplingRate)+PaddingBit
如果MPEGVersion是MPEG1就用Bitrate乘以144,否则乘以72.
我们的版本是MPEG1所以我们计算如下:
((128000*144)/44100)+0=418 Byte
a) main_data这个部分的数据比较复杂,经过了一些压缩,我们已经不能看到具体的意义了。只能基本看到数据被“FF FB 92 00”分为一堆一堆的,每堆被“LAME3.90(alpha)”分为两块,LAME3.90后面的数据是一堆标记信息。具体内容已经被加密得面目全非,不必深究。下面来看看libmad部分。
一、初探libmadLibmad解码mp3的具体过程中有些地方涉及到的知识还是比较深涩的。有兴趣的可以自己研究研究,我们来介绍一些简单的概念。
Libmad主要结构体:
mad_stream 存放解码前的Bitstream数据
mad_synth 存放解码合成滤波后的PCM数据
mad_pcm 定义了音频的采样率,声道个数和PCM采样数据,用来初始化音频MPEG
mad_frame 记录MPEG帧解码后PCM数据的数据结构,其中的mad_header用来记录帧的基本信息,比如MPEG层数、声道模式、流比特率、采样比特率。声道模式包括单声道、双声道、联合立体混音道以及一般立体声。
我们可以再stream.h中找到mad_stream的定义:(在基于zn-X开发板的例子中,已经加上比较详细的注释)
这里就不详细列出,大家可以到工程中查看。libmad解码的流程是:
1. 我们从SD卡中读出文件存入InputBuffer中,
2. 从InputBuffer相关信息送给stream。
3. 经过解码,得到PCM数据放入mad_frame的相应位置。
4. 经过子带合成滤波处理,放入mad_synth相应位置。
5. 从Synth.pcm.samples[0]中读出数据,进行处理。
一、利用LIBMAD实现功能
为了方便验证解码是否正确,我们决定将解码得到的信息存入一个文件中,然后利用cooledit进行查看。
1. 读入数据
要实现这个功能我们必须要从SD卡中读取出相应的数据。这里我们采用南哥的znFAT
在madlld.c这个文件中有这样一个标志
这就是解码的第一步,读入数据:
起始,read_num和ReadStart都为0。至于remaining的值,就是解码之后剩下不足一帧的数据的长度。我们将其放入InputBuffer(输入缓冲区)头,然后再从SD卡中读取数据填满InputBuffer进行解码。
这之后,我们将InputBuffer移交给Stream结构体。读入数据的第一步就完成啦!
2. 帧解码
实现解码的是mad_frame_decode这一句,如果返回值非零,表示出现错误,对错误进行一些处理。
3. 合成滤波
这个部分的代码就这么一句。
4. 数据存储
每一帧解码出来的PCM数据有左右声道各1152个。
二、数据查看
程序运行完成后,SD卡中会多出一个pcm.txt文档。我们打开CoolEdit
找到文件打开
设置格式
中间显示波形。
播放
就可以听到解码出来的歌曲啦!