#include "Mix/Private/Sound/Manager.h"

#include "Mix/Private/Sound/WaveReader.h"
#include "Mix/Private/Sound/SimpleController.h"
#include "Mix/Private/Sound/StreamingController.h"
#include "Mix/Private/Sound/Listener.h"
#include "Mix/Private/Sound/SimpleEmitter.h"
#include "Mix/Private/Sound/StreamingEmitter.h"
#include "Mix/Private/Sound/StreamingControllerMediator.h"
#include "Mix/Private/Sound/EmitterMediator.h"
#include "Mix/Private/Sound/StreamingControllerTask.h"
#include "Mix/Private/Sound/SimpleEmitterTask.h"
#include "Mix/Private/Sound/StreamingEmitterTask.h"
#include "Mix/Private/Memory/Buffer.h"
#include "Mix/Private/UserFile.h"
#include "Mix/IO/IManager.h"
#include "Mix/IO/IReader.h"

namespace Mix{ namespace Sound{

const wchar_t* Manager::FAILED_INITIALIZE           = L"サウンドマネージャの初期化に失敗";
const wchar_t* Manager::FAILED_LOADPLUGIN           = L"サウンドプラグインのロードに失敗";
const wchar_t* Manager::FAILED_CREATESTATIC         = L"スタティックサウンドコントローラーの作成に失敗";
const wchar_t* Manager::FAILED_CREATESTREAM         = L"ストリームサウンドコントローラーの作成に失敗";
const wchar_t* Manager::FAILED_CREATEEMITTER_STATIC = L"スタティッサウンドクエミッターの作成に失敗";
const wchar_t* Manager::FAILED_CREATEEMITTER_STREAM = L"ストリームサウンドエミッターの作成に失敗";
const wchar_t* Manager::FAILED_CREATELISTENER       = L"サウンドリスナーの作成に失敗";
const wchar_t* Manager::FAILED_CREATEDISTANCECURVE  = L"ディスタンスカーブの作成に失敗";
const wchar_t* Manager::FAILED_CLONESTATIC          = L"スタティックサウンドコントローラーの複製に失敗";
const wchar_t* Manager::FAILED_CLONESTREAM          = L"ストリームサウンドコントローラーの複製に失敗";

const wchar_t* Manager::STR_STREAM                  = L"ストリームサウンドコントローラーの";
const wchar_t* Manager::STR_STATIC_EMITTER          = L"スタティックサウンドエミッターの";
const wchar_t* Manager::STR_STREAM_EMITTER          = L"ストリームサウンドエミッターの";

const wchar_t* Manager::STR_CREATE                  = L"作成";
const wchar_t* Manager::STR_DUPLICATE               = L"複製";

const wchar_t* Manager::g_XAudio2ResultTextTable[5] =
{
	L"無効なパラメータがパラメータが渡されました",
	L"XMA ハードウェアに回復不可能なエラーが発生しました",
	L"エフェクトがインスタンス化できませんでした",
	L"サウンドデバイスが、切断されたか他の何らかのイベントにより、使用できない状態になりました",
	L"不明なエラー",
};

////////////////////////////////////////////////////////////////////////////////////////////////////
// Manager::DecoderPlugin
////////////////////////////////////////////////////////////////////////////////////////////////////

const char* Manager::DecoderPlugin::FN_CHECK_FORMAT = "CheckFormat";
const char* Manager::DecoderPlugin::FN_CREATE_DECODER = "CreateDecoder";

Manager::DecoderPlugin* Manager::DecoderPlugin::CreateInstance( HMODULE hModule )
{
	if( ( ::GetProcAddress( hModule, DecoderPlugin::FN_CHECK_FORMAT ) == NULL ) ||
		( ::GetProcAddress( hModule, DecoderPlugin::FN_CREATE_DECODER ) == NULL ) )
	{
		return NULL;
	}

	DecoderPlugin* pPlugin = MIX_LIB_NEW_T( Mix::Memory::SECTION_SOUND, DecoderPlugin );
	void* pProcAddr = ::GetProcAddress( hModule, DecoderPlugin::FN_CHECK_FORMAT );

	pPlugin->m_hModule = hModule;
	static_cast<DecoderPlugin::CheckFormatFunc>( pProcAddr )( NULL, pPlugin->m_KeySize );

	return pPlugin;
}

Manager::DecoderPlugin::DecoderPlugin( void ) :
m_hModule( NULL ),
m_KeySize( 0 )
{
}

Manager::DecoderPlugin::~DecoderPlugin( void )
{
	if ( m_hModule != NULL )
	{
		::FreeLibrary( m_hModule );
		m_hModule = NULL;
	}
}

void Manager::DecoderPlugin::Destroy( void )
{
	MIX_LIB_DELETE_THIS_T( DecoderPlugin, this );
}

UInt32 Manager::DecoderPlugin::GetKeySize( void ) const
{
	return m_KeySize;
}

Boolean Manager::DecoderPlugin::CheckFormat( const void* pData, UInt32& size )
{
	MIX_ASSERT( m_hModule != NULL );

	void* pProcAddr = ::GetProcAddress( m_hModule, DecoderPlugin::FN_CHECK_FORMAT );
	MIX_ASSERT( pProcAddr != NULL );

	return static_cast<DecoderPlugin::CheckFormatFunc>( pProcAddr )( pData, size );
}

Boolean Manager::DecoderPlugin::CreateDecoder( Mix::Plugin::Sound::IDecoder** ppDecoder )
{
	MIX_ASSERT( m_hModule != NULL );

	void* pProcAddr = ::GetProcAddress( m_hModule, DecoderPlugin::FN_CREATE_DECODER );
	MIX_ASSERT( pProcAddr != NULL );

	return static_cast<DecoderPlugin::CreatDecoderFunc>( pProcAddr )( ppDecoder );
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Manager
////////////////////////////////////////////////////////////////////////////////////////////////////

Manager* Manager::CreateInstance( void )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_SOUND, Manager );
}

Manager::Manager( void ) :
m_X3DACalcFunc( NULL ),
m_pXAudio( NULL ),
m_pMasterVoice( NULL ),
m_pFileMgr( NULL ),
m_EndEvent( True, False )
{
	Mix::IO::IManager* pFileMgr = Mix::IO::GetManagerPtr();

	MIX_ASSERT( pFileMgr != NULL );

	MIX_ADD_REF( pFileMgr );
	m_pFileMgr = pFileMgr;
}

Manager::~Manager( void )
{
	m_EndEvent.Set();
	m_Thread.Join();

	if( m_pMasterVoice != NULL )
	{
		m_pMasterVoice->DestroyVoice();
	}

	MIX_RELEASE( m_pXAudio );

	if( m_DecoderPluginList.size() > 0 )
	{
		for( Manager::DecoderPluginList::iterator it = m_DecoderPluginList.begin(); it != m_DecoderPluginList.end(); ++it )
		{
			( *it )->Destroy();
		}
	}

	MIX_RELEASE( m_pFileMgr );
}

Boolean Manager::Initialize( Mix::UserFile* pSysReport )
{
	HRESULT ret;
	UINT32 deviceCount;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// X3DAudio モジュール
	////////////////////////////////////////////////////////////////////////////////////////////////////

#ifdef _DEBUG
	if( m_X3DAModule.Load( L"X3DAudioD1_7.dll" ) == False )
	{
		if( m_X3DAModule.Load( L"X3DAudio1_7.dll" ) == False )
		{
			::MessageBox( ::GetActiveWindow(), L"DirectX が正しくインストールされていません : X3DAudio(1)", Mix::STR_ERROR, MB_OK | MB_ICONSTOP );
			MIX_LOG_ERROR( L"%s : m_X3DAModule.Load %s", FAILED_INITIALIZE, Mix::STR_RETERROR );
			return False;
		}
		else
		{
			MIX_LOG_INFO( L"リリース版 X3DAudio モジュールを使用します" );
		}
	}
#else //_DEBUG
	if( m_X3DAModule.Load( L"X3DAudio1_7.dll" ) == False )
	{
		::MessageBox(::GetActiveWindow(), L"DirectX が正しくインストールされていません X3DAudio(1)", Mix::STR_ERROR, MB_OK | MB_ICONSTOP);
		MIX_LOG_ERROR( L"%s : m_X3DAModule.Load %s", FAILED_INITIALIZE, Mix::STR_RETERROR );
		return False;
	}
#endif //_DEBUG

	if( ( m_X3DAModule.RegisterFunction( L"X3DAudioInitialize" ) == False ) ||
		( m_X3DAModule.RegisterFunction( L"X3DAudioCalculate" ) == False ) )
	{
		::MessageBox( ::GetActiveWindow(), L"DirectX が正しくインストールされていません : X3DAudio(2)", Mix::STR_ERROR, MB_OK | MB_ICONSTOP );
		MIX_LOG_ERROR( L"%s : m_X3DAModule.RegisterFunction %s", FAILED_INITIALIZE, Mix::STR_RETERROR );
		return False;
	}

	m_X3DACalcFunc = static_cast<Mix::Sound::PX3DAudioCalculate>( m_X3DAModule.GetFunction( L"X3DAudioCalculate" ) );

	MIX_ASSERT( m_X3DACalcFunc != NULL );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 初期化
	////////////////////////////////////////////////////////////////////////////////////////////////////

#ifdef _DEBUG
	ret = ::XAudio2Create( &m_pXAudio, XAUDIO2_DEBUG_ENGINE, XAUDIO2_DEFAULT_PROCESSOR );
	if( ret != S_OK )
	{
		ret = ::XAudio2Create( &m_pXAudio, 0, XAUDIO2_DEFAULT_PROCESSOR );
	}
#else //_DEBUG
	ret = ::XAudio2Create( &m_pXAudio, 0, XAUDIO2_DEFAULT_PROCESSOR );
#endif //_DEBUG

	if( ret != S_OK )
	{
		::MessageBox( ::GetActiveWindow(), L"DirectX が正しくインストールされていません : XAudio2(1)", Mix::STR_ERROR, MB_OK | MB_ICONSTOP );
		MIX_LOG_ERROR(L"%s : XAudio2Create %s : Result[%s]", FAILED_INITIALIZE, Mix::STR_RETERROR, GetXA2ResultText(ret));
		return False;
	}

	ret = m_pXAudio->GetDeviceCount( &deviceCount );
	if( ret != S_OK )
	{
		MIX_LOG_ERROR( L"%s : IXAudio2::GetDeviceCount %s : Result[%s]", FAILED_INITIALIZE, Mix::STR_RETERROR, GetXA2ResultText( ret ) );
		MIX_RELEASE( m_pXAudio );
		return False;
	}

	ret = m_pXAudio->GetDeviceDetails( 0, &m_Details );
	if( ret != S_OK )
	{
		MIX_LOG_ERROR( L"%s : IXAudio2::GetDeviceDetails %s : Result[%s]", FAILED_INITIALIZE, Mix::STR_RETERROR, GetXA2ResultText( ret ) );
		MIX_RELEASE( m_pXAudio );
		return False;
	}

	//デバイスをレポート
	if( pSysReport->Open() == True )
	{
		//タイトル
		pSysReport->WriteLine( L"////////////////////////////////////////////////////////////////////////////////////////////////////" );
		pSysReport->WriteLine( L"// サウンド                                                                                       //" );
		pSysReport->WriteLine( L"////////////////////////////////////////////////////////////////////////////////////////////////////" );
		pSysReport->WriteLine( L"" );

		//デバイス情報
		pSysReport->WriteLine( L"[デバイス]" );
		pSysReport->WriteLine( L"  識別子 : \"%s\"", m_Details.DeviceID );
		pSysReport->WriteLine( L"  表示名 : \"%s\"", m_Details.DisplayName );
		pSysReport->WriteLine( L"" );

		pSysReport->WriteLine( L"[フォーマット]" );
		pSysReport->WriteLine( L"  チャンネル数 : %d", m_Details.OutputFormat.Format.nChannels );
		pSysReport->WriteLine( L"  サンプリングレート : %fkHz", static_cast<Float32>( m_Details.OutputFormat.Format.nSamplesPerSec ) / 1000.0f );
		pSysReport->WriteLine( L"  平均データ転送速度 : %fKB", static_cast<Float32>( m_Details.OutputFormat.Format.nAvgBytesPerSec ) / 1000.0f );
		pSysReport->WriteLine( L"  ブロックのアライメント : %dByte", m_Details.OutputFormat.Format.nBlockAlign );
		pSysReport->WriteLine( L"  1サンプリングのビット数 : %dBit", m_Details.OutputFormat.Format.wBitsPerSample );
		pSysReport->WriteLine( L"" );

		pSysReport->Close();
	}

	ret = m_pXAudio->CreateMasteringVoice( &m_pMasterVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, 0, NULL );
	if( ret != S_OK )
	{
		MIX_LOG_ERROR( L"%s : IXAudio2::CreateMasteringVoice %s : Result[%s]", FAILED_INITIALIZE,
			Mix::STR_RETERROR, GetXA2ResultText( ret ) );
		MIX_RELEASE( m_pXAudio );
		return False;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// X3DAudio の初期化
	////////////////////////////////////////////////////////////////////////////////////////////////////

	static_cast<Mix::Sound::PX3DAudioInitialize>( m_X3DAModule.GetFunction( L"X3DAudioInitialize" ) )( m_Details.OutputFormat.dwChannelMask, X3DAUDIO_SPEED_OF_SOUND, m_hX3DAudio );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ストリームスレッドの開始
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_Thread.Start( Manager::ThreadEntry_Main, this ) == False )
	{
		MIX_LOG_ERROR( L"%s : スレッドを開始できませんでした", FAILED_INITIALIZE );
		MIX_RELEASE( m_pXAudio );
		return False;
	}

	return True;
}

Boolean Manager::LoadPlugin( Mix::Plugin::TYPE type, HMODULE hModule )
{
	MIX_ASSERT( type == Mix::Plugin::SOUND_DECODER );
	MIX_ASSERT( hModule != NULL );

	Manager::DecoderPlugin* pPlugin = Manager::DecoderPlugin::CreateInstance( hModule );
	if( pPlugin == NULL )
	{
		return False;
	}

	m_DecoderPluginList.push_back( pPlugin );

	return True;
}

Boolean Manager::CreateSimpleController( const wchar_t* pFilePath, Mix::Sound::IController** ppController )
{
	if( ( pFilePath == NULL ) ||
		( ::wcslen( pFilePath ) == 0 ) ||
		( ppController == NULL ) )
	{
		MIX_LOG_ERROR( L"%s : %s : pFilePath[%s] ppController[%s]",
			FAILED_CREATESTATIC,
			Mix::STR_ILLEGALARG,
			MIX_LOG_STR( pFilePath ),
			MIX_LOG_PTR( ppController ) );

		return False;
	}

	Mix::IO::IReader* pReader = NULL;

	if( m_pFileMgr->CreateFileReader( pFilePath, &pReader ) == False )
	{
		return False;
	}

	if( CreateSimpleController( pReader, ppController ) == False )
	{
		MIX_RELEASE( pReader );
		return False;
	}

	MIX_RELEASE( pReader );

	return True;
}

Boolean Manager::CreateSimpleController( Mix::IO::IReader* pReader, Mix::Sound::IController** ppController )
{
	if( ( pReader == NULL ) ||
		( ppController == NULL ) )
	{
		MIX_LOG_ERROR( L"%s : %s : pReader[%s] ppController[%s]",
			FAILED_CREATESTATIC,
			Mix::STR_ILLEGALARG,
			MIX_LOG_PTR( pReader ),
			MIX_LOG_PTR( ppController ) );

		return False;
	}

	HRESULT ret;
	Mix::Sound::WaveReader* pSource;
	Mix::Plugin::Sound::IDecoder* pDecoder;
	Mix::Sound::SimpleController* pController;
	Mix::Memory::Buffer* pBuffer;
	IXAudio2SourceVoice* pVoice;
	UInt32 waveSize;

	pSource = Mix::Sound::WaveReader::CreateInstance( pReader );
	if( pSource == NULL )
	{
		return False;
	}

	pDecoder = CreateDecoder( pSource, FAILED_CREATESTATIC );
	if( pDecoder == NULL )
	{
		pSource->Destroy();
		return False;
	}

	waveSize = static_cast<UInt32>( pDecoder->GetSize() );

	pBuffer = Mix::Memory::Buffer::CreateInstance();
	if( pBuffer == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s]", FAILED_CREATESTATIC, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pReader->GetFilePath() );
		pDecoder->Destroy();
		pSource->Destroy();
		return False;
	}

