MP3格式写数据到MP3数据帧.docx

上传人:b****3 文档编号:3271237 上传时间:2022-11-21 格式:DOCX 页数:14 大小:77.36KB
下载 相关 举报
MP3格式写数据到MP3数据帧.docx_第1页
第1页 / 共14页
MP3格式写数据到MP3数据帧.docx_第2页
第2页 / 共14页
MP3格式写数据到MP3数据帧.docx_第3页
第3页 / 共14页
MP3格式写数据到MP3数据帧.docx_第4页
第4页 / 共14页
MP3格式写数据到MP3数据帧.docx_第5页
第5页 / 共14页
点击查看更多>>
下载资源
资源描述

MP3格式写数据到MP3数据帧.docx

《MP3格式写数据到MP3数据帧.docx》由会员分享,可在线阅读,更多相关《MP3格式写数据到MP3数据帧.docx(14页珍藏版)》请在冰豆网上搜索。

MP3格式写数据到MP3数据帧.docx

MP3格式写数据到MP3数据帧

MP3格式音频文件结构解析

一、概述

Layer-3音频文件,MPEG(MovingPictureExpertsGroup)在汉语中译为活动图

像专家组,特指活动影音压缩标准,MPEGT频文件是MPEG标准中的声音部分,也叫MPEGt频层,它根据压缩质量和编码复杂程度划分为三层,即Layer-1、Layer2、

Layer3,且分别对应MP1MP2MP3这三种声音文件,并根据不同的用途,使用不同层次的编码。

MPEG!

频编码的层次越高,编码器越复杂,压缩率也越高,MP1和MP2的压缩率分别为4:

1和6:

1-8:

1,而MP3的压缩率则高达10:

1-12:

1,也就是说,一分钟CD音质的音乐,未经压缩需要10MB的存储空间,而经过MP3压缩编码后只有

1MB左右。

不过MP3对音频信号采用的是有损压缩方式,为了降低声音失真度,MP3

采取了“感官编码技术”,即编码时先对音频文件进行频谱分析,然后用过滤器滤掉噪音电平,接着通过量化的方式将剩下的每一位打散排列,最后形成具有较高压缩比的MP3文件,并使压缩后的文件在回放时能够达到比较接近原音源的声音效果。

二、整个MP3文件结构:

MP3文件大体分为三部分:

TAG_V2(ID3V2),音频数据,TAG_V1(ID3V1)

a).ID3V2在文件开始的位置,包含了作者,作曲,专辑等信息,长度不固定,扩展了ID3V1的信息量。

b).一系列的音频数据的帧,在文件的中间位置,个数由文件大小和帧长决定;

每个帧的长度可能不固定,也可能固定,由位率bitrate决定

每个帧又分为帧头和数据实体两部分

帧头记录了mp3的位率,采样率,版本等信息,每个帧之间相互独立。

c).ID3V1在文件结尾的位置,包含了作者,作曲,专辑等信息,长度为128Byte。

ID3V2

包含了作者,作曲,专辑等信息,长度不固定,扩展了ID3V1的信息量。

Frame

Frame

一系列的帧,个数由文件大小和帧长决定

每个FRAME勺长度可能不固定,也可能固定,由位率bitrate决定每个FRAMED分为帧头和数据实体两部分

帧头记录了mp3的位率,米样率,版本等信息,每个帧之间相互独立。

ID3V1

包含了作者,作曲,专辑等信息,长度为128BYTE

表格2.1

1、ID3V2

ID3V2到现在一共有4个版本,但流行的播放软件一般只支持第3版,既ID3V2.3。

由于ID3V1记录在MP3文件的末尾,ID3V2就只好记录在MP3文件的首部了(如果有一天发布ID3V3,真不知道该记录在哪里)。

也正是由于这个原因,对ID3V2的操作比ID3V1要慢。

而且ID3V2结构比ID3V1的结构要复杂得多,但比前者全面且可以伸缩和扩展。

下面就介绍一下ID3V2.3:

每个ID3V2.3的标签都一个标签头和若干个标签帧或一个扩展标签头组成。

关于曲

