Monthly Archives: 4月 2014

ステージ3のBGM制作中

Departure from the Voidの新ステージの制作状況のお知らせです。
ステージの制作はまだ構想段階ですが、先にBGMの制作を進めているところです。
新ステージは暗闇のステージとなるため、暗闇の中を疾走するようなイメージのBGMにしたいと考えているところです。

だいぶ前に作った酷いミックスのデータですが、以下に置いておきます。

今現在は、曲全体のメロディを打ち込んでいるところです。
また、ミックスが酷いため、ここら辺の調整も行っていく予定です。
上記のBGMはMP3データですが、ゲームを配布するときはBGMがMIDIデータのものとMP3(またはOggVorbis)版のものを二種類配布したいと考えています。
前者は容量重視、後者は音質重視という位置付けです。

BGMを先に作ってそこに合わせてステージを組み上げていくスタイルで制作を進めていきたいと思います。

オーディオクラスの設計

効果音や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の簡易版という位置付けです。

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

SMFの読み込み

前回の記事でSMF(Standard Midi File)の構造について解説しました。
これを踏まえ、今回はSMFを読み込むプログラムについて解説していきます。

SMFはヘッダチャンクの後にトラックチャンクが続く構造になります。
したがって、これらを順番に読み込んでいけば良いです。

読み込み処理の大枠は以下のようになります。

ReadHeader()がヘッダチャンクを読み込むメソッド、ReadTrack()がトラックチャンクを読み込むメソッドです。
トラックチャンクはヘッダチャンク内のトラック数(m_header.trackNum)だけ格納されているため、トラックの数だけReadTrack()を呼び出しています。

ReadHeader()メソッドの実装は以下のようになります。

チャンクタイプとデータ長は決まっているため、いずれも読み込んだ後にチェックするようにしています。
読み出されるヘッダ長、フォーマット、トラック数、時間単位はビッグエンディアンであるため、リトルエンディアンに変換しています。
今回はフォーマット0と1のみ扱うことにするため、それ以外のフォーマットの場合はエラーとします。
フォーマット0の場合は単一のトラックしか存在してはいけないため、複数あった場合はエラーとします。

ReadTrack()メソッドの実装は以下のようになります。

ヘッダチャンクの読み込み同様、まずチャンクタイプのチェックを行い、トラックのデータ長を読み込んでリトルエンディアンに変換しています。
その後にはイベントが続くため、イベントの読み込みをループ内で行っています。
イベントの末尾には終了イベントが入っているため、これが来たらループを抜けてトラックの読み込みを終了します。

イベントを読み込むループ内では、ReadDelta()でデルタタイムを読み込んだ後にReadEvent()でイベントを読み込んでいます。

ReadDelta()、ReadEvent()メソッドの実装は以下のようになります。

データの構造については前回の記事をご参照下さい。
注意すべきところは、ReadEvent()メソッド内でランニングステータスが来た場合の動作です。

MSBが0のときはステータスバイトが省略されてランニングステータスとなるため、
in.putback()でストリームポインタをひとつ戻して以前のステータスバイトの値を採用しています。

メタイベントの読み込みでは、イベント長チェックもしっかりと行うようにしています。

上記を踏まえ、SMFを読み込むまでのプログラムを以下に記しておきます。

SmfData.h

SmfData.cpp

Main.cpp

プロジェクトフォルダ上にtest.midファイルを置くとこのファイルを読み込みます。

このように、多少の手間はかかりますが、一度読み込み処理をクラス化してしまえば楽です。
MIDI再生を行うには複数のトラックをマージする必要があり、更に手間が増えますが
これもワンパターンなのでクラス化すれば問題ありません。

■参考サイト
EternalWindows MIDI / MIDI再生サンプル

SMFの構造

SMF(Standard Midi File)はMIDIの演奏データを扱う際に標準的に扱われているファイル形式です。
.midまたは.midi拡張子のファイルがそれです。

MIDIではイベントを所定のタイミングで演奏者(電子楽器や音源など)に送信して音楽を演奏する訳ですが、そのタイミングを定義した楽譜となるデータを用意する必要があります。
一方で、さまざまなソフトウェア間で共通で扱える形式が望ましいです。
このデファクトスタンダードとして生まれたのがSMFです。

WindowsMediaPlayerをはじめ、さまざまなプラットフォームでSMFを再生できます。
MIDI再生用の高レベルAPIも用意されており、再生目的ならそちらを利用したほうが良いでしょう。
しかし、MIDI再生に対して独自のエフェクトやテンポの変更、ループ再生などを行おうとした場合、上記のAPIでは対応できなくなる場合が存在します。
そのような場合にはSMFの構造を把握した上で色々と処理する必要が出てきます。

