Layer-3音频文件。MPEG(MovingPicture Experts Group)在汉语中译为活动图像专家组,特指活动影音压缩标准,MPEG音频文件是MPEG1标准中的声音部分。也叫MPEG音频层,它依据压缩质量和编码复杂程度划分为三层,即Layer-1、Layer2、Layer3。且分别相应MP1、MP2、MP3这三种声音文件,并依据不同的用途,使用不同层次的编码。MPEG音频编码的层次越高,编码器越复杂,压缩率也越高。MP1和MP2的压缩率分别为4:1和6:1-8:1,而MP3的压缩率则高达10:1-12:1。
MP3文件大体分为三部分:TAG_V2(ID3V2),音频数据。TAG_V1(ID3V1),当中ID3V2是ID3V1的补充,并非全部的MP3都有ID3V2补充。即是不是全部的MP3文件都有ID3V2。
ID3V2
假设MP3文件存在ID3V2。则一定在文件的头部,ID3V2结构分为头部(header)和若干标签帧。当中头部长度为10字节。10个字节的结构如表1:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
内容为”ID3” | 版本 | 副版本 | 存放标志的字节 | ID3V2总大小(帧头和之后的若干标签帧总和) |
表1
由于3、4、5字节所代表的意义并非MP3解码的重点,故此仅仅讲解前三字节和后四字节:
从表1可看出推断MP3文件是否存在ID3V2。仅仅须要推断文件前三个字节是否是”ID3”。
ID3V2数据大小计算公司:
total_size = (Size[0]&0x7F)*0x200000+ (Size[1]&0x7F)*0x400 + (Size[2]&0x7F)*0x80 +(Size[3]&0x7F) |
当中。size[0~3]。各自是表1中的6~9字节。
须要注意的是。这个公司计算的长度并不包含ID3V2的10个字节的头部。
ID3V2头部之后的若干标签帧每一帧结构分为标签ID(4字节)、帧内容大小(4字节。不包含标签帧帧头)、存放标志位(2字节)、内容。
当中标签ID的含义例如以下:
TEXT: 歌词作者 TENC: TCOM: 作曲家 TDAT: TPE4: 翻译(记录员、改动员) TYER: TALB: 专辑相当于ID3v1的Album TIT1: TIT2: 标题相当于ID3v1的Title TIT3: TCON: 流派(风格)相当于ID3v1的Genre AENC: TBPM: 每分钟节拍数COMM: TDLY: 播放列表返录 TFLT: 文件类型 TKEY: 最初keyword TLEN: 长度 TOAL: 原唱片集 TOLY: 原歌词作者 TOWM: 文件全部者(许可证者) TPOS: TPUB: 发行人 TRSN: Intenet电台名称 TRSO: TSRC: ISRC(国际的标准记录代码) TSSE: |
读取MP3文件ID3V2信息的函数可例如以下:
//定义头部和标签帧 typedefstruct ID3v2Header{ char Identify[3]; // ID3v2固定标志:ID3 。字义为abc00000 char Size[4]; // }ID3v2Header; typedef个字节 { char FrameID[4]; // char Size[4]; // char Flag[2]; // }ID3v2Frame; //输出信息并返回ID3V2大小 int ReadID3v2(FILE *pf) { ID3v2Headermp3header; ID3v2Frame mp3Frame; int FSize = 0; char str[4096] = {0}; char str2[5] = {0}; int ID3size; inthead_size = 0; inti; if(!pf) return -1; fseek(pf,0,SEEK_SET); fread(&mp3header,sizeof(mp3header),1,pf); if (mp3header.Identify[0]!='I' || mp3header.Identify[1]!='D' printf("此歌曲不支持ID3v2标准!\n"); //文件复位 rewind(pf); return -2; } printf("ID3v2标志:%.3s\n",mp3header.Identify); printf("ID3v2版本号:%d\n", mp3header.Ver); ID3size= (mp3header.Size[0]& 0x7F)<< 21|(mp3header.Size[1]& 0x7F)<< 14|(mp3header.Size[2] & 0x7F) << 7|(mp3header.Size[3] & 0x7F); printf("标签大小:%d\n***********\n",ID3size); for (i=0;i<ID3size;i=i+11+FSize){ memset(&mp3Frame,0,sizeof(mp3Frame)); memset(&str,0,sizeof(str)); fseek(pf,10+i,SEEK_SET); //移动到标签帧头 fread(&mp3Frame,sizeof(mp3Frame),1,pf); //原则上是不用-1的,可是实际发现,总有一个字节的差距,为了计算方便-1。所以出现-1时标明此区块无内容 FSize = (int)(mp3Frame.Size[0]*0x100000000 + mp3Frame.Size[1]*0x10000+ mp3Frame.Size[2]*0x100 + mp3Frame.Size[3]-1); if (FSize>0) { fseek(pf,10+11+i,SEEK_SET);//移动到内容区 fread(str,FSize,1,pf); GetStr(mp3Frame.FrameID,str2); printf("%s-%s:\t%s\n",str2,mp3Frame.FrameID,str); head_size+=11; }else{ return ID3size+10; } } return ID3size+10; } //通过FrameID获取相应的中文名 void GetStr(char* oldstr,char* { if (0==memcmp((LPCTSTR)"TIT2",oldstr,4)) { memcpy(str,"标题",4); }elseif(0==memcmp((LPCTSTR)"TPE1",oldstr,4)){ memcpy(str,"作者",4); }elseif(0==memcmp((LPCTSTR)"TALB",oldstr,4)){ memcpy(str,"专辑",4); }elseif(0==memcmp((LPCTSTR)"TRCK",oldstr,4)){ memcpy(str,"音轨",4); }elseif(0==memcmp((LPCTSTR)"TYER",oldstr,4)){ memcpy(str,"年代",4); }elseif(0==memcmp((LPCTSTR)"COMM",oldstr,4)){ memcpy(str,"备注",4); }elseif(0==memcmp((LPCTSTR)"TCON",oldstr,4)){ memcpy(str,"类型",4); }else{ memcpy(str,"未知",4); //其它的不是非常重要,所以省略了 } } |
MP3文件数据结构及处理流程
MP3数据解码流程借用图1描写叙述。
图1
MP3文件的音频数据部分。是分为非常多数据帧存放。每一帧数据播放的时间长度计算公式:
每帧持续时间(毫秒) 如果每帧採样数为1152,採样频率为44.1K,则每帧数据播放的时间约为26ms。 |
没帧数据的结构包含帧头(header)、帧边信息(side)、主数据(main
data)。
帧头(header)
数据帧帧头长度为4字节,结构如图2所看到的。
图2
由图可知。同步信息(synchronizationword)11位皆为1,其它位信息如表
版本号(ID) | 2bit | 00-MPEG 2.5 01-没有定义 10-MPEG | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
层(layer) | 2bit | 00-没有定义 01-Layer | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
CRC校验 | 1bit | 0-校验 1-不校验 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
位率索引 | 4bit |
V1 - MPEG 1 V2 - MPEG 2 and MPEG 2.5 L1 - Layer 1 L2 - Layer 2 L3 - Layer 3 "free"表示位率可变 "bad" 表示不同意值 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
採样频率 | 2bit | MPEG-1: 00-44.1kHz 01-48kHz 10-32kHz 11-没有定义 MPEG-2: 00-22.05kHz 01-24kHz 10-16kHz 11-没有定义 MPEG-2.5: 00-11.025kHz 01-12kHz 10-8kHz 11-没有定义 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
是否填充 | 1bit | 0-无需调整,1-调整 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
保留(reserved) | 1bit | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
声道模式 | 2bit | 00-立体声Stereo 01-Joint | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
保留(reserved) | 2bit | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
版权标志 | 1bit | 0-不合法 1-合法 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
原版标志 | 1bit | 0-非原版 1-原版 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
强调方式 | 2bit | 00-没有定义 01-50/15ms 10-保留 11-CCITT |
表2
数据帧大小计算公式:
Size=((採样个数 * 对于Mp3格式: Size=((1152 * (1 /採样率))*帧的比特率)/8 +帧的填充大小= 当中:帧的填充大小便是23bit,不是0则为1。 |
帧边信息(side)
帧边信息解码的主要目的在于找出解这帧的各个參数。包含主数据開始位置,尺度因子长度等。
帧边信息如图3所看到的。
图3
当中,main_data_begin(主数据開始)是一个偏移值。指出主数据是在同步字之前多少个字节開始。
须要注意的是。1.帧头不一定是一帧的開始,帧头CRC校验字和帧边信息在帧数据中是滑动的。
2.这个数值忽略帧头和帧边信息的存在,假设main_data_begin
= 0, 则主数据从帧边信息的下一个字节開始,示意图如图4.
图4
块类型(block_type)分为三种类型:
block_type = 0长块
block_type = 1開始块
block_type = 3结束块
block_type = 2短块
在编码过程中进行IMDCT变换时。针对不同信号为同一时候得到较好的时域和频域分辨率定义了两种不同的块长:长块的块长为18个样本,短块的块长为6个样本。这使得长块对于平稳的声音信号能够得到更高的频率分辨率,而短块对跳变信号能够得到更高的时域分辨率。由于在短块模式下。3个短块取代1个长块,而短块的大小恰好是一个长块的1/3。所以IMDCT的样本数不受块长的影响。
对于给定的一帧声音信号,IMDCT能够所有使用长块或所有使用短块,也能够长短块混合使用。
由于低频区的频域分辨率对音质有重大影响,所以在混合块模式下,IMDCT对最低频的2个子带使用长块,而对其余的30个子带使用短块。
这样,既能保证低频区的频域分辨率。又不会牺牲高频区的时域分辨率。
长块和短块之间的切换有一个过程。一般用一个带特殊长转短(即,起始块block_type
= 1)或短转长(即终止块,block_type = 3)数据窗体的长块来完毕这个长短块之间的切换。
因此长块也就是包括正常窗,起始块和终止块数据窗体的数据块;短块也包括18个数据,可是是由6个数据独立加窗后在经过连接计算得到的。
主数据(main_data)
main_data中有两粒度组。没个粒度组分为两个声道,取数据存储结构如图5。
图5
当中,每一个通道(chN_data)的结构图6.
图6