我们经常会听歌,对于一首歌,通常会存在歌名、作者、专辑、封面等信息。
如果我们想要获得这些信息,一方面,我们可以去网上搜索,获取相关信息,并将其归档;另一方面,实际上这些音频文件内部已经存储了相关的歌曲信息,我们只需要读取出来就可以,本文简单介绍了几种常见的歌曲标签标准,用来从歌曲中获取歌曲的相关信息。
1. 概述
最初的音频文件实际上只包含音频文件本身的信息,这就导致,如果没有对音频文件进行很好的归档(比如用数据库存储音频文件的相关信息),在没有音频识别技术的前提下,我们是无法获取这首歌的相关信息的,不知道作者是谁,不知道歌曲名是什么,这显然会带来很多的问题。
比如你听到一段好听的音乐,却除了音乐本身外,一无所知,这显然不是我们想要的结果,所以需要一种技术,将歌曲的相关信息写入到歌曲文件中,这样播放器在播放音乐的时候,便可以解析音乐中的相关信息,进而知道这首音乐的作者,专辑,歌名等信息。
ID3
,APE
便是其中的两个解决方案,它们通过将歌曲的一些信息存入到音频文件中,作为元数据(metadata
),进而可以被解析。
但是需要明确的一点是,这些标准只是被广泛使用,并不代表所有的音频文件都符合这一标准,可能有的播放器只能解析ID3V2
标签,而有的播放器只支持ID3V1
标签。
类型及标识
ID3v1
位于文件的结尾,以字符串TAG
为标识
ID3v2
位于文件的开头,长度不固定,以字符串ID3
为标识
APEv2
长度和位置都不固定,一般位于ID3v1
之前,以字符串APETAGEX
为标识
Lyrics3v2
位于ID3v1
之前,APEv2
(如果存在)之后,以字符串LYRICSBEGIN
为标识,主要用来存储Lrc类型的歌词文件
常见的标签在音频文件中的位置如下所示
这里需要注意的一点是,ID3
标签各个版本,通常用于mp3,即有损压缩的音频文件中;而APEv2通常用于无损压缩的音乐文件中,比如ape格式的音频文件;当然,一个音频文件中完全可以存在所有的标签(只要位置不冲突)。
对于flac格式的音频文件,存在flac标签进行标识,位于音频文件的开头,这时候就无法使用ID3v2.3
标签了
2. ID3v1
位置
ID3V1
通过将歌曲的信息写入音频文件的最后128字节,对音频文件进行标识,分为ID3V1
和ID3V1.1
两个版本,ID3V1.1
版本是对ID3V1
版本的补充。
编码
ID3v1
使用ANSI
方式对tag进行编码,
ANSI并不是某一种特定的字符编码,而是在不同的系统中,根据当前系统使用的字符集进行编码,那么写入tag的字符编码就有可能是任意的标准,比如ASCII
,UNICODE
,UTF-8
,GBK
,JIS
,EUC-KR
等编码,所以,读取tag时,需要判断字符的编码格式,这样读出来的内容才不会乱码。
结构
ID3v1
与ID3v1.1
的具体结构如下所示
* 标签头 TAG 3byte
* 标题(Tilte) 30byte
* 作者(Artist) 30byte
* 专辑(Album) 30byte
* 出版年份(Year) 4byte
* for ID3V1
* 备注(Comment) 30 byte
* for ID3V1.1
* 备注(Comment) 28byte
* 保留 1byte
* 音轨(Track) 1byte
* 流派/类型(Genre) 1byte
其中
-
最开始的三个字节一直是TAG,只有存在这个标记,才表明这是一个
ID3V1
或ID3v1.1
标签 -
对于tag的具体内容,如果某段信息没有被填满,比如标题只有8个字节,那么剩下的22个字节都用零字节进行填充,这样在读取的时候,只要读到零,就代表这段信息已经结束了。
-
由于
ID3V1
里面没有音轨,所以ID3v1.1
进行了改进,将Comment限制为28个字节,如果第29个字节为零,且第30个字节不为零,那么第30个字节的无符号整型值代表的就是音轨 -
对于Track,由于大多数文件都是以无符号整型值-1作为结尾,所以很多时候,往往无法获取流派的信息
-
其中流派中,无符号整型值对应的流派如下所示
-
``` 0.Blues 1.Classic Rock 2.Country 3.Dance 4.Disco 5.Funk 6.Grunge 7.Hip-Hop 8.Jazz 9.Metal 10.New Age 11.Oldies 12.Other 13.Pop 14.R&B 15.Rap 16.Reggae 17.Rock 18.Techno 19.Industrial 20.Alternative 21.Ska 22.Death Metal 23.Pranks 24.Soundtrack 25.Euro-Techno 26.Ambient 27.Trip-Hop 28.Vocal 29.Jazz+Funk 30.Fusion 31.Trance 32.Classical 33.Instrumental 34.Acid 35.House 36.Game 37.Sound Clip 38.Gospel 39.Noise 40.AlternRock 41.Bass 42.Soul 43.Punk 44.Space 45.Meditative 46.Instrumental Pop 47.Instrumental Rock 48.Ethnic 49.Gothic 50.Darkwave 51.Techno-Industrial 52.Electronic 53.Pop-Folk 54.Eurodance 55.Dream 56.Southern Rock 57.Comedy 58.Cult 59.Gangsta 60.Top 40 61.Christian Rap 62.Pop/Funk 63.Jungle 64.Native American 65.Cabaret 66.New Wave 67.Psychadelic 68.Rave 69.Showtunes 70.Trailer 71.Lo-Fi 72.Tribal 73.Acid Punk 74.Acid Jazz 75.Polka 76.Retro 77.Musical 78.Rock & Roll 79.Hard Rock 80.Folk 81.Folk-Rock 82.National Folk 83.Swing 84.Fast Fusion 85.Bebob 86.Latin 87.Revival 88.Celtic 89.Bluegrass 90.Avantgarde 91.Gothic Rock 92.Progressive Rock 93.Psychedelic Rock 94.Symphonic Rock 95.Slow Rock 96.Big Band 97.Chorus 98.Easy Listening 99.Acoustic 100.Humour 101.Speech 102.Chanson 103.Opera 104.Chamber Music 105.Sonata 106.Symphony 107.Booty Bass 108.Primus 109.Porn Groove 110.Satire 111.Slow Jam 112.Club 113.Tango 114.Samba 115.Folklore 116.Ballad 117.Power Ballad 118.Rhythmic Soul 119.Freestyle 120.Duet 121.Punk Rock 122.Drum Solo 123.A capella 124.Euro-House 125.Dance Hall
- 80 Folk and after following genres are Winamp extensions Deprecated Gradle features were used in this build, making it incompatible with Gradle 8.0. ```
3. ID3V2
由于ID3v1
只能存储固定128字节的歌曲信息,所以存储的信息有限,如果想要插入其他的信息,比如封面等,这时候就可以使用ID3v2
了,ID3v2
目前最常用的是ID3v2.3
版本,这里也是针对这个版本进行介绍。
位置
ID3v2.3
标签位于音频文件的开头,相比于ID3v1
,存储的内容更多,可以存储图片等,同时支持用户自定义帧。
结构
ID3v2的结构如下所示
其中,帧(frame)可以分为帧头(frame header)和帧内容(frame content)。
Header
Header的结构如下
ID3v2/file identifier 3 "ID3"
ID3v2 version 2 $03 00
ID3v2 flags 1 %abc00000
ID3v2 size 4 4 * %0xxxxxxx
Header一共有10bytes
,其中,
-
前3
bytes
始终为ID3
,表明这是一个ID3v2
标签 -
第4和第5
byte
标识了ID3的版本(主版本和修订版本),在这里版本就是3.0(03表示3,00表示0),由于这是ID3v2
才有的结构,所以版本全称就是ID3v2.3.0
-
第6
byte
为标志位,只使用高三位,其他位始终为0,这里需要注意的是b,也就是位6- 当b为0时,表明不存在扩展头,当b为1时,存在扩展头
-
第7到10
byte
为大小,规定了这个ID3v2
标签除了Header外的总长度(包括扩展头),每一个byte第7位始终为0,因此size的计算方式如下-
0xxxxxxx 0xxxxxxx 0xxxxxxx 0xxxxxxx => xxxxxxx xxxxxxx xxxxxxx xxxxxxx 共28位 4x7 用buffer数组表示Header,注意数组索引从0开始 size = (buffer[6] & 0x7f) << 21 | (buffer[7] & 0x7f) << 14 | (buffer[8] & 0x7f) << 7 | (buffer[9] & 0x7f); 或 size = ((buffer[6] & 0x7f) << 21) + ((buffer[7] & 0x7f) << 14) + ((buffer[8] & 0x7f) << 7) + (buffer[9] & 0x7f); 注意0x7f = 111 1111 这里用&主要是为了解决负数的问题,因为在Java中,byte可能为负,这时候为了将其表示为0~255范围内的数值,就需要&0x7f
- 这里需要注意的一点是,size只是大致范围,Header以及Extended Header、Frame的size总和
<=
size,多余的部分用0补齐
-
一个ID3v2
标签符合下面的格式
49 44 33 yy yy xx zz zz zz zz
由于Extended Header大多数情况下都没有,所以这里就不在赘述。
Frame
Frame包括Frame Header和Frame Content,其中Frame Content的大小至少为1byte,否则就不是有效的Frame
Frame Header
大小为10bytes
结构
Frame ID $xx xx xx xx (four characters)
Size $xx xx xx xx
Flags $xx xx
关于Frame ID,在id3v2.3.0 - ID3.org可找到详细的定义。
Size计算方式
xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
=>
xxxxxxx xxxxxxx xxxxxxx xxxxxxx 共32位 4x8
用buffer数组表示Header,注意数组索引从0开始
size = (buffer[4] & 0x7f) << 24 |
(buffer[5] & 0xff) << 16 |
(buffer[6] & 0xff) << 8 |
(buffer[7] & 0xff);
或
size = ((buffer[4] & 0x7f) << 24) +
((buffer[5] & 0xff) << 16) +
((buffer[6] & 0xff) << 8) +
(buffer[7] & 0xff);
0xff = 0000 0000
这里&0xff的理由同上,主要是针对java的byte
关于Flags,这里也是只取每一个字节的高3位,其他位为0,这样共有6个标志位,由于Flags用处不大,所以这里不做太多的介绍。
Frame Content
由于不同Frame ID对应的Frame Content结构不同,这里只介绍几个比较常用的Frame ID里面的内容
以T开头的帧都是文本标识,如TRCK、TXXX、TALB、TIT2等,它们的Frame Content结构类似,通常为两部分或三部分
第一部分1byte,其值为0-3,不同的值代表不同的编码
0-> ISO-8859-1
00 ...
1-> UTF-16 如果是这个 后面还需要两个字节标识编码 FF FE -> UTF-16LE
01 FF FE ...
2-> UTF-16BE 如果是这个 后面还需要两个字节标识编码 FE FF -> UTF-16BE
02 FE FF ...
3-> UTF-8 如果是这个,后面还需要跟3个字节标识编码 EF BB BF
03 EF BB BF ...
APIC为图片,内容结构如下
Text encoding $xx
MIME type <text string> $00
Picture type $xx
Description <text string according to encoding> $00 (00)
Picture data <binary data>
-
Text encoding结合Description使用,使用方式与T开头标签相同
-
MIME type值通常为
image/jpeg
,image/png
等,以0x00结尾 -
Picture type为图片所代表的内容,主要有以下类型
-
$00 Other $01 32x32 pixels 'file icon' (PNG only) $02 Other file icon $03 Cover (front) $04 Cover (back) $05 Leaflet page $06 Media (e.g. lable side of CD) $07 Lead artist/lead performer/soloist $08 Artist/performer $09 Conductor $0A Band/Orchestra $0B Composer $0C Lyricist/text writer $0D Recording Location $0E During recording $0F During performance $10 Movie/video screen capture $11 A bright coloured fish $12 Illustration $13 Band/artist logotype $14 Publisher/Studio logotype
-
-
Description为图片描述,以0x00结尾
-
Picture data为二进制的图片内容,可以直接截取这一段内容,将其写入文件,即可得到图片
4. FLAC
简介
FLAC格式的歌曲有自己的一套标签定义标注,因此并不兼容其他格式的标签,比如ID3,APEv2,虽然官网推荐的读写FLAC的工具可以识别并跳过ID3标签,但并不推荐在FLAC歌曲中写入ID3标签。
结构
The four byte string "fLaC" # 四字节串“fLaC”
The STREAMINFO metadata block # STREAMINFO元数据块
Zero or more other metadata blocks # 零个或多个其他元数据块
One or more audio frames # 一个或多个音频帧
FLAC格式其实可以简单用下面的结构表示
- fLaC 4bytes
- metadata block
- header
- content
- metadata block
- header
- content
- …
STREAMINFO block的结构实际上和metadata block结构一样,只是内容有些特殊,它包含采样率、通道数等信息,以及可以帮助解码器管理其缓冲区的数据,这个数据块是必须要有的。
Header
header部分的结构如下
- 1 bytes
- 1 bit(最高位) 0表示后面还有metadata block,1表示是最后一个metadata block
- 7 bit(0-6bit) 代表block的类型,不同的值有不同的含义
- 0 STREAMINFO
- 1 PADDING
- 2 APPLICATION
- 3 SEEKTABLE
- 4 VORBIS_COMMENT
- 5 CUESHEET
- 6 PICTURE
- 7-126 reserved
- 127 invalid
- 3bytes
- 24bit 表示这个block content的大小,不包括header
- 计算方式如下
- 00000000 00000000 00000000
- size = ((buffer[1] & 0xff) << 16) +
((buffer[2] & 0xff) << 8) +
((buffer[3] & 0xff));
Content
content为具体的内容,根据header的类型标志位进行标识,目前已经定义的共有七种类型,这里只介绍其中两种
VORBIS_COMMENT
存储了一系列的键值对,用来存储歌曲的相关信息,比如歌名,歌手名,歌词等,结构如下
- 4 bytes
- 表示vendor的长度
- vendor 注意这里采用UTF-8编码,长度为上面计算出的长度
- 4 bytes
- 表示共有多少键值对
- 4 bytes
- 键值对长度
- 键值对
- 4 bytes
- 键值对长度
- 键值对
- 4 bytes
- 键值对长度
- 键值对
...
-
vorbis comment规范全都使用UTF-8进行编码,因此这里默认的字符编码就是UTF-8
-
vorbis 采用小端编码的方式计算大小或数量,示例如下
(buffer[0] & 0xff) + ((buffer[1] & 0xff) << 8) + ((buffer[2] & 0xff) << 16) + ((buffer[3] & 0xff) << 24);
PICTURE
存储了歌曲的封面等信息,结构如下
- 4 bytes 图片类型 0-20,计算方式与content size计算方式相同
- 4 bytes MIME内容长度 计算方式同上
- MIME内容,编码格式为UTF-8
- 4 bytes 描述符长度,计算方式同上
- 描述符
- 4 bytes 图片宽度
- 4 bytes 图片长度
- 4 bytes 图片颜色深度
- 4 bytes 索引图使用的颜色数目,0非索引图
- 4 bytes 图片数据长度
- 图片数据
后记
关于歌词,实际上歌词里面很少有歌词,大多数只是带个封面就行了,虽然有lyrics3.0 v1和v2版本,但实际上很少用,因为ID3v2 的USLT帧里面可以存储lrc歌词,对于FLAC,代号为4,也就是VORBIS_COMMENT里面也有LYRICS属性,设置歌词,所以lyrics3.0有些鸡肋,
SYLT同步歌词,待看
krc转lrc
写入歌词可以实用kid3或foobar,注意要勾选歌词的相关属性才会显示歌词属性
APEv2标签貌似存在的必要性不是很大,因为FLAC也是无损音乐,而且大多数时候下载的都是FLAC格式,APE格式较少,所以目前主流标签其实应该是ID3v1
、ID3v2
以及FLAC
相对于ID3v2而言,其实FLAC在解析方面可能对开发者更加友好,不像ID3v2,实际编写程序进行读取的时候很混乱。
当然两者的官网内容感觉都有些晦涩难懂,但是由于FLAC格式歌曲的结构比较清晰,因此实际开发过程中,FLAC对开发者的体验更好。
参考资料
- ID3TAG
- ID3 org
- ID3v2
- MP3tag
- ID3v2 中文文档 (版本 2.3.0)_Simonyd的博客-CSDN博客_id3v2
- MP3 ID3v1 & ID3v2 &APEv2 标准总结_笑傲江湖曲的博客-CSDN博客
- flac
- FLAC音频格式标准
- vorbis-comment
实用工具
