Exemple d’objet Sound : Podcast Player

Flash Player 9 et les versions ultérieures, Adobe AIR 1.0 et les versions ultérieures

Un podcast est un fichier audio distribué sur Internet, sur demande ou sur abonnement. Les podcasts sont généralement publiés dans un annuaire. Etant donné que les épisodes de podcast peuvent durer d’une minute à plusieurs heures, ils sont généralement diffusés en continu pendant la lecture. Les épisodes de podcast, également appelés éléments, sont généralement fournis au format de fichier mp3. Les podcasts vidéo sont également courants, mais cet exemple d’application lit uniquement des podcasts audio utilisant des fichiers mp3.

Cet exemple n’est pas une application agrégatrice de podcasts comprenant toutes les fonctionnalités. Par exemple, elle ne gère pas les abonnements à des podcasts spécifiques et ne mémorise pas les podcasts qu’un utilisateur a écoutés lors de l’exécution suivante de l’application. Il peut servir de point de départ pour un agrégateur de podcasts comprenant toutes les fonctionnalités.

L’exemple Podcast Player illustre les techniques de programmation ActionScript suivantes :

  • Lecture d’un fil de syndication et analyse de son contenu XML

  • Création d’une classe SoundFacade pour simplifier le chargement et la lecture des fichiers audio

  • Affichage de la progression de la lecture du son

  • Interruption et reprise de la lecture du son

Pour obtenir les fichiers d’application de cet exemple, voir www.adobe.com/go/learn_programmingAS3samples_flash_fr. Les fichiers d’application Podcast Player se trouvent dans le dossier Samples/PodcastPlayer. L’application se compose des fichiers suivants :

Fichier

Description

PodcastPlayer.mxml

ou

PodcastPlayer.fla

Interface utilisateur de l’application pour Flex (MXML) ou Flash (FLA).

comp/example/programmingas3/podcastplayer/PodcastPlayer.as

Classe Document contenant la logique de l’interface utilisateur pour le lecteur de podcast (Flash uniquement).

SoundPlayer.mxml

Un composant MXML qui affiche les commandes de lecture et les barres de progression, et contrôle la lecture du son, pour Flex uniquement.

main.css

Styles associés à l’interface utilisateur de l’application (Flex uniquement)

image/

Icônes permettant le formatage des boutons (Flex uniquement).

comp/example/programmingas3/podcastplayer/SoundPlayer.as

Classe pour le symbole du clip SoundPlayer contenant la logique de l’interface utilisateur du lecteur de sons (Flash uniquement).

comp/example/programmingas3/podcastplayer/PlayButtonRenderer.as

Composant de rendu de cellule personnalisé permettant d’afficher un bouton de lecture dans une cellule de la grille de données (Flash uniquement).

com/example/programmingas3/podcastplayer/RSSBase.as

Une classe de base qui fournit les méthodes et les propriétés courantes pour la classe RSSChannel et la classe RSSItem.

com/example/programmingas3/podcastplayer/RSSChannel.as

Une classe ActionScript qui contient des données relatives à un canal RSS.

com/example/programmingas3/podcastplayer/RSSItem.as

Une classe ActionScript qui contient des données relatives à un élément RSS.

com/example/programmingas3/podcastplayer/SoundFacade.as

La classe ActionScript principale pour l’application. Elle encapsule les méthodes et les événements des classes Sound et SoundChannel et ajoute une prise en charge pour l’interruption et la reprise de la lecture.

com/example/programmingas3/podcastplayer/URLService.as

Une classe ActionScript qui récupère des données d’une URL distante.

playerconfig.xml

Un fichier XML contenant une liste des fils de syndication qui représentent des chaînes de podcast.

comp/example/programmingas3/utils/DateUtil.as

Classe permettant le formatage rapide de la date (Flash uniquement).

Lecture de données RSS pour une chaîne de podcast

L’application Podcast Player commence par lire les informations concernant des chaînes de podcasts et leurs épisodes :

1. L’application commence par lire un fichier de configuration XML qui contient une liste des chaînes de podcast et affiche la liste des chaînes pour l’utilisateur.

