PageRenderTime 44ms CodeModel.GetById 32ms app.highlight 9ms RepoModel.GetById 1ms app.codeStats 0ms

/flash/MP3FileReferenceLoaderLib/src/org/audiofx/mp3/MP3Parser.as

http://echo-nest-remix.googlecode.com/
ActionScript | 266 lines | 210 code | 34 blank | 22 comment | 9 complexity | cbcd159c146d6505a6df31351d251c6e MD5 | raw file
  1/*
  2Copyright (c) 2008 Christopher Martin-Sperry (audiofx.org@gmail.com)
  3
  4Permission is hereby granted, free of charge, to any person obtaining a copy
  5of this software and associated documentation files (the "Software"), to deal
  6in the Software without restriction, including without limitation the rights
  7to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8copies of the Software, and to permit persons to whom the Software is
  9furnished to do so, subject to the following conditions:
 10
 11The above copyright notice and this permission notice shall be included in
 12all copies or substantial portions of the Software.
 13
 14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 17AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 18LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 19OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 20THE SOFTWARE.
 21*/
 22
 23package org.audiofx.mp3
 24{
 25	import flash.events.Event;
 26	import flash.events.EventDispatcher;
 27	import flash.events.IOErrorEvent;
 28	import flash.net.FileReference;
 29	import flash.net.URLLoader;
 30	import flash.net.URLLoaderDataFormat;
 31	import flash.net.URLRequest;
 32	import flash.utils.ByteArray;
 33	
 34
 35	
 36	
 37	[Event(name="complete", type="flash.events.Event")]
 38	internal class MP3Parser extends EventDispatcher
 39	{
 40		private var mp3Data:ByteArray;
 41		private var loader:URLLoader;
 42		private var currentPosition:uint;
 43		private var sampleRate:uint;
 44		private var channels:uint;
 45		private var version:uint;
 46		private static var bitRates:Array=[-1,32,40,48,56,64,80,96,112,128,160,192,224,256,320,-1,-1,8,16,24,32,40,48,56,64,80,96,112,128,144,160,-1];
 47		private static var versions:Array=[2.5,-1,2,1];
 48		private static var samplingRates:Array=[44100,48000,32000];
 49		public function MP3Parser()
 50		{
 51			
 52			loader=new URLLoader();
 53			loader.dataFormat=URLLoaderDataFormat.BINARY;
 54			loader.addEventListener(Event.COMPLETE,loaderCompleteHandler);
 55			
 56			
 57		}
 58		internal function load(url:String):void
 59		{
 60			var req:URLRequest=new URLRequest(url);
 61			loader.load(req);
 62		}
 63		internal function loadFileRef(fileRef:FileReference):void
 64		{
 65			fileRef.addEventListener(Event.COMPLETE,loaderCompleteHandler);
 66			fileRef.addEventListener(IOErrorEvent.IO_ERROR,errorHandler);
 67			//fileRef.addEventListener(Event.COMPLETE,loaderCompleteHandler);
 68			fileRef.load();
 69		}
 70		private function errorHandler(ev:IOErrorEvent):void
 71		{
 72			trace("error\n"+ev.text);
 73		}
 74		private function loaderCompleteHandler(ev:Event):void
 75		{
 76			mp3Data=ev.currentTarget.data as ByteArray;
 77			currentPosition=getFirstHeaderPosition();
 78			dispatchEvent(ev);
 79		}
 80		private function getFirstHeaderPosition():uint
 81		{
 82			mp3Data.position=0;
 83			
 84			
 85			while(mp3Data.position<mp3Data.length)
 86			{
 87				var readPosition:uint=mp3Data.position;
 88				var str:String=mp3Data.readMultiByte(3,"us-ascii");
 89				
 90				
 91				if(str=="ID3") //here's an id3v2 header. fuck that for a laugh. skipping
 92				{
 93					mp3Data.position+=3;
 94					var b3:int=(mp3Data.readByte()&0x7F)<<21;
 95					var b2:int=(mp3Data.readByte()&0x7F)<<14;
 96					var b1:int=(mp3Data.readByte()&0x7F)<<7;
 97					var b0:int=mp3Data.readByte()&0x7F;
 98					var headerLength:int=b0+b1+b2+b3;
 99					var newPosition:int=mp3Data.position+headerLength;
100					trace("Found id3v2 header, length "+headerLength.toString(16)+" bytes. Moving to "+newPosition.toString(16));
101					mp3Data.position=newPosition;
102					readPosition=newPosition;
103				}
104				else
105				{
106					mp3Data.position=readPosition;
107				}
108				
109				var val:uint=mp3Data.readInt();
110				
111				if(isValidHeader(val))
112				{
113					parseHeader(val);
114					mp3Data.position=readPosition+getFrameSize(val);
115					if(isValidHeader(mp3Data.readInt()))
116					{
117						return readPosition;
118					}
119					
120				}
121
122			}
123			throw(new Error("Could not locate first header. This isn't an MP3 file"));
124		}
125		internal function getNextFrame():ByteArraySegment
126		{
127			mp3Data.position=currentPosition;
128			var headerByte:uint;
129			var frameSize:uint;	
130			while(true)
131			{
132				if(currentPosition>(mp3Data.length-4))
133				{
134					trace("passed eof");
135					return null;
136				}
137				headerByte=mp3Data.readInt();
138				if(isValidHeader(headerByte))
139				{
140					frameSize=getFrameSize(headerByte);
141					if(frameSize!=0xffffffff)
142					{
143						break;
144					}
145				}
146				currentPosition=mp3Data.position;
147				
148			}
149
150			mp3Data.position=currentPosition;
151			
152			if((currentPosition+frameSize)>mp3Data.length)
153			{
154				return null;
155			}
156			
157			currentPosition+=frameSize;
158			return new ByteArraySegment(mp3Data,mp3Data.position,frameSize);
159		}
160		internal function writeSwfFormatByte(byteArray:ByteArray):void
161		{
162			var sampleRateIndex:uint=4-(44100/sampleRate);
163			byteArray.writeByte((2<<4)+(sampleRateIndex<<2)+(1<<1)+(channels-1));
164		}
165		private function parseHeader(headerBytes:uint):void
166		{
167			var channelMode:uint=getModeIndex(headerBytes);
168			version=getVersionIndex(headerBytes);
169			var samplingRate:uint=getFrequencyIndex(headerBytes);
170			channels=(channelMode>2)?1:2;
171			var actualVersion:Number=versions[version];
172			var samplingRates:Array=[44100,48000,32000];
173			sampleRate=samplingRates[samplingRate];
174			switch(actualVersion)
175			{
176				case 2:
177					sampleRate/=2;
178					break;
179				case 2.5:
180					sampleRate/=4;
181			}
182			
183		}
184		private function getFrameSize(headerBytes:uint):uint
185		{
186			
187			
188			var version:uint=getVersionIndex(headerBytes);
189			var bitRate:uint=getBitrateIndex(headerBytes);
190			var samplingRate:uint=getFrequencyIndex(headerBytes);
191			var padding:uint=getPaddingBit(headerBytes);
192			var channelMode:uint=getModeIndex(headerBytes);
193			var actualVersion:Number=versions[version];
194			var sampleRate:uint=samplingRates[samplingRate];
195			if(sampleRate!=this.sampleRate||this.version!=version)
196			{
197				return 0xffffffff;
198			}
199			switch(actualVersion)
200			{
201				case 2:
202					sampleRate/=2;
203					break;
204				case 2.5:
205					sampleRate/=4;
206			}
207			var bitRatesYIndex:uint=((actualVersion==1)?0:1)*bitRates.length/2;
208			var actualBitRate:uint=bitRates[bitRatesYIndex+bitRate]*1000;			
209			var frameLength:uint=(((actualVersion==1?144:72)*actualBitRate)/sampleRate)+padding;
210			return frameLength;
211			
212		}
213		
214	 	private function isValidHeader(headerBits:uint):Boolean 
215	    {
216	        return (((getFrameSync(headerBits)      & 2047)==2047) &&
217	                ((getVersionIndex(headerBits)   &    3)!=   1) &&
218	                ((getLayerIndex(headerBits)     &    3)!=   0) && 
219	                ((getBitrateIndex(headerBits)   &   15)!=   0) &&
220	                ((getBitrateIndex(headerBits)   &   15)!=  15) &&
221	                ((getFrequencyIndex(headerBits) &    3)!=   3) &&
222	                ((getEmphasisIndex(headerBits)  &    3)!=   2)    );
223	    }
224	
225	    private function getFrameSync(headerBits:uint):uint     
226	    {
227	        return uint((headerBits>>21) & 2047); 
228	    }
229	
230	    private function getVersionIndex(headerBits:uint):uint  
231	    { 
232	        return uint((headerBits>>19) & 3);  
233	    }
234	
235	    private function getLayerIndex(headerBits:uint):uint    
236	    { 
237	        return uint((headerBits>>17) & 3);  
238	    }
239	
240	    private function getBitrateIndex(headerBits:uint):uint  
241	    { 
242	        return uint((headerBits>>12) & 15); 
243	    }
244	
245	    private function getFrequencyIndex(headerBits:uint):uint
246	    { 
247	        return uint((headerBits>>10) & 3);  
248	    }
249	
250	    private function getPaddingBit(headerBits:uint):uint    
251	    { 
252	        return uint((headerBits>>9) & 1);  
253	    }
254	
255	    private function getModeIndex(headerBits:uint):uint     
256	    { 
257	        return uint((headerBits>>6) & 3);  
258	    }
259	
260	    private function getEmphasisIndex(headerBits:uint):uint
261	    { 
262	        return uint(headerBits & 3);  
263	    }
264		
265	}
266}