// KJL - 19/03/2001 15:20:12
// 
// Asura_PlatformSound.cpp


#include "Core/Asura.h"                          // Global header.

#include <eekernel.h>
#include <sifdev.h> // for sceRead

#include "Sound/Asura_Sound.h"
#include "Core/Asura_PlatformBasics.h"
#include "Debugging/Asura_Assert.h"
#include "FileAccess/Asura_FileAccess.h"
#include "Core/Asura_Maths.h"
#include "Debugging/Asura_Logfile.h"

#include "Core/Asura_PS2_OS.h"       // for ps2 fileroot


// multistream stuff
#include "Sound/Asura_PS2_Multistream.h"


// I N T E R N A L   D E F I N E S ///////////////////////////////////////////
//#define ASURA_PS2_MAX_ACTIVE_SOUNDS (44)  // max voices - 4 reserved for streaming
#define ASURA_PS2_MAX_ACTIVE_SOUNDS (ASURA_PS2_MS_AVAILABLE_VOICES)
#define ASURA_PS2_NUM_SOUND_VOICES (48)

#define INVALID_LOADED_SOUND_ID (-1)
#define ASURA_PS2_SOUND_FADE_SHIFT (15)
#define ASURA_PS2_SOUND_MAX_FADE (1 << ASURA_PS2_SOUND_FADE_SHIFT)

// T Y P E S /////////////////////////////////////////////////////////////////
struct Asura_PS2_ReverbSettings
{
	u_int m_uType;
	u_int m_uDepth;
};


// This struct came from newsgroup message "re: VAG header format" 25.01.02
struct VAGHeader
{
    unsigned int  format;  /* always 'VAGp' for identifying*/
    unsigned int  ver;  /* format version (2) */
    unsigned int  ssa;  /* Source Start Address, always 0 (reserved for VAB format) */
    unsigned int  size;  /* Sound Data Size in byte */
    unsigned int  fs;   /* sampling frequency, 44100(>pt1000), 32000(>pt),22000(>pt0800)... */
    unsigned short  volL;  /* base volume for Left channel */
    unsigned short  volR;  /* base volume for Right channel */
    unsigned short  pitch;  /* base pitch (includes fs modulation)*/
    unsigned short  ADSR1;  /* base ADSR1 (see SPU manual) */
    unsigned short  ADSR2;  /* base ADSR2 (see SPU manual) */
    unsigned short  reserved; /* not in use */
    char    name[16];
};

enum Asura_PS2_LoadedSoundType
{
    LST_NotLoaded,
    LST_Resident,
    LST_PartiallyStreamed,
	LST_FullyStreamed,
};

struct Asura_PS2_LoadedSound
{
    Asura_PS2_LoadedSound() { eLoadedType = LST_NotLoaded; }
    Asura_PS2_LoadedSoundType eLoadedType;
    unsigned short usBasePitch;
    bool bLooping;
#ifdef ASURA_ASSERT
    bool bDoneLoopWarning;
#endif
};

struct Asura_PS2_ActiveSound
{
    Asura_PS2_ActiveSound() { Set(INVALID_LOADED_SOUND_ID, 0); }
    void Set(short __iLoadedSoundIndex, short __pitch) 
    { 
        iLoadedSoundIndex = __iLoadedSoundIndex;
        volumeL = volumeR = 0x3fff; 
        pitch = __pitch;
        bLooping = false;
#ifdef ASURA_PS2_SUPPORT_DTS
		usFade = 0;
#endif
    }

    int iLoadedSoundIndex;
    short volumeL;
    short volumeR;
#ifdef ASURA_PS2_SUPPORT_DTS
	u_short usFade;	// 0 = front, ASURA_PS2_SOUND_MAX_FADE (32768) back
#endif
    short pitch;
    bool  bLooping;

};

// G L O B A L S /////////////////////////////////////////////////////////////
#if !defined(ASURA_PS2_SUPPORT_DTS) && !defined(ASURA_PS2_DISABLE_REVERB)
static Asura_PS2_ReverbSettings g_axReverbSettings[Asura_Sound_System::ASURA_REVERB_ID_MAX] = 
{
	//	reverb type									reverb depth (0-0x7fff)
	{	Asura_PS2_Multistream::REVERB_STUDIO_C,		0		}, // REVERB_NONE,
	{	Asura_PS2_Multistream::REVERB_STUDIO_A,		0x2000	}, // REVERB_SMALL_ROOM,
	{	Asura_PS2_Multistream::REVERB_STUDIO_B,		0x3000	}, // REVERB_MEDIUM_ROOM,
	{	Asura_PS2_Multistream::REVERB_STUDIO_C,		0x3000	}, // REVERB_LARGE_ROOM,
	{	Asura_PS2_Multistream::REVERB_STUDIO_B,		0x3000	}, // REVERB_SMALL_HALL,
	{	Asura_PS2_Multistream::REVERB_STUDIO_C,		0x4000	}, // REVERB_MEDIUM_HALL,
	{	Asura_PS2_Multistream::REVERB_PIPE,			0x3000	}, // REVERB_PIPE,
};
#endif

static Asura_PS2_ActiveSound* sg_PS2_ActiveSounds;
static Asura_PS2_LoadedSound* sg_PS2_LoadedSounds;

DEBUGVAR bool dbg_bReportAllLoadedSounds = false;

#ifdef ASURA_PS2_SUPPORT_DTS
DEBUGVAR bool dbg_bTestProLogic = false;
#endif

//// PS2_Sound_ConvertFilename() adds host0 or cdrom0 and ;1 if necessary
//// If change_extension is non-NULL, hackily changes .WAV to contents of change_extension
//static void PS2_Sound_ConvertFilename(const char * filename, char * buffer, 
//                               u_int buffer_size, 
//                               const char * change_extension/* = NULL*/)
//{
//    Asura_Assert(!strstr(filename, "/"), "filenames cannot contain '/' on CD builds, use '\\'");
//
//#ifdef ASURA_PS2_CDEMUL
//    Asura_Assert(buffer_size >= strlen(filename) + strlen("cdrom0:\\") + strlen(";1"), "buffer too small");
//    strcpy(buffer, "cdrom0:\\");
//    strcat(buffer, filename);
//    if (change_extension)
//    {
//        int len = strlen(buffer);
//        Asura_Assert((!strcmp(&buffer[len-3], "WAV") || !strcmp(&buffer[len-3], "wav")), "should be a wav");
//        buffer[len - 3] = 0;
//        strcat(buffer, change_extension);
//    }
//    strcat(buffer, ";1");
//    strupr( buffer + 7 );
//    // (MH - 15.08.2002) hack to force rogue filenames - could be removed eventually.
//    char * pSlash = strstr(buffer, "/");
//    while (pSlash)
//    {
//        *pSlash = '\\';
//        pSlash = strstr(pSlash, "/");
//    }
//#else
//    Asura_Assert(buffer_size >= strlen(filename) + strlen(ASURA_PS2_FILEROOT), "buffer too small");
//    strcpy(buffer, ASURA_PS2_FILEROOT);
//    strcat(buffer, filename);
//    if (change_extension)
//    {
//        int len = strlen(buffer);
//        Asura_Assert((!strcmp(&buffer[len-3], "WAV") || !strcmp(&buffer[len-3], "wav")), "should be a wav");
//        buffer[len - 3] = 0;
//        strcat(buffer, change_extension);
//    }
//#endif
//}

