サウンドをチャンネルで管理2013/11/25

サウンドプレイヤーは最低限、同時再生の数だけ必要ですが、通常そんなものはたかがしれています。
システム側でプレイヤーをチャンネルとして管理、アプリ側はチャンネル番号を指定して再生などのサウンド処理を行うようにしてみます。


Sound.h
/******************
    サウンド管理
 ******************/
class SoundManager
{
	static SoundPlayer*		player;				// プレイヤー

public :

	static void		init(void);					// 初期化
	static void		quit(void);					// 終了

	static void		play(int, const void*, u32, int _loop = 1, float _vol = 1.0f);			// 再生
	static void		play(int);
	static void		prepare(int, const void*, u32, int _loop = 1, float _vol = 1.0f);		// 再生準備
	static void		play(void);																// 全て再生
	static void		stop(int);					// 停止
	static void		stop(void);					// 全て停止
	static void		set_volume(int, float);		// 音量設定
	static void		set_master_volume(float);	// マスター音量設定
	static void		set_next(int, const void*, u32, int _loop = 1);							// 連続再生設定
	static void		pause(int);					// 一時停止
	static void		pause(void);				// 全て一時停止
	static void		pause_system(void);			// システムによる一時停止
	static void		resume(int);				// 再開
	static void		resume(void);				// 全て再開
	static void		resume_system(void);		// システムによる再開
};

サウンド管理クラス sys::SoundManagerです。
メンバ関数は基本的に sys::SoundPlayerの関数の先頭の引数にチャンネル番号をくっつけたものとなっています。


Sound.cpp
SoundPlayer*	SoundManager::player = NULL;			// プレイヤー

/************************
    サウンド管理初期化
 ************************/
void	SoundManager::init(void)
{
	SoundPlayer::init_engine();							// サウンドエンジン初期化
	player = new SoundPlayer[SOUND_CHANNEL_MAX];		// プレイヤー
}

/**********
    終了
 **********/
void	SoundManager::quit(void)
{
	if ( player ) {
		delete[]	player;
		player = NULL;
	}
	SoundPlayer::quit_engine();							// サウンドエンジン終了
}

初期化・終了処理です。
プレイヤーのインスタンスの確保・解放を行います。また、sys::SoundPlayerの初期化・終了処理もここで呼んでしまいます。
チャンネル数 SOUND_CHANNEL_MAXは、def.hで定義しています。

/********************************************************
    再生
		引数	_channel = チャンネル番号
				_data    = サウンドデータ
				_size    = サウンドデータサイズ
				_loop    = ループ回数(0:無限ループ)
				_vol     = 音量
 *******************************************************/
void	SoundManager::prepare(int _channel, const void* _data, u32 _size, int _loop, float _vol)
{
	assert((0 <= _channel) && (_channel < SOUND_CHANNEL_MAX));

	player[_channel].prepare(_data, _size, _loop, _vol);
}

void	SoundManager::play(int _channel)
{
	assert((0 <= _channel) && (_channel < SOUND_CHANNEL_MAX));

	player[_channel].play();
}

void	SoundManager::play(int _channel, const void* _data, u32 _size, int _loop, float _vol)
{
	assert((0 <= _channel) && (_channel < SOUND_CHANNEL_MAX));

	player[_channel].prepare(_data, _size, _loop, _vol);
	player[_channel].play();
}

void	SoundManager::play(void)
{
	for (int i = 0; i < SOUND_CHANNEL_MAX; i++) {		// 全て再生開始
		player[i].play();
	}
}

/*******************************************
    停止
		引数	_channel = チャンネル番号
 *******************************************/
void	SoundManager::stop(int _channel)
{
	assert((0 <= _channel) && (_channel < SOUND_CHANNEL_MAX));

	player[_channel].stop();
}

void	SoundManager::stop(void)
{
	for (int i = 0; i < SOUND_CHANNEL_MAX; i++) {		// 全て停止
		player[i].stop();
	}
}

/*********************************************************
    音量設定
		引数	_channel = チャンネル番号
				_vol     = 音量(0.0:最小 ~ 1.0:最大)
 *********************************************************/
void	SoundManager::set_volume(int _channel, float _vol)
{
	assert((0 <= _channel) && (_channel < SOUND_CHANNEL_MAX));

	player[_channel].set_volume(_vol);
}

/*****************************************************
    マスター音量設定
		引数	_vol = 音量(0.0:最小 ~ 1.0:最大)
 *****************************************************/
