Library code snippets
Read MP3 Tag Information (ID3v1 and ID3v2)
I started this because I was writing a media player in C# and I couldn't find any good libraries that could handle ID3v2. So I wrote one. It can handle:
ID3v1 (and 1.1)
This reads all v1 and v1.1 tags. The only difference between these two is the last byte of the v1 comment field has been hijacked for track number in v1.1. It also has write ID3v1 code but hasn't been really tested. Use at your own risk.
ID3v2 (2.2, 2.3, 2.4)
Reads all header info and creates a hash indexed by key and an array of frames. Parses some of the more common frames for Title, Artist, etc... All frames are available w/ contents held in a byte array for user parsing.
MPEG Header Info
Finds and reads mpeg header information.
References, Acknowledgements and Thanks
- the specs from www.id3.org
- Many Thanx to James Heinrich of SiliSoftware. I used his code as an example in a few places and he was very helpful to me on the mailing list
- Ralf for his coding ponters.
- I got my MPEG Header info from this great site.
Source Code
The way I ended up writing this was in a couple classes. Yes it could be more elegant but I haven't been doing any real programming in a long time. Maybe I will massage it into some nicer form in the future. Anyway, here are the classes:
-
mp3info.csis the wrapper class. It contains the objects for the MPEG headers, id3v1 and v2 and some song length calculation code -
MPEG.cscontains the code to find and read the MPEG headers. -
ID3v1.cscontains the code to read and (possibly) write the id3v1 tags. -
ID3v2.cscontains the code to read id3v2.2 - 2.4 tags. Some of the tags it parses into Title, Artist, Etc... the rest are stored in an array (in order read for later writing), and a hash (keyed by frame name) -
BitReader.csWith all the flags and what not I had to deal w/ I ended up writing a class to convert a byte into an array of chars or bools representing the bits. this should be useful outside of this project. -
id3v2Frame.csA subclass of id3v2 frames. contains the size, flags, name, and contents of a frame.
To Do (incomplete List):
- MPEG: length in seconds may be off b/c I don't take into account the presence of id3v1
- MPEG: no VBR Calc
- ALL: no error handling
- ALL: this could be done w/ inheritance if I wanted to get fancy
- v1: haven't tested update
- v2: must write update code
- v2: must write parsing code
- write better webpage
- write some usage docs
- set up versioning
Related articles
Related discussion
-
Help me how to dynamic create row column of TableLayoutpanel at run time ??????
by anatha1 (0 replies)
-
Very Urgent regarding deleting the images from a folder
by rameshbandi (2 replies)
-
How to Export Datagridview contents to Excel
by BarbaMariolino (8 replies)
-
4Ways convert video to FLV./MP3/MP4/3GP
by dvdmm (18 replies)
-
Help accessing sound card
by daz4904 (0 replies)
Related podcasts
-
Object-Oriented Programming in Ruby
In this episode, I talk with Scott Bellware about object-oriented programming in Ruby, and Ruby's object model. This is taken from a private conversation, and the audio quality suffers at times. Much thanks to Scott for allowing this to be released.This episode of the Alt.NET Podcast is bro...
Is there any fix? The code does not work
Error is
"Index was outside the bounds of the array."
on line
bytes[3] = tagSize[3] | ((tagSize[2] & 1) << 7) ;
Im interested in your VB version
Sheratore9@hotmail.com
I'M having Problems reading APIC , and i think i have problems with SynchSafe Integers
Can AnyOne tell me if this is true
00000011 00000001 =
759 Normal?
385 SynchSafe?
and if i multiply the 2nd Byte * 128 an 3rd byte * 16384 and so on i get the values too?
Sorry for my English
Apparently synchsafe integers are not used in 2.2 or 2.3 - they were added in 2.4.
ulong GetFrameSize(char[] tagSize, int version)
{
int shift = 8;
int mask = 0xff;
if (version == 4)
{
// The ID3v2 tag size is encoded with four bytes
// where the most significant bit (bit 7)
// is set to zero in every byte,
// making a total of 28 bits.
// The zeroed bits are ignored
shift = 7;
mask = 0x7f;
}
ulong newSize = 0;
for (int i=0; i < tagSize.Length; i++)
{
newSize <<= shift;
newSize |= (ulong)tagSize[i] & mask;
}
return newSize;
}
public id3v2Frame ReadFrame (BinaryReader br, int version)
{
.... bunch of stuff
if (version == 2)
{
// only have 3 bytes for size ;
tagSize = br.ReadChars(3); // I use this to read the bytes in from the file
newSize = GetFrameSize(tagSize,version);
}
else if (version == 3 || version == 4)
{
// version 2.4
tagSize = br.ReadChars(4); // I use this to read the bytes in from the file
bytes = new int[4]; // for bit shifting
newSize = GetFrameSize(tagSize,version);
}
f1.frameSize = newSize;
....
}
Also, there are frames which can be added more than once (PRIV for example) which Hashtable.Add causes an exception. Ideally you should be able to add them with some other hash but I just catch and ignore the exception.
Hope this helps other people.
John
Pretty nice. You could simplify the synchsafe reading though.
ulong MakeSynchsafeInt(char[] tagSize)
{
const int shift = 7;
const int mask = 0x7f;
ulong newSize = 0;
for (int i=0; i < tagSize.Length; i++)
{
newSize <<= shift;
newSize |= (ulong)tagSize[i] & mask;
}
return newSize;
}
Funny thing though, seems that a lot of programs don't write the frame sizes in this format. I was trying to extend to read the APIC tag and realized I only got half a picture. Anyone else seen this?
if(this.framesHash.Contains("APIC"))
{
StringBuilder mimetype = new StringBuilder();
StringBuilder description = new StringBuilder();
byte [] bytes = ((id3v2Frame)this.framesHash["APIC"]).frameContents;
byte textEncoding;
byte pictureType;
int i = 0;
if (i < bytes.Length)
textEncoding = bytes[i++];
while (i < bytes.Length)
{
mimetype.Append(System.Convert.ToChar(bytes[i++]));
if (bytes[i-1] == 0)
break;
}
if (i < bytes.Length)
pictureType = bytes[i++];
while (i < bytes.Length)
{
description.Append(System.Convert.ToChar(bytes[i++]));
if (bytes[i-1] == 0)
break;
if (bytes[i-1] == 0xff)
{
i--;
break;
}
}
if (i < bytes.Length)
{
MemoryStream stream = new MemoryStream(bytes,i,bytes.Length - i);
albumCover = Image.FromStream(stream);
}
}
If you can provide the code I'd like to see it.
Thanks in advance
Dave Dolson
dadolson@hotmail.com
After downloading the C# classes, I converted them to VB, using online conversion tools. After sorting out a few conversion errors, I managed to get the classes working in VB.
After some extensive testing, I have started to spot and resolve some basic logic errors present in the original code. (Mainly trying to use objects which arent instantiated). The original classes were not particularly well thought out - for each operation (many of them), the code would create a new filestream from the underlying file. The file was being read 5-6 times. I've fixed that! It now passes round a reference to the FileStream instead of recreating it each time...that should save the live of a few HDDs ;)
I am using the classes to scan through 10,000+ files, and after some effort, it manages to process all of them. These classes use a lot of memory, so I am going to try and resolve that, so that all 10,000+ files can be processed in one batch. (I only have 1/2GB RAM, and it takes it all at present!). The classes were poorly written. I mean everything was "public", there were no properties!
If anyone wants what Ive done so far, give me a holla, and I will sort you out. :D
Hi,
This is the exact code I was looking for, but I'm newbie and have no C# knowledge, I'm a C guy working on embedded systems. If you could help me in this regard, I shall be obliged. Is there any documentation for your code or do you have any C version of this code?
I'm interested in reading the basic stuff like artist, album, and genre (nothing else), from ID3v1 and ID3v2 tags, please guide me I'm running out of time.
Hi
This is exactly what I've been looking for! Thanks!
I'm a little stuck though. When calling the read method of teh ID3V2 class, I get an IndexOutOfRange error. It's from the ReadFrame method of the ID3v2Frame class (line 105) - Additional information: Index was outside the bounds of the array.
This is on the line that calls :
bytes[3] = tagSize[2] | ((tagSize[1] & 1) << 7) ;
Any ideas?
Thanks
This thread is for discussions of Read MP3 Tag Information (ID3v1 and ID3v2).