//
// Asura_PSP_SAS.cpp
//
// Created by Kim Randell - 23/09/2004
// 
// Copyright (C) 2004 Rebellion, All Rights Reserved.
//

// core
#include "Core/Asura.h"

// This file's header
#include "Sound/Asura_PSP_SAS.h"

// Asura headers
#include "Core/Asura_Maths.h"
#include "Core/Memory/Asura_ResourceAllocator.h"
#include "Debugging/Asura_Logfile.h"
#include "FileAccess/Asura_FileAccess.h"
#include "Sound/Asura_Sound.h"

// PSP headers
#include "ChunkSystem/Asura_Chunk_ResourceFile_PSP_SoundData.h"
#include "Sound/Asura_PSP_SimpleAudio.h"
#include "Sound/Asura_PSP_Sound.h"

// Project headers...
#include "ProjectSettings/Asura_PSPConfiguration.h"

// PSP includes
#include <kernel.h>
#include <psperror.h>	// for SCE_OK
#include <libwave.h>
#include <libsas.h>
#include <audiooutput.h>

// Defines
#define	ASURA_PSP_VAG_HEADER_SIZE		(48)
#define ALLADSR (SCE_SAS_ATTACK_VALID | SCE_SAS_DECAY_VALID | SCE_SAS_SUSTAIN_VALID | SCE_SAS_RELEASE_VALID)

#ifndef ASURA_PSP_SAS_MIN_SIZE_FOR_PERMANENT_SOUNDS
#define ASURA_PSP_SAS_MIN_SIZE_FOR_PERMANENT_SOUNDS (2317398) // just enough for movie player.
#endif

#if 1
#define LOG_OUTPUT(s)
#else
#define LOG_OUTPUT(s)	Asura_OutputToDebugger(s)
#endif

// Static members
Asura_PSP_SAS_ActiveSound	Asura_PSP_SAS::s_axActiveSounds[uASURA_PSP_MAX_VOICES];
Asura_Mutex					Asura_PSP_SAS::s_xMutex;
u_char*						Asura_PSP_SAS::s_pucPermanentSounds;
u_int						Asura_PSP_SAS::s_uPermanentSoundsSize;

// Debug variables
DEBUGVAR bool dbg_bPrintOutput = false;
DEBUGVAR bool dbg_bAudioOn = true;

void Asura_PSP_SAS::Initialise()
{
	int iRet;

	iRet = sceSasInitWithGrain(ASURA_PSP_SA_AUDIO_OUTPUT_SAMPLE);

	if(iRet != SCE_OK)
	{
		Asura_Assertf(false, ("sceSasInit(): Failed %08X", iRet));
		return;
	}

	// Dry=ON(1), Wet=OFF(0)
	sceSasSetEffect(1, 0);

	for(u_int u = 0; u < uASURA_PSP_MAX_VOICES; ++u)
	{
		s_axActiveSounds[u].m_iLoadedSoundIndex = -1;
		s_axActiveSounds[u].m_iPitch = SCE_SAS_PITCH_BASE;
		s_axActiveSounds[u].m_uFlags = 0;
		s_axActiveSounds[u].m_iVolumeL = 0;
		s_axActiveSounds[u].m_iVolumeR = 0;

		// ADSR
		// MGH 29/6/2006: I've disabled the decay and sustain, as the decay was
		// causing looping sounds to fade away after a couple of minutes.
		iRet = sceSasSetADSR(u, ALLADSR, SCE_SAS_ENVELOPE_RATE_MAX, 0, 0, SCE_SAS_ENVELOPE_RATE_MAX);
		Asura_Assertf(iRet >= 0, ("sceSasSetADSR(): Failed %08X", iRet));
	}
}

void Asura_PSP_SAS::Shutdown()
{
	sceSasExit();
	Asura_DeallocateAlign(s_pucPermanentSounds);
	s_pucPermanentSounds = NULL;
	s_uPermanentSoundsSize = 0;
}

void Asura_PSP_SAS::Reset()
{
	// Clear out sounds?
}

