Пример звука: Podcast Player

Flash Player 9 и более поздних версий, Adobe AIR 1.0 и более поздних версий

Подкаст — это звуковой файл, распространяемый через Интернет по требованию или по подписке. Как правило, подкасты публикуются по сериям, которые также называются каналами подкастов. Так как эпизоды подкастов могут иметь продолжительность от одной минуты до многих часов, для них обычно используется потоковое воспроизведение. Эпизоды подкастов, которые также называются элементами, обычно передаются как файлы в формате mp3. Видеоподкасты также популярны, но это приложение воспроизводит только аудиоподкасты, использующие mp3-файлы.

Этот пример не является полнофункциональным приложением для агрегации подкастов. Например, оно не управляет подписками на определенные подкасты и не запоминает прослушанные пользователем подкасты до следующего запуска приложения. Его можно использовать как отправную точку для создания более функционального агрегатора подкастов.

Пример PodcastPlayer демонстрирует следующие приемы программирования на ActionScript:

  • чтение внешнего канала RSS и анализ его XML-содержимого;

  • создание класса SoundFacade для упрощения загрузки и воспроизведения звуковых файлов;

  • отображение прогресса воспроизведения звука;

  • приостановка и возобновление воспроизведения звука.

Получить файлы приложения для этого примера можно на странице www.adobe.com/go/learn_programmingAS3samples_flash_ru. Файлы приложения Podcast Player находятся в папке Samples/PodcastPlayer. Приложение состоит из следующих файлов.

File

Описание

PodcastPlayer.mxml

или

PodcastPlayer.fla

Пользовательский интерфейс приложения для Flex (MXML) или Flash (FLA).

comp/example/programmingas3/podcastplayer/PodcastPlayer.as

Класс документа, отвечающий за поведение интерфейса пользователя проигрывателя подкастов (только Flash).

SoundPlayer.mxml

Компонент MXML, который отображает кнопки воспроизведения и полосы прогресса, а также управляет воспроизведением звука, только для Flex.

main.css

Стили пользовательского интерфейса приложения (только Flex).

images/

Значки стиля кнопок (только Flex).

comp/example/programmingas3/podcastplayer/SoundPlayer.as

Класс символа фрагмента ролика SoundPlayer, отвечающий за поведение интерфейса пользователя проигрывателя (только Flash).

comp/example/programmingas3/podcastplayer/PlayButtonRenderer.as

Заказное средство визуализации ячеек, используемое для отображения кнопки воспроизведения в ячейке DataGrid (только Flash).

com/example/programmingas3/podcastplayer/RSSBase.as

Базовый класс, который обеспечивает общие свойства и методы для классов RSSChannel и RSSItem.

com/example/programmingas3/podcastplayer/RSSChannel.as

Класс ActionScript, который содержит данные о канале RSS.

com/example/programmingas3/podcastplayer/RSSItem.as

Класс ActionScript, который содержит данные об элементе RSS.

com/example/programmingas3/podcastplayer/SoundFacade.as

Основной класс ActionScript для приложения. Он содержит методы и события классов Sound и SoundChannel и добавляет поддержку для приостановки и возобновления воспроизведения.

com/example/programmingas3/podcastplayer/URLService.as

Класс ActionScript, который получает данные с удаленного URL-адреса.

playerconfig.xml

XML-файл, который содержит список каналов RSS, являющихся каналами подкастов.

comp/example/programmingas3/utils/DateUtil.as

Класс, используемый для упрощенного форматирования даты (только Flash).

Считывание данных RSS для канала подкастов

Приложение Podcast Player начинает работу со считывания информации о количестве каналов подкастов и их эпизодов.

1. Сначала приложение считывает XML-файл конфигурации, который содержит список каналов подкастов и отображает его пользователю.

2. Когда пользователь выбирает один из каналов подкастов, приложение считывает канал RSS для данного канала и отображает список его эпизодов.

В этом примере используется класс URLLoader для получения текстовых данных из удаленного местоположения или локального файла. Проигрыватель Podcast Player сначала создает объект URLLoader, чтобы получить список каналов RSS в формате XML из файла playerconfig.xml. Затем, когда пользователь выбирает из списка определенный канал, создается новый объект URLLoader для считывания данных RSS с URL-адреса этого канала.

Упрощение загрузки и воспроизведения звука с помощью класса SoundFacade

Архитектура звука в ActionScript 3.0 отличается мощностью и при этом сложностью. Приложения, в которых требуются только базовые функции загрузки и воспроизведения звука, могут использовать класс, который частично скрывает сложности, обеспечивая более простой набор методов и событий. В области шаблонов программного дизайна такой класс называется фасадом.

Класс SoundFacade представляет единый интерфейс для выполнения следующих задач:

  • загрузка аудиофайлов с помощью объектов Sound, SoundLoaderContext и класса SoundMixer;

  • воспроизведение звуковых файлов с помощью объектов Sound и SoundChannel;

  • отправка событий о прогрессе воспроизведения;

  • приостановка и возобновление воспроизведения звука с помощью объектов Sound и SoundChannel.

Класс SoundFacade пытается обеспечить максимальную функциональность звуковых классов ActionScript, при этом упрощая работу.

Следующий код демонстрирует объявление класса, свойства класса и конструктор 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(); 
        } 
    }

