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: 547
  • id3test2.png
    id3test2.png
    46.9 KB · Views: 566
  • id3test.zip
    212.2 KB · Views: 637
Last edited:

Informatix

Expert
Licensed User
Longtime User
:sign0098:

I added a file selection dialog to your demo to test with my own files. Until now, only one has not been fully decoded. Only the artwork is displayed. No album, no title, no artist.
 

Attachments

  • id3test+filedlg.zip
    217.6 KB · Views: 408

Djembefola

Active Member
Licensed User
Longtime User
Hi Informatix,
thank you for adding your selection dialog and for doing some tests.

Only the artwork is displayed. No album, no title, no artist.

No album, title, artist for one file or for all files? :eek:
I hope this did not happen to all of your files.

Reading my own mp3 files works fine, but they are all encoded with a valid id3v2.3 tag. Maybe there are some issues with id3v2.4 or other versions.

I must say it's no fun to deal with all those different id3 versions and with the garbage that can be found in many mp3 files. the class tries to skip corrupted frames and provides an error code, if an unreable frame is found.
 

stefanoa

Active Member
Licensed User
Longtime User
i've a problem with ID3 Class
when i try to load id3 tags in a specific album the program execution stops without errors.
analyzing the code i see that stops in sub mGetAllPictures at row:
B4X:
pos=mSeekString(raf, pos, TagSize,"APIC","")
with value:
pos=0
tagsize=2186334

so the problem seem to be in function "mSeekString" that is very very slow in code:
B4X:
Do Until (n < l)

      Log("mSeekString n: " & n)
      
     n = raf.ReadBytes(b,0,l,pos)
     If (n < l) Then Return -1
     s = BytesToString(b, 0, l, charset)
     If s = seeked Then Return pos
     pos = pos + 1
     If pos > maxbytes Then Return -1
   Loop

after about 3 minutes the execution start from the point where it stopped!

i think may be the image too big, I saw with a tag editing program that the image is 2 MB! (the other are 30-50kb)....

any ideas????

:sign0085:
 

stefanoa

Active Member
Licensed User
Longtime User
i've not errors..
the execution seem stopped but in debug the cycle "do until" is executing continued, with "n" value = "4" (loop)

i've to set a limit for image size in ID3 class to prevent this problem of slowness..
 

stefanoa

Active Member
Licensed User
Longtime User
Setting limit size of image to 250kb it works! (If TagSize > 250000.....)
any suggestion?

B4X:
Private Sub mGetAllPictures(raf As RandomAccessFile) As Int

   Dim pos, pos1, FrameStart, offset As Long
   Dim psize As Int
   Dim Buffer() As Byte
   Dim mimetype As String
   Dim x As Int
   Dim enc As Int
   Dim description As String
   Dim isURL As Boolean
   Dim url As String
   Dim counter As Int
   Dim charset As String

   If TagSize > 250000 Then Return    'UPDATED - discard image too big >250kb

   pos=mPicPos
   pos=mSeekString(raf, pos, TagSize,"APIC","")

   If pos<=0 Then Return
            .........................
 

Djembefola

Active Member
Licensed User
Longtime User
Hi Stefanoa,

thank you for reporting this issue.

The tagsize is written to the id3 header. Applications use the tagsize to skip the id3 information and find the first mp3 audio frame. Tagsize can be larger than 250 000, if there are many images embedded in the mp3 file.

The class determines the tagsize in mReadHeader.
The best place for setting a tagsize limit would be in this procedure.

I will try to add a large bitmap to one of my mp3 files and do some tests before uploading a new version.
 

stefanoa

Active Member
Licensed User
Longtime User
thanks, in the meantime, I try to insert it in mReadHeader and i'll inform you as soon as possible
 

stefanoa

Active Member
Licensed User
Longtime User
Temporarily added this code in mReadHeader:
B4X:
'*******************************************
If TagSize > 250000 Then     'UPDATED - discard Header too big >250kb
   raf.close
   Return -1       
End If
'*******************************************

Result:
B4X:
Private Sub mReadHeader(raf As RandomAccessFile) As Long
 Dim buffer(3) As Byte
 Dim s As String
 Dim x As Int
 Dim pos As Long
   
   
   raf.ReadBytes(buffer,0,3,0)
   s = mBytesToString(buffer,3,"8859-1")
   If s <> "ID3" Then
      raf.close
      Return -1
   Else
      Exists = True
   End If   
   
   
   Version = raf.ReadUnsignedByte(3)
   Revision = raf.ReadUnsignedByte(4)
   Flags = raf.ReadUnsignedByte(5)
   
   s = Bit.ToBinaryString(Flags)
   'Log ("Flags:" & s)         
   
   x =raf.ReadInt(6)
   'Log("TagSize before conversion: " & x)
   x=mUnsynchSafe(x)
   TagSize = x + 10 'plus Header Size
   'Log("TagSize: " & TagSize)
      
   If mCheckTagSize(raf, TagSize)=False Then
      'this will take a while
      TagSize=mSeekMP3Frame(raf,TagSize-1000)
      
   End If
   
   '*******************************************
   If TagSize > 250000 Then    'UPDATED - discard Header too big >250kb
      raf.close
         Return -1       
   End If
   '*******************************************

   Return 11  
   
End Sub
 
Last edited:

stefanoa

Active Member
Licensed User
Longtime User
it would be really important for my app to be able to edit and record these tags
any suggestions??
 

Djembefola

Active Member
Licensed User
Longtime User
it would be really important for my app to be able to edit and record these tags
any suggestions??

It's possible to add writing functions to this class. I have skipped this part, because i don't need it for my projects.
 

Djembefola

Active Member
Licensed User
Longtime User
have you some examples of how to do?

The id3 class does already collect all important informations(start, size) for each tag. These informations are stored in the ID3_Frame stucture.

If you only need to update a tag without changing its size, this should be pretty easy. (id3 editors often reserve bytes in the tag for updates: the tag size is a lot larger than the content, to make an update easy)

If you need to resize a tag or add a missing tag, things get a bit more complicated: you have to rewrite the whole file and the id3 header needs to be updated.
 

stefanoa

Active Member
Licensed User
Longtime User
yes, i need to resize a tag or add a missing tag..
I'll try to study the ID3 structure..
thanks
 

Gargoyle

Member
Licensed User
Longtime User
IIRC, the ID3 tag has padding after the tags to allow slight alterations to the tags, as long as you don't need to add loads of KB, then you can just rewrite the Tag leaving the rest in place (with altered padding size).

Hope this helps.
 

Djembefola

Active Member
Licensed User
Longtime User
i think may be the image too big, I saw with a tag editing program that the image is 2 MB! (the other are 30-50kb)....
any ideas????

Hi Stefanoa,

i have tested various mp3 files with extremly large embedded images (image size>5 MB). I had no problem with these files, the id3 tags and all embedded images were displayed very quickly in the listview.

i suppose, your mp3 file could be corrupted. At the moment i have no good idea how to handle this. Limiting the tag size is not a proper solution, because large images may exist in mp3 files and can be read without problems as long as they have a valid id3 tag.

Can you send me the file for testing?
 

stefanoa

Active Member
Licensed User
Longtime User
@Gargoyle
thanks, but I'm seeing that is very complex and it takes a bit of time to develop it..
 
Top