// function based on newsgroups message by Nicolas Bécavin <nbecavin@kalisto.com>
// 02 March 2001 18:24 
// sce.dev.prog.spu 
// Re: Another loopy question
static bool PS2SoundIsLooping( const char* pcVagData, int iDataSize)
{
    const char* pcLoopByte = pcVagData + iDataSize - 15;

    // (MH - 16.01.2003) I think this is wrong
    //if ( *pcLoopByte & 0x3 )
    if (*pcLoopByte == 0x03)
    {
        return true;
    }
    else
    {
        return false;
    }
}

static unsigned short GetPitchFromVAGHeader(const VAGHeader * vag_header)
{
    int wrong_endian_sample_rate = vag_header->fs;
    int sampling_rate = ((wrong_endian_sample_rate & 0xff000000) >> 24) | 
        ((wrong_endian_sample_rate & 0x00ff0000) >> 8);
    u_short pitch = (sampling_rate*4096)/48000;
    return pitch;
}

int Asura_Sound_System::PlatformInitialise(void)
{
    sg_PS2_ActiveSounds = new Asura_PS2_ActiveSound[ASURA_PS2_MAX_ACTIVE_SOUNDS];
    sg_PS2_LoadedSounds = new Asura_PS2_LoadedSound[ASURA_MAX_NUMBER_OF_LOADED_SOUNDS];

    return ASURA_PS2_MAX_ACTIVE_SOUNDS;
}

int Asura_Sound_System::Platform_GetMaxSoundsWithEQ(void)
{
	// voices 0-23 (core 0) can have effects on them.
	return 24;
}

void Asura_Sound_System::PlatformShutdown(void)
{
    delete [] sg_PS2_ActiveSounds;
    delete [] sg_PS2_LoadedSounds;
}

void Asura_Sound_System::PlatformReset(void)
{
    // (MP - 18.02.2003) TODO: Doesn't really belong here anymore because
    // MS does a bunch of stuff other than sound, eg. file access.
    Asura_PS2_Multistream::Reset();

#ifdef ASURA_PS2_SUPPORT_PARTIALLY_STREAMED_SOUNDS
	// (MH - 16.07.2003) multistream clears out partially streamed stubs at reset.
	for ( u_int u = 0; u < ASURA_MAX_NUMBER_OF_LOADED_SOUNDS; ++u)
	{
		if (sg_PS2_LoadedSounds[u].eLoadedType == LST_PartiallyStreamed)
		{
			sg_PS2_LoadedSounds[u].eLoadedType = LST_NotLoaded;
		}
	}
#endif // #ifdef ASURA_PS2_SUPPORT_PARTIALLY_STREAMED_SOUNDS
}

void Asura_Sound_System::PlatformSetResetPoint(void)
{
    Asura_PS2_Multistream::SetResetPoint();
}


void Asura_Sound_System::Platform_SetReverb(enum ReverbType eType, float fReverbAmount )
{
#ifdef ASURA_PS2_DISABLE_REVERB
	Asura_Assert(eType == ASURA_REVERB_ID_NONE, "reverb is disabled");
#else
	#ifdef ASURA_PS2_SUPPORT_DTS
		Asura_Assert(false, "reverb is not supported with DTS");
	#else
		Asura_PS2_Multistream::SetReverb(g_axReverbSettings[eType].m_uType, g_axReverbSettings[eType].m_uDepth);
	#endif
#endif
}

bool Asura_Sound_System::Platform_SetSoundWetDryVolume(Asura_Handle iPlayingSoundHandle, float fWetVolume, float fDryVolume)
{
	//TODO: Write me please
	return true;
}

bool Asura_Sound_System::Platform_SetOcclusionLPF(const Asura_Handle iPlayingSoundHandle, const float fOcclusion)
{
	//TODO: Write me please
	return true;
}

DEBUGVAR float dbg_fUnderwaterFreqMin = 16.0f;
DEBUGVAR float dbg_fDeafenedFreqMin = 4.0f;

void Asura_Sound_System::Platform_SetEQType(Asura_Handle iHandle, EQType eType, float fLevel)
{
	Asura_PS2_Multistream::EQ_FILTER_TYPE eFilterType;
	u_int uFilterFreq;
	switch(eType)
	{
		default:
		{
			Asura_Assert(false, "invalid EQ type");
			return;
		}
		case EQ_OFF:
		{
			break;
		}
		case EQ_UNDERWATER:
		{
			eFilterType = Asura_PS2_Multistream::FILTER_LOWPASS;
			uFilterFreq = Asura_Maths::FloatToInt(dbg_fUnderwaterFreqMin + ((512.0f - dbg_fUnderwaterFreqMin) * (1.0f - fLevel)));
			break;
		}
		case EQ_DEAFENED:
		{
			eFilterType = Asura_PS2_Multistream::FILTER_LOWPASS;
			uFilterFreq = Asura_Maths::FloatToInt(dbg_fDeafenedFreqMin + ((512.0f - dbg_fDeafenedFreqMin) * (1.0f - fLevel)));
			break;
		}
	}
	
	if ( (eType == EQ_OFF) || (fLevel == 0.0f) )
	{
		Asura_PS2_Multistream::SetEQMute(iHandle, true);
	}
	else
	{
		Asura_PS2_Multistream::SetEQ( iHandle, eFilterType, uFilterFreq );
	}
}


// JAT 03/03/06 
// Temporary code for development use only - should be gone in a week or 2's time
// Get the length of a file used during development only, not for final release
static int GetFileLength( const char* const szFileName )
{
	const char* const szFullFileName = Asura_PS2_OS::GetFullPath(szFileName, true);
	sce_stat xStatBuf;
	const int iCode = ::sceGetstat( szFullFileName, &xStatBuf );
	return xStatBuf.st_size;
}


// SD: Add Asura_Sound_System::PlatformLoadSound for on disk VAG files
bool Asura_Sound_System::PlatformLoadSound(const char* szFilename, const int iLoadedSoundIndex)
{
	Asura_Handle fileHandle = Asura_FileAccess::Open(szFilename);
	if (0 == fileHandle)
	{
		return false;
	}
	int fileSize = GetFileLength(szFilename); // FIXME: No Asura_FileAccess::GetLength(Asura_Handle) ?
	bool bStatus = PlatformLoadSoundFromResource(fileHandle, fileSize, iLoadedSoundIndex);
	Asura_FileAccess::Close(fileHandle);
	return bStatus;
}

