本来想写点进度控制与音量调整的代码的,后来发现还是太简单了,就是几个MCI命令,来回搬弄,自己都没兴趣写下去。所以我想还是写些独门一点的:音乐信息的读取!
目前常见的主流音乐格式就两种,MP3与WMA,它们都有在文件中保存音乐信息的特定格式,MP3使用的当然是家喻户晓的ID3格式,分为V1与V2两个版本;WMA是MS的宠儿,它只是ASF格式的一个分支,当然遵循ASF的包装规则。
怎么获取它们包含的音乐信息呢?一般是自己读取,当然XP系统开始提供了音乐文件的详细信息资料,利用FSO可以真接从系统那里读取到,这不在本文范围,毕竟不用控件更自由,通用性更好。所以,有必要去深入一下这几种音乐信息格式。还是用代码说话吧,先说说MP3的ID3V1与ID3V2的格式。
ID3V1很简单,共128个字节,写在文件尾部,格式如下:
Private Type Mp3ID3V1
Header As String * 3
Title As String * 30
Artist As String * 30
Album As String * 30
Year As String * 4
Comment As String * 30
Genre As String * 1
End Type
ID3V2是后来出现的,可扩展性很强,写在文件头部,采用标签组格式,分两部分,一是标签组的总头部,一是每个子标签的头部,分别定义如下:
Private Type Mp3ID3V2
Header As String * 3
Ver As Byte
Revision As Byte
Flag As Byte
Size(3) As Byte
End Type
Private Type Mp3ID3V2Tag
Tag As String * 4
Size(3) As Byte
Flag(1) As Byte
End Type
为了组织音乐信息的方便,我还定义了一个自己的结构,以便于使用:
'音乐类型
Private Enum MediaType
mciMIDI = 1
mciMP3 = 2
mciASF = 4
mciVIDEO = 8
mciWAVE = 16
End Enum
'装载音乐信息的结构
Private Type MusicInfo
FileName As String
MusicType As MediaType
Title As String
Artist As String
Album As String
Year As String
Lyrics As String
Writer As String
Composer As String
Bits As String
Sample As String
Length As Long
End Type
'我习惯于用代码说明问题,所以还是看看代码吧
Private Function GetMusicInfo(udtInfo As MusicInfo) As Boolean
Dim strFileName As String, a() As String, i As Long
With udtInfo
strFileName = Dir(.FileName, vbNormal Or vbHidden Or vbReadOnly Or vbSystem Or vbArchive)
If strFileName = vbNullString Then Exit Function
.MusicType = GetMCIType(strFileName)
If .MusicType And mciMP3 Then
GetMusicInfo = GetMP3Info(udtInfo)
ElseIf .MusicType And mciASF Then
GetMusicInfo = GetASFInfo(udtInfo)
End If
End With
End Function
Private Function GetMCIType(strFileName As String) As MediaType
Dim ext As String
If strFileName <> vbNullString Then
ext = LCase$(Mid$(strFileName, InStrRev(strFileName, ".")))
Select Case ext
Case ".mpg", ".mpeg", ".avi", ".mpe", ".mpa", ".m1v", ".ifo", ".vob"
GetMCIType = mciVIDEO
Case ".mp3"
GetMCIType = mciMP3
Case ".wav", ".snd", ".aif", ".au", ".aifc", ".aiff"
GetMCIType = mciWAVE
Case ".asf", ".wma", ".wm", ".wmd"
GetMCIType = mciASF
Case ".wmv"
GetMCIType = mciASF Or mciVIDEO
Case ".mid", ".midi", ".rmi"
GetMCIType = mciMIDI
End Select
End If
End Function
Private Function GetMP3Info(udtInfo As MusicInfo) As Boolean
Dim FreeNo As Long, n(1) As Byte, b() As Byte, TmpInfo As MusicInfo
Dim Power As Long, v As Long, j As Long, Tagh As Mp3ID3V2Tag
Dim id3 As Mp3ID3V1, s As String, Pos As Long, id32 As Mp3ID3V2
Dim sz As Long, s1 As String
TmpInfo = udtInfo
On Error GoTo exitg
FreeNo = FreeFile
Open TmpInfo.FileName For Binary As #FreeNo
With TmpInfo
Pos = LOF(FreeNo) - 127
If Pos > 0 Then
Get #FreeNo, Pos, id3
If UCase$(id3.Header) = "TAG" Then
s = Trim$(Replace$(id3.Title, vbNullChar, vbNullString))
If Len(s) > 0 Then
s = Replace$(s, "-", vbNullString)
s = Replace$(s, "——", vbNullString)
s = Replace$(s, ".mp3", vbNullString, , , vbTextCompare)
.Title = s
End If
s = Trim$(Replace$(id3.Artist, vbNullChar, vbNullString))
If Len(s) > 0 Then
.Title = Replace$(.Title, s, vbNullString)
.Artist = s
End If
s = Trim$(Replace$(id3.Album, vbNullChar, vbNullString))
If Len(s) > 0 Then .Album = s
s = Trim$(Replace$(id3.Year, vbNullChar, vbNullString))
If Len(s) > 0 Then .Year = s
End If
End If
Get #FreeNo, 1, id32
If id32.Header = "ID3" Then
sz = (id32.Size(1) And &H7F) * &H400 + (id32.Size(2) And &H7F) * &H80 + (id32.Size(3) And &H7F)
Pos = sz + 10
s1 = String(4, vbNullChar)
Get #FreeNo, , Tagh
Do While Not (Tagh.Tag = s1 Or Seek(FreeNo) > sz + 10)
j = Tagh.Size(1) * &H10000 + Tagh.Size(2) * &H100 + Tagh.Size(3)
If j > 0 Then
ReDim b(j - 1)
Get #FreeNo, , b
s = StrConv(b, vbUnicode)
s = Trim$(Replace$(s, vbNullChar, ""))
Select Case Tagh.Tag
Case "TIT2"
.Title = s
Case "TPE1"
.Artist = s
Case "TALB"
.Album = s
Case "TCOM"
.Composer = s
Case "TEXT"
.Writer = s
Case "TYER"
.Year = s
Case "USLT"
s = Replace$(s, " ", " ")
If LCase$(Left$(s, 3)) = "chi" Then
.Lyrics = Mid$(s, 4)
ElseIf LCase$(Left$(s, 3)) = "eng" Then
.Lyrics = Mid$(s, 4)
Else
.Lyrics = s
End If
End Select
End If
Get #FreeNo, , Tagh
Loop
Else
Pos = 1
End If
Get #FreeNo, Pos, n
sz = Pos
If Not (n(0) = &HFF And n(1) >= &HFA And n(1) <= &HFF) Then
Do While Not (n(0) = &HFF And n(1) = &HFB)
Pos = Pos + 1
If Seek(FreeNo) - sz > 8192 Then GoTo exitg
Get #FreeNo, Pos, n
Loop
End If
Get #FreeNo, , n
v = 0
For j = 4 To 7
Power = 2 ^ j
If (n(0) And Power) = Power Then v = v + Power
Next
v = v \ 16
.Bits = Trim$(Mid$("144 320 32 48 56 64 80 96 112 128 160 192 224 256 320 ", v * 4 + 1, 4)) & "Kbps"
v = 0
For j = 2 To 3
Power = 2 ^ j
If (n(0) And Power) = Power Then v = v + Power
Next
v = v \ 4
.Sample = Trim$(Mid$("44 48 32 ?? ", v * 3 + 1, 3)) & "KHz"
End With
udtInfo = TmpInfo
GetMP3Info = True
exitg:
Close #FreeNo
End Function
还有个ASF格式,有点烦,下篇再做说明吧!