bool Asura_PSP_SAS::Decode (void)
{
	s_xMutex.Lock();
	{
		const u_int	uEndFlags	= sceSasGetEndFlag();
		u_int		uVoiceMask	= 1;

		// Update each voice in-turn...
		for(u_int u = 0; u < uASURA_PSP_MAX_VOICES; u++, uVoiceMask <<= 1)
		{
			Asura_PSP_SAS_ActiveSound&	xActiveSound	= s_axActiveSounds[u];

			if (dbg_bAudioOn)
			{
				int	iRet;
				#if _DEBUG
					u_int uStartFlags = xActiveSound.m_uFlags;
				#endif

				// Has this voice just finished?
				if((uEndFlags & uVoiceMask) != 0)
				{
					if(xActiveSound.m_bPlaying)
					{
						LOG_OUTPUT(("Asura_PSP_SAS::Decode(): Voice %u finished\n", u));
						xActiveSound.m_bPlaying = false;
					}

					if(xActiveSound.m_bStopping)
					{
						xActiveSound.m_bStopping = false;
					}

					// (MH - 31.07.2006) KeyOff will assert if voice already stopped.
					xActiveSound.m_bKeyOff = false;
				}

				// Update this voice...
				UpdatePitch(u, xActiveSound);

				// Do we need to modify the voice?
				if (xActiveSound.m_bPauseOff)
				{
					xActiveSound.m_bPauseOn		= false;
					xActiveSound.m_bPauseOff	= !PauseOff(u, xActiveSound);
				}
				if(xActiveSound.m_bKeyOff)
				{
					xActiveSound.m_bStopping	= KeyOff(u, xActiveSound);
					xActiveSound.m_bKeyOff		= false;
				}
				if(xActiveSound.m_bPlaying && xActiveSound.m_bPauseOn)
				{
					xActiveSound.m_bPauseOff	= false;
					xActiveSound.m_bPauseOn		= !PauseOn(u, xActiveSound);
				}
				if(xActiveSound.m_bSetVoice)
				{
					Asura_Assertf(!xActiveSound.m_bPlaying, ("Asura_PSP_SAS::Decode(): Voice %u is already playing.", u));
					xActiveSound.m_bKeyOn		= SetVoice(u, xActiveSound);;
					xActiveSound.m_bSetVoice	= false;
				}
				if(xActiveSound.m_bKeyOn)
				{
					Asura_Assertf(!xActiveSound.m_bPlaying, ("Asura_PSP_SAS::Decode(): Voice %u is already playing.", u));
					xActiveSound.m_bPlaying	= KeyOn(u, xActiveSound);
					xActiveSound.m_bKeyOn	= false;
				}
			}
			else
			{
				xActiveSound.m_bPlaying = false;
			}
		}
	}
	s_xMutex.Unlock();

	// Do we need to generate a wave?
	return(dbg_bAudioOn);
}

// Load/unload methods

// Copied from PS2 version
struct VAGHeader
{
    unsigned int  format;		/* 04 always 'VAGp' for identifying*/
    unsigned int  ver;			/* 08 format version (2) */
    unsigned int  ssa;			/* 12 Source Start Address, always 0 (reserved for VAB format) */
    unsigned int  size;			/* 16 Sound Data Size in byte */
    unsigned int  fs;			/* 20 sampling frequency, 44100(>pt1000), 32000(>pt),22000(>pt0800)... */
    unsigned short  volL;		/* 22 base volume for Left channel */
    unsigned short  volR;		/* 24 base volume for Right channel */
    unsigned short  pitch;		/* 26 base pitch (includes fs modulation)*/
    unsigned short  ADSR1;		/* 28 base ADSR1 (see SPU manual) */
    unsigned short  ADSR2;		/* 30 base ADSR2 (see SPU manual) */
    unsigned short  reserved;	/* 32 not in use */
    char    name[16];			/* 48 */
};

