前段时间,在局域网里面做一个小网站,其中有板块是涉及音乐的在线播放。考虑到以后维护的方便,决定把mp3文件都按专辑分不同的目录存放。然后使用一个程序监控存放mp3的目录,把每个mp3文件的信息都存入数据库中,用过ASP.NET页面将mp3文件的信息呈现给用户。其中使用.NET来读取mp3 文件的信息虽然不难,但也需要不少技巧,故将该过程整理与大家分享。
首先我们来看看mp3歌曲的信息所存放的位置。Mp3文件包含一个叫做ID3的标签。其实有两个标签,一个叫做ID3v1,另外一个叫做ID3v2。为了讲述的简单起见,我们这里只介绍ID3v1。
ID3V1结构比较简单,存放在MP3文件的末尾,大家可以用16进制的编辑器(例如:UltraEdit)打开一个MP3文件,注意其末尾的128个字节,数据结构定义如下:
名称 位置 长度 内容
Header 1-3 3 标签头
Title 4-33 30 标题
Artist 34-63 30 艺术家
Album 64-93 30 专辑
Year 94-97 4 出品年代
Comment 98-127 30 备注
Cenre 128 1 类型
注意:上述的标签头必须是”TAG”, 否则表示没有标签
ID3v1的各项信息是按顺序依次存放的,每项信息之后并没有任何的结束标志,如果某项信息长度小于标准长度,使用”\0”来补充。另外Genre是个例外,它用一个字节表示歌曲流派,其对应表如下(由于该内容太多,只列出前50项):
0="Blues"
1="ClassicRock"
2="Country"
3="Dance"
4="Disco"
5="Funk"
6="Grunge"
7="Hip-Hop"
8="Jazz"
9="Metal"
10="NewAge"
11="Oldies"
12="Other"
13="Pop"
14="R&B"
15="Rap"
16="Reggae"
17="Rock"
18="Techno"
19="Industrial"
20="Alternative"
21="Ska"
22="DeathMetal"
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="SoundClip"
38="Gospel"
39="Noise"
40="AlternRock"
41="Bass"
42="Soul"
43="Punk"
44="Space"
45="Meditative"
46="InstrumentalPop"
47="InstrumentalRock"
48="Ethnic"
49="Gothic"
50="Darkwave"
知道了MP3歌曲信息存放的结构之后,我们就可以写出对应的代码。
首先定一个MP3Info类:
1PublicClassMp3Info2 3 4 5 PrivateConstTAGLENAsInteger=1286 7 8 9 Private_MP3TagAsString=String.Empty10 11 Private_ArtistAsString=String.Empty12 13 Private_TitleAsString=String.Empty14 15 Private_AlbumAsString=String.Empty16 17 Private_CommentAsString=String.Empty18 19 Private_YearAsString=String.Empty20 21 Private_GenreAsString=String.Empty22 23 Private_GenreIDAsByte24 25 26 27 PrivateGenres()AsString={"Blues","Classic Rock","Country","Dance","Disco","Funk","Grunge", _28 29 "Hip-Hop","Jazz","Metal","New Age","Oldies","Other","Pop","R&B","Rap","Reggae","Rock", _30 31 "Techno","Industrial","Alternative","Ska","Death Metal","Pranks","Soundtrack","Euro-Techno", _32 33 "Ambient","Trip-Hop","Vocal","Jazz+Funk","Fusion","Trance","Classical","Instrumental","Acid", _34 35 "House","Game","Sound Clip","Gospel","Noise","AlternRock","Bass","Soul","Punk","Space", _36 37 "Meditative","Instrumental Pop","Instrumental Rock","Ethnic","Gothic","Darkwave","Techno-Industrial", _38 39 "Electronic","Pop-Folk","Eurodance","Dream","Southern Rock","Comedy","Cult","Gangsta","Top 40", _40 41 "Christian Rap","Pop/Funk","Jungle","Native American","Cabaret","New Wave","Psychedelic","Rave", _42 43 "Showtunes","Trailer","Lo-Fi","Tribal","Acid Punk","Acid Jazz","Polka","Retro","Musical", _44 45 "Rock & Roll","Hard Rock","Folk","Folk/Rock","National Folk","Swing","Bebob","Latin","Revival", _46 47 "Celtic","Bluegrass","Avantgarde","Gothic Rock","Progressive Rock","Psychedelic Rock","Symphonic Rock", _48 49 "Slow Rock","Big Band","Chorus","Easy Listening","Acoustic","Humour","Speech","Chanson","Opera", _50 51 "Chamber Music","Sonata","Symphony","Booty Bass","Primus","Porn Groove","Satire","Slow Jam","Club", _52 53 "Tango","Samba","Folklore"}54 55 56 57 PublicPropertyMP3Tag()AsString58 59 Get60 61 Return_MP3Tag62 63 EndGet64 65 Set(ByValvalueAsString)66 67 _MP3Tag=value.Trim68 69 EndSet70 71 End Property72 73 74 75 PublicPropertyTitle()AsString76 77 Get78 79 Return_Title80 81 EndGet82 83 Set(ByValvalueAsString)84 85 _Title=value.Trim86 87 EndSet88 89 End Property90 91 92 93 PublicPropertyArtist()AsString94 95 Get96 97 Return_Artist98 99 EndGet100 101 Set(ByValvalueAsString)102 103 _Artist=value.Trim104 105 EndSet106 107 End Property108 109 110 111 PublicPropertyAlbum()AsString112 113 Get114 115 Return_Album116 117 EndGet118 119 Set(ByValvalueAsString)120 121 _Album=value.Trim122 123 EndSet124 125 End Property126 127 128 129 PublicPropertyComment()AsString130 131 Get132 133 Return_Comment134 135 EndGet136 137 Set(ByValvalueAsString)138 139 _Comment=value.Trim140 141 EndSet142 143 End Property144 145 146 147 PublicPropertyGenre()AsString148 149 Get150 151 Return_Genre152 153 EndGet154 155 Set(ByValvalueAsString)156 157 _Genre=value.Trim158 159 EndSet160 161 End Property162 163 164 165 PublicPropertyGenreID()AsByte166 167 Get168 169 Return_GenreID170 171 EndGet172 173 Set(ByValvalueAsByte)174 175 _GenreID=value176 177 EndSet178 179 End Property180 181 182 183 PublicPropertyYear()AsString184 185 Get186 187 Return_Year188 189 EndGet190 191 Set(ByValvalueAsString)192 193 _Year=value.Trim194 195 EndSet196 197 End Property198 199 End Class200 201 202 203 上面的类只包含了mp3歌曲信息对应的数据结构,我们还要为它添加具体读取mp3文件信息的过程:204 205 206 207 PublicFunctionGetMp3FileInfo(ByValfnameAsString)AsBoolean208 209 210 211 'Open the filestream212 213 DimmsfileAsFileStream214 215 Try216 217 msfile=NewFileStream(fname, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)218 219 IfNot(msfile.CanRead)Then220 221 ThrowNewIO.IOException("无法读取文件:"+fname)222 223 EndIf224 225 CatchExAsException226 227 ThrowNewIO.IOException("读取文件发生错误!"+Ex.Message)228 229 EndTry230 231 232 233 DimID3(TAGLEN-1)AsByte234 235 DimBinReaderAsBinaryReader236 237 DimStrInfoAsString238 239 240 241 '使用BinaryReader来读取信息242 243 BinReader=NewBinaryReader(msfile)244 245 246 247 msfile.Position=0248 249 msfile.Seek(-TAGLEN, SeekOrigin.End)250 251 252 253 StrInfo=CBytesToString(BinReader.ReadBytes(3))254 255 256 257 '判断标签头是否为 TAG258 259 IfStrInfo.ToUpper="TAG"Then260 261 262 263 '读取标题信息264 265 StrInfo=CBytesToString(BinReader.ReadBytes(30)).Replace(Chr(0),"")266 267 _Title=StrInfo268 269 270 271 '读取艺术家信息272 273 StrInfo=CBytesToString(BinReader.ReadBytes(30)).Replace(Chr(0),"")274 275 _Artist=StrInfo276 277 278 279 '读取专辑信息280 281 StrInfo=CBytesToString(BinReader.ReadBytes(30)).Replace(Chr(0),"")282 283 _Album=StrInfo284 285 286 287 '读取出版年度信息288 289 StrInfo=CBytesToString(BinReader.ReadBytes(4)).Replace(Chr(0),"")290 291 _Year=StrInfo292 293 294 295 '读取备注信息296 297 StrInfo=CBytesToString(BinReader.ReadBytes(30)).Replace(Chr(0),"")298 299 _Comment=StrInfo300 301 302 303 '读取歌曲流派信息304 305 _GenreID=BinReader.ReadByte306 307 308 309 EndIf310 311 312 313 BinReader.Close()314 315 msfile.Close()316 317 318 319 End Function320 321 322 323 '用于转换编码, 防止出现中文乱码324 325 PrivateFunctionCBytesToString(ByValBytes()AsByte)AsString326 327 '注意这里 需要对编码进行处理, 防止出现乱码328 329 DimGbCodeAsEncoding=Encoding.GetEncoding("gb2312")330 331 IfBytes.Length>0Then332 333 ReturnGbCode.GetString(Bytes)334 335 Else336 337 ReturnString.Empty338 339 EndIf340 341 End Function
我们可以用一个简单的Console程序来说明,如何使用Mp3Info类。使用Visual Studio 2005 Express,创建一个Console程序:
1ModuleModule12 3 4 5 Sub6 7 8 9 DimMp3AsNewMp3Info("D:\Music\Top 40 Singles\39 Embrace - Natures Law.mp3")10 11 12 13 Console.WriteLine("Title :"+Mp3.Title)14 15 Console.WriteLine("Artist:"+Mp3.Artist)16 17 Console.WriteLine("Album :"+Mp3.Album)18 19 Console.Read()20 21 End Sub22 23 24 End Module
运行该程序后输出为:
Title : Nature's Law
Artist: Embrace
Album : DHZ.INC
Genre : Blues
本文只针对mp3的ID3v1进行了讨论,而实际上很多mp3不仅仅包含ID3v1的信息,还包含ID3v2的信息。
但ID3v2要比ID3v1复杂,对于ID3v2的处理,要等下次有空的时候再写了。