Sylloge
A C# helper library
code/Audio/Metadata/ID3/V1.cs
Go to the documentation of this file.
00001 using System;
00002 using System.Collections.Generic;
00003 using System.Linq;
00004 using System.Text;
00005 
00006 namespace Sylloge.Audio.Metadata.ID3
00007 {
00011     public class V1
00012     {
00013         #region dev notes
00014 
00015         /* Tag info
00016         ID3v1 Tag information
00017         Last 128 bytes of MP3 file
00018         first 3 bytes MUST be 'TAG' for it to be valid
00019         Length(bytes)  Position(bytes)   Description
00020         3              (0-2)             Tag identification. Must contain 'TAG' if tag exists and is correct.
00021         30             (3-32)            Title
00022         30             (33-62)           Artist
00023         30             (63-92)           Album
00024         4              (93-96)           Year
00025         30             (97-126)          Comment
00026         1              (127)             Genre
00027           
00028         DEV_NOTE: The 'TAG' portion of the ID3v1 tag is read in ANSI.
00029         */
00030 
00031         #endregion
00032 
00033         #region Extended V1 (TAG+) Header Class
00034         
00035         public class TagPlus
00036         {
00037             public enum SpeedDesignator
00038             {
00039                 Unset = 0,
00040                 Slow,
00041                 Medium,
00042                 Fast,
00043                 Hardcore
00044             }
00045 
00049             public string ExtendedTitle { get; set; }
00053             public string ExtendedArtist { get; set; }
00057             public string ExtendedAlbum { get; set; }
00061             public SpeedDesignator Speed { get; set; }
00065             public string Genre { get; set; }
00069             public string StartTime { get; set; }
00073             public string EndTime { get; set; }
00078             public byte[] Data { get; protected set; }
00082             public bool IsValid { get; protected set; }
00083 
00084             public TagPlus()
00085             {
00086                 this.Clear();
00087             }
00088 
00089             public void Clear()
00090             {
00091                 this.ExtendedTitle = string.Empty;
00092                 this.ExtendedArtist = string.Empty;
00093                 this.ExtendedAlbum = string.Empty;
00094                 this.Speed = SpeedDesignator.Unset;
00095                 this.Genre = string.Empty;
00096                 this.StartTime = string.Empty;
00097                 this.EndTime = string.Empty;
00098             }
00099 
00100             public static TagPlus Parse(byte[] b)
00101             {
00102                 if (b == null) { throw new System.ArgumentNullException("byte value cannot be null"); }
00103                 if (b.Length != 227) { throw new System.ArgumentOutOfRangeException("ID3v1 extende tag length must be 227 bytes"); }
00104                 System.Text.Encoding enc = ID3.Helper.ISO;
00105                 if (enc.GetString(b, 0, 4) != "TAG+") { throw new System.ArgumentException("ID3v1 extended tag must begin with 'TAG+'"); };
00106                 try {
00107                     char[] tc = new char[] { '\0', ' ' };
00108                     TagPlus ret = new TagPlus();
00109                     /*
00110                     Note: The extended tag is 227 bytes long, and placed before the ID3v1 tag.
00111                     Field       Length  Description
00112                     header      4       "TAG+"
00113                     title       60      60 characters of the title
00114                     artist      60      60 characters of the artist name
00115                     album       60      60 characters of the album name
00116                     speed       1       0=unset, 1=slow, 2= medium, 3=fast, 4=hardcore
00117                     genre       30      A free-text field for the genre
00118                     start-time  6       the start of the music as mmm:ss
00119                     end-time    6       the end of the music as mmm:ss
00120                      */
00121                     ret.ExtendedTitle = enc.GetString(b, 4, 60).TrimEnd(tc); // 4+60
00122                     ret.ExtendedArtist = enc.GetString(b, 64, 60).TrimEnd(tc); // 64+60
00123                     ret.ExtendedAlbum = enc.GetString(b, 124, 60).TrimEnd(tc); // 124+60
00124                     ret.Speed = (SpeedDesignator)b[184]; // 184+1
00125                     ret.Genre = enc.GetString(b, 185, 30).TrimEnd(tc); // 185+30
00126                     ret.StartTime = enc.GetString(b, 215, 6).TrimEnd(tc); // 215+6
00127                     ret.EndTime = enc.GetString(b, 221, 6).TrimEnd(tc); // 221+6
00128                     ret.Data = b;
00129                     ret.IsValid = true;
00130                     return ret;
00131                 } catch (Exception ex) {
00132                     throw ex;
00133                 }
00134             }
00135         }
00136 
00137         #endregion
00138 
00139         #region ID3 Tag Values
00140 
00144         public string Artist { get; set; }
00148         public string Album { get; set; }
00152         public string Title { get; set; }
00156         public string Comment { get; set; }
00160         public string File { get; protected set; }
00164         public ID3.Genre Genre { get; set; }
00168         public bool IsValid { get; protected set; }
00172         public int Track { get; set; }
00176         public string Year { get; protected set; }
00180         public System.Version Version { get; protected set; }
00185         public byte[] Data { get; protected set; }
00189         public TagPlus Extended { get; protected set; }
00190 
00191         #endregion
00192 
00193         #region Methods
00194 
00198         public V1()
00199         {
00200             this.Clear();
00201             this.Version = new Version(1, 0, 0);
00202         }
00203 
00208         public void Clear()
00209         {
00210             this.IsValid = false;
00211             this.Title = string.Empty;
00212             this.Artist = string.Empty;
00213             this.Album = string.Empty;
00214             this.Year = "1970";
00215             this.Comment = string.Empty;
00216             this.Genre = ID3.Genre.Other;
00217             this.Track = 0;
00218             this.Extended = new TagPlus();
00219         }
00220 
00225         public bool Save()
00226         {
00227             if (!System.IO.File.Exists(this.File)) { return false; }
00228             try {
00229                 /*
00230                  * Last 128 bytes of MP3 file
00231                 first 3 bytes MUST be 'TAG' for it to be valid
00232                 Length(bytes)  Position(bytes)   Description
00233                 3              (0-2)             Tag identification. Must contain 'TAG' if tag exists and is correct.
00234                 30             (3-32)            Title
00235                 30             (33-62)           Artist
00236                 30             (63-92)           Album
00237                 4              (93-96)           Year
00238                 30*            (97-126)*         Comment
00239                 1*             (125)*            0-Byte pad for track (if present)
00240                 1*             (126)*            Track
00241                 1              (127)             Genre 
00242                 
00243                 Note that per ID3v1.1, if byte 125 is a binary 0, then byte 126 is the
00244                 track number. 
00245                  */
00246                 System.Text.Encoding enc = ID3.Helper.ISO;
00247                 byte[] tdata = new byte[128];
00248                 Sylloge.Memory.Copy(enc.GetBytes("TAG"), 0, tdata, 0, 3);
00249                 Sylloge.Memory.Copy(enc.GetBytes(this.Title), 0, tdata, 3, 30);
00250                 Sylloge.Memory.Copy(enc.GetBytes(this.Artist), 0, tdata, 33, 30);
00251                 Sylloge.Memory.Copy(enc.GetBytes(this.Album), 0, tdata, 63, 30);
00252                 Sylloge.Memory.Copy(enc.GetBytes(this.Year), 0, tdata, 93, 4);
00253                 if (this.Track > 0) {
00254                     Sylloge.Memory.Copy(enc.GetBytes(this.Comment), 0, tdata, 97, 28);
00255                     tdata[125] = 0x00;
00256                     tdata[126] = (byte)this.Track;
00257                 } else {
00258                     Sylloge.Memory.Copy(enc.GetBytes(this.Comment), 0, tdata, 97, 30);
00259                 }
00260                 tdata[127] = (byte)this.Genre;
00261                 try {
00262                     byte[] dval = new byte[128];
00263                     System.IO.FileStream fin = new System.IO.FileStream(this.File, System.IO.FileMode.Open, System.IO.FileAccess.ReadWrite, System.IO.FileShare.ReadWrite);
00264                     fin.Seek(-128, System.IO.SeekOrigin.End);
00265                     fin.Read(dval, 0, 128);
00266                     if (enc.GetString(dval, 0, 3) == "TAG") {
00267                         // There is a valid tag so we need to overwrite it
00268                         fin.Seek(-128, System.IO.SeekOrigin.End);
00269                         fin.SetLength(fin.Length - 128);
00270                     }
00271                     fin.Seek(0, System.IO.SeekOrigin.End); // Go to the end of the file
00272                     fin.Write(tdata, 0, 128);
00273                     fin.Close();
00274                     this.Data = tdata;
00275                     return true;
00276                 } catch (Exception ex) {
00277                     throw ex;
00278                 }
00279             } catch (Exception ex) {
00280                 throw ex;
00281             }
00282         }
00283 
00287         public void SetFile(string file) { this.File = file; }
00288 
00292         public void SetYear(DateTime d)
00293         {
00294             this.Year = d.ToString("yyyy");
00295         }
00296 
00300         public void SetYear(string d)
00301         {
00302             if (!string.IsNullOrEmpty(d)) {
00303                 if (!Sylloge.Extensions.TypeExtensions.IsNumeric(d)) { throw new System.ArgumentException("Year must be a numeric value"); }
00304                 if (d.Length > 4) { throw new System.ArgumentOutOfRangeException("Year cannot be more than 4 digits"); }
00305             }
00306             this.Year = d;
00307         }
00308 
00312         public void SetVersion(System.Version ver) { this.Version = ver; }
00313 
00317         public void SetVersion(string ver)
00318         {
00319             this.Version = System.Version.Parse(ver);
00320         }
00321 
00326         public override string ToString()
00327         {
00328             return this.Artist + " - " + this.Title;
00329         }
00330 
00331         #endregion
00332 
00333         #region Static Methods
00334 
00340         public static V1 Parse(byte[] b)
00341         {
00342             if (b == null) { throw new System.ArgumentNullException("byte value cannot be null"); }
00343             if (b.Length != 128) { throw new System.ArgumentOutOfRangeException("ID3v1 byte tag length must be 128 bytes"); }
00344             System.Text.Encoding enc = ID3.Helper.ISO;
00345             if (enc.GetString(b, 0, 3) != "TAG") { throw new System.ArgumentException("ID3v1 tag must begin with 'TAG'"); };
00346             try {
00347                 char[] tc = new char[] { '\0', ' ' };
00348                 V1 ret = new V1();
00349                 /*
00350                  * Last 128 bytes of MP3 file
00351                 first 3 bytes MUST be 'TAG' for it to be valid
00352                 Length(bytes)  Position(bytes)   Description
00353                 3              (0-2)             Tag identification. Must contain 'TAG' if tag exists and is correct.
00354                 30             (3-32)            Title
00355                 30             (33-62)           Artist
00356                 30             (63-92)           Album
00357                 4              (93-96)           Year
00358                 30*            (97-126)*         Comment
00359                 1*             (125)*            0-Byte pad for track (if present)
00360                 1*             (126)*            Track
00361                 1              (127)             Genre 
00362                 
00363                 Note that per ID3v1.1, if byte 125 is a binary 0, then byte 126 is the
00364                 track number. 
00365                  */
00366                 ret.Title = enc.GetString(b, 3, 30).TrimEnd(tc); // 3-32
00367                 ret.Artist = enc.GetString(b, 33, 30).TrimEnd(tc); // 33-62
00368                 ret.Album = enc.GetString(b, 63, 30).TrimEnd(tc); // 63-92
00369                 ret.Year = enc.GetString(b, 93, 4).TrimEnd(tc); ; // 93-96
00370                 if (b[125] == 0x00) { // 97-124 (track present)
00371                     // doesn't have a full comment, so set track accordingly (if set)
00372                     ret.Comment = enc.GetString(b, 97, 28).TrimEnd(tc); // 28
00373                     ret.Track = (int)b[126];
00374                 } else { // 97-126 (no track present)
00375                     ret.Comment = enc.GetString(b, 97, 30).TrimEnd(tc);
00376                     ret.Track = 0;
00377                 }
00378                 ret.Genre = (ID3.Genre)b[127];
00379                 ret.Data = b;
00380                 ret.IsValid = true;
00381                 return ret;
00382             } catch (Exception ex) {
00383                 throw ex;
00384             }
00385         }
00386 
00392         public static V1 Parse(string file)
00393         {
00394             // no try catch here since we want the exception to propigate up
00395             if (System.IO.File.Exists(file)) {
00396                 byte[] tag = new byte[128];
00397                 byte[] tagp = new byte[227];
00398                 System.IO.FileStream fin = new System.IO.FileStream(file, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite);
00399                 fin.Seek(-355, System.IO.SeekOrigin.End);
00400                 fin.Read(tagp, 0, 227); // read the "TAG+" first (doesn't matter if it exists since this advances the read stream)
00401                 fin.Read(tag, 0, 128); // read the "TAG" next
00402                 fin.Close(); fin = null;
00403                 if (ID3.Helper.ISO.GetString(tag, 0, 3) == "TAG") {
00404                     // Its a valid tag, so continue
00405                     V1 ret = V1.Parse(tag);
00406                     ret.File = file;
00407                     if (ID3.Helper.ISO.GetString(tagp, 0, 4) == "TAG+") {
00408                         ret.Extended = TagPlus.Parse(tagp);
00409                     }
00410                     return ret;
00411                 }
00412             }
00413             return null;
00414         }
00415 
00422         public static bool TryParse(byte[] b, out V1 v1)
00423         {
00424             try {
00425                 v1 = V1.Parse(b);
00426                 return true;
00427             } catch (Exception) {
00428                 v1 = null;
00429             }
00430             return false;
00431         }
00432 
00439         public static bool TryParse(string file, out V1 v1)
00440         {
00441             try {
00442                 v1 = V1.Parse(file);
00443                 return true;
00444             } catch (Exception) {
00445                 v1 = null;
00446             }
00447             return false;
00448         }
00449 
00461         public static bool Save(string fileName, string title, string artist, string album, string year, string comment, int track, ID3.Genre genre)
00462         {
00463             V1 tag = new V1();
00464             tag.File = fileName;
00465             tag.IsValid = true;
00466             tag.Title = title;
00467             tag.Artist = artist;
00468             tag.Album = album;
00469             tag.Year = year;
00470             tag.Comment = comment;
00471             tag.Track = track;
00472             tag.Genre = genre;
00473             return tag.Save();
00474         }
00475 
00476         #endregion
00477     }
00478 }
 All Classes Namespaces Files Functions Variables Enumerations Properties Events