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

Discussion in 'Additional libraries, classes and official updates' started by Djembefola, Sep 10, 2012.

  1. Djembefola

    Djembefola Active Member Licensed User

    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:

    Code:
    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:

    Code:
    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.
     

    Attached Files:

    Last edited: Sep 26, 2012
    noeleon likes this.
  2. Informatix

    Informatix Expert Licensed 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.
     

    Attached Files:

  3. Djembefola

    Djembefola Active Member Licensed User

    Hi Informatix,
    thank you for adding your selection dialog and for doing some tests.

    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.
     
  4. Informatix

    Informatix Expert Licensed User

    No no that's only one file on 500. So don't bother with that.
     
  5. stefanoa

    stefanoa Active Member Licensed 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:
    Code:
    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:
    Code:
    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:
     
  6. Informatix

    Informatix Expert Licensed User

    Probably an Out of Memory error.
     
  7. stefanoa

    stefanoa Active Member Licensed 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..
     
  8. stefanoa

    stefanoa Active Member Licensed User

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

    Code:
    Private Sub mGetAllPictures(raf As RandomAccessFileAs 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
                .........................
     
  9. Djembefola

    Djembefola Active Member Licensed 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.
     
  10. stefanoa

    stefanoa Active Member Licensed User

    thanks, in the meantime, I try to insert it in mReadHeader and i'll inform you as soon as possible
     
  11. stefanoa

    stefanoa Active Member Licensed User

    Temporarily added this code in mReadHeader:
    Code:
    '*******************************************
    If TagSize > 250000 Then     'UPDATED - discard Header too big >250kb
       raf.close
       
    Return -1       
    End If
    '*******************************************
    Result:
    Code:
    Private Sub mReadHeader(raf As RandomAccessFileAs Long
     
    Dim buffer(3As 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: Sep 25, 2012
  12. stefanoa

    stefanoa Active Member Licensed User

    it would be really important for my app to be able to edit and record these tags
    any suggestions??
     
  13. Djembefola

    Djembefola Active Member Licensed User

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

    stefanoa Active Member Licensed User

    have you some examples of how to do?
     
  15. Djembefola

    Djembefola Active Member Licensed User

    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.
     
  16. stefanoa

    stefanoa Active Member Licensed User

    yes, i need to resize a tag or add a missing tag..
    I'll try to study the ID3 structure..
    thanks
     
  17. Gargoyle

    Gargoyle Member Licensed 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.
     
  18. Djembefola

    Djembefola Active Member Licensed User

    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?
     
  19. stefanoa

    stefanoa Active Member Licensed User

  20. stefanoa

    stefanoa Active Member Licensed User

    @Gargoyle
    thanks, but I'm seeing that is very complex and it takes a bit of time to develop it..
     
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice