Make your own free website on Tripod.com

Intro

O.K.  If you don't already know, ID3Lib is the best open source MP3 tagging library available today... implementing full read and write of ID3v2.3 frames.  I discovered this library in December of 2000 while I was working on my MP3 librarian project... in Visual Basic.  This page documents my success in this effort.

The first thing a VB programmer will discover after downloading the source from the ID3Lib site, is that the project is written in C.  =)  A compiled binary DLL is also included in the project, but if you attempt to "declare" the functions or subs of the library, you will get a type mismatch error when you actually try to use the functions.  This has to do with how the functions are publicly exported from the dll (thank you again, Lothar).  =)  Delphi programmers will experience the same error.

Thus... it becomes necessary to modify the project.

 

Project Changes

In order to export the functions in a form VB (and Delphi) can use, 3 files must be modified (globals.h, id3lib.h, and c_wrapper.cpp) and 1 file must be added (id3lib.def).

globals.h

...

/* id3lib version.
* we prefix variable declarations so they can
* properly get exported in windows dlls.
* (borrowed from glib.h http://www.gtk.org)
*/

#ifdef WIN32
#   ifdef ID3LIB_COMPILATION
#      define ID3_C_EXPORT extern _declspec(dllexport)
# define CCONV __stdcall // Added 2000-12-12 For VB Compatibility - By FrogPrince Advised By Lothar
# else /* !ID3LIB_COMPILATION */
# define ID3_C_EXPORT extern _declspec(dllimport)
# define CCONV __stdcall // Added 2000-12-12 For VB Compatibility - By FrogPrince Advised By Lothar
# endif /* !ID3LIB_COMPILATION */
#else /* !WIN32 */
# define ID3_C_EXPORT
# define CCONV // Added 2000-12-12 For VB Compatibility - By FrogPrince Advised By Lothar
#endif /* !WIN32 */

...

 

id3lib.h
(example subset - all of the exports must be modified like this)

typedef struct { char _dummy; } ID3Field;
/* tag wrappers */
ID3_C_EXPORT ID3Tag *CCONV ID3Tag_New (void);
ID3_C_EXPORT void CCONV ID3Tag_Delete (ID3Tag *tag);
ID3_C_EXPORT void CCONV ID3Tag_Clear (ID3Tag *tag);
ID3_C_EXPORT bool CCONV ID3Tag_HasChanged (const ID3Tag *tag);
ID3_C_EXPORT void CCONV ID3Tag_SetUnsync (ID3Tag *tag, bool unsync);
ID3_C_EXPORT void CCONV ID3Tag_SetExtendedHeader (ID3Tag *tag, bool ext);
ID3_C_EXPORT void CCONV ID3Tag_SetPadding (ID3Tag *tag, bool pad);
ID3_C_EXPORT void CCONV ID3Tag_AddFrame (ID3Tag *tag, const ID3Frame *frame);
ID3_C_EXPORT void CCONV ID3Tag_AttachFrame (ID3Tag *tag, ID3Frame *frame);
ID3_C_EXPORT void CCONV ID3Tag_AddFrames (ID3Tag *tag, const ID3Frame *frames, size_t num);
ID3_C_EXPORT ID3Frame *CCONV ID3Tag_RemoveFrame (ID3Tag *tag, const ID3Frame *frame);
ID3_C_EXPORT ID3_Err CCONV ID3Tag_Parse (ID3Tag *tag, const uchar header[ID3_TAGHEADERSIZE], const uchar *buffer);
ID3_C_EXPORT size_t CCONV ID3Tag_Link (ID3Tag *tag, const char *fileName);
ID3_C_EXPORT ID3_Err CCONV ID3Tag_Update (ID3Tag *tag);

 

c_wrapper.cpp
(example subset - all of the defines must be modified like this)

// tag wrappers
#define ID3_CATCH(code) try { code; } catch (...) { }
ID3_C_EXPORT ID3Tag *CCONV
ID3Tag_New(void)
{
ID3_Tag* tag = NULL;
ID3_CATCH(tag = new ID3_Tag);
return reinterpret_cast<ID3Tag *>(tag);
}

ID3_C_EXPORT void CCONV
ID3Tag_Delete(ID3Tag *tag)
{
if (tag)
{
ID3_CATCH(delete reinterpret_cast<ID3_Tag*>(tag));
}
}