bool Asura_PSP_SAS::LoadSoundFromResource(Asura_Handle iFileHandle, int iFileSize, int iLoadedSoundID)
{
	s_xMutex.Lock();
	Asura_ResourcePushType(ASURA_RESOURCE_SOUND_SAMPLE);
	{
		const int iBufferSize = iFileSize - ASURA_PSP_VAG_HEADER_SIZE;
		if(dbg_bPrintOutput) Asura_OutputToDebugger(("Loading sound with size %i\n", iBufferSize));
		Asura_Assert(iBufferSize > 0, "Invalid buffer size");

		u_char* pucData;
		Asura_ResourceAllocateMemoryBlock(pucData, iBufferSize, ASURA_ALIGN_64_BYTE);
		VAGHeader xHeader;
		Asura_FileAccess::ReadData(iFileHandle, &xHeader, ASURA_PSP_VAG_HEADER_SIZE);
		u_int uSampleRate = ((xHeader.fs & 0x000000ff) << 24)
			+ ((xHeader.fs & 0x0000ff00) << 8 )
			+ ((xHeader.fs & 0x00ff0000) >> 8 )
			+ ((xHeader.fs & 0xff000000) >> 24);
		Asura_Assert(uSampleRate > 0 && uSampleRate <= 48000, "bad sample rate");
		Asura_FileAccess::ReadData(iFileHandle, pucData, iBufferSize);

		Asura_PSP_Sound::SoundLoaded( iLoadedSoundID, pucData, true, (static_cast<float>(uSampleRate) / 44100) * 0x1000, iBufferSize );

		// KOR - flush cache
		sceKernelDcacheWritebackInvalidateAll();
	}
	Asura_ResourcePopType();
	s_xMutex.Unlock();
	return true;
}

bool Asura_PSP_SAS::UnloadSound(int iLoadedSoundID)
{
	bool	bSuccess	= false;

	s_xMutex.Lock();
	const Asura_PSP_SAS_LoadedSound* pxLoadedSound = Asura_PSP_Sound::GetResidentSound( iLoadedSoundID );
	if(pxLoadedSound)
	{
		Asura_ResourcePushType(ASURA_RESOURCE_SOUND_SAMPLE);
		{
			const u_int	uNumStopped	= StopLoadedSound(iLoadedSoundID);

			if(uNumStopped > 0)
			{
				Asura_OutputToDebugger(("Asura_PSP_SAS::UnloadSound(): Stopped %u voices for sample %d\n", uNumStopped, iLoadedSoundID));
			}

			if ( pxLoadedSound->m_bAllocatedOwnData )
			{
				// Asura_ResourceDeallocateMemoryBlock clears the pointer by reference, but we do that with SoundUnloaded()
				u_char* pucData = pxLoadedSound->m_pucData;
				Asura_ResourceDeallocateMemoryBlock(pucData);
			}

			bSuccess	= true;

			Asura_PSP_Sound::SoundUnloaded(iLoadedSoundID);
		}
		Asura_ResourcePopType();
	}
	s_xMutex.Unlock();
	return(bSuccess);
}

