/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
////																					 ////
////																					 ////
////								   - HSound.cpp -   	 							 ////
////																					 ////
////																					 ////
////				Implémentation des fonctions SCOL de la librairie sonore			 ////
////									 Version  1.0									 ////
////																					 ////
////								  Hilaire Verschuere								 ////
////																					 ////
////																					 ////
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////


#include "../Basic/ZooWorld.h"
DEFINE_HGUID(IID_IDirectSound3DBuffer, 0x279AFA86, 0x4981, 0x11CE, 0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60);

//-----------------------------------------------------------------------------
// Global variables
//-----------------------------------------------------------------------------
extern mmachine	mm ;
extern int SND_TIME ;
extern int SND_NEXT ;
extern HWND hwnd ;
extern HSoundManager* pHSoundManager ;



//-----------------------------------------------------------------------------
// Name: HSound::HSound()
// Constructor
//-----------------------------------------------------------------------------
HSound::HSound( bool addToList )
{
	m_pBuffer			= NULL ;
	m_p3DBuffer			= NULL ;
	m_dwDSBufferSize	= 0	;
	m_pwfx				= NULL ;
	m_fileName			= NULL ;
	m_nextFileName		= NULL ;
	m_nextUsed			= false ;
	m_nextState			= true ;
	pHSndNotify			= NULL ;
	m_dwSize			= 0	;
	m_nextSize			= 0 ;
	m_lastPlayPos		= 0	;
    m_dwNextWriteOffset	= 0	;
	m_handlerEvtEnd		= 0 ;
	m_volumeCone		= INIT_VOLUME_CONE ;

	m_dwReadOffset		= 0	;
	m_dwNotifySize		= 0	;
	m_streaming			= false	;
	m_loop				= false ;
	m_playing			= false ;
	sndLocked   		= false ;

	WaveType			= false ;
	Mp3Type				= false ;
	DataType			= 0 ;
	AudioType			= false ;

	isInList			= addToList ;

	if( isInList ) pHSoundManager->listHSound.push_front(this) ;
}



//-----------------------------------------------------------------------------
// Name: HSound::HSound()
// Copy Constructor
//-----------------------------------------------------------------------------
HSound::HSound( const HSound &h )
{
	m_pBuffer = NULL ;
	if( !h.m_streaming ) pHSoundManager->GetDirectSound()->DuplicateSoundBuffer( h.m_pBuffer, &m_pBuffer ) ;
	m_p3DBuffer	= NULL ;
	if( h.m_p3DBuffer ) Get3DBufferInterface() ;

	m_dwDSBufferSize = h.m_dwDSBufferSize ;
	
	m_pwfx = (WAVEFORMATEX*)new CHAR[ sizeof(WAVEFORMATEX) ];
	_memcpy( (char*)m_pwfx, (char*)h.m_pwfx, sizeof(PCMWAVEFORMAT) ) ;

	m_fileName			= (char*)new char( strlen( h.m_fileName ) + 1 ) ;
	strcpy( m_fileName, h.m_fileName ) ;
	m_nextFileName		= (char*)new char( strlen( h.m_nextFileName ) + 1 ) ;
	strcpy( m_nextFileName, h.m_nextFileName ) ;
	
	pHSndNotify			= NULL ;
	m_dwSize			= h.m_dwSize ;
	m_nextSize			= h.m_nextSize ;
	m_nextUsed			= h.m_nextUsed ;
	m_nextState			= h.m_nextState ;
	m_dwNotifySize		= h.m_dwNotifySize ;
	m_dwReadOffset		= 0 ;
	m_lastPlayPos		= 0	;
    m_dwNextWriteOffset	= 0	;
	m_handlerEvtEnd		= 0 ;
	m_volumeCone		= h.m_volumeCone ;

	m_streaming			= h.m_streaming	;
	m_loop				= h.m_loop ;
	m_playing			= false ;
	sndLocked   		= false ;

	WaveType			= false ;
	Mp3Type				= false ;
	DataType			= 0 ;
	AudioType			= false ;

	isInList			= h.isInList ;

	if( isInList ) pHSoundManager->listHSound.push_front(this) ;

}



//-----------------------------------------------------------------------------
// Name: HSound::~HSound()
// Destructor
//-----------------------------------------------------------------------------
HSound::~HSound()
{
	if( isInList ) pHSoundManager->listHSound.remove(this) ;

	SAFE_DELETE_ARRAY( m_fileName ) ;
	SAFE_DELETE_ARRAY( m_nextFileName ) ;
	SAFE_DELETE_ARRAY( m_pwfx ) ;
    SAFE_RELEASE( m_pBuffer ) ;
}
	
	



//-----------------------------------------------------------------------------
// Name: HSound::CreateStatic()
// Create static sound ( size of buffer is equal t size of data )
//-----------------------------------------------------------------------------
HRESULT HSound::CreateStatic( DWORD flag )
{
    // Figure out how big the DSound buffer should be 
    m_dwDSBufferSize = m_dwSize ;

	// setting the streaming flag
	m_streaming	= false ;

    // Set up the direct sound buffer.  Request the NOTIFY flag, so that we are notified as the sound buffer plays.  
	// Note, that using this flag may limit the amount of hardware acceleration that can occur. 
    DSBUFFERDESC dsbd;
    ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) ) ;
    dsbd.dwSize          = sizeof(DSBUFFERDESC) ;
    dsbd.dwFlags         = flag | DSBCAPS_STATIC  ;
    dsbd.dwBufferBytes   = m_dwDSBufferSize ;
    dsbd.lpwfxFormat     = m_pwfx ;

    if( FAILED( pHSoundManager->GetDirectSound()->CreateSoundBuffer( &dsbd, &m_pBuffer, NULL ) ) )
	{
		m_pBuffer = NULL ;
        return ERRMSG( "CSoundManager::Create" );
	}
	
	// Create a the 3D buffer if it's needed
	if( flag & DSBCAPS_3D ) if( FAILED( Get3DBufferInterface() ) )
	{
		m_pBuffer = NULL ;
        return ERRMSG( "CSoundManager::Get3DBufferInterface" );
	}
	
    return S_OK;
}



//-----------------------------------------------------------------------------
// Name: HSound::CreateStreaming()
// Create streaming sound ( usually when size of data is very importante )
//-----------------------------------------------------------------------------
HRESULT HSound::CreateStreaming( DWORD flag )
{
	// Figure out how big the DSound buffer should be
    m_dwDSBufferSize = m_dwNotifySize * NB_PLAY_NOTIFICATIONS ;

	// setting the streaming flag
	m_streaming	= true ;

    // Set up the direct sound buffer.  Request the NOTIFY flag, so that we are notified as the sound buffer plays.
	// Note, that using this flag may limit the amount of hardware acceleration that can occur. 
    DSBUFFERDESC dsbd;
    ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) ) ;
    dsbd.dwSize          = sizeof(DSBUFFERDESC) ;
    dsbd.dwFlags         = flag | DSBCAPS_LOCSOFTWARE ;
    dsbd.dwBufferBytes   = m_dwDSBufferSize ;
    dsbd.lpwfxFormat     = m_pwfx ;
	
    if( FAILED( pHSoundManager->GetDirectSound()->CreateSoundBuffer( &dsbd, &m_pBuffer, NULL ) ) )
	{
		m_pBuffer = NULL ;
		return ERRMSG( "CSoundManager::CreateStreaming CreateSoundBuffer 2" ) ;
	}

	// Create a the 3D buffer if it's needed
	if( flag & DSBCAPS_3D ) if( FAILED( Get3DBufferInterface() ) )
	{
		m_pBuffer = NULL ;
        return ERRMSG( "CSoundManager::Get3DBufferInterface" );
	}

	pHSndNotify = new HSndNotify( this ) ;
	if( !(DataType & AS_SND_LINEAR) && !AudioType ) pHSndNotify->AddEvt( m_dwSize - 1, true ) ;
	if( FAILED( pHSndNotify->SetNotify() ) ) return ERRMSG("SetNotify Failed" ) ;
	if( FAILED( pHSndNotify->Start() ) ) return ERRMSG("Start Notify Thread Failed" ) ;

    return S_OK;
}





//-----------------------------------------------------------------------------
// Name: HSound::Get3DBufferInterface()
// Desc: Create 3D capacity for sound
//-----------------------------------------------------------------------------
HRESULT HSound::Get3DBufferInterface()
{
    if( !m_pBuffer ) return S_FALSE;
	
	SAFE_RELEASE( m_p3DBuffer ) ;

	if( FAILED( m_pBuffer->QueryInterface( IID_IDirectSound3DBuffer, (VOID**)&m_p3DBuffer ) ) )
	{
		m_p3DBuffer = NULL ;
		return ERRMSG("3D sound Interface failed" ) ;
	}

	return S_OK ;
}





//-----------------------------------------------------------------------------
// Name: HSound::Play()
// Desc: Plays the sound. Pass true to loop the sound.
//-----------------------------------------------------------------------------
HRESULT HSound::Play(bool loop)
{
	m_playing = true ;

    if( !m_pBuffer || !pHSoundManager->IsEnable() ) return S_FALSE ;
	
	m_loop = loop ;

    if( FAILED( m_pBuffer->Play( 0, 0, m_streaming ? DSBPLAY_LOOPING : loop ) ) ) return S_FALSE ;

	return S_OK ;
}





//-----------------------------------------------------------------------------
// Name: HSound::Stop()
// Desc: Stops the sound from playing
//-----------------------------------------------------------------------------
HRESULT HSound::Stop()
{
	m_playing = false ;

    if( !m_pBuffer ) return S_FALSE ;

	if( !IsSoundPlaying() ) return S_OK ;

	if( FAILED( m_pBuffer->Stop() ) ) return S_FALSE ;

	return S_OK ;
}




//-----------------------------------------------------------------------------
// Name: CStreamingSound::Reset()
// Desc: Resets the sound so it will begin playing at the beginning
//-----------------------------------------------------------------------------
HRESULT HSound::Reset()
{
    if( !m_pBuffer ) return S_FALSE ;

	if( FAILED( m_pBuffer->SetCurrentPosition( 0L ) ) ) return ERRMSG( "SetCurrentPosition( 0L )" ) ;
	
	if( m_streaming )
	{
		m_lastPlayPos		= 0 ;
		m_dwNextWriteOffset = 0 ;
		m_dwReadOffset		= 0	;

		if( FAILED( ResetFile() ) ) return ERRMSG("ResetFile") ;
		if( FAILED( FillBufferWithSound( 0L, m_dwDSBufferSize ) ) ) return ERRMSG( "FillBufferWithSound" ) ;
	}

	return S_OK ;
}




//-----------------------------------------------------------------------------
// Name: HSound::FillBufferWithSound()
// Desc: Fills a DirectSound buffer with a sound file 
//-----------------------------------------------------------------------------
HRESULT HSound::FillBufferWithSound( long position, long sizeToFill )
{
    VOID*   pDSLockedBuffer1      = NULL; // Pointer to the first locked buffer memory
	VOID*   pDSLockedBuffer2      = NULL; // Pointer to the second locked buffer memory
    DWORD   dwDSLockedBufferSize1 = 0;    // Size of the first locked DirectSound buffer
	DWORD   dwDSLockedBufferSize2 = 0;    // Size of the second locked DirectSound buffer
	DWORD   dwDSLockedBufferSize  = 0;    // Size of the locked DirectSound buffer
    DWORD   dwDataRead1			  = 0;    // Amount of data read from the file 
	DWORD   dwDataRead2			  = 0;    // Amount of data read from the file 

    if( m_pBuffer == NULL ) return S_FALSE;

	if( !sizeToFill ) return S_OK ;

    // Lock the buffer to write in
	while( sndLocked ) Sleep( 10 ) ;
	sndLocked = true ;
    if( FAILED( m_pBuffer->Lock( position, sizeToFill, &pDSLockedBuffer1, &dwDSLockedBufferSize1, &pDSLockedBuffer2, &dwDSLockedBufferSize2, 0L ) ) )
		return ERRMSG( "Lock Filling" ) ;
	
    if( dwDSLockedBufferSize1 ) dwDataRead1 = Read( (char*)pDSLockedBuffer1, dwDSLockedBufferSize1 ) ;
	if( dwDSLockedBufferSize2 ) dwDataRead2 = Read( (char*)pDSLockedBuffer2, dwDSLockedBufferSize2 ) ;
	
	if( ((int)dwDataRead1 == -1) || ((int)dwDataRead2 == -1) ) 
	{
		// Unlock the buffer, we don't need it anymore.
		m_pBuffer->Unlock( pDSLockedBuffer1, dwDSLockedBufferSize1, pDSLockedBuffer2, dwDSLockedBufferSize2 );
		sndLocked = false ;
		return S_FALSE ;
	}
   
	// No more data, fill in silence 
    if( dwDataRead1 < dwDSLockedBufferSize1 )
    {  
		if( m_nextUsed )
		{
			int fadeSize = MsToChar( m_pwfx, 1 ) ;
			ApplyFadeOut( (unsigned char*)pDSLockedBuffer1 + dwDataRead1 - fadeSize, fadeSize, 0, 0 ) ;

			SwitchFile() ;

			Read( (char*)pDSLockedBuffer1 + dwDataRead1, dwDSLockedBufferSize1 - dwDataRead1 ) ;
			Read( (char*)pDSLockedBuffer2 + dwDataRead2, dwDSLockedBufferSize2 - dwDataRead2 ) ;
			ApplyFadeIn( (unsigned char*)pDSLockedBuffer1 + dwDataRead1, fadeSize ) ;
		}
		else
		{
			_FillMemory( (char*)pDSLockedBuffer1 + dwDataRead1, dwDSLockedBufferSize1 - dwDataRead1, (BYTE)(m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) ) ;
			_FillMemory( (char*)pDSLockedBuffer2 + dwDataRead2, dwDSLockedBufferSize2 - dwDataRead2, (BYTE)(m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) ) ;
		}
	}
	else if( dwDataRead2 < dwDSLockedBufferSize2 )
	{
		if( m_nextUsed )
		{
			int fadeSize = MsToChar( m_pwfx, 1 ) ;
			ApplyFadeOut( (unsigned char*)pDSLockedBuffer2 + dwDataRead2 - fadeSize, fadeSize, 0, 0 ) ;

			SwitchFile() ;

			Read( (char*)pDSLockedBuffer2 + dwDataRead2, dwDSLockedBufferSize2 - dwDataRead2 ) ;
			ApplyFadeIn( (unsigned char*)pDSLockedBuffer2 + dwDataRead2, fadeSize ) ;
		}
		else
		{
			_FillMemory( (char*)pDSLockedBuffer2 + dwDataRead2, dwDSLockedBufferSize2 - dwDataRead2, (BYTE)(m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) ) ;
		}
	}

    // Unlock the buffer, we don't need it anymore.
    m_pBuffer->Unlock( pDSLockedBuffer1, dwDSLockedBufferSize1, pDSLockedBuffer2, dwDSLockedBufferSize2 );
	sndLocked = false ;

    return S_OK;
}





//-----------------------------------------------------------------------------
// Name: HSound::HandleStreamNotification()
// Desc: Handle the notification that tell us to put more wav data in the 
//       circular buffer
//-----------------------------------------------------------------------------
HRESULT HSound::HandleStreamNotification()
{
    VOID*   pDSLockedBuffer = NULL ;  // Pointer to the locked buffer memory
    DWORD   dwDSLockedBufferSize ;    // Size of the locked DirectSound buffer

    if( !m_pBuffer ) return S_FALSE ;

	if( !IsSoundPlaying() ) return S_OK ;

	DWORD dwCurrentPlayPos = GetBufferPosition() ;	

	// Just to be sure it's the good event
	if( dwCurrentPlayPos>m_dwNextWriteOffset && dwCurrentPlayPos<(m_dwNextWriteOffset+m_dwNotifySize) ) return S_OK ;

    // Lock the DirectSound buffer
	while( sndLocked ) Sleep( 100 ) ;
	sndLocked = true ;
    m_pBuffer->Lock( m_dwNextWriteOffset, m_dwNotifySize, &pDSLockedBuffer, &dwDSLockedBufferSize, NULL, NULL, 0L ) ;

    // Fill the DirectSound buffer with data
    DWORD dwBytesWrittenToBuffer = Read( (char*)pDSLockedBuffer, dwDSLockedBufferSize ) ;

	if( (int)dwBytesWrittenToBuffer == -1 )
	{
		// Unlock the DirectSound buffer
		m_pBuffer->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 );
		sndLocked = false ;
		return S_FALSE ;
	}

    // If the number of bytes written is less than the amount we requested, we have not enough data.
    if( dwBytesWrittenToBuffer < dwDSLockedBufferSize )
    {
		if( m_nextUsed )
		{
			int fadeSize = MsToChar( m_pwfx, 1 ) ;

			if( m_dwNextWriteOffset || ( fadeSize <= dwBytesWrittenToBuffer ) )
				ApplyFadeOut( (unsigned char*)pDSLockedBuffer + dwBytesWrittenToBuffer - fadeSize, fadeSize, 0, 0 ) ;
			else
			{
				int size1 = fadeSize - ( dwBytesWrittenToBuffer + m_dwNextWriteOffset ) ;
				int size2 = dwBytesWrittenToBuffer + m_dwNextWriteOffset ;
				ApplyFadeOut( (unsigned char*)pDSLockedBuffer + m_dwDSBufferSize - size1 , size1, (unsigned char*)pDSLockedBuffer, size2 ) ;
			}

			SwitchFile() ;
			Read( (char*)pDSLockedBuffer + dwBytesWrittenToBuffer, dwDSLockedBufferSize - dwBytesWrittenToBuffer ) ;
			ApplyFadeIn( (unsigned char*)pDSLockedBuffer + dwBytesWrittenToBuffer, MIN( fadeSize, dwDSLockedBufferSize - dwBytesWrittenToBuffer ) ) ;
		}
		else
		{
			// Fill in silence for the rest of the buffer.
			_FillMemory( (char*)pDSLockedBuffer + dwBytesWrittenToBuffer, dwDSLockedBufferSize - dwBytesWrittenToBuffer, (BYTE)(m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) ) ;
		}
    }

    // Unlock the DirectSound buffer
    m_pBuffer->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 );
	sndLocked = false ;

    // Update where the buffer will lock (for next time)
    m_dwNextWriteOffset += dwDSLockedBufferSize ; 
    m_dwNextWriteOffset %= m_dwDSBufferSize ; // Circular buffer

    return S_OK;
}



