共用方式為


觸控與執行

在 Windows Phone 7 中播放音訊檔案

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