過去ログ

アフィリエイト広告を利用しています

いまさらはじめるDirectX9 その3 OGGを再生する2

ということで昨日のまとめ。
案外oggの参考になるところが見つけられなかった。というか資料無くても普通はもっとスムーズに出来るんだろうな。orz

  1. ファイル→新規作成→プロジェクトを選択
  2. Visual C++を選択し、Win32コンソールアプリケーションを選ぶ。プロジェクト名や場所は適宜選択。(今回はoggplayとした)
  3. ウィザードが立ち上がるので次へを選択。
  4. コンソールアプリケーション、プリコンパイル済みヘッダーを選択されていることを確認。完了ボタンを押す
  5. oggplay.cppに下記のコードを入力する。
  6. oggplay.cppが置いてあるフォルダ内に「libogg.lib」、「vorbis.lib」、「vorbisfile.lib」を入れる
  7. oggplay.cppが置いてあるフォルダ内にliboggとlibvorbisの下にあるincludeフォルダをコピーする
  8. プロジェクト→プロパティを選択。
  9. 構成プロパティ→デバッグ→作業ディレクトリに「..\debug」を追加*1
  10. 構成プロパティ→C/C++→全般→追加インクルードディレクトリに「.\include」を追加
  11. 構成プロパティ→リンカ→入力→追加の依存ファイルに「vorbis.lib libogg.lib winmm.lib vorbisfile.lib」を追加
  12. ビルド→oogplayのビルドを選択する。
  13. oggplay.exeが出来ているdebugフォルダに「libogg.dll」、「vorbis.dll」、「vorbisfile.dll」、「test.ogg」をコピーする
  14. Visual Studio上でF5押して実行。
  15. 曲がなれば成功。


ソースコード

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <vorbis/vorbisfile.h>
#include <windows.h>


#define OGG_FILE "test.ogg"
#define BUFFER_SIZE (1024 * 1024) /* 1024 kBytes */
#define BUF_COUNT 3

static bool last = false;
static int bitstream;
 
static char buffer[BUF_COUNT][BUFFER_SIZE];
WAVEHDR wavehdrs[BUF_COUNT];
/* if WAVEHDR::dwUser is 1, this is last data. */
 
int bufferIdx = 0;
int playIdx = 0;
 
/**
 *   バッファをデコードした PCM で埋めます。
 *   @param   vf     デコードのための Vorbisfile 構造体へのポインタ
 *   @param   wavehdr     PCM を格納するバッファを管理する WAVEHDR 構造体へのポインタ
 */
long fillBuffer(OggVorbis_File *vf, WAVEHDR *wavehdr)
{
    long count = 0;
    int ret;
    char *buffer = (char *)wavehdr->lpData;

    while (count < BUFFER_SIZE) {
        ret = ov_read(vf, buffer + count, BUFFER_SIZE - count, 0, 2, 1, &bitstream);
        if (ret <= 0) {
            wavehdr->dwUser = 1; /* End of data. */
            break;
        }
        count += ret;
    }
    wavehdr->dwBufferLength = count;
    return count;
}
 
/**
 *   Windows メディア API で使用されるコールバック。
 */
void CALLBACK waveCallback(HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
    static int winret;

    switch(uMsg) {
        case WOM_CLOSE:
            break;
        case WOM_DONE:
            /* 再生 */
            winret = waveOutWrite(hwo, &(wavehdrs[playIdx]), sizeof(WAVEHDR) );
            if (winret != MMSYSERR_NOERROR) {
                exit(1);
            }

            _tprintf(_T("Callback called.\n") );
            Sleep(1000);

			{
				OggVorbis_File *vf = (OggVorbis_File *)dwInstance;
				/* 最終データが再生完了 */
				if (last) {
					/* VorbisFile 後処理 */
					ov_clear(vf);

					/* サウンド出力後処理 */
					for (int i = 0; i < BUF_COUNT; i++) {
						waveOutUnprepareHeader(hwo, &(wavehdrs[i]), sizeof(WAVEHDR) );
					}
					winret = waveOutClose(hwo);
					exit(0);
				}
				/* 先ほどの再生データが最終 */
				if (wavehdrs[playIdx].dwUser == 1) {
					last = true;
				}

				playIdx = (playIdx + 1) % (BUF_COUNT);

				/* 次のバッファを読む */
				fillBuffer(vf, &(wavehdrs[bufferIdx]));
				bufferIdx = (bufferIdx + 1) % (BUF_COUNT);
            }
             break;
        case WOM_OPEN:
            break;
        default:
            break;
    }
}

