DirectShowを用いたオーディオファイルのストリーミング再生

ゲーム中でBGMを再生したいとき、MP3やOggVorbisファイルなどの巨大なサイズのファイルを再生する必要が出てくるかもしれません。
これらのファイルをすべてメモリ上にデコードして展開してから再生することも最近のPCなら苦にならないかもしれません。
しかし、メモリを数10MB消費したり、デコード処理で一旦フリーズしてしまったりする可能性があります。
オーディオファイルのサイズに比例してメモり消費量が増大していくのはあまり望ましく無いでしょう。

これを解決するのに一般的に用いられているのがストリーミング再生です。
これは、ファイルを少しずつデコードしてメモリ上に読み込んでいくため、メモリ使用量は一定です。
デコード時間も少しずつのためフリーズしてしまうことはまずないでしょう。

オーディオファイルのストリーミング再生を行う簡単な方法はDirectShowを用いることです。
コーデックがインストールされていることが前提となりますが、大抵のPCにはMP3ファイルの主要なコーデックはインストールされています。

前置きが長くなりましたが、再生手順を記します。

再生には以下の手順を踏みます。

1.プログラミングの準備
DirectShowを使えるようにするために、以下のファイルをインクルードします。

  #include <windows.h>
  #include <dshow.h>

また、以下のライブラリをインポートします。

  #pragma comment(lib, "strmiids.lib")

2.COMオブジェクトの初期化
COMを初期化するために、以下の関数を呼び出します。

  CoInitialize(NULL);

3.グラフビルダの作成
CoCreateInstance関数で、グラフビルダを作成します。

  IGraphBuilder *pGraphBuilder;

  CoCreateInstance(
    CLSID_FilterGraph,
    NULL,
    CLSCTX_INPROC,
    IID_IGraphBuilder,
    reinterpret_cast<lpvoid *>(&pGraphBuilder)
  );

4.フィルタグラフの操作、シークをできるようにする
QueryInterfaceメソッドで、IGraphBuilderからIMediaControl、IMediaSeekingインタフェースを取得します。

  IMediaControl *pMediaControl;
  IMediaSeeking *pMediaSeeking;

  pGraphBuilder->QueryInterface(
    IID_IMediaControl,
    reinterpret_cast<lpvoid *>(&pMediaControl)
  );
  pGraphBuilder->QueryInterface(
    IID_IMediaSeeking,
    reinterpret_cast<lpvoid *>(&pMediaSeeking)
  );

5.再生準備を行う
オーディオファイルからグラフを作成します。

  pGraphBuilder->RenderFile(pFileName, NULL);

6.再生、停止する
これでデータの再生ができるようになったので、以下のメソッドを用いて再生や停止、再生位置の変更を行います。

・再生

  pMediaControl->Run();

・停止

  pMediaControl->Pause();

・再生位置の変更

  pMediaSeeking->SetPositions(
    ¤tTime,
    AM_SEEKING_AbsolutePositioning,
    NULL,
    AM_SEEKING_NoPositioning
  );

7.後始末
不要になったら、以下の処理でオブジェクトを開放します。

・オブジェクトの開放

  pMediaControl->Release();
  pMediaSeeking->Release();
  pGraphBuilder->Release();

・COMの後始末

  CoUninitialize();

以上のような流れになります。
MP3やWMAファイルをはじめMIDIファイルの演奏も出来てしまいます。
恐ろしく簡単ですね。

一方で、OggVorbisファイルなどはコーデックがプリインストールされていない場合が多いため、この状態ではDirectShowを使うことが出来ません。
解決方法としては、ユーザさんにOggVorbis用のコーデックをインストールしてもらう、アプリケーション内で動的にコーデックを作成して適用するなどと言った方法が挙げられます。

DirectShowは非常に便利ですが、各々のPCの環境に依存するところが難でもあります。