ID3 UDF ID3v1 & ID3v2 MP3 Tags

180 posts in this topic

Posted (edited) · Report post

ID3.au3 UDF Description:

  • Reads ID3v1.0, ID3v1.1, ID3v1.1+, ID3v2.2, ID3v2.3, ID3v2.4, APEv2 and MPEG frame header
  • Writes ID3v1.1, ID3v2.3, ID3v2.4
  • Latest Version v3.4 20120610
  • AutoIt Version Required: 23rd December, 2011 - v3.3.8.0 of AutoIt
  • ID3 functions only works with .mp3 files

Notes:

Changes in latest version (see files for all changes):
ID3_v3.4.au3 (06/10/2012)

  • Added resetting of all global ID3 variables in _ID3ReadTag() to make sure they are set back to the default values
  • changed _ID3WriteTag() to determine what tags have been read and to only write back those version if $iTagVersion=-1
  • edited _ID3WriteTag() comments
  • added $iReturnTypeFlag to _ID3GetTagField() inputs for ID3v2 functions
  • fixed _ID3GetTagField() so that @extended is set to the Number of frames that exist with same $sFrameIDRequest
  • fixed _h_ID3v2_EncodeStringToBinary() variable typo ($FrameData should be $bFrameData), thanks BrewManNH!

ID3_Example_GUI_v3.4.au3 (06/10/2012)

  • changed all _ID3v1Field_GetString and _ID3v2Field_GetString to _ID3GetTagField

ID3_SimpleExamples_v3.4.au3 (06/10/2012)

  • I have realized that the GUI example has become overly complex, so I have added this file to help show how I intended ID3.au3 to be used.

Simple Read Tag Example:


$Filename = FileOpenDialog ( "Select Mp3 File", "", "Muisc (*.mp3)")
$sTagInfo = _ID3ReadTag($Filename)
MsgBox(0,"TagInfo",$sTagInfo)
MsgBox(0,"ID3v1 Title",_ID3GetTagField("Title")) ;Title from ID3v1
MsgBox(0,"ID3v2 Title",_ID3GetTagField("TIT2")) ;Title from ID3v2

Simple Write Tag Example:


$Filename = FileOpenDialog( "Select Mp3 File", "", "Muisc (*.mp3)")
$sTagInfo = _ID3ReadTag($Filename)
_ID3SetTagField("COMM","TEST COMMENT - ID3v2 Comment Tag")
_ID3WriteTag($Filename)



Latest Version v3.4 20120610

ID3v2.3TagSpec.pdf

ID3v2.4TagSpec.pdf

post-21864-0-66446400-1339116656_thumb.p

post-21864-0-87423500-1339176159_thumb.p

ID3_Example_GUI_v3.4.au3

ID3_SimpleExamples_v3.4.au3

ID3_v3.4.au3

Edited by joeyb1275

Share this post


Link to post
Share on other sites



Posted · Report post

nice job!!

Share this post


Link to post
Share on other sites

Posted · Report post

Very Nice! works great!

Share this post


Link to post
Share on other sites

Posted · Report post

After searching differant ways to read the ID3 tags on an mp3 file I came across a few dlls that might help,

cddbcontrol.dll

id3com.dll

UltraID3Lib.dll

I was wondering if anybody had any luck using these to read/write the tag information. The most promising looked like the ID3Tag object in id3com.dll, but I couldn't get to far with it.

Please let me know if there is anything better out there for reading and writing tags, for all the tag fields.

Share this post


Link to post
Share on other sites

Posted · Report post

nice job.. i've beeing looking into id tags editing/reading for a while..

Share this post


Link to post
Share on other sites

Posted · Report post

joyeb1275

After searching differant ways to read the ID3 tags on an mp3 file I came across a few dlls that might help,

cddbcontrol.dll

id3com.dll

UltraID3Lib.dll

Where you find two last dll's? can you show the link please? i use the first one (cddbcontrol.dll), and it's help me to write/read id3 tags, but sometimes it's make my app to crash :shocked:

Share this post


Link to post
Share on other sites

