//
// Asura_PCSound.cpp
//
// Created by Kevin Lea - 05/10/2001 16:39:53
// 
// Copyright (C) 2001 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"

#if !defined(ASURA_USE_AUDIO_SYSTEM)

#include "Asura_PC_Sound.h"

#include "Camera/Asura_Camera.h"
#include "Core/Asura_Windows.h"
#include "Debugging/Asura_Assert.h"
#include "FileAccess/Asura_FileAccess.h"
#ifdef ASURA_TOOLS
#include "Load&Save/Tools_BatchProcessing.h"
#endif
#include "Rendering/Asura_Render.h"
#include "Sound/Asura_PC_ReplaceableSounds.h"
#include "Sound/Asura_Wav.h"

#ifdef ASURA_TOOLS
	#include "Sound/Tools_Sound_System.h"
#endif

// S T A T I C   C L A S S   D A T A /////////////////////////////////////////

Asura_PC_Sound_API				Asura_PC_Sound::s_xAPI;	// Agnostic of what low-level engine code we use

Asura_PC_LoadedSound			Asura_PC_Sound::s_axLoadedSounds[ASURA_MAX_NUMBER_OF_LOADED_SOUNDS];
int						        Asura_PC_Sound::s_iNumberOfLoadedSounds = 0;
int								Asura_PC_Sound::s_iLoadedSoundsSizeTotal = 0;

Asura_PC_ActiveSound			Asura_PC_Sound::s_axActiveSounds[ASURA_PC_SOUND_MAX_NUMBER_OF_ACTIVE_SOUNDS];
int						        Asura_PC_Sound::s_iNumberOfActiveSounds = 0;

bool                            Asura_PC_Sound::s_bReverbChanged = false;
float							Asura_PC_Sound::s_fSoundFXVolume = 1.0f;

// C L A S S   I M P L E M E N T A T I O N S /////////////////////////////////

Asura_PC_LoadedSound::Asura_PC_LoadedSound() :
	m_uSize( 0 ),
	m_pucWaveData( NULL )
	#if defined( ASURA_TOOLS)
		, m_bAttemptedLoad( false )
	#endif	
{

}

Asura_PC_LoadedSound::~Asura_PC_LoadedSound()
{
#if defined( ASURA_ASSERT)
	#if defined( ASURA_TOOLS)
		const char* pszName = m_xFilename.GetString();
	#else
		const char* pszName = "Unknown";
	#endif
#endif
	Asura_Assertf( NULL == m_pucWaveData, ("This sound has not been deallocated. '%s'", pszName));
}

bool Asura_PC_Sound::Initialise()
{
#if defined( ASURA_TOOLS )
	if ( Tools_BatchProcessing::IsConsoleMode() )
	{
		return false;
	}
#endif

	if ( !s_xAPI.Initialise() )
	{
		return false;
	}

	// initialise effects
    InitReverb();

    return true;
}

bool Asura_PC_Sound::Shutdown()
{
	// Release and sound buffers
//    SAFE_DELETE( WaveSoundRead );
/*	for (int i=0; i<s_iNumberOfLoadedSounds; i++)
	{
		s_axLoadedSounds[i]->Release();
	}
*/

	s_xAPI.Destroy();

    return true;
}


void Asura_PC_Sound::Reset()
{
}


void Asura_PC_Sound::Update()
{
	s_xAPI.Update();
}


bool Asura_PC_Sound::LoadSound( const char* filename, int loadedSoundIndex)
{
	Asura_Assert( loadedSoundIndex >= 0 && loadedSoundIndex < ASURA_MAX_NUMBER_OF_LOADED_SOUNDS, "" );

    if ( loadedSoundIndex < 0 || loadedSoundIndex >= ASURA_MAX_NUMBER_OF_LOADED_SOUNDS )
	{
		return false;
	}
	
	s_axLoadedSounds[loadedSoundIndex].m_uSize = 0;
	s_axLoadedSounds[loadedSoundIndex].m_pucWaveData = 0;

	#if ( defined( ASURA_TOOLS ) )
		//In tools, delay loading of the sounds until it is needed. RAR 27/07/2006
		s_axLoadedSounds[loadedSoundIndex].m_bAttemptedLoad = false;
		s_axLoadedSounds[loadedSoundIndex].m_xFilename = filename;
		return true;
	#else
		return LoadSoundInternal(filename,loadedSoundIndex);
	#endif
}

#ifdef ASURA_TOOLS
void Asura_PC_Sound::ForceSoundToBeLoaded( int iLoadedSoundIndex )
{
	Asura_Assert( iLoadedSoundIndex >= 0 && iLoadedSoundIndex < ASURA_MAX_NUMBER_OF_LOADED_SOUNDS, "" );

    if ( iLoadedSoundIndex < 0 || iLoadedSoundIndex >= ASURA_MAX_NUMBER_OF_LOADED_SOUNDS )
	{
		return;
	}

	Asura_PC_LoadedSound* pxLoadedSound = &s_axLoadedSounds[ iLoadedSoundIndex ];
    
	//Attempt to load sound now if it hasn't been loaded yet. RAR 27/07/2006
	if ( !pxLoadedSound->IsLoaded() && !pxLoadedSound->m_bAttemptedLoad && !pxLoadedSound->m_xFilename.IsBlank() )
	{
		LoadSoundInternal( pxLoadedSound->m_xFilename.GetString(),iLoadedSoundIndex );
	}
}
#endif //ASURA_TOOLS

