//------------------------------------------------------------------------------
// Lamp : Open source game middleware
// Copyright (C) 2004  Junpei Ohtani ( Email : junpee@users.sourceforge.jp )
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//------------------------------------------------------------------------------

/** @file
 * サウンドマネージャ実装
 * @author Junpee
 */

#include "LampBasic.h"
#include "Sound/System/SoundManager.h"
#include "Sound/System/SoundDefinition.h"
#include "Sound/Reader/WaveReader.h"
#include "Sound/Reader/OggVorbisReader.h"
#include "Sound/Stereo/StaticSound.h"
#include "Sound/Stereo/StreamSound.h"
#include "Sound/3D/StaticSound3D.h"
#include "Sound/3D/StreamSound3D.h"

namespace Lamp{

//------------------------------------------------------------------------------
// コンストラクタ
SoundManager::SoundManager(DirectSound* directSound) :
	directSound_(directSound), algorithm_(algorithmNormal){
}
//------------------------------------------------------------------------------
// デストラクタ
SoundManager::~SoundManager(){
	Assert(sounds_.getCount() == 0);
	Assert(updateSounds_.getCount() == 0);
}
//------------------------------------------------------------------------------
// サウンド操作
//------------------------------------------------------------------------------
// サウンドの削除
void SoundManager::destroy(Sound* sound){
	sounds_.removeByValue(sound);
	delete sound;
}
//------------------------------------------------------------------------------
// 全サウンドの破棄
void SoundManager::destroyAll(){
	int soundCount = getCount();
	for(int i = 0; i < soundCount; i++){ delete getSound(i); }
	sounds_.clear();
}
//------------------------------------------------------------------------------
// 静的サウンド
//------------------------------------------------------------------------------
// 静的サウンドの作成
StaticSound* SoundManager::createStaticSound(
	u_int size, int sample, int channel, int bit, Sound::Focus focus){
	// ウェーブフォーマットの構築
	WAVEFORMATEX waveFormat;
	buildWaveFormat(&waveFormat, sample, channel, bit);
	// バッファ記述の構築
	DSBUFFERDESC description;
	buildBufferDescription(&description, &waveFormat, size, focus);
	// パン追加
	description.dwFlags |= DSBCAPS_CTRLPAN;
	// バッファの構築
	DirectSoundBuffer* soundBuffer = buildSoundBuffer(&description);
	// サウンドの構築
	StaticSound* sound = new StaticSound(soundBuffer);
	sound->initialize(size, sample, channel, bit, focus);
	sounds_.pushBack(sound);
	return sound;
}
//------------------------------------------------------------------------------
// 静的サウンドの作成
StaticSound* SoundManager::createStaticSound(
	SoundReader* soundReader, const String& name, Sound::Focus focus){
	if(soundReader == NULL){ return NULL; }
	if(!soundReader->readHeader()){ return NULL; }
	// サウンド作成
	StaticSound* sound = createStaticSound(
		soundReader->getSize(), soundReader->getSample(),
		soundReader->getChannel(), soundReader->getBit(), focus);
	sound->setName(name);
	sound->setComment(soundReader->getComment());
	// サウンド読み込み
	if(!readSoundBuffer(sound, soundReader)){
		destroy(sound);
		sound = NULL;
	}
	return sound;
}
//------------------------------------------------------------------------------
// 静的サウンドの作成
StaticSound* SoundManager::createStaticSound(
	const String& fileName, Sound::Focus focus){
	FilePath filePath(fileName);
	SoundReader* soundReader = createSoundReader(filePath);
	StaticSound* sound =
		createStaticSound(soundReader, filePath.getFileName(), focus);
	SafeDelete(soundReader);
	return sound;
}
//------------------------------------------------------------------------------
// 静的サウンドの複製
StaticSound* SoundManager::cloneStaticSound(StaticSound* staticSound){
	DirectSoundBuffer* soundBuffer =
		duplicateSoundBuffer(staticSound->getSoundBuffer());
	// サウンドの構築
	StaticSound* sound = new StaticSound(soundBuffer);
	sounds_.pushBack(sound);
	return sound;
}
//------------------------------------------------------------------------------
// ストリームサウンド
//------------------------------------------------------------------------------
// ストリームサウンドの作成
StreamSound* SoundManager::createStreamSound(
	u_int size, int sample, int channel, int bit, Sound::Focus focus){
	// ウェーブフォーマットの構築
	WAVEFORMATEX waveFormat;
	buildWaveFormat(&waveFormat, sample, channel, bit);
	// バッファ記述の構築
	DSBUFFERDESC description;
	buildBufferDescription(&description, &waveFormat, size, focus);
	// パン、通知追加
	description.dwFlags |= (DSBCAPS_CTRLPAN | DSBCAPS_CTRLPOSITIONNOTIFY);
	// バッファの構築
	DirectSoundBuffer* soundBuffer = buildSoundBuffer(&description);
	// サウンドの構築
	StreamSound* sound = new StreamSound(soundBuffer);
	sound->initialize(size, sample, channel, bit, focus);
	sounds_.pushBack(sound);
	return sound;
}
//------------------------------------------------------------------------------
// ストリームサウンドの作成
StreamSound* SoundManager::createStreamSound(
	SoundReader* soundReader, const String& name, Sound::Focus focus){
	if(soundReader == NULL){ return NULL; }
	if(!soundReader->readHeader()){ return NULL; }
	// ストリームバッファサイズの算出
	u_int size = (u_int)(SoundDefinition::streamBufferLength *
		soundReader->getOneSecondBytes());
	// サウンド作成
	StreamSound* sound = createStreamSound(
		size, soundReader->getSample(),
		soundReader->getChannel(), soundReader->getBit(), focus);
	sound->setName(name);
	sound->setComment(soundReader->getComment());
	// サウンドリーダの設定
	sound->setSoundReader(soundReader);
	return sound;
}
//------------------------------------------------------------------------------
// ストリームサウンドの作成
StreamSound* SoundManager::createStreamSound(
	const String& fileName, Sound::Focus focus){
	FilePath filePath(fileName);
	SoundReader* soundReader = createSoundReader(filePath);
	StreamSound* sound =
		createStreamSound(soundReader, filePath.getFileName(), focus);
	// サウンドリーダはストリームサウンドによって削除される
	return sound;
}
//------------------------------------------------------------------------------
// 静的3Dサウンド
//------------------------------------------------------------------------------
// 静的3Dサウンドの作成
StaticSound3D* SoundManager::createStaticSound3D(u_int size, int sample,
	int channel, int bit, Sound::Focus focus){
	if(channel != 1){
		ErrorOut("SoundManager::createStaticSound3D() "
			"モノラルでないサウンドを3Dサウンドにできません。");
	}
	// ウェーブフォーマットの構築
	WAVEFORMATEX waveFormat;
	buildWaveFormat(&waveFormat, sample, channel, bit);
	// バッファ記述の構築
	DSBUFFERDESC description;
	buildBufferDescription(&description, &waveFormat, size, focus);
	// 3D
	description.dwFlags |= DSBCAPS_CTRL3D;
	// 3Dアルゴリズム
	if(algorithm_ == algorithmNormal){
		description.guid3DAlgorithm = DS3DALG_DEFAULT;
	}else if(algorithm_ == algorithmLightHRTF){
		description.guid3DAlgorithm = DS3DALG_HRTF_LIGHT;
	}else if(algorithm_ == algorithmFullHRTF){
		description.guid3DAlgorithm = DS3DALG_HRTF_FULL;
	}
	// バッファの構築
	DirectSoundBuffer* soundBuffer = buildSoundBuffer(&description);
	// サウンドの構築
	StaticSound3D* sound = new StaticSound3D(soundBuffer);
	sound->initialize(size, sample, channel, bit, focus);
	sounds_.pushBack(sound);
	return sound;
}
//------------------------------------------------------------------------------
// 静的3Dサウンドの作成
StaticSound3D* SoundManager::createStaticSound3D(SoundReader* soundReader,
	const String& name, Sound::Focus focus){
	if(soundReader == NULL){ return NULL; }
	if(!soundReader->readHeader()){ return NULL; }
	// サウンド作成
	StaticSound3D* sound = createStaticSound3D(soundReader->getSize(),
		soundReader->getSample(),soundReader->getChannel(),
		soundReader->getBit(), focus);
	sound->setName(name);
	sound->setComment(soundReader->getComment());
	// サウンド読み込み
	if(!readSoundBuffer(sound, soundReader)){
		destroy(sound);
		sound = NULL;
	}
	return sound;
}
//------------------------------------------------------------------------------
// 静的3Dサウンドの作成
StaticSound3D* SoundManager::createStaticSound3D(
	const String& fileName, Sound::Focus focus){
	FilePath filePath(fileName);
	SoundReader* soundReader = createSoundReader(filePath);
	StaticSound3D* sound = createStaticSound3D(
		soundReader, filePath.getFileName(), focus);
	SafeDelete(soundReader);
	return sound;
}
//------------------------------------------------------------------------------
// 静的3Dサウンドの複製
StaticSound3D* SoundManager::cloneStaticSound3D(StaticSound3D* staticSound3D){
	DirectSoundBuffer* soundBuffer =
		duplicateSoundBuffer(staticSound3D->getSoundBuffer());
	// サウンドの構築
	StaticSound3D* sound = new StaticSound3D(soundBuffer);
	sounds_.pushBack(sound);
	return sound;
}
//------------------------------------------------------------------------------
// ストリーム3Dサウンド
//------------------------------------------------------------------------------
// ストリーム3Dサウンドの作成
StreamSound3D* SoundManager::createStreamSound3D(
	u_int size, int sample, int channel, int bit, Sound::Focus focus){
	// ウェーブフォーマットの構築
	WAVEFORMATEX waveFormat;
	buildWaveFormat(&waveFormat, sample, channel, bit);
	// バッファ記述の構築
	DSBUFFERDESC description;
	buildBufferDescription(&description, &waveFormat, size, focus);
	// 3D、通知追加
	description.dwFlags |= (DSBCAPS_CTRL3D | DSBCAPS_CTRLPOSITIONNOTIFY);
	// 3Dアルゴリズム
	if(algorithm_ == algorithmNormal){
		description.guid3DAlgorithm = DS3DALG_DEFAULT;
	}else if(algorithm_ == algorithmLightHRTF){
		description.guid3DAlgorithm = DS3DALG_HRTF_LIGHT;
	}else if(algorithm_ == algorithmFullHRTF){
		description.guid3DAlgorithm = DS3DALG_HRTF_FULL;
	}
	// バッファの構築
	DirectSoundBuffer* soundBuffer = buildSoundBuffer(&description);
	// サウンドの構築
	StreamSound3D* sound = new StreamSound3D(soundBuffer);
	sound->initialize(size, sample, channel, bit, focus);
	sounds_.pushBack(sound);
	return sound;
}
//------------------------------------------------------------------------------
// ストリーム3Dサウンドの作成
StreamSound3D* SoundManager::createStreamSound3D(
	SoundReader* soundReader, const String& name, Sound::Focus focus){
	if(soundReader == NULL){ return NULL; }
	if(!soundReader->readHeader()){ return NULL; }
	// ストリームバッファサイズの算出
	u_int size = (u_int)(SoundDefinition::streamBufferLength *
		soundReader->getOneSecondBytes());
	// サウンド作成
	StreamSound3D* sound = createStreamSound3D(
		size, soundReader->getSample(),
		soundReader->getChannel(), soundReader->getBit(), focus);
	sound->setName(name);
	sound->setComment(soundReader->getComment());
	// サウンドリーダの設定
	sound->setSoundReader(soundReader);
	return sound;
}
//------------------------------------------------------------------------------
// ストリーム3Dサウンドの作成
StreamSound3D* SoundManager::createStreamSound3D(
	const String& fileName, Sound::Focus focus){
	FilePath filePath(fileName);
	SoundReader* soundReader = createSoundReader(filePath);
	StreamSound3D* sound =
		createStreamSound3D(soundReader, filePath.getFileName(), focus);
	// サウンドリーダはストリーム3Dサウンドによって削除される
	return sound;
}
//------------------------------------------------------------------------------
// サウンドバッファ
//------------------------------------------------------------------------------
// ウェーブフォーマットの構築
void SoundManager::buildWaveFormat(
	WAVEFORMATEX* waveFormat, int sample, int channel, int bit){
	Assert((sample >= DSBFREQUENCY_MIN) && (sample <= 100000));
	Assert((channel >= 1) && (channel <= 2));
	Assert((bit == 16) || (bit == 8));
	::memset(waveFormat, 0, sizeof(WAVEFORMATEX));
	waveFormat->wFormatTag = WAVE_FORMAT_PCM;
	waveFormat->nSamplesPerSec = sample;
	waveFormat->nChannels = channel;
	waveFormat->wBitsPerSample = bit;
	waveFormat->nBlockAlign =
		(waveFormat->nChannels * waveFormat->wBitsPerSample) / 8;
	waveFormat->nAvgBytesPerSec =
		waveFormat->nSamplesPerSec * waveFormat->nBlockAlign;
}
//------------------------------------------------------------------------------
// バッファ記述の構築
void SoundManager::buildBufferDescription(DSBUFFERDESC* description,
	WAVEFORMATEX* waveFormat, int size, Sound::Focus focus){
	::memset(description, 0, sizeof(DSBUFFERDESC));
	description->dwSize = sizeof(DSBUFFERDESC);
	description->lpwfxFormat = waveFormat;
	description->dwBufferBytes = size;
	// 動的ボイス管理、ボリューム、周波数、正確な再生カーソル
	description->dwFlags = (DSBCAPS_LOCDEFER | DSBCAPS_CTRLVOLUME |
		DSBCAPS_CTRLFREQUENCY | DSBCAPS_GETCURRENTPOSITION2);
	if(focus == Sound::focusSticky){
		description->dwFlags |= DSBCAPS_STICKYFOCUS;
	}else if(focus == Sound::focusGlobal){
		description->dwFlags |= DSBCAPS_GLOBALFOCUS;
	}
	// 追加すべきフラグ
	// Stereo : DSBCAPS_CTRLPAN
	// 3D     : 3Dアルゴリズム
	// Stream : DSBCAPS_CTRLPOSITIONNOTIFY
}
//------------------------------------------------------------------------------
// サウンドバッファの構築
DirectSoundBuffer* SoundManager::buildSoundBuffer(DSBUFFERDESC* description){
	IDirectSoundBuffer* buffer;
	if(DirectXFailed(directSound_->CreateSoundBuffer(
		description, &buffer, NULL))){
		ErrorOut("SoundManager::buildSoundBuffer() "
			"サウンドバッファの構築に失敗しました");
	}
	DirectSoundBuffer* soundBuffer;
	if(DirectXFailed(buffer->QueryInterface(
		DirectSoundBufferInterfaceID, (void**)&soundBuffer))){
		ErrorOut("SoundManager::buildSoundBuffer() "
			"サウンドバッファのインターフェース取得に失敗しました");
	}
	SafeRelease(buffer);
	return soundBuffer;
}
//------------------------------------------------------------------------------
// サウンドリーダの作成
SoundReader* SoundManager::createSoundReader(const FilePath& filePath){
	SoundReader* soundReader = NULL;
	// リーダの識別
	if(WaveReader::isWaveFileName(filePath)){
		soundReader = new WaveReader(filePath);
	}else if(OggVorbisReader::isOggVorbisFileName(filePath)){
		soundReader = new OggVorbisReader(filePath);
	}
	return soundReader;
}
//------------------------------------------------------------------------------
// サウンドバッファの読み込み
bool SoundManager::readSoundBuffer(
	SoundBuffer* soundBuffer, SoundReader* soundReader){
	SoundBuffer::Lock lock = soundBuffer->lock();
	bool result = lock.isValid();
	if(result){
		u_int bufferSize = lock.getSize0();
		Assert(bufferSize == soundReader->getSize());
		int resdSize = soundReader->read(lock.getAddress0(), bufferSize);
		result = (resdSize == bufferSize);
		soundBuffer->unlock();
	}
	return result;
}
//------------------------------------------------------------------------------
// サウンドバッファの複製
DirectSoundBuffer* SoundManager::duplicateSoundBuffer(
	DirectSoundBuffer* soundBuffer){
	IDirectSoundBuffer* buffer;
	if(DirectXFailed(directSound_->DuplicateSoundBuffer(
		soundBuffer, &buffer))){
		ErrorOut("SoundManager::duplicateSoundBuffer() "
			"サウンドバッファの複製に失敗しました");
	}
	DirectSoundBuffer* duplicatedBuffer;
	if(DirectXFailed(buffer->QueryInterface(
		DirectSoundBufferInterfaceID, (void**)&duplicatedBuffer))){
		ErrorOut("SoundManager::duplicateSoundBuffer() "
			"サウンドバッファのインターフェース取得に失敗しました");
	}
	SafeRelease(buffer);
	return duplicatedBuffer;
}
//------------------------------------------------------------------------------
// アップデート
//------------------------------------------------------------------------------
// アップデート
void SoundManager::update(){
	for(int i = 0; i < updateSounds_.getCount(); i++){
		Sound* sound = updateSounds_[i];
		if(sound->update()){
			updateSounds_.remove(i);
			i--;
		}
	}
}
//------------------------------------------------------------------------------
// アップデートサウンドの追加
void SoundManager::addUpdateSound(Sound* sound){
	updateSounds_.pushBack(sound);
}
//------------------------------------------------------------------------------
// アップデートサウンドの削除
void SoundManager::removeUpdateSound(Sound* sound){
	int result = updateSounds_.removeByValue(sound);
	Assert(result != -1);
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
