Esempio audio: Lettore Podcast

Flash Player 9 e versioni successive, Adobe AIR 1.0 e versioni successive

Un podcast è un file audio che viene distribuito su Internet, su richiesta ("on demand”) o su abbonamento. I podcast vengono solitamente pubblicati come parti di una serie, definita anche canale podcast. Poiché possono avere una durata molto variabile (da un minuto a molte ore), gli episodi di un podcast vengono di solito riprodotti in streaming. Gli episodi di un podcast, definiti anche elementi, vengono generalmente distribuiti in formato mp3. Anche i podcast video sono molto popolari, ma questa applicazione di esempio riproduce solo podcast audio che utilizzano file mp3.

Questo esempio non rappresenta un aggregatore di podcast completo. Ad esempio, non gestisce gli abbonamenti a podcast specifici né memorizza quali podcast sono stati ascoltati dall'utente al successivo avvio dell'applicazione. Può tuttavia fungere da punto di partenza per un aggregatore di podcast dotato di maggiori funzionalità.

L'esempio Podcast Player illustra le seguenti tecniche di programmazione ActionScript:

  • lettura di un feed RSS esterno e analisi del suo contenuto XML;

  • creazione di una classe SoundFacade per semplificare il caricamento e la riproduzione dei file audio;

  • visualizzazione dell'avanzamento della riproduzione dell'audio;

  • sospensione e ripresa della riproduzione dell'audio.

Per ottenere i file dell'applicazione per questo esempio, visitate la pagina www.adobe.com/go/learn_programmingAS3samples_flash_it . I file dell'applicazione Podcast Player sono disponibili nella cartella Samples/PodcastPlayer. L'applicazione è composta dai seguenti file:

File

Descrizione

PodcastPlayer.mxml

o

PodcastPlayer.fla

Interfaccia utente dell'applicazione per Flex (MXML) o Flash (FLA).

comp/example/programmingas3/podcastplayer/PodcastPlayer.as

Classe di documento contenente la logica dell'interfaccia utente per il lettore di podcast (solo Flash).

SoundPlayer.mxml

Componente MXML che visualizza i pulsanti di riproduzione e le barre di avanzamento e che controlla la riproduzione audio (solo per Flex).

main.css

Stili per l'interfaccia utente dell'applicazione (solo Flex).

images/

Icone per lo stile dei pulsanti (solo Flex).

comp/example/programmingas3/podcastplayer/SoundPlayer.as

Classe per il simbolo del clip filmato SoundPlayer contenente la logica dell'interfaccia utente per il lettore audio (solo Flash).

comp/example/programmingas3/podcastplayer/PlayButtonRenderer.as

Renderer di celle personalizzato per la visualizzazione di un pulsante di riproduzione in una cella di una griglia di dati (solo Flash).

com/example/programmingas3/podcastplayer/RSSBase.as

Classe di base che fornisce le proprietà e i metodi comuni per le classi RSSChannel e RSSItem.

com/example/programmingas3/podcastplayer/RSSChannel.as

Classe ActionScript che contiene i dati relativi a un canale RSS.

com/example/programmingas3/podcastplayer/RSSItem.as

Classe ActionScript che contiene i dati relativi a un elemento RSS.

com/example/programmingas3/podcastplayer/SoundFacade.as

Classe ActionScript principale dell'applicazione, che incorpora i metodi e gli eventi della classe Sound e della classe SoundChannel e che aggiunge il supporto per la sospensione e la ripresa della riproduzione.

com/example/programmingas3/podcastplayer/URLService.as

Classe ActionScript che recupera i dati da un URL remoto.

playerconfig.xml

File XML che contiene un elenco di feed RSS che rappresentano i canali podcast.

comp/example/programmingas3/utils/DateUtil.as

Classe utilizzata per semplificare la formattazione delle date (solo Flash).

Lettura dei dati RSS per un canale podcast

L'applicazione Podcast Player comincia leggendo le informazioni relative a un numero di canali podcast e ai relativi episodi:

1. Innanzi tutto l'applicazione legge un file di configurazione XML che contiene un elenco di canali podcast e visualizza l'elenco dei canali per l'utente.

2. Quando l'utente seleziona uno dei canali podcast, l'applicazione legge il feed RSS per il canale e visualizza un elenco dei relativi episodi.

Questo esempio utilizza la classe di utilità URLLoader per recuperare i dati basati su testo da una posizione remota o da un file locale. L'applicazione Podcast Player crea come prima cosa un oggetto URLLoader per ottenere un elenco di feed RSS in formato XML dal file playerconfig.xml. Quindi, quando l'utente seleziona un specifico feed dall'elenco, viene creato un nuovo oggetto URLLoader per leggere i dati RSS dall'URL del feed.

Semplificazione del caricamento e della riproduzione dell'audio mediante la classe SoundFacade

L'architettura audio di ActionScript 3.0 è potente ma complessa. Le applicazioni che richiedono solo funzioni di base di caricamento e riproduzione audio possono utilizzare una classe che nasconde alcune caratteristiche particolarmente complesse fornendo un set semplificato di chiamate a metodi ed eventi. Nel mondo della progettazione software, una classe di questo tipo viene definita facade (o “di facciata”).

La classe SoundFacade presenta un'unica interfaccia per eseguire le operazioni seguenti:

  • caricamento dei file audio mediante un oggetto Sound, un oggetto SoundLoaderContext e la classe SoundMixer;

  • riproduzione dei file audio mediante l'oggetto Sound e l'oggetto SoundChannel;

  • invio di eventi progress per la riproduzione;

  • sospensione e ripresa della riproduzione dell'audio mediante l'oggetto Sound e l'oggetto SoundChannel.

La classe SoundFacade ha lo scopo di offrire la maggior parte delle funzionalità delle classi audio di ActionScript ma con un livello inferiore di complessità.

Il codice seguente illustra la dichiarazione della classe, le proprietà della classe e il metodo di costruzione 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(); 
        } 
    }

