ホーム < ゲームつくろー! < Ogg Vorbis入門編
Ogg Vorbis入門編
その3 Oggファイルでストリーム再生:サンプルプログラム
Ogg Vorbis入門編その3「Oggファイルでストリーム再生」で説明した内容を踏まえたサンプルプログラムです。実行するとコンソール画面が現れて、指定のOggファイルがストリーム再生されます。
サンプルスクリーンショット。どちらに書き込んでいるかが刻々と表示されます。
このサンプルはOgg VorbisからPCM音声をデコードしてDirectSoundを通してストリーム再生をしています。エスケープを押すと終了できます。
サンプルを動かすために必要なmain.cppファイルはこちらからダウンロードできます(OggSmp_No3.lzh)。もしくは以下のプログラムを空のコンソールアプリケーションに貼り付けても動作します。プロジェクトフォルダに「Test.Ogg」というOggファイルが必要です。お手元の音楽をOggファイルに変換して試してみてください。
○ Ogg Vorbisライブラリについて
このサンプルを動かすにはOgg Vorbisライブラリを組み込む必要があります。組み込み方についてはOgg Vorbis入門編その1「Ogg Vorbisライブラリのインストール」に詳しく記載しておりますのでご参照ください。うまくいかない場合は掲示板にご報告下さい。
// Ogg Vorbisでストリーム再生テスト // // 製作者 : IKD // HP : ○×つくろ〜どっとコム // http://marupeke296.com // #pragma comment ( lib, "ogg_static.lib" ) #pragma comment ( lib, "vorbis_static.lib" ) #pragma comment ( lib, "vorbisfile_static.lib" ) #pragma comment ( lib, "dxguid.lib" ) #pragma comment ( lib, "dsound.lib" ) #include <windows.h> #include <tchar.h> #include <dsound.h> #include "vorbis/vorbisfile.h" // 指定サイズでPCM音声バッファを埋める関数 unsigned int getPCMBuffer( OggVorbis_File *ovf, char* buffer, int bufferSize, bool isLoop, bool* isEnd = 0 ) { if ( buffer == 0 ) { if ( isEnd ) *isEnd = true; return 0; } if ( isEnd ) *isEnd = false; memset( buffer, 0, bufferSize ); int requestSize = 4096; int bitstream = 0; int readSize = 0; int comSize = 0; bool isAdjust = false; if ( bufferSize < requestSize ) { requestSize = bufferSize; isAdjust = true; // 調整段階 } while( 1 ) { readSize = ov_read( ovf, (char*)( buffer + comSize ), requestSize, 0, 2, 1, &bitstream ); if ( readSize == 0 ) { // ファイルエンドに達した if ( isLoop == true ) { // ループする場合読み込み位置を最初に戻す ov_time_seek( ovf, 0.0 ); } else { // ループしない場合ファイルエンドに達したら終了 if ( isEnd ) *isEnd = true; return comSize; } } comSize += readSize; if ( comSize >= bufferSize ) { // バッファを埋め尽くしたので終了 return comSize; } if ( bufferSize - comSize < 4096 ) { isAdjust = true; // 調整段階 requestSize = bufferSize - comSize; } } return 0; // 良くわからないエラー } // コンソールのウィンドウハンドル取得 HWND GetConsoleHwnd(void) { TCHAR pszWindowTitle[ 1024 ]; GetConsoleTitle( pszWindowTitle, 1024 ); return FindWindow( NULL, pszWindowTitle ); } // メイン int _tmain(int argc, _TCHAR* argv[]) { OggVorbis_File ovf; if ( ov_fopen( "Test.ogg", &ovf ) != 0 ) return 0; // Oggファイルの音声フォーマット情報 vorbis_info* oggInfo = ov_info( &ovf, -1 ); // DirectSoundの作成 IDirectSound8 *pDS8; DirectSoundCreate8( NULL, &pDS8, NULL ); pDS8->SetCooperativeLevel( GetConsoleHwnd(), DSSCL_PRIORITY ); // セカンダリバッファ作成作業 int playTime = 1; // 1秒分 vorbis_info *info = ov_info( &ovf, -1 ); // WAVE情報 WAVEFORMATEX waveFormat; waveFormat.wFormatTag = WAVE_FORMAT_PCM; waveFormat.nChannels = info->channels; waveFormat.nSamplesPerSec = info->rate; waveFormat.wBitsPerSample = 16; waveFormat.nBlockAlign = info->channels * 16 / 8; waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; waveFormat.cbSize = 0; // DirectSoundBuffer情報 DSBUFFERDESC DSBufferDesc; DSBufferDesc.dwSize = sizeof( DSBUFFERDESC ); DSBufferDesc.dwFlags = 0; DSBufferDesc.dwBufferBytes = waveFormat.nAvgBytesPerSec * playTime; DSBufferDesc.dwReserved = 0; DSBufferDesc.lpwfxFormat = &waveFormat; DSBufferDesc.guid3DAlgorithm = GUID_NULL; // セカンダリバッファ作成 IDirectSoundBuffer *ptmpBuf = 0; IDirectSoundBuffer8 *pDSBuffer = 0; if ( SUCCEEDED( pDS8->CreateSoundBuffer( &DSBufferDesc, &ptmpBuf, NULL ) ) ) { ptmpBuf->QueryInterface( IID_IDirectSoundBuffer8 , (void**)&pDSBuffer ); } else { pDS8->Release(); ov_clear( &ovf ); return 0; } ptmpBuf->Release(); // バッファをロックして初期データ書き込み void* AP1 = 0, *AP2 = 0; DWORD AB1 = 0, AB2 = 0; if ( SUCCEEDED( pDSBuffer->Lock( 0, 0, &AP1, &AB1, &AP2, &AB2, DSBLOCK_ENTIREBUFFER ) ) ) { getPCMBuffer( &ovf, (char*)AP1, AB1, false ); pDSBuffer->Unlock( AP1, AB1, AP2, AB2 ); } else { pDSBuffer->Release(); pDS8->Release(); ov_clear( &ovf ); return false; } // 再生開始 pDSBuffer->Play( 0, 0, DSBPLAY_LOOPING ); // ストリーム再生 unsigned int size = DSBufferDesc.dwBufferBytes / 2; unsigned int flag = 0; DWORD point = 0; while( 1 ) { Sleep( 16 ); printf( "." ); pDSBuffer->GetCurrentPosition( &point, 0 ); if ( flag == 0 && point >= size ) { // 前半に書き込み if ( SUCCEEDED( pDSBuffer->Lock( 0, size, &AP1, &AB1, &AP2, &AB2, 0 ) ) ) { getPCMBuffer( &ovf, (char*)AP1, AB1, true ); pDSBuffer->Unlock( AP1, AB1, AP2, AB2 ); flag = 1; printf( "\n前半書き込み[%d]\n", point ); } } else if ( flag == 1 && point < size ) { // 後半に書き込み if ( SUCCEEDED( pDSBuffer->Lock( size, size * 2, &AP1, &AB1, &AP2, &AB2, 0 ) ) ) { getPCMBuffer( &ovf, (char*)AP1, AB1, true ); pDSBuffer->Unlock( AP1, AB1, AP2, AB2 ); flag = 0; printf( "\n後半書き込み[%d]\n", point ); } } // Escapeキーを押したら抜ける if ( GetAsyncKeyState( VK_ESCAPE ) ) break; } pDSBuffer->Stop(); pDSBuffer->Release(); pDS8->Release(); ov_clear( &ovf ); return 0; }