id3_logo

ID3 tag is an mp3 audio media file tagging system. The ID3 tags are stored in a pre-defined location in the mp3 media file, either in the start or at the end (or both). There are two major versions of the ID3 tags. ID3v1 and ID3v2. The tag store data about the song, like track name, album name, artist name, genre, track no, year, composer, licensing information, lyrics, even multiple images inside the tag. To get more information about ID3 tag please check the official website of ID3 tag: http://www.id3.org/, also check this article What are ID3 Tags all about?

Brief

In this post i will present a ID3v1 tag parsing library. The ID3v1 tagging system is a simple fixed length tagging system, and easy to code. This library is able to read the ID3v1 tag from the file and write a tag into a file. The structure of an ID3v1 tag is easy. The last 128 bytes of an mp3 file stores the metadata about the mp3 file. The first 3 bytes of these 128 bytes contain the string “TAG”. This indicates that the last 128 bytes contains an ID3v1 tag. And then follows the informatin as follows: 30 bytes Track Name, 30 bytes Artist name, 30 bytes Album name, 4 bytes year, 28 bytes Comment, 1 byte track number, 1 byte genre. When reading anything we first check that the first 3 bytes of the last 128 bytes, if they represent the string “TAG”, then we continue reading the information as fixed length fields specification, else the tag does not exist.

A more detailed information is provided in this article : What are ID3 Tags all about?. A table indicating the fixed fields of the 128 byte tag is shown below:

ID3v1.x Tag structure
byte 0 to 2 byte 3 to 32 byte 33 to 62 byte 63 to 92 byte 93 to 96 byte 97 to 126 byte 127
Always the string “TAG” (3 bytes) Track Name (30 bytes) Artist (30 bytes) Album (30 bytes) Year (4 bytes) Comment (28+2bytes)

ID3v1.1 Update:
byte 97 to 124: Comment (28bytes)
byte 125 = 0
byte 126 = Track No.

Genre (1 byte)

Structure

First a structure is defined which will be used to hold the tag information. The structure used in this code is shown below:

#define TAG_SIZE 128
#define TRACK_SIZE 30
#define ARTIST_SIZE 30
#define ALBUM_SIZE 30
#define YEAR_SIZE 4
#define COMMENT_SIZE 28

typedef struct _id3v1_tag
{
  char track_name[TRACK_SIZE];
  char artist[ARTIST_SIZE];
  char album[ALBUM_SIZE];
  char year[YEAR_SIZE];
  char comment[COMMENT_SIZE];
  unsigned char zero;
  unsigned char trackno;
  unsigned char genre;
} __attribute__ ((packed)) id3v1_tag;

The structure is renamed as id3v1_tag . It is to be noted that because the next byte of the comment field is always kept zero in ID3v1.1 tag, unsigned char zero is declared, which will always be kept zero during the code. The sizes of the fields are not NUL terminated. The __attribute__ ((packed)) is a gcc extension, this ensures that the order of the structure members are not altered by the compiler and the alignment of each element be one byte, that is, each member element follows just the previous one without any padding between. This is done because we will be using the whole structuere variable to write the tag at once so th order of the member elements and also the padding between two member elements are critical.

Functions

I am describing briefly the working principle of the library functions below. The function definitions are not shown below. Download the Source Code file to get the full code.

id3v1_tag *id3v1_tag_allocate (void)

This function is used to allocate an id3v1_tag type pointer variable instance. This will allocate an id3v1_tag type variable and return a pointer to it. This also clears the allocated structure and blanks it.

void id3v1_tag_free (id3v1_tag * tag);

This will simply free an allocated id3v1_tag type pointer.

id3v1_tag *id3v1_tag_read (char *file_name);

This function opens the file sent as the argument, reads the last 128 bytes of the file, checks if the string “TAG” exists in the first 3 bytes of the read 128 bytes. If yes it parses the tag and accordingly populates the id3v1_tag structure. It returns a pointer of the populated tag.

int id3v1_tag_write (char *file_name, id3v1_tag * tag);

This function writes the data containing in the id3v1_tag structure into the last 128 bytes of the file passed as the first argument. if the tag does not exist, it simply appends the new tag. If the tag exists it will then only overwrite the existing tag with the new data.

int id3v1_tag_rip (char *file_name);

This function clears out the last 128 bytes of the file, whose name is passed as the argument.

The id3v1_tag_set_* functions are described. These functions sets the data passed to it to appropriate fields of the id3v1_tag structure. Note that the structure member array fields are not NUL terminated, so the NUL in the passed string is not stored.

int id3v1_tag_set_track (char *track_name, id3v1_tag * tag);
int id3v1_tag_set_artist (char *artist, id3v1_tag * tag);
int id3v1_tag_set_album (char *album, id3v1_tag * tag);
int id3v1_tag_set_year (char *year, id3v1_tag * tag);
int id3v1_tag_set_trackno (unsigned char track_no, id3v1_tag * tag);
int id3v1_tag_set_comment (char *comment, id3v1_tag * tag);
int id3v1_tag_set_genre (char genre, id3v1_tag * tag);

The above functions sets the track_name, artist, album, year, track_no, comment and genre respectively in the provided allocated the id3v1_tag structure. The genre number is as per the ID3v1 documentation.

Similarly the id3v1_tag_get_* functions are described. These simply fetches the data from the passed id3v1_tag structure from the appropriate fields of it and returns it. Note that the structure member array fields are not NUL terminated. The functions which returns a pointer to the string terminates with NUL before returning.

char *id3v1_tag_get_track (id3v1_tag * tag);
char *id3v1_tag_get_artist (id3v1_tag * tag);
char *id3v1_tag_get_album (id3v1_tag * tag);
char *id3v1_tag_get_year (id3v1_tag * tag);
char id3v1_tag_get_trackno (id3v1_tag * tag);
char *id3v1_tag_get_comment (id3v1_tag * tag);
unsigned char id3v1_tag_get_genre (id3v1_tag * tag);

The above functions gets the track name, artist name, album name, year string, track number, comment,and genre number. The genre number is as per the ID3v1 documentation.

Each of the genre number represents a genre, which is described in the ID3v1 documentation. The below two functions convert from genre number to string and a string to genre number.

char *get_genre_string (unsigned char genre_no);

Recieves a number, looks up for the corresponding genre and returns the genre string.

unsigned char get_genre_number (char *genre_string);

Recieves a string, looks up in the table for if such genre string exists, if yes returns the genre number of the corresponding string, else returns the genre number of “None”.

char *alloc_str (int size);

This function will simply allocate a string and clear it with 0 and return it.

Sourcecode

To download the code click here : Source Code of ID3v1 Tag read/write library

This archive contains id3v1_lib.c which contains the definitions of the library functions. id3v1.h which contains the prototypes, and the structures etc. There is another file in the archive id3v1_utility.c which uses these library functions, and reads, writes ID3v1 tags. id3v1_utility.c file is included to demonstrate library functions.

To compile the library

gcc -c id3v1_tag.c -o libid3tag.so

To compile other files using these library functions include id3v1.h , and execute:

gcc source_file.c -L./ -lid3tag

The path after the -L tag should point to the location of libid3tag.so , or keep it in a standard search location.

Note: This library is intended to be used as educational purpose. I would suggest to use taglib library to edit id3 tags in some application.

Advertisement

2 thoughts on “An ID3v1 Tag Parsing Library

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s