In 1996 Eric Kemp had the idea to add a small chunk of data to audio files; such method is now known as ID3v1. ID3 tags quickly became standard for storing metadata in MP3s. Nowadays the standard has evolved to add greater flexibility on how tags are stored (ID3v2, v2.3, and v2.4); however, for illustration purposes I will create a set of classes to read the ID3v1 tag off mp3 files.
The ID3v1 tag consists of 128 bytes of data added at the end of the file. The ID3v1 tag begins with the string TAG then it allows 30 bytes each for the title, artist, album, and comment; also, 4 bytes for the year and a byte to identify the genre and the track number.
ID3v1 tag format:
Field | Length | Description |
header | 3 | "TAG" |
title | 30 | 30 characters of the title |
artist | 30 | 30 characters of the artist name |
album | 30 | 30 characters of the album name |
year | 4 | A four-digit year |
comment | 30 | The comment. |
zero-byte | 1 | If a track number is stored, this byte contains a binary 0. |
Track | 1 | The number of the track on the album, or 0. Invalid, if previous byte is not a binary 0. |
Genre | 1 | Index in a list of genres, or 255 |
Now that we know the expected format we need to create a class to hold this data:
ID3Tag class:
public class ID3Tag
{
public string HeaderData;
public string TrackName;
public string ArtistName;
public string AlbumName;
public string Year;
public string Comments;
public int ZeroByte;
public int TrackNumber;
public int Genre;
}
I will add the parsing function in a separate class so if we ever want to add support for ID3v2 the project will be organized.
TagReader class:
public class TagReader
{
/// <summary>
/// Read ID3 tag
/// </summary>
/// <remarks>
/// TagHeader 0-2
/// TrackName 3-32
/// ArtistName 33-62
/// AlbumName 63-92
/// Year 93-96
/// Comment 97-126
/// ZeroByte 125
/// TrackNumber 126
/// Genres 127
/// </remarks>
/// <param name="file">filename</param>
/// <returns>ID3Tag object</returns>
public ID3Tag ReadTag(string file)
{
ID3Tag tag = new ID3Tag();
byte[] f = ReadID3FromFile(file);
string data = Encoding.UTF8.GetString(f);
if (data.Length >= 128)
{
tag.HeaderData = data.Substring(0,3);
tag.TrackName = data.Substring(3, 30);
tag.ArtistName = data.Substring(33, 30);
tag.AlbumName = data.Substring(63, 30);
tag.Year = data.Substring(93, 4);
tag.Comments = data.Substring(97, 30);
int zb = 0;
int.TryParse(data.Substring(125, 1), out zb);
tag.ZeroByte = zb;
int track = 0;
int.TryParse(data.Substring(125, 1), out track);
tag.TrackNumber = track;
int gen = 0;
int.TryParse(data.Substring(127, 1), out gen);
tag.Genre = gen;
}
return tag;
}
/// <summary>
/// Read ID3 tag from a file (last 128 bytes)
/// </summary>
/// <param name="mediaFile">file</param>
/// <returns>tag segment</returns>
private byte[] ReadID3FromFile(string mediaFile)
{
byte[] result = null;
FileStream fs = null;
BinaryReader reader = null;
try
{
fs = new FileStream(mediaFile, FileMode.Open, FileAccess.Read);
reader = new BinaryReader(fs);
reader.BaseStream.Position = fs.Length - 128;
result = reader.ReadBytes((int)fs.Length);
}
finally
{
if (reader != null)
{
reader.Close();
}
if (fs != null)
{
fs.Close();
fs.Dispose();
}
}
return result;
}
}
There are only two methods in this class. ReadTag method accepts the fullpath for the media file to be read while the ReadID3FromFile does strip the ID3 tag as a byte[] to be parsed.
USAGE:
TagReader reader = new TagReader();
ID3Tag tag = reader.ReadTag(@"C:\testing\mytrack.mp3");
Console.WriteLine(tag.TrackName);
Console.WriteLine(tag.ArtistName);
Console.WriteLine(tag.AlbumName);
Console.ReadLine();
This comment has been removed by the author.
ReplyDelete