La classe SoundFacade estende la classe EventDispatcher in modo da poter inviare degli eventi propri. Innanzi tutto, il codice della classe dichiara le proprietà di un oggetto Sound e di un oggetto SoundChannel. La classe memorizza anche il valore dell'URL del file audio e una proprietà bufferTime da utilizzare quando si utilizza audio in streaming. Inoltre, accetta alcuni valori di parametro booleani che influiscono sul comportamento del caricamento e della riproduzione:

  • Il parametro autoLoad indica all'oggetto che il caricamento dell'audio deve iniziare non appena l'oggetto viene creato.

  • Il parametro autoPlay indica che la riproduzione dell'audio deve iniziare non appena è stata caricata una quantità sufficiente di dati audio. Se si tratta di audio in streaming, la riproduzione ha inizio non appena è stata caricata la quantità di dati audio specificata dalla proprietà bufferTime .

  • Il parametro streaming indica che la riproduzione di questo file audio può iniziare prima che ne sia stato completato il caricamento.

Il valore predefinito del parametro bufferTime è -1. Se il metodo di costruzione rileva un valore negativo nel parametro bufferTime , imposta la proprietà bufferTime sul valore di SoundMixer.bufferTime . In questo modo, l'applicazione può essere impostata in modo predefinito sul valore SoundMixer.bufferTime globale come desiderato.

Se il parametro autoLoad è impostato su true , il metodo di costruzione chiama immediatamente il metodo load() seguente per avviare il caricamento del file audio:

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

Il metodo load() crea un nuovo oggetto Sound e successivamente aggiunge i listener per tutti gli eventi audio importanti. Quindi, specifica all'oggetto Sound di caricare il file audio, utilizzando un oggetto SoundLoaderContext per passare il valore bufferTime .

Dal momento che la proprietà url può essere modificata, è possibile utilizzare un’istanza SoundFacade per riprodurre diversi file audio in successione: per caricare il nuovo file audio, è sufficiente modificare la proprietà url e chiamare il metodo load() .

I tre eventi del listener di eventi seguenti mostrano il modo in cui l'oggetto SoundFacade tiene traccia dell'avanzamento del caricamento e decide quando iniziare la riproduzione del suono:

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

Il metodo onLoadOpen() viene eseguito quando inizia il caricamento dell'audio. Se l'audio può essere riprodotto in streaming, il metodo onLoadComplete() imposta immediatamente il flag isReadyToPlay su true . Il flag isReadyToPlay determina se l'applicazione può avviare la riproduzione dell'audio, ad esempio in risposta a un'azione dell'utente (come la selezione di un pulsante Riproduci). La classe SoundChannel gestisce la bufferizzazione dei dati audio, pertanto non è necessario verificare se sono stati caricati dati a sufficienza prima di chiamare il metodo play() .

