B4J Question Bug? Or something else?

GuyBooth

Active Member
Licensed User
Longtime User
For the sake of completeness I've attached the whole project I am trying to build to extract ID tags from flac and mp3 files. I am using the jaudiotagger external library (too large to upload here), and have run into a problem with inconsistency.
When I run the program (from within B4J), I get a set of results. If I re-run the test, multiple times, without closing the program, I get the same results. But if I close the program and restart it (from within B4J) I get different results. Usually (but not always) I get error messages concerning jaudiotagger.tag.Fieldkeys, eg:
B4X:
java.lang.IllegalArgumentException: No enum constant org.jaudiotagger.tag.FieldKey.DATE
Can I do something wrong in my program to produce this inconsistency? I'm not changing any code, parameters or anything else between tests. Just shutting down B4J, restarting it, rerunning the program.

Extracts from the results that demonstrate the problem (I have included three complete sets of logs in a single text file in the attached zip - each set from a different run with different results):

From result set #1 - I consider this to be a "clean" set, the results I expect to see:

B4X:
(FlacTag) FLAC OGG Tag content:
...
    ALBUM:Summer Side Of Life
    DATE:1971
    TRACKNUMBER:05
…
HasField DATE is true
HasField YEAR is false
…
Map Values:
...
DATE,  1971
...
YEAR,  Empty
...

From result set #2 - Date = null, Year = 1971
B4X:
(FlacTag) FLAC OGG Tag content:
...
    ALBUM:Summer Side Of Life
    DATE:1971
    TRACKNUMBER:05
...
java.lang.IllegalArgumentException: No enum constant org.jaudiotagger.tag.FieldKey.DATE
HasField YEAR is true
...
Map Values:
...
YEAR,  1971
...
DATE,  null
...


From result set #3 - Date = null, Year = empty
B4X:
(FlacTag) FLAC OGG Tag content:
...
    ALBUM:Summer Side Of Life
    DATE:1971
...
java.lang.IllegalArgumentException: No enum constant org.jaudiotagger.tag.FieldKey.DATE
HasField YEAR is true
...
Map Values:
...
YEAR,  Empty
...
DATE,  null

The code that produces these results are a Main module and a Class.
Main:
B4X:
#Region Project Attributes
    #MainFormWidth: 600
    #MainFormHeight: 600
    #AdditionalJar: jaudiotagger-2.2.6-SNAPSHOT
#End Region

Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Private btnRunTest As Button
    Private SourceFolder As String
    Private MusicFile As String
    Private IDTagger As clIDTag
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("TestButtonPanel") 'Load the layout file.
    MainForm.Show
   
'' You must set these to a valid folder and music file,
'' For example:
''    SourceFolder = "C:\Buffers\CD-R\RippedFiles"
''    MusicFile = "Summer Side Of Life-05.flac"
'' Flac files seem to be especially troublesome

    SourceFolder = "C:/Buffers/CD-R/Ripped Files"
    MusicFile = "Summer Side Of Life-05.flac"
   
    IDTagger.Initialize    (SourceFolder)
End Sub

'Return true to allow the default exceptions handler to handle the uncaught exception.
Sub Application_Error (Error As Exception, StackTrace As String) As Boolean
    Return True
End Sub

Sub btnRunTest_Click
    If SourceFolder = "" Or MusicFile = "" Then
        fx.Msgbox2(MainForm, "You must enter a valid SourceFolder and MusicFile name in the code in AppStart", _
                        "Test ID Tags", "", "OK", "", fx.MSGBOX_INFORMATION)
    Else
        Dim mapTags As Map
        mapTags.Initialize
        mapTags.Clear
        Wait For (IDTagger.Set_TagSet(MusicFile)) Complete (TagIsSet As Boolean)
        If TagIsSet Then
            wait for (IDTagger.Map_AllTags) Complete (mapTags As Map)
            Log($"Map Values:"$)
            For Each k As String In mapTags.Keys
                If mapTags.Get(k) <> "null" Then
                    If mapTags.Get(k) <> "" Then
                        Log($"${k},  ${mapTags.Get(k)}"$)
                    End If
                End If
            Next
            Log($"Map Values (Empty):"$)
            For Each k As String In mapTags.Keys
                If mapTags.Get(k) <> "null" Then
                    If mapTags.Get(k) = "" Then
                        Log($"${k},  Empty"$)
                    End If
                End If
            Next
            Log($"Map Values (Null):"$)
            For Each k As String In mapTags.Keys
                If mapTags.Get(k) = "null" Then
                    Log($"${k},  null"$)
                End If
            Next
        End If
    End If
