本來想寫點進度控制與音量調整的代碼的,後來發現還是太簡單了,就是幾個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格式,有點煩,下篇再做說明吧!