ID3_C_EXPORT void CCONV
ID3Tag_Clear(ID3Tag *tag)
{
if (tag)
{
ID3_CATCH(reinterpret_cast<ID3_Tag*>(tag)->Clear());
}
}

ID3_C_EXPORT bool CCONV
ID3Tag_HasChanged(const ID3Tag *tag)
{
bool changed = false;
if (tag)
{
ID3_CATCH(changed = reinterpret_cast<const ID3_Tag * >(tag)->HasChanged());
}
return changed;
}

ID3_C_EXPORT void CCONV
ID3Tag_SetUnsync(ID3Tag *tag, bool unsync)
{
if (tag)
{
ID3_CATCH(reinterpret_cast<ID3_Tag *>(tag)->SetUnsync(unsync));
}
}

ID3_C_EXPORT void CCONV
ID3Tag_SetExtendedHeader(ID3Tag *tag, bool ext)
{
if (tag)
{
ID3_CATCH(reinterpret_cast<ID3_Tag *>(tag)->SetExtendedHeader(ext));
}
}

ID3_C_EXPORT void CCONV
ID3Tag_SetPadding(ID3Tag *tag, bool pad)
{
if (tag)
{
ID3_CATCH(reinterpret_cast<ID3_Tag *>(tag)->SetPadding(pad));
}
}

 

id3lib.def
(example subset - you need to add this file in order to clean up the exported function names.  In order, all of the functions exported from id3lib not to be listed here with their names.)

LIBRARY id3lib

EXPORTS

ID3Tag_New @1
ID3Tag_Delete @2
ID3Tag_Clear @3
ID3Tag_HasChanged @4
ID3Tag_SetUnsync @5
ID3Tag_SetExtendedHeader @6
ID3Tag_SetPadding @7
ID3Tag_AddFrame @8
ID3Tag_AttachFrame @9
ID3Tag_AddFrames @10
ID3Tag_RemoveFrame @11
ID3Tag_Parse @12
...

The complete copy of these modified files is available for download... under the heading of vbID3Lib since these changes have not yet been officially made in project.  Remember if you compile and distribute this dll to name it vbID3Lib.dll.  Until those changes are made in the release source, the copy of the source available here will remain current with the latest releases of the ID3Lib and also include any currently available patches or fixes.

 

Using In VB

Once you have a workable dll, the next step is to declare all of the functions and subs in a VB module.  Also, you will find it handy to go through the globals.h file and replicate all of the enums and constants out into VB as well, so you can use them in your declare statements and in your own VB functions.  I have, however, already done both of these tasks and produced a module called ID3Constants.bas... which is available as part of the sample VB project and that you are more than welcome to use.

The next trick is to learn how the DLL actually expects to operate.  =)  While it sounds simple... I found I had to go through a bit of trial and error before I finally got things working smoothly.  The example VB app demonstrates all of the major ID3Lib functions.  Before asking me any questions about the "how to", I definitely recommend you go through the example project.

 

VB Class

Once you lay your foundation work, the obvious thing to do is to wrap up the functionality into a class file... which I also have already done.  =)  My current class supports the following frames:

The class provides simple access to adding, editing, or deleting tag frames.  Additionally, it exposes the frames collection directly, allowing you to code to frames I haven't yet made as properties off the main class.

The ID3Lib project does currently have one "gotcha"... that helps to explain one of the behaviors of my class.  Currently, when reading a frame for which there is an ID3v1 equivalent field in the file, the v1 value is returned and not the v2.  Currently, I am looking into the source to see how to fix this, but until then, my class is coded to strip v1 tags on an update.  This effectively cures the problem... albeit, not perfectly.  The real answer is to fix the Read function in the source code.

Other than that, the class also contains some VB code that attempts to parse the MP3's duration, frequency, and bitrate.  I will tell you now that the code I have in there currently does not work in all cases... but it gets most of 'em.  I am looking into enhancing the class by re-coding it to use functions from the xAudio library.

My VB class (along with related functions) and the app that produced the screen shots above, is available for download.  I have included a current compiled copy of VBID3Lib.dll in that ZIP so if you download it, you will have everything you need to be up and running.