背景
前几天了解到MP3文件的ID3v1信息和ID3v2信息结构,其中ID3v1信息存储的内容比较简单,有歌曲名、艺术家、专辑、发行年、备注、曲目编号、流派。其版本有1.0和1.1,其中1.0没有曲目编号。
正文
ID3v1信息存储在MP3文件的尾部,一共128字节,可有可无。以下是其信息排列:
ID3v1.1(1.0中无曲目编号,所以其备注包括Null character和Track,共30字节)
Tag Field
Data(character)
Offset(from end of mp3)
TAG
3
-128 to -126
Song title(歌曲名)
30
-125 to -96
Artist(艺术家)
30
-95 to -66
Album(专辑)
30
-65 to -36
Year(发行年)
4
-35 to -32
Comment(备注)
28
-31 to -4
Null character
1
-3
Track(曲目编号)
1
-2
Genre(流派)
1
-1
说明
Year:以字符串形式存在,获取时无需将其从byte[4]转换成int。
Null character:此为保留位,1.1中始终为(byte)0,所以通过其判断Comment的大小和ID3v1的版本。
Track:int类型,其值为 0 - 255,毕竟1byte只能存这么多。
Genre: int类型,其对应类型详见附录。
知道了以上信息就可以开始编码了。
publicenumID3v1TagVersion
{
ID3v10,
ID3v11
}
ID3v1
privateID3v1(){ }publicID3v1(stringpath)
{
MP3Path=path;
ReadPath(MP3Path);
}
私有字段和共属性
#regionPrivate FieldsprivateID3v1TagVersion _tagVersion;privatestring_title;//30 charactersprivatestring_artist;//30 charactersprivatestring_album;//30 charactersprivatestring_year;//4 charactersprivatestring_comment;//28 characters, sometimes it's 30 characters when the next byte is not be 0 and this tag has not track information.privatestring_reserved;//1 byte, if it's 0 that means the next byte should contain which track on the CD this music comes from.privateint_track;//1 byte, sometimes not exist if the reserved byte is not 0.privateint_genre=12;//1 byteprivatestringMP3Path;//mp3 file path#endregion#regionPropertypublicID3v1TagVersion TagVersion
{get{return_tagVersion; }set{ _tagVersion=value;if(value==ID3v1TagVersion.ID3v11) {this.Comment=this._comment; } }
}publicstringTitle
{get{return_title; }set{ _title=GetString(value,30); }
}publicstringArtist
{get{return_artist; }set{ _artist=GetString(value,30); }
}publicstringAlbum
{get{return_album; }set{ _album=GetString(value,30); }
}publicstringYear
{get{return_year; }set{ _year=GetString(value,4); }
}publicstringComment
{get{return_comment; }set{ _comment=GetString(value,this._tagVersion==ID3v1TagVersion.ID3v10?30:28); }
}privatestringReserved
{get{return_reserved; }set{ _reserved=value; }
}publicintTrack
{get{return_track; }set{if(value>=0&&value<=0xff)
{
_track=value;if(this._tagVersion==ID3v1TagVersion.ID3v10)
{this.TagVersion=ID3v1TagVersion.ID3v11;
}
}
}
}publicintGenre
{get{return_genre; }set{ _genre=value; }
}#endregion
读取ID3v1信息
privatevoidReadPath(stringpath)
{using(FileStream stream=File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{this.ReadStream(stream);
}
}privatevoidReadStream(Stream stream)
{if(stream.Length>=128)
{
Encoding encode=Encoding.Default;byte[] tag=newbyte[128];
stream.Seek(-128L, SeekOrigin.End);
stream.Read(tag,0,128);if("TAG"==encode.GetString(tag,0,3))
{this._title=encode.GetString(tag,3,30);this._artist=encode.GetString(tag,33,30);this._album=encode.GetString(tag,63,30);this._year=encode.GetString(tag,93,4);if(tag[125]==0)
{this._tagVersion=ID3v1TagVersion.ID3v11;this._comment=encode.GetString(tag,97,28);this._track=tag[126];
}else{this._tagVersion=ID3v1TagVersion.ID3v10;this._comment=encode.GetString(tag,97,30);this._track=0;
}this._genre=(int)tag[127];
}
}
}
保存ID3v1信息
privatevoidSave(stringpath)
{using(FileStream stream=File.Open(path, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
Save(stream);
}
}privatevoidSave(Stream stream)
{byte[] header=SafeGetBytes("TAG");byte[] title=SafeGetBytes(this._title);byte[] artist=SafeGetBytes(this._artist);byte[] album=SafeGetBytes(this._album);byte[] year=SafeGetBytes(this._year);byte[] comment=SafeGetBytes(this._comment);
stream.Seek((long)-GetTagSize(stream), SeekOrigin.End);
stream.Write(header,0,3);
WriteBytesPadded(stream, title,30);
WriteBytesPadded(stream, artist,30);
WriteBytesPadded(stream, album,30);
WriteBytesPadded(stream, year,4);if(this._tagVersion==ID3v1TagVersion.ID3v11)
{
WriteBytesPadded(stream, comment,28);
stream.WriteByte(0);
stream.WriteByte((byte)this._track);
}else{
WriteBytesPadded(stream, comment,30);
}
stream.WriteByte((byte)this._genre);
}
私有函数
privatestaticstringGetString(stringvalue,intmaxLength)
{if(value==null)
{returnnull;
}
value=value.Trim();returnvalue.Length>maxLength?value.Substring(0, maxLength) : value;
}privatestaticintGetTagSize(stringpath)
{using(FileStream stream=File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{returnGetTagSize(stream);
}return0;
}privatestaticintGetTagSize(Stream stream)
{if(stream.Length>=128L)
{byte[] header=newbyte[3];
stream.Seek(-128L, SeekOrigin.End);
stream.Read(header,0,3);if(Encoding.Default.GetString(header)=="TAG")
{return128;
}
}return0;
}privatestaticbyte[] SafeGetBytes(stringvalue)
{if(value==null)
{returnnewbyte[0];
}returnEncoding.Default.GetBytes(value);
}privatestaticvoidWriteBytesPadded(Stream stream,byte[] buffer,intlength)
{intindex=0;while((index
{
stream.WriteByte(buffer[index]);
index++;
}while(index
{
stream.WriteByte(0);
index++;
}
}
结语
有了这些就可以实现MP3的ID3v1信息的读取和修改了。
附录
流派信息有两部分,摘自ID3.ORG。若想知道其他流派信息,请自行搜索。
1. ID3v1定义的(流派前的数字是编号)
ID3v1定义
0. Blues1. Classic Rock2. Country3. Dance4. Disco5. Funk6. Grunge7. Hip-Hop8. Jazz9. Metal10. New Age11. Oldies12. Other13. Pop14. R&B15. Rap16. Reggae17. Rock18. Techno19. Industrial20. Alternative21. Ska22. Death Metal23. Pranks24. Soundtrack25. Euro-Techno26. Ambient27. Trip-Hop28. Vocal29. Jazz+Funk30. Fusion31. Trance32. Classical33. Instrumental34. Acid35. House36. Game37. Sound Clip38. Gospel39. Noise40. AlternRock41. Bass42. Soul43. Punk44. Space45. Meditative46. Instrumental Pop47. Instrumental Rock48. Ethnic49. Gothic50. Darkwave51. Techno-Industrial52. Electronic53. Pop-Folk54. Eurodance55. Dream56. Southern Rock57. Comedy58. Cult59. Gangsta60. Top4061. Christian Rap62. Pop/Funk63. Jungle64. Native American65. Cabaret66. New Wave67. Psychadelic68. Rave69. Showtunes70. Trailer71. Lo-Fi72. Tribal73. Acid Punk74. Acid Jazz75. Polka76. Retro77. Musical78. Rock&Roll79. Hard Rock
2. Winamp扩展的
Winamp扩展
80. Folk81. Folk-Rock82. National Folk83. Swing84. Fast Fusion85. Bebob86. Latin87. Revival88. Celtic89. Bluegrass90. Avantgarde91. Gothic Rock92. Progressive Rock93. Psychedelic Rock94. Symphonic Rock95. Slow Rock96. Big Band97. Chorus98. Easy Listening99. Acoustic100. Humour101. Speech102. Chanson103. Opera104. Chamber Music105. Sonata106. Symphony107. Booty Bass108. Primus109. Porn Groove110. Satire111. Slow Jam112. Club113. Tango114. Samba115. Folklore116. Ballad117. Power Ballad118. Rhythmic Soul119. Freestyle120. Duet121. Punk Rock122. Drum Solo123. A capella124. Euro-House125. Dance Hall