Windows Phone用のシンプルなプレーヤーを作成します

この記事では、Windows Phone用の簡単な音楽プレーヤーを作成する方法を示します。



主な機能:プレーヤーはプレイリストで動作し、音楽を制御(曲の切り替え)でき、プレーヤーはバックグラウンドで再生できます。



著者は主に、プレイヤーの執筆の基本的ではあるが常に明らかではないいくつかの側面がカバーされていない記事によって導かれました。 この記事ではそれらを分解しています。





プロジェクト作成







通常のSilverlight for Windows Phoneソリューションを基本として、 SimplePlayerと呼びましょう。







既存のソリューションに、 Windows Phone Audio Playback Agentという形式の別のプロジェクトを追加し、それをAudioPlaybackAgentと呼びましょう。







SimplePlayerプロジェクトAudioPlaybackAgentへのリンクを追加します。





SimplePlayerはメインプロジェクトであり、グラフィカルインターフェイスを含んで維持し、最初に起動します。AudioPlaybackAgentは、トラックを再生するように求められたときにメインプロジェクトから起動される内部Windows Phoneプレーヤーの上のシェルです。

アプリケーションの実行中の2つのプロジェクト間の通信は非常に制限されており、いくつかの制限があります。それらをどのように克服できるかを検討します。



インターフェース設計



デザインを見てみましょう。非常にシンプルで、アプリケーションに必要な機能がすぐにわかります。

これを行うには、インターフェースのレイアウトを含むMainPage.xamlを開きます。

1.次のコードを使用して、 TitlePanelという名前のStackPanelに置き換える必要があります。

<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28"> <TextBlock x:Name="ApplicationTitle" Text="SIMPLE PLAYER" Style="{StaticResource PhoneTextNormalStyle}"/> <TextBlock x:Name="PageTitle" Text="playlist" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/> </StackPanel>
      
      





2. ContentPanelという名前のグリッドを次のコードで置き換える必要があります。

 <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <StackPanel Orientation="Vertical"> <ListBox x:Name="PlayListBox" SelectionChanged="PlayListBox_SelectionChanged"/> <StackPanel Orientation="Horizontal"> <Button x:Name="PrevButton" Content="prev" Height="140" Width="140" Click="PrevButton_Click"/> <Button x:Name="PlayButton" Content="play" Height="140" Width="140" Click="PlayButton_Click"/> <Button x:Name="NextButton" Content="next" Height="140" Width="140" Click="NextButton_Click"/> </StackPanel> </StackPanel> </Grid>
      
      





その後、デザイナーのインターフェイスは次のようになります。





このデザインは完成しました。



プレイリスト



プレイリストをトラックと一緒に保存する方法と、これらのトラックをどこから取得するかについて話します。



mp3またはwma形式のトラックをSipmlePlayerプロジェクトに追加します。 (この例では、1.mp3、2.mp3、3.mp3)







次に、 MainPage.xaml.csファイルを開きます。このファイルには、インターフェイスを提供するコードが表示されています。 MainPageクラスにフィールドを追加します。

 private List<string> playlist;
      
      





同じフィールドをAudioPlayerクラスのAudioPlaybackAgentプロジェクトに追加する必要があります。



このフィールドには、ファイルのリストを保存します。簡単にするために、トラック、アーティスト、アルバムの名前の保存と表示をすぐに拒否します。



プレイリストを静的にコードに保存するのではなく、XMLファイルに保存します。XMLファイルはプレイリストフィールドで逆シリアル化されます。 メインプロジェクトにxmlファイルを追加し、 playlist.xmlという名前を付けます。 コンテンツは次のようになります。

 <?xml version="1.0" encoding="utf-8"?> <ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <string>1.mp3</string> <string>2.mp3</string> <string>3.mp3</string> </ArrayOfString>
      
      





ファイルの別のリストについては、タグを追加、変更、削除するだけです。







注:すべての音楽ファイルとプレイリストファイルで、[ コンテンツ]の[ ビルドアクション] 、[ コピー]の[出力ディレクトリコピー]を常に設定することを忘れないでください。



両方のプロジェクトでplaylist.xmlを利用できるようにします これを行うには、両方のプロジェクトがアクセスできるIsolatedStorageにコピーする必要があります。 現時点では、イベントと現在のトラックに加えて、バックグラウンドエージェントに情報を報告する唯一の方法はストレージです。



プロジェクトからリポジトリにファイルを保存する機能:

 private void CopyToIsolatedStorage(string fullFilePath, string storeFileName) { using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication()) { if (!storage.FileExists(storeFileName)) { StreamResourceInfo resource = Application.GetResourceStream(new Uri(fullFilePath, UriKind.Relative)); using (IsolatedStorageFileStream file = storage.CreateFile(storeFileName)) { const int chunkSize = 4096; byte[] bytes = new byte[chunkSize]; int byteCount; while ((byteCount = resource.Stream.Read(bytes, 0, chunkSize)) > 0) { file.Write(bytes, 0, byteCount); } } } } }
      
      





MainPageクラスに追加する必要がありますが、この記事で詳しく検討することは意味がありません。 次に、コンストラクターから呼び出します。

 CopyToIsolatedStorage("playlist.xml", "playlist.xml");
      
      





そして今、私たちはそれをデシリアライズし、このために、前のものの直後にコンストラクターで呼び出す必要がある関数を書きます。

 private void LoadPlaylist() { using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication()) { using (IsolatedStorageFileStream stream = myIsolatedStorage.OpenFile("playlist.xml", FileMode.Open)) { XmlSerializer serializer = new XmlSerializer(typeof(List<string>)); playlist = (List<string>)serializer.Deserialize(stream); } } }
      
      