2. Lorsque l’utilisateur sélectionne l’une des chaînes de podcast, il lit le flux RSS pour la chaîne et affiche une liste des épisodes de chaîne.

Cet exemple utilise la classe d’utilitaire URLLoader pour récupérer des données de texte depuis un emplacement distant ou un fichier local. L’application Podcast Player crée d’abord un objet URLLoader pour obtenir une liste des fils de syndication au format XML du fichier playerconfig.xml. Ensuite, lorsque l’utilisateur sélectionne un fil de syndication spécifique dans la liste, un nouvel objet URLLoader est créé pour lire les données RSS de l’URL de ce fil.

Simplification de la lecture et du chargement du son à l’aide de la classe SoundFacade

L’architecture audio ActionScript 3.0 est puissante mais complexe. Les applications nécessitant des fonctions de lecture et de chargement de son de base uniquement peuvent utiliser une classe masquant une partie de la complexité en fournissant un ensemble d’appels et d’événements plus simple. Dans l’univers des modèles de conception de logiciel, une telle classe est appelée façade.

La classe SoundFacade présente une seule interface permettant d’effectuer les tâches suivantes :

  • Chargement de fichiers audio à l’aide d’un objet Sound, d’un objet SoundLoaderContext et d’une classe SoundMixer

  • Lecture de fichiers audio à l’aide des objets Sound et SoundChannel

  • Envoi d’événements de progression de la lecture

  • Interruption et reprise de la lecture du son à l’aide des objets Sound et SoundChannel

La classe SoundFacade essaie d’offrir le meilleur de la fonctionnalité des classes de son ActionScript avec moins de complexité.

Le code suivant indique la déclaration de classe, les propriétés de classe et la méthode constructeur 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 étend la classe EventDispatcher pour qu’elle puisse envoyer ses propres événements. Le code de classe déclare d’abord les propriétés pour un objet Sound et un objet SoundChannel. La classe stocke également la valeur de l’URL du fichier audio et une propriété bufferTime à utiliser lors de la lecture du son en continu. De plus, elle accepte des valeurs de paramètre booléennes qui affectent le comportement de lecture et de chargement :

  • Le paramètre autoLoad indique à l’objet que le chargement du son doit commencer dès la création de cet objet.

  • Le paramètre autoPlay indique que la lecture du son doit commencer dès qu’une quantité suffisante de données audio a été chargée. S’il s’agit d’un son diffusé en continu, la lecture commence dès qu’une quantité suffisante de données (comme spécifié par la propriété bufferTime) est chargée.

  • Le paramètre streaming indique que ce fichier audio peut commencer la lecture avant la fin du chargement.

Le paramètre bufferTime prend la valeur -1 par défaut. Si la méthode constructeur détecte une valeur négative dans le paramètre bufferTime, elle définit la propriété bufferTime sur la valeur de SoundMixer.bufferTime. Ceci permet à l’application de prendre la valeur SoundMixer.bufferTime globale, par défaut, comme souhaité.

Si le paramètre autoLoad est défini sur true, la méthode constructeur appelle immédiatement la méthode load() suivante pour commencer le chargement du fichier 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); 
}

La méthode load() crée un objet Sound puis ajoute des écouteurs pour tous les événements de son importants. Elle indique ensuite à l’objet Sound de charger le fichier audio, à l’aide d’un objet LoaderContext pour transmettre la valeur bufferTime.

Etant donné que la propriété url peut être modifiée, vous pouvez utiliser une occurrence de SoundFacade pour lire différents fichiers audio à la suite : il vous suffit de modifier la propriété url et d’appeler la méthode load() afin de charger le nouveau fichier audio.

Les trois méthodes d’écouteur d’événement suivantes indiquent comment l’objet SoundFacade suit la progression du chargement et décide quand lancer la lecture du son :

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

La méthode onLoadOpen() s’exécute lorsque le chargement du son commence. Si vous pouvez lire le son en mode continu, la méthode onLoadComplete() définit immédiatement l’indicateur isReadyToPlay sur true. L’indicateur isReadyToPlay détermine si l’application peut lancer la lecture du son, peut-être en réponse à une action utilisateur (clic sur un bouton de lecture, par exemple). La classe SoundChannel gère la mise en mémoire tampon des données audio. Par conséquent, il est inutile de vérifier si suffisamment de données ont été chargées avant d’appeler la méthode play().