今回はこのような用途を念頭においてSMFの構造について解説していきます。

■SMFの全体像
SMFは大まかに以下のような構造になっています。

 [“MThd”][ヘッダ長][ヘッダデータ]
 [“MTrk”][トラック長][トラックデータ]
 [“MTrk”][トラック長][トラックデータ]
  ・・・

1行目にはSMFのヘッダ、2行目以降にはSMFの演奏情報が格納されたトラックが入ります。
このファイル構造はチャンクと呼ばれる構造になっており、WAVEファイルもこの形式を用いています。

■ヘッダの構造
ヘッダの構造は以下のようになります。

 [“MThd” (4byte)]
 [ヘッダ長(必ず6) (4byte)]
 [フォーマット(0~2) (2byte)]
 [トラック数 (2byte)]
 [時間単位 (2byte)]

最初の4byteはチャンクタイプ、次の4byteにはデータ長が入ります。
チャンクタイプは必ず”MThd”となります。
データ長にはチャンクタイプとデータ長を除いたヘッダの全体サイズが入ります。
したがって、必ず6になります。
SMFかどうかはチャンクタイプとデータ長が前述の値になっているかどうかで判断できます。

フォーマットにはSMFのフォーマット情報が入ります。
フォーマットは0,1,2の3種類が定義されています。

 フォーマット0
  単一のマルチチャネルトラック
 フォーマット1
  複数同時に再生されるシーケンストラック
 フォーマット2
  複数のシーケンス的に独立した単一のトラックパターン

このうちフォーマット2はほとんど使用されていないため、通常はフォーマット0と1の場合のみ考慮すればよいかと思います。

トラック数にはMIDI演奏者となるチャネルの数が格納されます。
MIDIでは1チャネルが1つの演奏者に相当し、最大で16チャネルまで同時に演奏することができます。

時間単位にはMIDIイベントを送信する時間間隔の分解能が入ります。
この値が大きいほどより細かなタイミングでMIDIイベントを送信できるようになります。

■トラックの構造
トラックの構造は以下のようになります。

 [“MTrk” (4byte)]
 [データ長 (4byte)]
 [イベント]
 [イベント]
 [イベント]
  ・・・

先頭4byteには必ず”MTrk”が入ります。
次の4byteにはトラックのデータ長が入ります。これはトラック全体サイズ-8となります。
データ長以降にはイベントが連なっています。
このイベントは以下の構造になっています。

 [デルタタイム(1~4byte)][イベント]

デルタタイムにはイベントを発生させるまでの待機時間が入ります。
このデルタタイムは1~4byteの可変長となり、以下のような構造になります。

 [0AAAAAAA]
  → AAAAAAA
  or
 [1AAAAAAA][0BBBBBBB]
  → AAAAAAABBBBBBB
  or
 [1AAAAAAA][1BBBBBBB][0CCCCCCC]
  → AAAAAAABBBBBBBCCCCCCC
  or
 [1AAAAAAA][1BBBBBBB][1CCCCCCC][0DDDDDDD]
  → AAAAAAABBBBBBBCCCCCCCDDDDDDD

最上位ビット(MSB)が0ならそこでデルタタイムのデータは終了です。
そうでない場合はデルタタイムのデータが続きます。
取得したA~DビットのMSBを除外して詰めたものがデルタタイムの値となります。

このデルタタイムを用い、以下の式により待機時間[ms]が算出できます。

 待機時間 = デルタタイム × テンポ / 1000 / 時間単位

デルタタイムがこのような形式になっている理由は、SMFのファイルサイズ削減のためです。
もしデルタタイムが常に4byte固定なら、データサイズが無駄に大きくなってしまいます。

イベントには以下3種類が存在します。

 [MIDIイベント] or [SysExイベント] or [メタイベント]

MIDIイベントは各チャネルに対して送信するメッセージです。
SysExイベントはシステム固有イベントです。
メタイベントはテンポの変更や歌詞などの情報です。

MIDIイベントは以下のような構造になっています。

 [ステータスバイト(1byte)][データバイト1(1byte)][データバイト2(1byte)]
  or
 [ステータスバイト(1byte)][データバイト1(1byte)]

データバイトはステータスバイトの上位4bitによって1または2つに変わります。
ステータスバイトは以下の構成になります。

 [1AAABBBB]
  A – ステータス
  B – チャネル

MSBが1に固定されている理由は、ステータスバイトが省略される可能性があるためです。(ランニングステータス)
ステータスバイトが省略される場合、MSBは必ず0になります。

