/***************************************************************************/
/** @file       CWaveFile.cpp
	@brief      
	@author     shom
	@internal
----------------------------------------------------------------------------
	$id
****************************************************************************/

#include "pch_core.h"
#pragma warning( disable : 4819 )  // Warning”Unicode形式での保存”を無効に
#include "WaveFile.h"
#pragma warning( default : 4819 )

#pragma comment( lib, "winmm.lib" )


/***************************************************************************
	CWaveFile
****************************************************************************/

CWaveFile::CWaveFile()
{
}

CWaveFile::~CWaveFile()
{
}

//-------------------------------------------------------------------
// Waveファイルを開く
//-------------------------------------------------------------------
HRESULT	CWaveFile::Open(
	LPWSTR		WvName,
	theWaveData	*pWvData
	)
{
	///Waveファイルを開く
	pWvData->Hmmio = mmioOpen( WvName,
							   // 追加のパラメータ
							   NULL,		  // なし
							   // フラグ
							   MMIO_ALLOCBUF  // バッファリングされた入出力用に開く
							   | MMIO_READ	  // 読み取り専用として開く
							 );
	if( pWvData->Hmmio == NULL )
	{
		DXTRACE_ERR( _T("FAIL:mmioOpen"), E_FAIL );
	}


	///RIFFチャンクを検索して、子チャンクに降りる、RIFFチャンクの情報を取得する
	pWvData->RiffInfo.fccType	= mmioFOURCC( 'W', 'A', 'V', 'E' );
	if( mmioDescend( pWvData->Hmmio,
					 // チャンク情報の格納先
					 &(pWvData->RiffInfo),
					 // 親チャンクのチャンク情報の格納先
					 NULL,					 // ルートを調べる
					 // 検索フラグ
					 MMIO_FINDRIFF			 // 識別子が”RiFF”で、指定されたフォームタイプのチャンクを検索する
				    ) != MMSYSERR_NOERROR )
	{
		if( FAILED( Close( pWvData ) ) )
		{
			return DXTRACE_ERR( _T("FAIL:Close"), E_FAIL );
		}

		return DXTRACE_ERR( _T("FAIL:mmioDescend <RIFF>"), E_FAIL );
	}

	//有効なファイルか確認する
	if( ( pWvData->RiffInfo.ckid != FOURCC_RIFF )
		|| ( pWvData->RiffInfo.fccType != mmioFOURCC( 'W', 'A', 'V', 'E' ) ) )
	{
		if( FAILED( this->Close( pWvData ) ) )
		{
			return DXTRACE_ERR( _T("FAIL:Close"), E_FAIL );
		}

		return DXTRACE_ERR( _T("FAIL:mmioFOURCC WAVE"), E_FAIL );
	}


	///フォーマットチャンクの検索
	MMCKINFO CkInfo;
	CkInfo.ckid = mmioFOURCC( 'f', 'm', 't', ' ' );	 // ”fmt ”識別子を格納
	if( mmioDescend( pWvData->Hmmio, &CkInfo, &(pWvData->RiffInfo),
					 MMIO_FINDCHUNK ) // 指定された識別子のチャンクを検索
		!= MMSYSERR_NOERROR )
	{
		if( FAILED( this->Close( pWvData ) ) )
		{
			return DXTRACE_ERR( _T("FAIL:Close"), E_FAIL );
		}

		return DXTRACE_ERR( _T("FAIL:mmioDescend <fmt >"), E_FAIL );
	}


	// === フォーマットチャンクのチャンク情報の獲得 ===
	// - フォーマットチャンクが特別なパラメータを持つ場合、処理できない -
	if( CkInfo.cksize < (LONG)sizeof(PCMWAVEFORMAT) )
	{
		if( FAILED( Close( pWvData ) ) )
		{
			return DXTRACE_ERR( _T("FAIL:Close"), E_FAIL );
		}

		return DXTRACE_ERR( _T( "FAIL:sizeof(PCMWAVEFORMAT)" ), E_FAIL );
	}

	// - フォーマットチャンクのチャンク情報を取得する -
	WAVEFORMATEX pcmwf;
	if( mmioRead( pWvData->Hmmio, (HPSTR)&pcmwf, sizeof(pcmwf) ) != sizeof(pcmwf) )
	{
		if( FAILED( Close( pWvData ) ) )
		{
			return DXTRACE_ERR( _T("FAIL:Close"), E_FAIL );
		}

		return DXTRACE_ERR( _T("FAIL:mmioRead"), E_FAIL );
	}

	// - PCMフォーマットでない場合、処理できない -
	if( pcmwf.wFormatTag != WAVE_FORMAT_PCM )
	{
		if( FAILED( Close( pWvData ) ) )
		{
			return DXTRACE_ERR( _T( "FAIL:Close" ), E_FAIL );
		}

		return DXTRACE_ERR( _T( "FAIL:WAVE_FORMAT_PCM" ), E_FAIL );
	}

	// - 取得したチャンク情報を格納する -
	memcpy( &pWvData->WvFormatEx, &pcmwf, sizeof(pcmwf) );
	pWvData->WvFormatEx.cbSize = 0;	 // 追加フォーマットでない


	// === フォーマットチャンクから抜けて、RIFFチャンクに戻る ===
	if( mmioAscend( pWvData->Hmmio, &CkInfo, 0 ) != MMSYSERR_NOERROR )
	{
		if( FAILED( Close( pWvData ) ) )
		{
			return DXTRACE_ERR( _T("FAIL:Close"), E_FAIL );
		}

		return DXTRACE_ERR( _T("FAIL:mmioAscend"), E_FAIL );
	}


	// === 読み出し位置を頭に戻す === 
	if( FAILED( Rewind( pWvData )) )
	{
		if( FAILED( Close( pWvData ) ) )
		{
			return DXTRACE_ERR( _T("FAIL:Close"), E_FAIL );
		}

		return DXTRACE_ERR( _T("FAIL:Rewind"), E_FAIL );
	}

	return S_OK;
}


/**
 * Waveファイルを閉じる
 * 
 * @param pWvData
 * 
 * @return 
 */
HRESULT	CWaveFile::Close(
	theWaveData* pWvData
	)
{
	if( !pWvData->Hmmio )
	{
		return S_FALSE;
	}

	///ファイルを閉じる
	if( FAILED( mmioClose( pWvData->Hmmio, 0 ) ) )
	{
		return DXTRACE_ERR( _T("FAIL:mmioClose"), GetLastError() );
	}

	return S_OK;
}


/**
 * 開いたWaveファイルからデータを読み出す
 * 
 * @param pWvData
 * @param ToReadSz
 * @param pBuf
 * @param pReadedSz
 * 
 * @return 
 */
HRESULT CWaveFile::Read(
					   theWaveData* pWvData,
					   u32        ToReadSz,
					   BYTE*        pBuf,
					   u32*       pReadedSz
					   )
{
	///初期処理
	//エラーチェック
	if( pWvData->Hmmio == NULL || pBuf == NULL || pReadedSz == NULL )
	{
		DXTRACE_ERR( _T(""), E_FAIL );
	}

	// - ToReadSzが0であれば、行う処理はない -
	if( ToReadSz == 0 )
	{
		*pReadedSz	= 0;
		DXTRACE_ERR( _T(""), S_OK );
	}

	// - pReadedSzをゼロリセット -
	*pReadedSz	= 0;


	// === ファイルに関する情報を取得、ファイル入出力バッファへの直接アクセスを開始 ===
	MMIOINFO	Ioinfo;
	if( mmioGetInfo( pWvData->Hmmio, &Ioinfo, 0 ) != 0 )
	{
		DXTRACE_ERR( _T("FAIL:mmioGetInfo"), E_FAIL );
	}


	// === 読み込むサイズを、波形データに合わせる ===
	UINT	ReadBytes	= ToReadSz;
	if( ReadBytes > pWvData->DataInfo.cksize )
	{
		ReadBytes   = pWvData->DataInfo.cksize;
	}


	///データの読み出し
	for( u32 iReadByte=0; iReadByte<ReadBytes; iReadByte++ )
	{
		// - ファイル終端であるか -
		if( Ioinfo.pchNext == Ioinfo.pchEndRead )
		{
			// pchNextとpchEndReadの値を更新する
			if( mmioAdvance( pWvData->Hmmio, &Ioinfo, MMIO_READ ) != 0 )
			{
				DXTRACE_ERR( _T("FAIL:mmioAdvance"), E_FAIL );
			}

			// 更新したにも関わらず、ファイル終端がきている
			if( Ioinfo.pchNext == Ioinfo.pchEndRead )
			{
				DXTRACE_ERR( _T("FAIL:Ioinfo.pchNext"), E_FAIL );
			}
		}

		// - コピー -
		*((BYTE*)pBuf + iReadByte) = *((BYTE*)Ioinfo.pchNext);

		// - 読み進める -
		Ioinfo.pchNext++;
	}


	// === 取得した情報を更新、ファイル入出力バッファへの直接アクセスを終了 ===
	if( mmioSetInfo( pWvData->Hmmio, &Ioinfo, 0 ) != 0 )
	{
		DXTRACE_ERR( _T("FAIL:mmioSetInfo"), E_FAIL );
	}


	// === 実際に読み込まれたデータサイズをReadedSzに格納 ===
	*pReadedSz = ReadBytes;


	return S_OK;
}


/**
 * Waveファイルの読み出し位置を、データチャンクに頭出しする
 * 
 * @param pWvData
 * 
 * @return 
 */
HRESULT CWaveFile::Rewind(
	theWaveData* pWvData
	)
{
	if( pWvData->Hmmio == NULL )
	{
		return DXTRACE_ERR( _T("pWvData->Hmmio is null pointer"), E_FAIL );
	}


	///Waveファイルの巻き戻す
	//ファイル位置をRIFFチャンクの頭に変更
	if( mmioSeek( pWvData->Hmmio,
				  // オフセット
				  pWvData->RiffInfo.dwDataOffset + sizeof(FOURCC),	// RIFFチャンクの頭
				  // オフセットの解釈方法を示すフラグ
				  SEEK_SET )  // ファイルの先頭からオフセットバイトの位置を探す
		== -1 )
	{
		return DXTRACE_ERR( _T("FAIL:mmioSeek"), E_FAIL );
	}


	// === データチャンクを探して、その情報をpWvData.DataInfoに格納する ===
	pWvData->DataInfo.ckid = mmioFOURCC( 'd', 'a', 't', 'a' );
	if( mmioDescend( pWvData->Hmmio, &(pWvData->DataInfo),
					 &(pWvData->RiffInfo), MMIO_FINDCHUNK )
		!= MMSYSERR_NOERROR )
	{
		return DXTRACE_ERR( _T("FAIL:mmioDescend"), E_FAIL );
	}

	return S_OK;
}

