Ogg/OpenALテスト
Table of Contents
2 遊び方
動作確認はWindowsXP SP3と Windows7 SP1 64bit で行いました。W系APIを使用していますのでWindows98 とかでは動かないかも。以下の7zファイルを適当なディレクトリに展開します。
7zファイルを展開して、oggplayer.exeを実行するとプレイヤーウインドが開きます。 OpenAL32.dllとalut.dllを付属したのですが、別途インストール済みであればoggplayer.exe だけあれば実行できると思います。
以下、基本操作です。
- 「File → OGG Open」でファイル選択ダイアログが開きます。適当なOGGファイルを 選べば再生が始まります。ファイルエクスプローラーなどからOGGファイルを ドラッグ&ドロップことでもファイルを指定できます。
- ボタンは 一時停止と再生 のトグルになります。ファイルが選択されていない場合は「non」 と表示されています。
- スライダーは音量のコントロールを行います。
複数のファイルを順次再生するとかはできません(^^;; 実験プログラムなので、興味のある方は 改造して遊んでみてください。
3 プログラムソース
D言語(2.0)で書かれています。
Windows API プロジェクトのWinAPIバインディングを少し手を入れて利用させていただいています。 D言語はコンパイラバージョンが変わると、すぐにコンパイルが通らなくなりますので、 現時点でコンパイルできるセットをwin32-r391c.tar.xzとして勝手に固めさせてもらいました。 そんな訳で、Windowsでしか動きません(^^;
libOgg、libVorbisをリンクしています。mingw用に野良ビルドしたものをスタティックリンク しています。
コンパイラは2012/12時点でgithubでメンテされている GDC を使用しました。 ただし、メインラインのtrunkはMinGW向けにビルドできません。 このため、MinGW向けに venix1さんがforkしているDMD2.060対応されたバージョンに少し手を入れて 使用しています(ビルドの参考)。
$ gdc -v Using built-in specs. COLLECT_GCC=C:\MinGW\gdc031_2060_463\bin\gdc.exe COLLECT_LTO_WRAPPER=c:/mingw/gdc031_2060_463/bin/../libexec/gcc/mingw32/4.6.3/lto-wrapper.exe Target: mingw32 Configured with: ../gcc-4.6.3/configure --build=mingw32 --with-arch=i686 --enable-languages=c,d --prefix=/mingw/gdc031_2060_463 --enable-threads --enable-fully-dynamic-string --enable-libstdcxx-debug --enable-version-specific-runtime-libs --disable-nls --disable-win32-registry --disable-symvers --disable-werror --enable-sjlj-exceptions --with-bugurl=https://bitbucket.org/goshawk/gdc/issue --disable-bootstrap --disable-shared --disable-libgomp --disable-libmudflap Thread model: win32 gcc version 4.6.3 (GCC)
ビルド方法は以下の通り。~/download/ の下にtar.xzアーカイブをダウンロードしたとします。 作業ディレクトリは適当な場所だとします。
xz -dc ~/download/oggplayer_v001_src.tar.xz | tar xvf -
cd oggplayer_v001_src
xz -dc ~/download/win32-r391c.tar.xz | tar xvf -
./mk.sh -a
どちらかと言うとコンパイル環境を整えるのにかなり手間がかかりますが、それをいとわないという方 ならば適当に改造するなどして遊んでみてください。
4 制作メモ
Windowsアプリにしてみると実に地味な感じなのですが、中では意外と面倒臭い事をやる必要があったと思います(^^;;
OpenALでwaveファイルを再生するAPIが存在したのですが、ファイルサイズ分のメモリを バッファとして確保します。それだと実行時に使用するメモリ量があまりにも多すぎるので、どうにか できないか?というのがlibOgg/libVorbisを使用するきっかけでした。
libOgg/libVorbisには音源デバイスにアクセスする機能は無く、PCMデータとして取り出されます。 平たく言うと圧縮ファイルをストリーム読み出しできるライブラリと同等な感じです。 しかし、これをOpenALに逐次流し込むとなると、少々工夫が必要でした。
Oggファイルは ov_fopen()関数でオープンして、ov_read()関数でPCMデータとして逐次読み出しします。 これはfopen(),fread()関数と同じノリだと思います。 OpenALは bufferとsourceという基本概念が存在します。bufferはPCMのデータを保持する為のバッファ、 sourceは音を出す為のチャンネルという感じみたいです。sourceにbufferをくくり付けた後、 sourceに対して再生操作を行うと音が出るという流れです。
ov_read()で読み出したデータをbufferに格納して、sourceにくくり付けた後、sourceに 再生操作を行うという事なのですが、PCMデータの再生にかかる時間は一定という所 を考慮する必要があります。 例えば、PCM再生速度よりもov_read()でデータを得る速度が速いと、 PCM再生待ちデータが溢れる事になります。逆にPCM再生速度よりも ov_read()でデータを 得る速度が遅いと音が途切れてしまいます。そこで、OpenALのalSourceQueueBuffers() というAPIを使って、複数のbufferを 待ち行列にをキューイングします。定期的にキューの 空き状態を監視して、待ち行列が減っていた場合は再び続きのデータをキューに補充する という事を繰り返します。こうすることで、ov_read()でデータを得る速度と、OpenALで PCM再生する速度差を吸収しています。
例えばキューの数が1個しか無いと、頻繁にキューの状態を監視して、空いたら即補充しなくては 音が途切れてしまいます。キューの数が多ければ、しばらく他の事をやってても大丈夫になります。 ただし、PCM再生速度よりもov_read()によるデータ供給が十分に速いのが条件です。
今となっては無い話だと思いますが、仮にoggの読み出しにかかる時間が音を再生する時間よりも 遅い場合、つまり消費よりも供給が遅いと、ループ再生のように無限にデータを供給する必要の あるような場合には、絶対に音が途切れてしまいます。
今回の実験プログラムでは、2048Byteのバッファを最大64個までキューイングするような 構造にしてみました。これだと Pentium4(3.06Ghz)で 1秒間隔のキュー監視&補充で 十分でした。
そんな訳で、ゲームなどでBGMを流すような場合、できるだけバッファに使用するメモリを 減らそうとすると、意外と面倒くさいんだなぁと思いました。