データバイトの個数は以下のように定められています。

 1AAAが0x8 or 0x9or 0xA or 0xB or 0xEのとき
  →2個
 1AAAが0xC or 0xDのとき
  →1個
 1AAAが0xFのとき
  →SysExイベント or メタイベント

1AAAが0xFのときはMIDIイベントではなく、後述するSysExまたはメタイベントである可能性があります。
SysExイベントかメタイベントのどちらであるかの判定は以下のようになります。

 1AAABBBBが0xF0のとき
  →SysExイベント
 1AAABBBBが0xFFのとき
  →メタイベント

SysExイベントの構造は以下の通りです。

 [イベント長(1~4byte)][0xF0(1byte)][イベントデータ(イベント長byte)]

データ長はデルタタイムと同様の方式で格納されています。
0xF0はSysExイベントを表す固定値です。
送信するSysExイベントは0xF0とイベントデータのかたまりになります。

メタイベントの構造は以下の通りです。

 [イベントタイプ(1byte)][イベント長(1~4byte)][イベントデータ(イベント長byte)]

イベントタイプにはメタ情報の種類が入ります。
この種類に応じてイベント長が以下のように定められています。

 イベントタイプが0x01~0x07 or 0x7Fのとき
  →イベント長は可変長
 イベントタイプが0x20のとき
  →イベント長は1
 イベントタイプが0x2Fのとき
  →イベント長は0
 イベントタイプが0x51のとき
  →イベント長は3
 イベントタイプが0x54のとき
  →イベント長は5
 イベントタイプが0x58のとき
  →イベント長は4
 イベントタイプが0x59のとき
  →イベント長は2
 イベントタイプが上記以外
  →エラー

上記で定められたイベント長に反する場合は不正なファイルとみなすことが出来ます。
イベント長はSysExイベント長とデルタタイム同様の形式で入っています。

トラックの末尾には必ずイベントタイプが0x2Fのメタイベントが入ります。
これはトラック終了イベントです。

以上駆け足になってしまいましたが、このSMFの構造を理解することで、SMFからMIDIイベントを解析してMIDIデータを再生できるようになります。
次回はSMFから実際にMIDIデータを読み込むプログラムについて解説したいと思います。

■参考サイト
SMF 【 Standard MIDI File 】
http://homepage3.nifty.com/sabanisoft/pgroom/midi.txt
SMF (Standard MIDI Files) の構造
SMF フォーマット解説
EternalWindows MIDI / MIDI再生サンプル(フォーマット0のみ)

暗号化処理の実装苦戦中・・・

Departure from the Voidの次回リリースに向け、ゲームデータを暗号化して配布する処理を実装しています。
これまでに作成したソースファイルが酷いおかげで実装に苦戦している状況です。
こんなことにはならないように設計はきちんと行うべきという教訓ですね・・・

暗号化処理のため詳細は明かすことが出来ませんが、以下のような設計を考えています。

dpv-enc-1

暗号化されてばらばらになっているゲームリソース(画像やBGMなど)のファイルを復号器によって復号化し、アプリケーションに渡す方法です。
この方法を用いることで、アプリケーション側は暗号化されたファイルや復号化方法などを気にせずにリソースデータを扱えるようになります。

これを今までは以下のように個々の暗号化されていないリソースファイルにアクセスしてリソースデータを読み込むようにしていました。

dpv-enc-2

上記一つ一つの処理を初めの図に示した方法に置き換えなければならないため大変な修正になります。

もしこれを初めの図のように何らかのモジュールをワンクッション挟んでおけば容易に機能の実装ができます。
ゲームを配布するときは第三者にゲームデータが行き渡ってしまうので、望ましくない閲覧を避けるためにも暗号化は必要不可欠な機能ですね。

Excelの表をWordPressに貼り付ける ‐ XLS2HTMLTableを使う

Excel2013の表をWordPressの記事に貼り付ける方法のご紹介です。
今回はExcelの表をHTMLファイルに出力できるExcelアドイン「XLS2HTMLTable」を使った貼り付け方法について説明します。

1.ダウンロード
XLS2HTMLTableアドインを下記サイトよりダウンロードします。

http://www.vector.co.jp/soft/win95/net/se223452.html

ダウンロードしたファイルはお好みの場所に解凍してください。
本手順では「C:UsersftvoidAppDataRoamingMicrosoftAddIns」下に解凍します。

2.アドインの有効化
Excelを開き、「ファイル」→「オプション」を選択します。

excel-tbl-1

「アドイン」を選択し、「管理(A)」を「Excel アドイン」にして「設定」ボタンをクリックします。