Il metodo onLoadProgress() viene eseguito periodicamente durante il processo di caricamento. Si limita a inviare un clone del proprio oggetto ProgressEvent per essere utilizzato dal codice che utilizza l'oggetto SoundFacade.

Quando i dati audio sono stati completamente caricati, viene eseguito il metodo onLoadComplete() e, se necessario, viene chiamato il metodo play() per l'audio non in streaming. Di seguito viene mostrato il metodo 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(); 
        } 
    } 
}

Il metodo play() chiama il metodo Sound.play() se l'audio è pronto per essere riprodotto. L'oggetto SoundChannel risultante viene memorizzato nella proprietà sc . Il metodo play() crea quindi un oggetto Timer da utilizzare per inviare eventi progress per la riproduzione a intervalli regolari.

Visualizzazione dell'avanzamento della riproduzione

La creazione di un oggetto Timer per gestire il monitoraggio della riproduzione è un'operazione complessa che è consigliabile codificare una sola volta. L'incorporamento di questa logica Timer in una classe riutilizzabile come SoundFacade consente alle applicazioni di intercettare gli stessi eventi di avanzamento sia per il caricamento che per la riproduzione dell'audio.

L'oggetto Timer creato dal metodo SoundFacade.play() invia un'istanza TimerEvent ogni secondo. Il metodo onPlayTimer() mostrato di seguito viene eseguito ogni volta che viene ricevuta una nuova istanza 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); 
}

Il metodo onPlayTimer() implementa la tecnica di stima delle dimensioni descritta nella sezione Monitoraggio della riproduzione . Quindi, crea una nuova istanza ProgressEvent con il tipo di evento SoundFacade.PLAY_PROGRESS , con la proprietà bytesLoaded impostata sulla posizione corrente dell'oggetto SoundChannel e la proprietà bytesTotal impostata sulla lunghezza stimata dei dati audio.

Sospensione e ripresa della riproduzione

Il metodo SoundFacade.play() mostrato in precedenza accetta un parametro pos che corrisponde a una posizione iniziale all'interno dei dati audio. Se il valore di pos è 0, la riproduzione dell'audio comincia dall'inizio.

Anche il metodo SoundFacade.stop() accetta un parametro pos , come mostrato di seguito:

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

Ogni qualvolta viene chiamato, il metodo SoundFacade.stop() imposta la proprietà pausePosition per consentire all'applicazione di sapere dove posizionare l'indicatore di riproduzione se l'utente desidera riprendere la riproduzione dello stesso file audio.

I metodi SoundFacade.pause() e SoundFacade.resume() mostrati di seguito chiamano rispettivamente i metodi SoundFacade.stop() e SoundFacade.play() , passando ogni volta un parametro pos .

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

Il metodo pause() passa il valore SoundChannel.position corrente al metodo play() , che lo memorizza nella proprietà pausePosition . Il metodo resume() inizia a riprodurre nuovamente lo stesso file audio utilizzando il valore pausePosition come punto iniziale.

Estensione dell'esempio Podcast Player

Questo esempio presenta un Podcast Player molto semplice che illustra l'utilizzo della classe riutilizzabile SoundFacade. Tuttavia, potete aggiungere altre funzionalità a questa applicazione per aumentarne l'utilità, ad esempio:

  • memorizzare l'elenco dei feed e delle informazioni sull'utilizzo per ciascun episodio in un'istanza SharedObject che è possibile utilizzare al successivo avvio dell'applicazione;

  • consentire all'utente di aggiungere i propri feed RSS all'elenco dei canali podcast;

  • memorizzare la posizione dell'indicatore di riproduzione quando l'utente interrompe o abbandona un episodio, in modo da poterlo riavviare dallo stesso punto al successivo utilizzo dell'applicazione;

  • scaricare file mp3 di episodi per l'ascolto non in linea, cioè quando l'utente non è collegato a Internet;

  • aggiungere funzioni di abbonamento che verificano periodicamente la disponibilità di nuovi episodi in un canale podcast e aggiornano automaticamente l'elenco degli episodi;

  • aggiungere funzioni di ricerca e consultazione dei podcast mediante un'API da un servizio di hosting di podcast come Odeo.com.