目的信息如标题、作者等都存放在不同的标签帧中,扩展标签头和标签帧并不是必

要的,但每个标签至少要有一个标签帧。

标签头和标签帧一起顺序存放在MP3文件

的首部。

1、标签头

在文件的首部顺序记录10个字节的ID3V2.3的头部。

数据结构如下:

charHeader[3];/*

必须为"ID3"否则认为标签不存在*/

charVer;/*

版本号ID3V2.3就记录3*/

charRevision;/*

副版本号此版本记录为0*/

charFlag;/*

存放标志的字节,这个版本只定义了三位,稍后详细解说*/

charSize[4];/*

标签大小,包括标签头的10个字节和所有的标签帧的大小*/

第5个字节:

副版本号,为0

1)标志字节

标志字节一般为0,定义如下:

abc00000

a--表示是否使用Unsynchronisation(这个单词不知道是什么意思,字典里也没有找到,一般不设置)

b--表示是否有扩展头部,一般没有(至少Winamp没有记录),所以一般也不设置

c--表示是否为测试标签(99.99%的标签都不是测试用的啦,所以一般也不设置)

第6个字节:

存放标志的字节,只定义了三位,这里值为0

2)标签大小

一共四个字节,但每个字节只用7位,最高位不使用恒为0。

所以格式如下

0XXXXXXX0XXXXXXX0XXXXXXX0XXXXXXX

计算大小时要将0去掉,得到一个28位的二进制数,就是标签大小(不懂为什么要这样做),计算公式如下:

total_size=(Size[0]&0x7F)*0x200000+(Size[1]&0x7F)*0x4000+

(Size[2]&0x7F)*0x80+(Size[3]&0x7F)

注意:

这里的帧大小,并不包含帧头的10个字节,只表示帧内容的大小•这里0X4000,很多写的是0x400是错的•

2、标签帧

每个标签帧都有一个10个字节的帧头和至少一个字节的不固定长度的内容组成。

们也是顺序存放在文件中,和标签头和其他的标签帧也没有特殊的字符分隔。

得到一个完整的帧的内容只有从帧头中的到内容大小后才能读出,读取时要注意大小,不要将其他帧的内容或帧头读入。

帧头的定义如下:

*/

charID[4];/*用四个字符标识一个帧,说明其内容,稍后有常用的标识对照表

charSize[4];/*帧内容的大小,不包括帧头,不得小于1*/

charFlags[2];/*存放标志,只定义了6位*/

1)帧标识

用四个字符标识一个帧,说明一个帧的内容含义,常用的对照如下:

TIT2二标题表示内容为这首歌的标题,下同

TPE1作者

TALB专集

TRCK音轨格式:

N/M其中N为专集中的第N首,M为专集中共M首,N和M为ASCII码表示的数字

TYER年代是用ASCII码表示的数字

TCON类型直接用字符串表示

COMM备注格式:

"eng\0备注内容",其中eng表示备注所使用的自然语言

2)大小

这个可没有标签头的算法那么麻烦,每个字节的8位全用,格式如下

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

算法如下:

intFSize=Size[0]*0x100000000+Size[1]*0x10000+Size[2]*0x100+Size[3];

注意:

这里的帧大小,并不包含帧头的10个字节,只表示帧内容的大小

3)标志

只定义了6位,另外的10位为0,但大部分的情况下16位都为0就可以了。

式如下:

abc00000ijk00000

a--标签保护标志,设置时认为此帧作废

b--文件保护标志,设置时认为此帧作废

c--只读标志,设置时认为此帧不能修改(但我没有找到一个软件理会这个标志)

i--压缩标志,设置时一个字节存放两个BCD码表示数字

j--加密标志(没有见过哪个MP3文件的标签用了加密)

k--组标志,设置时说明此帧和其他的某帧是一组

值得一提的是winamp在保存和读取帧内容的时候会在内容前面加个''\0'',并把

这个字节计算在帧内容的大小中。

2、音频数据帧

每个帧都有一个帧头Header,长度是4Byte(32bit),帧头后面可能有两个字节的