// Load VAG file
bool Asura_Sound_System::PlatformLoadSoundFromResource(Asura_Handle fileHandle, int fileSize, int iLoadedSoundIndex)
{
	Asura_Assert( iLoadedSoundIndex != -1, "Invalid Sound Index, Out of sound handles ?" );

    if (sg_PS2_LoadedSounds[iLoadedSoundIndex].eLoadedType != LST_NotLoaded)
    {
        Asura_Assert(false, "sound already loaded");
        Asura_FileAccess::SkipData(fileHandle,fileSize);
        return false;
    }

    char * EE_address = (char*)Asura_AllocateAlign(fileSize, ASURA_ALIGN_128_BYTE);
    if (!EE_address)
    {
        Asura_Assert(0, "failed to allocate EE memory for sound loading.\n");
        Asura_FileAccess::SkipData(fileHandle,fileSize);
        return false;
    }
    Asura_FileAccess::ReadData(fileHandle, EE_address, fileSize);

    bool ok = false;
    if (Asura_PS2_Multistream::LoadSoundFromMemory(EE_address, fileSize, iLoadedSoundIndex))
    {
        ok = true;
        sg_PS2_LoadedSounds[iLoadedSoundIndex].eLoadedType = LST_Resident;
        // read the VAG header for pitch info
        VAGHeader * vag_header = (VAGHeader *)EE_address;
        sg_PS2_LoadedSounds[iLoadedSoundIndex].usBasePitch = GetPitchFromVAGHeader(vag_header);
        sg_PS2_LoadedSounds[iLoadedSoundIndex].bLooping = PS2SoundIsLooping(EE_address, fileSize);
#ifdef ASURA_ASSERT
        sg_PS2_LoadedSounds[iLoadedSoundIndex].bDoneLoopWarning = false;
#endif
        
        if( dbg_bReportAllLoadedSounds )
        {
            Asura_OutputToDebugger(("sound loaded index = %d. looping = %d.\n", iLoadedSoundIndex, sg_PS2_LoadedSounds[iLoadedSoundIndex].bLooping));
        }
    }

    Asura_DeallocateAlign(EE_address);
    return ok;
}

bool Asura_Sound_System::PlatformUnloadSound(const int iLoadedSoundIndex)
{
    Asura_Assert((iLoadedSoundIndex >= 0) && (iLoadedSoundIndex < ASURA_MAX_NUMBER_OF_LOADED_SOUNDS), "invalid loaded sound index");
#ifdef ASURA_PS2_SUPPORT_PARTIALLY_STREAMED_SOUNDS
    bool bReplaceable = Asura_PS2_Multistream::IsLoadedIndexReplaceableSound(iLoadedSoundIndex);
#else
    bool bReplaceable = false;
#endif
	// (MH - 08.12.2003) Multistream clears out partially streamed stubs at reset and shutdown.
	// Only need to unload sounds which are resident. Sounds which the independent sound code 
	// thinks are loaded should be resident or replaceable, so check that.
	Asura_Assert(bReplaceable || sg_PS2_LoadedSounds[iLoadedSoundIndex].eLoadedType == LST_Resident, "sound not loaded");
	if (sg_PS2_LoadedSounds[iLoadedSoundIndex].eLoadedType == LST_Resident)
	{
		Asura_PS2_Multistream::UnloadSound(iLoadedSoundIndex);
	}
	sg_PS2_LoadedSounds[iLoadedSoundIndex].eLoadedType = LST_NotLoaded;
	return true;
}

bool Asura_Sound_System::PlatformReplaceSound(const int iExistingSoundLoadedID, const int iNewSoundLoadedID)
{
#ifdef ASURA_PS2_SUPPORT_PARTIALLY_STREAMED_SOUNDS
    // check arguments
    Asura_Assert((iExistingSoundLoadedID >= 0) && (iExistingSoundLoadedID < ASURA_MAX_NUMBER_OF_LOADED_SOUNDS), "invalid loaded sound index");
    Asura_Assert((iNewSoundLoadedID >= 0) && (iNewSoundLoadedID < ASURA_MAX_NUMBER_OF_LOADED_SOUNDS), "invalid loaded sound index");
    
    if (sg_PS2_LoadedSounds[iExistingSoundLoadedID].eLoadedType == LST_FullyStreamed &&
		sg_PS2_LoadedSounds[iNewSoundLoadedID].eLoadedType == LST_FullyStreamed)
    {
		// (AW - 20.09.2004) - If both sounds are fully streamed then no replacement need
		// take place.
		return true;
	}
    if (sg_PS2_LoadedSounds[iExistingSoundLoadedID].eLoadedType == LST_NotLoaded)
    {
        Asura_Assert(false, "existing sound not loaded");
        return PlatformLoadReplaceableSound(iNewSoundLoadedID);
    }
    if (sg_PS2_LoadedSounds[iNewSoundLoadedID].eLoadedType != LST_NotLoaded)
    {
        Asura_Assert(false, "new sound already loaded");
        return true;	// was return false but actually the new sound loaded ok
    }
    
    u_short usBasePitch;
    if (Asura_PS2_Multistream::ReplaceSound(iExistingSoundLoadedID, iNewSoundLoadedID, usBasePitch))
    {
        sg_PS2_LoadedSounds[iExistingSoundLoadedID].eLoadedType = LST_NotLoaded;
        Asura_PS2_LoadedSoundType eLoadedType;
		if (Asura_PS2_Multistream::IsLoadedIndexFullyStreamedSound(iNewSoundLoadedID))
		{
			eLoadedType = LST_FullyStreamed;
		}
        else if (Asura_PS2_Multistream::IsLoadedIndexPartiallyStreamedSound(iNewSoundLoadedID))
        {
            eLoadedType = LST_PartiallyStreamed;
        }
        else
        {
            eLoadedType = LST_Resident;
        }
        sg_PS2_LoadedSounds[iNewSoundLoadedID].eLoadedType = eLoadedType;
        sg_PS2_LoadedSounds[iNewSoundLoadedID].usBasePitch = usBasePitch;
        sg_PS2_LoadedSounds[iNewSoundLoadedID].bLooping = true; // all one-shot samples are now looping. 
        //They get played once using the SOUND_PlaySfxLoop() Multistream call with 1 for the number of times.
#ifdef ASURA_ASSERT
        sg_PS2_LoadedSounds[iNewSoundLoadedID].bDoneLoopWarning = false;
#endif
        return true;
    }
#endif // #ifdef ASURA_PS2_SUPPORT_PARTIALLY_STREAMED_SOUNDS
    return false;
}

bool Asura_Sound_System::PlatformLoadReplaceableSound(const int iLoadedSoundID)
{
#ifdef ASURA_PS2_SUPPORT_PARTIALLY_STREAMED_SOUNDS
    // check argument
    Asura_Assert((iLoadedSoundID >= 0) && (iLoadedSoundID < ASURA_MAX_NUMBER_OF_LOADED_SOUNDS), "invalid loaded sound index");
    
    if (sg_PS2_LoadedSounds[iLoadedSoundID].eLoadedType != LST_NotLoaded)
    {
        // NJD 14/07/2004 - changed this from 'return false'
        return true;
    }

    u_short usBasePitch;
    if (Asura_PS2_Multistream::LoadReplaceableSound(iLoadedSoundID, usBasePitch))
    {
        Asura_PS2_LoadedSoundType eLoadedType;
        if (Asura_PS2_Multistream::IsLoadedIndexFullyStreamedSound(iLoadedSoundID))
		{
			eLoadedType = LST_FullyStreamed;
		}
        else if (Asura_PS2_Multistream::IsLoadedIndexPartiallyStreamedSound(iLoadedSoundID))
        {
            eLoadedType = LST_PartiallyStreamed;
        }
        else
        {
            eLoadedType = LST_Resident;
        }
        sg_PS2_LoadedSounds[iLoadedSoundID].eLoadedType = eLoadedType;
        sg_PS2_LoadedSounds[iLoadedSoundID].usBasePitch = usBasePitch;
        sg_PS2_LoadedSounds[iLoadedSoundID].bLooping = true; // all one-shot samples are now looping. 
        //They get played once using the SOUND_PlaySfxLoop() Multistream call with 1 for the number of times.
#ifdef ASURA_ASSERT
        sg_PS2_LoadedSounds[iLoadedSoundID].bDoneLoopWarning = false;
#endif
        return true;
    }
#endif // #ifdef ASURA_PS2_SUPPORT_PARTIALLY_STREAMED_SOUNDS

    return false;

}

int Asura_Sound_System::Platform_GetReplaceableSoundSize(const int iLoadedSoundIndex)
{
#ifdef ASURA_PS2_SUPPORT_PARTIALLY_STREAMED_SOUNDS
    return Asura_PS2_Multistream::GetReplaceableSoundSize(iLoadedSoundIndex);
#else
    return 0;
#endif
}

bool Asura_Sound_System::PlatformCreateSound(int iLoadedSoundIndex, int iActiveSoundIndex, bool bUseEQ, bool bUseHardware3dPan)
{
    Asura_Assert((iLoadedSoundIndex >= 0) && (iLoadedSoundIndex < ASURA_MAX_NUMBER_OF_LOADED_SOUNDS), "invalid loaded sound index");
    Asura_Assert((iActiveSoundIndex >= 0) && (iActiveSoundIndex < ASURA_PS2_MAX_ACTIVE_SOUNDS), "invalid active sound index");
	// SD: 3d panning will be emulated 
	// Asura_Assert(!bUseHardware3dPan, "hardware pan not supported on PS2");

// I don't see why we actually need to check this - presumably when this fails it is because
// Multistream hasn't flagged a stopped sound as actually having stopped yet.
//    if (!Asura_PS2_Multistream::IsVoiceFree(iActiveSoundIndex))
//    {
//        // voice is not free
//        Asura_Assert(0, "sound system passed in voice which is not free");
//        return false;
//    }

    if (sg_PS2_LoadedSounds[iLoadedSoundIndex].eLoadedType == LST_NotLoaded)
    {
        return false;
    }

    Asura_Assert(sg_PS2_ActiveSounds[iActiveSoundIndex].iLoadedSoundIndex == INVALID_LOADED_SOUND_ID, "BUG: sound in use");
    Asura_Assert(sg_PS2_LoadedSounds[iLoadedSoundIndex].eLoadedType != LST_NotLoaded, "sound not loaded");
    short usBasePitch = sg_PS2_LoadedSounds[iLoadedSoundIndex].usBasePitch;
    sg_PS2_ActiveSounds[iActiveSoundIndex].Set(iLoadedSoundIndex, usBasePitch);

	if (iActiveSoundIndex < 24)
	{
		Asura_PS2_Multistream::SetVoiceUseEQ( iActiveSoundIndex, bUseEQ );
	}

    return true;
}

bool Asura_Sound_System::PlatformPlaySound(Asura_Handle iActiveSoundIndex, bool bLooping)
{
    Asura_Assert(iActiveSoundIndex >= 0 && iActiveSoundIndex < ASURA_PS2_MAX_ACTIVE_SOUNDS, "invalid sound handle");
    int iLoadedSoundIndex = sg_PS2_ActiveSounds[iActiveSoundIndex].iLoadedSoundIndex;
    Asura_Assert(iLoadedSoundIndex != INVALID_LOADED_SOUND_ID, "play sound error: not yet created");
    if (iLoadedSoundIndex == INVALID_LOADED_SOUND_ID)
    {
        return false;
    }

#ifdef ASURA_PS2_SUPPORT_PARTIALLY_STREAMED_SOUNDS
	Asura_PS2_LoadedSoundType eLoadedType = sg_PS2_LoadedSounds[iLoadedSoundIndex].eLoadedType;
    if ( (eLoadedType == LST_PartiallyStreamed) || (eLoadedType == LST_FullyStreamed) )
    {
        return Asura_PS2_Multistream::
                    PlayPartiallyStreamingSound(iLoadedSoundIndex, 
                                                iActiveSoundIndex, 
                                                sg_PS2_ActiveSounds[iActiveSoundIndex].volumeL, 
                                                sg_PS2_ActiveSounds[iActiveSoundIndex].volumeR);
    }
#endif // #ifdef ASURA_PS2_SUPPORT_PARTIALLY_STREAMED_SOUNDS
    
    // (MH - 04.03.2003) MS has functionality to play looping VAGs a set number of times, or forever.
    if (sg_PS2_LoadedSounds[sg_PS2_ActiveSounds[iActiveSoundIndex].iLoadedSoundIndex].bLooping)
    {
        u_int uLoopCount = bLooping ? 0 : 1;
#ifdef ASURA_PS2_SUPPORT_DTS
		if (Asura_PS2_Multistream::IsDTSOn())
		{
			u_short usFade = sg_PS2_ActiveSounds[iActiveSoundIndex].usFade;
			u_short usOneMinusFade = ASURA_PS2_SOUND_MAX_FADE - usFade;
			int iVolLF = (sg_PS2_ActiveSounds[iActiveSoundIndex].volumeL * usFade) >> ASURA_PS2_SOUND_FADE_SHIFT;
			int iVolRF = (sg_PS2_ActiveSounds[iActiveSoundIndex].volumeR * usFade) >> ASURA_PS2_SOUND_FADE_SHIFT;
			int iVolLB = (sg_PS2_ActiveSounds[iActiveSoundIndex].volumeL * usOneMinusFade) >> ASURA_PS2_SOUND_FADE_SHIFT;
			int iVolRB = (sg_PS2_ActiveSounds[iActiveSoundIndex].volumeR * usOneMinusFade) >> ASURA_PS2_SOUND_FADE_SHIFT;
			Asura_PS2_Multistream::PlayLoopingSound(   iLoadedSoundIndex,
														ASURA_PS2_MS_DTS_FRONT_VOICE(iActiveSoundIndex),
														iVolLF,
														iVolRF,
														sg_PS2_ActiveSounds[iActiveSoundIndex].pitch
														uLoopCount
													);

			if (dbg_bTestProLogic)
			{
				int iBackVol = (iVolLB + iVolRB) / 2;
				Asura_PS2_Multistream::PlayLoopingSound(   iLoadedSoundIndex,
															ASURA_PS2_MS_DTS_REAR_VOICE(iActiveSoundIndex),
															iBackVol,
															0x7fff - iBackVol,
															sg_PS2_ActiveSounds[iActiveSoundIndex].pitch,
															uLoopCount
														);
			}
			else
			{
				Asura_PS2_Multistream::PlayLoopingSound(   iLoadedSoundIndex,
															ASURA_PS2_MS_DTS_REAR_VOICE(iActiveSoundIndex),
															iVolLB,
															iVolRB,
															sg_PS2_ActiveSounds[iActiveSoundIndex].pitch,
															uLoopCount
														);
			}
		}
		else // if (Asura_PS2_Multistream::IsDTSOn())
#endif // #ifdef ASURA_PS2_SUPPORT_DTS
		{
			Asura_PS2_Multistream::PlayLoopingSound(   iLoadedSoundIndex,
														iActiveSoundIndex,
														sg_PS2_ActiveSounds[iActiveSoundIndex].volumeL,
														sg_PS2_ActiveSounds[iActiveSoundIndex].volumeR,
														sg_PS2_ActiveSounds[iActiveSoundIndex].pitch,
														uLoopCount
													);
		}
    }
    else
    {
#ifdef ASURA_ASSERT
    if (!sg_PS2_LoadedSounds[sg_PS2_ActiveSounds[iActiveSoundIndex].iLoadedSoundIndex].bDoneLoopWarning)
    {
        Asura_OutputToDebugger(("old VAG: no loop flag.\n"));
        sg_PS2_LoadedSounds[sg_PS2_ActiveSounds[iActiveSoundIndex].iLoadedSoundIndex].bDoneLoopWarning = true;
    }
#endif
        // (MH - 04.03.2003) play the sound anyway using the old method
#ifdef ASURA_PS2_SUPPORT_DTS
		if (Asura_PS2_Multistream::IsDTSOn())
		{
			u_short usFade = sg_PS2_ActiveSounds[iActiveSoundIndex].usFade;
			u_short usOneMinusFade = ASURA_PS2_SOUND_MAX_FADE - usFade;
			int iVolLF = (sg_PS2_ActiveSounds[iActiveSoundIndex].volumeL * usFade) >> ASURA_PS2_SOUND_FADE_SHIFT;
			int iVolRF = (sg_PS2_ActiveSounds[iActiveSoundIndex].volumeR * usFade) >> ASURA_PS2_SOUND_FADE_SHIFT;
			int iVolLB = (sg_PS2_ActiveSounds[iActiveSoundIndex].volumeL * usOneMinusFade) >> ASURA_PS2_SOUND_FADE_SHIFT;
			int iVolRB = (sg_PS2_ActiveSounds[iActiveSoundIndex].volumeR * usOneMinusFade) >> ASURA_PS2_SOUND_FADE_SHIFT;
			Asura_PS2_Multistream::PlaySound(   sg_PS2_ActiveSounds[iActiveSoundIndex].iLoadedSoundIndex,
												ASURA_PS2_MS_DTS_FRONT_VOICE(iActiveSoundIndex),
												iVolLF,
												iVolRF,
												sg_PS2_ActiveSounds[iActiveSoundIndex].pitch);
			if (dbg_bTestProLogic)
			{
				int iBackVol = (iVolLB + iVolRB) / 2;
				Asura_PS2_Multistream::PlaySound(   sg_PS2_ActiveSounds[iActiveSoundIndex].iLoadedSoundIndex,
													ASURA_PS2_MS_DTS_REAR_VOICE(iActiveSoundIndex),
													iBackVol,
													0x7fff - iBackVol,
													sg_PS2_ActiveSounds[iActiveSoundIndex].pitch);
			}
			else
			{
				Asura_PS2_Multistream::PlaySound(   sg_PS2_ActiveSounds[iActiveSoundIndex].iLoadedSoundIndex,
													ASURA_PS2_MS_DTS_REAR_VOICE(iActiveSoundIndex),
													iVolLB,
													iVolRB,
													sg_PS2_ActiveSounds[iActiveSoundIndex].pitch);
			}
		}
		else // if (Asura_PS2_Multistream::IsDTSOn())
#endif // #ifdef ASURA_PS2_SUPPORT_DTS
        {
			Asura_PS2_Multistream::PlaySound(   sg_PS2_ActiveSounds[iActiveSoundIndex].iLoadedSoundIndex,
												iActiveSoundIndex,
												sg_PS2_ActiveSounds[iActiveSoundIndex].volumeL,
												sg_PS2_ActiveSounds[iActiveSoundIndex].volumeR,
												sg_PS2_ActiveSounds[iActiveSoundIndex].pitch);
		}
    }

    return true;
}

bool Asura_Sound_System::PlatformStopSound(Asura_Handle iActiveSoundIndex)
{
    Asura_Assert(iActiveSoundIndex >= 0 && iActiveSoundIndex < ASURA_PS2_MAX_ACTIVE_SOUNDS, "invalid sound handle");
    int iLoadedSoundIndex = sg_PS2_ActiveSounds[iActiveSoundIndex].iLoadedSoundIndex;
    Asura_Assert(iLoadedSoundIndex != INVALID_LOADED_SOUND_ID, "stop sound error: not created");
    if (iLoadedSoundIndex == INVALID_LOADED_SOUND_ID) return false;

#ifdef ASURA_PS2_SUPPORT_DTS

	#ifdef ASURA_PS2_SUPPORT_PARTIALLY_STREAMED_SOUNDS
	#error "partially streamed sounds and DTS don't mix - need code to avoid streaming twice."
	#endif//#ifdef ASURA_PS2_SUPPORT_PARTIALLY_STREAMED_SOUNDS

		Asura_PS2_Multistream::StopSound(ASURA_PS2_MS_DTS_FRONT_VOICE(iActiveSoundIndex));
		Asura_PS2_Multistream::StopSound(ASURA_PS2_MS_DTS_REAR_VOICE(iActiveSoundIndex));

#else // not #ifdef ASURA_PS2_SUPPORT_DTS

	#ifdef ASURA_PS2_SUPPORT_PARTIALLY_STREAMED_SOUNDS
		Asura_PS2_LoadedSoundType eLoadedType = sg_PS2_LoadedSounds[iLoadedSoundIndex].eLoadedType;
		if ( (eLoadedType == LST_PartiallyStreamed) || (eLoadedType == LST_FullyStreamed) )
		{
			Asura_PS2_Multistream::StopPartiallyStreamingSound(iActiveSoundIndex);
		}
		else
	#endif//#ifdef ASURA_PS2_SUPPORT_PARTIALLY_STREAMED_SOUNDS
		{
			Asura_PS2_Multistream::StopSound(iActiveSoundIndex);
		}

#endif // #else not #ifdef ASURA_PS2_SUPPORT_DTS
    return true;
}

bool Asura_Sound_System::PlatformPauseSound(Asura_Handle iActiveSoundIndex)
{
    Asura_Assert(iActiveSoundIndex >= 0 && iActiveSoundIndex < ASURA_PS2_MAX_ACTIVE_SOUNDS, "invalid sound handle");
#ifdef ASURA_PS2_SUPPORT_DTS
    Asura_PS2_Multistream::SetPitch(ASURA_PS2_MS_DTS_FRONT_VOICE(iActiveSoundIndex), 0);
    Asura_PS2_Multistream::SetPitch(ASURA_PS2_MS_DTS_REAR_VOICE(iActiveSoundIndex), 0);
#else
	Asura_PS2_Multistream::SetPitch(iActiveSoundIndex, 0);
#endif
    return true;
}

