ホーム < ゲームつくろー! < 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;
}