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