#include "stdafx.h"
#include "refCSound.h"

namespace FDK
{
// public:
	CSound::CSound()
	{
		this->Disposed = gcnew CDisposed();
		this->n音量 = 100;
		this->n位置 = 0;
		this->hBassStream = 0xFFFFFFFF;
		this->eデバイス種別 = ESoundDeviceType::Unknown;
		this->strファイル名 = nullptr;
		this->byArrWAVファイルイメージ = nullptr;
	}
	CSound::~CSound()
	{
		if( this->Disposed->bDispose済みまたは処理中である )
			return;		// 例外を発出させない。

		if( !this->Disposed->t開始を宣言する() )
			return;		// 例外を発出させない。


		// this を全インスタンスリストから削除。

		if( nullptr != CSound::listインスタンス && CSound::listインスタンス->Contains( this ) )
			CSound::listインスタンス->Remove( this );


		// その他の Managed の解放。

		if( this->hGC.IsAllocated )
			this->hGC.Free();

		SAFE_REF_DELETE( this->strファイル名 );
		SAFE_REF_DELETE( this->byArrWAVファイルイメージ );


		// ファイナライザを呼び出して Unmanaged リソースを解放したのち、ファイナライザの呼び出しを抑制する。
		this->!CSound();

		//GC::SuppressFinalize( this );   --> コンパイラが勝手にやる
	}
	CSound::!CSound()
	{
		// Unmanaged リソースを解放する。ディスポーザが呼び出されていればファイナライザは呼び出されないようコンパイラが調整する。

		// BASS の解放

		if( 0xFFFFFFFF != this->hBassStream )
		{
			::BASS_Mixer_ChannelRemove( this->hBassStream );
			::BASS_StreamFree( this->hBassStream );
		}
	}

	void CSound::tWASAPIサウンドを作成する( String^ strファイル名, int hMixer, ESoundDeviceType eデバイス種別 )
	{
		this->tWASAPIサウンドを作成する( File::ReadAllBytes( strファイル名 ), hMixer, eデバイス種別 );
	}
	void CSound::tWASAPIサウンドを作成する( array<byte>^ byArrWAVファイルイメージ, int hMixer, ESoundDeviceType eデバイス種別 )
	{
		this->Disposed->tDispose済みまたは実行中なら例外発生( "CSound" );

		try
		{
			// バイト配列をピン留め。

			this->byArrWAVファイルイメージ = byArrWAVファイルイメージ;
			this->hGC = GCHandle::Alloc( byArrWAVファイルイメージ, System::Runtime::InteropServices::GCHandleType::Pinned );


			// BASSファイルストリームを作成。

			this->hBassStream = ::BASS_StreamCreateFile( TRUE, hGC.AddrOfPinnedObject().ToPointer(), 0, byArrWAVファイルイメージ->Length, BASS_STREAM_DECODE | BASS_SAMPLE_FLOAT | BASS_UNICODE );

			if( this->hBassStream == 0 )
				throw gcnew Exception( "サウンドストリームの生成に失敗しました。" );


			// ミキサーにBASSファイルストリームを追加。

			::BASS_Mixer_StreamAddChannel( hMixer, this->hBassStream, BASS_SPEAKER_FRONT | BASS_MIXER_PAUSE );	// PAUSE を指定しないといきなり再生される


			// インスタンスリストに登録。

			CSound::listインスタンス->Add( this );


			// 作成後に設定する。（作成に失敗してると例外発出されてここは実行されない）

			this->eデバイス種別 = eデバイス種別;
		}
		catch(...)
		{
			if( this->hGC.IsAllocated )
				this->hGC.Free();
		}
	}

	void CSound::t先頭から再生する()
	{
		this->Disposed->tDispose済みまたは実行中なら例外発生( "CSound" );

		// ミキサーへ出力開始。

		this->t再生位置を先頭に戻す();
		::BASS_Mixer_ChannelFlags( this->hBassStream, 0, BASS_MIXER_PAUSE );	// BASS_MIXER_PAUSE フラグのみ除去。（自動的に再生が始まる）
	}
	void CSound::t停止する()
	{
		this->Disposed->tDispose済みまたは実行中なら例外発生( "CSound" );

		// ミキサへの出力を停止。

		::BASS_Mixer_ChannelFlags( this->hBassStream, BASS_MIXER_PAUSE, BASS_MIXER_PAUSE );	// BASS_MIXER_PAUSE フラグをセット。
	}
	void CSound::t一時停止する()
	{
		this->Disposed->tDispose済みまたは実行中なら例外発生( "CSound" );

		// ミキサへの出力を一時停止。

		::BASS_Mixer_ChannelFlags( this->hBassStream, BASS_MIXER_PAUSE, BASS_MIXER_PAUSE );	// BASS_MIXER_PAUSE フラグをセット。
	}
	void CSound::t再開する()
	{
		this->Disposed->tDispose済みまたは実行中なら例外発生( "CSound" );

		// ミキサへの出力を再開。

		::BASS_Mixer_ChannelFlags( this->hBassStream, 0, BASS_MIXER_PAUSE );	// BASS_MIXER_PAUSE フラグのみ除去。（自動的に再生が始まる）
	}

	void CSound::t再生位置を先頭に戻す()
	{
		this->Disposed->tDispose済みまたは実行中なら例外発生( "CSound" );

		// 再生位置を先頭に戻す。

		::BASS_Mixer_ChannelSetPosition( this->hBassStream, 0, BASS_POS_BYTE );
	}
	void CSound::t再生位置を変更する( Int64 n位置ms )
	{
		this->Disposed->tDispose済みまたは実行中なら例外発生( "CSound" );

		// 再生位置をmsからbyteに換算して変更する。

		::BASS_Mixer_ChannelSetPosition( this->hBassStream, ::BASS_ChannelSeconds2Bytes( this->hBassStream, n位置ms / 1000.0 ), BASS_POS_BYTE );
	}

// static public:
	ESoundDeviceType CSound::tサウンドデバイスを自動判別する()
	{
		//this->Disposed->tDispose済みまたは実行中なら例外発生( "CSound" );		static なのでノーチェック

		BASS_WASAPI_DEVICEINFO info;
		if( BASS_WASAPI_GetDeviceInfo( 0, &info ) )
		{
			return ESoundDeviceType::ExclusiveWASAPI;
		}

		return ESoundDeviceType::Unknown;
	}
}