2010年2月24日 星期三

AS3的MP3音樂編輯器!?


很久以前剛開始碰byteArray類別時,覺得很多東西可以用byteArray來玩,就曾經去找過mp3的檔案結構來看,然後寫了一個可以把mp3切來切去組來組去的類別檔。
但是後來發現雖然可以切割組合,但是卻遇到了兩個問題:
1. 無法混音,因為mp3裡的音樂資料都是被編碼過的,以AS3的效能來看,即使有codec,AS3也跑不動。
2. Sound類別無法直接讀取byteArray來播放mp3。(這邊指的byteArray是用URLStream或FileReference所讀入的mp3的byteArray,而不是FP 10之後Sound類別透過SampleDataEvent發聲的那種byteArray。)既然無法直接將mp3 的byteArray丟到Sound裡,那麼這個將mp3拆解組合的類別基本上就是一個很雞肋的東西了。
由於遇到這兩個問題,因此我很久就沒再去想起這個類別。

但是最近在查資料的時候,卻意外的發現上述第二點有解法了!這個解法是由Christopher Martin-Sperry這個人所寫的(http://www.flexiblefactory.co.uk/flexible/?p=46),他主要的原理是先在記憶體裡產生一個swf bytecode,再將mp3的資料「注射」到這個swf裡,接著用loadBytes的方式實體化swf,再用getDefinition的方式取出Sound類別,有興趣的可以自己點上面的聯結過去,那個頁面裡有他的類別可以下載,基本上如果我們如果純粹只是要將mp3的byteArray轉成Sound的話,只會用到ByteArraySegment.as、MP3Parser.as及SoundClassSwfByteCode.as這三個類別而已。
發現了這個solution之後,我這兩天就又把之前我自己搞的那個類別拿出來玩了,即使沒辦法混音,想說這樣至少還可以線上切割組合mp3之後馬上播放來聽,而且可以在mp3裡加入一些防君子不防小人的版權保護的東西,於是很手癢的就動手寫了起來。
只不過該怎麼說呢,應該說沒有每天在過年的,我遇到了另一個問題,努力了兩天之後目前無解,情況如下:
現在拆解切割組合以及播放都很ok,歸功於FP10,組好的檔還可以直接存在本機端。但是不管我怎麼修改找問題,切出來的那一塊部份,總是跟我原本想切的那一塊有一點點的時間差,曲子越長誤差越大,可以誤差到約一秒多的時間。

flash點這裡
有興趣的可以自己點過去玩玩看(需要Flash Player10),上傳一個mp3(別太大,因為我又有跑Sound.extract(),所以mp3太大的話記憶體會很耗),拉一個區塊,點「播放被選取的」聽聽看,再點「另存新檔」,再聽聽實體檔,就知道我在說什麼了。

我後來想一想,會不會是我在拆解mp3的byteArray時查找frame header時出了問題?
一般header的格式是4個bytes,以binary的來看的話其格式會是像這樣『AAAAAAAA』『AAABBCCD』 『EEEEFFGH』『IIJJKLMM』的格式,每個字母代表一個意義,而且每個frame的header不一定會一樣,我卻是偷懶直接取header的前兩個bytes來當辨識元,只要符合0xFF 0xFB的我就把它視為是header,我知道光用兩個bytes就來判斷是否是header的做法很冒險,而且運氣差一點的話有些header的第二個byte搞不好根本不是0xFB。
但如果真的是frame header出了問題,我目前說真的還沒想到要怎樣很有效率的依序找出每一個frame的header。

算了,也許這個問題我就只好先擺著了。
如果路過的有高手知道我可能犯錯的問題在哪的話,還請幫忙解答一下~~
感謝!

6 則留言:

  1. 混音的功能 我也正在找资料,网上有例子在07年实现了,只是作者不再提供代码。http://richapps.de/?p=99,这个例子在adobe官网也有文章介绍,就是没提供代码下载

    回覆刪除
  2. 他讀入wav的方法跟我這篇裡寫的那個Christopher Martin-Sperry所用的方法是一樣的, 我想其實重點還是在音樂資料編碼的部份. mp3的解碼跟編碼對AS3來說即使真的有codec可能還是跑不動, 所以無法實作mixer, 但是wav的編碼可能就簡單多了, 所以輸出也是輸出成wav.
    Flash player 10.1之後也可以開始輸出wav的聲音, 但要轉成mp3仍然再需要server或本機端再一道手續.

    回覆刪除
  3. HI 請問這個要怎麼實作
    可以再深入教學一下嗎?
    因為我發現把sound丟給byteArray 然後用SampleDataEvent.SAMPLE_DATA事件去播放
    我不知道如何暫停再播放耶?可否教學一下,感謝

    回覆刪除
  4. eric:
    這篇是沒用到SampleDataEvent的.
    但如果你用SampleDataEvent, 要暫停的話就是停止繼續把byteArray丟出來就好了. 要繼續播放的話就是記住上次暫停時播放到byteArray的哪一個地方, 接著再從上次的地方繼續丟byteArray出來就好.

    回覆刪除
  5. 關於這個方法,有幾點我不會實踐
    就是我對於byteArray裡的data不會控制orz,
    所以我不會存暫停點@@?
    所以我想到先剪好音檔然後再丟byteArray去播放
    因為我對sound.extract()函式不熟悉,然後我看到你這篇可以剪輯音檔所以想請教jason是如何剪輯音檔可否對sound.extract()解密一下,然後暫停點也可否解說一下
    感謝orz

    回覆刪除
  6. sound.extract()並不能剪接音檔.sound.extract()主要是用來取出音檔裡的頻譜而已.
    我剪接音檔,其實只是記錄使用者選擇的區塊,然後直接拆解"原mp3的bytearray",將它的所有keyframe拆出來,依照比例重新組合.
    而這個bytearray跟sound.extract()所取得的bytearray是完全不同的.(跟SampleDataEvent的bytearray也不同),這個bytearray是直接讀取mp3的binary.

    回覆刪除