Editing MP3 Tags

mdodge

Board Regular
Joined
May 3, 2004
Messages
97
MP3 files have imbedded metadata that I would like to edit. I have between 35K and 40K files that I would like to edit one or two fields in each. I would also like to export to an Excel database a selection of fields: Title, Artist, Comment and so on. I can open each individual file in File Explore and select Properties and edit the field but I could spend years doing that. Has anyone had any experience using VBA to automate the process? I have some experience with VBA over the years but the interface between Excel and File Explorer is my problem.
 
Yes. That is how it shows in File Explorer. The ID3 tag property is called TrackPosition. If I could figure out why I can read that property (using the posted code) but can't edit it that would be great. There are other threads where using CddbControlRoxio.dll was a solution so I tried to download and register that file but it seems it is dependent upon another dll. Windows regsvr32 doesn't reveal dependencies when it fails . There are utilities you can install to determine dependencies but I don't want to go down that road. I think that the problem with most of these tag editing threads is that they're so old. I figure that has something to do with the fact that many codes don't work anymore.
Yes. Same with me. I could never make any of those dlls work be it CddbControl.dll or CddbControlRoxio.dll, not even properly register them. Even worse if using x64 excel.

Relying on 3rd party libraries is often bound to cause problems down the road. I think the only reliable way is to use the IPropertyStore and then defining the correct PROPERTYKEY fom the wtypes.h header file.

This requires performing some low level vtable calls. I have succesfully done similar stuff whith other interfaces so I will give this a shot and post back if anything useful comes up.

Regards.
 
Upvote 0

Excel Facts

Copy PDF to Excel
Select data in PDF. Paste to Microsoft Word. Copy from Word and paste to Excel.
I don't want you to waste any time for me, so I'll reiterate what I was trying to do. I've seen a few (and downloaded/tried 2) tag editing programs. They appear to make it easy to do many things, but batch editing of tags (in my case, the track position) to match any particular part of the file name. For example, if I have
file name: 02 Back in Black I want the track position to be 2. I'm certain that I can do this with VBA over hundreds/thousands of files if only it would work. Otherwise, it is a manual one by one operation using the tag editor program and that's not an option for me. Since people always ask why, it's because my car lists songs by folder, artist, title and maybe other things. If I choose a folder I'll get a list by alpha and 02 will come after 01. If I choose artist or title the list will be by track position and I'd rather these 2 lists be in the same order regardless of the view.

I hope I have not hijacked this thread now...
 
Upvote 0
I don't want you to waste any time for me, so I'll reiterate what I was trying to do. I've seen a few (and downloaded/tried 2) tag editing programs. They appear to make it easy to do many things, but batch editing of tags (in my case, the track position) to match any particular part of the file name. For example, if I have
file name: 02 Back in Black I want the track position to be 2. I'm certain that I can do this with VBA over hundreds/thousands of files if only it would work. Otherwise, it is a manual one by one operation using the tag editor program and that's not an option for me. Since people always ask why, it's because my car lists songs by folder, artist, title and maybe other things. If I choose a folder I'll get a list by alpha and 02 will come after 01. If I choose artist or title the list will be by track position and I'd rather these 2 lists be in the same order regardless of the view.

I hope I have not hijacked this thread now...
I too am interested in being able to edit mp3 tags with vba. I have seen this question asked many times before but there has never been an answer at least that I am aware of ... So, really, I won't be wasting my time in vain if I look into this.

Are all the song titles following the same pattern ie 0# + space + text ?
 
Upvote 0
Are all the song titles following the same pattern ie 0# + space + text ?
Not quite,
##
+space+filename so first 2 characters are like 01, 02, ... 10, 11 ,12 ...
The reason I'm on this project is because so much of my stuff was on vinyl so tags are missing because of the audio source. They look like
1708473921472.png

You can see that there are no position tag values for this folder of files. Now that pic might make one think that the ability to edit a tag is not possible unless that property exists (as if it were a collection of properties that are not built in) but don't let that fool you. I have not been able to edit the position property for files that do have a value, yet I've been able to retrieve them. But if I use a tag editor app, I can edit the position property - one by one. So if no value is shown, I figure it is an empty string.
 
Upvote 0
Not quite,
##
+space+filename so first 2 characters are like 01, 02, ... 10, 11 ,12 ...
The reason I'm on this project is because so much of my stuff was on vinyl so tags are missing because of the audio source. They look like
View attachment 107189
Ok. So we are talking about song file names (not song titles) . Those are two diff property tags.
Also, are the first starting ##s always joined with the alpha characters that follow via a hyphen ?

I assume that the above image shot was taken from a windows explorer folder?
 
Upvote 0
Yes, the image is from File Explorer. My apologies - I made an image from a poor sample folder because that folder has not be formatted yet. The file name (which is what I've consistently referred to?) is of no consequence IMO. If I can manage to edit ID3 tags, the rest I can manage on my own as I'm fairly experienced with Access vba. The overall plan is to catalog thousands of songs in a db. Using the current file names (e.g. C:\Users\Micron\Music\Rush\Moving Pictures\YYZ.mp3) I will fix the file names via various edits, to remove hyphens, octothorpes, whatever. I will end up with ##+space+filename including the mp3 extension. When I choose "folder" view in my car I will see the extension, but that doesn't matter to me. In summary, I'm pretty sure that if I can manage to edit any ID3 tag I can do the rest.
Regards.
 
Upvote 0
Ok- I seem to have managed to live-edit the mp3 TrackNumber Tag using the IPropertyStore interface.

I have only tested this on Windows 10. I think that the minimum OS requirement is Windows XP that has SP2 and Windows Vista.

Place the following code in a new Standard Module:
VBA Code:
Option Explicit

#If VBA7 Then
    Private Declare PtrSafe Function DispCallFunc Lib "oleAut32.dll" (ByVal pvInstance As LongPtr, ByVal offsetinVft As LongPtr, ByVal CallConv As Long, ByVal retTYP As Integer, ByVal paCNT As Long, ByRef paTypes As Integer, ByRef paValues As LongPtr, ByRef retVAR As Variant) As Long
    Private Declare PtrSafe Sub SetLastError Lib "kernel32.dll" (ByVal dwErrCode As Long)
    Private Declare PtrSafe Function CLSIDFromString Lib "ole32" (ByVal OleStringCLSID As LongPtr, ByRef cGUID As Any) As Long
    Private Declare PtrSafe Function SHGetPropertyStoreFromParsingName Lib "shell32" (ByVal pszPath As LongPtr, pbc As Any, ByVal Flags As Long, riid As GUID, ppv As Any) As Long
#Else
    Private Enum LongPtr
        [_]
    End Enum
    Private Declare Function DispCallFunc Lib "oleAut32.dll" (ByVal pvInstance As LongPtr, ByVal offsetinVft As LongPtr, ByVal CallConv As Long, ByVal retTYP As Integer, ByVal paCNT As Long, ByRef paTypes As Integer, ByRef paValues As LongPtr, ByRef retVAR As Variant) As Long
    Private Declare Sub SetLastError Lib "kernel32.dll" (ByVal dwErrCode As Long)
    Private Declare Function CLSIDFromString Lib "ole32" (ByVal OleStringCLSID As LongPtr, ByRef cGUID As Any) As Long
    Private Declare Function SHGetPropertyStoreFromParsingName Lib "shell32" (ByVal pszPath As LongPtr, pbc As Any, ByVal Flags As Long, riid As GUID, ppv As Any) As Long
#End If

#If Win64 Then
    Private Const NULL_PTR = 0^
    Private Const PTR_LEN = 8&
#Else
    Private Const NULL_PTR = 0&
    Private Const PTR_LEN = 4&
#End If

Private Enum Vtble_Ordinals
    'IUnknown
    QueryInterface = 0&
    Release = 2&
    'IPropertyStore
    SetValue = 6&
    Commit = 7&
End Enum

Private Type GUID
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(0 To 7) As Byte
End Type

Private Type PROPERTYKEY
    fmtid As GUID
    pid As Long
End Type


Public Function Set_MP3_TrackNumber_Value(ByVal sMP3File As String, ByVal NewValue As Variant) As Boolean

    Const IID_PropertyStore = "{886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99}"
    Const GPS_READWRITE = 2&, GPS_OPENSLOWITEM = &H10
    Const CC_STDCALL = 4&, S_OK = 0&
    Dim tIID As GUID, pPS As LongPtr
 
    If CLSIDFromString(StrPtr(IID_PropertyStore), tIID) <> S_OK Then
        Debug.Print "unable to get the IID_PropertyStore interface."
        GoTo ReleaseInterface
    End If
 
    If SHGetPropertyStoreFromParsingName _
      (StrPtr(sMP3File), ByVal NULL_PTR, GPS_READWRITE Or GPS_OPENSLOWITEM, tIID, pPS) <> S_OK Then
        Debug.Print "unable to get the property store from the file."
        GoTo ReleaseInterface
    End If
 
    ' IPropertyStore::SetValue
    If vtblCall(pPS, SetValue * PTR_LEN, vbLong, CC_STDCALL, _
        VarPtr(PKEY_Music_TrackNumber), VarPtr(CVar(NewValue))) <> S_OK Then
        Debug.Print "unable to set the property value."
        GoTo ReleaseInterface
    End If
 
    ' IPropertyStore::Commit
    If vtblCall(pPS, Commit * PTR_LEN, vbLong, CC_STDCALL) <> S_OK Then
        Debug.Print "unable to commit the property value."
        GoTo ReleaseInterface
    End If

    Set_MP3_TrackNumber_Value = True

ReleaseInterface:
    If pPS Then
        ' IPropertyStore::Release
        Call vtblCall(pPS, Release * PTR_LEN, vbLong, CC_STDCALL)
    End If

End Function

Private Function PKEY_Music_TrackNumber() As PROPERTYKEY
    Dim tPk As PROPERTYKEY
    With tPk.fmtid
        .Data1 = &H56A3372E
        .Data2 = CInt(&HCE9C)
        .Data3 = CInt(&H11D2)
        .Data4(0) = &H9F
        .Data4(1) = &HE
        .Data4(2) = &H0
        .Data4(3) = &H60
        .Data4(4) = &H97
        .Data4(5) = &HC6
        .Data4(6) = &H86
        .Data4(7) = &HF6
    End With
    tPk.pid = 7&
    PKEY_Music_TrackNumber = tPk
End Function

Private Function vtblCall( _
    ByVal InterfacePointer As LongPtr, _
    ByVal VTableOffset As Long, _
    ByVal FunctionReturnType As Long, _
    ByVal CallConvention As Long, _
    ParamArray FunctionParameters() As Variant _
) As Variant
        Dim vParamPtr() As LongPtr

    If InterfacePointer = 0& Or VTableOffset < 0& Then Exit Function
    If Not (FunctionReturnType And &HFFFF0000) = 0& Then Exit Function

    Dim pIndex As Long, pCount As Long
    Dim vParamType() As Integer
    Dim vRtn As Variant, vParams() As Variant

    vParams() = FunctionParameters()
    pCount = Abs(UBound(vParams) - LBound(vParams) + 1&)
    If pCount = 0& Then
        ReDim vParamPtr(0 To 0)
        ReDim vParamType(0 To 0)
    Else
        ReDim vParamPtr(0 To pCount - 1&)
        ReDim vParamType(0 To pCount - 1&)
        For pIndex = 0& To pCount - 1&
            vParamPtr(pIndex) = VarPtr(vParams(pIndex))
            vParamType(pIndex) = VarType(vParams(pIndex))
        Next
    End If

    pIndex = DispCallFunc(InterfacePointer, VTableOffset, CallConvention, FunctionReturnType, pCount, _
    vParamType(0), vParamPtr(0), vRtn)
    If pIndex = 0& Then
        vtblCall = vRtn
    Else
        SetLastError pIndex
    End If

End Function


Code Usage:

WARNING!
First time you try this code, please do it on a mock folder containing mock mp3 files, just in case anything goes wrong.
Once you are confident that the code works as expected, apply it to your actual folder\files.

VBA Code:
Sub Test()

    ' This routine will edit the TrackNumber Tag in all the mp3 files
    ' in the specific folder based on the first starting numbers in the file names.

    Dim oFso As Object
    Dim oFolder As Object
    Dim sFileName As Object
    Dim sFolderPath As String
    Dim lHyphenPos As Long, sExtractedNumFromFileName As String, lTotalFileTagsEdited As Long

    sFolderPath = "C:\Users\hp\Music\Playlists"  ' <= change music folder path to suit.
  
    If Right(sFolderPath, 1&) <> Application.PathSeparator Then
        sFolderPath = sFolderPath & Application.PathSeparator
    End If
  
    Set oFso = CreateObject("Scripting.FileSystemObject")
    Set oFolder = oFso.GetFolder(sFolderPath)
  
    For Each sFileName In oFolder.Files
        If UCase(oFso.GetExtensionName(sFileName.Path)) = "MP3" Then
            lHyphenPos = InStr(sFileName.Name, "-")
            If lHyphenPos > 1& Then
                sExtractedNumFromFileName = Trim(Left(sFileName.Name, lHyphenPos - 1&))
                If IsNumeric(sExtractedNumFromFileName) Then
                    If Set_MP3_TrackNumber_Value _
                         (sMP3File:=sFileName.Path, NewValue:=CLng(sExtractedNumFromFileName)) Then
                         lTotalFileTagsEdited = lTotalFileTagsEdited + 1&
                    End If
                End If
            End If
        End If
    Next
  
    Set oFso = Nothing: Set oFolder = Nothing
  
    MsgBox "Total files successfully edited : [" & lTotalFileTagsEdited & "]", , "MP3 TrackNumber Tag Editor"
  
End Sub
 
Last edited:
Upvote 0
Here's the results of compiling the code:
1708528120362.png

I presume that no library reference is required to fix an API function compile error, but I'm guessing the next one would be a reference issue.
1708528228764.png

I should be able to get around this easily enough but Application.PathSeparator is an Excel object reference. I'm doing this in Access as I mentioned.
VBA Code:
    If Right(sFolderPath, 1&) <> Application.PathSeparator Then
These have to be at the top of the module. When I move them there those compile errors were fixed.
Private Enum Vtble_Ordinals, Private Type GUID and Private Type PROPERTYKEY

I really appreciate the effort you put into solving the issue. I'll see what I can do to get it working.
 
Upvote 0
So, did placing all the Type and Enum declarations at the top of the module (ie: above the api declarations) solve the two compile errors in the end?

VBA Code:
 If Right(sFolderPath, 1&) <> Application.PathSeparator Then
can be easly replaced with this
VBA Code:
    If Right(sFolderPath, 1&) <> "\" Then
        sFolderPath = sFolderPath & "\"
    End If

I presume that no library reference is required to fix an API function compile error, but I'm guessing the next one would be a reference issue.
No library references are required.

PS: Can you try the code in excel for testing? what happens? doest it work on your side?

Can you please let me know if this works for you like it did for me as I am planning to write a generic class for reading and editing file tags using this IPropertyStore technique.
 
Last edited:
Upvote 0
So, did placing all the Type and Enum declarations at the top of the module (ie: above the api declarations) solve the two compile errors in the end?
I think you are referring to this comment
SHGetPropertyStoreFromParsingName
so yes but I guess I should have written "moved".
When I move them there those compile errors were fixed.
I've already applied the same fix for the PathSeparator issue.
I'll try to compile the original code in Excel first but I suspect I will still have to move the type declarations there also. This will have to wait until tonight (based on EST time zone).
 
Upvote 0

Forum statistics

Threads
1,223,228
Messages
6,170,874
Members
452,363
Latest member
merico17

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top