bool Asura_PSP_SAS::LoadSoundBlock(Asura_Handle iFileHandle, u_int uSoundDataSize, u_int uNumEntries)
{
	static bool ls_bFirstTime = true;
	bool bFirstTime = ls_bFirstTime;
	ls_bFirstTime = false;

	if (bFirstTime)
	{
		// allocate aligned buffer for VAG data
		u_int uMinSize = ASURA_PSP_SAS_MIN_SIZE_FOR_PERMANENT_SOUNDS;
		s_uPermanentSoundsSize = Asura_Maths::Max(uSoundDataSize, uMinSize);
		s_pucPermanentSounds = reinterpret_cast<u_char*>
			(Asura_AllocateAlign( s_uPermanentSoundsSize, ASURA_ALIGN_64_BYTE ));

	}
	else
	{
		Asura_Assert(s_uPermanentSoundsSize >= uSoundDataSize, "sound data size changed?");
	}

	if (!s_pucPermanentSounds)
	{
		Asura_Assert(false, "failed to alloc memory for permanent sounds");
		s_uPermanentSoundsSize = 0;
		return false;
	}

	// read the sound data in
	if (!Asura_FileAccess::ReadData( iFileHandle, s_pucPermanentSounds, uSoundDataSize ))
	{
		Asura_Assert(false, "failed to read in permanent sounds data");
		return false;
	}

	if (bFirstTime)
	{
		// read in each header and setup the sound
		u_char* pucCurData = s_pucPermanentSounds;
		for (u_int uEntry = 0; uEntry < uNumEntries; ++uEntry)
		{
			Asura_Chunk_ResourceFile_PSP_SoundData::HdrEntry xEntry;
			if ( !Asura_FileAccess::ReadData( iFileHandle, &xEntry, sizeof(xEntry) ) )
			{
				Asura_Assert(false, "failed to read in permanent sounds header entry");
				return false;
			}

			// get new loaded index from sound system
			int iLoadedIndex = Asura_Sound_System::SoundLoadedByPlatformCode( xEntry.iSoundID );

			// fill out loaded sound entry
			if (iLoadedIndex >= 0)
			{
				Asura_PSP_Sound::SoundLoaded( iLoadedIndex, pucCurData, false, 
					(static_cast<float>(xEntry.uSampleRate) / 44100) * 0x1000, xEntry.uUnalignedSize);
			}

			// skip to start of next sound
			u_int uAlignedSize = (xEntry.uUnalignedSize + 63) & ~63;
			pucCurData += uAlignedSize;
		}
	}
	else
	{
		// It's not the first time, can check all sounds are still loaded.
		// Could even allow for sound data being different here, e.g. to allow
		// changing of language after boot up, but that's probably not needed.
#ifdef ASURA_ASSERT
		// read in each header
		const u_char* pucCurData = s_pucPermanentSounds;
		for (u_int uEntry = 0; uEntry < uNumEntries; ++uEntry)
		{
			Asura_Chunk_ResourceFile_PSP_SoundData::HdrEntry xEntry;
			if ( !Asura_FileAccess::ReadData( iFileHandle, &xEntry, sizeof(xEntry) ) )
			{
				Asura_Assert(false, "failed to read in permanent sounds header entry");
				return false;
			}
			
			int iLoadedIndex = Asura_Sound_System::GetSoundLoadedIndex( xEntry.iSoundID );
			Asura_Assert( iLoadedIndex >= 0, "sound no longer loaded?");
			if (iLoadedIndex >= 0)
			{
				const Asura_PSP_SAS_LoadedSound* pxLoadedSound = Asura_PSP_Sound::GetResidentSound( iLoadedIndex );
				if (pxLoadedSound)
				{
					Asura_Assert(pucCurData == pxLoadedSound->m_pucData, "sound data changed");
					Asura_Assert(!pxLoadedSound->m_bAllocatedOwnData, "sanity check");
					Asura_Assert((static_cast<float>(xEntry.uSampleRate) / 44100) * 0x1000 == pxLoadedSound->m_fPitchModifier, "sanity check");
					Asura_Assert(xEntry.uUnalignedSize == pxLoadedSound->m_uSize, "sound data changed");
				}
			}
			// skip to start of next sound
			u_int uAlignedSize = (xEntry.uUnalignedSize + 63) & ~63;
			pucCurData += uAlignedSize;
		}
#endif
	}

	// flush cache (as in LoadSoundFromResource)
	sceKernelDcacheWritebackInvalidateAll();

	return true;
}

// Standard play methods
bool Asura_PSP_SAS::CreateSound(int iLoadedSoundID, Asura_Handle iSoundHandle)
{
	bool bCreated = false;

	s_xMutex.Lock();

	Asura_Assertf(iLoadedSoundID >= 0 && iLoadedSoundID < ASURA_MAX_NUMBER_OF_LOADED_SOUNDS, 
		("Invalid loaded sound ID %d for voice %d", iLoadedSoundID, iSoundHandle));
    if(iLoadedSoundID >= 0)
	{
		if(dbg_bPrintOutput) Asura_OutputToDebugger(("Creating voice %i from sound %i\n", iSoundHandle, iLoadedSoundID));
		Asura_Assert(iSoundHandle < 16, "Voice out of range");
		const Asura_PSP_SAS_LoadedSound* pxLoadedSound = Asura_PSP_Sound::GetResidentSound( iLoadedSoundID );
		if (pxLoadedSound)
		{
			Asura_Assert(pxLoadedSound->m_pucData && pxLoadedSound->m_uSize, "No sound loaded at that index");
			s_axActiveSounds[iSoundHandle].m_iLoadedSoundIndex = iLoadedSoundID;
			// (MH - 03.08.2006) initialise pitch as otherwise if sound starts paused it might never get set.
			int iInitialPitch = Asura_Maths::FloatToInt_Chop(pxLoadedSound->m_fPitchModifier);
			s_axActiveSounds[iSoundHandle].m_iPitch = Asura_Maths::ClampToRange(iInitialPitch, SCE_SAS_PITCH_MIN, SCE_SAS_PITCH_MAX);
			bCreated = true;
		}
	}

	s_xMutex.Unlock();
	return(bCreated);
}