bool Asura_Sound_System::PlatformUnpauseSound(Asura_Handle iActiveSoundIndex)
{
    Asura_Assert(iActiveSoundIndex >= 0 && iActiveSoundIndex < ASURA_PS2_MAX_ACTIVE_SOUNDS, "invalid sound handle");
    Asura_PS2_Multistream::SetPitch(iActiveSoundIndex, sg_PS2_ActiveSounds[iActiveSoundIndex].pitch);
    return true;
}

bool Asura_Sound_System::PlatformDestroySound(Asura_Handle iActiveSoundIndex)
{
    Asura_Assert(iActiveSoundIndex >= 0 && iActiveSoundIndex < ASURA_PS2_MAX_ACTIVE_SOUNDS, "invalid sound handle");
    Asura_Assert(sg_PS2_ActiveSounds[iActiveSoundIndex].iLoadedSoundIndex != INVALID_LOADED_SOUND_ID, "stop sound error: not created");
    if (PlatformSoundIsPlaying(iActiveSoundIndex))
    {
        PlatformStopSound(iActiveSoundIndex);
    }
    sg_PS2_ActiveSounds[iActiveSoundIndex].Set(INVALID_LOADED_SOUND_ID, 0);
	// reset voice EQ to default
	if (iActiveSoundIndex < 24)
	{
		Asura_PS2_Multistream::SetVoiceUseEQ( iActiveSoundIndex, true );
	}
    return true;
}

static bool dbg_bDisableMS = false;
void Asura_Sound_System::PlatformUpdate()
{
    // (MP - 20.09.2002) TODO: It would be good to move this somewhere else;
    // the multistream stuff isn't inherently part of the sound system.
    if (!dbg_bDisableMS)
        Asura_PS2_Multistream::Update();
}

bool Asura_Sound_System::PlatformSoundIsPlaying(Asura_Handle iActiveSoundIndex)
{
    Asura_Assert(iActiveSoundIndex >= 0 && iActiveSoundIndex < ASURA_PS2_MAX_ACTIVE_SOUNDS, "invalid sound handle");
    int iLoadedSoundIndex = sg_PS2_ActiveSounds[iActiveSoundIndex].iLoadedSoundIndex;
    Asura_Assert(iLoadedSoundIndex != INVALID_LOADED_SOUND_ID, "PlatformSoundIsPlaying error: not created");
    if (iLoadedSoundIndex == INVALID_LOADED_SOUND_ID) return false;
    
#ifdef ASURA_PS2_SUPPORT_PARTIALLY_STREAMED_SOUNDS
	Asura_PS2_LoadedSoundType eLoadedType = sg_PS2_LoadedSounds[iLoadedSoundIndex].eLoadedType;
    if ( (eLoadedType == LST_PartiallyStreamed) || (eLoadedType == LST_FullyStreamed) )
	{
		return Asura_PS2_Multistream::IsPartiallyStreamedSoundPlaying(iActiveSoundIndex);
	}
	else
#endif//#ifdef ASURA_PS2_SUPPORT_PARTIALLY_STREAMED_SOUNDS
	{
		return Asura_PS2_Multistream::IsSoundPlaying(iActiveSoundIndex);
	}
}

bool Asura_Sound_System::PlatformSoundIsPreparing( Asura_Handle iPlayingSoundHandle )
{
	return false;
}

