声音示例:Podcast Player

Flash Player 9 和更高版本,Adobe AIR 1.0 和更高版本

播客是通过 Internet 以按需方式或订阅方式分发的声音文件。播客通常是作为系列的一部分发布的,此系列也称为播客频道。由于播客节目的持续时间从一分钟到数小时不等,因此,通常在播放的同时对其进行流式传输。播客节目(也称为项目)通常是以 mp3 文件格式提供的。视频播客也非常受欢迎,但此范例应用程序仅播放使用 mp3 文件的音频播客。

此示例并不是一个功能完备的播客聚合器应用程序。例如,它不能管理对特定播客的订阅,或在下次运行应用程序时记住用户已收听的播客。它可用作功能更完备的播客聚合器的起点。

Podcast Player 示例说明了以下 ActionScript 编程方法:

  • 读取外部 RSS 新闻频道并分析其 XML 内容

  • 创建 SoundFacade 类以简化加载和播放声音文件的过程

  • 显示声音播放进度

  • 暂停和恢复声音播放

若要获取此范例的应用程序文件,请参阅 www.adobe.com/go/learn_programmingAS3samples_flash_cn。Podcast Player 应用程序文件位于文件夹 Samples/PodcastPlayer 中。该应用程序包含以下文件:

文件

说明

PodcastPlayer.mxml

PodcastPlayer.fla

适用于 Flex (MXML) 或 Flash (FLA) 的应用程序的用户界面。

comp/example/programmingas3/podcastplayer/PodcastPlayer.as

包含 podcast 播放器用户界面逻辑的文档类(仅限 Flash)。

SoundPlayer.mxml

一个显示播放按钮和进度栏并控制声音播放的 MXML 组件(仅用于 Flex)。

main.css

应用程序用户界面的样式(仅限 Flex)。

images/

用于为按钮增加样式的图标(仅限 Flex)。

comp/example/programmingas3/podcastplayer/SoundPlayer.as

包含声音播放器用户界面逻辑的 SoundPlayer 影片剪辑元件的类(仅限 Flash)。

comp/example/programmingas3/podcastplayer/PlayButtonRenderer.as

用于在数据网格单元格中显示播放按钮的自定义单元格渲染器(仅限 Flash)。

com/example/programmingas3/podcastplayer/RSSBase.as

为 RSSChannel 类和 RSSItem 类提供公共属性和方法的基类。

com/example/programmingas3/podcastplayer/RSSChannel.as

保存 RSS 频道的相关数据的 ActionScript 类。

com/example/programmingas3/podcastplayer/RSSItem.as

保存 RSS 项目的相关数据的 ActionScript 类。

com/example/programmingas3/podcastplayer/SoundFacade.as

应用程序的主 ActionScript 类。它封装 Sound 类和 SoundChannel 类的方法和事件,并添加对播放暂停和恢复的支持。

com/example/programmingas3/podcastplayer/URLService.as

从远程 URL 检索数据的 ActionScript 类。

playerconfig.xml

这是一个 XML 文件,其中包含表示播客频道的 RSS 新闻频道列表。

comp/example/programmingas3/utils/DateUtil.as

用于简化日期格式设置的类(仅限 Flash)。

读取播客频道的 RSS 数据

Podcast Player 应用程序先读取一些播客频道及其节目的相关信息:

1. 首先,应用程序读取包含播客频道列表的 XML 配置文件,并向用户显示该频道列表。

2. 当用户选择其中的一个播客频道时,应用程序会读取该频道的 RSS 新闻频道并显示频道节目列表。

此示例使用 URLLoader 实用程序类,从远程位置或本地文件检索基于文本的数据。Podcast Player 先创建一个 URLLoader 对象,以便从 playerconfig.xml 文件中获取采用 XML 格式的 RSS 新闻频道列表。接下来,当用户从列表中选择特定新闻频道时,将创建一个新的 URLLoader 对象以从该新闻频道的 URL 中读取 RSS 数据。

使用 SoundFacade 类简化声音加载和播放

ActionScript 3.0 声音体系结构功能强大,但非常复杂。如果应用程序只需要基本的声音加载和播放功能,可使用通过提供一组更简单的方法调用和事件来隐藏某些复杂性的类。在软件设计模式领域中,这样的类称为“外观”。

SoundFacade 类表示用于执行以下任务的单个接口:

  • 使用 Sound 对象、SoundLoaderContext 对象以及 SoundMixer 类来加载声音文件

  • 使用 Sound 对象和 SoundChannel 对象来播放声音文件

  • 调度播放进度事件

  • 使用 Sound 对象和 SoundChannel 对象来暂停和恢复声音播放

SoundFacade 类尝试以更简化的方式提供 ActionScript Sound 类的大部分功能。

以下代码显示了类声明、类属性以及 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 标志设置为 trueisReadyToPlay 标志确定应用程序能否开始播放声音,这可能为了响应用户动作,如单击“播放”按钮。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 对象以实现播放监视是一个很复杂的操作,您只需对其编码一次。通过在可重用的类(如 SoundFacade 类)中封装此 Timer 逻辑,应用程序可以在加载和播放声音时侦听相同类型的进度事件。

SoundFacade.play() 方法创建的 Timer 对象每秒调度一个 TimerEvent 实例。每当新的 TimerEvent 到达时,就会执行以下 onPlayTimer() 方法:

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() 方法可实现在监视播放一节中介绍的大小估计技术。然后,它创建一个事件类型为 SoundFacade.PLAY_PROGRESS 的新 ProgressEvent 实例,并将 bytesLoaded 属性设置 SoundChannel 对象的当前位置,而将 bytesTotal 属性设置为估计的声音数据长度。

暂停和恢复播放

以前显示的 SoundFacade.play() 方法接受 pos 参数,该参数与声音数据中的起始位置相对应。如果 pos 值为零,则从开头开始播放声音。

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 文件,以便用户在没有连接到 Internet 时可以脱机收听。

  • 添加订阅功能,以便定期检查播客频道中的新节目并自动更新节目列表。

  • 使用来自播客托管服务(如 Odeo.com)的 API 添加播客搜索和浏览功能。