bool Asura_PSP_SAS::PlaySound(Asura_Handle iSoundHandle, bool bLooping)
{
	bool	bPlaying	= false;

	s_xMutex.Lock();
	{
		Asura_PSP_SAS_ActiveSound&	xActiveSound	= s_axActiveSounds[iSoundHandle];

		LOG_OUTPUT(("Asura_PSP_SAS::PlaySound(): iSoundHandle = %d\n", iSoundHandle));
		if(xActiveSound.m_iLoadedSoundIndex >= 0)
		{
			if((xActiveSound.m_bPlaying || xActiveSound.m_bSetVoice || xActiveSound.m_bKeyOn) && 
				!xActiveSound.m_bKeyOff &&
				!xActiveSound.m_bStopping)
			{
				Asura_Assert(false, "Cannot play this voice - its already playing.");
			}
			else
			{
				const bool	bKeyOff		= xActiveSound.m_bKeyOff;
				const bool	bPauseOff	= xActiveSound.m_bPauseOff;

				if(dbg_bPrintOutput)
				{
					Asura_OutputToDebugger(("Playing voice %i\n", iSoundHandle));
				}
				if (bKeyOff)
				{
					if (dbg_bPrintOutput)
					{
						Asura_OutputToDebugger(("Voice %i waiting for key off\n", iSoundHandle));
					}
				}
				if (bPauseOff)
				{
					if (dbg_bPrintOutput)
					{
						Asura_OutputToDebugger(("Voice %i waiting for pause off\n", iSoundHandle));
					}
				}

				xActiveSound.m_uFlags		= 0;
				xActiveSound.m_bSetVoice	= true;
				xActiveSound.m_bKeyOff		= bKeyOff;
				xActiveSound.m_bPauseOff	= bPauseOff;
				xActiveSound.m_bLooping		= bLooping;
				bPlaying					= true;
			}
		}
		else
		{
			Asura_Assertf(false, ("Invalid loaded sound ID (voice %d)", iSoundHandle));
		}
	}
	s_xMutex.Unlock();
	return(bPlaying);
}

bool Asura_PSP_SAS::IsSoundPlaying(Asura_Handle iSoundHandle)
{
	bool	bPlaying;

	s_xMutex.Lock();
	{
		const Asura_PSP_SAS_ActiveSound&	xActiveSound	= s_axActiveSounds[iSoundHandle];

		bPlaying	= xActiveSound.m_bPlaying || xActiveSound.m_bSetVoice || xActiveSound.m_bKeyOn;
	}
	s_xMutex.Unlock();
	return(bPlaying);
}

bool Asura_PSP_SAS::IsSoundPaused(Asura_Handle iSoundHandle)
{
	bool	bPaused;

	s_xMutex.Lock();
	{
		const Asura_PSP_SAS_ActiveSound&	xActiveSound	= s_axActiveSounds[iSoundHandle];

		bPaused	= xActiveSound.m_bPausedByPitch || xActiveSound.m_bPausedByRequest;
	}
	s_xMutex.Unlock();
	return(bPaused);
}

bool Asura_PSP_SAS::StopSound(Asura_Handle iSoundHandle)
{
	s_xMutex.Lock();
	{
		Asura_PSP_SAS_ActiveSound&	xActiveSound	= s_axActiveSounds[iSoundHandle];

		LOG_OUTPUT(("Asura_PSP_SAS::StopSound(): iSoundHandle = %d\n", iSoundHandle));
		if(dbg_bPrintOutput) Asura_OutputToDebugger(("Stopping voice %i\n", iSoundHandle));

		if(xActiveSound.m_bSetVoice || xActiveSound.m_bKeyOn)
		{
			bool bKeyOff = xActiveSound.m_bKeyOff;
			xActiveSound.m_uFlags = 0;
			xActiveSound.m_bKeyOff = bKeyOff;
		}
		else if(xActiveSound.m_bPlaying)
		{
			xActiveSound.m_bPauseOff	= xActiveSound.m_bPausedByPitch || xActiveSound.m_bPausedByRequest;
			xActiveSound.m_bKeyOff		= true;
		}
	}
	s_xMutex.Unlock();
	return true;
}

