Exemplo de som: Podcast Player

Flash Player 9 e posterior, Adobe AIR 1.0 e posterior

Um podcast é um arquivo de som distribuído pela Internet sob demanda ou por assinatura. Podcasts normalmente são publicados como parte de uma série, o que também é chamado de canal de podcast. Como episódios de podcast podem durar de um minuto a muitas horas, eles normalmente são transmitidos enquanto estão sendo carregados. Episódios de podcast, que também são chamados de itens, normalmente são entregues no formato de arquivo mp3. Os podcasts de vídeo também são populares, mas esse aplicativo de amostra reproduz apenas podcasts de áudio que usam arquivos mp3.

Este exemplo não é de um aplicativo agregador de podcast completo. Por exemplo, ele não gerencia assinaturas para podcasts específicos ou lembra quais podcasts foram ouvidos pelo usuário na próxima vez que o aplicativo é executado. Ele pode funcionar como um ponto de partida para um agregador de podcast mais completo.

Este exemplo de Podcast Player ilustra as seguintes técnicas de programação do ActionScript:

  • Leitura de um RSS feed externo e análise de seu conteúdo XML

  • Criação de uma classe SoundFacade para simplificar o carregamento e a reprodução de arquivos de som.

  • Exibição do progresso da reprodução do som.

  • Pausa e reinício da reprodução do som.

Para obter os arquivos de aplicativo desse exemplo, consulte www.adobe.com/go/learn_programmingAS3samples_flash_br . Os arquivos do aplicativo Podcast Player podem ser encontrados na pasta Amostras/PodcastPlayer. O aplicativo consiste nos seguintes arquivos:

Arquivo

Descrição

PodcastPlayer.mxml

ou

PodcastPlayer.fla

A interface do usuário do aplicativo para Flex (MXML) ou Flash (FLA).

comp/example/programmingas3/podcastplayer/PodcastPlayer.as

Classe Document que contém a lógica da interface de usuário do player de podcast (somente no Flash).

SoundPlayer.mxml

Um componente MXML que exibe botões de reprodução e barras de progresso e controla a reprodução de som, apenas para Flex.

main.css

Estilos da interface de usuário do aplicativo (somente no Flex).

images/

Ícones para aplicar estilo aos botões (somente no Flex).

comp/example/programmingas3/podcastplayer/SoundPlayer.as

Classe do símbolo de clipe de filme SoundPlayer que contém a lógica da interface de usuário do player de som (somente no Flash).

comp/example/programmingas3/podcastplayer/PlayButtonRenderer.as

Renderizador de células personalizado para exibir um botão Reproduzir em uma célula de grade de dados (somente no Flash).

com/example/programmingas3/podcastplayer/RSSBase.as

Uma classe base que fornece propriedades e métodos comuns para as classes RSSChannel e RSSItem.

com/example/programmingas3/podcastplayer/RSSChannel.as

Uma classe ActionScript que mantém dados sobre um canal RSS.

com/example/programmingas3/podcastplayer/RSSItem.as

Uma classe ActionScript que mantém dados sobre um item RSS.

com/example/programmingas3/podcastplayer/SoundFacade.as

A classe principal do ActionScript para o aplicativo. Ela encapsula os métodos e eventos da classe Sound e da classe SoundChannel e adiciona suporte para pausar e reiniciar a reprodução.

com/example/programmingas3/podcastplayer/URLService.as

Uma classe ActionScript que recupera dados de uma URL remota.

playerconfig.xml

Um arquivo XML que contém uma lista de RSS feeds que representam canais de podcast.

comp/example/programmingas3/utils/DateUtil.as

Classe usada para facilitar a formatação de dados (somente no Flash).

Leitura de dados RSS para um canal de podcast

O aplicativo Podcast Player começa lendo informações sobre vários canais de podcast e seus episódios:

1. Primeiro, o aplicativo lê um arquivo de configuração XML que contém uma lista de canais de podcast e exibe a lista de canais para o usuário.