//-----------------------------------------------------------------------------
// Name: HSound::StopStreaming()
// Desc: Stop the streaming of the sound
//-----------------------------------------------------------------------------
void HSound::StopStreaming()
{
	if( !m_loop && m_nextState ) Stop() ;

	// if there is a callback waiting for an end sound event
	if( m_handlerEvtEnd	) PostMessage( hwnd, SND_TIME, m_handlerEvtEnd, NULL ) ;

	if( m_nextState ) Reset() ;
}



//-----------------------------------------------------------------------------
// Name: HSound::GetPlayProgress()
// Desc: Get how much data has been read since the begining of data in bytes
//-----------------------------------------------------------------------------
DWORD HSound::GetPlayProgress()
{
	if( !m_pBuffer )  return 0 ;

	GetBufferPosition() ;

	if( m_dwReadOffset > m_dwSize )
	{
		if( m_nextUsed )
		{
			m_dwReadOffset -= m_dwSize ;
			m_dwSize = m_nextSize ;
			m_nextSize = 0 ;
			if( !m_nextFileName ) m_nextUsed = false ;

			return m_dwReadOffset ;
		}

		m_nextState = true ;
		StopStreaming() ;
	}

	return MIN( m_dwReadOffset, m_dwSize ) ;
}




//-----------------------------------------------------------------------------
// Name: HSound::GetBufferPosition()
// Desc: Get the read position in the buffer in bytes
//-----------------------------------------------------------------------------
DWORD HSound::GetBufferPosition()
{
	DWORD currentPlayPos ;

	if( FAILED( m_pBuffer->GetCurrentPosition( &currentPlayPos, NULL ) ) ) return ERRMSG( "GetBufferPosition()" );
	
	// The difference between the current play position and the old play position give how much data have been read since the last GetBufferPosition()
	// but we have to check if the reading position is return at the beginning of the buffer
	// The Direct Sound methode GetCurrentPosition gives a result with a possible error of 20 millisecondes!!!!! so we use m_dwDSBufferSize/2 because sometime
	// this error give a currentPlayPos < m_lastPlayPos whereas the reading position isn't return to the beginning of the buffer !!!!!!!!! Well done Microsoft ... !!!!!!!!
	if( m_streaming ) m_dwReadOffset += ( (long)currentPlayPos - m_lastPlayPos + ( ( m_lastPlayPos - (long)currentPlayPos > (long)m_dwDSBufferSize/2 ) ? m_dwDSBufferSize : 0 ) ) ;
	else              m_dwReadOffset  = currentPlayPos ;

	m_lastPlayPos = currentPlayPos ;

	return currentPlayPos ;
}





//-----------------------------------------------------------------------------
// Name: HSound::SetNextFile( char* strFileName ) 
// Desc: 
//-----------------------------------------------------------------------------
void HSound::SetNextFile( char* strFileName ) 
{ 
	m_nextFileName = (char*)new char[ strlen( strFileName ) + 1 ] ;
	strcpy( m_nextFileName, strFileName ) ;

	m_nextState = false ; 
	m_nextUsed = true ; 
}




//-----------------------------------------------------------------------------
// Name: HSound::SwitchFile()
// Desc: Change the audio file
//-----------------------------------------------------------------------------
void HSound::SwitchFile()
{
	OpenNext( m_nextFileName ) ;
	SAFE_DELETE_ARRAY( m_nextFileName ) ;

	PostMessage( hwnd, SND_NEXT, (int)m_pWSound, NULL ) ;
}






//-----------------------------------------------------------------------------
// Name: HSound::GetBufferPosition()
// Desc: Apply a fade out on audio data in the buffer
//-----------------------------------------------------------------------------
void HSound::ApplyFadeOut( unsigned char* pBuffer1, int size1, unsigned char* pBuffer2, int size2 )
{
	float coeff = 1.0f / (float)(size1 + size2) ;

	if( m_pwfx->wBitsPerSample == 8 )
	{
		if( m_pwfx->nChannels == 1 )
		{
			while( size1-- )
			{			
				*pBuffer1 =  ((float)*pBuffer1 - 128) * (size1 * coeff) + 128 ;
				pBuffer1++ ;
			}

			while( size2-- )
			{			
				*pBuffer2 =  ((float)*pBuffer2 - 128) * (size2 * coeff) + 128 ;
				pBuffer2++ ;
			}
		}
		else
		{
			while( size1 )
			{			
				*pBuffer1 =  ((float)*pBuffer1 - 128) * (size1 * coeff) + 128 ;
				pBuffer1++ ;

				*pBuffer1 =  ((float)*pBuffer1 - 128) * (size1 * coeff) + 128 ;
				pBuffer1++ ;

				size1 -= 2 ;
			}

			while( size2 )
			{			
				*pBuffer2 =  ((float)*pBuffer2 - 128) * (size2 * coeff) + 128 ;
				pBuffer2++ ;

				*pBuffer2 =  ((float)*pBuffer2 - 128) * (size2 * coeff) + 128 ;
				pBuffer2++ ;

				size2 -= 2 ;
			}
		}
	}
	else
	{
		short s ;
		
		if( m_pwfx->nChannels == 1 )
		{
			while( size1 > 0 )
			{
				s = (short)(pBuffer1[1]<<8 | pBuffer1[0]) * (size1 * coeff) ;	
				pBuffer1[0] = s & 0xFF ;
				pBuffer1[1] = (s>>8) & 0xFF ;
				pBuffer1 += 2 ;

				size1 -= 2 ;
			}

			while( size2 > 0 )
			{
				s = (short)(pBuffer2[1]<<8 | pBuffer2[0]) * (size2 * coeff) ;	
				pBuffer2[0] = s & 0xFF ;
				pBuffer2[1] = (s>>8) & 0xFF ;
				pBuffer2 += 2 ;

				size2 -= 2 ;
			}
		}
		else
		{
			while( size1 > 0 )
			{
				s = (short)(pBuffer1[1]<<8 | pBuffer1[0]) * (size1 * coeff) ;		
				pBuffer1[0] = ( s & 0xFF ) ;
				pBuffer1[1] = ( (s>>8) & 0xFF ) ;
				pBuffer1 += 2 ;

				s = (short)(pBuffer1[1]<<8 | pBuffer1[0]) * (size1 * coeff) ;
				pBuffer1[0] = s & 0xFF ;
				pBuffer1[1] = (s>>8) & 0xFF ;
				pBuffer1 += 2 ;

				size1 -= 4 ;
			}

			while( size2 > 0 )
			{
				s = (short)(pBuffer2[1]<<8 | pBuffer2[0]) * (size2 * coeff) ;		
				pBuffer2[0] = ( s & 0xFF ) ;
				pBuffer2[1] = ( (s>>8) & 0xFF ) ;
				pBuffer2 += 2 ;

				s = (short)(pBuffer2[1]<<8 | pBuffer2[0]) * (size2 * coeff) ;
				pBuffer2[0] = s & 0xFF ;
				pBuffer2[1] = (s>>8) & 0xFF ;
				pBuffer2 += 2 ;

				size2 -= 4 ;
			}
		}
	}
}