Posted (edited) · Report post

joyeb1275

Where you find two last dll's? can you show the link please? i use the first one (cddbcontrol.dll), and it's help me to write/read id3 tags, but sometimes it's make my app to crash :shocked:

I came across these dlls in the first post of a topic

http://www.autoitscript.com/forum/index.ph...p;hl=id3com.dll

I then googled the names of the dlls and found a bunch of sites where I could download them.

I didn't have much luck with them. I really want the ability to extract the album art in the ID3V2 Tags and I could not see how that could be done with any one of those dlls.

Good Luck!

Edited by joeyb1275

Share this post


Link to post
Share on other sites

Posted · Report post

If I need all of the information, this code appears to open and close the MP3 file for every tag I want, which can be quite slow. Any chance on writing a function to return an array of all of the tags with just one open/close?

Share this post


Link to post
Share on other sites

Posted · Report post

If I need all of the information, this code appears to open and close the MP3 file for every tag I want, which can be quite slow. Any chance on writing a function to return an array of all of the tags with just one open/close?

Yeah that would be no problem. Is it really that slow? I've actually read thousands of mp3s in a loop and it didn't seem to take that long. The reason it is built this way is so that if you only want one tag field, I didn't want to have to wait for all the fields to be read. When I first wrote this it did a single opening and closing of the file as you suggested but then when I started using it and adding more fields it would take a long time to get the album art if thats all I wanted. So in an attempt to optimize the speed for a sigle tag quary I made it open and close the file for each tag quary. I think I'll write a seperate function to retrieve all the tags and return them in an array. I'm also trying to write my own FileRead functions from ReadFile() in Kernel32.dll to optimize the way I have to offset and scan into a file. It might be a little while before I can write a function to read all the tags to an array so I welcome you to hack apart my code and see if you can create a .....

_ID3ReadToArray() perhaps? Maybe as an input there could be a $sfilter to get the tags that the user wants like if $sfilter = "Artist|Album|Title|Track" then the returned array will be have 5 elements

[0] = 4

[1] = Artist

[2] = Album

[3] = Title

[4] = Track

or if $sfilter = -1 it would get all the tag fields. And maybe still have an input for what version id3tag to get with the ability to get both versions. So I'm thinking it would look like..

Func _ID3ReadToArray($Filename, $sfilter = -1, $Version = 2)
     ...
EndFunc

What do you think? Would a function like this be useful to you? Now that I've thought about it some more I might add it sooner then later :shocked:

Share this post


Link to post
Share on other sites

Posted · Report post

Yes, it is slow when you are grabbing all of the tags. You suggested function would be a great addition as long as it minimized file IO.

Do you think there might be able possibility of reading a few more of the tags like bitrate, channels, KPS, etc.?

Thanks.

Dave

Share this post


Link to post
Share on other sites

Posted (edited) · Report post

Yes, it is slow when you are grabbing all of the tags. You suggested function would be a great addition as long as it minimized file IO.

Do you think there might be able possibility of reading a few more of the tags like bitrate, channels, KPS, etc.?

Thanks.

Dave

bitrate, channels, KPS... that information is in the MPEG Header, I'm still working on a reader for that.

I changed my entire code for ID3.au3 it should be better only one fileopen and more frame ids are included you only need to use _ID3TagToArray() and if you don't want all the frames just set the $sFilter to the FrameIds of the ones you want. I included a function to delete any files created by ID3.au3 like the album art and now the lyrics are saved to a text file. I minimized the file includes and i think the code a bit more readable. When I first started this project I was still a new user of AutoIt, so my code was not the greatest but it worked.

Here is an example script

#include-once
#include <ID3.au3>
#include <Array.au3>

$Filename = FileOpenDialog ( "Select Mp3 File", "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}", "Music (*.mp3)")

$sFilter = "TIT2|TALB|TPE1|TYER|APIC"
$ID3v2Tag = _ID3TagToArray($Filename,2,$sFilter)
;~ $ID3v2Tag = _ID3TagToArray($Filename,2)
_ArrayDisplay($ID3v2Tag,"$ID3v2Tag")