bool Asura_PSP_SAS::DestroySound(Asura_Handle iSoundHandle)
{
	s_xMutex.Lock();
	{
		Asura_PSP_SAS_ActiveSound&	xActiveSound	= s_axActiveSounds[iSoundHandle];

		LOG_OUTPUT(("Asura_PSP_SAS::DestroySound(): iSoundHandle = %d\n", iSoundHandle));
		if(dbg_bPrintOutput) Asura_OutputToDebugger(("Destroying voice %i\n", iSoundHandle));
		// KOR - it seems we do not explicitly free a voice -
		// just stop it, then overwrite the voice settings with whatever comes along next
		if(xActiveSound.m_bPlaying || xActiveSound.m_bSetVoice || xActiveSound.m_bKeyOn)
		{
			StopSound(iSoundHandle);
		}

        // (MH - 27.07.2006) to prevent UnloadSound from thinking this sound still
		// needs keying off reset loaded sound index.
		xActiveSound.m_iLoadedSoundIndex = -1;
	}
	s_xMutex.Unlock();
	return true;
}

bool Asura_PSP_SAS::PauseSound(Asura_Handle iSoundHandle)
{
	s_xMutex.Lock();
	{
		Asura_PSP_SAS_ActiveSound&	xActiveSound	= s_axActiveSounds[iSoundHandle];

		if (!xActiveSound.m_bPausedByRequest)
		{
			xActiveSound.m_bPausedByRequest	= true;
			PauseIfNecessary(iSoundHandle);
		}
	}
	s_xMutex.Unlock();
	return true;
}

bool Asura_PSP_SAS::UnpauseSound(Asura_Handle iSoundHandle)
{
	s_xMutex.Lock();
	{
		Asura_PSP_SAS_ActiveSound&	xActiveSound	= s_axActiveSounds[iSoundHandle];

		if (xActiveSound.m_bPausedByRequest)
		{
			xActiveSound.m_bPausedByRequest	= false;
			UnpauseIfNecessary(iSoundHandle);
		}
	}
	s_xMutex.Unlock();
	return true;
}

// Property setting
bool Asura_PSP_SAS::SetSoundVolumePanAndFade(Asura_Handle iSoundHandle, float fVolume, float fPan, float fFade)
{
	s_xMutex.Lock();
	{
		// Only 2D sound on the PSP, so ignore fade
		s_axActiveSounds[iSoundHandle].m_iVolumeL = SCE_SAS_VOLUME_MAX * fVolume * Asura_Maths::Sqrt(1.f - fPan);
		s_axActiveSounds[iSoundHandle].m_iVolumeR = SCE_SAS_VOLUME_MAX * fVolume * Asura_Maths::Sqrt(fPan);
	}
	s_xMutex.Unlock();
	return true;
}

bool Asura_PSP_SAS::SetSoundPitch(Asura_Handle iSoundHandle, float fPitch)
{
	s_xMutex.Lock();
	const Asura_PSP_SAS_LoadedSound* pxLoadedSound = Asura_PSP_Sound::GetResidentSound( s_axActiveSounds[iSoundHandle].m_iLoadedSoundIndex );
	if (pxLoadedSound)
	{
		const float fPitchModifier = pxLoadedSound->m_fPitchModifier;
		if (fPitch == 0.0f)
		{
			if (!s_axActiveSounds[iSoundHandle].m_bPausedByPitch)
			{
				s_axActiveSounds[iSoundHandle].m_bPausedByPitch = true;
				PauseIfNecessary(iSoundHandle);
			}
		}
		else
		{
			s_axActiveSounds[iSoundHandle].m_iPitch = Asura_Maths::ClampToRange(Asura_Maths::FloatToInt_Chop(fPitch * fPitchModifier), SCE_SAS_PITCH_MIN, SCE_SAS_PITCH_MAX);
			if (s_axActiveSounds[iSoundHandle].m_bPausedByPitch)
			{
				s_axActiveSounds[iSoundHandle].m_bPausedByPitch = false;
				UnpauseIfNecessary(iSoundHandle);
			}
		}
	}
	s_xMutex.Unlock();
	return true;
}

