How to load ByteArray to Sound Class (1)

摘要:How to load ByteArray to Sound Class (1)

Adobe Flex提供相當多支援多媒體的Class,包括:Sound、Video等,豐富並便利像我對於一些多媒體運用比較不熟悉的程式人員,
透過簡單的使用URLRequest(url:String) 將所需要的多媒體檔案之路徑或URL網址給予這些多媒體Class,即可以快速取得,並且進行播放。
但是我自己卻遇到了一個問題,如果今天我存在於Client或Server的多媒體檔(以MP3為例),那麼,我將必需知道所有檔案的路徑,老實說,
這樣有點辛苦,程式撰寫起來也不是說這麼方便。況且,現在是Database大量使用的年代,大部分的人都會把多媒體資訊儲存於Database之中
(但這不包括大型娛樂多媒體檔案),而這些存在於Database中的檔案,通常是利用二位元陣列(ByteArray)來存放,那在Adobe Flex所提供
的方式, 主要是透過URLRequest,這種情形下就沒有辦法支援讀取ByteArray的內容了。
看到這裡也許會有人誤會了我的意思,可能會被誤解為,那我可以透過 Embed的方式,就可以讓Sound來播放ByteArray了啊。這個方式,
如下之範例:
[Embed(source="assets/Sounds/voices/hello.mp3")]    
private var musicClass:Class;        
private var music:Sound = new musicClass();    
private var soundChannel:SoundChannel=null;            
public function Test() 
{        
  soundChannel = music.play();        
} 
這個方式主要是把原始檔案根據路徑讀取,並且轉成Class的方式提供給Sound使用。這個方式其實沒有錯。但跟我真正遇到的問題還是不同的,
因為我主要強調是在於已經存於Database中的MP3,它是以ByteArray的方式存在,而不是一個檔案的方式,當然我也試過比較笨的方式,先把資
料從Database讀出來再寫入Disk,最後透過路徑的方式進行播放。這些都是不錯的方式,但效率不是特別好。
因此,我找到了一篇 <<How to load MP3 files from a FileReference>>,說明如透過FileReference把存在的MP3檔,
轉換成ByteArray並且進行播放。
文中的第一段就提到我剛才所描述的問題:「FileReference.load is a nice new feature in Flash Player 10, but the only thing you get back from it is the FileReference.data property, which is a ByteArray. This is useless (well, not altogether, as we’ll see) in the case of MP3 audio files, because the built-in Sound API does not support loading from ByteArray.」
這篇文章中也有提供Souce Code的下載,MP3FileReferenceLoaderLib.zip,我稍微的為大家解釋一下:這個 MP3FileReferenceLoaderLib,
主要分成幾個重要的Class,
1. ByteArraySegement.as:做為擷取MP3 Data中的片段,進行部分資料與Header上修改上的使用。
2. SoundClassSwfByteCode.as:內部主要放了大量16位元的參數,做為Header上的位元資料修正與增加。
3. MP3FileReferenceLoader.as:主是產生MP3檔案的載入器,包括引入MP3Parser去剖析你所選擇的檔案,並且修改原本讀進來的檔案,
                                                    轉成Sound與SWF所能看懂的多媒體格式。
4. MP3Parser.as:剖析使用者選擇的檔案,將選擇的MP3檔轉成ByteArray,取得相關ID3之資訊與提供編寫ByteArray的方法。
5. MP3SoundEvent.as:整個Libary最後指定將ByteArray指定給Sound的事件。
另外更有範例的說明,如下表。主要操作方式:
1)先宣告一個MP3FileReferenceLoader的object(稱loader)與FileReference的object(稱fileReference),
2)接著透過這個fileReference.browser()跳出使用者選擇畫面,供使用者選擇MP3檔案,3)選擇完成觸發Event.SELECT事件,
   進行loader.getSound(),
將實體檔案轉成SWF可以播放的Sound物件,編轉完成後,4)觸發MP3SoundEvent.COMPLETE事件,進行音樂的播放。
 
   1: package {
   2:     import flash.display.Sprite;
   3:     import flash.events.Event;
   4:     import flash.events.MouseEvent;
   5:     import flash.net.FileFilter;
   6:     import flash.net.FileReference;
   7:     
   8:     import org.audiofx.mp3.MP3FileReferenceLoader;
   9:     import org.audiofx.mp3.MP3SoundEvent;
  10:  
  11:     public class MP3FileReferenceTest extends Sprite
  12:     {
  13:         private var loader:MP3FileReferenceLoader;
  14:         private var fileReference:FileReference;
  15:         public function MP3FileReferenceTest()
  16:         {
  17:             loader=new MP3FileReferenceLoader();
  18:             loader.addEventListener(MP3SoundEvent.COMPLETE,mp3LoaderCompleteHandler);
  19:             fileReference=new FileReference();
  20:             fileReference.addEventListener(Event.SELECT,fileReferenceSelectHandler);
  21:             stage.addEventListener(MouseEvent.CLICK,clickHandler);
  22:         }
  23:         private function clickHandler(ev:MouseEvent):void
  24:         {
  25:             fileReference.browse([new FileFilter("mp3 files","*.mp3")]);
  26:         }
  27:         private function fileReferenceSelectHandler(ev:Event):void
  28:         {
  29:             loader.getSound(fileReference);
  30:         }
  31:         private function mp3LoaderCompleteHandler(ev:MP3SoundEvent):void
  32:         {
  33:             ev.sound.play();
  34:         }
  35:     }
  36: }
 
上面的Demo Code我相信不難理解。其另外5個主要的Class才是真的比較不好懂的地方,小弟不材,有些相關於SWF與MP3格式與
位元偏移的概念, 不是特別熟悉,因此沒有辦法多做詳細的說明。如果各位覺得有需要補充的,也可以留言在下面,大家彼此分享。
另外,如果你在執行上有遇到 “SecurityError: Error #3015: Loader.loadBytes() is not permitted to load content with executable code.”
這個問題時, 你可以參考[2],你只需要在 "MP3FileReferenceLoader.as"的generateSound()中,找到var swfBytesLoader:Loader=new Loader();
,並且修改為如下:
var swfBytesLoader:Loader=new Loader();
var loaderContext:LoaderContext = new LoaderContext();
loaderContext.allowLoadBytesCodeExecution = true;
swfBytesLoader.contentLoaderInfo.addEventListener(Event.COMPLETE,swfCreated);
swfBytesLoader.loadBytes(swfBytes,loaderContext);
將可以修改掉剛SecurityError: Error #3015: Loader.loadBytes() is not permitted to load content with executable code.所出現的錯誤。
 
References: