B4A Library [Class] ID3 - Reads tags and images from mp3 files

I could not find a b4a library, which is able to extract images from mp3 files, and so I had to write my own class in pure b4a.
(My java knowledge is too limited to write a b4a wrapper for an existing library)

With this class it should be easy to read all metadata(Artist, Title etc.) and all attached images(Cover Art and other pictures)from a mp3 file.

Note that i have tested this class with my own mp3 files collection but it is not bullet-proof, there are a lot of different id3v2 versions and a lot of mp3 files containing "garbage" in the id3v2 tag.

This is an example for extracting the cover image:

B4X:
Dim ID3 as ID3


ID3.Initialize(myDir, myFile)
If ID3.hasPictures then
   Dim bmp as Bitmap
   bmp = ID3.Cover
   '...do something with the cover bitmap
End if

This is an example for extracting all available metadata into a Listview:

B4X:
Sub ReadID3Tag (Dir As String, Filename As String)
   Dim i       As Int
   Dim ID3     As ID3
   Dim Frame   As ID3_Frame
   Dim Pic     As ID3_Picture
   Dim Comment As ID3_Comment
   Dim Other   As ID3_OtherData
    '   
   ID3.Initialize(myFolder, myFile)
   If ID3.Exists = False Then lv.AddTwoLines(myFile, "no ID3 Tag found.")
      
   If ID3.version >= 3 Then 'id3v2.2 not supported
       
      If ID3.hasPictures Then lv.AddTwoLinesAndBitmap(myFile, "", ID3.Cover)
      
      lv.AddTwoLines("Title",             ID3.Title)
      lv.AddTwoLines("Artist",            ID3.Artist)
      lv.AddTwoLines("Album",             ID3.Album)
       lv.AddTwoLines("Genre",             ID3.Genre)
      lv.AddTwoLines("Track",             ID3.Track)
      lv.AddTwoLines("Comment",           ID3.Comment)
      lv.AddTwoLines("Copyright",         ID3.Copyright)
      lv.AddTwoLines("BPM",             ID3.bpm)
      
      'Get all attached pictures: 
       For i = 0 To ID3.Pictures.Size-1
           Pic = ID3.Pictures.Get(ID3.Pictures.GetKeyAt(i))
          lv.AddTwoLinesAndBitmap(Pic.Label, Pic.Description,ID3.GetBitmap(Pic.Picuretype))
      Next
      
      'Get all standard text information frames:
      For i = 0 To ID3.Frames.Size-1
           Frame = ID3.Frames.Get(ID3.Frames.GetKeyAt(i))
          lv.AddTwoLines(Frame.Name & " - " & Frame.label, Frame.Content)
       Next
      
      'Get all comments:
      For i = 0 To ID3.Comments.Size-1
           Comment = ID3.Comments.Get(ID3.Comments.GetKeyAt(i))
          lv.AddTwoLines(Comment.Label & " " & Comment.Language & " " & Comment.Description,Comment.Content)
       Next
      
      'Get all other frames:
      For i = 0 To ID3.OtherData.Size-1
           Other = ID3.otherdata.Get(ID3.otherdata.GetKeyAt(i))
          lv.AddTwoLines(Other.Name & " - " & Other.Label, Other.Content)
       Next
      lv.AddTwoLines("Version", "id3v2." & ID3.Version & "." & ID3.Revision)
      
   
   Else
   
       ID3.ReadVersion1
      lv.AddTwoLines("Title",    ID3.V1.Title)
      lv.AddTwoLines("Artist",   ID3.V1.Artist)
      lv.AddTwoLines("Album",    ID3.V1.Album)
       lv.AddTwoLines("Genre",    ID3.V1.Genre)
      lv.AddTwoLines("Track",    ID3.V1.Track)
      lv.AddTwoLines("Comment",  ID3.V1.Comment)
      
      lv.AddTwoLines("Version", "id3v1")
   End If
      
   If ID3.errCode > 0 Then
      lv.AddTwoLines("Error in id3 tag found", "Error " & ID3.ErrCode & " at Position " & ID3.ErrPosition)
    End If
      
End Sub


UPDATE Version 1.1:

Fixed an error with older id3v2 versions (id3v2.2).

Currently this class reads id3v2.3 and id3v1 tags.
id3v2.4 tags are not fully supported.
id3v2.2 tags are not supported.
 

Attachments

  • id3test1.png
    id3test1.png
    50.2 KB · Views: 639
  • id3test2.png
    id3test2.png
    46.9 KB · Views: 664
  • id3test.zip
    212.2 KB · Views: 732
Last edited:

Djembefola

Active Member
Licensed User
Longtime User
I have updated the class to version 1.1. (see first post)