2. Quando o usuário seleciona um dos canais de podcast, ele lê o RSS feed do canal e exibe uma lista de episódios de canal.

Este exemplo usa a classe do utilitário URLLoader para recuperar dados com base em texto de um local remoto ou de um arquivo local. O Podcast Player primeiro cria um objeto URLLoader para obter uma lista de RSS feeds em formato XML do arquivo playerconfig.xml. Em seguida, quando o usuário seleciona um feed específico da lista, um novo objeto URLLoader é criado para ler os dados RSS daquela URL do feed.

Simplificação do carregamento e da reprodução de som usando a classe SoundFacade

A arquitetura de som do ActionScript 3.0 é poderosa, mas complexa. Aplicativos que precisam apenas de recursos básicos de carregamento e reprodução de som podem usar uma classe que oculte um pouco da complexidade fornecendo um conjunto mais simples de chamadas e eventos de métodos. No mundo de padrões de design de software, essa classe é chamada de facade .

A classe SoundFacade apresenta uma única interface para executar as seguintes tarefas:

  • Carregamento de arquivos de som usando um objeto Sound, um objeto SoundLoaderContext e uma classe SoundMixer.

  • Reprodução de arquivos de som usando os objetos Sound e SoundChannel.

  • Despacho de eventos de progresso da reprodução.

  • Pausa e reinício da reprodução do som usando os objetos Sound e SoundChannel.

A classe SoundFacade tenta oferecer mais da funcionalidade das classes de som do ActionScript com menos complexidade.

O código a seguir mostra a declaração da classe, as propriedades da classe e o método construtor 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(); 
        } 
    }

A classe SoundFacade estende a classe EventDispatcher para poder despachar seus próprios eventos. O código da classe primeiro declara propriedades de um objeto Sound e de um objeto SoundChannel. A classe também armazena o valor da URL do arquivo de som e uma propriedade bufferTime a ser usada ao transmitir o som em fluxo. Além disso, ele aceita alguns valores de parâmetros boolianos que afetam o comportamento do carregamento e da reprodução:

  • O parâmetro autoLoad indica ao objeto que o carregamento do som deve iniciar assim que esse objeto é criado.

  • O parâmetro autoPlay indica que a reprodução do som deve iniciar assim que dados de som suficientes estiverem carregados. Se esse for um fluxo de som, a reprodução será iniciada assim que dados suficientes, conforme especificado pela propriedade bufferTime , estiverem carregados.

  • O parâmetro streaming indica que a reprodução desse arquivo pode ser iniciada antes do carregamento ser concluído.

O parâmetro bufferTime é padronizado como um valor de -1. Se o método construtor detectar um valor negativo no parâmetro bufferTime , ele definirá a propriedade bufferTime como o valor de SoundMixer.bufferTime . Isso permite que o aplicativo seja padronizado para o valor global SoundMixer.bufferTime conforme desejado.

Se o parâmetro autoLoad for definido como true , o método construtor chamará imediatamente o método load() seguinte para iniciar o carregamento do arquivo de som:

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

O método load() cria um novo objeto Sound e adiciona ouvintes para todos os eventos de som importantes. Em seguida, ele indica ao objeto Sound para carregar o arquivo de som usando um objeto SoundLoaderContext para passar o valor de bufferTime .

Como a propriedade url pode ser alterada, uma ocorrência de SoundFacade pode ser usada para reproduzir arquivos de som diferentes em sucessão: simplesmente altere a propriedade url e chame o método load() e o novo arquivo de som será carregado.

Os três métodos ouvintes de eventos a seguir mostram como o objeto SoundFacade rastreia o progresso do carregamento e decide quando iniciar a reprodução do som:

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

O método onLoadOpen() é executado quando o carregamento do som é iniciado. Se o som puder ser reproduzido em modo de streaming, o método onLoadComplete() definirá o sinalizador isReadyToPlay como true imediatamente. O sinalizador isReadyToPlay determina se o aplicativo pode iniciar a reprodução do som, talvez em resposta a uma ação do usuário, como um clique em um botão de reprodução. A classe SoundChannel gerencia o buffer de dados de som, portanto não há necessidade de verificar explicitamente se dados suficientes foram carregados antes de chamar o método play() .

