一触即发

播放 Windows Phone 中的音频文件

Charles Petzold

下载代码示例

Charles Petzold
当我第一次读 Windows Phone OS 7.1 中的功能增强包括播放声音和音乐文件在后台进行,我想,应用程序的方式"我们没有,已经吗?"

事实证明我是正确的但只有一点点。它是的确可能 Windows Phone OS 7.0 应用程序播放音乐文件在后台进行,但只有在非常特殊的个案。在所有其他情况下,您的应用程序移动到背景时,将会停止任何 Windows Phone OS 7.0 应用程序播放的音乐文件。当然,对于大多数应用程序,这种行为是完全适当和可能完全要。

但是,考虑将音乐传送到你的手机,除了手机的正常音乐库应用程序。这样的应用程序,它是非常理想,仍可继续播放其他应用程序占用的前景色或屏幕时超时,进入锁定状态。即使对于我们这些人没有必要写这样的应用程序,这项设施提供了一种有趣和进入探索"背景代理人"Windows Phone OS 7.1 中引入的新世界的入口点。

在下一期,我将向您展示如何编写一个播放背景音乐文件的 Windows Phone 程序。但我想在 Windows Phone 上提供更广阔的音响设施,开始以更标准的方法播放音频文件,以及 Windows Phone OS 7.0 7.1 版支持此列中。

MediaElement 及来源

Silverlight 程序播放音乐或声音文件的最常见方法是 MediaElement。什么也不是简单的:MediaElement 来自 FrameworkElement,因此您可以把它放在可视化树的 XAML 文件,并只将来源属性设置为 URL:

<MediaElement Source="http://www.SomeWebSite.com/CoolSong.mp3" />

当加载时的 XAML 文件时,音乐文件将自动开始播放。 MediaElement 支持 MP3、 WMA、 WAV 文件。 详细信息记录在 msdn.microsoft.com/library/ff462087(VS.92)

作为互联网上的引用文件的替代方法,您可以在您的应用程序可执行文件中嵌入声音或音乐文件。 将文件添加到 Visual Studio 中的程序,并标记为内容或资源的生成操作。 (内容是首选,并将该文件嵌入在 XAP 可执行文件 ; 与资源,该文件被嵌入在程序的 DLL 中。)源属性设置为一个 URL 引用同一个文件夹的名称,如果适用的文件名称:

<MediaElement Source="Music/LocalSong.wma" />

MediaElement 可以非常简单,虽然有很多方法来使其更为复杂。 一种方法是在运行时,指定的音频文件中的 MediaElementDemo 程序,它是这篇文章的下载代码的一部分一样。

在此程序中,MediaElement 是仍在可视化树中,但不设置源属性,和自动播放设置为 False。 媒体­ElementDemo 让你玩的勃拉姆斯小提琴协奏曲的三个乐章。 (这些文件都是从互联网档案馆的 archive.org/­详细信息/BrahmsViolinConcerto-海菲兹。 它是 1939年表现与小提琴家 · 海菲兹和塞尔日科乌谢维茨基进行,维克托 · 78 rpm 磁盘上原本可用。)三个单选按钮元素具有其标记的属性设置为三个音乐文件的来源。 第一个单选按钮,也是互联网存档 Web 站点上的音乐文件的完整 URL。 第二乐章,我下载的音乐文件 (名为 02Ii.Adagio.mp3) 到我的 PC,创建一个名为音乐,在 Visual Studio 项目文件夹和该文件添加到文件夹。 第二个单选按钮引用该文件的名称为"Music/02Ii.Adagio.mp3"。当检查这两个按钮之一时,事件处理程序获取的 Tag 属性创建一个 Uri 对象,它 (UriKind.Absolute 为指定的 Web 引用和 UriKind.Relative 的内容) 和 MediaElement 的源属性集。

第二乐章是 4.5 MB,有关的文件和它明显增加通过大批量的可执行文件的大小。 将此大小的文件添加到您的可执行文件不是建议,仅为示范在这里做 !

如果您的应用程序需要该大小的文件,可能妥协是可用的:应用程序可以通过互联网一次下载文件,并将其保存到独立存储。 这就是我所做的小提琴协奏曲 》 第三次运动。 第三个单选按钮 (这分配的名称"isoStoreRadio­按钮") 有其为启用属性最初设置为 false。 图 1 显示下载过程。 在页的构造函数中,如果该文件不在独立存储,WebClient 启动后台传输。 转让完成后,该文件保存到独立存储和单选按钮处于启用状态。

图 1 网络将文件下载到独立存储

public MainPage()
{
  InitializeComponent();
  // ...
// Check if file is in Isolated Storage; otherwise start downloading it
  using (IsolatedStorageFile isoStore =
    IsolatedStorageFile.GetUserStoreForApplication())
  {
    if (isoStore.FileExists(isoStoreRadioButton.Tag as string))
    {
      isoStoreRadioButton.IsEnabled = true;
    }
    else
    {
      WebClient webClient = new WebClient();
      webClient.OpenReadCompleted += OnWebClientOpenReadCompleted;
      webClient.OpenReadAsync(new Uri("http://www.archive.org/....mp3"));
    }
  }
  // ...
}
// When the music file is downloaded, save it to Isolated Storage
void OnWebClientOpenReadCompleted(object sender, 
  OpenReadCompletedEventArgs args)
{
  if (!args.Cancelled && args.Error == null)
  {
    Stream inpStream = args.Result;
    byte[] buffer = new byte[inpStream.Length];
    inpStream.Read(buffer, 0, buffer.Length);
    using (IsolatedStorageFile isoStore =
      IsolatedStorageFile.GetUserStoreForApplication())
    {
      string isoPathName = isoStoreRadioButton.Tag as string;
      string isoDirName = Path.GetDirectoryName(isoPathName);
      if (!isoStore.DirectoryExists(isoDirName))
      {
        isoStore.CreateDirectory(isoDirName);
      }
      using (IsolatedStorageFileStream isoStream =
        isoStore.CreateFile(isoPathName))
      {
        isoStream.Write(buffer, 0, buffer.Length);
        isoStoreRadioButton.IsEnabled = true;
      }
    }
  }
}

Windows Phone OS 7.1 在有些情况下,您可以定义的"isostore"前缀的 URI 引用的文件中独立存储,但这并不为 MediaElement 工作。 幸运的是,媒体­元已接受一个记录流对象的 SetSource 属性。 图 2显示的单选按钮元素的检查处理程序如何处理这些分歧。

图 2 在 MediaElement 上设置的源

void OnRadioButtonChecked(object sender, RoutedEventArgs args)
{
  RadioButton radioButton = sender as RadioButton;
  string uriString = radioButton.Tag as string;
  // Save index for tombstoning
  radioButtonIndex = radioButtonPanel.Children.IndexOf(radioButton);
  if (radioButton == isoStoreRadioButton)
  {
    // Call SetSource on MediaElement using Isolated Storage stream.
using (IsolatedStorageFile storage =
      IsolatedStorageFile.GetUserStoreForApplication())
    {
      using (Stream isoStream = storage.OpenFile(uriString, FileMode.Open))
      {
        mediaElement.SetSource(isoStream);
      }
    }
  }
  else
  {
    // Set Source property on MediaElement using URI
    Uri uri = new Uri(uriString, uriString.Contains(':')
      ?
UriKind.Absolute : UriKind.Relative);
    mediaElement.Source = uri;
  }
}

运输和墓碑

您可以使 MediaElement 更加困难为自己的另一种方式是通过添加控件要暂停,并将移动到开头或结尾的文件。 更多的乐趣是滑块控件使您可以将移动到某个特定的点,在文件中,如图所示,在图 3

The MediaElementDemo Program
图 3 MediaElementDemo 程序

ApplicationBar 四个按钮执行非常简单。 分别,他们设置 MediaElement 为零、 调用 MediaElement 方法播放、 暂停方法调用并将位置属性设置为 NaturalDuration 属性的位置的属性。

最为棘手的部分是启用和禁用按钮。 这份工作,处理的 MediaElement CurrentStateChanged 事件。 MediaElement 逻辑时,最好先要使用 Debug.WriteLine,在事件处理程序,以了解如何在神经系统属性更改音乐文件加载时,缓冲,播放、 暂停和结束。

电话时,所有的音乐和声音文件播放通过单一的软件和硬件称为 Zune 媒体队列。 如果您使用标准音乐 + 视频应用程序在电话上播放歌曲或专辑,从您的音乐收藏,那音乐将继续当您离开该应用程序中启动其他应用程序时在后台播放 — 甚至当你开始的 MediaElementDemo 程序。 但是,如果您启动的勃拉姆斯小提琴协奏曲演奏运动之一,将会停止背景音乐。 现在,MediaElementDemo 是在控件中。

但如果 MediaElementDemo 叶前景 — 是否由用户按开始按钮,或让屏幕超时时间 — 勃拉姆斯将停止,即使该程序不是逻辑删除。

在这种情况下,做你想什么用户返回到该程序时,会发生? 如果答案是"无",你很走运 ! 但是,如果要再次启动从它停止音乐,MediaElementDemo 演示如何做到这。 在其 OnNavigatedFrom 重写,该程序可以节省运动当前播放,(可能播放或暂停) 的状态和位置的索引。 在 OnNavigatedTo,该程序检查单选按钮,并设置的状态和 MediaOpened 处理程序中的位置。

MediaLibrary 和 MediaPlayer

我所说之前,Windows Phone OS 7.1,设施已经存在某些音乐文件在后台在电话上的播放。 渔获是这些音乐文件必须是手机的音乐库的一部分。 您的程序可以播放这首歌,之一或者它可以发挥一张专辑的所有歌曲或特定艺术家或体裁,所有的歌曲或所有的歌曲播放列表中。

要执行此操作的类都是 Microsoft.Xna.Framework.Media 命名空间的成员。 要使用 Silverlight 项目中的这些新华社类电话,您首先需要添加到 Microsoft.Xna.Framework 库的引用。 Windows Phone OS 7.0,Visual Studio 了你这样的警告。 警告 Windows Phone OS 7.1 拿走了。

Silverlight 的任何程序都使用 XNA 类来播放音乐必须包括一类特殊,实现 IApplicationService 并调用 FrameworkDispatcher.Update 每三十分之一秒。 你可以给该类任何的名称,但您会引用它 App.xaml 文件 ApplicationLifetimeObjects 部分中:

<local:XnaFrameworkDispatcherService />

若要播放一首歌,从用户的音乐库,开始通过 MusicLibrary 类实例化。 名为艺术家、 唱片、 流派和播放列表的属性提供的对象的类型的艺术家、 专辑、 流派和播放列表,集合,所有这些类包括歌曲 SongCollection 是宋对象的集合类型的属性。 (这些集合是只读的 ; 您的应用程序不能添加任何用户的音乐库,或以任何方式对其进行修改。)

若要开始玩的东西,使用 MediaPlayer 静态类的成员。 MediaPlayer.Play 方法接受宋对象,SongCollection 或 SongCollection 的索引,指示这首歌开始。

PlayRandomSong 程序包含标记为"播放随机歌曲,"的按钮,当你点击的下面的代码执行:

void OnButtonClick(object sender, RoutedEventArgs args)
{
    MediaLibrary mediaLib = new MediaLibrary();
    AlbumCollection albums = mediaLib.Albums;
    Album album = albums[random.Next(albums.Count)];
    SongCollection songs = mediaLib.Songs;
    Song song = songs[random.Next(songs.Count)];
    MediaPlayer.Play(song);
}

此代码从您的收藏,这张专辑的歌曲,随机提取随机的专辑,并开始播放它。 (Windows Phone 仿真程序包含一张专辑与几个小小的歌曲文件,所以就好在模拟器上运行此程序。)

