Przykład pracy z dźwiękiem: odtwarzacz podkastów

Flash Player 9 i nowsze wersje, Adobe AIR 1.0 i nowsze wersje

Podkast jest to plik dźwiękowy rozpowszechniany za pośrednictwem Internetu, na żądanie lub na zasadzie subskrypcji. Podkasty są zwykle publikowane jako część serii, zwanej kanałem podkastów. Ponieważ wątki podkastów mogą mieć długość od minuty do nawet wielu godzin, zwykle są one przesyłane strumieniowo podczas odtwarzania. Wątki podkastów, zwane również pozycjami, są zwykle dostarczane w formacie mp3. Podkasty wideo również zyskały sporą popularność, lecz omawiana aplikacja odtwarza jedynie podkasty korzystające z plików mp3.

Niniejszy przykład nie jest w pełni funkcjonalną aplikacją do agregacji podkastów. Na przykład, nie zapewnia ona zarządzania subskrypcjami na specyficzne podkasty ani nie zapamiętuje, które podkasty zostały już przez użytkownika odsłuchane, jeśli w międzyczasie aplikacja została zamknięta. Aplikacja ta może być jednak punktem wyjściowym do opracowania w pełni funkcjonalnego agregatora podkastów.

Przykładowa aplikacja Podcast Player ilustruje następujące techniki programowania w języku ActionScript:

  • Odczytywanie zewnętrznych kanałów RSS i analizowanie składni ich zawartości XML

  • Tworzenie klasy SoundFacade w celu uproszczenia ładowania i odtwarzania plików dźwiękowych

  • Wyświetlanie postępu odtwarzania dźwięków

  • Wstrzymywanie i wznawianie odtwarzania dźwięku

Aby pobrać pliki tej przykładowej aplikacji, należy przejść na stronę www.adobe.com/go/learn_programmingAS3samples_flash_pl . Pliki aplikacji Podcast Player znajdują się w folderze Samples/PodcastPlayer. Aplikacja składa się z następujących plików:

File

Opis

PodcastPlayer.mxml

lub

PodcastPlayer.fla

Interfejs użytkownika dla aplikacji Flex (MXML) lub Flash (FLA).

comp/example/programmingas3/podcastplayer/PodcastPlayer.as

Klasa dokumentu zawierająca logikę interfejsu użytkownika dla odtwarzacza podkastów (tylko dla aplikacji Flash).

SoundPlayer.mxml

Składnik MXML wyświetlający przyciski odtwarzania oraz pasku postępu i elementy sterujące odtwarzaniem dźwięku, tylko dla aplikacji Flex.

main.css

Style dla interfejsu użytkownika aplikacji (tylko dla aplikacji Flex).

images/

Ikony do stylizacji przycisków (tylko dla aplikacji Flex).

comp/example/programmingas3/podcastplayer/SoundPlayer.as

Klasa dla symbolu klipu filmowego SoundPlayer zawierająca logikę interfejsu użytkownika dla odtwarzacza dźwięku (tylko dla aplikacji Flash).

comp/example/programmingas3/podcastplayer/PlayButtonRenderer.as

Niestandardowy podprogram renderujący komórki służący do wyświetlania przycisku odtwarzania w komórce obiektu DataGrid (tylko dla aplikacji Flash).

com/example/programmingas3/podcastplayer/RSSBase.as

Klasa bazowa udostępniająca typowe właściwości i metody dla klasy RSSChannel oraz klasy RSSItem.

com/example/programmingas3/podcastplayer/RSSChannel.as

Klasa ActionScript przechowująca dane dotyczące kanału RSS.

com/example/programmingas3/podcastplayer/RSSItem.as

Klasa ActionScript przechowująca dane dotyczące pozycji RSS.

com/example/programmingas3/podcastplayer/SoundFacade.as

Główna klasa ActionScript dla aplikacji. Obejmuje ona metody i zdarzenia klasy Sound class oraz klasy SoundChannel, a dodatkowo uzupełnia aplikację o obsługę wstrzymywania i wznawiania odtwarzania.

com/example/programmingas3/podcastplayer/URLService.as

Klasa ActionScript pobierająca dane ze zdalnego adresu URL.

playerconfig.xml

Plik XML zawierający listę kanałów RSS reprezentujących kanały podkastów.

comp/example/programmingas3/utils/DateUtil.as

Klasa ułatwiająca formatowanie danych (tylko dla aplikacji Flash).

Odczytywanie danych RSS dla kanału podkastu

Aplikacja Podcast rozpoczyna się od odczytu informacji dotyczących liczby kanałów podkastów oraz ich epizodów:

1. Po pierwsze aplikacja odczytuje plik konfiguracji XML zawierający listę kanałów podkastów i wyświetla listę kanałów użytkowników.

2. Po wybraniu przez użytkownika jednego z kanałów podkastów czyta ona kanał RSS dla kanału podkastu i wyświetla listę dostępnych dla niego epizodów.

