OggVorbisとはMP3やWMAなどと同じ音声圧縮技術の1つです。この音声圧縮技術の中で現在最も普及しているのはMP3です。しかしMP3は、個人などが非営利目的で利用する場合は無償で利用できるのですが、商用目的で利用した場合、特許ライセンス料を支払わなければなりません。
そこで開発されたのがOggVorbisです。この音声圧縮技術は特許ライセンスを必要とせず、無償で利用できます。また、一般にMP3よりも高音質で高圧縮といわれています。今回はこのOggVorbisを、DirectMusicで利用する方法を紹介します。
OggVorbisをWaveに変換するには、OggVorbisWin32SDKが必要です。Vorbis.comで入手できます。入手したファイルを解凍し、適当な場所に展開して、インクルードファイルとライブラリファイルのパスを設定します。下記の例はCドライブに展開した時の例です。
OggVorbisをWaveへ変換(デコード)するために「vorbis」フォルダにある「codec.h」「vorbisfile.h」をインクルードし「ogg_static.lib」「vorbis_static.lib」「vorbisfile_static.lib」をリンクします。そして、リンクタブにあるプロジェクトオプションに「/nodefaultlib:"LIBCMT"」を追加します。
#include <vorbis/codec.h> #include <vorbis/vorbisfile.h>
OggVorbisのファイル情報は「ov_open()」で取得します。第1引数には「fopen()」で取得したファイルポインタ、第2引数にはOggVorbis_File構造体のポインタをします。OggVorbisの情報は取得は「ov_info()」で取得します。第1引数にはOggVorbis_File構造体のポインタを指定します。
// デコードするファイル名 char szName[] = "bgm.ogg"; // ファイルを開く FILE *lpFile = fopen( szName, "rb" ); if( lpFile == NULL ) return FALSE; // OggVorbisのファイル情報を取得 OggVorbis_File vf; if( ov_open( lpFile, &vf, NULL, 0 ) < 0 ) { fclose( lpFile ); return FALSE; } // OggVorbisの情報を取得 vorbis_info *vi = ov_info( &vf, -1 ); if( vi == NULL ) { ov_clear( &vf ); return FALSE; }
Waveのヘッダを格納する構造体を用意します。
typedef struct{ char cRIFF[4]; int iSizeRIFF; char cType[4]; char cFmt[4]; int iSizeFmt; WAVEFORMATEX WaveFmt; char cData[4]; int iSizeData; } WAVEFILEHEADER;
Waveのヘッダを格納する構造体のサイズを取得します。「sizeof(WAVEFILEHEADER)」とすると、実際のサイズより大きくなるので、下記のように1つずつサイズを取得します。
WAVEFILEHEADER wh; LONG lWHSize = sizeof(wh.cRIFF) + sizeof(wh.iSizeRIFF) + sizeof(wh.cType) + sizeof(wh.cFmt) + sizeof(wh.iSizeFmt) + sizeof(wh.WaveFmt) + sizeof(wh.cData) + sizeof(wh.iSizeData);
OggVorbisをデコードした時の大きさを求めて、それにヘッダサイズを足し、デコードしたWaveを格納するメモリを確保します。演奏時間は「ov_time_total()」で取得します。
INT iWord = 2; // 量子化バイト数(1 or 2) LONG lDataSize = (LONG)ceil( vi->channels * vi->rate * ov_time_total( &vf,-1 ) * iWord ); CHAR* lpWaveData = new CHAR[ lDataSize + lWHSize ]; if( lpWaveData == NULL ) { ov_clear( &vf ); return FALSE; }
OggVorbisをWaveへ変換(デコード)するには「ov_read()」を使用します。第1引数にOggVorbis_File構造体のポインタ、第2引数にバッファのポインタ、第3引数にバッファサイズ、第4引数に0、第5引数に量子化バイト数、第6引数に1、第7引数にint型変数へのポインタを指定します。戻り値はバッファに書き込まれたバイト数です。
INT iCurrentSection; LONG lWriteSize; LONG lReadSize = 0; while( 1 ) { // デコード lWriteSize = ov_read( &vf, lpWaveData + lReadSize + lWHSize, lDataSize - lReadSize, 0, iWord, 1, &iCurrentSection ); // デコード終了 if( ! lWriteSize ) { break; } // エラー else if ( lWriteSize < 0 ) { /* #define SAFE_RELEASE(p) if(p){p->Release();p=NULL;} */ SAFE_DELETE( lpWaveData ); ov_clear( &vf ); return FALSE; } // 全てデコードできなかった else { lReadSize += lWriteSize; } }
デコードしたWaveのヘッダをメモリ上のヘッダの書き込みます。
// ヘッダの初期化 memcpy( wh.cRIFF, "RIFF", 4 ); wh.iSizeRIFF = lWHSize + lReadSize - 8; memcpy( wh.cType, "WAVE", 4 ); memcpy( wh.cFmt, "fmt ", 4 ); wh.iSizeFmt = sizeof(WAVEFORMATEX); wh.WaveFmt.cbSize = sizeof(WAVEFORMATEX); wh.WaveFmt.wFormatTag = WAVE_FORMAT_PCM; wh.WaveFmt.nChannels = vi->channels; wh.WaveFmt.nSamplesPerSec = vi->rate; wh.WaveFmt.nAvgBytesPerSec = vi->rate * vi->channels * iWord; wh.WaveFmt.nBlockAlign = vi->channels * iWord; wh.WaveFmt.wBitsPerSample = iWord * 8; memcpy( wh.cData, "data", 4 ); wh.iSizeData = lReadSize; // メモリ上のヘッダの書き込み int iSize = 0; memcpy(*lpWaveData, &wh.cRIFF, sizeof(wh.cRIFF)); iSize += sizeof(wh.cRIFF); memcpy(*lpWaveData + iSize, &wh.iSizeRIFF, sizeof(wh.iSizeRIFF)); iSize += sizeof(wh.iSizeRIFF); memcpy(*lpWaveData + iSize, &wh.cType, sizeof(wh.cType)); iSize += sizeof(wh.cType); memcpy(*lpWaveData + iSize, &wh.cFmt, sizeof(wh.cFmt)); iSize += sizeof(wh.cFmt); memcpy(*lpWaveData + iSize, &wh.iSizeFmt, sizeof(wh.iSizeFmt)); iSize += sizeof(wh.iSizeFmt); memcpy(*lpWaveData + iSize, &wh.WaveFmt, sizeof(wh.WaveFmt)); iSize += sizeof(wh.WaveFmt); memcpy(*lpWaveData + iSize, &wh.cData, sizeof(wh.cData)); iSize += sizeof(wh.cData); memcpy(*lpWaveData + iSize, &wh.iSizeData, sizeof(wh.iSizeData));
OggVorbisのファイル情報を「ov_clear()」でクリアします。この関数でファイルポインタを閉じてくれるので「fclose()」は使用しません。
ov_clear( &vf );
MidiとWaveの再生 / 初期化と同じ処理をします。
IDirectMusicSegment8を先程デコードしたWaveから作成します。
// 変換したWaveのサイズ LONG lWaveSize = lReadSize + lWHSize; // パラメータを設定 DMUS_OBJECTDESC desc; ZeroMemory( &desc, sizeof(DMUS_OBJECTDESC) ); desc.dwSize = sizeof(DMUS_OBJECTDESC); desc.dwValidData = DMUS_OBJ_CLASS | // guidClass有効 DMUS_OBJ_MEMORY; // メモリ内からセグメント作成 desc.guidClass = CLSID_DirectMusicSegment; desc.pbMemData = (LPBYTE)lpWaveData; // メモリ内のデータへのポインタ desc.llMemLength = lWaveSize; // メモリ内のデータのサイズ // セグメント作成 IDirectMusicSegment8* g_lpSegment = NULL; g_lpLoader->GetObject( &desc, IID_IDirectMusicSegment8, (LPVOID*)g_lpSegment );
MidiとWaveの再生 / 再生と同じ処理をします。
MidiとWaveの再生 / 終了処理と同じ処理をし、確保したメモリを解放します。
/* #define SAFE_RELEASE(p) if(p){p->Release();p=NULL;} */ SAFE_DELETE( lpWaveData );
ここまでの作業でOggVorbisをWaveに変換することができます。DirectMusicでの再生例のソースを置いておきます。