同じ関数をAudioPlaybackAgentプロジェクトにコピーし、 AudioPlayerクラスのコンストラクターにその呼び出しを追加する必要があります。





注:この関数では、 System.Xml.Serializationへのリンクを追加する必要があります



LoadPlaylist()の直後にMainPageクラスのコンストラクターで呼び出す必要がある関数を記述することで、音楽ファイルの読み込みを処理します。

 private void LoadMusicFiles() { foreach (var filepath in playlist) { CopyToIsolatedStorage(filepath, filepath); } }
      
      





重要:この方法は最適ではなく、アプリケーションのロードを大幅に遅らせるため、実際のアプリケーションではBackgroundWorkerを使用することをお勧めします。



プレイリストが表示され、ユーザーがそれを操作できるように、コンストラクターのMainPageクラスで、 InitializeComponent()を呼び出した後、次の行を追加します。

 PlayListBox.ItemsSource = playlist;
      
      







イベントハンドラー



XAMLコードを編集したときに、イベントハンドラーを宣言しましたが、それらについては説明しませんでした。 コードを考慮してください:

 private void PlayListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { string filepath = (string)e.AddedItems[0]; BackgroundAudioPlayer.Instance.Track = new AudioTrack(new Uri(filepath,UriKind.Relative),null,null,null,null); BackgroundAudioPlayer.Instance.Play(); } private void PrevButton_Click(object sender, RoutedEventArgs e) { BackgroundAudioPlayer.Instance.SkipPrevious(); } private void PlayButton_Click(object sender, RoutedEventArgs e) { if (BackgroundAudioPlayer.Instance.PlayerState == PlayState.Playing) BackgroundAudioPlayer.Instance.Pause(); else BackgroundAudioPlayer.Instance.Play(); } private void NextButton_Click(object sender, RoutedEventArgs e) { BackgroundAudioPlayer.Instance.SkipNext(); }
      
      





BackgroundAudioPlayerとは何ですか?



実際、これはバックグラウンドエージェントとの唯一の直接接続です。

これらのハンドラーでは、エージェントの動作を設定しますが、エージェントからイベントを受け取ることもできます(たとえば、トラックの停止、再生の開始など)。このため、 MainPageクラスにメソッド追加します。

 void Instance_PlayStateChanged(object sender, EventArgs e) { switch (BackgroundAudioPlayer.Instance.PlayerState) { case PlayState.Playing: PlayButton.Content = "pause"; //  ,    PlayListBox.SelectedItem = BackgroundAudioPlayer.Instance.Track.Source.OriginalString; break; case PlayState.Paused: case PlayState.Stopped: PlayButton.Content = "play"; break; } }
      
      







MainPageクラスのコンストラクターで、 の行を使用してエージェントにバインドします。

 BackgroundAudioPlayer.Instance.PlayStateChanged += Instance_PlayStateChanged;
      
      





BackgroundAudioPlayer.Instance.PlayerStateフィールドには、バックグラウンドエージェントの現在の状態が含まれていることに注意してください。 音楽は一時停止、オーバーなどで再生されます。 BackgroundAudioPlayer.Instance.Positionフィールドもあります。これはファイル内の現在の再生場所を反映しており、便利な場合もありますが、この機能は省略します。



この段階では、プロジェクトはほぼ完了していますが、トラックの切り替えは機能せず、現在のトラックの再生が終了しても次のトラックに移動しません。



AuidioPlayerクラスでは、2つの興味深い関数OnPlayStateChangedOnUserActionを見ることができます。 前者はエージェントの状態の変更を処理し、後者はメインプロジェクトの影響を処理します。 両方とも、次のトラックと前のトラックにフィードするスタブメソッドが呼び出されます。



私たちは実現します:

 private AudioTrack GetNextTrack() { int next = (playlist.IndexOf(BackgroundAudioPlayer.Instance.Track.Source.OriginalString) + 1) % (playlist.Count); return new AudioTrack(new Uri(playlist[next], UriKind.Relative), null, null, null, null); ; } private AudioTrack GetPreviousTrack() { int prev = (playlist.IndexOf(BackgroundAudioPlayer.Instance.Track.Source.OriginalString) -1 + playlist.Count) % (playlist.Count); return new AudioTrack(new Uri(playlist[prev], UriKind.Relative), null, null, null, null); ; }
      
      





作成者が知らない理由により、テンプレートの開発者は、トラックの最後( PlayState.TrackEnded )で、前のトラックを次に再生することを決定しました。この誤解を解決するために、 OnPlayStateChangedハンドラーのplayer.Track = GetPreviousTrack()置き換え ます。 player.Track = GetNextTrack();



ユーザーがトラックを選択しておらず、ボタン(play / next / prev)のいずれかをクリックすると、 AuidioPlayerクラスで現在のトラックが選択されないため、重大な例外が発生する可能性があります。

ユーザーの最初のイベントで、現在のトラックがまだ選択されていない場合は最初に定義し、 OnUserActionメソッドに最初の行を追加します。

 if (player.Track == null) player.Track = new AudioTrack(new Uri(playlist[0], UriKind.Relative), null, null, null, null);
      
      







これで、アプリケーションには、すべてのプレーヤーが必要とする宣言された基本機能がすべて揃っています。



おわりに



プレーヤーの実装はかなり典型的なタスクであり、この記事で単純なプレーヤーの作成方法が明確になることを願っています。



ソースコード: ダウンロード / 表示

記事に基づくサンプルアプリケーション: New Year's Music



All Articles