excel-tbl-2

「参照(B)」ボタンをクリックし、解凍したディレクトリ下のアドインファイル「XLS2HTMLTable.xla」を選択します。

excel-tbl-3
excel-tbl-4

「Xls2Htmltable」にチェックを入れ、「OK」ボタンをクリックします。

excel-tbl-5

最後に、アドインを有効にするためにExcelを再起動します。
これでアドインが使用できるようになります。

3.HTMLタグへの変換
XLS2HTMLTableを使ってエクセル内の表をHTMLタグに変換します。
変換したい表を選択し、「アドイン」→「XLS2HTML Table」を選択します。

excel-tbl-6

表の設定画面が出てきます。
ここで表のサイズや色などを設定できます。
今回はデフォルトのままにして「Execute!」ボタンをクリックします。

excel-tbl-7

変換されたHTMLタグが表示されます。
「Copy All」ボタンをクリックしてタグ全体をクリップボードにコピーします。

excel-tbl-8

WordPressの記事編集エリアに貼り付けます。
ここで、貼り付けたタグの先頭と末尾に存在するコメントは削除します。

excel-tbl-9

これで記事のプレビューを表示すると表が挿入されていることが確認できます。

excel-tbl-10

表の縦線が表示されていませんが、これは表のスタイル設定によるものです。
CSSファイルにてスタイルの設定を行うときちんとした表が表示されるようになります。

今回は以下のスタイルを定義して表を表示させてみました。

先ほど貼り付けた表のtableタグには忘れずにCSSで定義したクラスを指定しておきます。
これで以下のように表がきちんと表示されるようになります。

excel-tbl-11

セルの境界線などを完全に再現するわけではありませんが、
表を素早く作成できる点がGoodですね!

■参考サイト
Excel2013(エクセル2013)基本講座:分析ツール(アドイン)を組み込む
Excelで作成した表組みをWordPressに取り込む

新しいタイトルデザイン考案中

Departure from the Voidの現状のタイトルデザインがどうもイマイチなので、
後々のバージョンアップで一新しようかどうかと考えています。

現状のデザインは以下の通りです。

dpv-old-titletheme

新しいデザインはまだ考案中ですが、以下のようなものを考えています。

dpv-new-titletheme

画面は作成途中のものです。
ゲームのグラフィックにあわせて黒基調のデザインにしようとしています。

タイトルデザインの変更は次回のリリース時に行えればと思います。

DirectSoundでWAVEファイルを再生する

前回はDirectSoundによるOggVorbisファイルの再生について書きましたが、
今回はDirectSoundによるWAVEファイルの再生について書きます。

全体的な処理の流れは以下のようになります。

 WAVEファイルオープン
   ↓
 WAVEファイルのフォーマット・デコード後サイズ取得
   ↓
 DirectSoundの初期化
   ↓
 WAVEファイルのデータをDirectSoundバッファにデコード
   ↓
 バッファの再生
   ↓
 後始末

OggVorbisファイルの時と処理の流れはほぼ一緒です。

以上の流れのソースコードを以下に記します。

WAVEファイルはRIFFファイルとして定義されています。
WAVEファイルの読み込みにはmmio~関数を用います。
これはWindowsのマルチメディア操作用関数のことで、RIFFファイルを比較的楽に扱うことが出来ます。

RIFFファイルはWAVEファイルに限らずたとえばMP3などのサウンドデータもサポートします。

これらのAPIを使用するためにはwinmm.libをインポートする必要があります。

WAVEファイルはmmioOpen関数で開きます。

ファイルを開いたら、WAVEFORMATEX構造体データと実データを読み込みます。
今回は読み込み処理を示すだけにして詳細の説明は割愛させていただきます。

ここまで出来たら、後はDirectSoundバッファにデータを読み込んで再生できるようになります。

OggVorbisファイルの再生同様にワンパターンな処理なので、関数化やクラス化して使ったほうが良いでしょう。

■参考サイト
RIFFファイルフォーマット
WAVEの再生

OggVorbisデータを汎用ストリーム経由で再生する

OggVorbisライブラリは汎用ストリームに対応しています。
C++でいうiostream、istream、ostreamに相当します。
今回はistreamを用いてOggVorbisデータを再生してみます。
この汎用ストリームを用いることで、ファイル以外にメモリ上やネットワークからOggVorbisデータを読み込み、再生することが可能になります。

OggVirbisデータをストリームでオープンするにはov_open_callbacks()関数を使います。
これは以下のフォーマットになっています。