	if( pBuffer->Create( Mix::Memory::SECTION_SOUND, waveSize ) == False )
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s]", FAILED_CREATESTATIC, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pReader->GetFilePath() );
		MIX_RELEASE( pBuffer );
		pDecoder->Destroy();
		pSource->Destroy();
		return False;
	}

	if( pDecoder->Read( pSource, pBuffer->GetPointer(), waveSize ) != waveSize )
	{
		MIX_LOG_ERROR( L"%s : デコード中にエラーが発生 : %s[%s]", FAILED_CREATESTATIC, Mix::STR_FILEPATH, pReader->GetFilePath() );
		MIX_RELEASE( pBuffer );
		pDecoder->Destroy();
		pSource->Destroy();
		return False;
	}

	ret = m_pXAudio->CreateSourceVoice( &pVoice, pDecoder->GetFormat(), 0, XAUDIO2_DEFAULT_FREQ_RATIO, NULL, NULL, NULL );
	if( ret != S_OK )
	{
		MIX_LOG_ERROR( L"%s : CreateSourceVoice %s : %s[%s] Result[%s]", FAILED_CREATESTATIC, Mix::STR_RETERROR, Mix::STR_FILEPATH, pReader->GetFilePath(), GetXA2ResultText( ret ) );
		MIX_RELEASE( pBuffer );
		pDecoder->Destroy();
		pSource->Destroy();
		return False;
	}

	pController = Mix::Sound::SimpleController::CreateInstance( pReader->GetFilePath(), pDecoder->GetFormat(), pVoice, pBuffer );
	if( pController == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s", FAILED_CREATESTATIC, Mix::STR_OUTOFMEMORY );
		pVoice->DestroyVoice();
		MIX_RELEASE( pBuffer );
		pDecoder->Destroy();
		pSource->Destroy();
		return False;
	}

	pDecoder->Destroy();
	pSource->Destroy();

	( *ppController ) = pController;

	return True;
}

Boolean Manager::CreateStreamingController( const wchar_t* pFilePath, Boolean bBuffered, Mix::Sound::IController** ppController )
{
	if( ( pFilePath == NULL ) ||
		( ppController == NULL ) )
	{
		MIX_LOG_ERROR( L"%s : %s : pFilePath[%s] ppController[%s]",
			FAILED_CREATESTREAM,
			Mix::STR_ILLEGALARG,
			MIX_LOG_STR( pFilePath ),
			MIX_LOG_PTR( ppController ) );

		return False;
	}

	Mix::IO::IReader* pReader = NULL;

	pReader = CreateReader( pFilePath, bBuffered );
	if( pReader == NULL )
	{
		return False;
	}

	if( CreateStreamingController( STR_CREATE, pReader, ppController ) == False )
	{
		MIX_RELEASE( pReader );
		return False;
	}

	MIX_RELEASE( pReader );

	return True;
}

Boolean Manager::CreateStreamingController( Mix::IO::IReader* pReader, Mix::Sound::IController** ppController )
{
	if( ( pReader == NULL ) ||
		( ppController == NULL ) )
	{
		MIX_LOG_ERROR( L"%s : %s : pReader[%s] ppController[%s]",
			FAILED_CREATESTREAM,
			Mix::STR_ILLEGALARG,
			MIX_LOG_PTR( pReader ),
			MIX_LOG_PTR( ppController ) );

		return False;
	}

	return CreateStreamingController( STR_CREATE, pReader, ppController );
}

Boolean Manager::CreateListener( const Mix::Vector3& localFront, const Mix::Vector3& localUp, Mix::Sound::IListener** ppListener, const wchar_t* pDebugName )
{
	if( ppListener == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s] : pListener[%s]",
			FAILED_CREATELISTENER,
			Mix::STR_ILLEGALARG,
			Mix::STR_DEBUGNAME,
			MIX_SAFE_NAME( pDebugName ),
			MIX_LOG_PTR( ppListener ) );

		return False;
	}

	Mix::Sound::Listener* pListener = Mix::Sound::Listener::CreateInstance( localFront, localUp );
	if( pListener == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s]",
			FAILED_CREATELISTENER,
			Mix::STR_OUTOFMEMORY,
			Mix::STR_DEBUGNAME,
			MIX_SAFE_NAME( pDebugName ) );

		return False;
	}

	( *ppListener ) = pListener;

	return True;
}

Boolean Manager::CreateSimpleEmitter( Mix::Sound::IListener* pListener, const wchar_t* pFilePath, const Mix::Vector3& localFront, const Mix::Vector3& localUp, Mix::Sound::IEmitter** ppEmitter )
{
	if( ( pListener == NULL ) ||
		( pFilePath == NULL ) ||
		( ::wcslen( pFilePath ) == 0 ) ||
		( ppEmitter == NULL ) )
	{
		MIX_LOG_ERROR( L"%s : %s : pFileName[%s] ppEmitter[%s]",
			FAILED_CREATEEMITTER_STATIC,
			Mix::STR_ILLEGALARG,
			MIX_LOG_PTR( pListener ),
			MIX_LOG_STR( pFilePath ),
			MIX_LOG_PTR( ppEmitter ) );

		return False;
	}

	Mix::IO::IReader* pReader;

	if( m_pFileMgr->CreateFileReader( pFilePath, &pReader ) == False )
	{
		return False;
	}

	if( CreateSimpleEmitter( pListener, pReader, localFront, localUp, ppEmitter ) == False )
	{
		MIX_RELEASE( pReader );
		return False;
	}

	MIX_RELEASE( pReader );

	return True;
}

