第19回目 OpenALでストリーム再生

OpenALでwavファイルのストリーム再生をしてみた。
前回作成したリングバッファクラスやストリームクラスを使用していません。
今回はOpenALで作ったサンプルプログラムです。

動作環境

ソースファイルは
Windows版がVisualStudio.NET2003用でWin32コンソールプロジェクト。
MacOSX版はまだ作ってません。

サンプルプログラム (Windows版)

#include "stdafx.h"
#include <windows.h>
#include "standard.h"
#include "OpenAL/include/al.h"
#include "OpenAL/include/alc.h"

#pragma comment(lib, "OpenAL/libs/Win32/OpenAL32.lib")

static const ALint BUFFER_MAX = 4;
static const size_t DATA_SAMPLE_MAX = 44100 * 2;

#pragma pack(push, 1)

struct RiffHeader
{
	uint32_t riff;
	uint32_t size;
	uint32_t type;
};

struct Chank
{
	uint32_t id;
	uint32_t size;
};

struct Fmt
{
	uint16_t format_id;
	uint16_t channel;
	uint32_t sampls_per_sec;
	uint32_t average_bytes_per_sec;
	uint16_t block_size;
	uint16_t extension_size;
	uint8_t extension[1];
};

#pragma pack(pop)

int _tmain(int argc, _TCHAR* argv[])
{
	if(argc < 2)
	{
		exit(-1);
	}
	// waveファイル読み込み
	FILE* file;
	file = _tfopen(argv[1], _T("rb"));
	if(file == NULL)
	{
		exit(-1);
	}
	// waveヘッダ解析
	RiffHeader header;
	fread(&header, 1, sizeof(RiffHeader), file);
	if((header.riff != 'FFIR') || (header.type != 'EVAW'))
	{
		fclose(file);
		exit(-1);
	}
	// waveチャンク解析
	uint32_t channel = 0;
	uint32_t sampls_per_sec = 0;
	Chank chank;
	Fmt* fmt = NULL;
	for(;;)
	{
		if(fread(&chank, 1, sizeof(Chank), file) == 0)
		{
			fclose(file);
			exit(-1);
		}
		if(chank.id == 'atad')
		{
			// dataチャンクなら終了
			break;
		}
		else if(chank.id == ' tmf')
		{
			// fmtチャンクならfmtを読み込み
			fmt = reinterpret_cast<Fmt*>(new char[chank.size]);
			if(fread(fmt, 1, chank.size, file) == 0)
			{
				delete [] fmt;
				fclose(file);
				exit(-1);
			}
			channel = fmt->channel;
			sampls_per_sec = fmt->sampls_per_sec;
			delete [] fmt;
		}
		else
		{
			// その他のチャンクならスキップ
			if(chank.size != 0)
			{
				if(fseek(file, chank.size, SEEK_CUR) != 0)
				{
					fclose(file);
					exit(-1);
				}
			}
		}
	}
	if(((channel < 1) && (channel > 2)) || (sampls_per_sec == 0))
	{
		fclose(file);
		exit(-1);
	}
	// OpenAL初期化
	ALCdevice* device_handle;
	ALCcontext* context_handle;
	ALuint source_handle;
	ALint state;
	device_handle = alcOpenDevice(NULL);
	context_handle = alcCreateContext(device_handle, NULL);
	alcMakeContextCurrent(context_handle);
	alGenSources(1, &source_handle);
	// waveをOpenALでストリーミング再生
	for(;;)
	{
		ALint processed_count;
		ALint buffer_count;
		// 再生し終わったバッファがあれば削除する
		alGetSourcei(source_handle, AL_BUFFERS_PROCESSED, &processed_count);
		if(processed_count)
		{
			ALuint delete_buffer_handle[BUFFER_MAX];
			alSourceUnqueueBuffers(source_handle, processed_count, delete_buffer_handle);
		}
		// ソースに登録可能ならバッファを作成して登録する
		alGetSourcei(source_handle, AL_BUFFERS_QUEUED, &buffer_count);
		if(buffer_count < BUFFER_MAX)
		{
			int16_t data[DATA_SAMPLE_MAX];
			size_t readsize;
			// wave読み込み
			readsize = fread(data, 1, DATA_SAMPLE_MAX * 2, file);
			if(readsize != 0)
			{
				ALuint buffer_handle;
				// バッファ作成
				alGenBuffers(1, &buffer_handle);
				alBufferData(buffer_handle, (channel == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16), data, static_cast<ALsizei>(readsize), sampls_per_sec);
				// ソースに登録
				alSourceQueueBuffers(source_handle, 1, &buffer_handle);
				alDeleteBuffers(1, &buffer_handle);
				// 再生していない場合は再生する
				alGetSourcei(source_handle, AL_SOURCE_STATE, &state);
				if(state != AL_PLAYING)
				{
					alSourcePlay(source_handle);
				}
			}
			else
			{
				// データの終わりに到達したので再生が終了したら演奏終了
				alGetSourcei(source_handle, AL_SOURCE_STATE, &state);
				if(state != AL_PLAYING)
				{
					break;
				}
			}
		}
		Sleep(100);
	}
	// 終了
	fclose(file);
	alDeleteSources(1, &source_handle);
	alcMakeContextCurrent(NULL);
	alcDestroyContext(context_handle);
	alcCloseDevice(device_handle);
	return 0;
}

操作方法

wavファイルをopenalstream_sample.exeにドラッグアンドドロップしてください。
wavファイルのフォーマットが8ビットの場合は再生できません。

ダウンロード

実行ファイル

(Windows版)

fileopenalstream_sample_windows_20090814.zip

(MacOSX版)
なし

ソースファイル

(Windows版)

fileopenalstream_sample_windows_source_20090814.zip

(MacOSX版)
なし

前に戻る?


トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS