www.pudn.com > clsMP3Info.rar > clsMP3Info.cls, change:2009-02-12,size:36752b
VERSION 1.0 CLASS BEGIN MultiUse = -1 'True Persistable = 0 'NotPersistable DataBindingBehavior = 0 'vbNone DataSourceBehavior = 0 'vbNone MTSTransactionMode = 0 'NotAnMTSObject END Attribute VB_Name = "clsMP3Info" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = True Attribute VB_PredeclaredId = False Attribute VB_Exposed = False Option Explicit ' MP3 info class version 1.5 ' Written by Mike D Sutton of EDais ' and Norm Cook (normcook@cableone.net) ' ' E-Mail: EDais@mvps.org ' WWW: Http://www.mvps.org/EDais/ ' ' Written: 16/10/2001 ' Last edited: 19/08/2003 ' 'Version history: '---------------- ' Version 1.5 (19/08/2003): ' .HeaderPos property now re-named to .HeaderOffset ' .FormatTime() method replaced by .FormattedTime property ' .LoadMP3() method now private, use .FileName Property Let instead ' Changed most of how the class works internally ' ' Added: Padding, GenreCount, FrameLength, ID3Present, ID3v2Present, ' ID3v2Size, ID3v2Offset, FormattedTime, PrivateBit, HeaderOffset, ' Title*, Artist*, Album*, Year*, Comment*, GenreID*, TrackNum* ' and GenreString* properties ' (* = ID3 tag information) ' ' IDFromGenreString() - Attempts to match a string to a genre ' RemovePreHeader() - Removes any pre-header detected before the main MPEG header ' RemoveTag() - Removes the ID3 tag from the file ' WriteTag() - Writes the ID3 tag information to the file ' IsHeader() - Tests a DWord value for MPEG header frame sync. bit pattern ' ValidHeader() - Attempts to interpret (and validate) an MPEG header block ' GetFileTimes() - Gets a file's creation, last access and last write times ' SetFileTimes() - Sets a file's creation, last access and last write times ' ClearTag() - Clears the internal ID3 tag details ' TrimNull() - Converts a null-terminated string to a VB string ' Min() - Returns the minimum of two DWord values ' FileExist() - Checks to see if the given file exists ' MakeID3String() - Converts a string to an ID3 compatible string ' FlipDWord() - Converts a DWord value from little to big endian (and visa-versa) ' SyncSafeDWord() - Converts a standard DWord to an ID3v2 'sync-safe' DWord ' SyncSafeWord() - Converts a standard Word to an ID3v2 'sync-safe' Word ' SyncSafeByte() - Converts a standard Byte to an ID3v2 'sync-safe' Byte ' DeSyncSafeDWord() - Converts an ID3v2 'sync-safe' DWord to a standard DWord ' DeSyncSafeWord() - Converts an ID3v2 'sync-safe' Word to a standard Word ' DeSyncSafeByte() - Converts an ID3v2 'sync-safe' Byte to a standard Byte ' GetGenreString() - Returns a genre string based on the given genre ID '------------------------ ' Version 1.0/1.1 (16/10/2001): ' Added: ValidMP3, FileName, FileSize, BitRate, Frequency, SongLength, ' NumFrames, isVBR, MPEGVer, Layers, Protected, ChannelMode, ' Copyrighted, Original, HeaderPos and Emphasis properties ' ' LoadMP3() - Parses the given MP3 file to extract its header ' FormatTime() - Format track time into HH:MM:SS (Or MM:SS) ' ValidHead() - Tests for MPEG header sync information ' ClearInfo() - Clears out all current information '----------------- ' 'About: ' Retrieves information about an MPEG audio file header including ' its bit-rate, frequency, length etc. ' 'You use this code at your own risk, I don't accept any ' responsibility for anything nasty it may do to your machine! 'Feel free to re-use this code in your own applications (Yeah, ' like I could stop you anyway ;) However, please don't attempt ' to sell or re-distribute it without my written consent. 'Visit my site for any updates to this and more strange graphics ' related VB code, comments and suggestions always welcome! Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Long, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long Private Declare Function GetFileTime Lib "kernel32" (ByVal hFile As Long, ByRef lpCreationTime As FileTime, ByRef lpLastAccessTime As FileTime, ByRef lpLastWriteTime As FileTime) As Long Private Declare Function SetFileTime Lib "kernel32" (ByVal hFile As Long, ByRef lpCreationTime As FileTime, ByRef lpLastAccessTime As FileTime, ByRef lpLastWriteTime As FileTime) As Long Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long Private Declare Sub RtlMoveMemory Lib "kernel32" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long) ' Types Private Type ID3Tag ' 128 bytes (+3) tagID As String * 3 tagTitle As String * 30 tagArtist As String * 30 tagAlbum As String * 30 tagYear As String * 4 tagComments As String * 30 ' Only 28 chars with ID3 v1.1 tagGenre As Byte ' These fields are not part of the tag structure itself, but ' hold additional information relating to ID3 v1.1 tags tagTrackNum As Byte ' Only used with ID3 v1.1 tags tagID311 As Boolean ' Does this look like a ID3 v1.1 tag End Type Private Type FileTime dwLowDateTime As Long dwHighDateTime As Long End Type Private Type typFileTimes ftCreationTime As FileTime ftLastAccessTime As FileTime ftLastWriteTime As FileTime End Type Private Type typID3v2Inf iiID As String * 3 iiVersion As Integer iiFlags As Byte iiSize As Long End Type Private m_MPEGVer As Single Private m_Layers As Long Private m_Protected As Boolean Private m_Private As Boolean Private m_BitRate As Long Private m_Frequency As Long Private m_ChannelMode As String Private m_Copyrighted As Boolean Private m_Original As Boolean Private m_Emphasis As String Private m_ID3Present As Boolean Private m_ID3v2Present As Boolean Private m_Padding As Byte Private m_ValidMP3 As Boolean Private m_FileName As String Private m_FileSize As Long Private m_SongLength As Long Private m_NumFrames As Long Private m_FrameLength As Long Private m_VBR As Boolean Private m_HeaderOffset As Long Private m_ActSize As Long Private m_Tag As ID3Tag Private m_ID3v2Size As Long Private m_ID3v2Offset As Long Public HeaderTest As Boolean ' Constants Private Const FrameSync As Long = &HE0FF& ' Byte order reversed - Usually 0xFFE00000 Private Const ID3TagID As String * 3 = "TAG" ' ID3v1.x header ID Private Const VBRHeadID As String * 4 = "Xing" ' VBR (Variable Bit-Rate) header ID Private Const ID3v2HeadID As String * 3 = "ID3" ' ID3v2 header ID Private Const ID3v2FootID As String * 3 = "3DI" ' ID3v2 footer ID Private Const ID3v2Sig As Long = &H334449 ' ASCII "ID3" Private Const NumGenre As Long = &H95 Private Const GENERIC_WRITE As Long = &H40000000 Private Const OPEN_EXISTING As Long = &H3 Private Const FILE_SHARE_READ As Long = &H1 Private Const FILE_SHARE_WRITE As Long = &H2 ' Public interface to member variables Public Property Get Filename() As String Filename = m_FileName End Property Public Property Let Filename(ByVal inNew As String) m_FileName = inNew Call LoadMP3 End Property Public Property Get ValidMP3() As Boolean ValidMP3 = m_ValidMP3 End Property Public Property Get Padding() As Byte Padding = m_Padding End Property Public Property Get GenreCount() As Long GenreCount = NumGenre End Property Public Property Get FrameLength() As Long FrameLength = m_FrameLength End Property Public Property Get ID3Present() As Boolean ID3Present = m_ID3Present End Property Public Property Get ID3v2Present() As Boolean ID3v2Present = m_ID3v2Present End Property Public Property Get ID3v2Size() As Long ID3v2Size = m_ID3v2Size End Property Public Property Get ID3v2Offset() As Long ID3v2Offset = m_ID3v2Offset End Property Public Property Get FileSize() As Long FileSize = m_FileSize End Property Public Property Get BitRate() As Long BitRate = m_BitRate End Property Public Property Get Frequency() As Long Frequency = m_Frequency End Property Public Property Get SongLength() As Long SongLength = m_SongLength End Property Public Property Get FormattedTime() As String ' Format track time into HH:MM:SS (Or MM:SS) FormattedTime = Format$(CDate(m_SongLength / 86400), IIf(m_SongLength > 3600, "h:nn:ss", "nn:ss")) End Property Public Property Get NumFrames() As Long NumFrames = m_NumFrames End Property Public Property Get isVBR() As Boolean isVBR = m_VBR End Property Public Property Get MPEGVer() As Single MPEGVer = m_MPEGVer End Property Public Property Get Layers() As Byte Layers = m_Layers End Property Public Property Get Protected() As Boolean Protected = m_Protected End Property Public Property Get PrivateBit() As Boolean PrivateBit = m_Private End Property Public Property Get ChannelMode() As String ChannelMode = m_ChannelMode End Property Public Property Get Copyrighted() As Boolean Copyrighted = m_Copyrighted End Property Public Property Get Original() As Boolean Original = m_Original End Property Public Property Get HeaderOffset() As Long HeaderOffset = m_HeaderOffset End Property Public Property Get Emphasis() As String Emphasis = m_Emphasis End Property Public Property Get Title() As String Title = TrimNull(m_Tag.tagTitle) End Property Public Property Let Title(ByVal inNew As String) m_Tag.tagTitle = MakeID3String(inNew, 30) End Property Public Property Get Artist() As String Artist = TrimNull(m_Tag.tagArtist) End Property Public Property Let Artist(ByVal inNew As String) m_Tag.tagArtist = MakeID3String(inNew, 30) End Property Public Property Get Album() As String Album = TrimNull(m_Tag.tagAlbum) End Property Public Property Let Album(ByVal inNew As String) m_Tag.tagAlbum = MakeID3String(inNew, 30) End Property Public Property Get Year() As String Year = TrimNull(m_Tag.tagYear) End Property Public Property Let Year(ByVal inNew As String) If (Not (Left$(inNew, 4) Like "*[!0123456789]*")) Then _ m_Tag.tagYear = MakeID3String(inNew, 4) ' Validate as numeric only End Property Public Property Get Comment() As String Comment = TrimNull(m_Tag.tagComments) End Property Public Property Let Comment(ByVal inNew As String) ' We'll store the full 30-char string, but the last two ' characters will be overwritten if a track number is set m_Tag.tagComments = MakeID3String(inNew, 30) End Property Public Property Get GenreID() As Byte GenreID = m_Tag.tagGenre End Property Public Property Let GenreID(ByVal inNew As Byte) m_Tag.tagGenre = inNew End Property Public Property Get TrackNum() As Byte TrackNum = m_Tag.tagTrackNum End Property Public Property Let TrackNum(ByVal inNew As Byte) m_Tag.tagTrackNum = inNew m_Tag.tagID311 = (inNew <> &H0) End Property Public Property Get GenreString() As String GenreString = GetGenreString(m_Tag.tagGenre) End Property ' Class event handlers Private Sub Class_Initialize() m_Tag.tagID = ID3TagID HeaderTest = True ' By default, enable the header test End Sub ' Public subs & functions Public Function IDFromGenreString(ByVal inGenreString As String) As Byte Dim TrimGenre As String Dim LoopGenre As Long TrimGenre = Trim$(inGenreString) For LoopGenre = 0 To NumGenre ' Search known genre's for the given genre string If (StrComp(TrimGenre, GetGenreString(LoopGenre), vbTextCompare) = 0) Then Exit For Next LoopGenre If (LoopGenre > NumGenre) Then _ IDFromGenreString = &HFF Else _ IDFromGenreString = LoopGenre End Function Public Function RemovePreHeader(Optional ByVal inRetainDate As Boolean = True) As Boolean Dim FBuffer() As Byte, BufLen As Long Dim FNum As Long, OldDate As Date Dim OrigTimes As typFileTimes If (FileExist(m_FileName) And (m_HeaderOffset <> 0)) Then If (Not (FileLen(m_FileName)) = m_FileSize) Then Me.Filename = m_FileName ' File has changed since we first read it, re-parse If (m_HeaderOffset = 0) Then Exit Function End If If (inRetainDate) Then inRetainDate = GetFileTimes(m_FileName, OrigTimes) BufLen = m_FileSize - m_HeaderOffset If (BufLen < 1) Then Exit Function ReDim FBuffer(BufLen - 1) As Byte FNum = FreeFile() ' Open & read in the file Open m_FileName For Binary Access Read Lock Read Write As #FNum Seek #FNum, m_HeaderOffset + 1 ' Seek past header Get #FNum, , FBuffer() ' Read file data Close #FNum ' Delete the old file Call Kill(m_FileName) FNum = FreeFile() Open m_FileName For Binary Access Write Lock Read Write As #FNum Put #FNum, , FBuffer() ' Write new file data Close #FNum If (m_ID3v2Present And (m_ID3v2Offset = 0)) Then m_ID3v2Present = False m_ID3v2Offset = 0 m_ID3v2Size = 0 End If If (inRetainDate) Then Call SetFileTimes(m_FileName, OrigTimes) RemovePreHeader = True End If End Function Public Function RemoveTag(Optional ByVal inRetainDate As Boolean = True) As Boolean Dim FNum As Integer Dim FBuffer() As Byte, BufLen As Long Dim OrigTimes As typFileTimes If (Not (m_ID3Present Or FileExist(m_FileName))) Then Exit Function If (FileLen(m_FileName) <> m_FileSize) Then Me.Filename = m_FileName ' File has changed since we last read it, re-parse If (Not m_ID3Present) Then Exit Function End If If (inRetainDate) Then inRetainDate = GetFileTimes(m_FileName, OrigTimes) BufLen = m_FileSize - 128 If (BufLen < 1) Then Exit Function ReDim FBuffer(BufLen - 1) As Byte FNum = FreeFile() Open m_FileName For Binary Access Read Lock Read Write As #FNum Get #FNum, , FBuffer() ' Read current file excluding ID3 tag Close #FNum Call Kill(m_FileName) ' Delete old file FNum = FreeFile() Open m_FileName For Binary Access Write Lock Read Write As #FNum Put #FNum, , FBuffer() ' Write new file data Close #FNum m_ID3Present = False If (inRetainDate) Then Call SetFileTimes(m_FileName, OrigTimes) RemoveTag = True End Function Public Function WriteTag(Optional ByVal inRetainDate As Boolean = True) As Boolean Dim FNum As Long Dim TagBuf(127) As Byte Dim OrigTimes As typFileTimes ' Copy first 128 bytes of tag into buffer Call RtlMoveMemory(TagBuf(0), m_Tag, 128) If (m_Tag.tagID311) Then ' Append track number if ID3 v1.1 TagBuf(125) = &H0 ' Clip comment string to 28 bytes TagBuf(126) = m_Tag.tagTrackNum ' Inject track number End If If (inRetainDate) Then inRetainDate = GetFileTimes(m_FileName, OrigTimes) FNum = FreeFile() ' Open & find the end of file Open m_FileName For Binary Access Write Lock Read Write As FNum Seek FNum, LOF(FNum) + IIf(m_ID3Present, -127, 1) Put #FNum, , TagBuf() ' Write the tag Close FNum m_ID3Present = True If (inRetainDate) Then Call SetFileTimes(m_FileName, OrigTimes) WriteTag = True End Function ' Private routines Private Function LoadMP3() As Boolean Dim FNum As Integer Dim ReadPos As Long Dim GetHeader As Long Dim VBRHeader(11) As Byte Dim CheckChars() As Byte Dim NoHeader As Boolean Dim ReadID3v2 As typID3v2Inf Call ClearInfo(True) FNum = FreeFile() Open m_FileName For Binary Access Read Lock Write As FNum m_FileSize = LOF(FNum) If (m_FileSize > 128) Then ' Read in the last 128 bytes of the file Get #FNum, m_FileSize - 127, m_Tag m_ID3Present = (m_Tag.tagID = ID3TagID) ' Check for ID3 tag ID If (m_ID3Present) Then ' Check to see if this looks like an ID3 v1.1 tag CheckChars() = StrConv(Right$(m_Tag.tagComments, 2), vbFromUnicode) m_Tag.tagID311 = (CheckChars(0) = &H0) And (CheckChars(1) > &H0) m_Tag.tagTrackNum = IIf(m_Tag.tagID311, CheckChars(1), &H0) Else: Call ClearTag End If End If ' Read first 10 bytes to check for ID3v2 header Seek #FNum, 1 Get #FNum, , ReadID3v2 ' Validate tag header ID m_ID3v2Present = (ReadID3v2.iiID = ID3v2HeadID) If (Not m_ID3v2Present) Then ' No ID3v2 tag found at the start of the file, have a look for the ID3v2.4+ footer at the end m_ID3v2Offset = m_FileSize - IIf(m_ID3Present, 128, 0) - 9 If (m_ID3v2Offset > 1) Then Seek #FNum, m_ID3v2Offset Get #FNum, , ReadID3v2 m_ID3v2Present = (ReadID3v2.iiID = ID3v2FootID) End If If (m_ID3v2Present) Then m_ID3v2Offset = Seek(FNum) - 1 Else m_ID3v2Offset = 0 End If If (m_ID3v2Present) Then ' Extract size of full ID3v2 tag - Aware of additional footer size defined in ID3v2.4 m_ID3v2Size = DeSyncSafeDWord(FlipDWord(ReadID3v2.iiSize)) + IIf(ReadID3v2.iiFlags And &H10, 20, 10) If (m_ID3v2Offset = 0) Then ' Tag precedes audio data, offset read start position ReadPos = m_ID3v2Size Else ' Tag follows audio data, project tag start position m_ID3v2Offset = m_ID3v2Offset - m_ID3v2Size End If End If Do ' Scan until a valid header is found Do ' Scan through the file looking for possible headers ReadPos = ReadPos + 1 Seek #FNum, ReadPos Get #FNum, , GetHeader If (ReadPos >= (m_FileSize - 5)) Then NoHeader = True Exit Do End If If ((Not m_ID3v2Present) And HeaderTest) Then ' Check for embedded ID3v2 tag before the frame header If ((GetHeader And &HFFFFFF) = ID3v2Sig) Then ' Found what looks like an ID3v2 pre-header Seek #FNum, ReadPos Get #FNum, , ReadID3v2 If ((ReadID3v2.iiVersion < &HFF) And (ReadID3v2.iiSize And &H80808080) = 0&) Then ' Look's valid m_ID3v2Present = True m_ID3v2Offset = ReadPos - 1 m_ID3v2Size = DeSyncSafeDWord(FlipDWord(ReadID3v2.iiSize)) + _ IIf(ReadID3v2.iiFlags And &H10, 20, 10) ReadPos = ReadPos + m_ID3v2Size - 1 End If End If End If Loop Until IsHeader(GetHeader) Loop Until ValidHeader(GetHeader, ReadPos) Or NoHeader If (NoHeader) Then Call ClearInfo Else ' Read in variable bitrate header If (ReadPos < (m_FileSize - 48)) Then Seek #FNum, ReadPos + 36 Get #FNum, , VBRHeader() End If End If Close #FNum If (NoHeader) Then Exit Function m_VBR = Left$(StrConv(VBRHeader(), vbUnicode), 4) = VBRHeadID If (m_VBR) Then m_ValidMP3 = (VBRHeader(7) And &H1) = &H1 If (m_ValidMP3) Then ' FRAMES_FLAG m_NumFrames = ((VBRHeader(8) And &H7F) * &H1000000) Or (VBRHeader(9) * &H10000) Or (VBRHeader(10) * &H100) Or VBRHeader(11) If (VBRHeader(8) And &H80) Then m_NumFrames = Not ((Not m_NumFrames) And &H7FFFFFFF) m_BitRate = (((m_FileSize / m_NumFrames) * m_Frequency) / (1000 * IIf(((GetHeader And &H1800) \ &H800) = &H3, 12, 144))) \ 12 m_SongLength = ((8 * m_ActSize) \ 1000) \ m_BitRate End If End If LoadMP3 = True End Function Private Function IsHeader(ByRef inHead As Long) As Boolean ' Tests for MPEG header sync information IsHeader = (inHead And FrameSync) = FrameSync End Function Private Function ValidHeader(ByVal inHead As Long, ByVal ReadPos As Long) As Boolean Dim HeadVals(11) As Byte Dim FlipHead As Long ' 32-bit MP3 frame header construction: ' 87654321 87654321 87654321 87654321 ' AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM ' Test for valid sync. bits pattern (A) If (Not IsHeader(inHead)) Then Exit Function ' Header byte order must be flipped, since it was interpreted as a ' little endian DWord - Remove frame sync. to avoid sign issues FlipHead = FlipDWord(inHead And Not FrameSync) HeadVals(0) = (FlipHead And &H180000) \ &H80000 ' MPEG version (B) HeadVals(1) = (FlipHead And &H60000) \ &H20000 ' Layer description (C) HeadVals(2) = (FlipHead And &H10000) \ &H10000 ' Protection bit (D) HeadVals(3) = (FlipHead And &HF000&) \ &H1000& ' Bit-rate index (E) HeadVals(4) = (FlipHead And &HC00&) \ &H400& ' Sample rate (F) HeadVals(5) = (FlipHead And &H200&) \ &H200& ' Padding bit (G) HeadVals(6) = (FlipHead And &H100&) \ &H100& ' Private bit (H) HeadVals(7) = (FlipHead And &HC0&) \ &H40& ' Channel mode (I) HeadVals(8) = (FlipHead And &H30&) \ &H10& ' Mode extension (J) HeadVals(9) = (FlipHead And &H8&) \ &H8& ' Copyright bit (K) HeadVals(10) = (FlipHead And &H4&) \ &H4& ' Original bit (L) HeadVals(11) = FlipHead And &H3& ' Emphasis (M) Select Case HeadVals(0) ' Select frequency based on MPEG version Case 0: m_Frequency = Choose(HeadVals(4) + 1, 11025, 12000, 8000, 0) ' MPEG 2.5 Case 1: m_Frequency = 0 ' Reserved Case 2: m_Frequency = Choose(HeadVals(4) + 1, 22050, 24000, 26000, 0) ' MPEG 2.0 Case 3: m_Frequency = Choose(HeadVals(4) + 1, 44100, 48000, 32000, 0) ' MPEG 1.0 End Select If (m_Frequency = 0) Then Exit Function If (HeadVals(0) = &H3) Then ' MPEG 1.0 Select Case HeadVals(1) Case &H3: m_BitRate = Choose(HeadVals(3) + 1, 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0) Case &H2: m_BitRate = Choose(HeadVals(3) + 1, 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 284, 0) Case &H1: m_BitRate = Choose(HeadVals(3) + 1, 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0) End Select Else ' MPEG 2.0, 2.5 or undefined Select Case HeadVals(1) Case &H3: m_BitRate = Choose(HeadVals(3) + 1, 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0) Case &H2, &H1: m_BitRate = Choose(HeadVals(3) + 1, 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0) End Select End If If (m_BitRate = 0) Then Exit Function m_ActSize = m_FileSize - ReadPos - IIf(m_ID3Present, &H80, &H0) m_NumFrames = m_ActSize \ ((m_BitRate * 144000) \ m_Frequency) m_HeaderOffset = ReadPos - 1 m_SongLength = ((8 * m_ActSize) \ 1000) \ m_BitRate m_MPEGVer = Choose(HeadVals(0) + 1, 2.5, 0, 2, 1) m_Layers = Choose(HeadVals(1) + 1, 0, 3, 2, 1) If ((m_MPEGVer = 0) Or (m_Layers = 0)) Then Exit Function m_Padding = HeadVals(5) m_Private = HeadVals(6) If (m_Layers = 1) Then _ m_FrameLength = (12 * (m_BitRate * 1000&) \ m_Frequency + m_Padding) * 4 Else _ m_FrameLength = (144 * (m_BitRate * 1000&) \ m_Frequency) + m_Padding m_ChannelMode = Choose(HeadVals(7) + 1, "Stereo", "Joint stereo", "Dual channel", "Single channel") m_Protected = HeadVals(2) = &H0 m_Copyrighted = HeadVals(9) = &H1 m_Original = HeadVals(10) = &H1 m_Emphasis = Choose(HeadVals(11) + 1, "None", "50/15 ms", "Reserved", "CCIT j.17") m_ValidMP3 = True ValidHeader = True End Function Private Sub ClearInfo(Optional ByVal inLeaveFilename As Boolean = False) ' Clears out all current information m_MPEGVer = 0 m_Layers = 0 m_Protected = False m_BitRate = 0 m_Frequency = 0 m_ChannelMode = "" m_Copyrighted = False m_Original = False m_ValidMP3 = False m_FileSize = 0 m_SongLength = 0 m_NumFrames = 0 m_VBR = False m_HeaderOffset = 0 m_Emphasis = "" m_ID3Present = False m_Padding = 0 m_FrameLength = 0 m_ActSize = 0 m_ID3v2Present = False m_ID3v2Offset = 0 m_ID3v2Size = 0 If (Not (inLeaveFilename)) Then m_FileName = "" Call ClearTag End Sub Private Function GetFileTimes(ByVal inFile As String, ByRef outFileTimes As typFileTimes) As Boolean Dim hFile As Long hFile = CreateFile(inFile, GENERIC_WRITE, FILE_SHARE_READ, ByVal 0&, OPEN_EXISTING, 0, 0) GetFileTimes = CBool(hFile) If (GetFileTimes) Then Call GetFileTime(hFile, outFileTimes.ftCreationTime, _ outFileTimes.ftLastAccessTime, outFileTimes.ftLastWriteTime) Call CloseHandle(hFile) End Function Private Function SetFileTimes(ByVal inFile As String, ByRef inFileTimes As typFileTimes) As Boolean Dim hFile As Long hFile = CreateFile(inFile, GENERIC_WRITE, FILE_SHARE_READ, ByVal 0&, OPEN_EXISTING, 0, 0) SetFileTimes = CBool(hFile) If (SetFileTimes) Then Call SetFileTime(hFile, inFileTimes.ftCreationTime, _ inFileTimes.ftLastAccessTime, inFileTimes.ftLastWriteTime) Call CloseHandle(hFile) End Function Private Sub ClearTag() Dim BlankStruct As ID3Tag m_Tag = BlankStruct ' Clear the internal ID3 tag m_Tag.tagID = ID3TagID ' Re-set the tag header End Sub Private Function TrimNull(ByVal inString As String) As String Dim NullPos As Long NullPos = InStr(inString, vbNullChar) If (NullPos) Then TrimNull = Left$(inString, NullPos - 1) Else TrimNull = inString End Function Private Function Min(ByVal inA As Long, ByVal inB As Long) As Long Min = IIf(inA < inB, inA, inB) End Function Private Function FileExist(ByVal inFile As String) As Boolean On Error Resume Next FileExist = CBool(FileLen(inFile) + 1) End Function Private Function MakeID3String(ByVal inString As String, ByVal inLength As Long) As String Dim TrimLength As Long TrimLength = Min(Len(TrimNull(inString)), inLength) MakeID3String = Left$(inString, TrimLength) & String$(inLength - TrimLength, vbNullChar) End Function Private Function FlipDWord(ByVal inDWord As Long) As Long FlipDWord = (((inDWord And &HFF000000) \ &H1000000) And &HFF) Or _ (((inDWord And &HFF0000) \ &H10000) * &H100) Or _ (((inDWord And &HFF00&) \ &H100) * &H10000) Or _ ((inDWord And &H7F) * &H1000000) If (inDWord And &H80) Then FlipDWord = Not ((Not FlipDWord) And &H7FFFFFFF) End Function ' Sync-safe integer conversion routines based on ID3v2 specifications section 6.2 Private Function SyncSafeDWord(ByVal inDWord As Long) As Long Dim UseDWord As Long UseDWord = inDWord And &HFFFFFFF ' Mask low 28-bits and shift bytes SyncSafeDWord = (UseDWord And &H7F) Or ((UseDWord And &H3F80) * &H2) Or _ ((UseDWord And &H1FC000) * &H4) Or ((UseDWord And &HFE00000) * &H8) End Function Private Function SyncSafeWord(ByVal inWord As Integer) As Integer Dim UseWord As Long UseWord = inWord And &H3FFF ' Mask low 14-bits and shift bytes SyncSafeWord = (UseWord And &H7F) Or ((UseWord And &H3F80) * &H2) End Function Private Function SyncSafeByte(ByVal inByte As Byte) As Byte SyncSafeByte = inByte And &H7F ' Mask low 7-bits End Function Private Function DeSyncSafeDWord(ByVal inDWord As Long) As Long DeSyncSafeDWord = (inDWord And &H7F) Or ((inDWord And &H7F00) \ &H2) Or _ ((inDWord And &H7F0000) \ &H4) Or ((inDWord And &H7F000000) \ &H8) End Function Private Function DeSyncSafeWord(ByVal inWord As Integer) As Integer DeSyncSafeWord = (inWord And &H7F) Or ((inWord And &H7F00) \ &H2) End Function Private Function DeSyncSafeByte(ByVal inByte As Byte) As Byte DeSyncSafeByte = inByte And &H7F End Function Private Function GetGenreString(ByVal inGenre As Byte) As String Select Case inGenre ' Extracted from in_mp3.dll which ships with NullSoft's WinAMP v2.91 Case &H0: GetGenreString = "Blues" Case &H1: GetGenreString = "Classic Rock" Case &H2: GetGenreString = "Country" Case &H3: GetGenreString = "Dance" Case &H4: GetGenreString = "Disco" Case &H5: GetGenreString = "Funk" Case &H6: GetGenreString = "Grunge" Case &H7: GetGenreString = "Hip-Hop" Case &H8: GetGenreString = "Jazz" Case &H9: GetGenreString = "Metal" Case &HA: GetGenreString = "New Age" Case &HB: GetGenreString = "Oldies" Case &HC: GetGenreString = "Other" Case &HD: GetGenreString = "Pop" Case &HE: GetGenreString = "R&B" Case &HF: GetGenreString = "Rap" Case &H10: GetGenreString = "Reggae" Case &H11: GetGenreString = "Rock" Case &H12: GetGenreString = "Techno" Case &H13: GetGenreString = "Industrial" Case &H14: GetGenreString = "Alternative" Case &H15: GetGenreString = "Ska" Case &H16: GetGenreString = "Death Metal" Case &H17: GetGenreString = "Pranks" Case &H18: GetGenreString = "Soundtrack" Case &H19: GetGenreString = "Euro-Techno" Case &H1A: GetGenreString = "Ambient" Case &H1B: GetGenreString = "Trip-Hop" Case &H1C: GetGenreString = "Vocal" Case &H1D: GetGenreString = "Jazz+Funk" Case &H1E: GetGenreString = "Fusion" Case &H1F: GetGenreString = "Trance" Case &H20: GetGenreString = "Classical" Case &H21: GetGenreString = "Instrumental" Case &H22: GetGenreString = "Acid" Case &H23: GetGenreString = "House" Case &H24: GetGenreString = "Game" Case &H25: GetGenreString = "Sound Clip" Case &H26: GetGenreString = "Gospel" Case &H27: GetGenreString = "Noise" Case &H28: GetGenreString = "Rock" Case &H29: GetGenreString = "Alt" Case &H2A: GetGenreString = "Bass" Case &H2B: GetGenreString = "Soul" Case &H2C: GetGenreString = "Punk" Case &H2D: GetGenreString = "Space" Case &H2E: GetGenreString = "Meditative" Case &H2F: GetGenreString = "Instrumental Pop" Case &H30: GetGenreString = "Instrumental Rock" Case &H31: GetGenreString = "Ethnic" Case &H32: GetGenreString = "Gothic" Case &H33: GetGenreString = "Darkwave" Case &H34: GetGenreString = "Techno-Industrial" Case &H35: GetGenreString = "Electronic" Case &H36: GetGenreString = "Pop-Folk" Case &H37: GetGenreString = "Eurodance" Case &H38: GetGenreString = "Dream" Case &H39: GetGenreString = "Southern Rock" Case &H3A: GetGenreString = "Comedy" Case &H3B: GetGenreString = "Cult" Case &H3C: GetGenreString = "Gangsta Rap" Case &H3D: GetGenreString = "Top 40" Case &H3E: GetGenreString = "Christian Rap" Case &H3F: GetGenreString = "Pop/Funk" Case &H40: GetGenreString = "Jungle" Case &H41: GetGenreString = "Native American" Case &H42: GetGenreString = "Cabaret" Case &H43: GetGenreString = "New Wave" Case &H44: GetGenreString = "Psychedelic" Case &H45: GetGenreString = "Rave" Case &H46: GetGenreString = "Showtunes" Case &H47: GetGenreString = "Trailer" Case &H48: GetGenreString = "Lo-Fi" Case &H49: GetGenreString = "Tribal" Case &H4A: GetGenreString = "Acid Punk" Case &H4B: GetGenreString = "Acid Jazz" Case &H4C: GetGenreString = "Polka" Case &H4D: GetGenreString = "Retro" Case &H4E: GetGenreString = "Musical" Case &H4F: GetGenreString = "Rock & Roll" ' Genre's added by Nullsoft; may be interpreted differently by other applications.. Case &H50: GetGenreString = "Hard Rock" Case &H51: GetGenreString = "Folk" Case &H52: GetGenreString = "Folk/Rock" Case &H53: GetGenreString = "National Folk" Case &H54: GetGenreString = "Swing" Case &H55: GetGenreString = "Fast-Fusion" Case &H56: GetGenreString = "Bebob" Case &H57: GetGenreString = "Latin" Case &H58: GetGenreString = "Revival" Case &H59: GetGenreString = "Celtic" Case &H5A: GetGenreString = "Bluegrass" Case &H5B: GetGenreString = "Avantgarde" Case &H5C: GetGenreString = "Gothic Rock" Case &H5D: GetGenreString = "Progressive Rock" Case &H5E: GetGenreString = "Psychedelic Rock" Case &H5F: GetGenreString = "Symphonic Rock" Case &H60: GetGenreString = "Slow Rock" Case &H61: GetGenreString = "Big Band" Case &H62: GetGenreString = "Chorus" Case &H63: GetGenreString = "Easy Listening" Case &H64: GetGenreString = "Acoustic" Case &H65: GetGenreString = "Humour" Case &H66: GetGenreString = "Speech" Case &H67: GetGenreString = "Chanson" Case &H68: GetGenreString = "Opera" Case &H69: GetGenreString = "Chamber Music" Case &H6A: GetGenreString = "Sonata" Case &H6B: GetGenreString = "Symphony" Case &H6C: GetGenreString = "Booty Bass" Case &H6D: GetGenreString = "Primus" Case &H6E: GetGenreString = "Porn Groove" Case &H6F: GetGenreString = "Satire" Case &H70: GetGenreString = "Slow Jam" Case &H71: GetGenreString = "Club" Case &H72: GetGenreString = "Tango" Case &H73: GetGenreString = "Samba" Case &H74: GetGenreString = "Folklore" Case &H75: GetGenreString = "Ballad" Case &H76: GetGenreString = "Power Ballad" Case &H77: GetGenreString = "Rhythmic Soul" Case &H78: GetGenreString = "Freestyle" Case &H79: GetGenreString = "Duet" Case &H7A: GetGenreString = "Punk Rock" Case &H7B: GetGenreString = "Drum Solo" Case &H7C: GetGenreString = "A Cappella" Case &H7D: GetGenreString = "Euro-House" Case &H7E: GetGenreString = "Dance Hall" Case &H7F: GetGenreString = "Goa" Case &H80: GetGenreString = "Drum & Bass" Case &H81: GetGenreString = "Club-House" Case &H82: GetGenreString = "Hardcore" Case &H83: GetGenreString = "Terror" Case &H84: GetGenreString = "Indie" Case &H85: GetGenreString = "BritPop" Case &H86: GetGenreString = "Negerpunk" Case &H87: GetGenreString = "Polsk Punk" Case &H88: GetGenreString = "Beat" Case &H89: GetGenreString = "Christian Gangsta Rap" Case &H8A: GetGenreString = "Heavy Metal" Case &H8B: GetGenreString = "Black Metal" Case &H8C: GetGenreString = "Crossover" Case &H8D: GetGenreString = "Contemporary Christian" Case &H8E: GetGenreString = "Christian Rock" Case &H8F: GetGenreString = "Merengue" Case &H90: GetGenreString = "Salsa" Case &H91: GetGenreString = "Thrash Metal" Case &H92: GetGenreString = "Anime" Case &H93: GetGenreString = "JPop" Case &H94: GetGenreString = "Synthpop" Case Else: GetGenreString = vbNullString End Select End Function