Ejemplo de sonido: Podcast Player

Flash Player 9 y posterior, Adobe AIR 1.0 y posterior

Una emisión podcast es un archivo de sonido que se distribuye por Internet, bajo demanda o por suscripción. Las emisiones podcast suelen publicarse como parte de una serie (denominada "canal podcast"). Puesto que los episodios de emisiones podcast pueden durar entre un minuto y muchas horas, normalmente se transmiten mientras se reproducen. Los episodios de emisiones podcast, también denominados "elementos", se suelen transmitir en formato MP3. Las emisiones podcast de vídeo gozan de una gran popularidad, pero esta aplicación de ejemplo solo reproduce emisiones podcast de audio que utilizan archivos MP3.

Este ejemplo no constituye un agregador de emisiones podcast completo. Por ejemplo, no gestiona suscripciones a emisiones podcast específicas ni recuerda las emisiones podcast que ha escuchado el usuario la próxima vez que se ejecuta la aplicación. Podría servir de punto de partida para un agregador de emisiones podcast más completo.

El ejemplo de Podcast Player ilustra las siguientes técnicas de programación con ActionScript:

  • Leer un fuente RSS externa y analizar su contenido XML

  • Crear una clase SoundFacade para simplificar la carga y la reproducción de archivos de sonido

  • Mostrar el progreso de la reproducción de sonido

  • Pausar y reanudar la reproducción de sonido

Para obtener los archivos de la aplicación de este ejemplo, consulte www.adobe.com/go/learn_programmingAS3samples_flash_es . Los archivos de la aplicación Podcast Player se encuentran en la carpeta Samples/PodcastPlayer. La aplicación consta de los siguientes archivos:

Archivo

Descripción

PodcastPlayer.mxml

o

PodcastPlayer.fla

La interfaz de usuario de la aplicación para Flex (MXML) o Flash (FLA).

comp/example/programmingas3/podcastplayer/PodcastPlayer.as

Clase de documento que contiene la lógica de la interfaz de usuario para el reproductor de podcast (solo Flash).

SoundPlayer.mxml

Componente MXML que muestra botones de reproducción y barras de progreso, y controla la reproducción del sonido, solo para Flex.

main.css

Estilos de la interfaz de usuario de la aplicación (solo Flex).

images/

Iconos de aplicación de estilos a los botones (solo Flex).

comp/example/programmingas3/podcastplayer/SoundPlayer.as

Clase del símbolo de clip de película SoundPlayer que contiene la lógica de la interfaz de usuario para el reproductor de sonidos (solo Flash).

comp/example/programmingas3/podcastplayer/PlayButtonRenderer.as

Procesador de celdas personalizado para visualizar un botón de reproducción en una celda de cuadrícula de datos (solo Flash).

com/example/programmingas3/podcastplayer/RSSBase.as

Una clase base que proporciona propiedades y métodos comunes para las clases RSSChannel y RSSItem.

com/example/programmingas3/podcastplayer/RSSChannel.as

Una clase ActionScript que contiene datos sobre un canal RSS.

com/example/programmingas3/podcastplayer/RSSItem.as

Una clase ActionScript que contiene datos sobre un elemento RSS.

com/example/programmingas3/podcastplayer/SoundFacade.as

La clase ActionScript principal de la aplicación. Reúne los métodos y los eventos de las clases Sound y SoundChannel, y añade las funciones de pausa y reanudación de reproducción.

com/example/programmingas3/podcastplayer/URLService.as

Una clase ActionScript que recupera datos de un URL remoto.

playerconfig.xml

Un archivo XML que contiene una lista de las fuentes RSS que representan canales podcast.

comp/example/programmingas3/utils/DateUtil.as

Clase que se utiliza para aplicar formato de fechas fácilmente (solo Flash).

Lectura de datos RSS para un canal podcast

La aplicación Podcast Player empieza leyendo información sobre varios canales podcast y sus episodios:

1. En primer lugar, la aplicación lee un archivo de configuración XML que contiene una lista de canales podcast y muestra dicha lista al usuario.