Boolean Manager::CreateSimpleEmitter( Mix::Sound::IListener* pListener, Mix::IO::IReader* pReader, const Mix::Vector3& localFront, const Mix::Vector3& localUp, Mix::Sound::IEmitter** ppEmitter )
{
	if( ( pListener == NULL ) ||
		( pReader == NULL ) ||
		( ppEmitter == NULL ) )
	{
		MIX_LOG_ERROR( L"%s : %s : pListener[%s] pReader[%s] ppEmitter[%s]",
			FAILED_CREATEEMITTER_STATIC,
			Mix::STR_ILLEGALARG,
			MIX_LOG_PTR( pListener ),
			MIX_LOG_PTR( pReader ),
			MIX_LOG_PTR( ppEmitter ) );

		return False;
	}

	UInt32 waveSize;
	Mix::Sound::WaveReader* pSource;
	Mix::Plugin::Sound::IDecoder* pDecoder;
	Mix::Memory::Buffer* pBuffer;

	pSource = Mix::Sound::WaveReader::CreateInstance( pReader );
	if( pSource == NULL )
	{
		return False;
	}

	pDecoder = CreateDecoder( pSource, FAILED_CREATEEMITTER_STATIC );
	if( pDecoder == NULL )
	{
		pSource->Destroy();
		return False;
	}

	waveSize = static_cast<UInt32>( pDecoder->GetSize() );

	pBuffer = Mix::Memory::Buffer::CreateInstance();
	if( pBuffer == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s]", FAILED_CREATEEMITTER_STATIC, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pReader->GetFilePath() );
		pDecoder->Destroy();
		pSource->Destroy();
		return False;
	}

	if( pBuffer->Create( Mix::Memory::SECTION_SOUND, waveSize ) == False )
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s]", FAILED_CREATEEMITTER_STATIC, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pReader->GetFilePath() );
		MIX_RELEASE( pBuffer );
		pDecoder->Destroy();
		pSource->Destroy();
		return False;
	}

	if( pDecoder->Read( pSource, pBuffer->GetPointer(), waveSize ) != waveSize )
	{
		MIX_LOG_ERROR( L"%s : デコード中にエラーが発生 : %s[%s]", FAILED_CREATEEMITTER_STATIC, Mix::STR_FILEPATH, pReader->GetFilePath() );
		MIX_RELEASE( pBuffer );
		pDecoder->Destroy();
		pSource->Destroy();
		return False;
	}

	if( CreateSimpleEmitter( STR_CREATE, pReader->GetFilePath(), pListener, pDecoder->GetFormat(), pBuffer, localFront, localUp, ppEmitter ) == False )
	{
		MIX_RELEASE( pBuffer );
		pDecoder->Destroy();
		pSource->Destroy();
		return False;
	}

	MIX_RELEASE( pBuffer );
	pDecoder->Destroy();
	pSource->Destroy();

	return True;
}

Boolean Manager::CreateStreamingEmitter( Mix::Sound::IListener* pListener, const wchar_t* pFilePath, Boolean bBuffered, const Mix::Vector3& localFront, const Mix::Vector3& localUp, Mix::Sound::IEmitter** ppEmitter )
{
	if( ( pListener == NULL ) ||
		( pFilePath == NULL ) ||
		( ppEmitter == NULL ) )
	{
		MIX_LOG_ERROR( L"%s : %s : pFilePath[%s] ppEmitter[%s]",
			FAILED_CREATEEMITTER_STREAM,
			Mix::STR_ILLEGALARG,
			MIX_LOG_PTR( pListener ),
			MIX_LOG_STR( pFilePath ),
			MIX_LOG_PTR( ppEmitter ) );

		return False;
	}

	Mix::IO::IReader* pReader;

	pReader = CreateReader( pFilePath, bBuffered );
	if( pReader == NULL )
	{
		return False;
	}

	if( CreateStreamingEmitter( STR_CREATE, pListener, pReader, localFront, localUp, ppEmitter ) == False )
	{
		MIX_RELEASE( pReader );
		return False;
	}

	MIX_RELEASE( pReader );

	return True;
}

Boolean Manager::CreateStreamingEmitter( Mix::Sound::IListener* pListener, Mix::IO::IReader* pReader, const Mix::Vector3& localFront, const Mix::Vector3& localUp, Mix::Sound::IEmitter** ppEmitter )
{
	if( ( pListener == NULL ) ||
		( pReader == NULL ) ||
		( ppEmitter == NULL ) )
	{
		MIX_LOG_ERROR( L"%s : %s : pReader[%s] ppEmitter[%s]",
			FAILED_CREATEEMITTER_STREAM,
			Mix::STR_ILLEGALARG,
			MIX_LOG_PTR( pListener ),
			MIX_LOG_PTR( pReader ),
			MIX_LOG_PTR( ppEmitter ) );

		return False;
	}

	return CreateStreamingEmitter( STR_CREATE, pListener, pReader, localFront, localUp, ppEmitter );
}

Mix::IO::IReader* Manager::CreateReader( const wchar_t* pFilePath, Boolean bBuffered )
{
	Mix::IO::IReader* pReader;

	if( bBuffered == False )
	{
		if( m_pFileMgr->CreateFileReader( pFilePath, &pReader ) == False )
		{
			return NULL;
		}
	}
	else
	{
		if( m_pFileMgr->CreateBufferedReader( pFilePath, &pReader ) == False )
		{
			return NULL;
		}
	}

	return pReader;
}

Mix::Plugin::Sound::IDecoder* Manager::CreateDecoder( Mix::Sound::WaveReader* pSource, const wchar_t* pFailedMsg )
{
	DecoderPluginList::iterator it_plugin_begin = m_DecoderPluginList.begin();
	DecoderPluginList::iterator it_plugin_end = m_DecoderPluginList.end();
	DecoderPluginList::iterator it_plugin;

	Mix::Plugin::Sound::IDecoder* pDecoder = NULL;
	UInt32 preKeySize = 0;

	//デコーダを検索＆作成
	for( it_plugin = it_plugin_begin; ( ( it_plugin != it_plugin_end ) && ( pDecoder == NULL ) ); ++it_plugin )
	{
		Manager::DecoderPlugin* pPlugin = ( *it_plugin );
		UInt32 keySize = pPlugin->GetKeySize();

		MIX_ASSERT( keySize > 0 );

		if( preKeySize < keySize )
		{
			m_KeyTemp.resize( keySize );

			pSource->Read( &( m_KeyTemp[0] ), keySize );
			pSource->Seek( Mix::Sound::WaveReader::BEGIN, 0 );

			preKeySize = keySize;
		}

		if( pPlugin->CheckFormat( &( m_KeyTemp[0] ), keySize ) == True )
		{
			if( pPlugin->CreateDecoder( &pDecoder ) == False )
			{
				pDecoder = NULL;
			}
		}
	}

	if( pDecoder != NULL )
	{
		if( pDecoder->Initialize( pSource ) == false )
		{
			MIX_LOG_ERROR( L"%s : 初期化中にエラーが発生しました : File[%s]", pFailedMsg, pSource->GetFilePath() );
			pDecoder->Destroy();
		}
	}
	else
	{
		MIX_LOG_ERROR( L"%s : 指定されたファイルをサポートするデコーダが見つかりません : File[%s]", pFailedMsg, pSource->GetFilePath() );
	}

	return pDecoder;
}