如果你开始玩 PlayRandomSong 的一首歌,你会发现您可以导航到其他程序或甚至终止程序,这首歌将继续播放。 这是因为如果你玩过这首歌从手机的定期音乐 + 视频应用程序 — 如果您启动该应用程序,你会看到张专辑封面和歌曲的标题。 此外,如果您按电话上的音量控制按钮,您会看到这首歌在屏幕的顶部,获得按钮可暂停或转到开头或结尾的这首歌。

正如手机的音乐 + 视频应用程序知道什么歌玩过的 MediaPlayer.Play,您的应用程序可以确定手机的音乐 + 视频应用程序当前正在播放的歌曲。 此信息是可用从队列属性的 MediaPlayer,它提供一个 MediaQueue 对象,指示当前播放的歌曲,歌曲的集合,如果影集或播放列表玩的。 PlayRandomSong 程序使用计时器来检查队列的 ActiveSong 属性,并显示信息的那首歌。 或者,您可以设置的 MediaPlayer ActiveSongChanged 事件处理程序。

创建对象的歌

PlayRandomSong 程序获取歌曲对象从一个属性或集合的 MediaLibrary,但歌也有一个名为 FromUri,创建一个基于不在您的音乐库中的文件的歌曲属性的静态属性。 此 URI 可以引用在互联网上的音乐文件,或者是该程序的 XAP 文件的一部分。 (不能引用在独立存储中的文件)。然后,您可以使用 MediaPlayer 播放这首歌的对象。 (您无法创建您自己的 SongCollection 对象)。

MediaPlayerDemo 程序显示它如何完成。 这个程序让你玩勃拉姆斯双协奏曲 (从 archive.org/details/BrahmsDoubleConcerto_339 的另一个 1939年录音) 进行的尤金 · 奥曼迪与再次海菲兹、 伊曼纽尔一七三大提琴演奏。 因为你不能使用独立存储的 MediaPlayer,第一个和最后一个动作是 Web 引用。

另一个区别是 MediaElement 的位置属性是可获取和设置,而 MediaPlayer 的 PlayPosition 属性是只可获取。 因此,两个 ApplicationBar 按钮转到开头和结尾的轨道不是合适的。 此外,显然没有去 Song 对象创建这种方式,因此滑块以及无关的持续时间的方式。 我也已经从该程序删除所有的逻辑删除逻辑,因为它不可能启动跟踪你停止的位置。

因为 MediaPlayer 播放 Song 对象获得从背景中的手机的音乐库,你可能会还要在后台播放歌曲的任何对象。 事实并非如此。 在这方面,MediaPlayer 是就像 MediaElement 一样。 音乐停止,一旦您导航到其他应用程序。 但是,如果您离开的 MediaPlayerDemo 程序,并不是逻辑删除 — 经常发生与 Windows Phone OS 7.1 — 音乐只暂停。 当您导航回应用程序时,它拿下来的地方。

我想当你得到了好处,弊,Silverlight MediaElement 是有点超前新华社 MediaPlayer,但汽车­马蒂奇恢复播放的是非常不错的 MediaPlayer 功能。

流与超越

我已经讨论了播放 WMA 中常见的音频和音乐文件和 MP3 格式。 Windows Phone 还允许一个程序来生成动态应用程序内的声音。 在 Windows Phone 编程中,这被称为"流"。我证明我 2011 年 2 月用户界面前沿列中的 SpeakMemo 程序中的一种方法 (msdn.microsoft.com/magazine/gg598930) 使用 XNA DyanamicSoundEffectInstance 类。 您还可以在 Silverlight 中使用 MediaStreamSource 类,做类似的事情。 这是如何实现电子音乐合成的电话。 再次,然而,这些只可由前台应用程序使用。

Windows Phone OS 7.1 年开始,已引入"背景代理人"的概念,和您可以使用此播放或音乐文件或流音频,而您的程序悬浮在背景中。

在下一期,我将讨论如何做到这一点。

Charles Petzold 是长期的特约编辑 MSDN 杂志。他的网站是 charlespetzold.com

多亏了以下技术专家审查这篇文章: Mark Hopkins