End Sub
Class clIDTag:
B4X:
Sub Class_Globals
    Private fx As JFX
    Dim Fi As JavaObject
    Dim audioFile As JavaObject
'    Dim audioHeader As JavaObject
    Dim TagSet As JavaObject
    Dim Fi As JavaObject
    Dim audioTaggerFileIO As JavaObject
    Dim mSourceFolder As String
    Dim mapTags As Map
End Sub
'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(Folder As String)
    setSourceFolder(Folder)
    Fill_TagMap
End Sub
' Change this property if the SourceFolder changes
Public Sub setSourceFolder (sVar As String)
    mSourceFolder = sVar  
End Sub

Sub Fill_TagMap
    ' Initialize the Tags map and enter known tags with empty values
    mapTags.Initialize
    mapTags.Put("ALBUM", "")
    mapTags.Put("ARTIST", "")
    mapTags.Put("GENRE", "")
    mapTags.Put("DATE", "")
    mapTags.Put("YEAR", "")
    mapTags.Put("DISCNUMBER", "")
    mapTags.Put("TOTALDISCS", "")
    mapTags.Put("TOTALTRACKS", "")
    mapTags.Put("ALBUM", "")
    mapTags.Put("TRACKNUMBER", "")
    mapTags.Put("TITLE", "")
    ' May not exist
    mapTags.Put("ALBUM_ARTIST", "")
    mapTags.Put("COMPOSER", "")
    mapTags.Put("CONDUCTOR", "")
    mapTags.Put("ORCHESTRA", "")
    mapTags.Put("SOLOISTS", "")
    mapTags.Put("COMMENT", "")
End Sub
' Run this to set up each new track file
Public Sub Set_TagSet (FileName As String) As ResumableSub
    Dim TagIsSet As Boolean
    Try
        ' get an instance of AudioFileIO so we can read the file
        audioTaggerFileIO.InitializeNewInstance("org.jaudiotagger.audio.AudioFileIO",Null)
        ' Check that the file (Folder, Name) exists:
        If File.Exists(mSourceFolder, FileName) Then
            ' file to read must be a java.io.File just pass full path to it
            Fi.InitializeNewInstance("java.io.File",Array(File.Combine(mSourceFolder, FileName)))
            audioFile = audioTaggerFileIO.RunMethod("read", Array(Fi))
            ' read the headers from audioFile and put into audioHeaders
            ' again audioHeaders will assume the correct class as it's a java object
'            audioHeader = audioFile.RunMethod("getAudioHeader",Null)          
            ' read the tags from the audioFile
            TagSet = audioFile.RunMethod("getTag",Null)
            TagIsSet = True
        Else
            fx.Msgbox2(Null, $"File ${FileName} not found."$, "TagSet Error", _
                "", "OK", "", fx.MSGBOX_ERROR)
        End If
    Catch
        Log($"Exception Message: ${LastException.Message}"$)
    End Try
    Log(TagSet)
    Return TagIsSet
End Sub

Public Sub Map_Tag (Key As String) As ResumableSub
    Try
    Dim HasField As Boolean
    Dim TagValue As String
    HasField = TagSet.RunMethod("hasField", Array(Key))
        Log($"HasField ${Key} is ${HasField}"$)
    If HasField Then
        TagValue = TagSet.runmethod("getFirst", Array(Key))
        TagValue = TagValue.Trim
    Else
        TagValue = ""
    End If
    Catch
        Log(LastException.Message)
        TagValue = Null
    End Try  
    Return TagValue
End Sub
'
Public Sub Map_AllTags As ResumableSub
    For Each s As String In mapTags.Keys
        Wait for (Map_Tag (s)) complete (TagValue As String)
        mapTags.Put(s, TagValue)
    Next
    Return mapTags
End Sub
I would love to see a simple solution to this, even if it's a dumb mistake that I have made!
 

Attachments

  • audiotagger tests.zip
    4.9 KB · Views: 234

OliverA

Expert
Licensed User
Longtime User
Yes. Look at my code again and use it to modify your sub that fills your mapTags map. Then look at second code snippet I provided to see how you can use your map to properly call getFirst.

Clarification: The yes referred to there being a better way than rewriting everything with types.
 
Last edited:
Upvote 0

GuyBooth

Active Member
Licensed User
Longtime User
Sorry OliverA I missed your first post.
I think the problem is that hasField method uses a string and getFirst uses an enum, yet you are using a string.

To get the enum value, I think you need the following code: ….