void Manager::AddTask( Mix::Sound::Task* pTask )
{
	m_TaskListSync.Enter();
	m_TaskList.push_back( pTask );
	m_TaskListSync.Leave();
}

const wchar_t* Manager::GetXA2ResultText( HRESULT ret )
{
	switch( ret )
	{
	case XAUDIO2_E_INVALID_CALL:
		return g_XAudio2ResultTextTable[0];
	case XAUDIO2_E_XMA_DECODER_ERROR:
		return g_XAudio2ResultTextTable[1];
	case XAUDIO2_E_XAPO_CREATION_FAILED:
		return g_XAudio2ResultTextTable[2];
	case XAUDIO2_E_DEVICE_INVALIDATED:
		return g_XAudio2ResultTextTable[3];
	}

	return g_XAudio2ResultTextTable[4];
}

Boolean Manager::CreateStreamingController( const wchar_t* actionMsg, Mix::IO::IReader* pReader, Mix::Sound::IController** ppController )
{
	if( ( pReader == NULL ) ||
		( ppController == NULL ) )
	{
		MIX_LOG_ERROR( L"%s%sに失敗 : %s : pReader[%s] ppController[%s]",
			STR_STREAM,
			Mix::STR_ILLEGALARG,
			actionMsg,
			( pReader != NULL )? L"○" : L"NULL",
			( ppController != NULL )? L"○" : L"×"
			);

		return False;
	}

	HRESULT ret;
	Mix::Sound::StreamingControllerTask* pTask;
	Mix::Sound::StreamingControllerMediator* pMediator;
	Mix::Sound::StreamingController* pController;
	Mix::Sound::WaveReader* pSource;
	Mix::Plugin::Sound::IDecoder* pDecoder;
	IXAudio2SourceVoice* pVoice;

	//メディエータを作成
	pMediator = Mix::Sound::StreamingControllerMediator::CreateInstance();
	if( pMediator == NULL )
	{
		MIX_LOG_ERROR( L"%s%sに失敗 : %s : %s[%s]", STR_STREAM, actionMsg, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pReader->GetFilePath() );
		return False;
	}

	//タスクを作成
	pTask = MIX_LIB_NEW_T( Mix::Memory::SECTION_SOUND, Mix::Sound::StreamingControllerTask, pMediator );
	if( pTask == NULL )
	{
		MIX_LOG_ERROR( L"%s%sに失敗 : %s : %s[%s]", STR_STREAM, actionMsg, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pReader->GetFilePath() );
		MIX_RELEASE( pMediator );
		return False;
	}

	//ソースの作成
	pSource = Mix::Sound::WaveReader::CreateInstance( pReader );
	if( pSource == NULL )
	{
		MIX_LOG_ERROR( L"%s%sに失敗 : %s : %s[%s]", STR_STREAM, actionMsg, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pReader->GetFilePath() );
		MIX_RELEASE( pMediator );
		MIX_LIB_DELETE_T( StreamingControllerTask, pTask );
		return False;
	}

	//デコーダーを作成
	pDecoder = CreateDecoder( pSource, FAILED_CREATESTREAM );
	if( pDecoder == NULL )
	{
		pSource->Destroy();
		MIX_RELEASE( pMediator );
		MIX_LIB_DELETE_T( StreamingControllerTask, pTask );
		return False;
	}

	//ボイスを作成
	ret = m_pXAudio->CreateSourceVoice( &pVoice, pDecoder->GetFormat(), 0, XAUDIO2_DEFAULT_FREQ_RATIO, pTask->GetVoiceCallbackPtr(), NULL, NULL );
	if( ret != S_OK )
	{
		MIX_LOG_ERROR( L"%s : IXAudio2::CreateSourceVoice %s : Result[%s]", FAILED_CREATESTREAM, Mix::STR_RETERROR, GetXA2ResultText( ret ) );
		pDecoder->Destroy();
		pSource->Destroy();
		MIX_RELEASE( pMediator );
		MIX_LIB_DELETE_T( StreamingControllerTask, pTask );
		return False;
	}

	//タスクを初期化
	if( pTask->Initialize( pSource, pDecoder, pVoice ) == False )
	{
		//タスク初期化後は pSource pDecoder pVoice はタスクのデストラクタで解放されるので、失敗した際に解放する必要はない
		MIX_LOG_ERROR( L"%s%sに失敗 : %s : %s[%s]", STR_STREAM, actionMsg, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pReader->GetFilePath() );
		MIX_RELEASE( pMediator );
		MIX_LIB_DELETE_T( StreamingControllerTask, pTask );
		return False;
	}

	//インターフェースを作成
	pController = Mix::Sound::StreamingController::CreateInstance( pReader, pMediator );
	if( pController == NULL )
	{
		MIX_LOG_ERROR( L"%s%sに失敗 : %s : %s[%s]", STR_STREAM, actionMsg, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pReader->GetFilePath() );
		MIX_RELEASE( pMediator );
		MIX_LIB_DELETE_T( StreamingControllerTask, pTask );
		return False;
	}

	MIX_RELEASE( pMediator );

	AddTask( pTask );

	( *ppController ) = pController;

	return True;
}

Boolean Manager::CreateSimpleEmitter(	const wchar_t* pActionMsg,
										const wchar_t* pFilePath,
										Mix::Sound::IListener* pListener,
										const WAVEFORMATEX* pFormat,
										Mix::Memory::IBuffer* pBuffer,
										const Mix::Vector3& localFront,
										const Mix::Vector3& localUp,
										Mix::Sound::IEmitter** ppEmitter )
{
	HRESULT ret;
	Mix::Sound::EmitterMediator* pMediator;
	Mix::Sound::SimpleEmitterTask* pTask;
	Mix::Sound::SimpleEmitter* pEmitter;
	IXAudio2SourceVoice* pVoice;

	ret = m_pXAudio->CreateSourceVoice( &pVoice, pFormat, 0, XAUDIO2_DEFAULT_FREQ_RATIO, NULL, NULL, NULL );
	if( ret != S_OK )
	{
		MIX_LOG_ERROR( L"%s%s : CreateSourceVoice %s : %s[%s] Result[%s]", STR_STATIC_EMITTER, pActionMsg, Mix::STR_RETERROR, Mix::STR_FILEPATH, pFilePath, GetXA2ResultText( ret ) );
		return False;
	}

	pMediator = Mix::Sound::EmitterMediator::CreateInstance( m_X3DACalcFunc, m_hX3DAudio, m_Details.OutputFormat.Format.nChannels, pFormat->nChannels );
	if( pMediator == NULL )
	{
		MIX_LOG_ERROR( L"%s%s : %s : %s[%s]", STR_STATIC_EMITTER, pActionMsg, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pFilePath );
		pVoice->DestroyVoice();
		return False;
	}

	pTask = MIX_LIB_NEW_T( Mix::Memory::SECTION_SOUND, Mix::Sound::SimpleEmitterTask, pMediator, pBuffer, pVoice );
	if( pTask == NULL )
	{
		MIX_LOG_ERROR( L"%s%s : %s : %s[%s]", STR_STATIC_EMITTER, pActionMsg, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pFilePath );
		MIX_RELEASE( pMediator );
		pVoice->DestroyVoice();
		return False;
	}

	pEmitter = Mix::Sound::SimpleEmitter::CreateInstance(	static_cast<Mix::Sound::Listener*>( pListener ),
															pMediator,
															pFormat,
															pBuffer,
															localFront,
															localUp,
															m_Details.OutputFormat.Format.nChannels,
															pFilePath );
	if( pEmitter == NULL )
	{
		MIX_LOG_ERROR( L"%s%s : %s : %s[%s]", STR_STATIC_EMITTER, pActionMsg, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pFilePath );
		MIX_LIB_DELETE_T( SimpleEmitterTask, pTask );
		MIX_RELEASE( pMediator );
		pVoice->DestroyVoice();
		return False;
	}

	MIX_RELEASE( pMediator );

	AddTask( pTask );

	( *ppEmitter ) = pEmitter;

	return True;
}

Boolean Manager::CreateStreamingEmitter(	const wchar_t* actionMsg,
											Mix::Sound::IListener* pListener,
											Mix::IO::IReader* pReader,
											const Mix::Vector3& localFront,
											const Mix::Vector3& localUp,
											Mix::Sound::IEmitter** ppEmitter )
{
	if( ( pReader == NULL ) ||
		( ppEmitter == NULL ) )
	{
		MIX_LOG_ERROR( L"%s%sに失敗 : %s : pReader[%s] ppEmitter[%s]",
			STR_STREAM_EMITTER,
			Mix::STR_ILLEGALARG,
			actionMsg,
			MIX_LOG_PTR( pReader ),
			MIX_LOG_PTR( ppEmitter ) );

		return False;
	}

	HRESULT ret;
	Mix::Sound::EmitterMediator* pMediator;
	Mix::Sound::StreamingEmitterTask* pTask;
	Mix::Sound::StreamingEmitter* pEmitter;
	Mix::Sound::WaveReader* pSource;
	Mix::Plugin::Sound::IDecoder* pDecoder;
	IXAudio2SourceVoice* pVoice;

	//ソースを作成
	pSource = Mix::Sound::WaveReader::CreateInstance( pReader );
	if( pSource == NULL )
	{
		MIX_LOG_ERROR( L"%s%sに失敗 : %s : %s[%s]", STR_STREAM_EMITTER, actionMsg, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pReader->GetFilePath() );
		return False;
	}

	//デコーダーを作成
	pDecoder = CreateDecoder( pSource, FAILED_CREATEEMITTER_STREAM );
	if( pDecoder == NULL )
	{
		pSource->Destroy();
		return False;
	}

	//メディエータを作成
	pMediator = Mix::Sound::EmitterMediator::CreateInstance( m_X3DACalcFunc, m_hX3DAudio, m_Details.OutputFormat.Format.nChannels, pDecoder->GetFormat()->nChannels );
	if( pMediator == NULL )
	{
		MIX_LOG_ERROR( L"%s%sに失敗 : %s : %s[%s]", STR_STREAM_EMITTER, actionMsg, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pReader->GetFilePath() );
		pDecoder->Destroy();
		pSource->Destroy();
		return False;
	}

	//タスクを作成
	pTask = MIX_LIB_NEW_T( Mix::Memory::SECTION_SOUND, Mix::Sound::StreamingEmitterTask, pMediator );
	if( pTask == NULL )
	{
		MIX_LOG_ERROR( L"%s%sに失敗 : %s : %s[%s]", STR_STREAM_EMITTER, actionMsg, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pReader->GetFilePath() );
		MIX_RELEASE( pMediator );
		pDecoder->Destroy();
		pSource->Destroy();
		return False;
	}

	//ボイスを作成
	ret = m_pXAudio->CreateSourceVoice(	&pVoice,
										pDecoder->GetFormat(),
										0,
										XAUDIO2_DEFAULT_FREQ_RATIO,
										pTask->GetVoiceCallbackPtr(),
										NULL,
										NULL );
	if( ret != S_OK )
	{
		MIX_LOG_ERROR( L"%s : IXAudio2::CreateSourceVoice %s : Result[%s]", FAILED_CREATEEMITTER_STREAM, Mix::STR_RETERROR, GetXA2ResultText( ret ) );
		MIX_LIB_DELETE_T( StreamingEmitterTask, pTask );
		MIX_RELEASE( pMediator );
		pDecoder->Destroy();
		pSource->Destroy();
		return False;
	}

	//タスクを初期化
	if( pTask->Initialize( pSource, pDecoder, pVoice ) == False )
	{
		MIX_LOG_ERROR( L"%s%sに失敗 : %s : %s[%s]", STR_STREAM_EMITTER, actionMsg, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pReader->GetFilePath() );
		MIX_LIB_DELETE_T( StreamingEmitterTask, pTask );
		MIX_RELEASE( pMediator );
		return False;
	}

	//インターフェースを作成
	pEmitter = Mix::Sound::StreamingEmitter::CreateInstance(	static_cast<Mix::Sound::Listener*>( pListener ),
																pReader,
																pMediator,
																localFront,
																localUp,
																pDecoder->GetFormat()->nChannels,
																m_Details.OutputFormat.Format.nChannels );
	if( pEmitter == NULL )
	{
		MIX_LOG_ERROR( L"%s%sに失敗 : %s : %s[%s]", STR_STREAM_EMITTER, actionMsg, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pReader->GetFilePath() );
		MIX_LIB_DELETE_T( StreamingEmitterTask, pTask );
		MIX_RELEASE( pMediator );
		return False;
	}

	MIX_RELEASE( pMediator );

	AddTask( pTask );

	( *ppEmitter ) = pEmitter;

	return True;
}

Boolean Manager::CloneSimpleController( const Mix::Sound::IController* pSrc, Mix::Sound::IController** ppDst )
{
	HRESULT ret;
	const Mix::Sound::SimpleController* pSrcCtrl;
	Mix::Sound::SimpleController* pDstCtrl;
	IXAudio2SourceVoice* pVoice;

	pSrcCtrl = dynamic_cast<const Mix::Sound::SimpleController*>( pSrc );

	ret = m_pXAudio->CreateSourceVoice( &pVoice, pSrcCtrl->GetFormat(), 0, XAUDIO2_DEFAULT_FREQ_RATIO, NULL, NULL, NULL );
	if( ret != S_OK )
	{
		MIX_LOG_ERROR( L"%s : CreateSourceVoice %s : %s[%s] Result[%s]", FAILED_CLONESTATIC, Mix::STR_RETERROR, Mix::STR_FILEPATH, pSrcCtrl->GetFilePath(), GetXA2ResultText( ret ) );
		return False;
	}

	pDstCtrl = Mix::Sound::SimpleController::CreateInstance( pSrcCtrl, pVoice );
	if( pDstCtrl == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s : %s[%s]", FAILED_CLONESTATIC, Mix::STR_OUTOFMEMORY, Mix::STR_FILEPATH, pSrcCtrl->GetFilePath() );
		pVoice->DestroyVoice();
		return False;
	}

	( *ppDst ) = pDstCtrl;

	return True;
}

Boolean Manager::CloneStreamingController( Mix::IO::IReader* pSrcReader, Mix::Sound::IController** ppDst )
{
	if( pSrcReader->GetSourceType() != Mix::IO::IStream::S_BUFFER )
	{
		return False;
	}

	Mix::IO::IReader* pReader;

	if( pSrcReader->Clone( &pReader ) == False )
	{
		return False;
	}

	if( CreateStreamingController( STR_DUPLICATE, pReader, ppDst ) == False )
	{
		MIX_RELEASE( pReader );
		return False;
	}

	MIX_RELEASE( pReader );

	return True;
}

Boolean Manager::CloneSimpleEmitter( Mix::Sound::Listener* pListener, const wchar_t* pFilePath, const WAVEFORMATEX* pFormat, Mix::Memory::IBuffer* pBuffer, const Mix::Vector3& forward, const Mix::Vector3& up, Mix::Sound::IEmitter** ppDst )
{
	if( CreateSimpleEmitter( STR_DUPLICATE, pFilePath, pListener, pFormat, pBuffer, forward, up, ppDst ) == False )
	{
		return False;
	}

	return True;
}

Boolean Manager::CloneStreamingEmitter( Mix::Sound::Listener* pListener, Mix::IO::IReader* pSrcReader, const Mix::Vector3& localFront, const Mix::Vector3& localUp, Mix::Sound::IEmitter** ppDst )
{
	if( pSrcReader->GetSourceType() != Mix::IO::IStream::S_BUFFER )
	{
		return False;
	}

	Mix::IO::IReader* pReader;

	if( pSrcReader->Clone( &pReader ) == False )
	{
		return False;
	}

	if( CreateStreamingEmitter( STR_DUPLICATE, pListener, pReader, localFront, localUp, ppDst ) == False )
	{
		MIX_RELEASE( pReader );
		return False;
	}

	MIX_RELEASE( pReader );

	return True;
}

void Manager::ThreadMain( void )
{
	TaskList::iterator sst;
	TaskList::iterator sst_end;

	::CoInitialize( NULL );

	while( m_EndEvent.Wait( Manager::SLEEP_TIME ) == False )
	{
		m_TaskListSync.Enter();

		sst = m_TaskList.begin();
		sst_end = m_TaskList.end();

		while( sst != sst_end )
		{
			if( ( *sst )->Process() == True )
			{
				sst++;
			}
			else
			{
				MIX_LIB_DELETE_T( Task, ( *sst ) );
				sst = m_TaskList.erase( sst );
			}
		}

		m_TaskListSync.Leave();
	}

	m_TaskListSync.Enter();
	for( sst = m_TaskList.begin(); sst != m_TaskList.end(); ++sst )
	{
		MIX_LIB_DELETE_T( Task, ( *sst ) );
	}
	m_TaskList.clear();
	m_TaskListSync.Leave();

	::CoUninitialize();
}

void Manager::ThreadEntry_Main( void* pArg )
{
	Manager* pMgr = reinterpret_cast<Manager*>( pArg );
	pMgr->ThreadMain();
}

}}