bool Asura_Sound_System::PlatformSetSoundVolumePanAndFade(Asura_Handle iActiveSoundIndex, float fVolume, float fPan, float fFade)
{
    Asura_Assert(iActiveSoundIndex >= 0 && iActiveSoundIndex < ASURA_PS2_MAX_ACTIVE_SOUNDS, "invalid sound handle");
    Asura_Assert(sg_PS2_ActiveSounds[iActiveSoundIndex].iLoadedSoundIndex != INVALID_LOADED_SOUND_ID, "error: not created");
    // volume scale from 0 to 1; pan and fade should be 0 to 1
    Asura_Assert(fVolume >= 0.0f && fVolume <= 1.0f, "volume out of range");
    Asura_Assert(fPan >= 0.0f && fPan <= 1.0f, "pan out of range");
	Asura_Assert(fFade >= 0.0f && fFade <= 1.0f, "fade out of range");
    Asura_Assert((s_eSoundOutputType != SoundOutputMono) || ((fPan == 0.5f) && (fFade == 1.0f)), "pan/fade set but sound system is mono");

    // we used to have a pan weighting to prevent zero volume at either side
    // that might be worth having if headphones are selected.
    const float max_vol = (float)0x3fff;
    float vol = max_vol * fVolume;
//    float volr = vol * fPan;
//    float voll = vol - volr;
// from newsgroups - equal power panning
	//Pan = -0.5 to 0.5
	//volL = sqrt(0.5-pan)
	//volR = sqrt(0.5+pan)
	//float fPan2 = fPan - 0.5f;
	float voll = vol * Asura_Maths::Sqrt(1.0f - fPan);//(0.5f - (fPan - 0.5f));
	float volr = vol * Asura_Maths::Sqrt(fPan);//0.5f + (fPan - 0.5f));

    // set volumes in active sound struct (for stopping/restarting)
    short sVolL = Asura_Maths::FloatToInt(voll);
    short sVolR = Asura_Maths::FloatToInt(volr);

	// set the phase for ProLogic2
#ifdef ASURA_PS2_SUPPORT_DTS
	if (!Asura_PS2_Multistream::IsDTSOn())
#endif
	{
		if (s_eSoundOutputType == SoundOutputSurround)
		{
			bool bSoundIsBehind = (fFade < 0.5f);
			short sPrevVolL = sg_PS2_ActiveSounds[iActiveSoundIndex].volumeL;
			short sPrevVolR = sg_PS2_ActiveSounds[iActiveSoundIndex].volumeR;

			// (MH - 11.06.2003) codied copied from Buzz Burrowes' newsgroup post
			if (bSoundIsBehind)
			{   
				// new azimuth has the sound in the back...
				// channels need to be out of phase with each other.
				if( (sPrevVolL < 0 && sPrevVolR > 0) || (sPrevVolL > 0 && sPrevVolR < 0) )
				{
					// previous volumes where out of phase... match phase...
					sVolL *= (sPrevVolL < 0) ? -1 : 1;
					sVolR *= (sPrevVolR < 0) ? -1 : 1;
				}
				else
				{
					// previous volumes were in phase with each other.
					// we need to switch one...
					if( sVolL < sVolR )
					{
						// switch the phase of the left channel...
						sVolL *= (sPrevVolL < 0) ? 1 : -1;
						// keep the phase of the right channel...
						sVolR *= (sPrevVolR < 0) ? -1 : 1;
					}
					else
					{
						// keep the phase of the left channel...
						sVolL *= (sPrevVolL < 0) ? -1 : 1;
						// switch the phase of the right channel...
						sVolR *= (sPrevVolR < 0) ? 1 : -1;
					}
				}
			}
			else
			{   
				// new azimuth has the sound in the front...
				// channels need to be in phase with each other.
				if(sPrevVolL < 0 && sPrevVolR < 0)
				{
					// old volume was in phase but both negative.
					// maintain this relationship...
					sVolL *= -1;
					sVolR *= -1;
				}
				else if( (sPrevVolL > 0 && sPrevVolR < 0) || (sPrevVolL < 0 && sPrevVolR > 0) )
				{
					// old volume was out of phase... figure out which one to flip...
					if(abs(sPrevVolL) > abs(sPrevVolR))
					{
						// sPrevVolR was smaller... let's play with that...
						sVolL *= (sPrevVolL < 0) ? -1 : 1;
						sVolR *= (sPrevVolL < 0) ? -1 : 1; // match phase of left!
					}
					else
					{
						// sPrevVolR was smaller... let's play with that...
						sVolL *= (sPrevVolR < 0) ? -1 : 1; // match phase of right!
						sVolR *= (sPrevVolR < 0) ? -1 : 1;
					}
				}
				// if neither of the above tests is true then the old volumes were
				// in-phase and both positive. we don't need to do anything in that case.
			}

		}
	}

	//printf("pan = %.02f, fade = %.02f. volL = %d, volR = %d.\n", fPan, fFade, sVolL, sVolR);
		
    sg_PS2_ActiveSounds[iActiveSoundIndex].volumeL = sVolL;
    sg_PS2_ActiveSounds[iActiveSoundIndex].volumeR = sVolR;
#ifdef ASURA_PS2_SUPPORT_DTS
	sg_PS2_ActiveSounds[iActiveSoundIndex].usFade = static_cast<u_short>(static_cast<float>(ASURA_PS2_SOUND_MAX_FADE) * fFade);
#endif

    // set volume if playing
    if (PlatformSoundIsPlaying(iActiveSoundIndex))
    {
#ifdef ASURA_PS2_SUPPORT_DTS
		if (Asura_PS2_Multistream::IsDTSOn())
		{
			u_short usFade = sg_PS2_ActiveSounds[iActiveSoundIndex].usFade;
			u_short usOneMinusFade = ASURA_PS2_SOUND_MAX_FADE - usFade;
			int iVolLF = (sg_PS2_ActiveSounds[iActiveSoundIndex].volumeL * usFade) >> ASURA_PS2_SOUND_FADE_SHIFT;
			int iVolRF = (sg_PS2_ActiveSounds[iActiveSoundIndex].volumeR * usFade) >> ASURA_PS2_SOUND_FADE_SHIFT;
			int iVolLB = (sg_PS2_ActiveSounds[iActiveSoundIndex].volumeL * usOneMinusFade) >> ASURA_PS2_SOUND_FADE_SHIFT;
			int iVolRB = (sg_PS2_ActiveSounds[iActiveSoundIndex].volumeR * usOneMinusFade) >> ASURA_PS2_SOUND_FADE_SHIFT;
			Asura_PS2_Multistream::SetVolume(ASURA_PS2_MS_DTS_FRONT_VOICE(iActiveSoundIndex), iVolLF, iVolRF);
			if (dbg_bTestProLogic)
			{
				int iBackVol = (iVolLB + iVolRB) / 2;
				Asura_PS2_Multistream::SetVolume(ASURA_PS2_MS_DTS_REAR_VOICE(iActiveSoundIndex), iBackVol, 0x7fff-iBackVol);
			}
			else
			{
				Asura_PS2_Multistream::SetVolume(ASURA_PS2_MS_DTS_REAR_VOICE(iActiveSoundIndex), iVolLB, iVolRB);
			}
		}
		else // if (Asura_PS2_Multistream::IsDTSOn())
#endif // #ifdef ASURA_PS2_SUPPORT_DTS
        {
			Asura_PS2_Multistream::SetVolume(iActiveSoundIndex, sVolL, sVolR);
		}
    }
    return true;
}

bool Asura_Sound_System::PlatformSetSoundPitch(Asura_Handle iActiveSoundIndex, float __fPitch)
{
    Asura_Assert(iActiveSoundIndex >= 0 && iActiveSoundIndex < ASURA_PS2_MAX_ACTIVE_SOUNDS, "invalid sound handle");
    int iLoadedSoundIndex = sg_PS2_ActiveSounds[iActiveSoundIndex].iLoadedSoundIndex;
    u_short usBasePitch = sg_PS2_LoadedSounds[iLoadedSoundIndex].usBasePitch;
    u_short pitch = (u_short)(__fPitch * (float)usBasePitch);
    // set pitch in active sound struct (for stopping/restarting)
    sg_PS2_ActiveSounds[iActiveSoundIndex].pitch = pitch;
    // set voice pitch if playing
    if (PlatformSoundIsPlaying(iActiveSoundIndex))
    {
#ifdef ASURA_PS2_SUPPORT_DTS
        Asura_PS2_Multistream::SetPitch(ASURA_PS2_MS_DTS_FRONT_VOICE(iActiveSoundIndex), pitch);
        Asura_PS2_Multistream::SetPitch(ASURA_PS2_MS_DTS_REAR_VOICE(iActiveSoundIndex), pitch);
#else
        Asura_PS2_Multistream::SetPitch(iActiveSoundIndex, pitch);
#endif
    }
    return true;
}


int Asura_Sound_System::Platform_GetMaxActiveReplaceableSounds()
{
	// (MH - 07.04.2003) on PS2 replaceable sounds happen to all be partially streamed.
	// At some point we should change this as there is no reason other than memory 
	// fragmentation that we can't dynamically load variable-sized sounds in and out.
	// However for now you can't guarantee that more than 2 such sounds can be playing
	// at once on PS2.
#ifdef ASURA_PS2_MAX_CONCURRENT_PARTIALLY_STREAMED_SOUNDS
	return ASURA_PS2_MAX_CONCURRENT_PARTIALLY_STREAMED_SOUNDS;
#else
	return 0;
#endif
}


// S T R E A M I N G   S O U N D S ///////////////////////////////////////////
DEBUGVAR bool dbg_bEnableMusic = true;

int Asura_Sound_System::Platform_GetMaxStreamingSounds()
{
    return ASURA_PS2_MAX_CONCURRENT_SOUND_STREAMS;
}

bool Asura_Sound_System::Platform_CreateStreamingSound( Asura_Handle iHandle, const char* szFilename)
{
	if (!dbg_bEnableMusic) return false;

// (MP - 08.05.2003) This was a problem with MS that's now been fixed.
//    // (MH - 19.12.2002) seem to be having trouble streaming on the artist debug stations
//    if (Asura_PS2_OS::IsArtistTestStation()) return false;

	Asura_Assert( (iHandle >= 0) && (iHandle <= ASURA_PS2_MAX_CONCURRENT_SOUND_STREAMS), 
		"invalid streaming sound index");

#ifdef ASURA_ASSERT
    // We expect the unconverted filename to be a WAV.
    {
        u_int uLen = strlen( szFilename );
        Asura_Assert( uLen >= 5 && !stricmp(&szFilename[uLen - 3], "wav"), "Expected WAV extension." );
    }
#endif

    // (MH - 06.12.2002) can just pass eSoundGroup as int
    return Asura_PS2_Multistream::OpenStreamingSoundFile( iHandle, szFilename );
}

