ID3v1信息結構(MP3文件)的讀取、修改(C#)
背景
前幾天了解到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類型,其對應類型詳見附錄。
知道了以上信息就可以開始編碼了。
public enum ID3v1TagVersion
{
ID3v10,
ID3v11
}
ID3v1
private ID3v1(){ }
public ID3v1(string path)
{
MP3Path = path;
ReadPath(MP3Path);
}
私有字段和共屬性
#region Private Fields
private ID3v1TagVersion _tagVersion;
private string _title; //30 characters
private string _artist; //30 characters
private string _album; //30 characters
private string _year; // 4 characters
private string _comment; //28 characters, sometimes it's 30 characters when the next byte is not be 0 and this tag has not track information.
private string _reserved; // 1 byte, if it's 0 that means the next byte should contain which track on the CD this music comes from.
private int _track; // 1 byte, sometimes not exist if the reserved byte is not 0.
private int _genre = 12; // 1 byte
private string MP3Path; //mp3 file path
#endregion
#region Property
public ID3v1TagVersion TagVersion
{
get { return _tagVersion; }
set { _tagVersion = value; if (value == ID3v1TagVersion.ID3v11) { this.Comment = this._comment; } }
}
public string Title
{
get { return _title; }
set { _title = GetString(value, 30); }
}
public string Artist
{
get { return _artist; }
set { _artist = GetString(value, 30); }
}
public string Album
{
get { return _album; }
set { _album = GetString(value, 30); }
}
public string Year
{
get { return _year; }
set { _year = GetString(value, 4); }
}
public string Comment
{
get { return _comment; }
set { _comment = GetString(value, this._tagVersion == ID3v1TagVersion.ID3v10 ? 30 : 28); }
}
private string Reserved
{
get { return _reserved; }
set { _reserved = value; }
}
public int Track
{
get { return _track; }
set {
if (value >= 0 && value <= 0xff)
{
_track = value;
if (this._tagVersion == ID3v1TagVersion.ID3v10)
{
this.TagVersion = ID3v1TagVersion.ID3v11;
}
}
}
}
public int Genre
{
get { return _genre; }
set { _genre = value; }
}
#endregion
讀取ID3v1信息
private void ReadPath(string path)
{
using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
this.ReadStream(stream);
}
}
private void ReadStream(Stream stream)
{
if (stream.Length >= 128)
{
Encoding encode = Encoding.Default;
byte[] tag = new byte[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信息
private void Save(string path)
{
using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
Save(stream);
}
}
private void Save(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);
}
私有函數
private static string GetString(string value, int maxLength)
{
if (value == null)
{
return null;
}
value = value.Trim();
return value.Length > maxLength ? value.Substring(0, maxLength) : value;
}
private static int GetTagSize(string path)
{
using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
return GetTagSize(stream);
}
return 0;
}
private static int GetTagSize(Stream stream)
{
if (stream.Length >= 128L)
{
byte[] header = new byte[3];
stream.Seek(-128L, SeekOrigin.End);
stream.Read(header, 0, 3);
if (Encoding.Default.GetString(header) == "TAG")
{
return 128;
}
}
return 0;
}
private static byte[] SafeGetBytes(string value)
{
if (value == null)
{
return new byte[0];
}
return Encoding.Default.GetBytes(value);
}
private static void WriteBytesPadded(Stream stream, byte[] buffer, int length)
{
int index = 0;
while ((index < length && index < buffer.Length) && buffer[index] != 0)
{
stream.WriteByte(buffer[index]);
index++;
}
while (index < length)
{
stream.WriteByte(0);
index++;
}
}
結語
有了這些就可以實現MP3的ID3v1信息的讀取和修改了。
附錄
流派信息有兩部分,摘自ID3.ORG。若想知道其他流派信息,請自行搜索。
1. ID3v1定義的(流派前的數字是編號)
ID3v1定義
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
2. Winamp擴展的
Winamp擴展
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



浙公網安備 33010602011771號