CRC校验值,这两个字节的是否存在决定于Header信息的第16bit,为0则帧头后面无校验,为1则有校验,校验值长度为2个字节,紧跟在Header后面,接着就是帧的实体数据了,格式如下:

1、帧头格式

帧头长4字节,对于固定位率的MP3文件,所有帧的帧头格式一样其数据结构如下

unsignedintoriginal:

1;//原始媒体

unsignedintcopyright";//版权标志

//声道模式

unsignedintchannel_mode:

2;

}FHEADER,*LPHEADER;

1)计算帧长度

我们首先区分两个术语:

帧大小和帧长度。

帧大小即每帧采样数表示一帧中采样的

个数,这是恒定值。

其值如下表所示

帧长度是压缩时每一帧的长度,包括帧头。

它将填充的空位也计算在内。

Layerl的

一个空位长4字节,Layerll和LayerIII的空位是1字节。

当读取MPEGt件时必须计算该值以便找到相邻的帧。

注意:

因为有填充和比特率变换,帧长度可能变化。

从头中读取比特率,采样频率和填充的值后可以进行计算,

Lyaerl使用公式:

帧长度(字节)二((每帧采样数/8*比特率)/采样频率)+填充*4

LyerII禾口LyaerIII使用公式:

帧长度(字节)二((每帧采样数/8*比特率)/采样频率)+填充

例:

LayerIII比特率128000,采样频率44100,填充0二〉帧大小417字节;

如图2.3中,比特率为128K采样率为44.1K,填充0,则其帧长度为:

(1152/8*128K)/44.1K=417(字节)

2)每帧的持续时间

每帧的持续时间可以通过计算获得,下面给出计算公式

每帧持续时间(毫秒)=每帧采样数/采样频率*1000

如图2.3中,其每帧时间为:

1152/44.1K*1000=26.12(约等于26ms)

如果是MPEG2LayerIII采样率为16KHz的话那一帧要持续36毫秒,这个相差还是蛮大的,所以还是应该通过计算来获的。

3)CRC校验

如果帧头的校验位为0,则帧头后就有一个16位的CRC直,这个值是big-endian的值,把这个值和该帧通过计算得出的CRC直进行比较就可以得知该帧是否有效。

4)帧数据

在帧头后边是Sidelnfo(姑且称之为通道信息)。

对标准的立体声MP3文件来说其长度为32字节。

通道信息后面是Scalefactor(增益因子)信息。

当解码器在读到上述信息后,就可以进行解码了。

图2.3中地址为0x880到0x89F(含),此处数据

全为0。

对于mp3来说现在有两种编码方式,一种是CBR也就是固定位率,固定位率的帧的大小在整个文件中都是是固定的(公式如上所述),只要知道文件总长度,和从第一帧帧头读出的信息,就都可以通过计算得出这个mp3文件的信息,比如总的帧

数,总的播放时间等等,要定位到某一帧或某个时间点也很方便,这种编码方式不需要文件头,第一帧开始就是音频数据。

另一种是VBR就是可变位率,VBR是XING公司推出的算法,所以在MP3的FRAME!

会有“Xing"这个关键字(也有用"Info"来标识的,现在很多流行的小软件也可以进行VBF压缩,它们是否遵守这个约定,

那就不得而知了),它存放在MP3文件中的第一个有效帧的数据区里,它标识了这个MP3文件是VBR的。

同时第一个帧里存放了MP3文件的帧的总个数,这就很容易获得了播放总时间,同时还有100个字节存放了播放总时间的100个时间分段的帧索引,假设4分钟的MP3歌曲,240S,分成100段,每两个相邻INDEX的时间差就是2.4S,所以通过这个INDEX只要前后处理少数的FRAME就能快速找出我们需

文件时也像VBF那样将信息记入第一帧,比如著名的lame,它使用"Info"来做CBR

的标记。

5)VBR头

这里列出VBR的第一帧存储文件信息的头的格式。

有两种格式,一种是常见的

XINGHeader(头部包含字符’Xing'),另一种是VBRIHeader(头部包含字符

'VBRI')鉴于VBRIHeader不常见,下面只说XINGHeader:

XINGHeader的起始位置,相对于第一帧帧头的位置,单位是字节

36-39"Xing"文件为MPEG并且不是单声道(大多数VBR的mp3文件都是如此)

21-24"Xing"文件为MPEG并且是单声道

21-24"Xing"文件为MPEG并且不是单声道

13-16"Xing"文件为MPEG并且是单声道

在VBF格式的第一帧中,XINGHeader包括帧头一共最多只需要156个字节就够了,当然也可以在XINGHeader后面存储编码器的信息,比如lame在其后就是存储其版本,这需要给第一帧留足够的空间才行。

3、ID3v1

ID3V1标准并不周全,存放的信息少,无法存放歌词,无法录入专辑封面、图片等。

ID3V2是一个相当完备的标准,但给编写软件带来困难,虽然赞成此格式的人很多,在软件中绝大多数MP3仍在使用ID3V1标准。

ID3v1标签包含艺术家,标题,唱片集,发布年代和流派。

另外还有额外的注释空间。

位于音频文件的最后固定为128

字节。

可以读取该文件的最后这128字节获得标签。

ID3V1结构如下:

AAABBBBBBBBBBBBBBBBBBBBBBBBBBBBB

BCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCD

DDDDDDDDDDDDDDDDDDDDDDDDDDDDDEEE

EFFFFFFFFFFFFFFFFFFFFFFFFFFFFGHI

表3.1ID3V1.0

文件尾说明

字节长度(字

子节节)

说明

存放“TAG字符,表示ID3V1.0标准,紧接其后的是歌曲

1-3(A)3

信息。

4-33(B)30

歌名

34-63(C)30

作者

64-93(D)30

专辑名

94-97(E)4

年份

98-125(F)28

附注

126(G)1

保留位

127(H)1

音轨号

128(I)1

MP3t乐类别,共147种。

ID3V1的各项信息都是顺序存放,没有任何标识将其分开,比如标题信息不足30个

字节,则使用''\0''填充,数据结构定义如下:

typedefstructtaglD3V1

{

charHeader[3];

/*标签头必须是"TAG"否则认为没有标签*/

charTitle[30];

/*标题*/

charArtist[30];

/*作者*/

charAlbum[30];

/*专集*/

charYear[4];

/*出品年代*/

charComment[28];

/*备注*/

charreserve;

/*保留*/

chartrack;

/*音轨*/

charGenre;

/*类型*/

}ID3V1,*pID3V1;

例子:

向MP3文件写入自己的数据帧

unituWriteDataToMp3;

interface

uses

Windows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms,

Dialogs,StdCtrls,Buttons,ExtCtrls;

type

TForm1=class(TForm)

Panel1:

TPanel;

Panel2:

TPanel;

BitBtn1:

TBitBtn;

BitBtn2:

TBitBtn;

procedureBitBtn1Click(Sender:

TObject);

procedureBitBtn2Click(Sender:

TObject);

private

{Privatedeclarations}

procedureWriteMIO_DataToMp3」D3(mp3File:

string);

public

{Publicdeclarations}

end;

var

Form1:

TForm1;

implementation

{$R*.dfm}

procedureTForm1.WriteMIO_DataToMp3」D3(mp3File:

string);type

ID3Header=record//ID3标签

ID3:

array[0..2]ofChar;//==ID3

Ver1:

Byte;//3

Ver2:

Byte;//0

Flag:

Byte;//

Size:

array[0..3]ofChar;//string[4];

//包括标签头的10个字节和所有的标签帧的大小

//(Size[0]&0x7F)*0x200000+(Size[1]&0x7F)*0x4000+(Size[2]&

//0x7F)*0x80+(Size[3]&0x7F)

end;

FrameHeader=record//帧头

ID:

array[0..3]ofChar;//4个字符标识一个帧,说明其内容

Size:

array[0..3]ofChar;//Size[0]*0x100000000+Size[1]*0x10000+

//Size[2]*0x100+Size[3]

Flag:

array[0..1]ofChar;//==00

end;

wx_Head=record//写数据

ID:

Word;

data_len:

Integer;