// interface for borrowing sound memory (for FMV playback)
void Asura_PSP_SAS::GetMemoryFromPermanentSounds(char** ppcMemoryBlock)
{
	// need to pause all sounds here? or is blocking the semaphore enough?
	s_xMutex.Lock();

	*ppcMemoryBlock = reinterpret_cast<char*>(s_pucPermanentSounds);
}

void Asura_PSP_SAS::RestorePermanentSounds()
{
	// reload the sounds
	Asura_Sound_System::LoadCoreProjectSounds();

	// release the semaphore
	s_xMutex.Unlock();
}

void Asura_PSP_SAS::PauseIfNecessary(Asura_Handle iSoundHandle)
{
	// Assumed we within the semaphore of the calling function.
	if (s_axActiveSounds[iSoundHandle].m_bPausedByRequest ^ s_axActiveSounds[iSoundHandle].m_bPausedByPitch)
	{
		s_axActiveSounds[iSoundHandle].m_bPauseOn	= true;
		s_axActiveSounds[iSoundHandle].m_bPauseOff	= false;
	}
}

void Asura_PSP_SAS::UnpauseIfNecessary(Asura_Handle iSoundHandle)
{
	// Assumed we within the semaphore of the calling function.
	if (!s_axActiveSounds[iSoundHandle].m_bPausedByRequest && !s_axActiveSounds[iSoundHandle].m_bPausedByPitch)
	{
		s_axActiveSounds[iSoundHandle].m_bPauseOn	= false;
		s_axActiveSounds[iSoundHandle].m_bPauseOff	= true;
	}
}

bool Asura_PSP_SAS::UpdatePitch (const u_int uVoice, Asura_PSP_SAS_ActiveSound& xActiveSound)
{
	const int	iPitch	= xActiveSound.m_iPitch;
	const int	iRet	= sceSasSetPitch(uVoice, iPitch);

	if (iRet >= 0)
	{
		return(true);
	}
	else
	{
		Asura_Assertf(false, ("sceSasSetPitch(): Failed %08X, iPitch = %d, voice = %u", iRet, iPitch, uVoice));
		return(false);
	}
}

bool Asura_PSP_SAS::SetVoice (const u_int uVoice, Asura_PSP_SAS_ActiveSound& xActiveSound)
{
	// Is the sound ID valid?
	if(xActiveSound.m_iLoadedSoundIndex >= 0)
	{
		const Asura_PSP_SAS_LoadedSound* pxLoadedSound = Asura_PSP_Sound::GetResidentSound( xActiveSound.m_iLoadedSoundIndex );
		if (!pxLoadedSound)
		{
			return false;
		}

		const int iLoopFlag	 = (xActiveSound.m_bLooping) ? SCE_SAS_LOOP_ENABLE : SCE_SAS_LOOP_DISABLE;
		const int iRet = sceSasSetVoice(uVoice, pxLoadedSound->m_pucData, pxLoadedSound->m_uSize, iLoopFlag);

		LOG_OUTPUT(("Asura_PSP_SAS::SetVoice(): uVoice = %u\n", uVoice));
		if(iRet >= 0)
		{
			return(true);
		}
		else
		{
			Asura_Assertf(false, ("sceSasSetVoice(): Failed %08X, voice = %u", iRet, uVoice));
			return(false);
		}
	}
	else
	{
		return(false);
	}
}

bool Asura_PSP_SAS::KeyOn (const u_int uVoice, Asura_PSP_SAS_ActiveSound& xActiveSound)
{
	int	iRet	= sceSasSetKeyOn(uVoice);

	LOG_OUTPUT(("Asura_PSP_SAS::KeyOn(): uVoice = %u\n", uVoice));
	if(iRet >= 0)
	{
		return(true);
	}
	else
	{
		Asura_Assertf(false, ("sceSasSetKeyOn(): Failed %08X, voice = %u. Attempting KeyOff.", iRet, uVoice));
		iRet = sceSasSetKeyOff(uVoice);
		if (iRet < 0)
		{
			Asura_Assertf(false, ("sceSasSetKeyOff() for sceSasSetKeyOn retry: Failed %08X, voice = %u. Attempting PauseOff", iRet, uVoice));
			// attempt unpause
			iRet = sceSasSetPause(uVoice, 0);
			Asura_Assertf(iRet >= 0, ("sceSasSetPause(0) for sceSasSetKeyOn retry: Failed %08X, voice = %u.", iRet, uVoice));
			iRet = sceSasSetKeyOff(uVoice);
		}
		if (iRet >= 0)
		{
			iRet = sceSasSetKeyOn(uVoice);
			if(iRet >= 0)
			{
				// hurray!
				return(true);
			}
			else
			{
				Asura_Assertf(false, ("sceSasSetKeyOn() retry failed %08X, voice = %u.", iRet, uVoice));
			}
		}
		else
		{
			Asura_Assertf(false, ("sceSasSetKeyOff() for sceSasSetKeyOn retry: Failed %08X, voice = %u.", iRet, uVoice));
		}
		return(false);
	}
}