So I changed my code. But I always get this
B4X:
Exception Message: java.lang.RuntimeException: Field: DISCNUMBER not found in: org.jaudiotagger.tag.FieldKey
Exception Message: java.lang.RuntimeException: Field: TOTALDISCS not found in: org.jaudiotagger.tag.FieldKey
Exception Message: java.lang.RuntimeException: Field: TOTALTRACKS not found in: org.jaudiotagger.tag.FieldKey
Exception Message: java.lang.RuntimeException: Field: TRACKNUMBER not found in: org.jaudiotagger.tag.FieldKey

Even though my Log(TagSet) line seems to produce values for those fields:
B4X:
(FlacTag) FLAC OGG Tag content:
    VENDOR:reference libFLAC 1.2.1 20070917
    ARTIST:Gordon Lightfoot
    TITLE:Cotton Jenny
    ALBUM:Summer Side Of Life
    DATE:1971
    TRACKNUMBER:05
    GENRE:Folk
    COMMENT:
    BAND:
    ALBUMARTIST:
    COMPOSER:
    DISCNUMBER:1
    TOTALDISCS:1
    TOTALTRACKS:11

And SOMETIMES, but not always, I get this
B4X:
java.lang.IllegalArgumentException: No enum constant org.jaudiotagger.tag.vorbiscomment.VorbisCommentFieldKey.YEAR
When I get this message, the YEAR tag shows as empty. When I don't get the message, it shows 1971, as it should.
 
Upvote 0

GuyBooth

Active Member
Licensed User
Longtime User
look at this page - these are the only names it knows
http://www.jthink.net/jaudiotagger/javadoc/org/jaudiotagger/tag/FieldKey.html

for example DISCNUMBER is actually DISC_NO
Oops of course, thanks Daestrum.
Now those are fixed, back to the problem I first posted - inconsistency.
Here are the results from two separate tests. Identical code, identical music file:
Test 1 - looks good:
B4X:
Waiting for debugger to connect...
Program started.
Jan. 28, 2019 6:51:14 P.M. org.jaudiotagger.audio.flac.FlacInfoReader read
INFO: C:\Buffers\CD-R\Ripped Files\Summer Side Of Life-05.flac BlockType:STREAMINFO DataLength:34 isLastBlock:false
Jan. 28, 2019 6:51:14 P.M. org.jaudiotagger.audio.flac.FlacInfoReader read
INFO: C:\Buffers\CD-R\Ripped Files\Summer Side Of Life-05.flac BlockType:SEEKTABLE DataLength:378 isLastBlock:false
Jan. 28, 2019 6:51:14 P.M. org.jaudiotagger.audio.flac.FlacInfoReader read
INFO: C:\Buffers\CD-R\Ripped Files\Summer Side Of Life-05.flac BlockType:VORBIS_COMMENT DataLength:263 isLastBlock:false
Jan. 28, 2019 6:51:14 P.M. org.jaudiotagger.audio.flac.FlacInfoReader read
INFO: C:\Buffers\CD-R\Ripped Files\Summer Side Of Life-05.flac BlockType:PADDING DataLength:8192 isLastBlock:true
(FlacTag) FLAC OGG Tag content:
    VENDOR:reference libFLAC 1.2.1 20070917
    ARTIST:Gordon Lightfoot
    TITLE:Cotton Jenny
    ALBUM:Summer Side Of Life
    DATE:1971
    TRACKNUMBER:05
    GENRE:Folk
    COMMENT:
    BAND:
    ALBUMARTIST:
    COMPOSER:
    DISCNUMBER:1
    TOTALDISCS:1
    TOTALTRACKS:11
Map Values:
ALBUM,  Summer Side Of Life
ARTIST,  Gordon Lightfoot
GENRE,  Folk
YEAR,  1971
DISC_NO,  1
DISC_TOTAL,  Empty
TRACK_TOTAL,  Empty
TRACK,  05
TITLE,  Cotton Jenny
ALBUM_ARTIST,  Empty
COMPOSER,  Empty
CONDUCTOR,  Empty
ORCHESTRA,  Empty
COMMENT,  Empty
Test 2: Not so good
B4X:
Waiting for debugger to connect...
Program started.
Jan. 28, 2019 6:55:44 P.M. org.jaudiotagger.audio.flac.FlacInfoReader read
INFO: C:\Buffers\CD-R\Ripped Files\Summer Side Of Life-05.flac BlockType:STREAMINFO DataLength:34 isLastBlock:false
Jan. 28, 2019 6:55:44 P.M. org.jaudiotagger.audio.flac.FlacInfoReader read
INFO: C:\Buffers\CD-R\Ripped Files\Summer Side Of Life-05.flac BlockType:SEEKTABLE DataLength:378 isLastBlock:false
Jan. 28, 2019 6:55:44 P.M. org.jaudiotagger.audio.flac.FlacInfoReader read
INFO: C:\Buffers\CD-R\Ripped Files\Summer Side Of Life-05.flac BlockType:VORBIS_COMMENT DataLength:263 isLastBlock:false
Jan. 28, 2019 6:55:44 P.M. org.jaudiotagger.audio.flac.FlacInfoReader read
INFO: C:\Buffers\CD-R\Ripped Files\Summer Side Of Life-05.flac BlockType:PADDING DataLength:8192 isLastBlock:true
(FlacTag) FLAC OGG Tag content:
    VENDOR:reference libFLAC 1.2.1 20070917
    ARTIST:Gordon Lightfoot
    TITLE:Cotton Jenny
    ALBUM:Summer Side Of Life
    DATE:1971
    TRACKNUMBER:05
    GENRE:Folk
    COMMENT:
    BAND:
    ALBUMARTIST:
    COMPOSER:
    DISCNUMBER:1
    TOTALDISCS:1
    TOTALTRACKS:11
java.lang.IllegalArgumentException: No enum constant org.jaudiotagger.tag.vorbiscomment.VorbisCommentFieldKey.YEAR
java.lang.IllegalArgumentException: No enum constant org.jaudiotagger.tag.vorbiscomment.VorbisCommentFieldKey.DISC_NO
java.lang.IllegalArgumentException: No enum constant org.jaudiotagger.tag.vorbiscomment.VorbisCommentFieldKey.DISC_TOTAL
java.lang.IllegalArgumentException: No enum constant org.jaudiotagger.tag.vorbiscomment.VorbisCommentFieldKey.TRACK_TOTAL
java.lang.IllegalArgumentException: No enum constant org.jaudiotagger.tag.vorbiscomment.VorbisCommentFieldKey.TRACK
Map Values:
ALBUM,  Summer Side Of Life
ARTIST,  Gordon Lightfoot
GENRE,  Folk
YEAR,  Empty
DISC_NO,  Empty
DISC_TOTAL,  Empty
TRACK_TOTAL,  Empty
TRACK,  Empty
TITLE,  Cotton Jenny
ALBUM_ARTIST,  Empty
COMPOSER,  Empty
CONDUCTOR,  Empty
ORCHESTRA,  Empty
COMMENT,  Empty

What am I missing here? Is this indeed a timing issue? The results are better than they were originally, i.e the errors are less frequent. I also now seem to get all the errors shown here, or none at all, as in the "Test 1" results above. But still - I feel there's still something not working 100%
 
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
How often are you trying to read the file?
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
1) I hope you did not 100% modify the map the way I asked you. Looks like you're using the map to store the results for getFirst. This would override the enum values that I had you store in the first place.
2) This is what is happening (from what I can tell). org.jaudiotagger.tag is just an interface (see https://lgtm.com/projects/b/ijabz/j...r/tag/Tag.java?sort=name&dir=ASC&mode=heatmap). The actual implementation is done by the code that is responsible for decoding the type of media file you are accessing. In your case it's a MP3 and therefore org.jaudiotagger.tag.vorbiscomment.tag will be used. Looking at org.jaudiotagger.tag.vorbiscomment.tag, the hasField method (https://lgtm.com/projects/b/ijabz/j...tTag.java?sort=name&dir=ASC&mode=heatmap#L331) uses the generic FieldKey enum, but the getFirst method (https://lgtm.com/projects/b/ijabz/j...tTag.java?sort=name&dir=ASC&mode=heatmap#L317) wants the VorbisCommentFieldKey value. You may want to play with the getAll method (https://lgtm.com/projects/b/ijabz/j...tTag.java?sort=name&dir=ASC&mode=heatmap#L274). That method maps the generic FieldKey value to the corresponding VorbisCommentFieldKey and then returns ALL values (a list) of entries for that key.
 
Upvote 0

GuyBooth

Active Member
Licensed User
Longtime User
How often are you trying to read the file?
I'm not sure it matters: If the errors show up the first time I read the file, nothing I can do will correct the problem except to close B4J, and reopen it, and restart the program. I've tried reinitializing everything from within the program and the errors just repeat. Since this whole tag read (and eventually write) routine will be in the middle of a much larger program, it's not an option.
 
Upvote 0

GuyBooth

Active Member
Licensed User
Longtime User
1) I hope you did not 100% modify the map the way I asked you. Looks like you're using the map to store the results for getFirst. This would override the enum values that I had you store in the first place.
No I didn't do that. I created a second map for the enum values.:)
2) This is what is happening (from what I can tell). org.jaudiotagger.tag is just an interface (see https://lgtm.com/projects/b/ijabz/j...r/tag/Tag.java?sort=name&dir=ASC&mode=heatmap). The actual implementation is done by the code that is responsible for decoding the type of media file you are accessing. In your case it's a MP3 and therefore org.jaudiotagger.tag.vorbiscomment.tag will be used. Looking at org.jaudiotagger.tag.vorbiscomment.tag, the hasField method (https://lgtm.com/projects/b/ijabz/j...tTag.java?sort=name&dir=ASC&mode=heatmap#L331) uses the generic FieldKey enum, but the getFirst method (https://lgtm.com/projects/b/ijabz/j...tTag.java?sort=name&dir=ASC&mode=heatmap#L317) wants the VorbisCommentFieldKey value. You may want to play with the getAll method (https://lgtm.com/projects/b/ijabz/j...tTag.java?sort=name&dir=ASC&mode=heatmap#L274). That method maps the generic FieldKey value to the corresponding VorbisCommentFieldKey and then returns ALL values (a list) of entries for that key.
I'm using flac files in these examples, not mp3 - but I take your point. I will look into it and see what I can figure out. I can understand something not working properly, but it's the inconsistency that's driving me crazy. I do think there's been some progress, but we're not there yet.
I hadn't seen the documents for which you provided links. I notice that the vorbisscommenttag fields do indeed contain a Date field ...
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
Instead of
B4X:
dim jo as JavaObject
jo.InitializeStatic("org.jaudiotagger.tag.FieldKey")
'Populate your map with the right enum's
'I'm just doing a couple here for demo purposes
mapTags.Put("ALBUM", jo.GetField("ALBUM"))
mapTags.Put("ARTIST", jo.GetField("ARTIST"))
mapTags.Put("GENRE", jo.GetField("GENRE"))

Try
B4X:
dim jo as JavaObject
jo.InitializeStatic("org.jaudiotagger.tag.vorbiscomment.VorbisCommentFieldKey")
'Populate your map with the right enum's
'I'm just doing a couple here for demo purposes
mapKeyFields.Put("ALBUM", jo.GetFieldJO("ALBUM"))
mapKeyFields.Put("ARTIST", jo.GetFieldJO("ARTIST"))
mapKeyFields.Put("GENRE", jo.GetFieldJO("GENRE"))
Notice that I'm initializing a different object (VorbisCommentFieldKey) and that I'm using GetFieldJO instead of GetField. Also, use the VorbisCommentFieldKey enum names for your key (instead of FieldKey). See http://www.jthink.net/jaudiotagger/.../tag/vorbiscomment/VorbisCommentFieldKey.html. Notice, VorbisCommentFieldKey has DATE, but not YEAR. So adjust any of your keys as necessary.
 
Upvote 0

GuyBooth

Active Member
Licensed User
Longtime User
OK, I'm using joVC to designate VorbisComment in my code. my line
B4X:
        mapTags.Put(Key, TagSet.runmethod("getFirst", Array(mapVCKeys.Get(Key))))
must need some adjustment, it throws the error
B4X:
java.lang.RuntimeException: Method: getFirst not matched.
Is it looking for a string, but we're now passing it a javaobject?
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
Change it back to GeField instead of GetFieldJO and see what happens. Looking at the source, there are abstract classes, interfaces and overloaded methods galore to make one's head spin (at least mine). Maybe someone else can shed some light on this.
 
Upvote 0

GuyBooth

Active Member
Licensed User
Longtime User
My head's spinning too ... I'm going to back off for a while and think of my options. Summing up for now:
  • I have been unable to find a way to read the tags that report the Disc_Total and Track_Total, even though logging the TagSet produced by
B4X:
audioFile.RunMethod("getTag",Null)
clearly shows tags TOTALDISCS and TOTALTRACKS with values attached.
  • Errors are sometimes - but not always - thrown showing:
B4X:
java.lang.IllegalArgumentException: No enum constant org.jaudiotagger.tag.vorbiscomment.VorbisCommentFieldKey.<Tagname>
These usually include the YEAR tag, DISC_NO, DISC_TOTAL, TRACK_TOTAL and TRACK, and once they show up the only way to resolve them seems to be to restart the whole application - which is not an option. I have modified the code to re-dimension some of the javaobjects in the class initialization routine to see if this is a workaround.

I might be able to live without the Disc and Track totals, provided I can write modified tags back to the music file (haven't even come close to trying that yet :( ) but I won't be able to live with having to restart the whole app to lost the errors.

I would like to thank everyone who has contributed to this thread for the last couple of days to try to help me understand what's going on. Your assistance is very much appreciated.
 
Upvote 0
Top