int fseek2(FILE *fp, ogg_int64_t offset, int origin)
{
	if(fp==NULL)
		return(-1);

	return fseek(fp, offset, origin) ? 1 : 0 ;
}

/**
 *   メイン。
 */
int _tmain(int argc, _TCHAR* argv[])
{
    int ret;
	FILE *fp;

    OggVorbis_File vf;
    vorbis_info *vi;

     /* Ogg ファイルのオープン */
    fp = fopen((const char *)OGG_FILE, "rb");
    if (NULL == fp) {
        _ftprintf(stderr, _T("ERROR: Can not open file.\n") );
        exit(1);
    }

    /* OggVorbils_File 構造体 */
	ov_callbacks callbacks = {
		(size_t (*)(void *, size_t, size_t, void *))  fread,
		(int (*)(void *, ogg_int64_t, int))           fseek2,
		(int (*)(void *))                             fclose,
		(long (*)(void *))                            ftell
	};

	ret = ov_open_callbacks(fp, &vf, NULL, 0, callbacks);
	//ret = ov_open(fp, &vf, NULL, 0);
    if (ret < 0) {
        _ftprintf(stderr, _T("ERROR: ov_open()\n") );
        fclose(fp);
        exit(1);
    }

    /* Vorbis 情報 */
    vi = ov_info(&vf, -1);
    if (NULL == vi) {
        _ftprintf(stderr, _T("ERROR: ov_info()\n") );
        fclose(fp);
        ov_clear(&vf);
        exit(1);
    }

    /* サウンド出力初期化 */
    HWAVEOUT hwo;
    WAVEFORMATEX wfmx = {
        WAVE_FORMAT_PCM,
        vi->channels,
        vi->rate,
        vi->rate * vi->channels * (16 / 8),
        vi->channels * 16 / 8,
        16,
        0
    };
    MMRESULT winret = waveOutOpen(&hwo, WAVE_MAPPER, &wfmx,(DWORD)waveCallback, (DWORD)&vf, CALLBACK_FUNCTION);
    if (MMSYSERR_NOERROR != winret) {
        _ftprintf(stderr, _T("ERROR: waveOutOpen()\n") );
        fclose(fp);
        ov_clear(&vf);
        exit(1);
    }

    /* WAVEHDR 初期化 */
    for (int i = 0; i < BUF_COUNT; i++) {
        memset( (void *)&(wavehdrs[i]), 0, sizeof(WAVEHDR) );
        wavehdrs[i].lpData = buffer[i];
        wavehdrs[i].dwBufferLength = BUFFER_SIZE;
        winret = waveOutPrepareHeader(hwo, &(wavehdrs[i]), sizeof(WAVEHDR) );
        if (MMSYSERR_NOERROR != winret) {
            _ftprintf(stderr, _T("ERROR: waveOutPrepareHeader() [i = %d]\n"), i);
            fclose(fp);
            ov_clear(&vf);
            exit(1);
        }
        wavehdrs[i].dwLoops = 1;
        /* 初期 PCM データ */
        fillBuffer(&vf, &(wavehdrs[i]) );
    }

    /* 再生開始 (コールバックのトリガー) */
    winret = waveOutWrite(hwo, &(wavehdrs[0]), sizeof(WAVEHDR) );
    if (winret != MMSYSERR_NOERROR) {
        exit(1);
    }
    playIdx = (playIdx + 1) % (BUF_COUNT);
    bufferIdx = 0;  /* 次にデータを入れるのは 0 番目 */

    /* main を終わらせないため */
    while(1) {
        Sleep(1000);
    }

    return 0;
}

Programming Room - Ogg Vorbisを一部修正しただけですが。
現状のコードだと音が一瞬だけど音が飛ぶんだよな。
次はOGGファイルをメモリに読み込んで再生とクラス化といったところかね。スレッド化もした方がいいかな?

*1:構成によって変更すること