W tym przykładzie użyto klasy narzędziowej URLLoader w celu pobrania danych tekstowych z lokalizacji zdalnej lub pliku lokalnego. Aplikacja Podcast Player tworzy najpierw obiekt URLLoader w celu pobrania listy kanałów RSS w formacie XML z pliku playerconfig.xml. Następnie, po wybraniu przez użytkownika określonego kanału RSS z listy, tworzony jest nowy obiekt URLLoader w celu odczytania danych RSS z adresu URL tego kanału RSS.

Uproszczenie ładowania i odtwarzania przez użycie klasy SoundFacade

Architektura dźwięku w programie ActionScript 3.0 ma oferuje ogromne możliwości, lecz jest dość złożona. Aplikacje wymagające wyłącznie podstawowych funkcji, takich jak ładowanie i odtwarzanie dźwięku mogą korzystać z klasy ukrywającej niektóre złożone aspekty aplikacji przez udostępnienie uproszczonych zestawów wywołań metod i zdarzeń. W świecie konstruowania wzorców aplikacji klasa taka zwie się fasadą .

Klasa SoundFacade prezentuje pojedynczy interfejs umożliwiający realizację następujących zadań:

  • Ładowanie plików dźwiękowych za pomocą obiektu Sound, obiektu SoundLoaderContext oraz klasy SoundMixer

  • Odtwarzanie plików dźwiękowych za pomocą obiektu Sound oraz obiektu SoundChannel

  • Dysponowanie zdarzeń postępu odtwarzania

  • Wstrzymywanie i wznawianie odtwarzania dźwięków za pomocą obiektu Sound oraz obiektu SoundChannel

Klasa SoundFacade pomaga zaoferować większość funkcjonalności klas związanych z obsługą dźwięku w języku ActionScript w prosty sposób.

Poniższy kod przedstawia deklarację klasy, właściwości klasy oraz metodę konstruktora 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(); 
        } 
    }

Klasa SoundFacade rozszerza klasę EventDispatcher tak, że może ona dysponować własne zdarzenia. Kod klasy deklaruje najpierw właściwości dla obiektu Sound oraz obiektu SoundChannel. Klasa zapisuje również wartość adresu URL pliku dźwiękowego oraz właściwość bufferTime używaną podczas przesyłania strumieniowego dźwięku. Ponadto przyjmuje ona niektóre wartości parametrów typu Boolean wpływające na zachowania podczas lądowania i odtwarzania:

  • Parametr autoLoad przekazuje do obiektu informację, że ładowanie dźwięku powinno rozpocząć się możliwie najszybciej po utworzeniu obiektu.

  • Parametr autoPlay wskazuje również, że odtwarzanie dźwięku powinno rozpocząć się możliwie szybko po załadowaniu wystarczającej ilości danych dźwiękowych. Jeśli jest to dźwięk przesyłany strumieniowo, odtwarzanie go rozpocznie się niezwłocznie po załadowaniu wystarczającej ilości danych, zgodnie z definicją we właściwości bufferTime .

  • Parametr streaming wskazuje ponadto, że ten plik dźwiękowy może zacząć być odtwarzany jeszcze przed ukończeniem ładowania.

Parametr bufferTime przyjmuje wartość domyślną -1. Jeśli metoda konstruktora wykryje wartość ujemną w parametrze bufferTime , ustawi ona właściwość bufferTime na wartość odpowiadającą SoundMixer.bufferTime . Umożliwia to aplikacji przejście na domyślną wartość globalną SoundMixer.bufferTime , gdy tylko będzie taka potrzeba.

W przypadku ustawienia parametru autoLoad na wartość true metoda konstruktora niezwłocznie wywoła następującą metodę load() w celu rozpoczęcia ładowania pliku dźwiękowego:

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); 
}

Metoda load() tworzy nowy obiekt Sound, a następnie dodaje moduły wykrywania zdarzeń dla wszystkich ważniejszych zdarzeń związanych z dźwiękiem. Następnie przekazuje ona do obiektu Sound polecenie załadowania pliku dźwiękowego za pomocą obiektu SoundLoaderContext, umożliwiającego przekazanie wartości bufferTime .

Ponieważ istnieje możliwość zmiany właściwości url , instancja SoundFacade może być używana do odtwarzania kolejno różnych plików dźwiękowych: należy po prostu zmienić właściwość url i wywołać metodę load() , a zostanie załadowany nowy plik dźwiękowy.

Poniższe trzy metody modułu wykrywania zdarzeń ilustrują sposób śledzenia przez obiekt SoundFacade postępu ładowania oraz decydowanie, kiedy ma rozpocząć się odtwarzanie dźwięku:

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(); 
    } 
}

Metoda onLoadOpen() jest wykonywana po rozpoczęciu ładowania dźwięku. Jeśli dźwięk może być odtwarzany w trybie przesyłania strumieniowego, metoda onLoadComplete() niezwłocznie ustawia flagę isReadyToPlay na wartość true . Flaga isReadyToPlay określa, czy aplikacja może rozpocząć odtwarzanie dźwięku, być może w odpowiedzi na czynność wykonaną przez użytkownika, taką jak kliknięcie przycisku Odtwórz. Klasa SoundChannel zarządza buforowaniem danych dźwiękowych, dlatego nie ma potrzeby jawnego sprawdzani, czy załadowana została wystarczająca ilość danych, przed wywołaniem metody play() .