第1引数のdatasourceにはアプリケーションで使うデータへのポインタを指定します。
これは後述するコールバック関数に渡されるポインタです。
したがって、ストリームのインスタンスなどを指定すればよいです。

第2引数のvfにはOggVorbis_File型の変数へのポインタをしていします。

第3引数のinitialはとりあえずNULLで構いません。
第4引数のibytesには0を指定します。

第5引数のcallbacksには次の構造体データへのポインタを指定します。

この関数ポインタからコールバック関数を呼び出します。
ov_open_callbacks()関数の呼び出し以降は通常通りにOggVorbisデータを扱うことが出来るようになります。
重要なのは初期化の部分です。

以上を踏まえ、istream経由でOggVorbisファイルを再生するプログラムを以下に記します。

今回のポイントは次の部分です。

ifstreamでOggVorbisファイルを開き、それをov_open_callbacksの第1引数に指定しています。
ov_callbacks構造体には自前で用意した関数を指定しています。
これらの関数は以下のように実装しました。

Callback_Read()はデータ読み込み、Callback_Seek()はシーク、Callback_Close()はストリームクローズ、Callback_Tell()はストリーム位置を返す関数となります。
上記プログラムでは、istreamを用いてこれらの動作を実現させています。

このように、汎用ストリームでの再生はすんなりと出来ます。
因みに、ov_open()関数は以下のように実装されています。

上記より、C言語の標準ライブラリのファイル操作関数を直で指定していることが分かります。

このように、OggVorbisライブラリは最初から汎用ストリームを意識して設計されているので融通が利きますね。

■参考サイト
その5 メモリにあるOggファイルを再生する
https://svn.xiph.org/trunk/Tremor/vorbisfile.c

OggファイルをDirectSoundで再生する

OggVorbisファイルをDirectSoundで再生する方法のメモ書きです。

全体的な処理の流れは以下のようになります。

 OggVorbisファイルオープン
   ↓
 OggVorbisファイルのフォーマット・デコード後サイズ取得
   ↓
 DirectSoundの初期化
   ↓
 OggVorbisファイルをDirectSoundバッファにデコード
   ↓
 バッファの再生
   ↓
 後始末

以上の流れのソースコードを以下に記します。

長い上汚いコードになってしまいました(泣)
実際に使うときはこのようなベタ書きはせずにクラス化や関数分割しましょう。

順を追って説明していきます。

1.ヘッダファイル、ライブラリの準備

まず、必要なヘッダファイルとライブラリをインクルードします。

OggVorbisのAPIを使用するためには、vorbis/codec.hとvorbis/vorbisfile.hをインクルードします。
また、libogg_static.lib、libvorbis_static.lib、libvorbisfile_static.libをインポートします。

DirectSoundのAPIを使用するためには、dsound.hをインクルードし、dsound.libをインポートします。
dxguid.libをインポートしているのは、DirectSoundのCOMオブジェクトのGUIDを扱えるようにするためです。

2.OggVorbisファイルの読み込み準備
OggVorbisファイルを開いて必要な情報を読み込みます。
この時点ではまだデータのデコードは行いません。

3.再生用ウィンドウの作成
DirectSoundの再生対象となるウィンドウを作成します。
簡単のため、何も無いウィンドウにします。

4.DirectSoundの初期化
DirectSoundを初期化します。
ここで再生対象のウィンドウハンドルを指定し、再生用バッファの作成を行います。

バッファの作成はほぼ処理が決まっています。
今回はデコード後全体のサイズ分のバッファを作成しています。

5.OggVorbisデータをDirectSoundバッファにデコードする
バッファの作成が出来たため、いよいよデコードを実行します。
作成したバッファにOggVorbisデータをデコードします。

今回のプログラムの場合、デコードしたらOggVorbisファイルへのアクセスは必要なくなるため、ファイルを閉じます。

6.再生
デコードされたデータが格納されたDirectSoundバッファを再生します。

再生中は空ウィンドウを表示させ続けるようにします。
今回はウィンドウを閉じると以降の後始末を行うようにしました。

7.後始末
再生を停止してからDirectSoundオブジェクトの後始末を行います。
これによりDirectSound関連のリソースはすべて開放されます。

以上のような流れでOggVorbisファイルをDirectSoundで再生できます。
既にお気づきの方もいらっしゃるかもしれませんが、OggVorbisのデコード処理はループ文の中で行っています。
したがって、ストリーミング再生も簡単に実現できます。
サイズの小さな効果音ファイルの再生では、メモリ上にデータをすべて展開しても問題ないですが、BGMファイルのような巨大なファイルの場合はストリーミング再生が必要不可欠になるでしょう。