#ifdef ASURA_TOOLS
void Asura_PC_Sound::ReloadSound( int iLoadedSoundIndex )
{
	Asura_Assert( iLoadedSoundIndex >= 0 && iLoadedSoundIndex < ASURA_MAX_NUMBER_OF_LOADED_SOUNDS, "" );

    if ( iLoadedSoundIndex < 0 || iLoadedSoundIndex >= ASURA_MAX_NUMBER_OF_LOADED_SOUNDS )
	{
		return;
	}

	Asura_PC_LoadedSound* pxLoadedSound = &s_axLoadedSounds[ iLoadedSoundIndex ];
    
	if ( !pxLoadedSound->m_xFilename.IsBlank() )
	{
		// Make sure no playing sounds are using this
		for ( int i = 0; i < ASURA_PC_SOUND_MAX_NUMBER_OF_ACTIVE_SOUNDS; ++i )
		{
			if ( ( s_axActiveSounds[i].m_iLoadedSoundIndex == iLoadedSoundIndex )
				&& s_axActiveSounds[i].m_xAPI.IsValid() )
			{
				StopSound( i );
				s_axActiveSounds[i].Destroy();

				//Flag the sound to indicate that it is known that the sound buffer is null, so any
				//further attempts to use it should just try to fail nicely.
				//RAR 05/07/2007
				s_axActiveSounds[i].m_bInvalidatedByTools = true;
			}
		}
		
		// Deallocate the wav data, and mark the sound as not loaded, so it will be loaded again when next played
		if ( s_axLoadedSounds[iLoadedSoundIndex].m_pucWaveData )
		{
    		--s_iNumberOfLoadedSounds;
			s_iLoadedSoundsSizeTotal -= s_axLoadedSounds[iLoadedSoundIndex].m_uSize;
		}

		delete[] s_axLoadedSounds[iLoadedSoundIndex].m_pucWaveData;
    	s_axLoadedSounds[iLoadedSoundIndex].m_pucWaveData = NULL;    
		s_axLoadedSounds[iLoadedSoundIndex].m_uSize = 0;    
		s_axLoadedSounds[iLoadedSoundIndex].m_bAttemptedLoad = false;
	}
}
#endif //ASURA_TOOLS

bool Asura_PC_Sound::LoadSoundInternal( const char* filename, int loadedSoundIndex )
{
	Asura_Assert( loadedSoundIndex >= 0 && loadedSoundIndex < ASURA_MAX_NUMBER_OF_LOADED_SOUNDS, "" );

    if ( loadedSoundIndex < 0 || loadedSoundIndex >= ASURA_MAX_NUMBER_OF_LOADED_SOUNDS )
	{
		return false;
	}	

	#ifdef ASURA_TOOLS
		s_axLoadedSounds[loadedSoundIndex].m_bAttemptedLoad = true;
	#endif

	#ifdef ASURA_TOOLS
		if ( Tools_Sound_System::IsDontLoadWavs() )
		{
			//Don't want to load wav files , so create a short silent wav to use instead.
			//RAR 24/01/2007
			CreateBlankWav( loadedSoundIndex );
		}
		else
	#endif
	{
		#ifdef ASURA_PC_ALLOW_LOAD_FROM_NETWORK
			//Get filename to use for loading
			char szFilename[MAX_PATH];
			Asura_FileAccess::PlatformGetNetworkFilenameOrGetLocalCopy(filename,szFilename,MAX_PATH);
			filename = szFilename;
		#endif

    	CWaveSoundRead waveSoundRead;
		HRESULT hr;

		if ( FAILED( hr = waveSoundRead.Open( filename ) ) )
    	{
			Asura_Assertf( SUCCEEDED( hr ), ("Unable to load sound file <%s> HR %x", filename, hr ) );

			#if defined ASURA_TOOLS
				// For tools builds we have to keep the failed sound slot, so that the filenames remain
				// available.RAR 26/10/2005
				return true;
			#else
    			return false;
			#endif
    	}

    	if ( !ReadWav( &waveSoundRead, loadedSoundIndex ) )
		{
    	    Asura_Assert( 0, "failed to create system RAM buffer for sound" );
			
			#if defined ASURA_TOOLS
				// For tools builds we have to keep the failed sound slot, so that the filenames remain
				// available.RAR 26/10/2005
				return true;
			#else
    			return false;
			#endif	
		}
	}
	

	++s_iNumberOfLoadedSounds;
	s_iLoadedSoundsSizeTotal += s_axLoadedSounds[loadedSoundIndex].m_uSize;

	return true;
}

bool Asura_PC_Sound::LoadSoundFromResource( Asura_Handle fileHandle, int fileSize, int loadedSoundIndex )
{
	Asura_Assert( loadedSoundIndex >= 0 && loadedSoundIndex < ASURA_MAX_NUMBER_OF_LOADED_SOUNDS, "" );

    if ( loadedSoundIndex < 0 || loadedSoundIndex >= ASURA_MAX_NUMBER_OF_LOADED_SOUNDS )
	{
		return false;
	}

	#ifdef ASURA_TOOLS
		//In tools we may come across a sound resource for an already loaded sound
		//RAR 15/01/2006
		if ( s_axLoadedSounds[loadedSoundIndex].m_pucWaveData || Tools_Sound_System::IsDontLoadWavs() )
		{
			Asura_FileAccess::SkipData( fileHandle, fileSize );
			return false;
		}
	#endif

    BYTE* tempBuffer = reinterpret_cast<BYTE*>( Asura_Allocate( fileSize ) );

    if ( !tempBuffer )
    {
        Asura_Assert( tempBuffer, "failed to create temp buffer for sound" );
        return false;
    }
    
	// XXX synchronous IO!?
    Asura_FileAccess::ReadData( fileHandle, tempBuffer, fileSize );

    CWaveSoundRead waveSoundRead;
	HRESULT hr;

    if ( FAILED( hr = waveSoundRead.OpenFromMemory(tempBuffer, fileSize) ) )
    {
		Asura_Assertf( SUCCEEDED( hr ), ( "Failed to load sound file from resource, HR %x", hr ) );
        Asura_Deallocate( tempBuffer );
        return false;
    }

    if ( !ReadWav( &waveSoundRead, loadedSoundIndex ) )
	{
        Asura_Assert( 0, "failed to create system RAM buffer for sound" );
        Asura_Deallocate( tempBuffer );
		return false;
	}
	
	++s_iNumberOfLoadedSounds;
	s_iLoadedSoundsSizeTotal += s_axLoadedSounds[loadedSoundIndex].m_uSize;

    Asura_Deallocate( tempBuffer );
	return true;
}

bool Asura_PC_Sound::UnloadSound( int iLoadedSoundIndex )
{
	Asura_Assert( iLoadedSoundIndex >= 0 && iLoadedSoundIndex < ASURA_MAX_NUMBER_OF_LOADED_SOUNDS, "" );

    if ( iLoadedSoundIndex < 0 || iLoadedSoundIndex >= ASURA_MAX_NUMBER_OF_LOADED_SOUNDS )
	{
		return false;
	}

	if ( s_axLoadedSounds[iLoadedSoundIndex].m_pucWaveData )
	{
    	--s_iNumberOfLoadedSounds;
		s_iLoadedSoundsSizeTotal -= s_axLoadedSounds[iLoadedSoundIndex].m_uSize;
	}

	delete[] s_axLoadedSounds[iLoadedSoundIndex].m_pucWaveData;
    s_axLoadedSounds[iLoadedSoundIndex].m_pucWaveData = NULL;

	#ifdef ASURA_TOOLS
		s_axLoadedSounds[iLoadedSoundIndex].m_bAttemptedLoad = false;
		s_axLoadedSounds[iLoadedSoundIndex].m_xFilename.MakeNull();
	#endif

	return true;
}

bool Asura_PC_Sound::CreateSound( int iLoadedSoundID, Asura_Handle iPlayingSoundHandle, bool bUseHardware3dPan )
{
    Asura_Assert( iLoadedSoundID >= 0 && iLoadedSoundID < ASURA_MAX_NUMBER_OF_LOADED_SOUNDS, "invalid loaded sound index" );
    Asura_Assert( iPlayingSoundHandle >= 0 && iPlayingSoundHandle < ASURA_PC_SOUND_MAX_NUMBER_OF_ACTIVE_SOUNDS, "invalid sound index" );

	Asura_PC_LoadedSound* pxLoadedSound = &s_axLoadedSounds[ iLoadedSoundID ];

    #ifdef ASURA_TOOLS
		// Attempt to load sound now if it hasn't been loaded yet. RAR 27/07/2006
		if ( !pxLoadedSound->IsLoaded() && !pxLoadedSound->m_bAttemptedLoad && !pxLoadedSound->m_xFilename.IsBlank() )
		{
			LoadSoundInternal( pxLoadedSound->m_xFilename.GetString(), iLoadedSoundID );
		}
	#endif
    
    if ( !pxLoadedSound->IsLoaded() )
    {
        // sSund is known about but not in memory. Candidate for streaming?
        Asura_Assertf( false, ( "Asura_PCSound_System::CreateSound - sound %d is not loaded\n", iLoadedSoundID ) );
        return false;
    }

    Asura_PC_ActiveSound& activeSound = s_axActiveSounds[iPlayingSoundHandle];
	
	if ( pxLoadedSound->m_xWfx.nChannels == 2 && bUseHardware3dPan )
	{
		Asura_Assertf( false, ("Asura_PCSound_System::CreateSound - sound %d is stereo, cannot use hardware 3D pan.", iLoadedSoundID ) );
		bUseHardware3dPan = false;
	}

    if ( !activeSound.Create( s_axLoadedSounds[iLoadedSoundID], bUseHardware3dPan ) )
    {
        Asura_Assertf( false, ("Asura_PCSound_System::CreateSound - failed to create sound %d", iLoadedSoundID) );
        return false;
    }

	Asura_Assert( activeSound.m_xAPI.IsValid(), "invalid sound" );
    activeSound.m_iLoadedSoundIndex = iLoadedSoundID;
    activeSound.m_bPaused = false;
    activeSound.m_bPausedByPitch = false;
	activeSound.m_bUseForcedPanAndFade = false;
    activeSound.m_fVolume = 1.0f;
	#ifdef ASURA_TOOLS
		activeSound.m_bInvalidatedByTools = false;
	#endif

    return true;
}

