//********************************************************************************************
/**
 * @file		wavvol.h
 * @brief		WAVE波形から音量を抽出するクラス.
	
		大部分が Windows API サンプルそのままなので書式が古いです.
 * 
 */
//********************************************************************************************

#ifndef	_WAVVOL_H_
#define	_WAVVOL_H_

//********************************************************************************************
/**
 * @class		WAVEVOL
 * @brief		
 * @author		N.Takeda
 * @date		2011/06/13(月) 16:19:31
 */
//********************************************************************************************
class WAVEVOL
{
public:
	enum EnDataKind
	{
		enDataKindBiggest,
		enDataKindAverage,
		enDataKindBit,
		enDataKindMax,
	};
	
private:
/*
	unsigned long	get_ulong( FILE *_f );
	unsigned		get_ushort( FILE *_f );
	unsigned		get_ubyte( FILE *_f );
	void			AllocateMemory();
*/
	unsigned long get_ulong( FILE *_f )
	{
		unsigned char ss[4];
		unsigned res;
		
		if (fread(ss, 4, 1, _f) != 1) {
			res = 0;
		} else {
			res = ss[0] + 256LU * (ss[1] + 256LU * (ss[2] + 256LU * ss[3]));
		}
		return res;
	}

	unsigned get_ushort( FILE *_f )
	{
		unsigned char ss[2];
		unsigned res;
		
		if (fread(ss, 2, 1, _f) != 1) {
			res = 0;
		} else {
			res = ss[0] + 256U * ss[1];
		}
		return res;
	}

	unsigned get_ubyte( FILE *_f )
	{
		unsigned char ss[1];
		unsigned res;
		
		if (fread(ss, 1, 1, _f) != 1) {
			res = 0;
		} else {
			res = ss[0];
		}
		return res;
	}

	void	AllocateMemory()
	{
		if( m_bAllocated )
		{
			free(m_pInWave);
			free(m_pInWaveR);
		}
		m_pInWave = (Float64 *)calloc(m_iWaveSize, sizeof(Float64));
		m_pInWaveR = (Float64 *)calloc(m_iWaveSize, sizeof(Float64));
		m_bAllocated = TRUE;
	}
	
	
	long			m_iBytesInFile;
	short			m_iChBytesPerSample;
	short			m_iBytesPerSample;
	long			m_iBytesPerSec;
	
	short			m_iMono1Stereo2;
	long			m_iSamplingFreq;
	short			m_iBitLength;
	long			m_iTotalBytes;
	long			m_iWaveSize;
	
	
	//	元のサンプルの名残り
	Float64			*m_pInWave;
	Float64			*m_pInWaveR;					//ステレオの場合 Rの波形データ
	BOOL			m_bAllocated;
	BOOL			m_bLoaded;
	
public:
	//! ------- setters ---------
	
	//! ------- getters ---------
	long	GetWaveSize() const {	return m_iWaveSize;	}
	short	GetMono1Stereo2() const {	return m_iMono1Stereo2;	}
	long	GetSamplingFreq() const {	return m_iSamplingFreq;	}
	short	GetBitLength() const {	return m_iBitLength;	}
	long	GetTotalBytes() const {	return m_iTotalBytes;	}
	
	Float64*	GetWave( const Int32 _iR = FALSE ) const {	if(_iR) return m_pInWaveR;	else	return m_pInWave;	}
	
//	Int32		ConvWav2Vol( LPCTSTR _pFileNameId, char *_pMem, Int32 _iBuffLength, Int32 _iVolperFrame, Int32 _iPar, Int32 _iKind );
	//********************************************************************************************
	/**
	 * @brief		ConvWav2Vol関数.
	 * @author		G.Nanashi
	 * @date		2011/06/13(月) 15:39:10
	 *
	 * @param[in]		LPCTSTR	_pFileNameId	ファイル名ID.
	 * @param[in,out]	char*	_pMem			.
	 * @param[in]		Int32	_iBuffLength	バッファ長さ.
	 * @param[in]		Int32	_iVolperFrame	.
	 * @param[in]		Int32	_iPar			.
	 * @param[in]		Int32	_iKind			.
	 *
	 * @return		Int32	
	 */
	//********************************************************************************************
	Int32	ConvWav2Vol( LPCTSTR _pFileNameId, char *_pMem, Int32 _iBuffLength, Int32 _iVolperFrame, Int32 _iPar, Int32 _iKind )
	{
		unsigned long	uLen;
		unsigned char	ss[ 10 ];
		FILE			*ifp = NULL;
		Int32			iVolSize;
		
		if(_iVolperFrame <= 0 || _iVolperFrame > 60)	return 0;
		
	#ifdef	_UNICODE
		CTOSJIS	toJis;
		if(fopen_s( &ifp, toJis.From(_pFileNameId), "rb" ) == 0)
	#else
		if(fopen_s( &ifp, _pFileNameId, "rb" ) == 0)
	#endif
		{
			fread( ss, 4, 1, ifp );					///< "RIFF" 
			m_iBytesInFile = get_ulong(ifp);		///< total bytes in the file
			fread(ss, 8, 1, ifp);					///< "WAVEfmt" 
			uLen = get_ulong(ifp);					///< 16 -- always
			uLen = get_ushort(ifp);					///< 1: pcm
			m_iMono1Stereo2 = get_ushort(ifp);		///< 1: mono, 2; stereo
			m_iSamplingFreq = get_ulong(ifp);		///< sampling frequency
			if(m_iSamplingFreq == 0){
				/// 有効なwav形式ではない.
				return -1;
			}
			m_iBytesPerSec = get_ulong(ifp);		///< bytes per second
			m_iChBytesPerSample = get_ushort(ifp);	///< bytes per sample
			m_iBitLength = get_ushort(ifp);			///< bit length
			do{										///< look for "data" chunk
				fread(ss, 4, 1, ifp);				///< expect "data"
				if(((ss[0] == 'd')&(ss[1] == 'a')&(ss[2] == 't')&(ss[3] == 'a')) == 0){	///< step over chunks unless it's "data" chunk
					uLen = get_ulong(ifp);
					fseek(ifp, uLen, SEEK_CUR);
				}
				else{
					break;
				}
			}while(1);
			
			m_iTotalBytes = get_ulong(ifp);			///< total bytes of wav data
			m_iBytesPerSample = m_iBitLength / 8;
			m_iWaveSize = m_iTotalBytes / (m_iMono1Stereo2 * m_iBytesPerSample);
			
			Int32		ii, m;
			short	x;
			
			AllocateMemory();
			if (m_iMono1Stereo2 == 1) {
				for (ii = 0; ii < m_iWaveSize; ii++) {	///< read mono wav data
					if (fread(ss, m_iBytesPerSample, 1, ifp) != 1) break;
					if (m_iBytesPerSample == 2) x = (short)(ss[0] + 256 * ss[1]);
					else x = (short)(ss[0]) - 128;
					m_pInWave[ii] = (Float64)(x);
				}
			}
			else{
				for(ii = 0; ii < m_iWaveSize * 2; ii++){	///< read stereo wav data
					m = ii / 2;
					if (fread(ss, m_iBytesPerSample, 1, ifp) != 1) break;
					if (m_iBytesPerSample == 2) x = (short)(ss[0] + 256 * ss[1]);
					else x = (short)(ss[0]) - 128;
					if(ii%2==0) m_pInWave[m] = (Float64)(x);
					else m_pInWaveR[m] = (Float64)(x);
				}
			}
			fclose( ifp );
			m_bLoaded = TRUE;
		}else{
			return 0;
		}
		
		#define	DATA_SEC	(m_iWaveSize/(Float64)m_iSamplingFreq)		///< 秒.
		
		Int32		ii, jj, iFrame;
		u_long		dat;
		
		
		iFrame = 60 / _iVolperFrame;
		iVolSize = (Int32)(iFrame * DATA_SEC);
		
		
		/// 音量データにコンバート.
		
		if( _iKind != 2 )	///< 平均、最大値をとる
		{
			if( !_pMem )	return iVolSize;	///< ポインタNULL でサイズ取得.
			if( iVolSize > _iBuffLength )	return 0;
			
			memset( _pMem, 0, iVolSize );
			
			for( ii=0 ; ii<iVolSize ; ii++ )
			{
				dat = 0;
				switch(_iKind)
				{
					case 0:	///< 平均値をとる
						for( jj=0 ; jj<(Float64)m_iSamplingFreq / iFrame ; jj++ )
						{
							dat += abs( (Int32)m_pInWave[ (Int32)((Float64)m_iSamplingFreq / iFrame * ii + jj) ] );
						}
						dat /= (Int32)((Float64)m_iSamplingFreq / iFrame);
						break;
					case 1:	///< 最大値をとる
						for( jj=0 ; jj<(Float64)m_iSamplingFreq / iFrame ; jj++ )
						{
							if( (Int32)dat < abs((Int32)m_pInWave[ (Int32)((Float64)m_iSamplingFreq / iFrame * ii + jj) ]) )
								dat = abs( (Int32)m_pInWave[ (Int32)((Float64)m_iSamplingFreq / iFrame * ii + jj) ] );
						}
						break;
				}
				
				*(_pMem + ii) = (u_char)dat;
			}
		}else{			///< 単純２値をとる
			Int32	iDataSize = ( iVolSize + 7 ) / 8;
			
			if( !_pMem )	return iDataSize;	///< ポインタNULL でサイズ取得.
			if( iDataSize > _iBuffLength )	return 0;
			
			memset( _pMem, 0, iDataSize );
			
			for( ii=0 ; ii<iVolSize ; ii++ )
			{
				dat = 0;
				for( jj=0 ; jj<(Float64)m_iSamplingFreq / iFrame ; jj++ )
				{
					dat += abs( (Int32)m_pInWave[ (Int32)((Float64)m_iSamplingFreq / iFrame * ii + jj) ] );
				}
				dat /= (Int32)((Float64)m_iSamplingFreq / iFrame);	///< 一度平均値をとる.
				
				/// ２値化.
				if( (Int32)dat < 255*_iPar/100 )	dat = 0;
				else								dat = 1;
				
				bitcontrol8( (BYTE*)_pMem, 0, ii, static_cast<EnBitCtrl>(dat) );
			}
			
		}
		
		return iVolSize;
	}
	
	//********************************************************************************************
	/**
	 * @brief		コンストラクタ.
	 * @author		N.Takeda
	 * @date		2011/06/15(水) 22:40:39
	 *
	 * @return		なし (none)
	 */
	//********************************************************************************************
	WAVEVOL()
	 : m_pInWave( NULL )		///< 波形データ
	 , m_pInWaveR( NULL )		///< ステレオの場合 Rの波形データ
	 , m_iTotalBytes( 11025 )
	 , m_iBitLength( 8 )		///< サンプリングビット数
	 , m_iSamplingFreq( 11025 )	///< サンプリングレート
	 , m_iMono1Stereo2( 1 )		///< モノラル or ステレオ
	 , m_iWaveSize( 0 )
	{
		
		m_iBytesInFile = 11025;
		m_iChBytesPerSample = 1;
		m_iBytesPerSample = 1;
		m_iBytesPerSec = 11025;
		m_iWaveSize = 11025;
		m_bAllocated = FALSE;
		m_bLoaded = FALSE;
		
	}
	
	//********************************************************************************************
	/**
	 * @brief		デストラクタ.
	 * @author		N.Takeda
	 * @date		2011/06/15(水) 22:40:46
	 *
	 * @return		なし (none)
	 */
	//********************************************************************************************
	virtual ~WAVEVOL()
	{
		
		if( m_bAllocated )
		{
			free(m_pInWave);
			free(m_pInWaveR);
		}
	}
	
};




#endif	//_WAVVOL_H_