$ID3v1Tag = _ID3TagToArray($Filename,1)
_ArrayDisplay($ID3v1Tag,"$ID3v1Tag")


_ID3DeleteFiles()

See first post for latest version . The other files that were originally posted are not needed!

Let me know if you have problems with it.

Edited by joeyb1275

Share this post


Link to post
Share on other sites

Posted (edited) · Report post

Posted latest version...

Added support for ID3v2.2 and fix a bug for ID3v2 tag reading.

Edited by joeyb1275

Share this post


Link to post
Share on other sites

Posted · Report post

Thanks for working on this. I'll do some benchmarking, change the code to use the new code and repeat my tests. I'll report when I'm done.

Looking forward to the new tags. In looking at the spec for ID3, I am amazed at the flexability but at the cost of complexity. You are doing good work.

Dave

Share this post


Link to post
Share on other sites

Posted · Report post

I created a fileset of 129 MP3 files (4.7 GB) and ran the older code and the newer code. I also ran it against the same files on a hard drive and on a DVD.

The new code was 7.8% faster from the DVD and 16% faster from a hard drive. Well worth the effort I think

In search of more speed( :( ) I looked at your code. I started with the V1 code as it's the simplest. You are doing one FileOpen(), several FileRead() calls, then a FileClose. I wondered whether one FileOpen, one FileRead() for the entire 128 bytes of the tag, then a series of in-memory BinaryMid() calls (beta only) then a FileClose() would be faster. I rewrote the _ReadID3V1() function like this and guess what? It's about the same speed. :shocked:

Oh well. Thanks again.

Dave

Share this post


Link to post
Share on other sites

Posted (edited) · Report post

I created a fileset of 129 MP3 files (4.7 GB) and ran the older code and the newer code. I also ran it against the same files on a hard drive and on a DVD.

The new code was 7.8% faster from the DVD and 16% faster from a hard drive. Well worth the effort I think

In search of more speed( :( ) I looked at your code. I started with the V1 code as it's the simplest. You are doing one FileOpen(), several FileRead() calls, then a FileClose. I wondered whether one FileOpen, one FileRead() for the entire 128 bytes of the tag, then a series of in-memory BinaryMid() calls (beta only) then a FileClose() would be faster. I rewrote the _ReadID3V1() function like this and guess what? It's about the same speed. :shocked:

Oh well. Thanks again.

Dave

That was a good idea but its too bad it was still the same speed. I have another idea, I found a UDF for the kernel32.dll file reading functions and there is a function called _APIFileSetPos( ByRef $hFile, ByRef $nPos ) this sets the position of the file pointer. So what I'm thinking is for _ReadID3V1() instead of FileRead($hfile,FileGetSize($Filename)-128) to get to the right position in the file maybe trying _APIFileSetPos( ByRef $hFile, ByRef $nPos ) . I'm not sure there will be a big differance, but if you really want speed, I think its worth it. Nice job with the speed testing! I will try the same.

I don't think I want to include APIFileReadWrite.au3. I'd like to keep it simple.

I updated ID3.au3 again...

added a minor bug fix

also when $sFilter is not -1 the returned array will only be the items that are in $sFilter.

Edited by joeyb1275

Share this post


Link to post
Share on other sites

Posted · Report post

I agree with you about including other code, especially when it may or may not make a difference.

Your new filter code addition is very timely for me. I will use it tomorrow. Does it return the tags in the order it was specified in the filter?

Dave

Share this post


Link to post
Share on other sites

Posted · Report post

I agree with you about including other code, especially when it may or may not make a difference.

Your new filter code addition is very timely for me. I will use it tomorrow. Does it return the tags in the order it was specified in the filter?

Dave

It returns the tags in the order they are found in the file. I think doing some kind of array sorting might slow things down. I would like to return the tags in the order that the user requests them, so I'll give it some more thought.

Share this post


Link to post
Share on other sites

Posted · Report post

Array sorting (as implemented in AutoIt) is rather slow. If your code would return the tags in the order they were specified, it would make for a predictable result.

Dave

Share this post


Link to post
Share on other sites

Posted · Report post

forgive me for bein stupid here but how do i use this UDF. i used include on id3.au3 and placed the .au3 in my include folder but the only thing i get back when i define $filename is the $frame msgbox. Nowhere do i see the array and no files are saved anywhere.

Share this post


Link to post
Share on other sites

Posted · Report post

forgive me for bein stupid here but how do i use this UDF. i used include on id3.au3 and placed the .au3 in my include folder but the only thing i get back when i define $filename is the $frame msgbox. Nowhere do i see the array and no files are saved anywhere.

What is the string that defines $filename? It should have the fullpath to the .mp3. What is the line of code where you call $ID3v2Tag = _ID3TagToArray()? Are you using the latest id3.au3(see first post)? I did a search for Msgbox(0,"$frame" in my code and it did not find that string so I'm not sure where that msgbox came from.

Share this post


Link to post
Share on other sites

Posted · Report post

Updated ID3.au3 (see first post)

New Function:

_ID3GetTagField($Filename, $sFieldIDRequest)

It will read all the tag info into a buffer and return the requested tag info. If called again it checks the buffer to see if the tag data is there and returns the requested tag info. The file is still only opened once. See the comments for return values and @error and @extended values.

Here is some example code to use it:

$Filename = FileOpenDialog ( "Select Mp3 File", "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}", "Muisc (*.mp3)")


$Title = _ID3GetTagField($Filename, "TIT2") ;gets ID3v2 Title
$err = @error
$ext = @extended
MsgBox(0,"$Title",$Title)
MsgBox(0,"Error & Extended", "Error: " & $err & @CRLF & "Extended: " & $ext)


$Title = _ID3GetTagField($Filename, "Title") ;gets ID3v1 Title
$err = @error
$ext = @extended
MsgBox(0,"$Title",$Title)
MsgBox(0,"Error & Extended", "Error: " & $err & @CRLF & "Extended: " & $ext)


$Comm = _ID3GetTagField($Filename, "COMM")
$err = @error
$ext = @extended
MsgBox(0,"$Comm",$Comm)
MsgBox(0,"Error & Extended", "Error: " & $err & @CRLF & "Extended: " & $ext)


_ID3DeleteFiles()

Let me know if there are any bugs.

Share this post


Link to post
Share on other sites

Posted · Report post

In regards to id3 tags....

Anyone know what tag WMP writes to when you enable the global rating scheme...this is your personal rating that it writes to the file so your ratings don't get lost if you format your system and lose your WMP database.

Also need to know how to read/write it.

Thanks.

Share this post


Link to post
Share on other sites

Posted · Report post

Hi, I have just written a script for writing ID3v1 and ID3v2 tags using the ID3COM.DLL library. I have found there are more versions of this dll available on the net, so the one i have used was downloaded from http://mysite.wanadoo-members.co.uk/johnadcock/ID3COM.htm.

After downloading you will need to register the dll using "regsvr32 id3com.dll" from the command line.

Here is the sample code, maybe it will be useful for someone.

CODE
$tag = ObjCreate("Id3com.ID3comTag")

If Not IsObj($tag) Then

MsgBox (0,"error","error")

Exit

EndIf

$tag.link("testfile.mp3")

$tag.title = "Sample song"

$tag.Artist = "Some great artist"

$tag.Album = "The best of"

$tag.year = "1981"

$tag.comment = "Some comments..."

$tag.track = 11

$tag.Genre = 12

$tag.Savev1Tag

$tag.Savev2Tag

Share this post


Link to post
Share on other sites

Posted · Report post

Is it possible to read out the interpreter of the mp3 file? Your example does just return twice the title, but the third msgbox is empty.

Share this post


Link to post
Share on other sites

Posted · Report post

Sorry for the bump.. But I was having trouble getting this going...

821,43) : ERROR: BinaryString(): undefined function. $ImageHdr = BinaryString("0x" & $ImageHdr)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^

Know how I can get this going ?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.