bool Asura_PC_Sound::PlaySound( Asura_Handle iPlayingSoundHandle, bool bLooping )
{
	Asura_Assert( iPlayingSoundHandle >= 0 && iPlayingSoundHandle < ASURA_PC_SOUND_MAX_NUMBER_OF_ACTIVE_SOUNDS, "invalid sound index" );

    // (MH - 13.06.2002) remember the play flags so we can pause and restart this sound
    s_axActiveSounds[iPlayingSoundHandle].m_bIsLooping = bLooping;

	#ifdef ASURA_TOOLS
		if ( s_axActiveSounds[iPlayingSoundHandle].m_bInvalidatedByTools )
		{
			return false;
		}
	#endif

	// Better commit any deferred settings that have been changed before starting playback
	s_xAPI.ForceListenerUpdate();	
	// TBD is this actually necessary, even in DirectSound? (Listener always commits it's deferred settings immediately anyway)

	Asura_Assert( s_axActiveSounds[iPlayingSoundHandle].m_xAPI.IsValid(), "invalid sound" );

	if ( !s_axActiveSounds[iPlayingSoundHandle].AnyPauseFlagSet() )
	{
		return s_axActiveSounds[iPlayingSoundHandle].m_xAPI.Play( bLooping );
	}

	return true;
}

bool Asura_PC_Sound::StopSound( Asura_Handle iPlayingSoundHandle )
{
	Asura_Assert( iPlayingSoundHandle >= 0 && iPlayingSoundHandle < ASURA_PC_SOUND_MAX_NUMBER_OF_ACTIVE_SOUNDS, "invalid sound index" );

	#ifdef ASURA_TOOLS
		if ( s_axActiveSounds[iPlayingSoundHandle].m_bInvalidatedByTools )
		{
			return true;
		}
	#endif
 
	Asura_Assert( s_axActiveSounds[iPlayingSoundHandle].m_xAPI.IsValid(), "invalid sound" );
	bool result = s_axActiveSounds[iPlayingSoundHandle].m_xAPI.Stop();
	
	return result;
}

bool Asura_PC_Sound::DestroySound( Asura_Handle iPlayingSoundHandle )
{
	Asura_Assert( iPlayingSoundHandle >= 0 && iPlayingSoundHandle < ASURA_PC_SOUND_MAX_NUMBER_OF_ACTIVE_SOUNDS, "invalid sound index" );

    s_axActiveSounds[iPlayingSoundHandle].Destroy();
    return true;
}

bool Asura_PC_Sound::SoundIsPlaying( Asura_Handle iPlayingSoundHandle )
{
	Asura_Assert( iPlayingSoundHandle >= 0 && iPlayingSoundHandle < ASURA_PC_SOUND_MAX_NUMBER_OF_ACTIVE_SOUNDS, "invalid sound index" );

	#ifdef ASURA_TOOLS
		if ( s_axActiveSounds[iPlayingSoundHandle].m_bInvalidatedByTools )
		{
			return false;
		}
	#endif

    Asura_Assert( Asura_PC_Sound::s_axActiveSounds[iPlayingSoundHandle].m_xAPI.IsValid(), "invalid sound" );

    // (MH - 19.08.2002) bug fix - say the sound is playing if it is paused
    if ( Asura_PC_Sound::s_axActiveSounds[iPlayingSoundHandle].m_bPaused 
		|| Asura_PC_Sound::s_axActiveSounds[iPlayingSoundHandle].m_bPausedByPitch )
    {
        return true;
    }

	return Asura_PC_Sound::s_axActiveSounds[iPlayingSoundHandle].m_xAPI.IsPlaying();
}

bool Asura_PC_Sound::SetVolume( Asura_Handle iPlayingSoundHandle, float fVolume )
{
	Asura_Assert( iPlayingSoundHandle >= 0 && iPlayingSoundHandle < ASURA_PC_SOUND_MAX_NUMBER_OF_ACTIVE_SOUNDS, "invalid sound index" );

	#ifdef ASURA_TOOLS
		if ( s_axActiveSounds[iPlayingSoundHandle].m_bInvalidatedByTools )
		{
			return false;
		}
	#endif

    return s_axActiveSounds[iPlayingSoundHandle].SetVolume(fVolume);
}

bool Asura_PC_Sound::SetWetDryVolume ( Asura_Handle iPlayingSoundHandle, float fWetVolume, float fDryVolume )
{
	Asura_Assert( iPlayingSoundHandle >= 0 && iPlayingSoundHandle < ASURA_PC_SOUND_MAX_NUMBER_OF_ACTIVE_SOUNDS, "invalid sound index" );

	#ifdef ASURA_TOOLS
	if ( s_axActiveSounds[iPlayingSoundHandle].m_bInvalidatedByTools )
	{
		return false;
	}
	#endif

	return s_axActiveSounds[iPlayingSoundHandle].m_xAPI.SetWetDryVolume(fWetVolume, fDryVolume);
}
	
bool Asura_PC_Sound::SetPan( Asura_Handle iPlayingSoundHandle, float fPan )
{
	Asura_Assert( iPlayingSoundHandle >= 0 && iPlayingSoundHandle < ASURA_PC_SOUND_MAX_NUMBER_OF_ACTIVE_SOUNDS, "invalid sound index" );
	Asura_Assert( fPan >= 0.0f && fPan <= 1.0f, "invalid pan" );

	#ifdef ASURA_TOOLS
		if ( s_axActiveSounds[iPlayingSoundHandle].m_bInvalidatedByTools )
		{
			return false;
		}
	#endif

	Asura_Assert( s_axActiveSounds[iPlayingSoundHandle].m_xAPI.IsValid(), "invalid sound" );
	
	return s_axActiveSounds[iPlayingSoundHandle].m_xAPI.SetPan( fPan );
}

