public
Last active

  • Download Gist
MP3Loader.as
ActionScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
// Author: Claus Wahlers
// License: Public Domain
 
// Dependencies:
// as3commons-bytecode-1.0-RC1.swc
// as3commons-lang-0.3.2.swc
// as3commons-logging-2.0.swc
// as3commons-reflect-1.3.4.swc
// as3swf.swc
// MinimalComps_0_9_9.swc
 
package
{
import com.bit101.components.PushButton;
import com.codeazur.as3swf.SWF;
import com.codeazur.as3swf.SWFData;
import com.codeazur.as3swf.data.SWFSymbol;
import com.codeazur.as3swf.tags.TagDefineSound;
import com.codeazur.as3swf.tags.TagDoABC;
import com.codeazur.as3swf.tags.TagEnd;
import com.codeazur.as3swf.tags.TagFileAttributes;
import com.codeazur.as3swf.tags.TagShowFrame;
import com.codeazur.as3swf.tags.TagSymbolClass;
 
import org.as3commons.bytecode.abc.AbcFile;
import org.as3commons.bytecode.emit.IAbcBuilder;
import org.as3commons.bytecode.emit.IClassBuilder;
import org.as3commons.bytecode.emit.IPackageBuilder;
import org.as3commons.bytecode.emit.impl.AbcBuilder;
import org.as3commons.bytecode.io.AbcSerializer;
 
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.media.Sound;
import flash.net.FileFilter;
import flash.net.FileReference;
import flash.utils.ByteArray;
 
public class MP3FileRefLoader extends Sprite
{
private static const PACKAGENAME:String = "tmp";
private static const CLASSNAME:String = "SoundClass";
private static const QNAME:String = PACKAGENAME + "." + CLASSNAME;
public function MP3FileRefLoader()
{
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE
addChild(new PushButton(this, 10, 10, "Load MP3", function():void {
var ref:FileReference = new FileReference();
ref.addEventListener(Event.SELECT, function(e:Event):void { ref.load(); });
ref.addEventListener(Event.COMPLETE, function(e:Event):void { loadBytes(ref.data); });
ref.browse([new FileFilter("MP3 (*.mp3)", "*.mp3")]);
}));
}
 
protected function loadBytes(mp3:ByteArray):void {
// Wrap the MP3 with a SWF
var swf:ByteArray = createSWFFromMP3(mp3);
// Load the SWF with Loader::loadBytes()
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.INIT, initHandler);
loader.loadBytes(swf);
}
 
protected function initHandler(e:Event):void {
// Get the sound class definition
var SoundClass:Class = LoaderInfo(e.currentTarget).applicationDomain.getDefinition(QNAME) as Class;
// Instantiate the sound class
var sound:Sound = new SoundClass() as Sound;
// Play the sound
sound.play();
}
 
protected function createSWFFromMP3(mp3:ByteArray):ByteArray
{
// Create an empty SWF
// Defaults to v10, 550x400px, 50fps, one frame (works fine for us)
var swf:SWF = new SWF();
 
// Add FileAttributes tag
// Defaults: as3 true, all other flags false (works fine for us)
swf.tags.push(new TagFileAttributes());
 
// Add DefineSound tag
// The ID is 1, all other parameters are automatically
// determined from the mp3 itself.
swf.tags.push(TagDefineSound.createWithMP3(1, mp3));
 
// Create and add DoABC tag
// Contains the AS3 byte code for the class definition for the embedded sound:
// package tmp {
// public dynamic class SoundClass extends flash.media.Sound {
// }
// }
var abcBuilder:IAbcBuilder = new AbcBuilder();
var packageBuilder:IPackageBuilder = abcBuilder.definePackage(PACKAGENAME);
var classBuilder:IClassBuilder = packageBuilder.defineClass(CLASSNAME, "flash.media.Sound");
var abcFile:AbcFile = abcBuilder.build();
var abcSerializer:AbcSerializer = new AbcSerializer();
var abcBytes:ByteArray = abcSerializer.serializeAbcFile(abcFile);
swf.tags.push(TagDoABC.create(abcBytes));
 
// Add SymbolClass tag
// Binds the sound class definition to the embedded sound
var symbolClass:TagSymbolClass = new TagSymbolClass();
symbolClass.symbols.push(SWFSymbol.create(1, QNAME));
swf.tags.push(symbolClass);
 
// Add ShowFrame tag
swf.tags.push(new TagShowFrame());
 
// Add End tag
swf.tags.push(new TagEnd());
 
// Publish the SWF
var swfData:SWFData = new SWFData();
swf.publish(swfData);
 
return swfData;
}
}
}

Hi Claus,
I am glad to see that someone has encountered this exact problem since it is the one that I am facing currently. I am however experiencing a problem when attempting to use this solution. I upload the mp3 and get the following error:

RangeError: Error #1125: The index 0 is out of range 0.
at com.codeazur.as3swf::SWFTimelineContainer/publishTags()[/Users/claus/Projects/as3swf/src/com/codeazur/as3swf/SWFTimelineContainer.as:222]
at com.codeazur.as3swf::SWF/publish()[/Users/claus/Projects/as3swf/src/com/codeazur/as3swf/SWF.as:68]
at MP3FileRefLoader/createSWFFromMP3()[E:\Projects\AudioRecorder\src\MP3FileRefLoader.as:123]
at MP3FileRefLoader/loadBytes()[E:\Projects\AudioRecorder\src\MP3FileRefLoader.as:64]
at Function/()[E:\Projects\AudioRecorder\src\MP3FileRefLoader.as:57]

I am using Flash Builder 4.5, compiling with Flex sdk version 4.1 and running Flash Player 10.3. I am hoping that you might have an idea of where I'm going wrong here.
Many thanks,Gareth

Gareth,
you are doing nothing wrong. A recent as3swf update introduced this issue. I committed a bugfix, please pull as3swf.swc from the repo and try again. Thanks for reporting this.
Claus.

Many thanks for the prompt action on this Claus.
I managed to put a little hack around the raw tags issue last night but I'll replace this with your latest swc today.

Hi Claus,

Where can i get the repository link for com.codeazur.as3swf
Thanks,
Rajan

hi, thanks for that !
this goes way beyond my understanding of th player but it works :)

I have an issue though: when using your technique, the ID3 tags seem to be discarded.
have I done something wrong or is it a limitation?
is there (simple) a workaround ?

anyway, thank you for this one
this is really brilliant
Nicolas

Nicolas, yeah i'm stripping anything that's not an MPEG frame.
The relevant code is in TagDefineSound.processMP3.

(thanks for your swift answer :) )
I see...
so the the ID3 tags must be located somewhere between 0 and 10 + ((mp3[i + 6] << 21) | (mp3[i + 7] << 14) | (mp3[i + 8] << 7) | mp3[i + 9]); in the ByteArray.
this is really way beyond my understanding ;)

I found an ID3Reader lib here : http://blog.benstucki.net/?p=3 ( metaphile on the SVN is up to date )
combining your scripts with his might do the trick.

again thanks a bunch for this,
cheers,
Nicolas

I should have documented that piece of code, because it's even beyond my understanding today, lol.
You should be able to use the MP3 as is, btw. FP should handle it fine. So you could try and just copy the MP3 bytearray to TagDefineSound.soundData. The main thing processMP3() does (apart from stripping non-frame stuff) is determining the MP3's parameters.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.