Hi,
I'm working on an application which will read the ID3 tags of my MP3 files, and sort them in directories after Artist/Album etc.
ID3v1 was easy to understand, but ID3v2 tags are alot harder and I'm currently stuck.. So I'd appritiate some help with this :)
Here's my current code:
And here's the documentation I've been using:
http://www.id3.org/id3v2.4.0-structure
http://en.wikipedia.org/wiki/Synchsafe
Currently it does read some information from the MP3 ID3v2 tag, but it skips ALOT for some reason, Either it's the UnSynch() function that doesn't work as intented, or there's some other bug in the code :P
Change the FromDir to a diretory where you have some MP3s ;P
Thanks :)
I'm working on an application which will read the ID3 tags of my MP3 files, and sort them in directories after Artist/Album etc.
ID3v1 was easy to understand, but ID3v2 tags are alot harder and I'm currently stuck.. So I'd appritiate some help with this :)
Here's my current code:
Framework BRL.FileSystem Import BRL.StandardIO Import BRL.Retro Import BRL.EndianStream Global FromDir:String = "CHANGE ME!" Global ToDir:String = "Not in use yet." Global ErrorList:TList = CreateList() Print "" SortDirectory( FromDir ) Print "" Print "Sorted '"+FromDir+"' with "+CountList(ErrorList)+" errors." Print "## Printing Errors ##" For Local A:String = EachIn ErrorList Print A Next Function SortDirectory( Dir:String ) Print "! DIRECTORY ! ("+Dir+")" Local SortDir = ReadDir( Dir ) Repeat Local ObjName:String = NextFile( SortDir ) Select ObjName Case "" Exit Case "." Case ".." Default Select FileType( Dir+ObjName ) Case FILETYPE_FILE If Upper(Right( ObjName, 3 )) = "MP3" ProcessMP3File( Dir+ObjName ) EndIf Case FILETYPE_DIR SortDirectory( Dir+ObjName+"" ) Default End Select End Select Forever End Function Function ProcessMP3File( FileDir:String ) Print "" Print "## FILE START ## '"+FileDir+"'." 'If ReadID3v1Tag( FileDir ) = "" ReadID3v2Tag( FileDir ) 'EndIf Print "## FILE END ##" Print "" End Function Function ReadID3v1Tag:String( FileName:String ) Local Id3Tag:String = "" Local Artist:String Local Album:String Local Title:String Local File:TStream = ReadFile( FileName ) SeekStream( File, StreamSize( File )-128) If ReadPart( File, 3 ) = "TAG" Title = Trim(ReadPart( File, 30)) Artist = Trim(ReadPart( File, 30)) Album = Trim(ReadPart( File, 30)) Id3Tag = Artist + "|" + Album + "|" + Title Print "ID3v1: " + Artist + " => " + Album + " => " + Title EndIf CloseFile( File ) Return Id3Tag End Function Function ReadID3v2Tag:String( FileName:String ) Local Artist:String Local Album:String Local Title:String Local Id3Tag:String = "" Local Id3Version:Int = 0 Local Id3Flags:Int = 0 Local Id3Size:Int = 0 '################################################################################################# Local File:TStream = BigEndianStream( ReadFile( FileName ) ) '############################################################################################ 'ID3v2 Header If Upper(ReadPart( File, 3 )) = "ID3" Print "ID3v2" Id3Version = Int(ReadPart( File, 2, False )) Print "ID3v2 Version: "+Id3Version Id3Flags = Int( Bin( ReadPartInt( File, 1 ) ) ) Print "ID3v2 Flags: "+Id3Flags Id3Size = UnSynch( ReadPartInt( File, 4 ) ) Print "ID3v2 Size: " + Id3Size '################################################################################################# 'ID3v2 Header Flags Select Id3Flags Case 0'NO FLAGS Case 1000000 Print "Unsynchronisation Not Supported!" Return "" Case 100000 Print "Extended Header Not Supported!" Return "" Case 10000 Print "Experimental Indicator Not Supported!" Return "" Default Print "HeaderFlag Not Supported!" ' Return "" End Select '################################################################################################# 'Loop Through Frames Local FrameID:String Local FrameSize:Int Local FrameFlags:Int Repeat If Id3Version = 30 FrameID = ReadPart( File, 4 ) ' Print "FrameID: "+FrameID 'FrameSize = UnSynch( Int(ReadPart( File, 4, False )) ) FrameSize = UnSynch( ReadPartInt( File, 4 ) ) Print "FrameSize: "+FrameSize FrameFlags = Int(ReadPart( File, 2, 0 )) Print "FrameFlags: "+FrameFlags Select Trim(FrameID) Case "TIT2"'Title Print "Title: "+ReadPart( File, FrameSize ) Case "TPE2", "TPE1", "TP1"'Artist Print "Artist: "+ReadPart( File, FrameSize ) Case "TALB"'Album Print "Album: "+ReadPart( File, FrameSize ) Default Print "Unknown FrameID: "+FrameID Print ReadPart( File, FrameSize ) End Select If StreamPos( File ) => Id3Size Exit EndIf Else Exit EndIf Forever '################################################################################################# EndIf CloseFile( File ) Return Id3Tag End Function Function UnSynch( In:Int ) Local Out:Int, I:Int Local Mask:Int = Int("$7F000000") For I = 0 Until 4 Out = Out Shr 1 Out :| ( In & Mask ) Mask :Shr 8 Next Return Out End Function Function ReadPart:String( Stream:TStream, Size:Int, Char:Int = 1 ) Local Str:String = "" For Local X:Int = 1 To Size If Char = 1 Str = Str + Chr(ReadByte(Stream)) Else Str = Str + ReadByte(Stream) EndIf Next Return Str End Function Function ReadPartInt:Int( Stream:TStream, Size:Int ) Local I:Int = 0 For Local X:Int = 1 To Size I = I + Int(ReadByte(Stream)) Next Return I End Function
And here's the documentation I've been using:
http://www.id3.org/id3v2.4.0-structure
http://en.wikipedia.org/wiki/Synchsafe
Currently it does read some information from the MP3 ID3v2 tag, but it skips ALOT for some reason, Either it's the UnSynch() function that doesn't work as intented, or there's some other bug in the code :P
Change the FromDir to a diretory where you have some MP3s ;P
Thanks :)