bool Asura_PC_Sound::SetPitch( Asura_Handle iPlayingSoundHandle, float fPitch )
{
	Asura_Assert( iPlayingSoundHandle >= 0 && iPlayingSoundHandle < ASURA_PC_SOUND_MAX_NUMBER_OF_ACTIVE_SOUNDS, "invalid sound index" );
    Asura_Assert( fPitch >= 0.0f, "pitch must be positive" );

	#ifdef ASURA_TOOLS
		if ( s_axActiveSounds[iPlayingSoundHandle].m_bInvalidatedByTools )
		{
			return false;
		}
	#endif

    if ( fPitch == 0.0f )
    {
        if ( SoundIsPlaying( iPlayingSoundHandle ) && !s_axActiveSounds[iPlayingSoundHandle].m_bPausedByPitch)
        {
			s_axActiveSounds[iPlayingSoundHandle].m_bPausedByPitch = true;
            return PauseSoundIfRequired( iPlayingSoundHandle );
        }
		//Always set this value (we may be in this function before the sound has commenced)
		s_axActiveSounds[iPlayingSoundHandle].m_bPausedByPitch = true;
    }
    else
    {
		Asura_Assert( s_axActiveSounds[iPlayingSoundHandle].m_xAPI.IsValid(), "invalid sound" );

		bool result = s_axActiveSounds[iPlayingSoundHandle].m_xAPI.SetPitch( fPitch );

		if ( !result )
		{
			return false;
		}

        if ( s_axActiveSounds[iPlayingSoundHandle].m_bPausedByPitch )
        {
            s_axActiveSounds[iPlayingSoundHandle].m_bPausedByPitch = false;
            return UnpauseSoundIfRequired( iPlayingSoundHandle );
        }
    }

    return true;
}

bool Asura_PC_Sound::SetUseReverb( Asura_Handle iPlayingSoundHandle, bool bUseReverb )
{
	Asura_Assert( iPlayingSoundHandle >= 0 && iPlayingSoundHandle < ASURA_PC_SOUND_MAX_NUMBER_OF_ACTIVE_SOUNDS, "invalid sound index" );

	#ifdef ASURA_TOOLS
		if ( s_axActiveSounds[iPlayingSoundHandle].m_bInvalidatedByTools )
		{
			return false;
		}
	#endif

	Asura_Assert( s_axActiveSounds[iPlayingSoundHandle].m_xAPI.IsValid(), "invalid sound" );
	
	return s_axActiveSounds[iPlayingSoundHandle].m_xAPI.SetUseReverb( bUseReverb );
}

bool Asura_PC_Sound::SetUseDistanceFilter( Asura_Handle iPlayingSoundHandle, bool bUseDistanceFilter )
{
	Asura_Assert( iPlayingSoundHandle >= 0 && iPlayingSoundHandle < ASURA_PC_SOUND_MAX_NUMBER_OF_ACTIVE_SOUNDS, "invalid sound index" );

	#ifdef ASURA_TOOLS
		if ( s_axActiveSounds[iPlayingSoundHandle].m_bInvalidatedByTools )
		{
			return false;
		}
	#endif

	Asura_Assert( s_axActiveSounds[iPlayingSoundHandle].m_xAPI.IsValid(), "invalid sound" );
	
	return s_axActiveSounds[iPlayingSoundHandle].m_xAPI.SetUseDistanceFilter( bUseDistanceFilter );
}

bool Asura_PC_Sound::SetSpatial( Asura_Handle iPlayingSoundHandle, bool bIsSpatial )
{
	// if this voice is using hardware 3d panning, disable the 3d processing
	Asura_Assert( iPlayingSoundHandle >= 0 && iPlayingSoundHandle < ASURA_PC_SOUND_MAX_NUMBER_OF_ACTIVE_SOUNDS, "invalid sound index" );

	if ( !s_axActiveSounds[iPlayingSoundHandle].m_bUseHardware3dPan 
		|| s_axActiveSounds[iPlayingSoundHandle].m_bUseForcedPanAndFade )
	{
		return true;
	}

	#ifdef ASURA_TOOLS
		if ( s_axActiveSounds[iPlayingSoundHandle].m_bInvalidatedByTools )
		{
			return false;
		}
	#endif

	if ( s_axActiveSounds[iPlayingSoundHandle].m_bEnable3dPanning != bIsSpatial )
	{
		s_axActiveSounds[iPlayingSoundHandle].m_bEnable3dPanning = bIsSpatial;

		return s_axActiveSounds[iPlayingSoundHandle].m_xAPI.SetSpatial( bIsSpatial );
	}

	return true;
}

bool Asura_PC_Sound::PauseSound(Asura_Handle iPlayingSoundHandle)
{
	Asura_Assert( iPlayingSoundHandle >= 0 && iPlayingSoundHandle < ASURA_PC_SOUND_MAX_NUMBER_OF_ACTIVE_SOUNDS, "invalid sound index" );

    Asura_Assert( !s_axActiveSounds[iPlayingSoundHandle].m_bPaused, "sound already paused" );
    s_axActiveSounds[iPlayingSoundHandle].m_bPaused = true;

	return PauseSoundIfRequired( iPlayingSoundHandle );
}

bool Asura_PC_Sound::UnpauseSound( Asura_Handle iPlayingSoundHandle )
{
	Asura_Assert( iPlayingSoundHandle >= 0 && iPlayingSoundHandle < ASURA_PC_SOUND_MAX_NUMBER_OF_ACTIVE_SOUNDS, "invalid sound index" );

    Asura_Assert( s_axActiveSounds[iPlayingSoundHandle].m_bPaused, "sound not paused" );
    s_axActiveSounds[iPlayingSoundHandle].m_bPaused = false;
    
	return UnpauseSoundIfRequired( iPlayingSoundHandle );
}