La méthode onLoadProgress() s’exécute régulièrement pendant le chargement. Elle envoie simplement une copie de son objet ProgressEvent pour le code qui utilise cet objet SoundFacade.

Une fois que les données audio ont été complètement chargées, la méthode onLoadComplete() s’exécute en appelant la méthode play() pour des sons non diffusés en continu, si nécessaire. La méthode play() est décrite ci-dessous.

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

La méthode play() appelle la méthode Sound.play() lorsque le son peut être lu. L’objet SoundChannel résultant est stocké dans la propriété sc. La méthode play() crée ensuite un objet Timer qui sera utilisé pour envoyer des événements de progression de la lecture à des intervalles réguliers.

Affichage de la progression de la lecture

La création d’un objet Timer pour surveiller la lecture est une opération complexe que vous devez coder une seule fois. Le fait d’encapsuler cette logique Timer dans une classe réutilisable telle que la classe SoundFacade permet aux applications d’écouter les mêmes types d’événements de progression lorsqu’un son est chargé et lorsqu’il est lu.

L’objet Timer créé par la méthode SoundFacade.play() envoie une occurrence de TimerEvent toutes les secondes. La méthode onPlayTimer() s’exécute chaque fois qu’un nouveau TimerEvent arrive :

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

La méthode onPlayTimer() implémente la technique d’estimation de la taille décrite dans la section Surveillance de la lecture. Elle crée ensuite une occurrence de ProgressEvent avec un type d’événement de SoundFacade.PLAY_PROGRESS, avec la propriété bytesLoaded définie sur la position actuelle de l’objet SoundChannel et la propriété bytesTotal définie sur la longueur estimée des données audio.

Interruption et reprise de la lecture

La méthode SoundFacade.play() décrite précédemment accepte un paramètre pos correspondant à une position de début dans les données audio. Si la valeur pos est zéro, la lecture du son commence au début.

La méthode SoundFacade.stop() accepte également un paramètre pos, comme indiqué ici :

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

Chaque fois que la méthode SoundFacade.stop() est appelée, elle définit la propriété pausePosition de façon à ce que l’application sache où positionner la tête de lecture si l’utilisateur souhaite reprendre la lecture du même son.

Les méthodes SoundFacade.pause() et SoundFacade.resume() indiquées ci-dessous appellent les méthodes SoundFacade.stop() et SoundFacade.play() respectivement, transmettant chaque fois une valeur de paramètre pos.

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

La méthode pause() transmet la valeur SoundChannel.position actuelle à la méthode play(), qui la stocke dans la propriété pausePosition. La méthode resume() recommence à lire le même son en utilisant la valeur pausePosition comme point de début.

Extension de l’exemple Podcast Player

Cet exemple présente un Podcast Player dépouillé qui présente l’utilisation de la classe SoundFacade réutilisable. Vous pouvez ajouter d’autres fonctions pour améliorer l’utilité de cette application, notamment :

  • stocker la liste des fils de syndication et des informations d’utilisation concernant chaque épisode dans une occurrence de SharedObject pouvant être utilisée la prochaine fois que l’utilisateur exécute l’application ;

  • permettre à l’utilisateur d’ajouter son fil de syndication à la liste des chaînes de podcast ;

  • mémoriser la position de la tête de lecture lorsque l’utilisateur arrête ou quitte un épisode de façon à ce qu’il puisse être redémarré à partir de ce point la prochaine fois que l’utilisateur exécute l’application ;

  • télécharger des fichiers mp3 d’épisodes pour les écouter hors ligne, lorsque l’utilisateur n’est pas connecté à Internet ;

  • ajouter des fonctions d’abonnement qui vérifient régulièrement la présence de nouveaux épisodes dans une chaîne de podcast et mettre à jour la liste des épisodes automatiquement ;

  • ajouter une fonctionnalité de recherche de podcasts à l’aide d’une API à partir d’un service d’hébergement de podcasts, tel que Odeo.com.