//-----------------------------------------------------------------------------
// Name: HSound::GetBufferPosition()
// Desc: Apply a fade in on audio data in the buffer
//-----------------------------------------------------------------------------
void HSound::ApplyFadeIn( unsigned char* pBuffer, int size )
{
	float coeff = 1.0f / (float)size ;
	int cursor = 0 ;
	
	if( m_pwfx->wBitsPerSample == 8 )
	{
		if( m_pwfx->nChannels == 1 )
		{
			while( cursor != size )
			{
				*pBuffer =  ((float)*pBuffer - 128) * (cursor * coeff) + 128 ;
				pBuffer++ ;

				cursor++ ;
			}
		}
		else
		{
			while( cursor != size )
			{
				*pBuffer =  ((float)*pBuffer - 128) * (cursor * coeff) + 128 ;
				pBuffer++ ;

				*pBuffer =  ((float)*pBuffer - 128) * (cursor * coeff) + 128 ;
				pBuffer++ ;

				cursor += 2 ;
			}
		}
	}
	else
	{
		short s ;
		
		if( m_pwfx->nChannels == 1 )
		{
			while( cursor <= size )
			{
				s = (short)(pBuffer[1]<<8 | pBuffer[0]) * (cursor * coeff) ;
				pBuffer[0] = ( s & 0xFF ) ;
				pBuffer[1] = ( (s>>8) & 0xFF ) ;
				pBuffer += 2 ;

				cursor += 2 ;
			}
		}
		else
		{
			while( cursor <= size )
			{
				s = (short)(pBuffer[1]<<8 | pBuffer[0]) * (cursor * coeff) ;		
				pBuffer[0] = s & 0xFF ;
				pBuffer[1] = (s>>8) & 0xFF ;
				pBuffer += 2 ;

				s = (short)(pBuffer[1]<<8 | pBuffer[0]) * (cursor * coeff) ;	
				pBuffer[0] = s & 0xFF ;
				pBuffer[1] = (s>>8) & 0xFF ;
				pBuffer += 2 ;

				cursor += 4 ;
			}
		}
	}
}




//-----------------------------------------------------------------------------
// Name: HSound::SetPosition()
// Desc: Set the read position in number of bytes
//-----------------------------------------------------------------------------
HRESULT	HSound::SetPosition( long position, int flag )
{
	if( !m_pBuffer || !m_pwfx ) return S_FALSE ;

	if( flag == SEEK_SET )  // From the begining of the file
	{
		position = labs( position ) ;
		if( position > m_dwSize ) position = m_dwSize ;
	}
	else if( flag == SEEK_CUR )  // From the current reading position
	{
		position = GetPlayProgress() + position ;
		if( position < 0 ) position = 0 ;
		else if( position > m_dwSize ) position = m_dwSize ;
	}
	else if( flag == SEEK_END )	// From the end of the file
	{
		position = m_dwSize - labs( position ) ;
		if( position < 0 ) position = 0 ;
	}
 
	// Translation of the position in a circular buffer
	unsigned long bufferPosition = position % m_dwDSBufferSize ;

	bool playing = IsSoundPlaying() ;
	if( playing ) Stop() ;

	// because the buffer is circular :
	long _bufferPosition = bufferPosition - ( bufferPosition % m_dwNotifySize ) ;

	if( m_streaming ) 
	{
		if( FAILED( SetFilePosition( position - ( position % m_dwNotifySize ) ) ) )  
		{
			Reset() ; 
			return S_FALSE ; 
		}
		else
		{
			// Keeping control var for streaming
			m_dwReadOffset		= position ; //- ( position % m_dwDSBufferSize ) ;
			m_lastPlayPos     = bufferPosition ;
			// Setting the next position to write in the buffer for the methode HandleStreamNotification
			m_dwNextWriteOffset = _bufferPosition ;

			// Filling the entire circular buffer beginning at the position bufferPosition
			if( FAILED( FillBufferWithSound( _bufferPosition, m_dwDSBufferSize ) ) ) return ERRMSG("FillBuffer failed") ;
		}
	}

	// Setting the reading position in the circular buffer
	m_pBuffer->SetCurrentPosition( bufferPosition ) ;

	if( playing ) Play( m_loop ) ;
	
	return S_OK ;
}







//-----------------------------------------------------------------------------
// Name: HSound::SetVolumeCone()
// Desc: Set the volume for the 3D sound cone
//       Volume should be give between 0 and 100
//-----------------------------------------------------------------------------
HRESULT	HSound::SetVolumeCone( int volumeCone )
{
	if( !m_pBuffer )  return S_FALSE;

	if( volumeCone < 0 )  volumeCone = 0 ;
	if( volumeCone > 100) volumeCone = 100 ;
		
	m_volumeCone = volumeCone ;

	float globalVolume = pHSoundManager->GetGlobalVolume() ;
	float vol = (float)volumeCone * (globalVolume / 10000.0) ;
	
	if( vol == 0 ) volumeCone = -10000 ;
	else volumeCone = (int)( 5000.0*log10( vol ) ) ;

	if( FAILED( m_p3DBuffer->SetConeOutsideVolume( volumeCone, DS3D_DEFERRED ) ) ) return ERRMSG("SetVolume failed") ;
	
	return S_OK ;
}







//-----------------------------------------------------------------------------
// Name: HSound::SetFrequency()
// Desc: Set the frequency for the sound
//       Frequency should be give between 5000Hz and 100000Hz
//-----------------------------------------------------------------------------
HRESULT	HSound::SetFrequency( int frequency )
{
	if( !m_pBuffer )  return S_FALSE;

	if(frequency < 100)	frequency = 100 ;
	else if(frequency > 100000) frequency = 100000 ;

	if( FAILED( m_pBuffer->SetFrequency( frequency ) ) ) return ERRMSG("SetFrequency failed") ;

	m_pwfx->nSamplesPerSec = frequency ;
	m_pwfx->nAvgBytesPerSec = m_pwfx->nSamplesPerSec * m_pwfx->nBlockAlign ;

	return S_OK ;
}



//-----------------------------------------------------------------------------
// Name: HSound::GetFrequency
// Desc: Return the frequency for the sound
//-----------------------------------------------------------------------------
DWORD HSound::GetFrequency()
{
	if( !m_pBuffer )  return 0 ;
	
	return m_pwfx->nSamplesPerSec ;
}




//-----------------------------------------------------------------------------
// Name: HSound::IsSoundPlaying()
// Desc: Tells if the buffer is playing or not.
//-----------------------------------------------------------------------------
bool HSound::IsSoundPlaying()
{
	if( !m_pBuffer ) return false ;

    DWORD dwStatus = 0 ;

    if( FAILED( m_pBuffer->GetStatus( &dwStatus ) ) ) { ERRMSG("IsSoundPlaying failed") ; return false ; }
	
    return dwStatus & DSBSTATUS_PLAYING ;
}



//-----------------------------------------------------------------------------
// Name: HSound::Set3dMode()
// Desc: Set 3D mode for Buffer 3D, flag is true or false
//-----------------------------------------------------------------------------
void HSound::Set3dMode( bool flag )
{
	if( m_pWSound->m_3dEnable ) 
	{
		m_p3DBuffer->SetMode( (flag ? DS3DMODE_NORMAL : DS3DMODE_DISABLE), DS3D_IMMEDIATE) ;
	}
	else m_p3DBuffer->SetMode( DS3DMODE_DISABLE, DS3D_IMMEDIATE ) ;
}



//-----------------------------------------------------------------------------
// Name: bool operator == ( const WAVEFORMATEX &w1, const WAVEFORMATEX &w2 )
// Desc: Compare 2 WAVEFORMATEX
//-----------------------------------------------------------------------------
bool operator != ( const WAVEFORMATEX &w1, const WAVEFORMATEX &w2 )
{
	if(		w1.wFormatTag		!=	w2.wFormatTag		||		w1.nChannels		!=	w2.nChannels			||
			w1.nSamplesPerSec	!=	w2.nSamplesPerSec	||		w1.nAvgBytesPerSec	!=	w2.nAvgBytesPerSec		||
			w1.nBlockAlign		!=	w2.nBlockAlign		||		w1.wBitsPerSample	!=	w2.wBitsPerSample		||
			w1.cbSize			!=	w2.cbSize )
	
	return true ;

	else return false ;
}