bool Asura_PC_Sound::PauseSoundIfRequired( Asura_Handle iPlayingSoundHandle )
{
	Asura_Assert( iPlayingSoundHandle >= 0 && iPlayingSoundHandle < ASURA_PC_SOUND_MAX_NUMBER_OF_ACTIVE_SOUNDS, "invalid sound index" );

	#ifdef ASURA_TOOLS
		if ( s_axActiveSounds[iPlayingSoundHandle].m_bInvalidatedByTools )
		{
			return false;
		}
	#endif

	if ( (	s_axActiveSounds[iPlayingSoundHandle].m_bPaused 
			|| s_axActiveSounds[iPlayingSoundHandle].m_bPausedByPitch )
			&& !s_axActiveSounds[iPlayingSoundHandle].m_xAPI.IsPaused()
		)
	{
		bool result = s_axActiveSounds[iPlayingSoundHandle].m_xAPI.Pause();
		Asura_Assert( result, "Failed to pause sound" );
		return result;
	}

    return true;
}

bool Asura_PC_Sound::UnpauseSoundIfRequired( Asura_Handle iPlayingSoundHandle )
{
	Asura_Assert( iPlayingSoundHandle >= 0 && iPlayingSoundHandle < ASURA_PC_SOUND_MAX_NUMBER_OF_ACTIVE_SOUNDS, "invalid sound index" );

	#ifdef ASURA_TOOLS
		if ( s_axActiveSounds[iPlayingSoundHandle].m_bInvalidatedByTools )
		{
			return false;
		}
	#endif

	if ( !s_axActiveSounds[iPlayingSoundHandle].m_bPaused && !s_axActiveSounds[iPlayingSoundHandle].m_bPausedByPitch )
	{
		bool result = s_axActiveSounds[iPlayingSoundHandle].m_xAPI.Unpause( s_axActiveSounds[iPlayingSoundHandle].m_bIsLooping );
		Asura_Assert( result, "Failed to play sound" );

		return result;
	}

    return true;
}

#ifdef ASURA_TOOLS
float Asura_PC_Sound::GetSoundLength( int iLoadedSoundIndex )
{
	Asura_Assert( iLoadedSoundIndex >= 0 && iLoadedSoundIndex < ASURA_MAX_NUMBER_OF_LOADED_SOUNDS, "" );

    if ( iLoadedSoundIndex < 0 || iLoadedSoundIndex >= ASURA_MAX_NUMBER_OF_LOADED_SOUNDS )
	{
		return 0.0f;
	}

	ForceSoundToBeLoaded( iLoadedSoundIndex );

	if ( Tools_Sound_System::IsDontLoadWavs() )
	{
		// Not loading wav files, so any duration information is going to be suspect
		return 0.0f;
	}

	Asura_PC_LoadedSound* pxLoadedSound = &s_axLoadedSounds[iLoadedSoundIndex];

	if ( pxLoadedSound->IsLoaded() && pxLoadedSound->m_xWfx.nAvgBytesPerSec )
	{
		return pxLoadedSound->m_uSize / static_cast<float>( pxLoadedSound->m_xWfx.nAvgBytesPerSec );
	}
	else
	{
		return 0.0f;
	}
}
#endif //ASURA_TOOLS

bool Asura_PC_Sound::ReadWav( class CWaveSoundRead* pWaveSoundRead, int iLoadedSoundIndex )
{
    HRESULT hr; 

	int nWaveFileSize = pWaveSoundRead->m_ckIn.cksize;
    int iBufferSize = nWaveFileSize;

    WAVEFORMATEX* pxWfx = pWaveSoundRead->m_pwfx;
    float fSampleRate = static_cast<float>( pxWfx->nSamplesPerSec );
	
	Asura_Assert( !s_axLoadedSounds[iLoadedSoundIndex].m_pucWaveData, "memory leak ahoy!" );
	s_axLoadedSounds[iLoadedSoundIndex].m_pucWaveData = new BYTE[iBufferSize];
	
	if ( !s_axLoadedSounds[iLoadedSoundIndex].m_pucWaveData ) 
	{
		return false;
	}

	UINT cbWavSize = 0;
    
	if ( FAILED( hr = pWaveSoundRead->Read( nWaveFileSize, s_axLoadedSounds[iLoadedSoundIndex].m_pucWaveData, &cbWavSize ) ) )           
	{
		delete[] s_axLoadedSounds[iLoadedSoundIndex].m_pucWaveData;
        s_axLoadedSounds[iLoadedSoundIndex].m_pucWaveData = NULL;
		return false;
	}

    // fill the loaded sound info
    s_axLoadedSounds[iLoadedSoundIndex].m_uSize = iBufferSize;
    s_axLoadedSounds[iLoadedSoundIndex].m_xWfx = *pWaveSoundRead->m_pwfx;
    s_axLoadedSounds[iLoadedSoundIndex].m_fFrequency = fSampleRate;

    return true;
}

#ifdef ASURA_TOOLS
void Asura_PC_Sound::CreateBlankWav( int iLoadedSoundIndex )
{
	const u_int uSize = 150;

	s_axLoadedSounds[iLoadedSoundIndex].m_pucWaveData = new BYTE[uSize];
	memset( s_axLoadedSounds[iLoadedSoundIndex].m_pucWaveData, 0x7f, uSize );
	s_axLoadedSounds[iLoadedSoundIndex].m_uSize = uSize;
	s_axLoadedSounds[iLoadedSoundIndex].m_fFrequency = static_cast<float>( uSize );
	s_axLoadedSounds[iLoadedSoundIndex].m_xWfx.nAvgBytesPerSec = uSize;
	s_axLoadedSounds[iLoadedSoundIndex].m_xWfx.nChannels = 1;
	s_axLoadedSounds[iLoadedSoundIndex].m_xWfx.nBlockAlign = 1;
	s_axLoadedSounds[iLoadedSoundIndex].m_xWfx.nSamplesPerSec = uSize;
	s_axLoadedSounds[iLoadedSoundIndex].m_xWfx.wBitsPerSample = 8;
	s_axLoadedSounds[iLoadedSoundIndex].m_xWfx.wFormatTag = 1;
	s_axLoadedSounds[iLoadedSoundIndex].m_xWfx.cbSize = 0;
}
#endif

void Asura_PC_Sound::SetListener( const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation )
{	
	s_xAPI.SetListener( xPosition, xOrientation );
}

void Asura_PC_Sound::InitReverb()
{
	SetReverb( Asura_Sound_System::ASURA_REVERB_ID_NONE, 1.0f );
}

void Asura_PC_Sound::SetReverb( Asura_Sound_System::ReverbType eType, float fReverbAmount )
{
	s_xAPI.SetReverb( eType, fReverbAmount );
    SetReverbChanged( true );
}

void Asura_PC_Sound::UpdateReverb( Asura_Handle iActiveSoundIndex )
{
    Asura_PC_ActiveSound& xActiveSound = s_axActiveSounds[iActiveSoundIndex];
	Asura_Assert( xActiveSound.m_xAPI.IsValid(), "invalid sound" );

	xActiveSound.SetReverbParams( s_xAPI.GetReverbParams() );
}

void Asura_PC_Sound::UpdateSound( Asura_Handle iActiveSoundIndex )
{
    Asura_PC_ActiveSound& xActiveSound = s_axActiveSounds[iActiveSoundIndex];
	Asura_Assert( xActiveSound.m_xAPI.IsValid(), "invalid sound" );

	xActiveSound.Update();
}

bool Asura_PC_Sound::SetSoundRadii( Asura_Handle iPlayingSoundHandle, float fInner, float fOuter )
{
	if ( iPlayingSoundHandle < 0 )
	{
		return false;
	}

	if ( !s_axActiveSounds[iPlayingSoundHandle].m_xAPI.IsValid() || !s_axActiveSounds[iPlayingSoundHandle].m_bEnable3dPanning )
	{
		return false;
	}

	if ( !s_axActiveSounds[iPlayingSoundHandle].m_xAPI.SetRadii( fInner, fOuter ) )
	{
		return false;
	}

	return true;
}

bool Asura_PC_Sound::SetCone( Asura_Handle iPlayingSoundHandle, const Asura_Vector_3& xDirection, float fInnerAngle, float fOuterAngle, float fVolumeOutsideCone )
{
	if ( iPlayingSoundHandle < 0 )
	{
		return false;
	}

	if ( !s_axActiveSounds[iPlayingSoundHandle].m_xAPI.IsValid() || !s_axActiveSounds[iPlayingSoundHandle].m_bEnable3dPanning )
	{
		return false;
	}

	if ( !s_axActiveSounds[iPlayingSoundHandle].m_xAPI.SetCone( xDirection, fInnerAngle, fOuterAngle, fVolumeOutsideCone ) )
	{
		return false;
	}
	
	return true;
}

bool Asura_PC_Sound::SetSoundForcedPanAndFade( Asura_Handle iPlayingSoundHandle, float fPan, float fFade )
{
	Asura_Assert( iPlayingSoundHandle >= 0 && iPlayingSoundHandle < ASURA_PC_SOUND_MAX_NUMBER_OF_ACTIVE_SOUNDS, "invalid sound index" );

	if ( !s_axActiveSounds[iPlayingSoundHandle].m_bUseHardware3dPan )
	{
		s_axActiveSounds[iPlayingSoundHandle].Destroy();
		s_axActiveSounds[iPlayingSoundHandle].Create( s_axLoadedSounds[s_axActiveSounds[iPlayingSoundHandle].m_iLoadedSoundIndex], true );
	}

	if ( !s_axActiveSounds[iPlayingSoundHandle].m_xAPI.IsValid() )
	{
		return false;
	}

	s_axActiveSounds[iPlayingSoundHandle].m_bUseForcedPanAndFade = true;

	bool result = s_axActiveSounds[iPlayingSoundHandle].m_xAPI.SetForcedPanAndFade( fPan, fFade );

	return result;
}

