SharpDXを使ってDirectXで音声ファイルを再生するには?[ユニバーサルWindowsアプリ開発]WinRT/Metro TIPS

WindowsストアアプリとPhoneアプリの両者でMediaElementコントロールを使わずに、DirectXをラップするライブラリを使うことで軽量に音声再生を実現する方法を解説する。

» 2014年07月17日 16時15分 公開
WinRT/Metro TIPS
業務アプリInsider/Insider.NET

powered by Insider.NET

「WinRT/Metro TIPS」のインデックス

連載目次

 ユニバーサルプロジェクトでオーディオファイルを再生するには、MediaElementコントロール(Windows.UI.Xaml.Controls名前空間)を使うのが簡単である。しかしMediaElementコントロールは、Windows Phoneのような非力なデバイスにとっては荷が重い。MediaElementコントロールを使わずに軽快にオーディオファイルを再生できないだろうか? 本稿では、DirectXを活用するライブラリなどを用いてそれを簡単に実装する方法を説明する。なお、本稿のサンプルは「Windows Store app samples:MetroTips #83」からダウンロードできる。

事前準備

 ユニバーサルプロジェクトを使ってユニバーサルWindowsアプリを開発するには、以下の開発環境が必要である。本稿では、無償のVisual Studio Express 2013 for Windowsを使っている。

  • SLAT対応のPC*1
  • 2014年4月のアップデート*2適用済みの64bit版Windows 8.1 Pro版以上*3
  • Visual Studio 2013 Update 2*4適用済みのVisual Studio 2013(以降、VS 2013)*5

*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」はデスクトップで動作するアプリ用)。


用語

 本稿では、紛らわしくない限り次の略称を用いる。

  • Windows:Windows 8.1とWindows RT 8.1(2014年4月のアップデートを適用済みのもの)
  • Phone:Windows Phone 8.1

サンプルコードについて

 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コントロールは簡単に利用できるのだが、非力なデバイスでは音が再生されなかったり途切れてしまったりすることがある。また、画面に多数の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による音声ファイルの再生方法

 DirectXの進化に伴い、サウンドを再生する方法も複数存在している。その中で、音声ファイルを再生するには「XAudio2」が適している。XAudio2は、従来の「DirectSound」や「XAudio」を置き換えるものだ。

 XAudio2のプログラミングについて詳しくは、次のドキュメントを参照してもらいたい。

「SharpDX」について

 「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と思われるかもしれないが、しかし次のようなメリットがある。

  • C#/VBのコード中に混在できる
     − C++でWindows Runtimeコンポーネントを作り、それをC#/VBのコードから呼び出すようにするのは、意外と手間が掛かるものだ。C#/VBのコードから利用できるSharpDXでは、そのような手間は必要ない。
  • Any CPUパッケージになる
     − C++でWindows Runtimeコンポーネントを素直に作ると、そのままではCPUに依存したパッケージになってしまう*12。全ての環境に対応するためには、Windowsストアアプリではx64/x86/ARMの3通り、Windows PhoneではARM(実機用)/x86(エミュレーター用)の2通りのパッケージを作成することになる。SharpDXを利用した場合はAny CPUパッケージになるので、そういった煩雑さから解放されるのだ。

*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]」で紹介した。


VBチームの「WavePlayer」クラスについて

 前述したように、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#に翻訳したものも入っている。ただし、今回のサンプルを自分で作ってみる、あるいは自作のプロジェクトでこれを使用する場合には名前空間を変更する必要があることに注意されたい)。

SharpDXを導入するには?

 ソリューションに対して、NuGetからSharpDXのパッケージをインストールする。

 ここでは、ソリューションは次のように作っておいてほしい。

  • C#:ユニバーサルプロジェクト
  • VB:PCLのプロジェクト(共通のコードを置く。ターゲット設定用のダイアログが表示されたら[Windows 8.1]と[Windows Phone 8.1]がチェックされていることを確認して[OK]ボタンをクリックする)、Windowsストアアプリのプロジェクト、Windows Phone Windows Runtimeアプリのプロジェクト(それぞれ、PCLのプロジェクトに参照設定をする。このとき、「この参照は、ファイルアセンブリ参照として扱われる」旨が表示される)

 SharpDXのパッケージは複数あるが、ここではオーディオの再生だけに絞っておこう。それには「SharpDX.Toolkit.Audio」パッケージだけを入れればよい。

 ソリューションエクスプローラーでソリューションを右クリックし、そのメニューから[ソリューションの NuGet パッケージの管理]を選ぶと[NuGet パッケージの管理]ダイアログが表示される。そこで、左のリストで[オンライン]を選び、右上の検索ボックスに「SharpDX」と入力してSharpDXのパッケージを検索する。SharpDX.Toolkit.Audioパッケージが見付かったら、その脇の[インストール]ボタンをクリックする(次の画像)。

