Reading ID3 V1 Tags from MP3 Files

by David Kiff 3. March 2009 19:06

What is ID3 V1?

ID3 is a specification that describes how users can append music related metadata to a file (commonly MP3 Files).  The metadata includes:

  • Title
  • Artist
  • Album
  • Track number
  • Genre
  • Comment
  • Year

The ID3 tag occupies the last 128 bytes of the music file and starts with the string "TAG".  Media players typically ignore the tag when the file is played, however some older players play a small burst of static.

Michael Mutschler made an improvement to the standard in 1997 to trim the comment by two bytes.  This was to accommodate the track number; this is detailed in the ID3v1.1 specification.

The .NET Framework does not include any classes for reading or writing ID3 V1/.1 metadata, so this article provides an ID3 V1/.1 reader class.

To use the code copy and paste it into a new code file, create an instance of it passing in a FileInfo object for the file.  If the file does not contain a valid ID3 V1 tag or the tag cannot be read, then an InvalidID3V1Tag exception is thrown.

FileInfo fileInfomation = new FileInfo(@"D:\Path\Filename.mp3");
ID3V1Tag tag = new ID3V1Tag(fileInformation);
string id3Title = tag.Title;

internal class ID3V1Tag
{
    #region Genres
    private string[] genres = {"Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "Alt. Rock", "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta Rap", "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret", "New Wave", "Psychedelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk", "Folk/Rock", "National Folk", "Swing", "Fast-Fusion", "Bebop", "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet", "Punk Rock", "Drum Solo", "A Cappella", "Euro-House", "Dance Hall", "Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror", "Indie", "BritPop", "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta Rap", "Heavy Metal", "Black Metal", "Crossover", "Contemporary Christian", "Christian Rock", "Merengue", "Salsa", "Thrash Metal"};
    #endregion
    #region Constructors
    public ID3V1Tag(FileInfo fileInfo)
    {
        BinaryReader binaryReader = OpenAndPrepareBinaryReader(fileInfo);
        try
        {
            CheckHeader(binaryReader);
            ReadID3V1TagAndPopulateProperties(binaryReader);
        }
        finally
        {
            binaryReader.Close();
        }
    }
    #endregion
    #region Public Properties
    public string Title { get; private set; }
    public string Artist { get; private set; }
    public string Album { get; private set; }
    public string Genre { get; private set; }
    public string Comment { get; private set; }
    public int Year { get; private set; }
    public int Track { get; private set; }
    #endregion
    #region Private Methods
    private BinaryReader OpenAndPrepareBinaryReader(FileInfo fileInfo)
    {
        FileStream fileStream = fileInfo.Open(FileMode.Open);
        BinaryReader binaryReader = new BinaryReader(fileStream);
        if (fileStream.Length > 128)
        {
            fileStream.Seek(-128, SeekOrigin.End);
        }
        else
       {
            throw new InvalidID3V1Tag("The file is too small to contain a valid ID3 V1 Tag");
        }
        return binaryReader;
    }
    private static void CheckHeader(BinaryReader _binaryReader)
    {
        char[] headerName = _binaryReader.ReadChars(3);
        if (!new string(headerName).Equals("TAG"))
        {
            throw new InvalidID3V1Tag("The TAG Header is missing from the music file.");
        }
    }
    private void ReadID3V1TagAndPopulateProperties(BinaryReader binaryReader)
    {
        try
        {
            Title = ConvertToTrimedString(binaryReader.ReadChars(30));
            Artist = ConvertToTrimedString(binaryReader.ReadChars(30));
            Album = ConvertToTrimedString(binaryReader.ReadChars(30));
            Year = ConvertToInt32(binaryReader.ReadChars(4));
            char[] v1Comment = binaryReader.ReadChars(30);
            if (v1Comment[28] == '\0')//Contains a track number V1.1
            {
                Track = (int)v1Comment[29];
                Array.Resize<char>(ref v1Comment, 28);
                Comment = ConvertToTrimedString(v1Comment);
            }
            else
            {
                Comment = ConvertToTrimedString(v1Comment);
            }
            Genre = ReadGenre(binaryReader.ReadChars(1));
        }
        catch (Exception ex)
        {
            throw new InvalidID3V1Tag("There was a problem reading the ID3 V1 Tag.", ex);
        }
    }
    private string ReadGenre(char[] characters)
    {
        string genreToReturn = "Unknown";
        int genreIndex = ConvertToInt32(characters);
        if (genres.Length > genreIndex)
        {
            genreToReturn = genres[genreIndex];
        }
        return genreToReturn;
    }
    private string ConvertToTrimedString(char[] characters)
    {
        return new string(characters).Trim("\0".ToCharArray()).Trim();
    }
    private int ConvertToInt32(char[] characters)
    {
        int integerToReturn;
        string stringRepresentation = ConvertToTrimedString(characters);
        int.TryParse(stringRepresentation, out integerToReturn);
        return integerToReturn;
    }
    #endregion
}
internal class InvalidID3V1Tag : Exception
{
    public InvalidID3V1Tag(string message) : base(message) { }
    public InvalidID3V1Tag(string message, Exception innerException) : base(message, innerException) { }
}

Download Code

Tags:

ID3 | MP3

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading