/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
////																					 ////
////																					 ////
////								   - HRecord.cpp -  								 ////
////																					 ////
////																					 ////
////				Implémentation des fonctions SCOL de la librairie sonore			 ////
////									 Version  1.0									 ////
////																					 ////
////								  Hilaire Verschuere								 ////
////																					 ////
////																					 ////
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
#include "../Basic/ZooScene.h"
DEFINE_HGUID(CLSID_DirectSoundCapture, 0xb0210780, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);
DEFINE_HGUID(IID_IDirectSoundCapture, 0xb0210781, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);

//-----------------------------------------------------------------------------
// Global variables
//-----------------------------------------------------------------------------
extern mmachine	mm ;
extern int AsRecorder ;
extern int SoundRec ;
extern int REC_EVT ;
extern int EASYREC_EVT ;
extern HWND hwnd ;


//-----------------------------------------------------------------------------
// Name: HRecord()
// Desc: Constructor
//-----------------------------------------------------------------------------
HRecord::HRecord()
{
	m_pDSCapture		= NULL ;
	m_pBuffer			= NULL ;
	m_pHRecNotify		= NULL ;
	m_pwfx				= NULL ;
	out					= NULL ;
	m_BufferSize		= 0 ;
	m_NotifySize		= 0 ;
	m_NextCaptureOffset = 0 ;
	m_seuil				= 0 ;
	m_retard			= 0 ;
}




//-----------------------------------------------------------------------------
// Name: ~HRecord()
// Desc: Destructor
//-----------------------------------------------------------------------------
HRecord::~HRecord()
{
	Release() ;
}




//-----------------------------------------------------------------------------
// Name: Release()
// Desc: Release resources
//-----------------------------------------------------------------------------
void HRecord::Release()
{
	if( m_pBuffer ) m_pBuffer->Stop() ;
	SAFE_DELETE( m_pHRecNotify ) ;
	SAFE_DELETE_ARRAY( m_pwfx ) ;
	SAFE_DELETE_ARRAY( out ) ;
	SAFE_RELEASE( m_pBuffer ) ;
    SAFE_RELEASE( m_pDSCapture ) ; 
}



//-----------------------------------------------------------------------------
// Name: HRecord::Create()
// Desc: Create the DirectSound Capture buffer
//-----------------------------------------------------------------------------
HRESULT HRecord::Init( int nbChannels, int bitPerSample, int frequence, int seuil )
{   
   	Release() ; // Reinitialisation if Create has ever been launch

	CoInitialize (NULL);
	
	if( FAILED( CoCreateInstance( (REFCLSID)CLSID_DirectSoundCapture, NULL, CLSCTX_INPROC,	(REFIID)IID_IDirectSoundCapture, (LPVOID*)&m_pDSCapture) ) )
	{
		//MessageBox(NULL,"DirectX is not properly installed.","Error",MB_OK|MB_ICONERROR) ;
		MMechostr( 1, "DirectX is not properly installed.\n" ) ;
		m_pDSCapture = NULL ;
		return S_FALSE ;
	}
	
	//if( FAILED( DirectSoundCaptureCreate( NULL, &m_pDSCapture, NULL ) ) ) 
	if( FAILED( m_pDSCapture->Initialize( NULL ) ) ) 
	{
		SAFE_DELETE( m_pDSCapture ) ;
		return ERRMSG( "DirectSoundCaptureCreate " ) ;
	}

	if( (nbChannels!=1 && nbChannels!=2) || (bitPerSample!=8 && bitPerSample!=16) || frequence<5000 || frequence>48000 ) 
	{
		MMechostr( 1, "Error in setting record parameters\n" ) ; 
		return S_FALSE ;
	}

	// setup wave format
	m_pwfx = (WAVEFORMATEX*)new CHAR[ sizeof(WAVEFORMATEX) ] ;
	memset(m_pwfx, 0, sizeof(PCMWAVEFORMAT)) ;
	m_pwfx->wFormatTag	  = WAVE_FORMAT_PCM ;
	m_pwfx->nChannels		  = nbChannels ;
	m_pwfx->wBitsPerSample  = bitPerSample ;
	m_pwfx->nSamplesPerSec  = frequence ;
	m_pwfx->nBlockAlign	  = ( bitPerSample / 8 ) * nbChannels ;
	m_pwfx->nAvgBytesPerSec = frequence * m_pwfx->nBlockAlign ;
	m_pwfx->cbSize		  = 0 ;

	// Set the bit per sample
	m_bitPerSample = bitPerSample ;

	// Set the threshold for silence detection
	if( seuil < 0 ) seuil = 0 ;
	else if( seuil > 100 ) seuil = 100 ;
	m_seuil = (float)( pow( seuil, 4 ) ) * (float)( bitPerSample == 8 ? 128 * 128 : 32768 * 32768 ) / 100000000.0 ;


	// Setting the position in the buffer where the record begins
	m_NextCaptureOffset = 0 ;
	
	return S_OK ;
}



//-----------------------------------------------------------------------------
// Name: HRecord::Create()
// Desc: Create the DirectSound Capture buffer
//-----------------------------------------------------------------------------
HRESULT HRecord::Create(int size, int obj  )
{   
	// Setting reflex
	m_obj = obj ;

    // Convert the size from millisecondes to Bytes to set the notification size
	m_NotifySize = size ;
	out = new char[m_NotifySize] ;
	
	// Set the buffer sizes 
    m_BufferSize = m_NotifySize * NB_REC_NOTIFICATIONS ;

	// Create the capture buffer
	DSCBUFFERDESC dscbd ;
    ZeroMemory( &dscbd, sizeof(dscbd) ) ;
    dscbd.dwSize        = sizeof(dscbd) ;
	dscbd.dwFlags		= DSCBCAPS_WAVEMAPPED ;
    dscbd.dwBufferBytes = m_BufferSize ;
    dscbd.lpwfxFormat   = m_pwfx ;

    if( FAILED( m_pDSCapture->CreateCaptureBuffer( &dscbd, &m_pBuffer, NULL ) ) ) 
	{
		m_pBuffer = NULL ;
		return ERRMSG( "CreateCaptureBuffer Record");
	}

	m_pHRecNotify = new HRecNotify( this ) ;
	if( FAILED( m_pHRecNotify->SetNotify( m_pBuffer, m_NotifySize, NB_REC_NOTIFICATIONS ) ) ) return ERRMSG("AddNotify Failed" ) ;
	if( FAILED( m_pHRecNotify->Start() ) ) return ERRMSG("Start Notify Thread Failed" ) ;
	
	return S_OK ;
}





//-----------------------------------------------------------------------------
// Name: HRecord::Start()
// Desc: Start recording sound
//-----------------------------------------------------------------------------
HRESULT HRecord::Start()
{  
	if( !m_pBuffer ) return S_FALSE ;
	if( IsRecording() ) return S_OK ;

	if( FAILED( m_pBuffer->Start( DSCBSTART_LOOPING ) ) ) return ERRMSG( "Start Capture" ) ;

	return S_OK ;
}



//-----------------------------------------------------------------------------
// Name: Stop()
// Desc: Stop recording sound
//-----------------------------------------------------------------------------
HRESULT HRecord::Stop()
{  
	if( !m_pBuffer ) return S_FALSE ;
	if( !IsRecording() ) return S_OK ;

	if( FAILED( m_pBuffer->Stop() ) ) return ERRMSG( "Stop Capture" ) ;

	return S_OK ;
}



//-----------------------------------------------------------------------------
// Name: HRecord::IsRecording()
// Desc: Is the capture interface recording sound.
//-----------------------------------------------------------------------------
inline bool HRecord::IsRecording()
{  
	if( !m_pBuffer ) return false ;

	DWORD Status = 0 ;

	if( FAILED( m_pBuffer->GetStatus( &Status ) ) ) { ERRMSG( "IsRecording" ) ; return false ; }

	return Status & DSCBSTATUS_CAPTURING ;
}




//-----------------------------------------------------------------------------
// Name: HRecord::RecordCapturedData()
// Desc: Copies data from the capture buffer to the output buffer 
//-----------------------------------------------------------------------------
int HRecord::RecordCapturedData() 
{
    VOID*   pDSLockedBuffer   = NULL ;  // Pointer to the locked buffer memory
    DWORD   dwDSLockedBufferSize ;		// Size of the locked DirectSound capture buffer

    if( NULL == m_pBuffer )  return S_FALSE;
    
	if( FAILED( m_pBuffer->Lock( m_NextCaptureOffset, m_NotifySize, &pDSLockedBuffer, &dwDSLockedBufferSize, NULL, NULL, 0L ) ) )
		return ERRMSG( "Lock Handle" ) ;

	// if sound is detected in recorder, send it to the callback
	if( IsSpeeching( (unsigned char*)pDSLockedBuffer, dwDSLockedBufferSize ) )
	{
		_memcpy( out, (char*)pDSLockedBuffer, m_NotifySize ) ;
		PostMessage( hwnd, ((m_obj == AsRecorder) ? REC_EVT : EASYREC_EVT), (int)this, NULL ) ;
	}

    // Unlock the capture buffer
    m_pBuffer->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 ) ;

	// Update where the buffer will lock (for next time)
    m_NextCaptureOffset += m_NotifySize ; 
    m_NextCaptureOffset %= m_BufferSize ; // Circular buffer

	return S_OK ;
}



//-----------------------------------------------------------------------------
// Name: HRecord::IsSpeeching()
// Desc: Detect if sound is recording using power of the signal
//-----------------------------------------------------------------------------
bool HRecord::IsSpeeching( unsigned char* pBufferData, unsigned long sizeData )
{
	if( !m_seuil ) return true ;

	short s ;
	float pui = 0 ;
	float offset = 0 ;
	unsigned long size = sizeData ;
	unsigned char* pBuffer = pBufferData ;

	if( m_bitPerSample == 8 )
	{// 8 bits
		// Getting the offset of the recorder
		while( size )
		{
			s = *pBuffer - 128  ;
			pBuffer++ ;
			size-- ;
			offset += (float)s ;
		}

		offset /= (float)sizeData ;

		size = sizeData ;
		pBuffer = pBufferData ;
		
		// Getting the power of signal
		while( size )
		{
			s = *pBuffer - 128 - offset ;
			pBuffer++ ;
			size-- ;
			pui += (float)( s * s ) ;
		}
		pui /= (float)sizeData ;
	}
	else
	{// 16 bits
		// Getting the offset of the recorder
		while( size )
		{
			s = pBuffer[1]<<8 | pBuffer[0]  ;
			pBuffer += 2 ;
			size -= 2 ;
			offset += (float)s ;
		}

		offset /= (float)(sizeData / 2) ;

		size = sizeData ;
		pBuffer = pBufferData ;

		// Getting the power of signal
		while( size )
		{
			s = ( (pBuffer[1]<<8 | pBuffer[0]) - offset ) ;
			pBuffer += 2 ;
			size -= 2 ;
			pui += (float)( s * s ) ;
		}
		pui /= ( (float)sizeData / 2.0 ) ;
	}

	if( pui < m_seuil ) //return false ;
	{
		if( m_retard == 0 ) return false ;
		else
		{
			m_retard-- ;
			return true ;
		}
	}
	
	
	// retard ŕ la fermeture de l'enregistrement pour ne pas couper brutalement le son
	m_retard = 10 ;

	return true ;
}

