Link to home
Start Free TrialLog in
Avatar of diggz
diggz

asked on

Get titles from mp3 filenames

I'm doing a ID3 mass editor, which works like this:
the user has an album (eg The Band's Finest), and wants to put an ID3 tag on all the files. This can take time, so i've made a mass editor for this. The program gets all filenames in the album directory, and stores them in a string array. the user types in all info, except the song title in the main screen. the user has the option to use "intelligent title generation", so that the titles for the id3 tags are generated automatically (since they are different for each song), but i'm not intelligent enough to get this to work.

Here's what i want it to do:
i want to 'extract' the title from the filenames. this can be done by checking the difference between two or more of the filenames, and seeing what characters are before and after the differences found, and then copy the difference (=the title) to a TitleArray, but how do i do this ?

another, IMPORTANT, thing: there are different formats for naming the mp3's. Most people use the "TrackNo Artist - Songname.mp3" format, but it may very well be different, and some elements might not even be there - some might be there many times!. Only sure thing is the extension. Also remember that some bands, or even songs begin with numbers, so isnumeric() won't do any good.

This has troubled me for a long time. please help. (if the question is not understood because of me being bad at explaining, i'll be happy to refine it)

regards,
diggz
Avatar of waty
waty
Flag of Belgium image

' #VBIDEUtils#************************************************************
' * Programmer Name  : Michael Karathanasis
' * Web Site         : http://home12.inet.tele.dk/mkaratha
' * E-Mail           : waty.thierry@usa.net
' * Date             : 6/10/99
' * Time             : 16:54
' **********************************************************************
' * Comments         : Read MP3 header
' *
' *
' **********************************************************************
'''Created By Michael Karathanasis 990728'''''''''''''''''''''
'''Now support for variable bit rate'''
'''please report improvements to http://home12.inet.tele.dk/mkaratha
'''User may use or distrubute the code without restrictions
'Sample how to use:

'Dim filename as string
'filename="c:\mymp3.mp3"
'Call ReadMP3(filename, True, True)
'Text1.Text = GetMP3Info.Bitrate
Type MP3Info
   Bitrate As Integer
   Frequency As Long
   Mode As String
   Emphasis As String
   'ModeExtension As String
   MpegVersion As Integer
   MpegLayer As Integer
   Padding As String
   CRC As String
   Duration As Long
   CopyRight As String
   Original As String
   PrivateBit As String
   HasTag As Boolean
   Tag As String
   Songname As String
   Artist As String
   Album As String
   Year As String
   Comment As String
   Genre As Integer
   Track As String
   VBR As Boolean
   Frames As Integer
End Type
Public GetMP3Info As MP3Info
''this function converts Binary string to decimal integer
Public Function BinToDec(BinValue As String) As Long
   BinToDec = 0
   For i = 1 To Len(BinValue)
      If Mid(BinValue, i, 1) = 1 Then
         BinToDec = BinToDec + 2 ^ (Len(BinValue) - i)
      End If
   Next i
End Function
Public Function ByteToBit(ByteArray) As String
   'convert 4*1 byte array to 4*8 bits'''''
   ByteToBit = ""
   For Z = 1 To 4
      For i = 7 To 0 Step -1
         If Int(ByteArray(Z) / (2 ^ i)) = 1 Then
            ByteToBit = ByteToBit & "1"
            ByteArray(Z) = ByteArray(Z) - (2 ^ i)
         Else
            If ByteToBit <> "" Then
               ByteToBit = ByteToBit & "0"
            End If
         End If
      Next
   Next Z
End Function
Public Function BinaryHeader(filename As String, ReadTag As Boolean, ReadHeader As Boolean) As String
   Dim ByteArray(4) As Byte
   Dim XingH As String * 4
   FIO% = FreeFile
   Open filename For Binary Access Read As FIO%
   n& = LOF(FIO%): If n& < 256 Then Close FIO%: Return 'ny
   If ReadHeader = False Then GoTo 5:   'if we only want to read the IDtag goto 5
   Dim x As Byte
   '''''start check startposition for header''''''''''''
   '''''if start position <>1 then id3v2 tag exists'''''
   For i = 1 To 5000            'check up to 5000 bytes for the header
      Get #FIO%, i, x
      If x = 255 Then             'header always start with 255 followed by 250 or 251
         Get #FIO%, i + 1, x
         If x > 249 And x < 252 Then
            Headstart = i       'set header start position
            Exit For
         End If
      End If
   Next i
   '''end check start position for header'''''''''''''

   ''start check for XingHeader'''
   Get #1, Headstart + 36, XingH
   If XingH = "Xing" Then
      GetMP3Info.VBR = True
      For Z = 1 To 4 '
         Get #1, Headstart + 43 + Z, ByteArray(Z)  'get framelength to array
      Next Z
      Frames = BinToDec(ByteToBit(ByteArray))   'calculate # of frames
      GetMP3Info.Frames = Frames                'set frames
   Else: GetMP3Info.VBR = False
   End If
   '''end check for XingHeader

   '''start extract the first 4 bytes (32 bits) to an array
   For Z = 1 To 4 '
      Get #1, Headstart + Z - 1, ByteArray(Z)
   Next Z
   '''stop extract the first 4 bytes (32 bits) to an array
5:
   If ReadTag = False Then GoTo 10     'if we dont want to read the tag goto 10
   ''''start id3 tag''''''''''''''''''''''''''''''''''''''''''''''''
   Dim Inbuf As String * 256
   Get #FIO%, (n& - 255), Inbuf:  Close FIO% 'ny
   p = InStr(1, Inbuf, "tag", 1)  'ny
   If p = 0 Then
      With GetMP3Info
         .HasTag = False
         .Songname = ""
         .Artist = ""
         .Album = ""
         .Year = ""
         .Comment = ""
         .Track = ""
         .Genre = 255
      End With
   Else
      With GetMP3Info
         .HasTag = True
         .Songname = RTrim(Mid$(Inbuf, p + 3, 30))
         .Artist = RTrim(Mid$(Inbuf, p + 33, 30))
         .Album = RTrim(Mid$(Inbuf, p + 63, 30))
         .Year = RTrim(Mid$(Inbuf, p + 93, 4))
         .Comment = RTrim(Mid$(Inbuf, p + 97, 29))
         .Track = RTrim(Mid$(Inbuf, p + 126, 1))
         .Genre = Asc(RTrim(Mid$(Inbuf, p + 127, 1)))
      End With
   End If
   ''''stop id3 tag''''''''''''''''''''''''''''''
10:
   Close FIO%
   BinaryHeader = ByteToBit(ByteArray)
End Function
Public Function ReadMP3(filename As String, ReadTag As Boolean, ReadHeader As Boolean) As MP3Info
   bin = BinaryHeader(filename, ReadTag, ReadHeader)                     'extract all 32 bits

   If ReadHeader = False Then Exit Function
   Version = Array(25, 0, 2, 1)                        'Mpegversion table
   MpegVersion = Version(BinToDec(Mid(bin, 12, 2)))    'get mpegversion from table
   layer = Array(0, 3, 2, 1)                           'layer table
   MpegLayer = layer(BinToDec(Mid(bin, 14, 2)))        'get layer from table
   SMode = Array("stereo", "joint stereo", "dual channel", "single channel") 'mode table
   Mode = SMode(BinToDec(Mid(bin, 25, 2)))              'get mode from table
   Emph = Array("no", "50/15", "reserved", "CCITT J 17") 'empasis table
   Emphasis = Emph(BinToDec(Mid(bin, 31, 2)))           'get empasis from table
   Select Case MpegVersion                                 'look for version to create right table
      Case 1                                                  'for version 1
         Freq = Array(44100, 48000, 32000)
      Case 2 Or 25                                            'for version 2 or 2.5
         Freq = Array(22050, 24000, 16000)
      Case Else
         Frequency = 0
         Exit Function
   End Select
   Frequency = Freq(BinToDec(Mid(bin, 21, 2)))             'look for frequency in table
   If GetMP3Info.VBR = True Then                           'check if variable bitrate
      Temp = Array(, 12, 144, 144)                        'define to calculate correct bitrate
      Bitrate = (FileLen(filename) * Frequency) / (Int(GetMP3Info.Frames)) / 1000 / Temp(MpegLayer)
   Else                                                 'if not variable bitrate

      Dim LayerVersion As String
      LayerVersion = MpegVersion & MpegLayer          'combine version and layer to string
      Select Case Val(LayerVersion)                        'look for the right bitrate table
         Case 11                                              'Version 1, Layer 1
            Brate = Array(0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448)
         Case 12                                              'V1 L1
            Brate = Array(0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384)
         Case 13                                               'V1 L3
            Brate = Array(0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320)
         Case 21 Or 251                                         'V2 L1 and 'V2.5 L1
            Brate = Array(0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256)
         Case 22 Or 252 Or 23 Or 253                            ''V2 L2 and 'V2.5 L2 etc...
            Brate = Array(0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160)
         Case Else                                               'if variable bitrate
            Bitrate = 1                                             'e.g. for Variable bitrate
            Exit Function
      End Select
      Bitrate = Brate(BinToDec(Mid(bin, 17, 4)))
   End If

   NoYes = Array("no", "yes")
   Original = NoYes(Mid(bin, 30, 1))                       'Set original bit
   CopyRight = NoYes(Mid(bin, 29, 1))                      'Set copyright bit
   Padding = NoYes(Mid(bin, 23, 1))                        'get padding bit
   PrivateBit = NoYes(Mid(bin, 24, 1))
   YesNo = Array("yes", "no")                              'CRC table
   CRC = YesNo(Mid(bin, 16, 1))                            'Get CRC
   ms = (FileLen(filename) * 8) / Bitrate                  'calculate duration
   Duration = Int(ms / 1000)
   With GetMP3Info                                          'set values
      .Bitrate = Bitrate                                  '
      .CRC = CRC
      .Duration = Duration
      .Emphasis = Emphasis
      .Frequency = Frequency
      .Mode = Mode
      .MpegLayer = MpegLayer
      .MpegVersion = MpegVersion
      .Padding = Padding
      .Original = Original
      .CopyRight = CopyRight
      .PrivateBit = PrivateBit
   End With
End Function
Public Function WriteTag(filename As String, Songname As String, _
   Artist As String, Album As String, Year As String, Comment As String, Genre As Integer) As Long
   Tag = "TAG"
   Dim sn As String * 30
   Dim com As String * 30
   Dim art As String * 30
   Dim alb As String * 30
   Dim yr As String * 4
   Dim gr As String * 1
   sn = Songname
   com = Comment
   art = Artist
   alb = Album
   yr = Year
   gr = Chr(Genre)
   Open filename For Binary Access Write As #1
   Seek #1, FileLen(filename) - 127
   Put #1, , Tag
   Put #1, , sn
   Put #1, , art
   Put #1, , alb
   Put #1, , yr
   Put #1, , com
   Put #1, , gr
   Close #1

End Function
Public Function GenreText(Index As Integer) As String
   Matrix = Array("Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", _
      "Hip -Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&b", "Rap", "Reggae", _
      "Rock", "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks", _
      "Soundtrack", "Euro -Techno", "Ambient", "Trip -Hop", "Vocal", "Jazz Funk", "Fusion", _
      "Trance", "Classical", "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel", _
      "Noise", "AlternRock", "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", _
      "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", "Techno -Industrial", "Electronic", _
      "Pop -Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta", "Top 40", _
      "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret", "New Wave", _
      "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo -Fi", "Tribal", "Acid Punk", "Acid Jazz", _
      "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk", "Folk/Rock", "National Folk", _
      "Swing", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", _
      "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", "Chorus", "Easy Listening", _
      "Acoustic", "Humour", "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", _
      "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", "Folklore", "Ballad", "Power Ballad", _
      "Rhythmic Soul", "Freestyle", "Duet", "Punk Rock", "Drum Solo", "A Cappella", _
      "Euro - House", "Dance Hall", "Goa", "Drum & Bass", "Club - House", "Hardcore", "Terror", "Indie", "BritPop", _
      "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta Rap", "Heavy Metal", "Black Metal", "Crossover", _
      "Contemporary Christian", "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop", "Synthpop")
   GenreText = Matrix(Index)
End Function
' #VBIDEUtils#************************************************************
' * Programmer Name  : Waty Thierry
' * Web Site         : www.geocities.com/ResearchTriangle/6311/
' * E-Mail           : waty.thierry@usa.net
' * Date             : 28/06/99
' * Time             : 13:03
' **********************************************************************
' * Comments         : Read MP3 tags
' *
' *
' **********************************************************************

Private Sub Command1_Click()
   
   Dim Filename   As String
   Dim HasTag     As Boolean
   Dim Tag        As String * 3
   Dim Songname   As String * 30
   Dim Artist     As String * 30
   Dim Album      As String * 30
   Dim Year       As String * 4
   Dim Comment    As String * 30
   Dim Genre      As String * 1
   
   Filename = "c:\1.mp3"
   Open Filename For Binary As #1
   Get #1, FileLen(Filename) - 127, Tag
   If Not Tag = "TAG" Then
      Close #1
      HasTag = False
      Exit Sub
   End If
   HasTag = True
   Get #1, , Songname
   Get #1, , Artist
   Get #1, , Album
   Get #1, , Year
   Get #1, , Comment
   Get #1, , Genre
   Close #1
End Sub
VERSION 1.0 CLASS
BEGIN
   MultiUse = -1  'True
   Persistable = 0  'NotPersistable
   DataBindingBehavior = 0  'vbNone
   DataSourceBehavior  = 0  'vbNone
   MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "class_Mp3"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False

' #VBIDEUtils#************************************************************
' * Programmer Name  : Waty Thierry
' * Web Site         : www.geocities.com/ResearchTriangle/6311/
' * E-Mail           : waty.thierry@usa.net
' * Date             : 23/09/1999
' * Time             : 17:45
' **********************************************************************
' * Comments         : Read/Write ID3 Tags of Mp3 files
' *
' *
' **********************************************************************
Option Explicit

Private strData As String * 127
Private strPath As String

Private strArtist As String
Private strTitle As String
Private strAlbum As String
Private strYear As String
Private strComment As String

Private TagCreated As Boolean

Public Sub LoadMp3File(valPath As String)

   strPath = valPath

   Open strPath For Binary As #1
   Get #1, FileLen(valPath) - 127, strData
   Close #1

   TagCreated = False

   If TagExists = True Then
      strArtist = Mid(strData, 34, 30)
      strTitle = Mid(strData, 4, 30)
      strAlbum = Mid(strData, 64, 30)
      strComment = Mid(strData, 98, 30)
      strYear = Mid(strData, 94, 4)
   Else
      strArtist = ""
      strTitle = ""
      strAlbum = ""
      strYear = ""
      strComment = ""
   End If

End Sub

Property Get Artist() As String

   Artist = RTrim(strArtist)

End Property

Property Get Title() As String

   Title = RTrim(strTitle)

End Property

Property Get Album() As String

   Album = RTrim(strAlbum)

End Property

Property Get Year() As String

   Year = RTrim(strYear)

End Property

Property Get Comment() As String

   Comment = RTrim(strComment)

End Property

Public Sub CloseMp3File()

   Dim ToBeWritten As String

   SetAttr strPath, vbNormal

   Open strPath For Binary As #1

   FileLen (strPath)

   ToBeWritten = "TAG"
   Put #1, FileLen(strPath) - 127, ToBeWritten

   ToBeWritten = strTitle & String(30 - Len(strTitle), " ")
   Put #1, FileLen(strPath) - 124, ToBeWritten

   ToBeWritten = strArtist & String(30 - Len(strArtist), " ")
   Put #1, FileLen(strPath) - 94, ToBeWritten

   ToBeWritten = strAlbum & String(30 - Len(strAlbum), " ")
   Put #1, FileLen(strPath) - 64, ToBeWritten

   ToBeWritten = strYear & String(4 - Len(strYear), " ")
   Put #1, FileLen(strPath) - 34, ToBeWritten

   ToBeWritten = strComment & String(30 - Len(strComment), " ")
   Put #1, FileLen(strPath) - 30, ToBeWritten

   Close #1

   TagCreated = True

End Sub

Public Function TagExists() As Boolean

   If InStr(strData, "TAG") >= 1 Or TagCreated = True Then
      If Right(strData, Len(strData) - 3) <> String(Len(strData) - 3, " ") Then
         TagExists = True
         Exit Function
      End If
   End If

   TagExists = False

End Function

Property Let Artist(valArtist As String)

   strArtist = valArtist

End Property

Property Let Title(valTitle As String)

   strTitle = valTitle

End Property

Property Let Album(valAlbum As String)

   strAlbum = valAlbum

End Property

Property Let Year(valYear As String)

   strYear = valYear

End Property

Property Let Comment(valComment As String)

   strComment = valComment

End Property

Just make your choice :)
Avatar of diggz
diggz

ASKER

You seem quite a hotshot, with fancy code and all. You're even the top VB answering-machine around here..
Naw, I didnt mean to be rude :-)
I was very pleased with all that code, but..

The code you posted is actually not what i had in mind. Writing the tags to the mp3s is no problem. In fact, this has really nothing to do with the mp3s themselves:

I have a directory which contains an album in mp3 format. There are certain patterns in those mp3s' filenames that are the same, eg the artist, the ".mp3" extension, possibly the tracknumber (+1 for every song). Now, how do I seperate the title from the rest? that is what this is all about.

regards,

diggz

Avatar of diggz

ASKER

i've made an InputBox where the users tells me what comes first, secon and last:

%1 %2 - %3.mp3

for

TrackNo Artist - Songname.mp3

but I still dont know how to handle this (without a million lines of code)

regards.
So, If I anderstand, it is only string manipulation, use the INSTR function

Give me a sample, and I will give you the code to extract what yo need
Avatar of diggz

ASKER

here's a sample (at least i hope it's a sample):

I have a directory, with the "Gamba"'s newest album "Plunder". here's the filelist:

01 Gamba - Hopalong.mp3
02 Gamba - Jorge.mp3
03 Gamba - Plunder for the people.mp3
04 Gamba - Are you Gamba'd.mp3
05 Gamba - I'm a horse.mp3

none of these mp3s have an id3 tag, so i've made a program that creates these tags from the filenames, and this program is supposed to be usable on all albums. I've set all the elements in the tag, except the songtitle, which is different for each file. Now, i want the code to single out the songtitle from the FILEname. I find this very hard, and to easify it, i've made an inputbox where the user tells me what order the TrackNo, Artist, and Songname are in, and one or more of those three might even not be there.

In my code, i've created a sorted Filename array which contains all the filenames (not path) for the album, and i've resized every element in the array with -4 to remove the ".mp3" extension.

How can i fill my Title array just by using the filename array and the inputbox' format of the filenames ?

Here is as far as i've come with the Title array:

For j = 0 To NumberOfFiles
  ReDim Preserve Title(j)
  Title(j) = Mid(FileArray(j), 1, _
             Len(FileArray(j)) - 4)
Next j

...which only removes the ".mp3"

need more code? i dont think you need it, and it's extremely bad.

regards.

Use the following code :

sMP3 = GetTokens(sFile, " ")

it will return all the parts, but the last one will have the MP3 to remove

Public Function GetTokens(sToParse As String, sToken As String) As Variant
   ' #VBIDEUtils#************************************************************
   ' * Programmer Name  : Waty Thierry
   ' * Web Site         : www.geocities.com/ResearchTriangle/6311/
   ' * E-Mail           : waty.thierry@usa.net
   ' * Date             : 29/10/98
   ' * Time             : 18:12
   ' * Module Name      : String_Module
   ' * Module Filename  : D:\TWA\Lib\String.bas
   ' * Procedure Name   : GetTokens
   ' * Parameters       :
   ' *                    sToParse As String
   ' *                    sToken As String
   ' **********************************************************************
   ' * Comments         :
   ' *   Returns an array to tokenized values
   ' *   Ex:  GetTokens("This is a test.", " ") = ({ "This", "is", "a", "test." })
   ' *
   ' *
   ' **********************************************************************

   Dim nTokenLen       As Integer
   Dim nTokenCnt       As Integer
   Dim nOffset         As Long
   Dim nPrevOffset     As Long
   Dim aTokens()       As String

   nTokenLen = Len(sToken)
   nOffset = InStr(sToParse, sToken)

   Do While nOffset > 0
      ReDim Preserve aTokens(nTokenCnt)
      If nOffset - nPrevOffset > 1 Then
         aTokens(nTokenCnt) = Mid$(sToParse, nPrevOffset + 1, nOffset - 1 - nPrevOffset)
      Else
         aTokens(nTokenCnt) = ""
      End If

      nPrevOffset = nOffset
      nOffset = InStr(nOffset + nTokenLen, sToParse, sToken)
      nTokenCnt = nTokenCnt + 1
   Loop

   ReDim Preserve aTokens(nTokenCnt)
   aTokens(nTokenCnt) = Mid$(sToParse, nPrevOffset + 1)
   GetTokens = CVar(aTokens)

End Function
there is a module or class or something that also has a player with it ... i just don't know if it writes to the file  but i know it can get the tag from the mp3
Avatar of diggz

ASKER

sorry waty, but what if the format of the mp3 filename is like this:

TrackNo.Artist_Songname.mp3

for now, i'm using a temporary solution: i tell the user to write the songtitle of a certain filename, and that way, i can find out the strings that are before and after the title, and apply it to all the files.

thasmartuno: it's just what i'm looking for. it's not important that it writes the tags, i only need to get the songnames. Please tell me where to find this module

regards..


ASKER CERTIFIED SOLUTION
Avatar of waty
waty
Flag of Belgium image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of diggz

ASKER

well waty, it is a solution, but i'll stick to my own for now since is is more reliable. only thing left is to see if ThaSmartUno finds out something. if he does not, you will be given the points.

regards
it doesn't write to the file ... but it does read the ID3 tag ... not the file name itself
oh ... i don't know ... i could upload it ... and its a dll not a module ... i didn't read your comment =) ... if u want me to upload it ... just ask
Avatar of diggz

ASKER

well.. if it does not read the filename, only the tag, it's no use..

what shall we do then ?

waty: just answer, and i'll give you the points.

Avatar of diggz

ASKER

you dont need to.