サウンド - 連続再生など2013/11/22

サウンドプレイヤーにに以下の機能を追加してみます。
・連続再生
・assetsファイル指定再生

連続再生
1つのサウンドデータを鳴らし終わった後に、続けて別のサウンドデータを鳴らす機能です。アプリ側で再生終了を監視して次のデータを再生するという手もありますが、ラグがあったり面倒だったりしますのでサウンドプレイヤーで処理するようにします。
BGMをイントロ部分と、メインのループ部分に分けて鳴らすようなときに使います。

ファイル指定再生
メモリに常駐するまでもない一時的なサウンドを、ファイルから読み込んで再生します。
ファイル上のサウンドデータを鳴らすだけならば、アプリ側でメモリに読み込んで再生すれば良いのですが、再生終了時にメモリを解放する必要があります。アプリ側で終了を監視するのはこれまた面倒なので、プレイヤーで処理します。

Sound.h
/********************
    サウンドデータ
 ********************/
struct SoundData
{
	char*		data;			// データ
	u32			size;			// データサイズ
	int			loop;			// ループカウンタ
	void*		file_data;		// 終了時に解放するデータ
	SoundData*	next;			// 次のデータ

		SoundData(char* _data, u32 _size, int _loop, void* _file = NULL)		// コンストラクタ
		{
			data = _data;
			size = _size;
			loop = _loop;
			file_data = _file;
			next = NULL;
		}
		~SoundData()							// デストラクタ
		{
			if ( file_data ) {
				free(file_data);
			}
			if ( next ) {
				delete	next;
			}
		}
	void	set_next(SoundData* _next)			// 次のデータを設定
			{
				if ( next ) {
					next->set_next(_next);
				}
				else {
					next = _next;
				}
			}
};

連続再生の予約で複数のサウンドデータを持つ必要があるため、サウンドデータを構造体 SoundDataで管理します。
再生終了時に解放するデータは、再生用データとは別に指定します。

Sound.cpp
	void*	_file_data;

	switch ( _size ) {
	  case FILE_ASSET :				// assetファイル
		_file_data = load_asset((char*)_data, &_size);
		_data = _file_data;
		break;

	  default :						// メモリ
		_file_data = NULL;
		break;
	}
			WaveFormat*		_info = (WaveFormat*)_data;

			format_pcm.formatType		= SL_DATAFORMAT_PCM;
			format_pcm.numChannels		= (SLuint32)_info->channel;
			format_pcm.samplesPerSec	= (SLuint32)_info->rate*1000;
			format_pcm.bitsPerSample	= (SLuint32)_info->bit;
			format_pcm.containerSize	= (SLuint32)_info->bit;
			format_pcm.channelMask		= (_info->channel == 1) ? SL_SPEAKER_FRONT_CENTER : (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT);
			format_pcm.endianness		= SL_BYTEORDER_LITTLEENDIAN;

			sound_data = new SoundData((char*)((u32)_data + sizeof(WaveFormat)), _info->data_size, _loop, _file_data);

再生準備関数 prepare内です。
assetsファイルを再生する場合は、_dataにファイル名文字列、_sizeに定数 sys::SoundPlayer::FILE_ASSETを指定します。
データ・サイズ等を管理する SoundDataを作成して sound_dataに保存します。

/****************************************************
    連続再生設定
		引数	_data = サウンドデータ
				_size = サウンドデータサイズ
				_loop = ループ回数(0:無限ループ)
 ****************************************************/
void	SoundPlayer::set_next(const void* _data, u32 _size, int _loop)
{
	if ( sound_data == NULL ) {				// すでに鳴り終わっている可能性
		play(_data, _size, _loop);
		return;
	}

	void*	_file_data;

	switch ( _size ) {
	  case FILE_ASSET :				// assetファイル
		_file_data = load_asset((char*)_data, &_size);
		_data = _file_data;
		break;

	  default :						// メモリ
		_file_data = NULL;
		break;
	}


	WaveFormat*		_info = (WaveFormat*)_data;

	sound_data->set_next(new SoundData((char*)((u32)_data + sizeof(WaveFormat)), _info->data_size, _loop, _file_data));
}

連続再生設定関数です。
再生準備と同様に SoundDataを作成して sound_dataの後ろにくっつけておきます。

void	SoundPlayer::callback_wav(void)
{
	if ( sound_data->loop == 1 ) {
		if ( sound_data ) {
			if ( sound_data->next == NULL ) {			// 終了
				delete	sound_data;
				sound_data = NULL;
			}
			else {										// 連続再生
				SoundData*	_next = sound_data->next;

				sound_data->next = NULL;
				delete	sound_data;
				sound_data = _next;
				(*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, sound_data->data, (SLuint32)sound_data->size);
				return;
			}
		}
		(*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);									// 停止
		state = STOPPED;
		return;
	}
	if ( sound_data->loop > 1 ) {
		sound_data->loop--;
	}
	(*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, sound_data->data, (SLuint32)sound_data->size);		// 再生開始
}

再生コールバックです。
再生終了時に次のデータがあった場合は sound_dataを入れ替えて、再びキューにデータを送って再生を続けます。
終了したデータは deleteされ、このときファイルから読み込んだデータは SoundDataのデストラクタで解放されます。

この他、停止関数やデストラクタでもsound_dataの解放を行います。


サンプルのBGM再生部分です。
AppMain.cpp
	sound_player[4].play("bgm.wav", sys::SoundPlayer::FILE_ASSET, 3);						// BGM再生開始
	sound_player[4].set_next("bgm2.wav", sys::SoundPlayer::FILE_ASSET, 1);					// 連続再生設定

"bgm.wav"を3回鳴らした後に、"bgm2.wav"を1回鳴らしています。


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