MP3 tags. Where are they?

I’m running Vista (Uggh! but that’s a whole 'nother issue) and noticed it tags SOME MP3 files with Genre and other things. My question is: Where is it getting this info? I’m looking at Robin Trower’s Bridge of sighs, which Vista says is "Rock/Pop.

I’ve opened the file in a hex editor and can’t find that info anywhere in the file so where’s it coming from?

Thanks

Testy

Probably from the ID3 tag - you can edit these in most modern music software. If you open the file in whatever the equivalent of Quicktime is on Windows (or Quicktime if you have it installed, since it can do this) you can get detailed info on a file - title, artist, album, track number etc - all sorts of metadata, some of which can be incorrect. It is easy enough (if time consuming, if you have a lot of files) to fix.

You can right click it in windows and go to properties and change it there, you can also embed album art this way

download picardmp3

best program out there

Joe H2O & ChrisBooth12

Thanks for this. I know the info is in there somewhere and can be edited by some apps. What I am wondering is why I can’t see it in the actual file with a hex editor. I can see lots of other things such as Album Title and date, rip date, and the app used for the rip but no genre tag. These are all in more-or-less plain English but not the genre. Weird!

Thanks again

Testy

The genre is a code from the last byte of the file.

From a visual basic program I wrote (I can post the entire thing if you want)

Case 34: fGetGenre = "Acid"
Case 74: fGetGenre = "Acid Jazz"
Case 73: fGetGenre = "Acid Punk"
Case 99: fGetGenre = "Acoustic"
Case 40: fGetGenre = "Alt.Rock"
Case 20: fGetGenre = "Alternative"
Case 26: fGetGenre = "Ambient"
Case 145: fGetGenre = "Anime"
Case 90: fGetGenre = "Avant Garde"
Case 116: fGetGenre = "Ballad"
Case 41: fGetGenre = "Bass"
Case 135: fGetGenre = "Beat"
Case 85: fGetGenre = "Bebob"

etc.

I think the id3v1 tags just stored the genre as a single byte that is interpreted based on a table, like K364 mentioned. id3v2 (at least the latest version; not sure about previous ones) allows you to specify the genre information as a string.

id3.org has interesting documentation about the format.

Hi K364

Yeah, that is more what I’m looking for. In my case I don’t see any kind of numeric right at the end of the file though. What I have is:

“TAGBridge of Sighs . . . . . . .Robin Trower. . . . . . . .1000 Top Rock & Roll Hits Vol. . . . . . . . .N”
The number of dots I used between the text strings is only approximate.

I’d love to see the VB program you wrote. It sounds like a very slick piece of code.

Thanks

Testy

Hi Chorpler

Great! If I can get a copy of the taable and manage to write something as nice as K364 did, I should be able to play with these things a bit.

Thanks

Testy

The “N” at the end is the character representation of 78, which is the code for Rock 'n’Roll.

heh, I’m not sure how slick it is. I wrote it as an exercise more or less… in Excel VBA which allowed me to dump an entire folder of mp3 info into a spreadsheet. Next couple of posts are the code.


Option Explicit

Private Type TagInfo
    Tag As String * 3
    Title As String * 30
    Artist As String * 30
    Album As String * 30
    Year As String * 4
    Comment As String * 30
    GenreCode As String * 1
End Type

Private Const BIF_RETURNONLYFSDIRS As Long = &H1
Private Const BIF_DONTGOBELOWDOMAIN As Long = &H2
Private Const BIF_RETURNFSANCESTORS As Long = &H8
Private Const BIF_BROWSEFORCOMPUTER As Long = &H1000
Private Const BIF_BROWSEFORPRINTER As Long = &H2000
Private Const BIF_BROWSEINCLUDEFILES As Long = &H4000
Private Const MAX_PATH As Long = 260

Type BrowseInfo
    hOwner As Long
    pidlRoot As Long
    pszDisplayName As String
    lpszINSTRUCTIONS As String
    ulFlags As Long
    lpfn As Long
    lParam As Long
    iImage As Long
End Type

Type SHFILEOPSTRUCT
    hwnd As Long
    wFunc As Long
    pFrom As String
    pTo As String
    fFlags As Integer
    fAnyOperationsAborted As Boolean
    hNameMappings As Long
    lpszProgressTitle As String
End Type

Declare Function SHGetPathFromIDListA Lib "shell32.dll" ( _
    ByVal pidl As Long, _
    ByVal pszBuffer As String) As Long

Declare Function SHBrowseForFolderA Lib "shell32.dll" ( _
    lpBrowseInfo As BrowseInfo) As Long
    
Type FileSpec
    Name As String
    DateModified As String
    Size As Long
End Type

Function BrowseFolder(Optional Caption As String = "") As String

    Dim BrowseInfo As BrowseInfo
    Dim FolderName As String
    Dim ID As Long
    Dim Res As Long

    With BrowseInfo
        .hOwner = 0
        .pidlRoot = 0
        .pszDisplayName = String$(MAX_PATH, vbNullChar)
        .lpszINSTRUCTIONS = Caption
        .ulFlags = BIF_RETURNONLYFSDIRS
        .lpfn = 0
    End With

    FolderName = String$(MAX_PATH, vbNullChar)

    ID = SHBrowseForFolderA(BrowseInfo)

    If ID Then
        Res = SHGetPathFromIDListA(ID, FolderName)
        If Res Then
            BrowseFolder = Left$(FolderName, InStr(FolderName, _
                vbNullChar) - 1)
        End If
    End If

End Function



Sub testcddb()
Dim cddbft As CDDBCONTROLLib.CddbFileTag, sFileName As String
Set cddbft = CreateObject("CDDBControl.CddbID3Tag")
sFileName = "C:\Ushare\Music	est.mp3"
cddbft.LoadFromFile sFileName, UI_EDITMODE
'cddbft.Title = "New Title"
'cddbft.SaveToFile sFileName
MsgBox cddbft.Title

End Sub

'The Windows Media Player provides an easy, quick way to drop MP3
'capability into a Visual Basic application. However, once you have
'an MP3, you may have wondered how to read information about the song,
'such as the song title and artist's name. If the MP3 file uses the
'most popular tag encryption, ID3, then you're in luck. This standard
'stores the Tag information in the last 128 bytes of the file
'(Tag:3, Title:30, Artist:30, Album:30, Year:4, Comment:30, Genre:1)
'
'To read this information, first open the MP3 file and grab the last
'128 bytes. With ID3, the first three slots hold the string TAG if the
'file actually contains information. If the file does contain Tag
'information, store the last 128 bytes in a custom variable. After that,
'cycle through the MP3 file, extracting information as you go. The
'following procedure shows the code that extracts this information as
'well as creates several important variables to use later on:
Sub test()
Dim a As String, b As String, C As String, d As String, e As String, f As String, g As String, h As String
GetTagInfo "C:\uShare\Music\Burn1\01a All I've Got To Do.mp3", a, b, C, d, e, f, g, h
Debug.Print "Tag", a
Debug.Print "Title", b
Debug.Print "Artist", C
Debug.Print "Album", d
Debug.Print "Year", e
Debug.Print "Comment", f
Debug.Print "Track", g
Debug.Print "Genre", h

End Sub

Public Sub GetTagInfo(Filename As String, _
                        Tag As String, _
                        Title As String, _
                        Artist As String, _
                        Album As String, _
                        Year As String, _
                        Comment As String, _
                        Track As String, _
                        Genre As String)

Dim CurrentTag As TagInfo

Open Filename For Binary As #1
With CurrentTag
    Get #1, FileLen(Filename) - 127, .Tag
    Tag = .Tag
    If Tag = "TAG" Then
        Get #1, , .Title
        Get #1, , .Artist
        Get #1, , .Album
        Get #1, , .Year
        Get #1, , .Comment
        Get #1, , .GenreCode
    Else
        Tag = "No tag"
        .Title = ""
        .Artist = ""
        .Comment = ""
        .GenreCode = Chr(255)
        .Title = ""
        .Year = ""
    End If
    Close #1

    Title = RTrim(.Title)
    Artist = RTrim(.Artist)
    Album = RTrim(.Album)
    Year = RTrim(.Year)
    Comment = .Comment
    If Mid(Comment, 29, 1) = Chr(0) Then
        Track = Trim(Str(Asc(Mid(Comment, 30, 1))))
        Comment = RTrim(Left(Comment, 28))
    Else
        Track = ""
        Comment = RTrim(Comment)
    End If
    Genre = fGetGenre(Asc(.GenreCode))
    
End With
End Sub


Private Function fGetGenre(GenreCode As Integer) As String
Select Case GenreCode
    Case 34: fGetGenre = "Acid"
    Case 74: fGetGenre = "Acid Jazz"
    Case 73: fGetGenre = "Acid Punk"
    Case 99: fGetGenre = "Acoustic"
    Case 40: fGetGenre = "Alt.Rock"
    Case 20: fGetGenre = "Alternative"
    Case 26: fGetGenre = "Ambient"
    Case 145: fGetGenre = "Anime"
    Case 90: fGetGenre = "Avant Garde"
    Case 116: fGetGenre = "Ballad"
    Case 41: fGetGenre = "Bass"
    Case 135: fGetGenre = "Beat"
    Case 85: fGetGenre = "Bebob"
    Case 96: fGetGenre = "Big Band"
    Case 138: fGetGenre = "Black Metal"
    Case 89: fGetGenre = "Blue Grass"
    Case 0: fGetGenre = "Blues"
    Case 107: fGetGenre = "Booty Bass"
    Case 132: fGetGenre = "Brit Pop"
    Case 65: fGetGenre = "Cabaret"
    Case 88: fGetGenre = "Celtic"
    Case 104: fGetGenre = "Chamber Music"
    Case 102: fGetGenre = "Chanson"
    Case 97: fGetGenre = "Chorus"
    Case 136: fGetGenre = "Christian Gangsta Rap"
    Case 61: fGetGenre = "Christian Rap"
    Case 141: fGetGenre = "Christian Rock"
    Case 1: fGetGenre = "Classic Rock"
    Case 32: fGetGenre = "Classical"
    Case 112: fGetGenre = "Club"
    Case 128: fGetGenre = "Club -House"
    Case 57: fGetGenre = "Comedy"
    Case 140: fGetGenre = "Contemporary Christian"
    Case 2: fGetGenre = "Country"
    Case 139: fGetGenre = "Crossover"
    Case 58: fGetGenre = "Cult"
    Case 3: fGetGenre = "Dance"
    Case 125: fGetGenre = "Dance Hall"
    Case 50: fGetGenre = "Darkwave"
    Case 22: fGetGenre = "Death Metal"
    Case 4: fGetGenre = "Disco"
    Case 55: fGetGenre = "Dream"
    Case 127: fGetGenre = "Drum & Bass"
    Case 122: fGetGenre = "Drum Solo"
    Case 120: fGetGenre = "Duet"
    Case 98: fGetGenre = "Easy Listening"
    Case 52: fGetGenre = "Electronic"
    Case 48: fGetGenre = "Ethnic"
    Case 54: fGetGenre = "Eurodance"
    Case 124: fGetGenre = "Euro -House"
    Case 25: fGetGenre = "Euro -Techno"
    Case 84: fGetGenre = "Fast Fusion"
    Case 80: fGetGenre = "Folk"
    Case 81: fGetGenre = "Folk / Rock"
    Case 115: fGetGenre = "Folklore"
    Case 119: fGetGenre = "Freestyle"
    Case 5: fGetGenre = "Funk"
    Case 30: fGetGenre = "Fusion"
    Case 36: fGetGenre = "Game"
    Case 59: fGetGenre = "Gangsta Rap"
    Case 126: fGetGenre = "Goa"
    Case 38: fGetGenre = "Gospel"
    Case 49: fGetGenre = "Gothic"
    Case 91: fGetGenre = "Gothic Rock"
    Case 6: fGetGenre = "Grunge"
    Case 79: fGetGenre = "Hard Rock"
    Case 129: fGetGenre = "Hardcore"
    Case 137: fGetGenre = "Heavy Metal"
    Case 7: fGetGenre = "Hip Hop"
    Case 35: fGetGenre = "House"
    Case 100: fGetGenre = "Humour"
    Case 131: fGetGenre = "Indie"
    Case 19: fGetGenre = "Industrial"
    Case 33: fGetGenre = "Instrumental"
    Case 46: fGetGenre = "Instrumental Pop"
    Case 47: fGetGenre = "Instrumental Rock"
    Case 8: fGetGenre = "Jazz"
    Case 29: fGetGenre = "Jazz -Funk"
    Case 146: fGetGenre = "JPop"
    Case 63: fGetGenre = "Jungle"
    Case 86: fGetGenre = "Latin"
    Case 71: fGetGenre = "Lo -fi"
    Case 45: fGetGenre = "Meditative"
    Case 142: fGetGenre = "Merengue"
    Case 9: fGetGenre = "Metal"
    Case 77: fGetGenre = "Musical"
    Case 82: fGetGenre = "National Folk"
    Case 64: fGetGenre = "Native American"
    Case 133: fGetGenre = "Negerpunk"
    Case 10: fGetGenre = "New Age"
    Case 66: fGetGenre = "New Wave"
    Case 39: fGetGenre = "Noise"
    Case 11: fGetGenre = "Oldies"
    Case 103: fGetGenre = "Opera"
    Case 12: fGetGenre = "Other"
    Case 75: fGetGenre = "Polka"
    Case 134: fGetGenre = "Polsk Punk"
    Case 13: fGetGenre = "Pop"
    Case 62: fGetGenre = "Pop / Funk"
    Case 53: fGetGenre = "Pop / Folk"
    Case 109: fGetGenre = "Porn Groove"
    Case 117: fGetGenre = "Power Ballad"
    Case 23: fGetGenre = "Pranks"
    Case 108: fGetGenre = "Primus"
    Case 92: fGetGenre = "Progressive Rock"
    Case 67: fGetGenre = "Psychedelic"
    Case 93: fGetGenre = "Psychedelic Rock"
    Case 43: fGetGenre = "Punk"
    Case 121: fGetGenre = "Punk Rock"
    Case 14: fGetGenre = "R& B"
    Case 15: fGetGenre = "Rap"
    Case 68: fGetGenre = "Rave"
    Case 16: fGetGenre = "Reggae"
    Case 76: fGetGenre = "Retro"
    Case 87: fGetGenre = "Revival"
    Case 118: fGetGenre = "Rhythmic Soul"
    Case 17: fGetGenre = "Rock"
    Case 78: fGetGenre = "Rock 'n'Roll"
    Case 143: fGetGenre = "Salsa"
    Case 114: fGetGenre = "Samba"
    Case 110: fGetGenre = "Satire"
    Case 69: fGetGenre = "Showtunes"
    Case 21: fGetGenre = "Ska"
    Case 111: fGetGenre = "Slow Jam"
    Case 95: fGetGenre = "Slow Rock"
    Case 105: fGetGenre = "Sonata"
    Case 42: fGetGenre = "Soul"
    Case 37: fGetGenre = "Sound Clip"
    Case 24: fGetGenre = "Soundtrack"
    Case 56: fGetGenre = "Southern Rock"
    Case 44: fGetGenre = "Space"
    Case 101: fGetGenre = "Speech"
    Case 83: fGetGenre = "Swing"
    Case 94: fGetGenre = "Symphonic Rock"
    Case 106: fGetGenre = "Symphony"
    Case 147: fGetGenre = "Synth Pop"
    Case 113: fGetGenre = "Tango"
    Case 18: fGetGenre = "Techno"
    Case 51: fGetGenre = "Techno -Industrial"
    Case 130: fGetGenre = "Terror"
    Case 144: fGetGenre = "Thrash Metal"
    Case 60: fGetGenre = "Top 40"
    Case 70: fGetGenre = "Trailer"
    Case 31: fGetGenre = "Trance"
    Case 72: fGetGenre = "Tribal"
    Case 27: fGetGenre = "Trip Hop"
    Case 28: fGetGenre = "Vocal"
    Case Else: fGetGenre = ""
End Select
End Function


The previous 3 posts have code you would stick in a vba module.

The following procedures are attached to buttons on a worksheet and reference a named range “Folder”. You have to have some understanding of VBA or this won’t make any sense.


Private Sub cmdStart_Click()
    Dim sFolder As String, sFile As String, R As Long, C As Integer
    Dim sTag As String, sTitle As String, sArtist As String, sAlbum As String, sYear As String
    Dim sComment As String, sTrack As String, sGenre As String
    
    sFolder = [Folder]
    
    If Right(sFolder, 1) = "\" Then
        sFolder = Left(sFolder, Len(sFolder) - 1)
    End If
    sFile = Dir(sFolder & "\*.mp3")
    If sFile = "" Then
        MsgBox "Folder was not found, or has no MP3 files.", vbCritical
        Exit Sub
    End If

    R = Range("HeaderStartCell").Row
    C = Range("HeaderStartCell").Column
    Range(Cells(R + 1, C), "IV65536").ClearContents
    
    Do While sFile <> ""
        R = R + 1
        GetTagInfo sFolder & "\" & sFile, _
                    sTag, _
                    sTitle, _
                    sArtist, _
                    sAlbum, _
                    sYear, _
                    sComment, _
                    sTrack, _
                    sGenre
        Cells(R, C) = sFile
        Cells(R, C + 1) = sTag
        Cells(R, C + 2) = sTitle
        Cells(R, C + 3) = sArtist
        Cells(R, C + 4) = sAlbum
        Cells(R, C + 5) = sYear
        Cells(R, C + 6) = sComment
        Cells(R, C + 7) = sTrack
        Cells(R, C + 8) = sGenre
        
        sFile = Dir()
    Loop
End Sub

Private Sub cmdSelectFolder_Click()
    Dim strFolder As String
    strFolder = BrowseFolder("Select Folder")
    If strFolder <> "" Then
        [Folder] = strFolder
    End If
End Sub


K635

Yowza! :eek: Thanks for this but I’m going to have to do some work before I actually understand what you’re doing.

Thank you for this, it’ll get me started.

Testy

LAME is the de-facto standard library for encoding MP3s.

mpg123 is an MP3 player with a decoder library spun off to be usable on its own. It even comes with an ID3 dumping program as an example of how to use the library.

Both of these projects release full source code in C and assembly, making them useful learning aids alongside the relevant standards documentation.

(Most of the code is, of course, focused on the MP3 audio part of the job. ID3 tagging is very simple in comparison.)

Derlith

Thanks for the references. I’ll tale a look at these and see what I can do with them.

Thanks again and best regards

Testy

“Vista” doesn’t tag anything unless you do so yourself from within Explorer. If the tag info is wrong then it was because of whoever originally ripped them or the software used to do so.

jz78817

Thank you and that is probably what happened, either someone mis-tagged the file or maybe the software they used. I understand that Vista is not trying to tag anything on its own but simply reports what is in the file tags.

Thanks again

Testy

I had an interesting thought. jz78817 mentioned that Windows Vista didn’t tag anything on its own and it got me to wondering whether it would be possible to design an application that *would *do this. Actually examine the file and categorize it in one of several genres.
I was thinking that possibly some kind of artificial neural network might be capable of this although I must hastily point out that I know next-to-nothing about these. I simply suggest it as a possibility since ANNs seem to excel at such problems. Any experts out there to tell me yea or nay?

Thanks

Testy

I don’t know about neural networks (except that they might be able to learn how to spell my user name right ;)) but you might be able to train a Bayesian statistical filter to sort music in the same way you can train it to sort email into ‘spam’ and ‘ham’ categories. (SpamAssassin is probably the go-to implementation of this idea, but it’s very common in the real world.)

Ultimately, though, the concept is doomed because ‘genres’ are as much social constructs as anything else, and aren’t always bound very strongly to what the music actually sounds like. For example, is the 1812 Overture heavy metal? You need a lot of domain knowledge (what humans like to call ‘common sense’) to sort that stuff out, and computers don’t do that very well.

You’d likely do a lot better to use computers to recognize which song it is, and then rely on a website-based tagging system to allow humans to sort songs into genres. Spammers, trolls, assholes, and crazies will get in eventually and sort every single Justin Beiber song into ‘DICKBUTT’ and ‘CH34P V14GR4’, but that’s just how it is.