Metoda onLoadProgress() jest wykonywana okresowo w trakcie całego procesu ładowania. Po prostu dysponuje ona klon jej obiektu ProgressEvent do użycia przez kod korzystający z obiektu SoundFacade.

Po całkowitym załadowaniu danych dźwiękowych wykonywana jest metoda onLoadComplete() , wywołująca w razie potrzeby, dla dźwięków niepodlegających przesyłaniu strumieniowemu, metodę play() . Samą metodę play( ) przedstawiono poniżej.

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(); 
        } 
    } 
}

Metoda play() wywołuje metodę Sound.play() , gdy dźwięk jest gotowy do odtworzenia. Wynikowy obiekt SoundChannel jest zapisywany we właściwości sc . Następnie metoda play() tworzy obiekt Timer, który będzie używany do dysponowania zdarzeń postępu odtwarzania w regularnych odstępach czasu.

Wyświetlanie postępu odtwarzania

Tworzenie obiektu Timer w celu prowadzenia monitorowania odtwarzania jest operacją złożoną, której kod należy starać się opracować raz. Zastosowanie tej logiki Timer w klasie umożliwiającej ponowne wykorzystanie, takiej jak klasa SoundFacade, umożliwia aplikacji wykrywanie tego samego rodzaju zdarzeń postępu zarówno, gdy dźwięk jest ładowany, jak i wówczas, gdy jest on odtwarzany.

Obiekt Timer tworzony za pośrednictwem metody SoundFacade.play() dysponuje instancję TimerEvent co sekundę. Poniższa metoda onPlayTimer() jest wykonywana za każdym razem po pojawieniu się nowego zdarzenia TimerEvent:

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); 
}

Metoda onPlayTimer() implementuje technikę szacowania rozmiaru opisaną w sekcji Monitorowanie odtwarzania . Następnie tworzy ona nową instancję ProgressEvent o typie zdarzenia SoundFacade.PLAY_PROGRESS , z właściwością bytesLoaded ustawioną na bieżące położenie obiektu SoundChannel oraz właściwością bytesTotal ustawioną na oszacowaną długość danych dźwiękowych.

Wstrzymywanie i wznawianie odtwarzania

Prezentowania wcześniej metoda SoundFacade.play() przyjmuje parametr pos odpowiadający pozycji początkowej w danych dźwiękowych. Jeśli wartość pos wynosi zero, odtwarzanie dźwięku rozpoczyna się od początku.

Metoda SoundFacade.stop() przyjmuje również parametr pos zgodnie z poniższym przykładem:

public function stop(pos:int = 0):void 
{ 
    if (this.isPlaying) 
    { 
        this.pausePosition = pos; 
        this.sc.stop(); 
        this.playTimer.stop(); 
        this.isPlaying = false; 
    }     
}

Za każdym razem po wywołaniu metody SoundFacade.stop() ustawia ona właściwość pausePosition tak, aby aplikacja wiedziała, w którym miejscu ustawić głowicę odtwarzania, jeśli użytkownik chciałby wznowić odtwarzanie tego samego dźwięku.

Metody SoundFacade.pause() i SoundFacade.resume() prezentowane poniżej wywołują odpowiednio metody SoundFacade.stop() i SoundFacade.play() , za każdym razem przekazując wartość parametru pos .

public function pause():void 
{ 
    stop(this.sc.position); 
} 
 
public function resume():void 
{ 
    play(this.pausePosition); 
}

Metoda pause() przekazuje bieżącą wartość SoundChannel.position do metody play() , która przechowuje tę wartość we właściwości pausePosition . Metoda resume() rozpoczyna ponownie odtwarzanie tego samego dźwięku, korzystając z wartości pausePosition jako punktu początkowego.

Rozszerzanie przykładu Aplikacja Podcast Player

Niniejszy przykład ilustruje samą aplikację Podcast Player, stanowiącą ilustrację wielokrotnego wykorzystania klasy SoundFacade. W celu rozszerzenia funkcjonalności aplikacji można dodać do niej inne funkcje, takie jak:

  • Zapisywanie listy kanałów RSS oraz informacji na temat użytkowania każdego z epizodów w instancji SharedObject, która może być używana następnym razem, gdy użytkownik uruchomi aplikację.

  • Umożliwienie użytkownikom dodania jej lub jego własnych kanałów RSS do listy kanałów podkastów.

  • Zapamiętywanie położenia głowicy odtwarzania po zatrzymaniu przez użytkownika lub opuszczeniu epizodu, tak że możliwe jest ponowne uruchomienie odtwarzania z tego punktu po uruchomieniu aplikacji następnym razem.

  • Pobranie plików mp3 epizodów w celu ich odsłuchiwania offline, bez połączenia z Internetem.

  • Dodawanie funkcji subskrypcji okresowo sprawdzających kanał podkastów w poszukiwaniu nowych wątków oraz automatycznie aktualizujących listę wątków.

  • Dodawanie wyszukiwania i przeglądania podkastów za pośrednictwem interfejsu API z usługi hostingu podkastów takiej jak Odeo.com.