我對as3聲音架構的理解

一直都對as3的聲音架構設計感到困惑,as3爲什麼要把播放和停止方法分開呢?爲什麼不能用一個Sound更換載入的聲音呢?這幾天有空專門專研了一下。原來as3對聲音的設計也是深思熟慮的。目的不是爲了把問題搞得複雜,而是爲了在功能強化的同時做到精簡。並儘可能方便我們使用。現在把我這兩天的理解拿出來共享,有什麼不到位的地方請多多建議。
[1b]架構圖:
[/1b]
[url=http://bbs.actionscript3.cn/attachment.php?aid=3966]
[img]http://bbs.actionscript3.cn/attachments/month_0805/20080511_0a5323449bfdc1f45cd7qsqNkZgNOgKD.jpg[/img]
[color="#000000"]架構圖.jpg[/color]
[/url]
可以看出,as3中的聲音處理是獨立的,不再依賴影片剪輯作爲聲道。as3使用多個類合作處理聲音,功能被細化到對應的類中,使得邏輯更加清晰。下面對着架構圖由下往上詳細闡述各個類所但當的任務。
Sound類負責創建、加載、播放聲音。Sound類更像BitmapData扮演的角色,用於描述數據。而不是控制聲音。使用Sound創建聲音有兩種創建方式:
[1b]1.[/1b][1b]通過庫中元件創建和載入[/1b]
庫中聲音是Sound的子類。要播放必須創建它們的實例。
[1b]2.[/1b][1b]通過外部加載的聲音[/1b]
外部加載聲音創建類型是Sound類本身。爲強調一個類描述一個聲音數據這種模式,Sound禁止第二次加載外部聲音。加載控制符合open—progress—complete的標準。
Sound的構造函數爲Sound(stream:URLRequest = null, context:SoundLoaderContext = null),創建外部聲音時與load方法參數相同。如果創建庫中的聲音則不需要任何參數。
[1b]方法:[/1b]
load(stream:URLRequest = null, context:SoundLoaderContext = null):void
加載聲音。不能使用一個Sound對象加載多個聲音。當有多聲音播放時,必須爲每個聲音創建不同的Sound對象。
play(startTime:Number = 0, loops:int = 0, sndTransform:SoundTransform = null):SoundChannel
播放聲音。startTimer爲開始播放事件,單位爲毫秒。loops爲循環次數,0和1都表示播放一次,如同flash屬性面板中的次數設置。sndTransform是分配給每個通道的初始對象,如果有必要還可以在soundChannel中修改這個對象。每次播放都會選擇不通的聲道,對應方法返回值。多次執行會導致重疊播放。
close():void
關閉聲音流。對於流式播放聲音,close很重要,因爲soundChannel的stop方法不足以停止流式下載的聲音。可能產生繼續播放的現象。
[1b]事件:[/1b]
open:
類型爲Event.OPEN。開始下載時觸發。
progress:
類型爲PrograssEvent.PROGRESS。下載時觸發。
complete:
類型爲Event.COMPLETE。下載完畢時觸發。
ioError:
類型爲Event.IO_ERROR。發生IO錯誤時觸發。
id3:
類型爲Event.ID3。可以訪問id3數據時觸發。
[1b]屬性:[/1b]
所有屬性都是隻讀的,用來訪問加載信息。
url:String
加載聲音的url值。
bytesLoaded:uint
加載字節數。
bytesTotal:int
總字節數。
length:Number
當前聲音長度,如果加載中只反應加載的長度。
id3:ID3Info
id3屬性包含唱片信息。
[1b]加載播放外部聲音的順序:[/1b]
加載播放外部聲音的順序如下:
[color="#000000"]
[img]http://bbs.actionscript3.cn/attachments/month_0805/20080509_086ff0682560a7ba59e2Hs3lf0oGWnsF.jpg[/img]
ID3Info類[/color]
ID3Info類用於描述聲音信息。對應Sound類的id3屬性。
[1b]
[color="#4f81bd"]屬性:[/color]
[/1b]
songName:String
歌曲名稱。
track:String
曲目編號。
artist:String
歌手名稱。
album:String
專輯名稱。
yesr:String
錄製年份。
genre:String
歌曲流派。
comment:String
相關注釋。
[1b]
[color="#4f81bd"]獲取唱片信息順序:[/color]
[/1b]
[url=http://bbs.actionscript3.cn/attachment.php?aid=3967]
[img]http://bbs.actionscript3.cn/attachments/month_0805/20080511_41c5f98e94dbbeee9a57yFkL8ZKBO1ZT.jpg[/img]
[color="#000000"]獲取唱片信息.jpg[/color]
[/url]
SoundChannel類
SoundChannel繼承EventDispatcher,使用import flash.media.SoundChannel聲明。SoundChannel用於描述聲音通道。有了這個類,聲音通道再不用依賴MovieClip了。多個聲道可以播放一個聲音,一個聲道可以播放多個聲音(同一時刻只能播放一個)。對聲音的操控和狀態描述從描述聲音數據的Sound類中分離出來。聲道與聲音之間的關係類似Bitmap和BitmapData的關係。這種邏輯描述更加精確。但與位圖不同的是聲音通道不是由用戶創建的而是系統生成的。這個紐帶是Sound的play方法。採用這種處理方式是爲了降低複雜性,因爲系統自動管理聲道免去我們因聲道分配分散注意力。系統每次選擇空餘聲道進行回放。當停止了一個聲道的播放後,聲道自動被as回收。這樣一來,我們完全不必去管聲道如何分配的,就如顯示對象的深度管理一樣。
[1b]方法:[/1b]
stop()
停止播放聲音。但不能停止流式下載時播放的聲音。
[1b]屬性:[/1b]
position:Number
播放頭位置,單位毫秒。只讀屬性。這表示position並不能設置播放進度。要設置播放進度只能使用play的參數。
leftPear:Number
左聲道音量。範圍爲0-1。這個屬性並不用來控制平移而是用來讀取左聲道峯值波幅。可以用這個屬性檢查是否是單聲道。
rightPear:Number
右聲道音量。範圍爲0-1。這個屬性並不用來控制平移而是用來讀取右聲道峯值波幅。可以用這個屬性檢查是否是單聲道。
soundTransform:SoundTransform
分配給聲道的soundTransform對象。
[1b]事件:[/1b]
soundComplete
類型爲Event.SoundComplete。聲音播放完畢後觸發。
[1b]監視和設置聲音播放進度:[/1b]
[1b]監視播放進度:[/1b]
對於下載完畢的聲音可以使用position監視播放進度。但對於正在下載中的聲音雖然position可以正常訪問,但length屬性只能在下載完畢後才能獲取,這給監視播放進度造成了困難。這時可以通過bytesLoaded和bytesTotal的比值估算出長度。公式爲:position*bytesTotal/bytesLoaded。
[1b]設置播放進度:[/1b]
由於position屬性是隻讀的。因此不能通過它設置播放進度。設置播放進度只能通過play方法的startTime參數。這是因爲考慮到功能重複。採取這種邏輯還有個長處是不必管播放的狀態。方法也只有播放和停止而沒有暫停方法。少了一個狀態和方法後,播放進度不必管是否播放或暫停。所以免去了playState屬性。position的只讀特性也避免設置進度時是否要保持播放或暫停狀態。現在一切都被簡化成播放和停止兩個方法了。邏輯大大簡化。而暫停和設置進度都由播放和停止兩個方法衍生出來。方法如下:
設置進度=stop+play(startTime)
暫停播放=記錄position+stop
恢復播放=play(記錄position)
SoundTansform類
SoundTransform
SoundMixer類繼承Object。使用import flash.media.SoundMixer聲明。由於不使用MovieClip綁定聲音通道。所以描述全局聲音通道的不是主時間軸而是SoundMixer類。由於與任何聲道無關,所以SoundMixer是個靜態類。
[1b]
[color="#4f81bd"]屬性:[/color]
[/1b]
SoundMixer.bufferTime:int
全局緩衝時間。如果在Sound類的play方法中未指定緩衝則由這裏決定。
SoundMixer.soundTransform:SoundTransform
全局SoundTransform對象。控制總體聲音效果。當應用了單個sound的soundTransform屬性後再應用總聲音特效。比如對於音量控制而言,這裏是總音量控制。
[1b]
[color="#4f81bd"]方法:[/color]
[/1b]
SoundMixer.stopAll():void
停止所有通道播放聲音。
SoundMixer.areSoundsInaccessible():Boolean
如果聲音因爲安全模型不能訪問則返回true.
SoundMixer.computeSpectrum(outputArray:ByteArray, FFTMode:Boolean = false, stretchFactor:int = 0):void
返回當前組合音頻快照。波形被分爲256級,分爲左聲道和右聲道。被記錄在outputArray中。outputArray是個二進制數組,擁有512個元素,類型爲Numer。使用readFloat方法訪問。前256個浮點數描述左聲道的波形,後256個浮點數描述右聲道的波形。浮點數也分爲兩種描述類型,FFTMode參數爲true時使用頻譜形式描述。範圍爲0~2的平方根。爲false時採用波形描述,範圍爲-1~1。strechFactor是採樣率。0爲44HZ,1爲22.HZ,以此類推。不管採樣率爲多少,始終都是256級別。採樣率只會影響波形是否平滑。
[1b]
[color="#4f81bd"]生成播放特效:[/color]
[/1b]
很多時候我們很羨慕media player的視覺特效吧。也很想知道是怎麼做出來的。答案就是兩個字——算法。現在利用as3提供的computeSpectrum可以實現我們的夢想,這是在as2中做不到的。下面我們來看看實現的原理。
不管使用什麼算法,數據都是相同的。我們可以寫一段小代碼,輸出某個音樂播放中的浮點狀態。
導入一首歌曲,庫中類名改爲Song。在幀上輸入如下代碼:
var s:Song=new Song();
s.play();
var ba:ByteArray=new ByteArray();
stage.addEventListener(MouseEvent.CLICK,onClick);
function onClick(event:MouseEvent):void
{
SoundMixer.computeSpectrum(ba);
ba.position=0;
for(var i:int=0;i
{
var num:Number=ba.readFloat();
trace(num);
}
}
看到數據的組織結構後,下一步就可以發揮你編程與藝術的完美結合,而media player中的特效就是完美最好的榜樣。在這麼多另人眼花繚亂的效果中,條形與波形是最直接最簡單的可視效果,它是數組的直接透視。現在我們來看看FFTMode爲true和false時的波形區別。
用於控制聲音的音量和平移。它跟多個類有聯繫,如:Microphone、NetStream、SoundChannel、SoundMixer都通過它調節聲音效果。再次顯示了聲音架構的標準統一。更可貴的的是在SimpleButton和Sprite中也有這個屬性。這代表着可以通過as調整flash 工具創作的按鈕和影片剪輯中嵌入的聲音。這種操控能力使得我們不必因爲嵌入的聲音對應調整音量上的干擾。但這並不代表我們能控制幀中聲音的播放和停止。SoundTransform的構造函數爲new SoundTransform(vol:Number=1,panning:Number=0)。構造函數的參數是它的屬性。
[1b]
[color="#4f81bd"]屬性:[/color]
[/1b]
volume:Number
音量,範圍爲0~1。
pan:Number
聲道平移,範圍爲-1~1。
leftToLeft:Number
左輸入在左聲道中的音量。
leftToRight:Number
左輸入在右聲道中的音量。
rightToLeft:Number
右輸入在左聲道中的音量。
rightToRight:Number
右輸入在右聲道中的音量。
SoundMixer類
[url=http://bbs.actionscript3.cn/attachment.php?aid=3954]
[img]http://bbs.actionscript3.cn/attachments/month_0805/20080509_9753e914b42fa1390793xN5k0oLvnlKp.jpg[/img]
[/url]
[1b]快速傅立葉變換:
[/1b]快速傅氏變換,是離散傅氏變換的快速算法,它是根據離散傅氏變換的奇、偶、虛、實等特性,對離散傅立葉變換的算法進行改進獲得的。它對傅氏變換的理論並沒有新的發現,但是對於在計算機系統或者說數字系統中應用離散傅立葉變換,可以說是進了一大步。
設x(n)爲N項的複數序列,由DFT變換,任一X(m)的計算都需要N次複數乘法和N-1次複數加法,而一次複數乘法等於四次實數乘法和兩次實數加法,一次複數加法等於兩次實數加法,即使把一次複數乘法和一次複數加法定義成一次“運算”(四次實數乘法和四次實數加法),那麼求出N項複數序列的X(m), 即N點DFT變換大約就需要N2次運算。當N=1024點甚至更多的時候,需要N2=1048576次運算,在FFT中,利用WN的週期性和對稱性,把一個N項序列(設N=2k,k爲正整數),分爲兩個N/2項的子序列,每個N/2點DFT變換需要(N/2)2次運算,再用N次運算把兩個N/2點的DFT 變換組合成一個N點的DFT變換。這樣變換以後,總的運算次數就變成N+2(N/2)2=N+N2/2。繼續上面的例子,N=1024時,總的運算次數就變成了525312次,節省了大約50%的運算量。而如果我們將這種“一分爲二”的思想不斷進行下去,直到分成兩兩一組的DFT運算單元,那麼N點的 DFT變換就只需要Nlog2N次的運算,N在1024點時,運算量僅有10240次,是先前的直接算法的1%,點數越多,運算量的節約就越大,這就是 FFT的優越性.
這就是快速傅立葉轉換的原理,但若頻繁的把波形轉化成頻譜,除了算法上的處理,還會會消耗一定的cpu資源,這就是as3直接提供了波形和頻譜兩種浮點模式的原因。
[1b]創建自定義播放效果:[/1b]
下面一個是我用矢量生成的一個特效。實際上還可以使用位圖,甚至結合flash工具創建的動畫來製作特效。可以說這方面的發揮空間是無窮無盡的。這裏我使用256條線段來描述聲道的波幅,使用25層來描述場密度。效果如下:
點擊下載此效果。
[url=http://bbs.actionscript3.cn/attachment.php?aid=3956]
[img]http://bbs.actionscript3.cn/attachments/month_0805/20080509_3cd5a8e2c71530db1b58SNJgWFIsuYr8.jpg[/img]
[color="#000000"]image007.jpg[/color]
[/url]
 
[img]http://space.actionscript3.cn/images/base/haveattach.gif[/img]
[url=http://bbs.actionscript3.cn/attachment.php?aid=3978][1b]
[color="#000000"]水紋效果.rar[/color]
[/1b][/url]
(2008-05-12 18:15:40, Size: 24.8 KB, Downloads: 6)
說明:由於嵌入聲音造成文檔太大,所以載入百度中的歌曲。歌曲地址放在界面文本框中又會影響美觀,所以我在路徑下放了一個songURL.txt的文本。大家可以更改網址。但同時訪問本地和網絡又會造成安全問題。於是找來我的flashBox。flashBox可不管什麼沙箱,本地網絡通吃。而且全屏播放又能加快執行速度。可以當作屏保一樣。真是很適合呢。
另外由於是矢量製作的因而不可能使得密度到達media player中水紋那樣的效果,我們可以使用BitmapData來達到。但以像素級來描述波形將會更慢。因爲flash player是不能象c++那樣調用系統API的。也不能直接使用顯卡支持的driectX或openGL。無法直接操作顯存。即便如此,我還是要說說使用BitmapData代替矢量呈現的方法。原理並不是很困難。使用矢量只不過是繪製線條。我們可以寫一個使用位圖創建線條的函數。原理如下:想象線段是一條路徑,一個爬蟲從一段爬到另一端,走過的路徑使用像素填充。這樣一來就非常簡單了。結合Tween來進行像素填充,爲位圖創建一個lineTo函數,代碼改動基本上不大。詳細代碼我就不寫了。請大家自己嘗試。
以上是我對as3架構的理解,不到之處還請見諒。謝謝!
本文轉自:http://www.5uflash.com/flashjiaocheng/Flash-as3-jiaocheng/3243.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章