聲音範例:Podcast PlayerFlash Player 9 以及更新的版本,Adobe AIR 1.0 以及更新的版本 Podcast 是在網際網路上以點播或訂閱方式散佈的聲音檔案。Podcast 通常會發佈做為一系列中的某一部分,而該系列又稱為 Podcast 頻道。因為 Podcast 的廣播節目可以在任何地方持續一分鐘到許多小時,所以在播放時通常會以串流的方式進行。Podcast 廣播節目 (也稱為項目) 通常是以 MP3 檔案格式傳送。雖然視訊 Podcast 也很流行,但是此樣本應用程式只會播放使用 MP3 檔案的音效 Podcast。 此範例不是具有完整功能的 Podcast Aggregator 應用程式。例如,它並不會管理特定 Podcast 的訂閱,也不會在下一次應用程式執行時,記住使用者已聽過哪些 Podcast。它可以做為功能更加完整之 Podcast Aggregator 的起點。 Podcast Player 範例將說明下列 ActionScript 程式設計技術:
若要取得此樣本的應用程式檔案,請參閱 www.adobe.com/go/learn_programmingAS3samples_flash_tw。您可以在 Samples/PodcastPlayer 資料夾中找到 Podcast Player 應用程式檔案,此應用程式是由下列檔案組成:
讀取 Podcast 頻道的 RSS 資料Podcast Player 應用程式會從讀取一些 Podcast 頻道及其廣播節目的資訊開始。 1. 首先,應用程式會讀取 XML 設定檔 (此檔案包含 Podcast 頻道的清單),並向使用者顯示頻道清單。 2. 當使用者選取其中一個 Podcast 頻道時,它會讀取該頻道的 RSS Feed,並顯示頻道節目的清單。 此範例會使用 URLLoader 公用程式類別,從遠端位置或本機檔案擷取文字資料。Podcast Player 首先會建立 URLLoader 物件,以從 playerconfig.xml 檔案取得 XML 格式的 RSS Feed 清單。接著,當使用者從清單選取特定的內容時,就會建立新的 URLLoader 物件,以便從該 Feed 的 URL 讀取 RSS 資料。 使用 SoundFacade 類別簡化聲音載入和播放作業ActionScript 3.0 的聲音架構非常強大,但卻也複雜。只需要基本聲音載入和播放功能的應用程式可以使用一個類別,藉由提供一組較簡單的方法呼叫與事件,隱藏某些複雜度。在軟體設計模式的世界中,這種類別就稱為「外觀」。 SoundFacade 類別會提供單一介面,以執行下列工作:
SoundFacade 類別會嘗試以較低的複雜度,提供 ActionScript 聲音類別的大部分功能。 下列程式碼會顯示類別宣告、類別屬性以及 SoundFacade() 建構函式方法: public class SoundFacade extends EventDispatcher { public var s:Sound; public var sc:SoundChannel; public var url:String; public var bufferTime:int = 1000; public var isLoaded:Boolean = false; public var isReadyToPlay:Boolean = false; public var isPlaying:Boolean = false; public var isStreaming:Boolean = true; public var autoLoad:Boolean = true; public var autoPlay:Boolean = true; public var pausePosition:int = 0; public static const PLAY_PROGRESS:String = "playProgress"; public var progressInterval:int = 1000; public var playTimer:Timer; public function SoundFacade(soundUrl:String, autoLoad:Boolean = true, autoPlay:Boolean = true, streaming:Boolean = true, bufferTime:int = -1):void { this.url = soundUrl; // Sets Boolean values that determine the behavior of this object this.autoLoad = autoLoad; this.autoPlay = autoPlay; this.isStreaming = streaming; // Defaults to the global bufferTime value if (bufferTime < 0) { bufferTime = SoundMixer.bufferTime; } // Keeps buffer time reasonable, between 0 and 30 seconds this.bufferTime = Math.min(Math.max(0, bufferTime), 30000); if (autoLoad) { load(); } } SoundFacade 類別會擴充 EventDispatcher 類別,讓它可以傳送屬於自己的事件。該類別程式碼首先會宣告 Sound 物件與 SoundChannel 物件的屬性。此類別也會儲存聲音檔案的 URL 值,以及串流聲音時要使用的 bufferTime 屬性值。除此之外,它還會接受一些會影響載入和播放行為的 Boolean 參數值:
bufferTime 參數預設值為 -1。如果建構函式方法在 bufferTime 參數中偵測到負值,它會將 bufferTime 屬性設定為 SoundMixer.bufferTime 的值。這可以讓應用程式將預設值設定為所需的全域 SoundMixer.bufferTime 值。 如果 autoLoad 參數已設定為 true,則建構函式方法會立即呼叫下列 load() 方法,開始載入聲音檔案: public function load():void { if (this.isPlaying) { this.stop(); this.s.close(); } this.isLoaded = false; this.s = new Sound(); this.s.addEventListener(ProgressEvent.PROGRESS, onLoadProgress); this.s.addEventListener(Event.OPEN, onLoadOpen); this.s.addEventListener(Event.COMPLETE, onLoadComplete); this.s.addEventListener(Event.ID3, onID3); this.s.addEventListener(IOErrorEvent.IO_ERROR, onIOError); this.s.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onIOError); var req:URLRequest = new URLRequest(this.url); var context:SoundLoaderContext = new SoundLoaderContext(this.bufferTime, true); this.s.load(req, context); } load() 方法會建立新的 Sound 物件,然後為所有重要的聲音事件增加偵聽程式。接著,它會告訴 Sound 物件使用 SoundLoaderContext 物件傳入 bufferTime 值,以載入聲音檔案。 由於您可以變更 url 屬性,因此可以使用 SoundFacade 實體來連續播放不同的聲音檔案:只需變更 url 屬性,再呼叫 load() 方法,就會載入新的聲音檔案。 下列三個事件偵聽程式方法將說明 SoundFacade 物件如何追蹤載入的進度,並決定何時開始播放聲音: public function onLoadOpen(event:Event):void { if (this.isStreaming) { this.isReadyToPlay = true; if (autoPlay) { this.play(); } } this.dispatchEvent(event.clone()); } public function onLoadProgress(event:ProgressEvent):void { this.dispatchEvent(event.clone()); } public function onLoadComplete(event:Event):void { this.isReadyToPlay = true; this.isLoaded = true; this.dispatchEvent(evt.clone()); if (autoPlay && !isPlaying) { play(); } } onLoadOpen() 方法會在聲音載入作業開始時執行。如果可以在串流模式中播放聲音,則 onLoadComplete() 方法會立即將 isReadyToPlay 旗標設定為 true。isReadyToPlay 旗標會決定應用程式是否可以開始聲音播放,也許回應如按一下「播放」按鈕等使用者動作。由於 SoundChannel 類別會管理聲音資料的緩衝,因此在呼叫 play() 方法之前,不需要明確檢查是否已載入足夠的資料。 onLoadProgress() 方法會在載入程序期間定期執行。它會直接傳送其 ProgressEvent 物件之副本,供使用此 SoundFacade 物件的程式碼使用。 已完全載入聲音資料後,就會執行 onLoadComplete() 方法,並視需要為非串流聲音呼叫 play() 方法。play() 方法本身如下所示。 public function play(pos:int = 0):void { if (!this.isPlaying) { if (this.isReadyToPlay) { this.sc = this.s.play(pos); this.sc.addEventListener(Event.SOUND_COMPLETE, onPlayComplete); this.isPlaying = true; this.playTimer = new Timer(this.progressInterval); this.playTimer.addEventListener(TimerEvent.TIMER, onPlayTimer); this.playTimer.start(); } } } play() 方法會在聲音可以播放時呼叫 Sound.play() 方法。產生的 SoundChannel 物件是儲存在 sc 屬性中。play() 方法接著會建立 Timer 物件,該物件將用以定期傳送播放進度事件。 顯示播放進度建立 Timer 物件以驅動播放監視功能是一項複雜的作業,因此您最好只撰寫一次程式碼就好。將此 Timer 邏輯封裝在如 SoundFacade 等可重複使用的類別中,可讓應用程式在聲音載入和播放時,偵聽相同種類的進度事件。 SoundFacade.play() 方法所建立的 Timer 物件每秒都會傳送 TimerEvent 實體。每當有新的 TimerEvent 到達時,下列 onPlayTimer() 方法就會執行: public function onPlayTimer(event:TimerEvent):void { var estimatedLength:int = Math.ceil(this.s.length / (this.s.bytesLoaded / this.s.bytesTotal)); var progEvent:ProgressEvent = new ProgressEvent(PLAY_PROGRESS, false, false, this.sc.position, estimatedLength); this.dispatchEvent(progEvent); } onPlayTimer() 方法會實作在監視播放作業一節中描述的大小估算技術。接著,它會建立具有 SoundFacade.PLAY_PROGRESS 事件類型的新 ProgressEvent 實體,其中 bytesLoaded 屬性會設定為 SoundChannel 物件的目前位置,而且 bytesTotal 屬性會設定為聲音資料的估算長度。 暫停和繼續播放之前顯示的 SoundFacade.play() 方法可接受對應至聲音資料中開始位置的 pos 參數。如果 pos 值為零,就會從一開始播放聲音。 SoundFacade.stop() 方法也會接受 pos 參數,如下所示: public function stop(pos:int = 0):void { if (this.isPlaying) { this.pausePosition = pos; this.sc.stop(); this.playTimer.stop(); this.isPlaying = false; } } 每當呼叫 SoundFacade.stop() 方法時,它就會設定 pausePosition 屬性,這樣一來,如果使用者想要繼續播放相同聲音時,應用程式就會知道要放置播放磁頭的位置。 下面所顯示的 SoundFacade.pause() 與 SoundFacade.resume() 方法會分別叫用 SoundFacade.stop() 與 SoundFacade.play() 方法,每一次都會傳遞 pos 參數值。 public function pause():void { stop(this.sc.position); } public function resume():void { play(this.pausePosition); } pause() 方法會將目前的 SoundChannel.position 值傳遞給 play() 方法,它會將該值儲存在 pausePosition 屬性中。resume() 方法會使用 pausePosition 值做為起點,再次開始播放相同的聲音。 擴充 Podcast Player 範例此範例顯示極為精簡的 Podcast Player,以展示可重複使用的 SoundFacade 類別的用法。您可以增加其它功能以增強此應用程式的有用性,其中包括下列項目:
|
|