Data:

string[128];

end;

var

wx:

wx_Head;

i,j,n,data_len,id3Size,frameSize:

Integer;

ms,ms2:

TMemoryStream;

p:

PChar;

wb,c:

Byte;

ID3:

ID3Header;

Frame:

FrameHeader;

rwFile:

Integer;

wMp3File:

string;

begin

//读取MP3文件的ID3头

rwFile:

=FileOpen(mp3File,fmOpenRead);

FileRead(rwFile,ID3,SizeOf(ID3Header));

FileClose(rwFile);

if(ID3.ID3<>'ID3')and(ID3.Ver1<>3)then

begin

ShowMessage('文件【’+mp3File+'】不是标准的ID3V2版本的MP3文件!

');

Exit;

end;

wMp3File:

='C:

\MyMp3.MP3';

//计算原来MP3文件的ID3大小

Id3Size:

=(ord(id3.Size[0])and$7F)*$200000+

(ord(id3.Size[1])and$7F)*$4000+

(ord(id3.Size[2])and$7F)*$80+

(ord(id3.Size[3])and$7F);

//我们的帧头

Frame.ID:

='SYLT';

Frame.Size:

='0000:

Frame.Flag:

='00';

//我们的PWM+IO数据头

wx.lD:

=$55AA;

wx.Data:

='向MP3文件里写自己的数据帧’;

ms:

=TMemoryStream.Create;

ms.Write(ID3,SizeOf(ID3Header));

ms.Write(Frame,SizeOf(FrameHeader));

ms.Write(wx,SizeOf(wx_Head));

data_len:

=SizeOf(wx_Head);

wx.data_len:

=data_len;//数据大小,不含头

frameSize:

=SizeOf(wx_Head);//我们使用帧大小

//帧大小

Frame.Size[0]

Frame.Size[1]

Frame.Size[2]

Frame.Size[3]

id3Size:

=id3Size+frameSize+SizeOf(ID3Header)+SizeOf(FrameHeader);//ID3新的大小

=chr((frameSizeshr24)and$FF);=chr((frameSizeshr16)and$FF);

=chr((frameSizeshr8)and$FF);

=chr(frameSizeand$FF);

//ID3填写的大小

ID3.Size[0]:

=chr((id3Sizeshr21)and$7F);

ID3.Size[1]:

=chr((id3Sizeshr14)and$7F);

ID3.Size[2]:

=chr((id3Sizeshr7)and$7F);

ID3.Size[3]:

=chr(id3Sizeand$7F);

//数据大小改变了,重写

ms.Position:

=0;

ms.Write(ID3,SizeOf(ID3Header));

ms.Write(Frame,SizeOf(FrameHeader));

ms.Write(wx,SizeOf(wx_Head));//更新数据长度数据

ms.Position:

=ms.Size;//移动、然后后面追加MP3数据

ms2:

=TMemoryStream.Create;//Mp3数据

ms2.LoadFromFile(mp3File);

ms2.Seek(SizeOf(ID3Header),soFromBeginning);//ms2要去掉LD310个字

节的头

n:

=ms2.Size-SizeOf(ID3Header);

p:

=GetMemory(n);

ms2.ReadBuffer(p»n);

ms2.Free;

ms.Write(pA,n*SizeOf(Byte));//追加MP3数据

FreeMemory(p);

ms.SaveToFile(wMp3File);//写文件

ms.Free;

ShowMessage('用MP3【'+mp3File+'】文件写目标文件【'+wMp3File+'】

已完成!

’);

end;

procedureTForm1.BitBtn1Click(Sender:

TObject);

var

aFile:

string;

i:

Integer;

begin

aFile:

='';

withTOpenDialog.Create(self)do

begin

Options:

=[ofHideReadOnly,ofNoChangeDir,ofAllowMultiSelect,

ofEnableSizing];

Filter:

='(*.mp3)|*.mp3';

ifExecutethenaFile:

=FileName;

Free;

end;

ifaFile<>''then

begin

WriteMIO_DataToMp3_ID3(aFile);

end;

end;

pr

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高中教育 > 初中教育

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1