|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionID3 is additional information that attaches to mp3 files. This information can be: text, files, images, lyrics and much more.
To learn how ID3 works in detail, look at The ID3 Homepage. This is the best place to help you understand the details of ID3. In this article I won't explain the details of ID3. I'll just explain the basics of ID3 and how the This class can Read/Write both versions of ID3: ID3v1 and ID3v2. For ID3v2 minor versions this class can read ID3v2.2, ID3v2.3, ID3v2.4 and can write ID3v2.3 and ID3v2.4. The most popular version of ID3 is 2.3.0 and version 1. In this article my focus is more on ID3v2 because there are too many other good references for ID3v1. To understand how this class works you need to know some basics. Classes, inheritance, and some useful .NET classes (like I wrote an application with ID3Info. This application doesn't use more than 60% of I'll just explain some basics here. ID3v1
ID3v1 usage is so simple. The last 128 bytes of file may contain ID3v1 information. It starts with 'TAG', this is ID3v1 identifier. 'TAG' keyword will follow with Title (30 character), Artist (30 Character), Album (30 Character), Year (4 Character), Comment (28 character), Track Number (1 byte) and Genre (1 byte). Genre has a table within ID3 definition. All softwares must use that table. As you can see, there's nothing complex in ID3v1. ID3v1 class, in ID3.ID3v1Frames namespace handles ID3v1 jobs. private string _FilePath;
private string _Title;
private string _Artist;
private string _Album;
private string _Year;
private string _Comment;
private byte _TrackNumber;
private byte _Genre;
The variables in this class hold ID3v1 data. There are some properties for each variable and they check the limitations of ID3v1. For example, There's another variable that allows the In the constructor, set the Loading ID3v1To load ID3v1 you must use the ' For reading text, I have used the ' public void Load()
{
FileStream FS = new FileStream(_FilePath, FileMode.Open);
if (!HaveID3(FS))
{
FS.Close();
_HaveTag = false;
return;
}
_Title = Frame.ReadText(FS, 30, TextEncodings.Ascii);
FS.Seek(-95, SeekOrigin.End);
//…
FS.Seek(-2, SeekOrigin.End);
_TrackNumber = Convert.ToByte(FS.ReadByte());
_Genre = Convert.ToByte(FS.ReadByte());
FS.Close();
_HaveTag = true;
}
Saving ID3v1To save ID3v1 we first need to understand if the file currently has ID3. To do so use the '
public void Save()
{
FileStream fs = new FileStream(_FilePath, FileMode.Open);
bool HTag = HaveID3(fs);
if (HTag && !_HaveTag) // just delete ID3
fs.SetLength(fs.Length - 128);
else if (!HTag && _HaveTag)
{
fs.Seek(0, SeekOrigin.End);
fs.Write(GetTagBytes, 0, 128);
}
else if (HTag && _HaveTag)
{
fs.Seek(-128, SeekOrigin.End);
fs.Write(GetTagBytes, 0, 128);
}
fs.Close();
}
An Mp3 file with ID3 looks something like this figure:
ID3v2ID3v2 saves at the beginning of the file. It saves as frames. Each frame contains some information, for example, one contains artist, another, title of song. A frame also contains: release date, composer, pictures related to track, track number and much more information. Each frame contains two main parts:
I have classified ID3 frames into 25 classes. All of them inherit the 'Frame' class ID3v2 minor versionsID3v2 has some minor versions which have small differences. The main difference is in version 2.2. In this version, Frame ClassIn 'Main.cs' the file is 'Frame' class. This is the main class used for ID3 frames. As you can see, this is an abstract class (must be inherited in Visual Basic) because for any frame there's another class that inherits this class. Some of the most important things are done in this class. In this class we have some abstract properties and methods.
These two properties and one method must override all inherited classes. This class also contains some static methods that are used to read/write data from/to When we want to save a frame first we need to save frame header. The ID3v2 classThis class reads and writes ID3v2. In the constructor you indicate the file path and must determine whether to load its data or not. To load data you can use the filter option.
You can add 'TIT2' to The public void Load()
{
FileStream ID3File = new FileStream(_FilePath, FileMode.Open);
if (!HaveID3(ID3File)) // If file doesn't contain ID3v2 exit function
{
_HaveTag = false;
ID3File.Close();
return;
}
ReadVersion(ID3File); // Read ID3v2 version
_Flags = (ID3v2Flags)ID3File.ReadByte();
// Extended Header Must Read Here
ReadFrames(ID3File, ReadSize(ID3File));
ID3File.Close();
_HaveTag = true;
}
Read Frames methodNote: private void ReadFrames(FileStream Data, int Length)
{
string FrameID;
int FrameLength;
FrameFlags Flags = new FrameFlags();
byte Buf;
// If ID3v2 is ID3v2.2 FrameID, FrameLength of Frames is 3 byte
// otherwise it's 4 character
int FrameIDLen = VersionInfo.Minor == 2 ? 3 : 4;
// Minimum frame size is 10 because frame header is 10 byte
while (Length > 10)
{
// check for padding( 00 bytes )
Buf = Frame.ReadByte(Data);
if (Buf == 0)
{
Length--;
continue;
}
// if read byte is not zero. it must read as FrameID
Data.Seek(-1, SeekOrigin.Current);
// ---------- Read Frame Header -----------------------
FrameID = Frame.ReadText(Data, FrameIDLen, TextEncodings.Ascii);
if (FrameIDLen == 3)
FrameID = FramesList.Get4CharID(FrameID);
FrameLength = Convert.ToInt32(Frame.Read(Data, FrameIDLen));
if (FrameIDLen == 4)
Flags = (FrameFlags)Frame.Read(Data, 2);
else
Flags = 0; // must set to default flag
long Position = Data.Position;
if (Length > 0x10000000)
throw (new FileLoadException("This file contain frame that
have more than 256MB data"));
bool Added = false;
if (IsAddable(FrameID)) // Check if frame is not filter
Added = AddFrame(Data, FrameID, FrameLength, Flags);
if (!Added)
{
// if don't read this frame
// we must go forward to read next frame
Data.Position = Position + FrameLength;
}
Length -= FrameLength + 10;
}
}
How are frames stored in this class?As you can see ' Text frames are frames that contain text only. For example: title, artist, album, etc.. All text frame
As I mentioned before there are about 25 classes to store frames. You can find these classes in the 'Frame classes' directory. Switch statement uses AttachedPictureFrame TempAttachedPictureFrame = new AttachedPictureFrame(
FrameID, Flags, Data, Length);
if (TempAttachedPictureFrame.IsReadableFrame)
{
_AttachedPictureFrames.Add(TempAttachedPictureFrame);
return true;
}
else
AddError(new ID3Error(TempAttachedPictureFrame.ErrorMessage, FrameID));
break;
As you can see I've created ' Note: you can store more than one piece of information in some frames. For example, in some frames you can save as many pictures as you want. But for other frames this is not possible. In the Relative volume frame, for example, the ID3v2 class just contains a variable for that frame. (To better understand frames that can hold additional information look at references). ErrorsIf you look more closely at Why error collection and no exceptionAdding errors to a collection allows the program to load other frames that have no error. If, when error occurred program throw exception other later frames will not read. Remember you can clear the Error list, and must do it when required. Frame EqualityAs I have said many times, for each type of frame we have a special class. Imagine for an mp3 file, we start reading frames. We find a frame with a 'TIT2' For each frame class there's an public override bool Equals(object obj)
{
if (obj.GetType() != this.GetType())
return false;
// if FrameID of two text frames were equal they are equal
// ( the text value is not important )
if (this._FrameID == ((TextFrame)obj)._FrameID)
return true;
else
return false;
}
In this method I have checked if the The usage of this override is in the ' So always remember frame equality means no two frames have exactly same data.
Using ID3InfoIn this part I explain how to use the How to load ID3InfoTo load data you can use constructor. ID3Info File = new ID3Info("Path", true);
Or ID3Info File = new ID3Info("Path", false);
// You can use filter here
// ID3File.ID3v2Info.Filter.Add("TIT2");
// ID3File.ID3v2Info.FilterType = FilterTypes.LoadFiltersOnly;
File.Load();
Reading Text informationAfter defining the ID3Info File = new ID3Info("Path", true);
String Title = File.ID3v2Info.GetTextFrame("TIT2");
And to set text information you can use 'SetInformation' method, And below.
ID3Info File = new ID3Info("Path", true);
File.ID3v2Info.SetTextFrame("TIT2","My Title", TextEncodings.Ascii,
ID3File.ID3v2Info.VersionInfo.Minor);
Note: For any text you must set a text encoding, and a minor version of ID3v2. This is because these two parameters are important to text information. In the next version both of them will be automatic. You can get a list of all text frames related to file from Unknown frameIf the application can't find what the Load linked framesLinked frames are information that is attached from another file. For example, you can attach a picture to one file, and for another file in the same album use a linked frame to add the same picture from that file. In this way you will save memory and consume less memory, saving data. Linked frames indicate which SavingTo save data you can call the ' ExamplesLoad this string "[Title] – [Album]"ID3Info File = new ID3Info("Path", true);
String MyText = File.ID3v2Info.GetTextFrame("TIT2") + " - " +
File.ID3v2Info.GetTextFrame("TALB");
// Now my text contain Title – Album
Load all attached picturesusing ID3;
using ID3.ID3v2Frames.BinaryFrames
// Code:
ID3Info File = new ID3Info("Path", true);
Image[] ImageArray = new Image[File.ID3v2Info.AttachedPictureFrames.Count];
for(int I = 0; I < File.ID3v2Info.AttachedPictureFrames.Count; i++)
ImageArray[I] = Image.FromStream(
ID3File.ID3v2Info.AttachedPictureFrames.Items[i])
// now ImageArray contain array of images attached to file
Retrieve English unsynchronized lyricsThis method returns English lyrics if they exist. using ID3;
using ID3.TextFrames;
ID3Info File = new ID3Info("Path", true);
foreach(TextWithLanguageFrame TWL in File.ID3v2Info.TextWithLanguageFrames)
if(TWL.FrameID == "USLT" && TWL.Language == "ENG")
return TWL.Text;
return null;
For more examples you can look at 'ID3 Editor' source code. If there are problems contact me. Note: ID3 editor has a help feature you can use to understand how ID3 editor works. And the source code is available to see how to work with each frame. In FutureI know parts of the ID3Class application need some changes. It can be more powerful. I will try to improve both the | ||||||||||||||||||||