Thank you, stefanoa for the example file.
The error was caused by an old id3v2.2 tag.

Currently this class will only work with id3v2.3 tags. Older versions are not supported.

I have modified the initialize method: older id3v2 versions will be skipped.

This should not be a big problem, because (as far as i know) the majority of available mp3 files is encoded with id3v2.3.

If anybody is willing to extend the class with id3v2.2 tags, i would appreciate it.
 

stefanoa

Active Member
Licensed User
Longtime User
great!
i'll try it on my app (YouMediaPlayer) and i'll keep you informed..
now, I hope to do the Tags edit ..
thanks..
:sign0098:
 

stefanoa

Active Member
Licensed User
Longtime User
i've implemented this function but ID3.Initialize(ID3Songpath, ID3Songfilename) is very slow...
any ideas??

B4X:
Sub readID3TagImage(ID3Songpath As String, ID3Songfilename As String)
Dim i As Int
   Dim ID3 As ID3
   Dim coverExist As Boolean 
   
   Try
      ID3.Initialize(ID3Songpath, ID3Songfilename)
      coverImage=Null
      
      If ID3.Exists = True  Then 
         If ID3.version >= 3 Then
            If ID3.hasPictures Then 
               coverImage=ID3.cover
               coverExist=True
            End If
            
         End If
      End If   
   Catch
   
   End Try
   
   Return coverExist
End Sub
 

Djembefola

Active Member
Licensed User
Longtime User
i've implemented this function but ID3.Initialize(ID3Songpath, ID3Songfilename) is very slow...
any ideas??

In the Initialize method all id3v2 frames (tags and images) are searched and collected by stepping through the bytes of the id3 section. I have no idea how to optimize this.

What exactly do you mean with "very slow"?
How many milliseconds does it take to read an mp3 file?
For my purposes it was always fast enough.

You should not initialize the class several times for one mp3 file just to find out, if a single image or tag exists. It would be better to initialize the class only once and read all tags and images.
 

stefanoa

Active Member
Licensed User
Longtime User
i have to show the image of each file mp3 file in the list (songs list), and so i call the function readID3TagImage(...,...) with initialize, for every file, and is slow...

for ex. if i have 100 songs, i have to call 100 times this function, and so i have to execute 100 initialize instruction...
 

Djembefola

Active Member
Licensed User
Longtime User
i have to show the image of each file mp3 file in the list (songs list), and so i call the function readID3TagImage(...,...) with initialize, for every file, and is slow...

for ex. if i have 100 songs, i have to call 100 times this function, and so i have to execute 100 initialize instruction...

I see three ways to improve the performance:

  1. modify the class and skip all frames except the image frames.
  2. read the mp3 files only once and cache the thumbnail images in a directory or database.
  3. load only the visible cover images and reload images when the list is scrolled.
 
Last edited:

enrico

Active Member
Licensed User
Longtime User
mediaplayerstream

Can this lib work with mediaplayerstream, loading a file from a website ?
 

melamoud

Active Member
Licensed User
Longtime User
great class

hi, this is a great class

If you want I can help improve it (or maybe others can do that as well) if you for example add a dump method that will dump the area you need to investigate (bytes) I can send you this dump for all the files it failed to parse or parse incorretcly.

I modified your example to just go over all my files and dump the attributes one by one to a log file, I can add the dump output and send it to ou so you enrich your testing set by many more files

out of my 845 files 140 had errors and 43 were empty - FYI

I guess some of them are unsupported versions, can you add to your class the unsupported version identification ? if you able to identify the ones you support the rest are unsupported, can you distinguish between error in parsing and unsupported version ? can you extract the version string even for unsupported verisons ?)
 

Rusty

Well-Known Member
Licensed User
Longtime User
Great class!
I'm trying to read ip3v2.3 information.
I've encoded about 100 bytes of information within the Album, which I can verify within Windows that it is indeed there.
When I use your class, I get only the first 10 characters.
I'm using your sample code without modification (except the file name MYFile).
Any suggestions on how I can acquire the remaining bytes?
Thanks,
Rusty
 

Robert Valentino

Well-Known Member
Licensed User
Longtime User
Just an FYI
Private Sub mCheckTagSize(raf As RandomAccessFile, size As Long) As Boolean
Private Sub mFindPicture(raf As RandomAccessFile) As Boolean
Private Sub mSeekBytePattern(raf As RandomAccessFile,pos As Long,maxsteps As Long,values() As Byte) As Long

The compiler complains that Not All Paths return a value
 

noeleon

Active Member
Licensed User
No update for this class? I wish it could write tags as well.

In my observation, it's not reading the right frame size of USLT (lyrics).
 
Top