blog.ashier.com flash / flex / air development
rss rss comment

I've been trying to develop an ID3 parser and here's what i got so far.

I've update the ID3Parser. It is much faster now.

Actionscript:
  1. package libs.ashier.id3 {
  2.    
  3.     package libs.ashier.id3 {
  4.    
  5.     import flash.events.EventDispatcher;
  6.     import flash.filesystem.File;
  7.     import flash.filesystem.FileMode;
  8.     import flash.filesystem.FileStream;
  9.     import flash.utils.ByteArray;
  10.     import flash.utils.Dictionary;
  11.     import flash.utils.clearInterval;
  12.     import flash.utils.setInterval;
  13.    
  14.     public class ID3Parser extends EventDispatcher{
  15.        
  16.         public function ID3Parser():void {}
  17.        
  18.         /**
  19.          * Bit 7 in the 'ID3v2 flags' indicates whether or not
  20.          * unsynchronisation is applied on all frames.
  21.          */
  22.         private var unsynchronisation:uint;
  23.        
  24.         /**
  25.          * The second bit (bit 6) indicates whether or not the header is
  26.          * followed by an extended header.
  27.          * A set bit indicates the presence of an extended header.
  28.          */
  29.         private var extendedHeader:uint;
  30.        
  31.         /**
  32.          * The third bit (bit 5) is used as an 'experimental indicator'. This
  33.          * flag SHALL always be set when the tag is in an experimental stage.
  34.          */
  35.         private var experimental:uint;
  36.        
  37.         /**
  38.          * FileStream / Bytes
  39.          */       
  40.         private var fs:FileStream;
  41.        
  42.         /**
  43.          * Id3 Version
  44.          */       
  45.         private var version:Number;
  46.        
  47.         /**
  48.          * Available Genres
  49.          */       
  50.         private var genres:Array = [
  51.                                 "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk",
  52.                                 "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other",
  53.                                 "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", "Industrial",
  54.                                 "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack",
  55.                                 "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion",
  56.                                 "Trance", "Classical", "Instrumental", "Acid", "House", "Game",
  57.                                 "Sound Clip", "Gospel", "Noise", "Alt. Rock", "Bass", "Soul",
  58.                                 "Punk", "Space", "Meditative", "Instrum. Pop", "Instrum. Rock",
  59.                                 "Ethnic", "Gothic", "Darkwave", "Techno-Indust.", "Electronic",
  60.                                 "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy",
  61.                                 "Cult", "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle",
  62.                                 "Native American", "Cabaret", "New Wave", "Psychadelic", "Rave",
  63.                                 "Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz",
  64.                                 "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk",
  65.                                 "Folk/Rock", "National Folk", "Swing", "Fusion", "Bebob", "Latin",
  66.                                 "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock",
  67.                                 "Progress. Rock", "Psychadel. Rock", "Symphonic Rock", "Slow Rock",
  68.                                 "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour",
  69.                                 "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony",
  70.                                 "Booty Bass", "Primus", "Porn Groove", "Satire", "Slow Jam",
  71.                                 "Club", "Tango", "Samba", "Folklore", "Ballad", "Power Ballad",
  72.                                 "Rhythmic Soul", "Freestyle", "Duet", "Punk Rock", "Drum Solo",
  73.                                 "A Capella", "Euro-House", "Dance Hall", "Goa", "Drum & Bass",
  74.                                 "Club-House", "Hardcore", "Terror", "Indie", "BritPop", "Negerpunk",
  75.                                 "Polsk Punk", "Beat", "Christian Gangsta Rap", "Heavy Metal",
  76.                                 "Black Metal", "Crossover", "Contemporary Christian", "Christian Rock",
  77.                                 "Merengue", "Salsa", "Thrash Metal", "Anime", "Jpop", "Synthpop"
  78.                                 ];
  79.        
  80.        
  81.         /**
  82.          * Get ID3 Profile
  83.          *
  84.          */  
  85.         private function getID3Profile():void {
  86.             fs = new FileStream();
  87.             try {
  88.                 fs.open(_file, FileMode.READ);
  89.             }catch (e:Error) {}
  90.             if(_file) {
  91.                 clearInterval(interval);
  92.                 interval = setInterval(checkID3, 10, fs);
  93.             }
  94.         }
  95.        
  96.         /**
  97.          * ID3 Flag
  98.          */       
  99.         private var flags:uint;
  100.         private var interval:uint;
  101.         private var interval2:uint;
  102.        
  103.         /**
  104.          * Check if ID3 is Valid.
  105.          * @param fs
  106.          *
  107.          */
  108.         public function checkID3(fs:FileStream):void {
  109.             clearInterval(interval);
  110.             fs.position = 0;
  111.             var type:String = fs.readUTFBytes(3);
  112.             if(type.toLocaleUpperCase() == "ID3") {
  113.                 version = fs.readByte() + fs.readByte()/100;
  114.                 if(version>= 3) {
  115.                     frameIdSize = 4;
  116.                 }
  117.                
  118.                 flags = fs.readByte();
  119.                 unsynchronisation = flags>> 7;
  120.                 extendedHeader = flags>> 6 && 01;
  121.                 experimental = flags>> 5 && 001;
  122.                 clearInterval(interval2);
  123.                 interval2 = setInterval(readFrames, 10);
  124.             }else {
  125.                 dispatchEvent(new ID3ParserEvent(ID3ParserEvent.ERROR, {message:"Not a valid ID3."}));
  126.             }
  127.         }
  128.        
  129.         /**
  130.          * Declared ID3v2 frames
  131.          */       
  132.         private var frames:Object;
  133.        
  134.         /**
  135.          * First FrameId Size. Default is 3.
  136.          */       
  137.         private var frameIdSize:uint = 3;
  138.        
  139.         /**
  140.          * Parse all types of frames in an ID3.
  141.          *
  142.          */   
  143.        
  144.         private var length:int = 0;
  145.         private var lastFSPosition:int = 0;
  146.        
  147.         private function readFrames():void {
  148.             clearInterval(interval2);
  149.             if(!frames) {
  150.                 frames = new Object();
  151.             }
  152.             length = convertSynchsafe(fs.readUnsignedInt()) + 10;
  153.             parseFrames();
  154.             lastFSPosition = fs.position;
  155.             fs.close();
  156.             dispatchEvent(new ID3ParserEvent(ID3ParserEvent.ID3, ID3Info));
  157.         }
  158.        
  159.          private var regEx:RegExp = new RegExp("" +
  160.                         "/AENC|APIC|PIC|" +
  161.                         "COMM|COMR|" +
  162.                         "ENCR|EQUA|ETCO|" +
  163.                         "GEOB|GRID|" +
  164.                         "IPLS|" +
  165.                         "LINK|" +
  166.                         "MCDI|MLLT|" +
  167.                         "OWNE|" +
  168.                         "PRIV|PCNT|POPM|POSS|" +
  169.                         "RBUF|RVAD|RVRB|" +
  170.                         "SYLT|SYTC|" +
  171.                         "T*" +
  172.                         "UFID|USER|USLT|" +
  173.                         "W*/", "ig");
  174.        
  175.         private function parseFrames():void {
  176.             var id:String = "";
  177.             var size:uint = 0;
  178.             if(fs.position <length) {
  179.                 try {
  180.                     id = fs.readUTFBytes(frameIdSize);
  181.                     size = fs.readUnsignedInt();
  182.                     if (version>= 3) {
  183.                         fs.readByte();
  184.                         fs.readByte();
  185.                     }
  186.                     if(id.match(regEx)) {
  187.                         var obj:Object = new Object();
  188.                         obj.encoding = fs.readByte();
  189.                         obj.text = fs.readUTFBytes(size - 1);
  190.                         frames[id] = obj;
  191.                     }
  192.                     parseFrames();
  193.                 }catch(e:Error) {}
  194.             }
  195.         }
  196.        
  197.         /**
  198.          * Get value from a ID3 frame.
  199.          * @param frameType
  200.          * @return
  201.          *
  202.          */       
  203.         public function getFrameType(frameType:String):Object {
  204.             return frames[frameType];
  205.         }
  206.         private var ID3Frames:Object = {GENRE:”TCON”,ALBUM:”TALB”,ARTIST:”TPE1″,COMMENT:”COMM”,TITLE:”TIT2″,TRACK_NUMBER:”TRCK”,LYRICIST:”TOLY”,YEAR:”TYER”};
  207.         /**
  208.          * Returns ID3Info.
  209.          * @return
  210.          *
  211.          */       
  212.         public function get ID3Info():Object {
  213.             if(frames) {
  214.                 var genreType:String = "";
  215.                 if(frames[ID3Frames.GENRE]) {
  216.                     try {
  217.                         genreType = frames[ID3Frames.GENRE].text;
  218.                         if(genreType.indexOf("(") != -1) {
  219.                             genreType = genreType.substr(1, genreType.indexOf(")") - 1);
  220.                             genreType = genres[Number(genreType)];
  221.                         }
  222.                     }catch(e:Error) {
  223.                         genreType = "";
  224.                     }
  225.                 }
  226.                 return {
  227.                             album:frames[ID3Frames.ALBUM] ? frames[ID3Frames.ALBUM].text : "",
  228.                             artist:frames[ID3Frames.ARTIST] ? frames[ID3Frames.ARTIST].text : "",
  229.                             comment:frames[ID3Frames.COMMENT] ? frames[ID3Frames.COMMENT].text : "",
  230.                             genre:genreType,
  231.                             title:frames[ID3Frames.TITLE] ? frames[ID3Frames.TITLE].text : "",
  232.                             track:frames[ID3Frames.TRACK_NUMBER] ? frames[ID3Frames.TRACK_NUMBER].text : "",
  233.                             lyrics:frames[ID3Frames.LYRICIST] ? frames[ID3Frames.LYRICIST].text : "",
  234.                             year:frames[ID3Frames.YEAR] ? frames[ID3Frames.YEAR].text : ""
  235.                         };
  236.             }
  237.             return new Object();
  238.         }
  239.        
  240.         private function convertSynchsafe(value:uint):uint {
  241.             return (value & 127) + 128 * ((value>> 8) & 127) + 16384 * ((value>>16) & 127) + 2097152 * ((value>> 24) & 127);
  242.         }
  243.        
  244.         private var _file:File;
  245.         public function set file(f:File):void {
  246.             _file = f;
  247.             getID3Profile();
  248.         }
  249.        
  250.         public function get file():File {
  251.             return _file;
  252.         }
  253.     }
  254. }

ID3 Parser Event

Actionscript:
  1. package libs.ashier.id3 {
  2.    
  3.     import flash.events.Event;
  4.  
  5.     public class ID3ParserEvent extends Event {
  6.        
  7.         public static const ERROR:String = "error";
  8.         public static const ID3:String = "id3";
  9.        
  10.         public function ID3ParserEvent(type:String, value:Object = null) {
  11.             super(type);
  12.             this.data = value;
  13.         }
  14.  
  15.         public var data:Object;
  16.         override public function clone():Event {
  17.             return new ID3ParserEvent(type, data);
  18.         }
  19.  
  20.     }
  21. }

Usage:

Actionscript:
  1. private var file:File = new File();
  2. private function initialize():void {
  3.         file.browseForDirectory("Browse...");
  4.     file.addEventListener(Event.SELECT, onDirectory);
  5. }
  6.  
  7. private function onDirectory(event:Event):void {
  8.      var files:Array = File(event.target).getDirectoryListing();
  9.          for( var i:Number = 0; i <files.length; i++ ){
  10.         var file:File = File(files[i]);
  11.         var prof:ID3Parser = new ID3Parser();
  12.         prof.addEventListener(ID3ParserEvent.ID3, onID3);
  13.         prof.file = file;
  14.          }
  15. }
  16. private function onID3(event:ID3ParserEvent):void {
  17.         //event.data
  18. }

This is written for an AIR application.

5 Responses to “ID3 Parser”

  1. Chris Brown AUSTRALIA Says:

    on the ID3Parser I get:
    Access of undefined property ID3Frames.

    you also have your package twice in ID3Parser.

  2. Chris Brown AUSTRALIA Says:

    No worries, got it working.
    For anyone else just add this to ID3Parser:

    private var ID3Frames:Object = {GENRE:”TCON”,ALBUM:”TALB”,ARTIST:”TPE1″,COMMENT:”COMM”,TITLE:”TIT2″,TRACK_NUMBER:”TRCK”,LYRICIST:”TOLY”,YEAR:”TYER”};

    you might also want to change the constructor name in ID3Parser ;)

  3. Ashier de Leon Says:

    ooops… nice find. Thanks

  4. Don INDIA Says:

    Great Help, Thanks.

  5. Flex Ajax UNITED STATES Says:

    Why would you use this over the sound method?

    var s:Sound = new Sound();
    s.addEventListener(Event.ID3, onID3InfoReceived);
    var url:URLRequest = new URLRequest(file.url);
    s.load(url);

    private function onID3InfoReceived(event:Event):void {
    var id3:ID3Info = event.target.id3;
    }

Leave a Reply