2. Cuando el usuario selecciona uno de los canales podcast, lee la fuente RSS del canal y muestra una lista de los episodios del canal.

Este ejemplo utiliza la clase de utilidad URLLoader para recuperar datos basados en texto desde una ubicación remota o un archivo local. Podcast Player crea primero un objeto URLLoader para obtener una lista de fuentes RSS en formato XML del archivo playerconfig.xml. A continuación, cuando el usuario selecciona una fuente específica de la lista, se crea un nuevo objeto URLLoader para leer datos RSS del URL de dicha fuente.

Simplificación de la carga y la reproducción de sonido mediante la clase SoundFacade

La arquitectura de sonido ActionScript 3.0 es eficaz, pero compleja. Las aplicaciones que solo necesitan funciones básicas de carga y reproducción de sonido pueden utilizar una clase que oculta parte de la complejidad; dicha clase proporciona un conjunto más sencillo de llamadas de método y eventos. En el mundo de los patrones de diseño de software, esta clase se denomina facade .

La clase SoundFacade presenta una interfaz única para realizar las tareas siguientes:

  • Cargar archivos de sonido con un objeto Sound, un objeto SoundLoaderContext y la clase SoundMixer

  • Reproducir archivos de sonido con los objetos Sound y SoundChannel

  • Distribuir eventos de progreso de reproducción

  • Pausar y reanudar la reproducción del sonido mediante los objetos Sound y SoundChannel

La clase SoundFacade intenta ofrecer la mayor parte de las funciones de las clase de sonido ActionScript, pero con una menor complejidad.

En el código siguiente se muestra la declaración de clase, las propiedades de clase y el método constructor 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 clase SoundFacade amplía la clase EventDispatcher para que pueda distribuir sus propios eventos. El código de clase declara primero las propiedades de un objeto Sound y un objeto SoundChannel. La clase también almacena el valor del URL del archivo de sonido y una propiedad bufferTime que se utilizará al transmitir el sonido. Asimismo, acepta algunos valores de parámetro booleanos que afectan al comportamiento de carga y reproducción.

  • El parámetro autoLoad indica al objeto que la carga de sonido debe empezar en cuanto se cree este objeto.

  • El parámetro autoPlay indica que la reproducción de sonido debe empezar en cuanto se hayan cargado suficientes datos de sonido. Si se trata de un flujo de sonido, la reproducción empezará tan pronto como se hayan cargado suficientes datos, según especifica la propiedad bufferTime .

  • El parámetro streaming indica que este archivo de sonido puede empezar a reproducirse antes de que haya finalizado la carga.

El parámetro bufferTime se establece de forma predeterminada en un valor de -1. Si el método constructor detecta un valor negativo en el parámetro bufferTime , establece la propiedad bufferTime en el valor de SoundMixer.bufferTime . Esto permite que la aplicación se establezca de forma predeterminada en el valor SoundMixer.bufferTime global, según se desee.

Si el parámetro autoLoad se establece en true , el método constructor llama inmediatamente al siguiente método load() para iniciar la carga del archivo de sonido:

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

El método load() crea un nuevo objeto Sound y añade detectores para todos los eventos de sonido importantes. A continuación, indica al objeto Sound que cargue el archivo de sonido, mediante un objeto SoundLoaderContext para pasar el valor bufferTime .

Puesto que se puede cambiar la propiedad url , se puede usar una instancia SoundFacade para reproducir distintos archivos de sonido en secuencia: solo hay que cambiar la propiedad url y llamar al método load() para que se cargue el nuevo archivo de sonido.

Los tres siguientes métodos de detección de eventos muestran la forma en que el objeto SoundFacade hace un seguimiento del progreso de carga y decide el momento en que se inicia la reproducción del sonido:

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