[NuGet パッケージの管理]ダイアログでSharpDX.Toolkit.Audioを検索してインストールする(VS 2013) [NuGet パッケージの管理]ダイアログでSharpDX.Toolkit.Audioを検索してインストールする(VS 2013)
ソリューションを右クリックして[ソリューションの NuGet パッケージの管理]を選ぶと、このダイアログが出てくる。左で[オンライン]を選び、右上で検索語句に「SharpDX」と入力すると、このように中央にSharpDXのパッケージが表示される。[SharpDX.Toolkit.Audio]の右にある[インストール]ボタンをクリックすると、SharpDX.Toolkit.Audioパッケージのインストールが始まる。
なお、筆者の環境では一度の検索ではSharpDX.Toolkit.Audioパッケージが検索結果に出てこないことがあった。そのときは、検索を何回か繰り返してみてほしい。

 SharpDX.Toolkit.Audioパッケージのインストールを指示すると、[プロジェクトの選択]ダイアログが表示される(次の画像)。ここは、C#のユニバーサルプロジェクトではWindowsとPhoneの両方のプロジェクトにチェックを入れ、VBのソリューションではPCLのプロジェクトだけにチェックを入れて、[OK]ボタンをクリックしてインストールを実行する。

ユニバーサルプロジェクトのどのプロジェクトにNuGetパッケージをインストールするか問い合わせるダイアログ(VS 2013) ユニバーサルプロジェクトのどのプロジェクトにNuGetパッケージをインストールするか問い合わせるダイアログ(VS 2013)
SharpDX.Toolkit.Audioのインストールを指示するとこのようにダイアログが出てきて、どのプロジェクトにインストールするのかを尋ねられる。ここでは、C#のユニバーサルプロジェクトではWindowsとPhoneの両方のプロジェクトに、VBのソリューションではPCLのプロジェクトだけにインストールする。

 なお、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>

音声ファイルを再生するためのユーザーコントロール(XAML)
どのようなUIでも構わないのだが、複数の音声ファイルを再生できるようにしておいてほしい。

音声ファイルを再生するためのユーザーコントロール(XAMLエディター) 音声ファイルを再生するためのユーザーコントロール(XAMLエディター)

 このユーザーコントロールのコードビハインドで、先ほど紹介した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

ユーザーコントロールのコードビハインドでWavePlayerクラスのインスタンスを管理する(上:C#、下:VB)
ここではインスタンスを破棄するタイミングを、簡易的にUnloadedイベントとした。このユーザーコントロールを利用するページでNavigationCacheModeが有効になっていると、Unloadedイベントで破棄された後に再表示されることがある(すると、音声を再生しようとするとNullReferenceException例外が出る)のでご注意願いたい。

 そうしたら、あとはオーディオファイルの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

WavePlayerクラスを使って音声ファイルを再生するコード(上:C#、下:VB)
ユーザーコントロールに配置した3つのボタンのClickイベントで、別々の音声ファイルを再生するようにした。
VBではアプリとは別のアセンブリ(ここではPCL)に音声ファイルを置いているため、そのパスの書き方がC#とは異なる。アセンブリの名前をパスの先頭に付ける必要がある。

実行結果

 上で作成したユーザーコントロールを、WindowsとPhoneの画面に貼り付けてビルド/実行してみよう(次の画像)。

完成したサンプルアプリ(左:Windows、右:Phoneエミュレーター) 完成したサンプルアプリ(左:Windows、右:Phoneエミュレーター)

 ボタンをタップすると、それぞれ異なる音声が再生される。また、ボタンを連続してタップしたり、複数のボタンを素早くタップしたりしてみると、ちゃんと音が重なって再生されるはずだ。

まとめ

 DirectXを直接使う(=C++でコーディングする)よりも、SharpDXを利用してC#/VBで書いた方が、Any CPUパッケージになるのでユニバーサルWindowsアプリ開発ではありがたい。SharpDXを利用するコードを書くにはかなりのスキルが要求されるのだが、その難しい部分をまとめたWavePlayerクラスのようなオープンソースが利用できるなら、ぜひとも利用するとよい。

「WinRT/Metro TIPS」のインデックス

WinRT/Metro TIPS

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。