オーディオクラスの設計

効果音やBGMなどのオーディオを再生するためのクラス設計について考えてみました。
今更感ありありですが、ご参考程度にまとめました。

■必要なこと
オーディオを再生するためには、以下のプロセスが必要になります。

 オーディオファイルを開く
   ↓
 オーディオを再生できるようにデコードする
   ↓
 オーディオを再生

アプリケーションを配布するとき、PCM形式などの非圧縮ファイルは容量が大きくなるためMP3やOggVorbis形式に圧縮する必要が出てくる可能性があります。
そのため、さまざまな形式のオーディオファイルに対応していることが望ましいです。

また、オーディオファイルを単体で置くとこれをプレイヤーソフトなどで再生できてしまうため、独自の細工を施したり暗号化したりする必要も出てくるかもしれません。
更に、アプリケーション上で動的生成したオーディオデータをメモリ上に展開し、これを再生したい場合もあるでしょう。

以上を踏まえ、モジュールを設計していきたいと思います。

■設計
オーディオファイルを開く処理では、様々なものに対応できることが望ましいです。
これを実現するためには、汎用入力ストリームを用いるのが良いでしょう。
C++で言えばstd::istreamがこれに相当します。

デコード処理では、オーディオデータの入力をレンダラが解釈できるように適切な形式に変換するためのモジュール(デコーダ)を用意すれば良いでしょう。
入力がPCM形式ならそのままレンダラにデータを渡せばよいですが、MP3やOggVorbis形式ではPCM形式に変換(デコード)する必要が出てきます。
デコーダの入力は汎用ストリーム入力、出力はレンダラのバッファとしたモジュールとすれば必要な機能は実現できます。

オーディオ再生では、デコーダより受け取ったオーディオデータをサウンドデバイスに渡す必要があります。
WindowsならwaveOut系のAPI、MacならCoreAudioを使ってサウンドカードにオーディオデータを指定し、実際に音を鳴らします。
これは、デコード後のオーディオデータを受け取るモジュール(レンダラ)を用意すれば必要な機能は実現できます。

これで一通りの流れは網羅できました。
しかし、ここでひとつ大きな問題があります。
それは、レンダラが受け取るオーディオデータの形式を定めていないことです。
デコード後のPCM形式のオーディオデータには44.1kHzや96kHzなどさまざまなサンプリングレートがありますし、そもそもPCM形式でない場合も存在します。

解決方法として、レンダラが受け取るオーディオデータの形式を定めておき、この定められた形式になるようにデコードするやり方があります。
デコーダ自体に仕事の負担が増えてしまいますが、デコーダ内部にサンプリングレートなどを変換するモジュールをラップしておけば問題ないかと思います。
(デコーダとレンダラの間にこのモジュールを置く手もありますが、設計が複雑になりそうなので断念しました。)

以上を踏まえると、オーディオデータの再生の設計は下図のようになります。

audio-play-design

また、クラス図は以下のようになるかと思います。

audio-play-class

クラス図は非常に適当なので悪しからずご了承ください(泣)

入力ストリームは、標準ライブラリのものをそのまま使う形にしました。
デコーダやレンダラは基底クラスから独自の形式を派生させていく感じです。
複数の効果音を鳴らしたい場合は、入力ストリームからレンダラまでの組を複数用意します。

余談ですが、DirectShowでは上記モジュールをフィルタという形で汎用できに扱えるようにしています。
これによって、コーデックさえあればどんな形式でも再生できるのです。
今回のクラスはこのDirectShowの簡易版という位置付けです。

次回からは、上記クラスの実装について説明していきたいと思います。