El método onLoadOpen() se ejecuta cuando se inicia la carga de sonido. Si se puede reproducir el sonido en el modo de transmisión, el método onLoadComplete() establece el indicador isReadyToPlay en true inmediatamente. El indicador isReadyToPlay determina si la aplicación puede empezar a reproducir el sonido, tal vez como respuesta a una acción de usuario, como hacer clic en un botón Reproducir. La clase SoundChannel administra el búfer de los datos de sonido, de modo que no es necesario comprobar explícitamente si se han cargado suficientes datos antes de llamar al método play() .

El método onLoadProgress() se ejecuta periódicamente durante el proceso de carga. Distribuye un clon de su objeto ProgressEvent que utilizará el código que usa este objeto SoundFacade.

Cuando los datos de sonido se han cargado completamente, se ejecuta el método onLoadComplete() y se llama al método play() para sonidos que no sean de una transmisión de flujo si es necesario. El método play() se muestra a continuación.

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

El método play() llama al método Sound.play() si el sonido está listo para reproducirse. El objeto SoundChannel resultante se almacena en la propiedad sc . El método play() crea un objeto Timer que se utilizará para distribuir eventos de progreso de reproducción a intervalos regulares.

Visualización del progreso de reproducción

Crear un objeto Timer para controlar la reproducción es una operación compleja que solo se debería tener que programar una sola vez. La encapsulación de esta lógica Timer en una clase reutilizable (como la clase SoundFacade) permite que las aplicaciones detecten las mismas clases de eventos de progreso cuando se carga y se reproduce un sonido.

El objeto Timer que crea el método SoundFacade.play() distribuye una instancia de TimerEvent cada segundo. El siguiente método onPlayTimer() se ejecuta cuando llega una nueva clase 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); 
}

El método onPlayTimer() implementa la técnica de estimación de tamaño descrita en la sección Control de la reproducción . Luego crea una nueva instancia de ProgressEvent con un tipo de evento de SoundFacade.PLAY_PROGRESS , con la propiedad bytesLoaded establecida en la posición actual del objeto SoundChannel y la propiedad bytesTotal establecida en la duración estimada de los datos de sonido.

Pausa y reanudación de la reproducción

El método SoundFacade.play() mostrado anteriormente acepta un parámetro pos correspondiente a un punto inicial de los datos de sonido. Si el valor pos es cero, el sonido empieza a reproducirse desde el principio.

El método SoundFacade.stop() también acepta un parámetro pos , tal como se muestra aquí:

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

Cuando se llama al método SoundFacade.stop() , este establece la propiedad pausePosition para que la aplicación sepa dónde colocar la cabeza lectora si el usuario desea reanudar la reproducción del mismo sonido.

Los métodos SoundFacade.pause() y SoundFacade.resume() que se muestran a continuación invocan los métodos SoundFacade.stop() y SoundFacade.play() respectivamente y pasan un valor de parámetro pos cada vez.

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

El método pause() pasa el valor SoundChannel.position al método play() , que almacena dicho valor en la propiedad pausePosition . El método resume() empieza a reproducir de nuevo el mismo sonido con el valor pausePosition como punto inicial.

Ampliación del ejemplo de Podcast Player

En este ejemplo se presenta un Podcast Player básico que aborda la utilización de la clase SoundFacade reutilizable. También se podrían añadir otras funciones para ampliar la utilidad de esta aplicación, entre las que se incluyen:

  • Almacenar la lista de fuentes e información de utilización sobre cada episodio de una instancia de SharedObject que se pueda utilizar la próxima vez que el usuario ejecute la aplicación.

  • Permitir que el usuario añada su propia fuente RSS a la lista de canales podcast.

  • Recordar la posición de la cabeza lectora cuando el usuario detenga o abandone un episodio; de este modo, se puede reiniciar desde dicho punto la próxima vez que el usuario ejecute la aplicación.

  • Descargar archivos MP3 de episodios para escuchar cuando el usuario no esté conectado a Internet.

  • añadir funciones de suscripción que busquen nuevos episodios periódicamente en un canal podcast y actualicen la lista de episodios de forma automática.

  • Añadir funciones de búsqueda y exploración de emisiones podcast con la API de un servicio de alojamiento de podcast, como Odeo.com.