void Asura_Sound_System::Platform_PauseStreamingSound( Asura_Handle iHandle )
{
	Asura_Assert( (iHandle >= 0) && (iHandle <= ASURA_PS2_MAX_CONCURRENT_SOUND_STREAMS), 
		"invalid streaming sound index");

    Asura_PS2_Multistream::PauseStreamingSound(iHandle);
}

void Asura_Sound_System::Platform_PlayStreamingSound( Asura_Handle iHandle, bool bLoop, bool bPreload )
{
	Asura_Assert( (iHandle >= 0) && (iHandle <= ASURA_PS2_MAX_CONCURRENT_SOUND_STREAMS), 
		"invalid streaming sound index");

    bool bMono = s_eSoundOutputType == SoundOutputMono;
    Asura_PS2_Multistream::PlayStreamingSound(iHandle, bLoop, bPreload, bMono);
}

void Asura_Sound_System::Platform_ReleaseStreamingSound( Asura_Handle iHandle )
{
	Asura_Assert( (iHandle >= 0) && (iHandle <= ASURA_PS2_MAX_CONCURRENT_SOUND_STREAMS), 
		"invalid streaming sound index");
	
    Asura_PS2_Multistream::CloseStreamingSoundFile(iHandle);

}

void Asura_Sound_System::Platform_ResumeStreamingSound( Asura_Handle iHandle )
{
	Asura_Assert( (iHandle >= 0) && (iHandle <= ASURA_PS2_MAX_CONCURRENT_SOUND_STREAMS), 
		"invalid streaming sound index");

    Asura_PS2_Multistream::ResumeStreamingSound(iHandle);
}

void Asura_Sound_System::Platform_StopStreamingSound( Asura_Handle iHandle )
{
	Asura_Assert( (iHandle >= 0) && (iHandle <= ASURA_PS2_MAX_CONCURRENT_SOUND_STREAMS), 
		"invalid streaming sound index");

    Asura_PS2_Multistream::StopStreamingSound(iHandle);
}

bool Asura_Sound_System::Platform_StreamingSoundIsReady( Asura_Handle iHandle )
{
	Asura_Assert( (iHandle >= 0) && (iHandle <= ASURA_PS2_MAX_CONCURRENT_SOUND_STREAMS), 
		"invalid streaming sound index");

    return Asura_PS2_Multistream::StreamingSoundIsPreloaded(iHandle);
}

void Asura_Sound_System::Platform_StartPreloadedStreamingSound( Asura_Handle iHandle )
{
    Asura_PS2_Multistream::StartPreloadedSound(iHandle);
}

void Asura_Sound_System::Platform_SetStreamingSoundOutput(Asura_Handle iHandle, SoundOutputType eOutputType)
{
    Asura_PS2_Multistream::ChangeMonoOrStereo(iHandle, (eOutputType == SoundOutputMono));
}

void Asura_Sound_System::Platform_EnsureAllStreamingSoundsAreStopped()
{
    Asura_PS2_Multistream::UpdateIOPBlocking();
}

void Asura_Sound_System::Platform_SetStreamingSoundVolume(Asura_Handle iHandle, float fVolume)
{
    int iVol = static_cast<int>(fVolume * static_cast<float>(0x1fff));
    Asura_PS2_Multistream::SetStreamVolume(iHandle, iVol);
    Asura_PS2_Multistream::ChangeMonoOrStereo(iHandle, (s_eSoundOutputType == SoundOutputMono));
}

bool Asura_Sound_System::Platform_StreamingSoundIsFinished(Asura_Handle iHandle)
{
	return !Asura_PS2_Multistream::IsStreamPlaying(iHandle);
}

float Asura_Sound_System::Platform_GetStreamingSoundPosition(Asura_Handle iHandle)
{
	// return position in seconds
	return Asura_PS2_Multistream::GetStreamPlayedSeconds(iHandle);
}

float Asura_Sound_System::Platform_GetStreamingSoundLength(Asura_Handle iHandle)
{
	// return position in seconds
	u_int uFrequency = Asura_PS2_Multistream::GetStreamFrequency(iHandle);
	if (!uFrequency) 
	{ 
		Asura_Assert(false, "0 frequency"); 
		return 0.0f; 
	}

	// frequency is in samples per second
	// on PS2 a sample is always 16-bits (2 bytes), but a 3.5:1 ADPCM compression is used.
	// Also, all streams are stereo. So to find the stream rate in bytes / second,
	// do ((freq * 2) / 3.5) * 2
	float fStreamRate = static_cast<float>(uFrequency) * 2.0f * 2.0f / 3.5f;
	
	float fLengthInBytes = static_cast<float>(Asura_PS2_Multistream::GetStreamFilesize(iHandle));

	float fLengthInSeconds = fLengthInBytes / fStreamRate;
	return fLengthInSeconds;
}

// Hardware panning is currently PC-only (it's the only way under DirectSound to access surround speakers)
// All other platforms allow us to specify speaker volumes directly (or if using Dolby PL2 have some concept of front and back as well as pan).
bool Asura_Sound_System::Platform_HardwarePanSetSoundVolume(Asura_Handle iSoundHandle, float fVolume)
{
	Asura_Assert(false, "this platform did not expect to use hardware 3d pan");
	return true;
}

bool Asura_Sound_System::Platform_HardwarePanSetSoundPosition(Asura_Handle iSoundHandle, const Asura_Vector_3& xPosition)
{
	Asura_Assert(false, "this platform did not expect to use hardware 3d pan");
	return true;
}

bool Asura_Sound_System::Platform_HardwarePanSetSoundSpatial(Asura_Handle iSoundHandle, bool bIsSpatial)
{
	Asura_Assert(false, "this platform did not expect to use hardware 3d pan");
	return true;
}

bool Asura_Sound_System::Platform_HardwarePanSetSoundRadii(Asura_Handle iSoundHandle, float fInner, float fOuter)
{
	Asura_Assert(false, "this platform did not expect to use hardware 3d pan");
	return true;
}

bool Asura_Sound_System::Platform_HardwarePanSetSoundForcedPanAndFade(Asura_Handle iSoundHandle, float fPan, float fFade)
{
	Asura_Assert(false, "this platform did not expect to use hardware 3d pan");
	return true;
}

bool Asura_Sound_System::Platform_SetSoundUsesReverb( Asura_Handle iPlayingSoundHandle, bool bUseReverb )
{
	Asura_Assert(!bUseReverb, "this platform did not expect to use reverb");
	return true;
}

bool Asura_Sound_System::Platform_SetSoundUsesDistanceFilter( Asura_Handle iPlayingSoundHandle, bool bUseFilter )
{
	Asura_Assert(!bUseFilter, "this platform did not expect to use distance filtering");
	return true;
}

bool Asura_Sound_System::Platform_SetSoundDistanceFilterGain (Asura_Handle iPlayingSoundHandle, float fNormalisedGain)
{
	//Platform does not use this function
	return true;
}