O método onLoadProgress() é executado periodicamente durante o processo de carregamento. Ele simplesmente despacha um clone de seu objeto ProgressEvent para uso pelo código que usa esse objeto SoundFacade.

Quando os dados do som estiverem totalmente carregados, o método onLoadComplete() é executado, chamando o método play() para sons que não sejam de fluxo, se necessário. O próprio método play( ) é mostrado a seguir.

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

O método play() chamará o método Sound.play() se o som estiver pronto para execução. O objeto SoundChannel resultante é armazenado na propriedade sc . Em seguida, o método play() cria um objeto Timer que será utilizado para despachar eventos de progresso da reprodução em intervalos regulares.

Exibição do progresso da reprodução

A criação de um objeto Timer para acionar o monitoramento da reprodução é uma operação complexa que você deve codificar apenas uma vez. O encapsulamento dessa lógica de Timer em uma classe reutilizável, como a classe SoundFacade, permite que os aplicativos ouçam alguns tipos de eventos de progresso quando um som está sendo carregado e quando ele está sendo reproduzido.

O objeto Timer criado pelo método SoundFacade.play() despacha uma ocorrência de TimerEvent a cada segundo. O método onPlayTimer() a seguir é executado sempre que um novo TimerEvent é recebido:

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

O método onPlayTimer() implementa a técnica de estimativa de tamanho descrita na seção Monitoramento da reprodução . Em seguida, ele cria uma nova ocorrência de ProgressEvent com um tipo de evento de SoundFacade.PLAY_PROGRESS , com a propriedade bytesLoaded definida como a posição atual do objeto SoundChannel e a propriedade bytesTotal definida como o comprimento estimado dos dados do som.

Pausa e reinício da reprodução

O método SoundFacade.play() mostrado anteriormente aceita um parâmetro pos correspondente a uma posição inicial nos dados de som. Se o valor pos for zero, a reprodução do som será executada do início.

O método SoundFacade.stop() também aceita um parâmetro pos conforme mostrado aqui:

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

Sempre que o método SoundFacade.stop() é chamado, ele define a propriedade pausePosition de forma que o aplicativo saiba onde posicionar o indicador de reprodução se o usuário desejar retomar a reprodução do mesmo som.

Os métodos SoundFacade.pause() e SoundFacade.resume() mostrados a seguir chamam os métodos SoundFacade.stop() e SoundFacade.play() respectivamente, passando um valor de parâmetro pos a cada vez.

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

O método pause() passa o valor atual de SoundChannel.position para o método play() que armazena o valor na propriedade pausePosition . O método resume() inicia a reprodução do mesmo som novamente usando o valor de pausePosition como o ponto de início.

Extensão do exemplo do Podcast Player

Este exemplo apresenta um Podcast Player reduzido ao essencial que demonstra o uso da classe SoundFacade reutilizável. É possível adicionar outros recursos para aprimorar a utilidade deste aplicativo, incluindo o seguinte:

  • Armazenar a lista de feeds e informações de uso sobre cada episódio em uma ocorrência de SharedObject que pode ser usada na próxima vez que o usuário executar o aplicativo.

  • Permitir que o usuário adicione seus próprios RSS feeds à lista de canais de podcast.

  • Memorizar a posição do indicador de reprodução quando o usuário interromper ou sair de um episódio, para que ele possa ser reiniciado daquele ponto na próxima vez que o usuário executar o aplicativo.

  • Baixar arquivos mp3 de episódios para ouvir offline, quando o usuário não estiver conectado à Internet.

  • Adicionar recursos de assinatura que verificam periodicamente novos episódios em um canal de podcast e atualizar a lista de episódios automaticamente.

  • Adicionar funcionalidade de pesquisa e de procura de podcast usando uma API de um serviço de host de podcast, como o Odeo.com.