bool Asura_PC_Sound::SetSoundPosition( Asura_Handle iPlayingSoundHandle, const Asura_Vector_3& xPosition )
{
	if ( iPlayingSoundHandle < 0 )
	{
		return false;
	}

	if ( !s_axActiveSounds[iPlayingSoundHandle].m_xAPI.IsValid() || !s_axActiveSounds[iPlayingSoundHandle].m_bEnable3dPanning )
	{
		return false;
	}

	if ( s_axActiveSounds[iPlayingSoundHandle].m_bUseForcedPanAndFade )
	{
		return false;
	}

	Asura_Vector_3 xFinalPos;

	if ( Asura_Render::ShouldRenderMirrored() )
	{
		// Need to reflect the sound's position in the plane whose normal is the camera's right vector

		Asura_Vector_3 xCameraRight;
		Asura_Camera::GetRightVector( xCameraRight );

		float fD = xCameraRight * *Asura_Camera::GetPosition();	// D value for plane is unit normal dotted with a point on it
		float fDist = xCameraRight * xPosition - fD;				// Get distance of point from plane

		// Move that much the other way along the normal
		xFinalPos = xCameraRight;
		xFinalPos *= -2.0f * fDist;
		xFinalPos += xPosition;
	}
	else
	{
		xFinalPos = xPosition;
	}

	bool result = s_axActiveSounds[iPlayingSoundHandle].m_xAPI.SetPosition( xFinalPos );

	return result;
}

#if _DEBUG
void Asura_PC_Sound::CheckSound( Asura_Handle activeSoundIndex )
{
	#ifdef ASURA_TOOLS
		if ( s_axActiveSounds[activeSoundIndex].m_bInvalidatedByTools )
		{
			return;
		}
	#endif

	Asura_Assert( s_axActiveSounds[activeSoundIndex].m_xAPI.IsValid(), "invalid sound" );

	s_axActiveSounds[activeSoundIndex].m_xAPI.CheckStatus();
}
#endif

void Asura_PC_Sound::SetSoundFXVolume( float fVolume )
{
	Asura_Assert( fVolume >= 0.0f && fVolume <= 1.0f, "invalid volume" );

	if ( fVolume == s_fSoundFXVolume )
	{
		return;
	}

	s_fSoundFXVolume = fVolume;

	// Nneed to update the volume in all the currently playing sounds
	for ( int i = 0; i < ASURA_PC_SOUND_MAX_NUMBER_OF_ACTIVE_SOUNDS; ++i )
	{
		if ( s_axActiveSounds[i].m_xAPI.IsValid() )
		{
			s_axActiveSounds[i].UpdateVolume();			
		}
	}
}

bool Asura_PC_Sound::SetOcclusionLPF( const Asura_Handle iPlayingSoundHandle, const float fOcclusion )
{
	if ( !s_axActiveSounds[iPlayingSoundHandle].m_xAPI.IsValid())
	{
		return false;
	}
	return s_axActiveSounds[iPlayingSoundHandle].m_xAPI.SetOcclusionLPF( fOcclusion );
}

bool Asura_PC_Sound::SetDistanceFilterGain ( Asura_Handle iPlayingSoundHandle, float fGain )
{
	if ( !s_axActiveSounds[iPlayingSoundHandle].m_xAPI.IsValid())
	{
		return false;
	}
	s_axActiveSounds[iPlayingSoundHandle].m_xAPI.SetDistanceFilterGain( fGain );
	return true;
}

// A C T I V E   S O U N D /////////////////////////////////////////////

Asura_PC_ActiveSound::Asura_PC_ActiveSound() :
    m_bIsLooping( 0 ),
    m_bPaused( false ),
    m_bPausedByPitch( false ),
	m_bUseHardware3dPan( false ),
	m_bEnable3dPanning( false ),
	m_bUseForcedPanAndFade( false ),
    m_iLoadedSoundIndex( -1 ),
	m_fVolume( 1.0f )
	#ifdef ASURA_TOOLS
	, m_bInvalidatedByTools( false )
	#endif
{
}

Asura_PC_ActiveSound::~Asura_PC_ActiveSound()
{
	Asura_Assert( !m_xAPI.IsValid(), "Buffer has not been destroyed!" );
}

bool Asura_PC_ActiveSound::Create( Asura_PC_LoadedSound& xLoadedSound, bool bUseHardware3dPan )
{
	Asura_Assert( Asura_PC_Sound::GetAPI().IsValid(), "Sound system not ready" );

	if ( !Asura_PC_Sound::GetAPI().IsValid() )
	{
		return false;
	}
	
	m_bUseHardware3dPan = bUseHardware3dPan;
	m_bEnable3dPanning = bUseHardware3dPan;

	bool result = m_xAPI.Initialise( bUseHardware3dPan, xLoadedSound.m_uSize, &xLoadedSound.m_xWfx, xLoadedSound.m_pucWaveData );
	Asura_Assert( result, "Failed to initialise sound buffer!" );

	return result;
}

void Asura_PC_ActiveSound::Update()
{
	m_xAPI.Update();
}

void Asura_PC_ActiveSound::Destroy()
{
	m_xAPI.Destroy();
}

bool Asura_PC_ActiveSound::SetVolume( float fVolume )
{
	Asura_Assert( fVolume >= 0.0f && fVolume <= 1.0f, "invalid volume for DS" );
	m_fVolume = fVolume;
	return UpdateVolume();
}

bool Asura_PC_ActiveSound::UpdateVolume()
{
	float totalVolume = m_fVolume * Asura_PC_Sound::GetSoundFXVolume();

	Asura_Assert( m_xAPI.IsValid(), "invalid sound" );

	bool result = m_xAPI.SetVolume( totalVolume );

	return result;
}

void Asura_PC_ActiveSound::SetReverbParams( const Asura_PC_Sound_ReverbParams& xReverbParams )
{
	m_xAPI.SetReverbParams( xReverbParams );
}

#endif	//!defined(ASURA_USE_AUDIO_SYSTEM)

//eof