bool Asura_PSP_SAS::KeyOff (const u_int uVoice, Asura_PSP_SAS_ActiveSound& xActiveSound)
{
	int	iRet	= sceSasSetKeyOff(uVoice);

	LOG_OUTPUT(("Asura_PSP_SAS::KeyOff(): uVoice = %u\n", uVoice));
	if (iRet >= 0)
	{
		return(true);
	}
	else
	{
		if (iRet < 0)
		{
			Asura_Assertf(false, ("sceSasSetKeyOff(): Failed %08X, voice = %u. Attempting pause off.", iRet, uVoice));
			// attempt unpause
			iRet = sceSasSetPause(uVoice, 0);
			Asura_Assertf(iRet >= 0, ("sceSasSetPause(0) for sceSasSetKeyOff retry: Failed %08X, voice = %u.", iRet, uVoice));
			iRet = sceSasSetKeyOff(uVoice);
			if (iRet >= 0)
			{
				// hurray!
				return(true);
			}
		}
		Asura_Assertf(false, ("sceSasSetKeyOff(): Failed %08X, voice = %u", iRet, uVoice));
		return(false);
	}
}

bool Asura_PSP_SAS::PauseOn (const u_int uVoice, Asura_PSP_SAS_ActiveSound& xActiveSound)
{
	const int	iRet	= sceSasSetPause(uVoice, 1);

	LOG_OUTPUT(("Asura_PSP_SAS::PauseOn(): uVoice = %u\n", uVoice));
	if (iRet >= 0)
	{
		return(true);
	}
	else
	{
		Asura_Assertf(false, ("sceSasSetPause(): Failed %08X, voice = %u", iRet, uVoice));
		return(false);
	}
}

bool Asura_PSP_SAS::PauseOff (const u_int uVoice, Asura_PSP_SAS_ActiveSound& xActiveSound)
{
	const int	iRet	= sceSasSetPause(uVoice, 0);

	LOG_OUTPUT(("Asura_PSP_SAS::PauseOff(): uVoice = %u\n", uVoice));
	if (iRet >= 0)
	{
		return(true);
	}
	else
	{
		Asura_Assertf(false, ("sceSasSetPause(): Failed %08X, voice = %u", iRet, uVoice));
		return(false);
	}
}

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_PSP_SAS::StopLoadedSound
// Author       : Andy Tate
// Date         : 24-Jul-2006
////////////////////////////////////////////////////////////////////////////////

u_int Asura_PSP_SAS::StopLoadedSound (const int iLoadedSoundID)
{
	u_int	uNumStopped	= 0;

	// Check each active sound in-turn...
	for(u_int u = 0; u < uASURA_PSP_MAX_VOICES; u++)
	{
		Asura_PSP_SAS_ActiveSound&	xActiveSound	= s_axActiveSounds[u];

		// Is this sound playing?
		if(xActiveSound.m_bSetVoice || xActiveSound.m_bKeyOn || xActiveSound.m_bPlaying)
		{
			// Yes - but is it the sound we're looking for?
			if(xActiveSound.m_iLoadedSoundIndex == iLoadedSoundID)
			{
				if(StopSound(u))
				{
					uNumStopped++;
				}
				else
				{
					Asura_Assertf(false, ("Asura_PSP_SAS::StopLoadedSound(): Failed to stop voice %u", u));
				}
			}
		}
	}

	// Return how many sounds we actually stopped...
	return(uNumStopped);
}

/// End of function 'Asura_PSP_SAS::StopLoadedSound'. ///


// E N D   O F   F I L E /////////////////////////////////////////////////////