Класс SoundFacade расширяет класс EventDispatcher, чтобы иметь возможность отправлять собственные события. Сначала код класса объявляет свойства для объектов Sound и SoundChannel. Класс также сохраняет значение URL-адреса аудиофайла и свойство bufferTime для использования при потоковом воспроизведении звука. Кроме того, он принимает несколько логических значений параметров, которые определяют поведение при загрузке и воспроизведении.

  • Параметр autoLoad сообщает объекту о том, что загрузка звука должна начаться сразу после создания этого объекта.

  • Параметр autoPlay указывает на то, что воспроизведение звука должно начаться, как только будет загружен достаточный объем аудиоданных. Если звук потоковый, воспроизведение начнется, как только будет загружен достаточный объем данных, заданный в свойстве bufferTime.

  • Параметр streaming указывает на то, что воспроизведение этого аудиофайла может начаться до завершения загрузки.

Параметр bufferTime имеет значение по умолчанию -1. Если конструктор обнаруживает отрицательное значение в параметре bufferTime, то задает свойству bufferTime значение SoundMixer.bufferTime. Благодаря этому приложение может использовать глобальное значение SoundMixer.bufferTime в соответствии с потребностями.

Если параметр autoLoad имеет значение true, конструктор немедленно вызывает следующий метод load() для загрузки аудиофайла.

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

Метод load() создает новый объект Sound и затем добавляет прослушиватели для всех важных событий звука. Затем он предписывает объекту Sound загрузить звук, используя объект SoundLoaderContext для передачи значения bufferTime.

Так как свойство url может быть изменено, экземпляр SoundFacade может использоваться для воспроизведения последовательности разных звуковых файлов: для этого нужно просто изменить свойство url и вызвать метод load(), чтобы загрузить новый звук.

Три следующих прослушивателя событий демонстрируют, как объект SoundFacade отслеживает прогресс загрузки и определяет время для начала воспроизведения звука.

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

Метод onLoadOpen() выполняется после начала загрузки звука. Если звук может воспроизводиться в потоковом режиме, метод onLoadComplete() сразу устанавливает для флага isReadyToPlay значение true. Флаг isReadyToPlay определяет, когда приложение может начать воспроизведение звука, возможно, в ответ на действие пользователя, например нажатие кнопки «Play» (воспроизведение). Класс SoundChannel управляет буферизацией аудиоданных, поэтому не требуется специально проверять, загружен ли достаточный объем данных, перед вызовом метода play().

Метод onLoadProgress() выполняется периодически в процессе загрузки. Он просто отправляет клон своего объекта ProgressEvent для кода, использующего объект SoundFacade.

После завершения загрузки аудиоданных выполняется метод onLoadComplete(), который при необходимости вызывает метод play() для непотоковых звуков. Сам метод 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(); 
        } 
    } 
}

Метод play() вызывает метод Sound.play(), если звук готов для воспроизведения. Созданный в результате этого объект SoundChannel сохраняется в свойстве sc. Затем метод play() создает объект Timer, который будет использоваться для отправки событий прогресса воспроизведения через регулярные интервалы.

Отображение прогресса воспроизведения звука

Создание объекта Timer для отслеживания воспроизведения — это сложная операция, но этот код нужно написать только один раз. Инкапсуляция этой логики Timer в неоднократно используемом классе, таком как SoundFacade, позволяет приложениям прослушивать те же типы событий прогресса как во время загрузки звука, так и во время его воспроизведения.

Объект Timer, созданный методом SoundFacade.play(), каждую секунду отправляет экземпляр TimerEvent. Следующий метод onPlayTimer() выполняется каждый раз при получении нового события 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); 
}

Метод onPlayTimer() оценивает общий размер, как описано в разделе Отслеживание воспроизведения. Затем он создает новый экземпляр ProgressEvent с типом события SoundFacade.PLAY_PROGRESS, в котором свойство bytesLoaded содержит текущую позицию объекта SoundChannel, а свойство bytesTotal указывает предполагаемую продолжительность аудиоданных.

Приостановка и возобновление воспроизведения звука

Метод SoundFacade.play(), описанный выше, принимает параметр pos, который соответствует начальной позиции аудиоданных. Если pos имеет значение 0, воспроизведение звука запускается с самого начала.

Метод SoundFacade.stop() также принимает параметр pos, как показано ниже.

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

При каждом вызове метод SoundFacade.stop() задает свойство pausePosition, чтобы сообщить приложению позицию точки воспроизведения на тот случай, если пользователь захочет возобновить воспроизведение того же звука.

Методы SoundFacade.pause() и SoundFacade.resume(), показанные ниже, вызывают методы SoundFacade.stop() и SoundFacade.play() соответственно, каждый раз передавая значение для параметра pos.

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

Метод pause() передает текущее значение SoundChannel.position методу play(), который сохраняет это значение в свойстве pausePosition. Метод resume() начинает воспроизведение того же звука, используя в качестве начальной точки значение свойства pausePosition.

Расширение примера Podcast Player

Данный пример представляет собой каркас проигрывателя Podcast Player, демонстрирующий применение класса SoundFacade многократного пользования. Чтобы сделать приложение более полезным, в него можно добавить другие функции, включая следующие:

  • Сохраняйте список каналов и информацию об использовании для каждого эпизода в экземпляре SharedObject, который может использоваться при следующем запуске приложения.

  • Дайте пользователю возможность добавлять свои каналы RSS в список каналов подкастов.

  • Запоминайте позицию точки воспроизведения, когда пользователь останавливает или закрывает эпизод, чтобы при следующем запуске приложения воспроизведение можно было начать с этого места.

  • Загружайте mp3-файлы эпизодов для прослушивания в автономном режиме, когда пользователь не подключен к Интернету.

  • Добавьте функции управления подписками, которые периодически проверяют наличие новых эпизодов в канале подкастов и автоматически обновляют их список.

  • Добавьте функции поиска и просмотра подкастов с помощью API-интерфейса службы размещения подкастов, такой как Odeo.com.