void	SoundManager::set_master_volume(float _vol)
{
	if ( SoundPlayer::set_master_volume(_vol) ) {
		for (int i = 0; i < SOUND_CHANNEL_MAX; i++) {
			player[i].set_volume();						// 音量再設定
		}
	}
}

/*******************************************************
    連続再生設定
		引数	_channel = チャンネル番号
				_data    = サウンドデータ
				_size    = サウンドデータサイズ
				_loop    = ループ回数(0:無限ループ)
 *******************************************************/
void	SoundManager::set_next(int _channel, const void* _data, u32 _size, int _loop)
{
	assert((0 <= _channel) && (_channel < SOUND_CHANNEL_MAX));

	player[_channel].set_next(_data, _size, _loop);
}

再生等の処理です。
指定されたチャンネルのプレイヤーの処理を呼んでます。
停止等の処理でチャンネルを指定しない場合は、すべてのチャンネルのプレイヤーで処理を行うようになっています。

マスター音量設定時の各プレイヤーの音量の再設定はここで行いますので、アプリ側で行う必要はなくなりました。

/*********************************************
    一時停止
		引数	_f = 状態フラグを変更するか
 *********************************************/
void	SoundPlayer::pause(Bool _f)
{
	if ( state == PLAYING ) {				// 再生中
		if ( bqPlayerObject ) {
			(*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PAUSED);		// 一時停止状態
		}
		if ( _f ) {
			state = PAUSED;
		}
	}
}

/*********************************************
    一時停止
		引数	_channel = チャンネル番号
 *********************************************/
void	SoundManager::pause(int _channel)
{
	assert((0 <= _channel) && (_channel < SOUND_CHANNEL_MAX));

	player[_channel].pause(TRUE);
}

void	SoundManager::pause(void)
{
	for (int i = 0; i < SOUND_CHANNEL_MAX; i++) {		// 全て一時停止
		player[i].pause(TRUE);
	}
}

void	SoundManager::pause_system(void)		// システムによる一時停止
{
	for (int i = 0; i < SOUND_CHANNEL_MAX; i++) {		// 全て一時停止
		player[i].pause(FALSE);
	}
}

/*********************************************
    再開
		引数	_channel = チャンネル番号
 *********************************************/
void	SoundManager::resume(int _channel)
{
	assert((0 <= _channel) && (_channel < SOUND_CHANNEL_MAX));

	player[_channel].resume();
}

void	SoundManager::resume(void)
{
	for (int i = 0; i < SOUND_CHANNEL_MAX; i++) {		// 全て再開
		player[i].resume();
	}
}

void	SoundManager::resume_system(void)		// システムによる再開
{
	for (int i = 0; i < SOUND_CHANNEL_MAX; i++) {
		if ( player[i].get_state() == SoundPlayer::PLAYING ) {
			player[i].play();
		}
	}
}

一時停止・再開の処理も基本的には同じですが、サスペンド・レジュームによる処理をシステム側で行うことにします。
アプリ側で行う一時停止もシステム側で行う一時停止も OpenSLのプレイヤーの処理としては変わりませんが、サウンド管理ではこの二つは分ける必要があります。

例えば、
・メニューを開く、等でBGMを一時停止(アプリ側)
・サスペンドでサウンドを一時停止(システム側)
・レジュームでサウンド再開(システム側)
のときに、アプリ側で一時停止されたBGMを再開させてはいけません。

ここではサスペンドによる一時停止のときはプレイヤーの状態変数を PLAYINGのまま変えず、レジューム時に状態が PLAYINGのプレイヤーだけ再開するようにしています。アプリ側で一時停止されたプレイヤーの状態は PAUSEDになっていますので、このときには再開されません。


サンプル
AppMain.cpp
	sys::SoundManager::play(4, "bgm.ogg", sys::SoundPlayer::FILE_ASSET, 0);					// BGM再生開始

	if ( sys::TouchPanel[0].flag & sys::TouchManager::TRIGGER ) {
		sys::SoundManager::play(se_track, sound_data[SOUND_SE], sound_size[SOUND_SE]);		// SE再生
		se_track = ++se_track % 4;
	}

チャンネル指定でサウンドの再生を行っています。
また、
・プレイヤーのインスタンス確保、解放
・サスペンド、レジューム時のサウンド処理
が削除されています。


プロジェクト一式は、こちらから。