WindowsストアアプリとPhoneアプリの両者でMediaElementコントロールを使わずに、DirectXをラップするライブラリを使うことで軽量に音声再生を実現する方法を解説する。
powered by Insider.NET
ユニバーサルプロジェクトでオーディオファイルを再生するには、MediaElementコントロール(Windows.UI.Xaml.Controls名前空間)を使うのが簡単である。しかしMediaElementコントロールは、Windows Phoneのような非力なデバイスにとっては荷が重い。MediaElementコントロールを使わずに軽快にオーディオファイルを再生できないだろうか? 本稿では、DirectXを活用するライブラリなどを用いてそれを簡単に実装する方法を説明する。なお、本稿のサンプルは「Windows Store app samples:MetroTips #83」からダウンロードできる。
ユニバーサルプロジェクトを使ってユニバーサルWindowsアプリを開発するには、以下の開発環境が必要である。本稿では、無償のVisual Studio Express 2013 for Windowsを使っている。
*1 SLAT対応ハードウェアは、Windows Phone 8.1エミュレーターの実行に必要だ。ただし未対応でも、ソースコードのビルドと実機でのデバッグは可能だ。SLAT対応のチェック方法はMSDNブログの「Windows Phone SDK 8.0 ダウンロードポイント と Second Level Address Translation (SLAT) 対応PCかどうかを判定する方法」を参照。なお、SLAT対応ハードウェアであっても、VM上ではエミュレーターが動作しないことがあるのでご注意願いたい。
*2 事前には「Windows 8.1 Update 1」と呼ばれていたアップデート。スタート画面の右上に検索ボタンが(環境によっては電源ボタンも)表示されるようになるので、適用済みかどうかは簡単に見分けられる。ちなみに公式呼称は「the Windows RT 8.1, Windows 8.1, and Windows Server 2012 R2 update that is dated April, 2014」というようである。
*3 Windows Phone 8.1エミュレーターを使用しないのであれば、32bit版のWindows 8.1でもよい。
*4 マイクロソフトのダウンロードページから誰でも入手できる。
*5 本稿に掲載したコードを試すだけなら、無償のExpressエディションで構わない。Visual Studio Express 2013 Update 2 for Windows(製品版)はマイクロソフトのページから無償で入手できる。Expressエディションはターゲットプラットフォームごとに製品が分かれていて紛らわしいが、Windowsストアアプリの開発には「for Windows」を使う(「for Windows Desktop」はデスクトップで動作するアプリ用)。
本稿では、紛らわしくない限り次の略称を用いる。
Visual Studio 2013 Update 2では、残念なことにVB用のユニバーサルプロジェクトのテンプレートは含まれていない*6。そのため、本稿で紹介するVBのコードはユニバーサルプロジェクトではなく、PCL(ポータブルクラスライブラリ)を使ったものである*7。
*6 VB用のユニバーサルプロジェクトは、来年にリリースされるといわれているVisual Studio「14」からの提供となるようだ。「Visual Studio UserVoice」(英語)のリクエストに対する、6月18日付けの「Visual Studio team (Product Team, Microsoft)」からの回答による。
*7 Visual Studio 2013 Update 2のVBでユニバーサルWindowsアプリを作る場合のお勧めは、「The Visual Basic Team」のブログ記事(英語)によれば、PCLを使う方法のようである。PCLに置いたものは、コードだけでなくXAML(画面)やリソースディクショナリなども共通に利用できる。そこで別途公開のサンプルコードでも、VBはWindows用/Phone用/共通コード(PCL)の3プロジェクト構成とした。ユニバーサルプロジェクトで作らなくてもユニバーサルWindowsアプリはリリースできるのである(「WinRT/Metro TIPS:ユニバーサルプロジェクトで開発するには?」参照)。
MediaElementコントロールは簡単に利用できるのだが、非力なデバイスでは音が再生されなかったり途切れてしまったりすることがある。また、画面に多数のMediaElementコントロールを配置すると、全く再生されなくなってしまうことさえある。そこで、Windows Phone 8.xのSilverlightアプリでは、ちょっと面倒ではあるがXNAフレームワークを使って音声ファイルを再生する方法がよく使われている*8。
ところが、Windows Phone 8.1のWindowsランタイムアプリでは、XNAフレームワークがサポートされていない。MediaElementコントロールを使わずにオーディオを再生するには、DirectXを使うことになるのである。
*8 「WinRT/Metro TIPS:アプリに埋め込んだ効果音を鳴らすには?[Win 8/WP 8]」(2012/12/13)参照。また、筆者が公開している「クラウディアさんタイマー」アプリのWindows Phone 8.0版でもXNAフレームワークを使っている。
DirectXの進化に伴い、サウンドを再生する方法も複数存在している。その中で、音声ファイルを再生するには「XAudio2」が適している。XAudio2は、従来の「DirectSound」や「XAudio」を置き換えるものだ。
XAudio2のプログラミングについて詳しくは、次のドキュメントを参照してもらいたい。
「SharpDX」は、Alexandre Mutel氏(現在シリコンスタジオ社所属)が2010年に始めたプロジェクトによって開発が進められているオープンソースのライブラリだ。SharpDXは、ほぼ全てのDirectX APIをカバーして、C#/VBから利用できるようにするラッパーである。デスクトップアプリやWindowsストアアプリ、Windows Phone 8.xのSilverlightアプリやWindows Phone 8.1のWindows Runtimeアプリなどから利用できる*9。
SharpDXは、基本的にDirectX APIをラップしてC#/VBから使えるようにしただけのライブラリなので*10、C++でDirectXをコーディングできるスキルが無いと利用するのは難しい。それならC++で書けばよい*11と思われるかもしれないが、しかし次のようなメリットがある。
*9 本稿執筆時点でのSharpDXの「Stable version」(安定版)は2.6.2である。このバージョンでは、Windows Phone 8.1のWindows Runtimeアプリをビルドできるのだが、Windows Phoneストアの審査に(WACKツールにも)引っ掛かってしまう。次バージョン(現在「Dev Package」となっている2.6.3)で対応されるので、それまで待つのが賢明だ。ただし、SharpDXのサイトからDev Package 2.6.3を入手することはすでに可能なので、自信のある方はその組み込みに挑戦してみてもよいだろう。筆者が公開している「クラウディアさんタイマー」アプリのユニバーサルWindowsアプリ版では、Dev Package 2.6.3を使ってストアの審査に合格している。
*10 簡単なフレームワークも付属している。今回利用する「SharpDX.Toolkit.Audio」のように、名前空間が「SharpDX.Toolkit」で始まっている部分だ。なお、ドキュメントの作成が追い付いていないようである。SharpDX.Toolkit.Audioは今年の5月にリリースされたSharpDX 2.6.0で追加されたのだが、まだドキュメントが見当たらない。
*11 C++で書くコードが実際にどのようなものになるかは、例えばMSDNの「ゲームのプログラミングについてのその他の資料」のページからリンクされている「XAudio2 audio file playback sample」(英語)の中の「XAudio2SoundPlayer.cpp」をご覧いただきたい。
*12 C++で簡単なWindows Runtimeコンポーネントを作るサンプルは、「WinRT/Metro TIPS:シフトJISのデータを読み取るには?[WP 8]」で紹介した。
前述したように、SharpDXのコーディングは難しい。そこで、SharpDXを利用する部分を、C#/VBのクラスやメソッドにまとめておく。その部分を開発するのは大変だが、あとは楽になるだろう。そして、そのようなソースコードがすでに公開されているのだとしたら、利用しない手はない。
本稿で利用する「WavePlayer」クラスは、正にそのようなSharpDXを利用するためのコードだ。マイクロソフトのVisual Basicチームが公開しているコードで、「VB Universal Windows App Part 4: using SharpDX for sound effects」(英語)からダウンロードできる。このクラスは、オーディオファイルを渡すだけで再生してくれる。さらに、一度再生したデータはキャッシュしてくれるので、2回目以降はファイルアクセスをしないという優れものである。
WavePlayerクラスは、上記ページ内の「Download source code "WavePlayer.vb"」リンクからダウンロードできる。これはVBのコードなので、C#で使いたい人は翻訳してほしい(別途公開のサンプルコードにはC#に翻訳したものも入っている。ただし、今回のサンプルを自分で作ってみる、あるいは自作のプロジェクトでこれを使用する場合には名前空間を変更する必要があることに注意されたい)。
ソリューションに対して、NuGetからSharpDXのパッケージをインストールする。
ここでは、ソリューションは次のように作っておいてほしい。
SharpDXのパッケージは複数あるが、ここではオーディオの再生だけに絞っておこう。それには「SharpDX.Toolkit.Audio」パッケージだけを入れればよい。
ソリューションエクスプローラーでソリューションを右クリックし、そのメニューから[ソリューションの NuGet パッケージの管理]を選ぶと[NuGet パッケージの管理]ダイアログが表示される。そこで、左のリストで[オンライン]を選び、右上の検索ボックスに「SharpDX」と入力してSharpDXのパッケージを検索する。SharpDX.Toolkit.Audioパッケージが見付かったら、その脇の[インストール]ボタンをクリックする(次の画像)。
SharpDX.Toolkit.Audioパッケージのインストールを指示すると、[プロジェクトの選択]ダイアログが表示される(次の画像)。ここは、C#のユニバーサルプロジェクトではWindowsとPhoneの両方のプロジェクトにチェックを入れ、VBのソリューションではPCLのプロジェクトだけにチェックを入れて、[OK]ボタンをクリックしてインストールを実行する。
なお、SharpDX.Toolkit.Audioパッケージのサイズは大きいので(インストール後に300Mbytes以上)、インストール完了までかなり時間がかかる。
まず、先ほど紹介したWavePlayerクラスのコードを、C#では共有プロジェクトに、VBではPCLのプロジェクトに配置しておく(別途公開のサンプルコードでは「SharpDxWrapper」というフォルダーの中に配置したが、プロジェクト直下でも構わない)。
次に、ここでは以下のようなユーザーコントロールを作ってみよう(次のコードと画像)。C#では共有プロジェクトに、VBではPCLのプロジェクトに配置する。また、「Assets」という名前のフォルダーを、C#では共有プロジェクトに、VBではPCLのプロジェクトに作成し、そこに適当な音声ファイルを3つ配置しておく(音声ファイルのプロパティで[ビルドアクション]が[コンテンツ]になっていることの確認を忘れずに)。
<UserControl
……省略……
>
……省略……
<Grid>
<StackPanel>
<Button Click="StartButton_Click" Content="開始" Background="Blue" />
<Button Click="EndButton_Click" Content="終了" Background="DarkGreen" />
<Button Click="ErrorButton_Click" Content="失敗" Background="DarkRed" />
</StackPanel>
</Grid>
</UserControl>
このユーザーコントロールのコードビハインドで、先ほど紹介したWavePlayerクラスのインスタンスを保持させる。ユーザーコントロールの初期化時にWavePlayerクラスのインスタンスを生成し、ユーザーコントロールのUnloadedイベントでインスタンスを破棄する(次のコード)。
public sealed partial class MyUserControl : UserControl
{
private WavePlayer _player = new WavePlayer();
public MyUserControl()
{
this.InitializeComponent();
this.Unloaded += MyUserControl_Unloaded;
}
void MyUserControl_Unloaded(object sender, RoutedEventArgs e)
{
_player.Dispose();
}
}
Public NotInheritable Class MyUserControl
Inherits UserControl
Private _player As WavePlayer = New WavePlayer()
Public Sub New()
' この呼び出しはデザイナーで必要です。
InitializeComponent()
' InitializeComponent() 呼び出しの後で初期化を追加します。
AddHandler Me.Unloaded, AddressOf MyUserControl_Unloaded
End Sub
Private Sub MyUserControl_Unloaded(sender As Object, e As RoutedEventArgs)
_player.Dispose()
End Sub
End Class
そうしたら、あとはオーディオファイルのStorageFileオブジェクト(Windows.Storage名前空間)を取得して、WavePlayerクラスのStartPlayメソッドを呼び出すだけだ(次のコード)。
// ファイルのパス(URL表記)を受け取って再生するメソッド
private async void Play(string soundFileUrl)
{
var file = await Windows.Storage.StorageFile
.GetFileFromApplicationUriAsync(new Uri(soundFileUrl));
_player.StartPlay(file);
}
// ボタンのクリックイベントハンドラー(3つ)
private void StartButton_Click(object sender, RoutedEventArgs e)
{
Play("ms-appx:///Assets/01sahajimeru.wav");
}
private void EndButton_Click(object sender, RoutedEventArgs e)
{
Play("ms-appx:///Assets/03syuuryou.wav");
}
private void ErrorButton_Click(object sender, RoutedEventArgs e)
{
Play("ms-appx:///Assets/34sippai.wav");
}
' ファイルのパス(URL表記)を受け取って再生するメソッド
Private Async Sub Play(soundFileUrl As String)
Dim file = Await Windows.Storage.StorageFile _
.GetFileFromApplicationUriAsync(New Uri(soundFileUrl))
_player.StartPlay(file)
End Sub
' ボタンのクリックイベントハンドラー(3つ)
Private Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
Play("ms-appx:///MetroTips083VB.Shared/Assets/01sahajimeru.wav")
End Sub
Private Sub EndButton_Click(sender As Object, e As RoutedEventArgs)
Play("ms-appx:///MetroTips083VB.Shared/Assets/03syuuryou.wav")
End Sub
Private Sub ErrorButton_Click(sender As Object, e As RoutedEventArgs)
Play("ms-appx:///MetroTips083VB.Shared/Assets/34sippai.wav")
End Sub
上で作成したユーザーコントロールを、WindowsとPhoneの画面に貼り付けてビルド/実行してみよう(次の画像)。
ボタンをタップすると、それぞれ異なる音声が再生される。また、ボタンを連続してタップしたり、複数のボタンを素早くタップしたりしてみると、ちゃんと音が重なって再生されるはずだ。
DirectXを直接使う(=C++でコーディングする)よりも、SharpDXを利用してC#/VBで書いた方が、Any CPUパッケージになるのでユニバーサルWindowsアプリ開発ではありがたい。SharpDXを利用するコードを書くにはかなりのスキルが要求されるのだが、その難しい部分をまとめたWavePlayerクラスのようなオープンソースが利用できるなら、ぜひとも利用するとよい。
Copyright© Digital Advantage Corp. All Rights Reserved.