バックグラウンドでの音楽再生

 Metro Style App は表示されている時だけ動作していて、他のアプリが表示されて非表示になってしまうと動作が中断されてしまいます。
 ビデオ再生などは非表示状態では見えないので中断されても問題ないですが、音楽の場合は非表示になっても再生していて欲しい場合があります。
 今回は、アプリが非表示になってもバックグラウンドで音楽再生を継続する機能を実装してみたいと思います。

 バックグラウンドで音楽再生を行うためには、アプリケーションマニフェストに宣言を追加する必要があります。
 プロジェクトのアプリケーションマニフェストを開き、宣言タブでバックグラウンドタスクを追加します。
 サポートされるタスクの種類でオーディオにチェックを入れて、スタートページを入力します。

 スタートページには音楽再生処理を組み込んだページのxamlファイル名を指定していますが、これは実際に必要なのかわかりません(適当な文字列を指定しても再生はできました)。アプリケーション設定が必須項目なので、一応スタートページを指定しておきます。

 画面には MediaElement を配置します。バックグラウンド再生用なので表示する必要はありません。バックグラウンド再生で使用するために AudioCategory プロパティを BackgroundCapableMedia と設定します。
 再生するファイルを選択するための Button とファイル名を表示する TextBox、再生を開始するための Button も配置します。

<Grid Background="{StaticResource ApplicationPageBackgroundBrush}">
    <MediaElement x:Name="bkMediaElem" Visibility="Collapsed" AudioCategory="BackgroundCapableMedia" AutoPlay="False" />
    <StackPanel Margin="100, 100">
        <Button x:Name="selectButton" Content="Select" HorizontalAlignment="Left" VerticalAlignment="Top" Width="98" Click="selectButton_Click"/>
        <TextBox x:Name="targetText" TextWrapping="Wrap" IsReadOnly="True"/>
        <Button x:Name="playButton" Content="Play" HorizontalAlignment="Left" VerticalAlignment="Top" Width="98" IsEnabled="False" Click="playButton_Click"/>
    </StackPanel>
</Grid>

 続いてコードを追加していきます。
 まず Select ボタン が押されたら、FileOpenPicker を使って音楽ファイルを選択し、選択したファイルを MediaElement に設定します。
 選択後、Play ボタンが押されたら、MediaElement の Play メソッドを呼び出します。

private async void selectButton_Click(object sender, RoutedEventArgs e)
{
    var picker = new FileOpenPicker();

    picker.SuggestedStartLocation = PickerLocationId.MusicLibrary;
    picker.FileTypeFilter.Add(".mp3");
    picker.FileTypeFilter.Add(".m4a");
    picker.FileTypeFilter.Add(".wma");

    var file = await picker.PickSingleFileAsync();
    if (file != null)
    {
        targetText.Text = file.Name;

        bkMediaElem.SetSource(await file.OpenAsync(FileAccessMode.Read), file.ContentType);
        MediaControl.TrackName = file.DisplayName;

        playButton.IsEnabled = true;
    }
}

private void playButton_Click(object sender, RoutedEventArgs e)
{
    bkMediaElem.Play();

    playButton.IsEnabled = false;
}

 MediaElement の Play メソッドを呼び出しただけでは再生されませんので、MediaControl のイベントハンドラを追加します。以下のソースに記述してあるイベントハンドラは全て追加する必要があります(必要なイベントハンドラが登録されていないと再生されません)。
 今回は PlayPauseTogglePressed にのみ処理を追加しています。処理内容は MediaElement の Pause または Play メソッドを呼び出す物ですが、このイベントはUIスレッド以外から呼び出されますので、Invoke が必要です。

public BlankPage()
{
    this.InitializeComponent();

    // 以下の4つ全てのハンドラを登録しないと再生されない
    MediaControl.PlayPauseTogglePressed += MediaControl_PlayPauseTogglePressed;
    MediaControl.PlayPressed += MediaControl_PlayPressed;
    MediaControl.PausePressed += MediaControl_PausePressed;
    MediaControl.StopPressed += MediaControl_StopPressed;
}

void MediaControl_PlayPauseTogglePressed(object sender, object e)
{
    Dispatcher.InvokeAsync(CoreDispatcherPriority.Normal, (s, a) =>
    {
        if (bkMediaElem.CurrentState == MediaElementState.Playing)
        {
            bkMediaElem.Pause();
        }
        else
        {
            bkMediaElem.Play();
        }
    }, this, null);
}

void MediaControl_StopPressed(object sender, object e)
{
}

void MediaControl_PausePressed(object sender, object e)
{
}

void MediaControl_PlayPressed(object sender, object e)
{
}

 全て実装したら実行してみます。
 まず、Select ボタンを押して表示された FileOpenPicker で音楽ファイルを選択し、Play ボタンを押します。
 すると、音楽再生が開始されます。

 この状態でスタート画面に戻ったり、他のアプリ(音楽再生を行わない物)を実行したりしても、再生は継続されます。

 再生中にボリュームアップ/ダウンボタン(一般的なノートPCに存在する物)を押すと、コントローラーが表示され、再生状態を制御できます(デスクトップマシンのキーボードでコントローラーを表示する方法は見つけられませんでした)。
 真ん中のボタンを押すと、PlayPauseTogglePressed のハンドラが呼び出されます。中断や再生の処理を実装しましたので、再生中に押した場合は中断、中断時に押した場合は再生が行われます。

 実際にアプリに組み込む場合は、前後の曲に移動する時のイベントハンドラ等も登録してコントローラーで制御できるようにする事になると思います。