// Asura_PS3_Multistream_Audio.cpp
// Thomas Young - 16.09.08
// Copyright (C) 2008 Rebellion, All Rights Reserved.

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

#ifdef ASURA_USE_AUDIO_SYSTEM

// Asura headers
#include "Core/Asura_Hash.h"
#include "Core/Asura_Memheap.h"
#include "Core/Resources/Asura_ResourceSet.h"

// PS3 Headers
#include "Sound/Asura_PS3_Multistream_Audio.h"	
#include "Sound/Asura_PS3_Multistream_File_Loading.h"
#include "Sound/Asura_PS3_Multistream_Voice.h"

// SDK headers
#include <errno.h>
#include <sys/timer.h>
#include <sys/ppu_thread.h>
#include <sysutil/sysutil_sysparam.h>
#include <cell/audio.h>
#include <cell/sysmodule.h>
#include <cell/spurs/task.h>
#include <cell/spurs/control.h>
#include <spu_printf.h>

// E X T E R N A L S /////////////////////////////////////////////////////////

extern u_int _binary_mstream_dsp_aux_send_pic_start;
extern u_int _binary_mstream_dsp_aux_return_pic_start;
extern u_int _binary_mstream_dsp_filter_pic_start;
extern u_int _binary_mstream_dsp_i3dl2_pic_start;
extern u_int _binary_mstream_dsp_i3dl2_pic_size;
extern u_int _binary_DSP_AddNoise_SPU_pic_start;
extern u_int _binary_DSP_AddNoise_SPU_pic_size;
extern u_int _binary_DSP_SimpleFilter_SPU_pic_start;
extern u_int _binary_DSP_SimpleFilter_SPU_pic_size;

// C O N S T A N T S /////////////////////////////////////////////////////////

static const int				iASURA_MS_MAX_CHANNELS				= 128 + 24;	//24 stereo voices
static const int				iASURA_MS_PPU_THREAD_PRIORITY		= 0;
static const size_t				uASURA_MS_PPU_THREAD_STACK_SIZE		= 16 * 1024;
static const usecond_t			uASURA_MS_PPU_THREAD_SLEEP			= 520;
static const int				iASURA_MS_SPURS_SPU_COUNT			= 1;
static const int				iASURA_MS_SPURS_THREAD_PRIORITY		= 250;
static const float				fASURA_MS_MASTER_VOLUME				= 0.5f;
static const float				fASURA_MS_REVERB_VOLUME				= 0.05f;
static const int				iASURA_MS_MAX_VOICE_HANDLE			= 16 * 1024;

static const int				iASURA_MS_MAX_DSPPAGE				= 3;
static const int				iASURA_MS_DSP_MEMORY_SIZE			= 1024 * 1024 * 3;	//3Mb (Reverb is huge for some reason and takes about 1Meg alone)

static const int				iASURA_MS_FIRST_FREE_SUBBUS			= 8;
static const int				iASURA_MS_LAST_FREE_SUBBUS			= 24;

static const CELL_MS_DSPSLOTS	iMS_VOICE_DSP_SLOT_AUXRETURN		= CELL_MS_DSP_SLOT_0;
static const CELL_MS_DSPSLOTS	iMS_VOICE_DSP_SLOT_REVERB			= CELL_MS_DSP_SLOT_1;

#ifdef ASURA_PS3_SUPPORT_SULPHA

static const int 				iASURA_MS_SULPHA_NAMED_OBJECTS		= 32;
static const int 				iASURA_MS_SULPHA_MEMORY_SIZE		= 1024 * 1024 * 2;

#endif

// G L O B A L   V A R I A B L E S ///////////////////////////////////////////

static CellSpurs g_xSPURSInfo  __attribute__((aligned (128)));

// S T A T I C   M E M B E R   V A R I A B L E S /////////////////////////////

void*						Asura_MultiStream_Audio::s_pSulphaMemory				= 0;
void*						Asura_MultiStream_Audio::s_pMSSystemMemory				= 0;
void*						Asura_MultiStream_Audio::s_pMP3Memory					= 0;

u_int						Asura_MultiStream_Audio::s_uMSPortNum					= 0;
sys_ppu_thread_t			Asura_MultiStream_Audio::s_xMultiStreamPuThread;
volatile bool				Asura_MultiStream_Audio::s_bMSThreadExitFlag			= false;

char*						Asura_MultiStream_Audio::s_pVoiceMemory					= 0;
Asura_MemHeap*				Asura_MultiStream_Audio::s_pxVoiceHeap					= 0;
Asura_MultiStream_Voice*	Asura_MultiStream_Audio::s_pxVoices						[ iASURA_MS_MAX_VOICES ];
Asura_Handle				Asura_MultiStream_Audio::s_iNextVoiceHandle				= 0;

int							Asura_MultiStream_Audio::s_iDSPHandle					[ ASURA_MS_DSP_EFFECT_NUM_HANDLES ];
Asura_MemHeap*				Asura_MultiStream_Audio::s_pxDSPHeap					= 0;
void*						Asura_MultiStream_Audio::s_pDSPMemory					= 0;

void*						Asura_MultiStream_Audio::s_pReverbBus_ReverbDSP			= 0;
void*						Asura_MultiStream_Audio::s_pReverbBus_AuxReturnDSP		= 0;
void*						Asura_MultiStream_Audio::s_pReverbBus_ReverbNoDSP		= 0;
void*						Asura_MultiStream_Audio::s_pReverbBus_AuxReturnNoDSP	= 0;

int							Asura_MultiStream_Audio::s_iActiveMasterDSPBus			= iAUSRA_MS_MASTER_DSP1_BUS_NUMBER;
float						Asura_MultiStream_Audio::s_fMasterDSPBusTransition		= 0.0f;

Asura_ResourceSet <Asura_MultiStream_Buffer> Asura_MultiStream_Audio::s_xBuffers ( "Multistream buffers" );

// F U N C T I O N   D E F I N I T I O N S ///////////////////////////////////

void Asura_MultiStream_Audio::Initialise ()
{
	if( !InitialiseSPURS() )
	{
		Asura_Assert(false, "FATAL! Failed to initialise SPURS" );
		return;
	}

	if( !InitialiseAudio() )
	{
		Asura_Assert(false, "FATAL! Failed to initialise libAudio" );
		return;
	}

	if( !InitialiseMultiStream() )
	{
		Asura_Assert( false, "FATAL! Failed to initialise Multistream" );
		return;
	}

	Asura_Multistream_Streaming::InitialiseStreaming();

	if( !InitialiseDSPEffects() )
	{
		Asura_Assert( false, "FATAL! Failed to intialise DSP effects" );
		return;
	}

	if( !InitialiseSubbusses() )
	{
		Asura_Assert( false, "FATAL! Failed to intialise bus routings" );
		return;
	}

	if( !InitialiseSulpha() )
	{
		Asura_Assert( false, "Failed to initialise Sulpha" );
		return;
	}

	//Set our 7.1 speakers as active
	Asura_Audio_System::SetSpeakerActive( Asura_Audio_System::ASURA_SPEAKER_FRONT_LEFT	);
	Asura_Audio_System::SetSpeakerActive( Asura_Audio_System::ASURA_SPEAKER_FRONT_RIGHT	);
	Asura_Audio_System::SetSpeakerActive( Asura_Audio_System::ASURA_SPEAKER_FRONT_CENTRE);
	Asura_Audio_System::SetSpeakerActive( Asura_Audio_System::ASURA_SPEAKER_LOW_FREQ	);
	Asura_Audio_System::SetSpeakerActive( Asura_Audio_System::ASURA_SPEAKER_REAR_LEFT	);
	Asura_Audio_System::SetSpeakerActive( Asura_Audio_System::ASURA_SPEAKER_REAR_RIGHT	);
	Asura_Audio_System::SetSpeakerActive( Asura_Audio_System::ASURA_SPEAKER_SIDE_LEFT	);
	Asura_Audio_System::SetSpeakerActive( Asura_Audio_System::ASURA_SPEAKER_SIDE_RIGHT	);

	// Allocate our voices heap (the size is approximate since we may allocate different data structures using the heap)
	const int iHeapSize = iASURA_MS_MAX_VOICES * sizeof( Asura_MultiStream_Voice ) + 1024 * 2;

	s_pVoiceMemory = static_cast<char*>( Asura_Allocate( iHeapSize ) );
	s_pxVoiceHeap = new Asura_MemHeap( s_pVoiceMemory, iHeapSize );
	
	// Enable thread safety as we may create voices from a loading thread (buffer) or on the main thread (streaming)
	s_pxVoiceHeap->EnableThreadSafety();

	Asura_Mem::Set( s_pxVoices, 0, sizeof( Asura_MultiStream_Voice* ) * iASURA_MS_MAX_VOICES );
}


bool Asura_MultiStream_Audio::InitialiseAudio()
{
	//This code expects SDK > 220
	ASURA_STATIC_ASSERT( CELL_SDK_VERSION > 0x220000);

	int iResult;

	iResult = cellSysmoduleLoadModule( CELL_SYSMODULE_AUDIO );
	Asura_Assert( iResult == CELL_OK, "PRX for libaudio already loaded?" );
	
	// Init the audio core
	iResult = cellAudioInit();
	if ( iResult != 0 )
	{
		Asura_Assertf( iResult == CELL_OK, ("cellAudioInit() error : %d\n", iResult) );
		return false;
	}

	// Open an audio port
	CellAudioPortParam   xAudioParam;

	xAudioParam.nChannel	= CELL_AUDIO_PORT_8CH;
	xAudioParam.nBlock		= CELL_AUDIO_BLOCK_16;
	xAudioParam.attr		= CELL_AUDIO_PORTATTR_INITLEVEL;
	xAudioParam.level		= 1.0f;
	
	iResult = cellAudioPortOpen(&xAudioParam, &s_uMSPortNum);
	if ( iResult != CELL_OK )
	{
		Asura_Assertf( false, ("cellAudioPortOpen() error : %d\n", iResult) );
		return false;
	}

	// Manual audio channel query
	CellAudioOutState xCurrentState;
	cellAudioOutGetState(CELL_AUDIO_OUT_PRIMARY, 0, &xCurrentState);  // There's only 1 primary output device.

	// This resets the chosen audio output channels and downmixing mechanism to the default

	cellMSSystemConfigureSysUtilEx(CELL_MS_AUDIOMODESELECT_SUPPORTSLPCM | CELL_MS_AUDIOMODESELECT_SUPPORTSDOLBY);
	
	// Configure the output port
	CellAudioPortConfig  xPortConfig;
	iResult = cellAudioGetPortConfig(s_uMSPortNum, &xPortConfig);
	Asura_Assertf( iResult == CELL_OK, ("cellAudioGetPortConfig() error : %d\n", iResult) );

	// Allow output from MultiStream to libAudio - MultiStream REQUIRES 'libmstreamSPURSAT3.a' and 'libspurs_stub.a'
	iResult = cellMSSystemConfigureLibAudio(&xAudioParam, &xPortConfig);  
	Asura_Assertf( iResult == CELL_OK, ("cellMSSystemConfigureLibAudio() error : %d\n", cellMSSystemGetLastError() ) );

	return true;	
}


bool Asura_MultiStream_Audio::InitialiseSPURS()
{
	sys_ppu_thread_t  thread_id;
	int               ppu_thr_prio __attribute__( ( aligned( 128 ) ) );

	sys_ppu_thread_get_id(&thread_id);
	int iResult = sys_ppu_thread_get_priority( thread_id, &ppu_thr_prio );
	if ( iResult != CELL_OK )
	{
		Asura_OutputToDebugger( ( "sys_ppu_thread_get_priority() error %d\n", iResult ) );
		return false;
	}

	iResult = cellSpursInitialize( &g_xSPURSInfo, iASURA_MS_SPURS_SPU_COUNT, iASURA_MS_SPURS_THREAD_PRIORITY, ppu_thr_prio - 1, 1 );
	if ( iResult != 0 )
	{
		Asura_OutputToDebugger( ( "cellSpursInitialize() error %d\n", iResult ) );
		return false;
	}

	return true;
}


bool Asura_MultiStream_Audio::InitialiseMultiStream	()
{
	int iResult = 0;

	// MS configuration
	CellMSSystemConfig xMSConfig = {	iASURA_MS_MAX_VOICES,	// https://ps3.scedev.net/forums/nodejump/216885 ...
										iASURA_MS_MAX_SUBBUS,	// when using routable streams each voice takes up a stream.
										iASURA_MS_MAX_DSPPAGE,
										(	CELL_MS_ROUTABLE_STREAMS_FLAG
										|	CELL_MS_DISABLE_SPU_PRINTF_SERVER
										|	CELL_MS_TD_ONLY_512
										|	CELL_MS_SEPERATE_VOLUMES_PER_ROUTE	)	}; 

	// Determine system memory requirements and allocate
	const int iMemSize = cellMSSystemGetNeededMemorySize( &xMSConfig );
	s_pMSSystemMemory = Asura_AllocateAlign(iMemSize, ASURA_ALIGN_128_BYTE);

	// Initialise a MultiStream SPURS thread
	const uint8_t auPriorities[8] = {1, 0, 0, 0, 0, 0, 0, 0};

	iResult = cellMSSystemInitSPURS( s_pMSSystemMemory, &xMSConfig, &g_xSPURSInfo, auPriorities );
	if ( iResult != 0 )
	{
		cellAudioQuit();
		Asura_Assertf( false, ( "cellMSSystemInitSPURS() error : %d\n", cellMSSystemGetLastError() ) );
		return false;
	}

	#ifndef ASURA_NO_DEBUG_OUTPUT
	// attach MS SPURS thread to our printf server
	sys_spu_thread_t xThreadID;
	u_int uNumThreads = 1;
	cellSpursGetSpuThreadId( &g_xSPURSInfo, &xThreadID, &uNumThreads );
	iResult = spu_printf_attach_thread( xThreadID );
	Asura_Assertf(  iResult == CELL_OK, ("spu_printf_attach_thread() failed for multistream thread with code 0x%08X", iResult) );
	#endif

	
	// Initialise MP3 playback
	const u_int uMaxChannels = iASURA_MS_MAX_CHANNELS;	//TODO: Add the max number of channels for streaming sounds to this
	const int iSize = cellMSMP3GetNeededMemorySize( uMaxChannels );

	s_pMP3Memory = Asura_AllocateAlign( iSize, ASURA_ALIGN_16_BYTE );
	if ( s_pMP3Memory == NULL )
	{
		Asura_Assert(false, "Unable to allocate MP3 playback memory" );
		return false;
	}

	int iErr = cellMSMP3Init( uMaxChannels, s_pMP3Memory );
	if ( iErr < 0 )
	{
		Asura_Assert(false, "Unable to initialise MP3 system");
		return false;
	}

	// Start a thread to update the MultiStream/libaudio
	iResult = sys_ppu_thread_create(	&s_xMultiStreamPuThread
									,	&MultiStreamUpdateThread
									,	0
									,	iASURA_MS_PPU_THREAD_PRIORITY
									,	uASURA_MS_PPU_THREAD_STACK_SIZE
									,	SYS_PPU_THREAD_CREATE_JOINABLE
									,	"MultiStream PU Thread");

	if ( iResult != 0 )
	{
		cellAudioQuit();
		Asura_Assertf( false, ("[Asura_PS3_MultiStream::Initialise] - sys_ppu_thread_create() error : %d\n", iResult) );
		return false;
	}

	return true;
}


bool Asura_MultiStream_Audio::InitialiseSulpha		()
{
	#ifdef ASURA_PS3_SUPPORT_SULPHA
	s_pSulphaMemory = Asura_AllocateAlign(iASURA_MS_SULPHA_MEMORY_SIZE, ASURA_ALIGN_16_BYTE);
	if(s_pSulphaMemory)
	{
		cellMSSulphaInit(s_pSulphaMemory,iASURA_MS_SULPHA_MEMORY_SIZE,iASURA_MS_SULPHA_NAMED_OBJECTS);
	}
	cellMSSulphaDECI3Start();

	cellMSSulphaNameObject( CELL_MS_SULPHA_NAMED_TYPE_SUB, iASURA_MS_REVERB_DSP_BUS_NUMBER		, "Reverb Bus (DSP)"	);
	cellMSSulphaNameObject( CELL_MS_SULPHA_NAMED_TYPE_SUB, iASURA_MS_REVERB_NODSP_BUS_NUMBER	, "Reverb Bus (No DSP)" );
	cellMSSulphaNameObject( CELL_MS_SULPHA_NAMED_TYPE_SUB, iAUSRA_MS_DEAD_BUS_NUMBER			, "Dead Bus"			);
	cellMSSulphaNameObject( CELL_MS_SULPHA_NAMED_TYPE_SUB, iAUSRA_MS_MASTER_DSP1_BUS_NUMBER		, "Master DSP Bus 1"	);
	cellMSSulphaNameObject( CELL_MS_SULPHA_NAMED_TYPE_SUB, iAUSRA_MS_MASTER_DSP2_BUS_NUMBER		, "Master DSP Bus 2"	);

	cellMSSulphaNameObject( CELL_MS_SULPHA_NAMED_TYPE_DSP, s_iDSPHandle[ ASURA_MS_DSP_EFFECT_HANDLE_REVERB		] , "Reverb"			);
	cellMSSulphaNameObject( CELL_MS_SULPHA_NAMED_TYPE_DSP, s_iDSPHandle[ ASURA_MS_DSP_EFFECT_HANDLE_LPF			] , "Lowpass Filter"	);
	cellMSSulphaNameObject( CELL_MS_SULPHA_NAMED_TYPE_DSP, s_iDSPHandle[ ASURA_MS_DSP_EFFECT_HANDLE_AUX_SEND	] , "Aux Send"			);
	cellMSSulphaNameObject( CELL_MS_SULPHA_NAMED_TYPE_DSP, s_iDSPHandle[ ASURA_MS_DSP_EFFECT_HANDLE_AUX_RETURN	] , "Aux Return"		);

	#endif
	return true;
}

bool Asura_MultiStream_Audio::InitialiseDSPEffects()
{
	#ifdef ASURA_ASSERT
	int iRet = 0;	
	#endif

	// Create our DSP heap
	s_pDSPMemory = Asura_AllocateAlign( iASURA_MS_DSP_MEMORY_SIZE, ASURA_ALIGN_16_BYTE );
	if( !s_pDSPMemory )
	{
		Asura_Assert( false, "Failed to allocate space for DSP effects" );
		return false;
	}
	s_pxDSPHeap	= new Asura_MemHeap( s_pDSPMemory, iASURA_MS_DSP_MEMORY_SIZE );

	if( !s_pxDSPHeap )
	{
		Asura_Assert( false, "Failed to create DSP memheap" );
		return false;
	}

	s_pxDSPHeap->EnableThreadSafety();	// see previous comment regarding the voice heap
	
	// Create Lowpass filter
	s_iDSPHandle[ ASURA_MS_DSP_EFFECT_HANDLE_LPF ] = LoadDSPFromMemory( reinterpret_cast<void*>( &_binary_mstream_dsp_filter_pic_start ) );
	Asura_Assert( s_iDSPHandle[ ASURA_MS_DSP_EFFECT_HANDLE_LPF ] >= 0, "LPF DSP failed to load");

	//Aux Send
	s_iDSPHandle[ ASURA_MS_DSP_EFFECT_HANDLE_AUX_SEND ] = LoadDSPFromMemory( reinterpret_cast<void*>( &_binary_mstream_dsp_aux_send_pic_start ) );
	Asura_Assert( s_iDSPHandle[ ASURA_MS_DSP_EFFECT_HANDLE_AUX_SEND ] >= 0, "Aux send DSP failed to load\n");

	//Aux return
	s_iDSPHandle[ ASURA_MS_DSP_EFFECT_HANDLE_AUX_RETURN ] = LoadDSPFromMemory( reinterpret_cast<void*>(&_binary_mstream_dsp_aux_return_pic_start ) );
	Asura_Assert( s_iDSPHandle[ ASURA_MS_DSP_EFFECT_HANDLE_AUX_RETURN ] >= 0, "Aux return DSP failed to load\n");

	// Add Noise
	s_iDSPHandle[ ASURA_MS_DSP_EFFECT_HANDLE_ADD_NOISE ] = LoadDSPFromMemory( reinterpret_cast<void*>(&_binary_DSP_AddNoise_SPU_pic_start ) );
	Asura_Assert( s_iDSPHandle[ ASURA_MS_DSP_EFFECT_HANDLE_ADD_NOISE ] >= 0, "Add noise DSP failed to load\n");
	
	// Simple Filter
	s_iDSPHandle[ ASURA_MS_DSP_EFFECT_HANDLE_SIMPLE_FILTER ] = LoadDSPFromMemory( reinterpret_cast<void*>(&_binary_DSP_SimpleFilter_SPU_pic_start ) );
	Asura_Assert( s_iDSPHandle[ ASURA_MS_DSP_EFFECT_HANDLE_SIMPLE_FILTER ] >= 0, "Simple filter DSP failed to load\n");

	// Create Reverb, we have to do this for both reverb busses
	s_iDSPHandle[ ASURA_MS_DSP_EFFECT_HANDLE_REVERB ] = LoadDSPFromMemory( reinterpret_cast<void*>( &_binary_mstream_dsp_i3dl2_pic_start ) );
	Asura_Assert( s_iDSPHandle[ ASURA_MS_DSP_EFFECT_HANDLE_AUX_RETURN ] >= 0, "I3DL2 Reverb DSP failed to load\n");

	CellMSFXI3DL2Params xTempReverb;
	Asura_Audio_Reverb xAsuraReverb;
	float fReverbAmount = 0.0f;
	Asura_Audio_System::GetCurrentReverb( xAsuraReverb, fReverbAmount );
	PopulateCellReverbParams( &xTempReverb, &xAsuraReverb );

	// The logic for setting up the reverb is the same so just loop over and do it twice
	
	for( int iLoop = 0; iLoop < 2; ++iLoop )
	{
		int		iSubbus				= 0;
		void*	pReverbBuffer		= 0;
		void*	pAuxReturnBuffer	= 0;

		if( iLoop )
		{
			// Do the dsp rerverb
			iSubbus = iASURA_MS_REVERB_DSP_BUS;
		}
		else
		{
			// Do the non-dsp reverb
			iSubbus = iASURA_MS_REVERB_NODSP_BUS;
		}

		// Set DSP effect to the slot after the aux return
		cellMSCoreSetDSP( iSubbus, iMS_VOICE_DSP_SLOT_REVERB, s_iDSPHandle[ ASURA_MS_DSP_EFFECT_HANDLE_REVERB ] );

		// TODO: Why does this not work if I pass 1 instead of 8 ?!
		const int iMemSize = cellMSFXI3DL2GetNeededMemorySize( 8 );
		pReverbBuffer = AllocateFromDSPHeap( iMemSize, 16 );

		// Initialise buffer to zero
		Asura_Mem::Set( pReverbBuffer, 0, iMemSize );

		int iResult = cellMSFXI3DL2Init( cellMSCoreGetDSPParamAddr( iSubbus, iMS_VOICE_DSP_SLOT_REVERB ) ,pReverbBuffer, &xTempReverb, 8 );
		if ( iResult < 0)
		{
			Asura_Assert( false, "Failed to set I3DL2 reverb" );
			return false;
		}

		// Set reverb DSP on input/output stage of reverb DSP
		for (int iIndex = 0; iIndex < 8; iIndex++ )
		{
			cellMSCoreSetMask( iSubbus, CELL_MS_INMASK, iMS_VOICE_DSP_SLOT_REVERB, iIndex, 0);	// Allow input from none
			cellMSCoreSetMask( iSubbus, CELL_MS_OUTMASK, iMS_VOICE_DSP_SLOT_REVERB, iIndex, 0);	// Allow input from none
		}
		cellMSCoreSetMask( iSubbus, CELL_MS_INMASK, iMS_VOICE_DSP_SLOT_REVERB, 0,	( 1 << CELL_MS_SPEAKER_FL ) +
																					( 1 << CELL_MS_SPEAKER_FR ) );	// Allow input from channel 1+2
		
		cellMSCoreSetMask( iSubbus, CELL_MS_OUTMASK, iMS_VOICE_DSP_SLOT_REVERB, 0,	( 1 << CELL_MS_SPEAKER_FL ) + 
																					( 1 << CELL_MS_SPEAKER_FR ) +
																					( 1 << CELL_MS_SPEAKER_RL ) +
																					( 1 << CELL_MS_SPEAKER_RR ) +
																					( 1 << CELL_MS_SPEAKER_EL ) +
																					( 1 << CELL_MS_SPEAKER_ER ) );	// Output to all these
		

		// Set the reverb as not bypassed
		cellMSCoreBypassDSP( iSubbus, iMS_VOICE_DSP_SLOT_REVERB, CELL_MS_NOTBYPASSED );
	
		//Setup the aux return on the first DSP slot of the reverb bus
		cellMSCoreSetDSP( iSubbus, iMS_VOICE_DSP_SLOT_AUXRETURN, s_iDSPHandle[ ASURA_MS_DSP_EFFECT_HANDLE_AUX_RETURN ] );
		void* pAuxReturnDSPParamAddr = cellMSCoreGetDSPParamAddr( iSubbus, iMS_VOICE_DSP_SLOT_AUXRETURN );
		if(pAuxReturnDSPParamAddr)
		{
			pAuxReturnBuffer = AllocateFromDSPHeap( cellMSFXAuxReturnGetNeededMemorySize(8), 16 );

			#ifdef ASURA_ASSERT
			iRet =
			#endif
			cellMSFXAuxReturnInit( pAuxReturnDSPParamAddr, pAuxReturnBuffer, 8, iSubbus );
			Asura_Assertf(iRet >= 0, ("cellMSFXAuxReturnInit failed, error %d", cellMSSystemGetLastError()) );

			#ifdef ASURA_ASSERT
			iRet =
			#endif
			cellMSFXAuxReturnSetThruMix( pAuxReturnDSPParamAddr, 0, 0.0f );
			Asura_Assertf(iRet >= 0, ("cellMSFXAuxReturnSetThruMix failed, error %d", cellMSSystemGetLastError()) );

			for (int iIndex = 0; iIndex < 8; iIndex++ )
			{
				cellMSCoreSetMask( iSubbus, CELL_MS_INMASK,  iMS_VOICE_DSP_SLOT_AUXRETURN, iIndex, 0);	// Allow input from none
				cellMSCoreSetMask( iSubbus, CELL_MS_OUTMASK, iMS_VOICE_DSP_SLOT_AUXRETURN, iIndex, 0);	// Allow output to none
			}
			cellMSCoreSetMask( iSubbus, CELL_MS_OUTMASK, iMS_VOICE_DSP_SLOT_AUXRETURN, 0,	( 1 << CELL_MS_SPEAKER_FL ) );
			cellMSCoreSetMask( iSubbus, CELL_MS_OUTMASK, iMS_VOICE_DSP_SLOT_AUXRETURN, 1,	( 1 << CELL_MS_SPEAKER_FR ) );
																							
			cellMSCoreBypassDSP( iSubbus, iMS_VOICE_DSP_SLOT_AUXRETURN, CELL_MS_NOTBYPASSED );

			// Set the buffer pointers
			if( iSubbus == iASURA_MS_REVERB_DSP_BUS )
			{
				// (the dsp rerverb)
				s_pReverbBus_ReverbDSP		= pReverbBuffer;
				s_pReverbBus_AuxReturnDSP	= pAuxReturnBuffer;
			}
			else
			{
				// (the non-dsp reverb)
				s_pReverbBus_ReverbNoDSP	= pReverbBuffer;
				s_pReverbBus_AuxReturnNoDSP = pAuxReturnBuffer;
			}
		}
	}
	return true;
}

bool Asura_MultiStream_Audio::InitialiseSubbusses ()
{
	// Unroute everything
	for( int iBus = CELL_MS_SUBBUS_1; iBus <= CELL_MS_SUBBUS_31; ++iBus )
	{
		cellMSCoreSetRouting( iBus | CELL_MS_BUS_FLAG, CELL_MS_MASTER_BUS, CELL_MS_WET_AND_DRY, CELL_MS_ROUTE_OFF, 0.0f, 0.0f );
	}

	// Route the Reverb DSP bus to both the master DSP busses
	cellMSCoreSetRouting( iASURA_MS_REVERB_DSP_BUS, iASURA_MS_MASTER_DSP1_BUS, CELL_MS_WET, CELL_MS_ROUTE_ON, 0.0f, fASURA_MS_REVERB_VOLUME );
	cellMSCoreSetRouting( iASURA_MS_REVERB_DSP_BUS, iASURA_MS_MASTER_DSP2_BUS, CELL_MS_WET, CELL_MS_ROUTE_ON, 0.0f, fASURA_MS_REVERB_VOLUME );

	// Route the Reverb No DSP bus to the master voice
	cellMSCoreSetRouting( iASURA_MS_REVERB_NODSP_BUS, CELL_MS_MASTER_BUS, CELL_MS_WET, CELL_MS_ROUTE_ON, 0.0f, fASURA_MS_REVERB_VOLUME );

	// Route the Master DSP bus 1 to the master voice (at zero volume)
	cellMSCoreSetRouting( iASURA_MS_MASTER_DSP1_BUS, CELL_MS_MASTER_BUS, CELL_MS_WET_AND_DRY, CELL_MS_ROUTE_ON, 0.0f, 0.0f );
	// Route the Master DSP bus 2 to the master voice (at zero volume)
	cellMSCoreSetRouting( iASURA_MS_MASTER_DSP2_BUS, CELL_MS_MASTER_BUS, CELL_MS_WET_AND_DRY, CELL_MS_ROUTE_ON, 0.0f, 0.0f );
	
	// Set the routing volume for the Master DSP which is initially active
//	const int iMasterDSPBus = s_iActiveMasterDSPBus | CELL_MS_BUS_FLAG;
	cellMSCoreSetRouting( iASURA_MS_MASTER_DSP1_BUS, CELL_MS_MASTER_BUS, CELL_MS_WET, CELL_MS_ROUTE_ON, 0.0f, 1.0f );
	
	// Bypass dry processing on busses which dont need it
	cellMSCoreSetBypass( iASURA_MS_REVERB_NODSP_BUS	, CELL_MS_DRY, CELL_MS_BYPASSED );
	cellMSCoreSetBypass( iASURA_MS_REVERB_DSP_BUS	, CELL_MS_DRY, CELL_MS_BYPASSED );
	cellMSCoreSetBypass( iASURA_MS_MASTER_DSP1_BUS	, CELL_MS_DRY, CELL_MS_BYPASSED );
	cellMSCoreSetBypass( iASURA_MS_MASTER_DSP2_BUS	, CELL_MS_DRY, CELL_MS_BYPASSED );
		
	// Set the volumes for the master DSP Busses 
	float afVolumes[64] = {	1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
							0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
							0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
							0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
							0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
							0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
							0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
							0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f };
	#ifdef ASURA_ASSERT
	int iRet = 
	#endif
	cellMSCoreSetVolume64( iASURA_MS_MASTER_DSP1_BUS, CELL_MS_WET_AND_DRY, afVolumes );
	Asura_Assertf(iRet >= 0, ("cellMSCoreSetVolume64 failed, error %d", cellMSSystemGetLastError()) );

	#ifdef ASURA_ASSERT
	iRet = 
	#endif
	cellMSCoreSetVolume64( iASURA_MS_MASTER_DSP2_BUS, CELL_MS_WET_AND_DRY, afVolumes );
	Asura_Assertf(iRet >= 0, ("cellMSCoreSetVolume64 failed, error %d", cellMSSystemGetLastError()) );

	// Set the volumes for the master
	for( int iVolEntry = 0; iVolEntry < 64; ++iVolEntry )
	{
		afVolumes[ iVolEntry ] *= fASURA_MS_MASTER_VOLUME;
	}

	#ifdef ASURA_ASSERT
	iRet = 
	#endif
	cellMSCoreSetVolume64(CELL_MS_MASTER_BUS, CELL_MS_WET_AND_DRY, afVolumes);
	Asura_Assertf(iRet >= 0, ("cellMSCoreSetVolume64 failed, error %d", cellMSSystemGetLastError()) );

	// Bypass all subbusses reserved for multichannel samples
	for( int iBus = iASURA_MS_FIRST_FREE_SUBBUS; iBus <= iASURA_MS_LAST_FREE_SUBBUS; ++iBus )
	{
		cellMSCoreSetBypass( iBus | CELL_MS_BUS_FLAG, CELL_MS_DRY, CELL_MS_BYPASSED );
	}

	return true;
}


void Asura_MultiStream_Audio::Update ()
{
	UpdateReverb();

	for( int iVoice = 0; iVoice < iASURA_MS_MAX_VOICES; ++iVoice )
	{
		if( s_pxVoices[ iVoice ] )
		{
			s_pxVoices[ iVoice ]->Update();

			if( s_pxVoices[ iVoice ]->CanDeleteMe() )
			{
				s_pxVoices[ iVoice ]->FakeDestructor ();
				s_pxVoiceHeap->Free( s_pxVoices[ iVoice ] );
				s_pxVoices[ iVoice ] = 0;
			}
		}
	}
}


void Asura_MultiStream_Audio::UpdateReverb ( )
{
	Asura_Audio_Reverb xAsuraReverb;
	float fReverbAmount = 0.0f;

	Asura_Audio_System::GetCurrentReverb( xAsuraReverb, fReverbAmount );

	CellMSFXI3DL2Params xCellReverb;

	PopulateCellReverbParams( &xCellReverb, &xAsuraReverb );

	float afVolumes[64] = {	1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
							0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
							0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
							0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
							0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
							0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
							0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
							0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f };

	for( int iVolEntry = 0; iVolEntry < 64; ++iVolEntry )
	{
		afVolumes[ iVolEntry ] *= fReverbAmount;
	}

	void* pDSPParam = cellMSCoreGetDSPParamAddr( iASURA_MS_REVERB_DSP_BUS, iMS_VOICE_DSP_SLOT_REVERB );
	if( s_pReverbBus_ReverbDSP && pDSPParam )
	{
		cellMSFXI3DL2SetParams( pDSPParam, s_pReverbBus_ReverbDSP, &xCellReverb, 0 );
		#ifdef ASURA_ASSERT
		int iRet = 
		#endif
		cellMSCoreSetVolume64( iASURA_MS_REVERB_DSP_BUS, CELL_MS_WET, afVolumes );
		Asura_Assertf(iRet >= 0, ("cellMSCoreSetVolume64 failed, error %d", cellMSSystemGetLastError()) );
	}

	pDSPParam = cellMSCoreGetDSPParamAddr( iASURA_MS_REVERB_NODSP_BUS, iMS_VOICE_DSP_SLOT_REVERB );
	if( s_pReverbBus_ReverbNoDSP && pDSPParam )
	{
		cellMSFXI3DL2SetParams( pDSPParam, s_pReverbBus_ReverbNoDSP, &xCellReverb, 0 );
		#ifdef ASURA_ASSERT
		int iRet = 
		#endif
		cellMSCoreSetVolume64( iASURA_MS_REVERB_NODSP_BUS, CELL_MS_WET, afVolumes );
		Asura_Assertf(iRet >= 0, ("cellMSCoreSetVolume64 failed, error %d", cellMSSystemGetLastError()) );
	}
}


void Asura_MultiStream_Audio::Shutdown ()
{
	// Delete all of our voices
	delete s_pxVoiceHeap;
	delete s_pVoiceMemory;
	Asura_Mem::Set( s_pxVoices, 0, sizeof( Asura_MultiStream_Voice* ) * iASURA_MS_MAX_VOICES );

	Asura_Multistream_Streaming::ShutdownStreaming();

	// Ask update thread to exit and wait for it to complete (blocks)
	s_bMSThreadExitFlag = true;
	uint64_t uThreadResult = 0;
	sys_ppu_thread_join( s_xMultiStreamPuThread, &uThreadResult );

	// Shutdown MultiStream system
	#ifdef ASURA_ASSERT
	void* pMemory = cellMSSystemClose();
	Asura_Assert(pMemory == s_pMSSystemMemory, "Memory pointers should be identical\n");
	#else
	cellMSSystemClose();
	#endif

	// Clean up all DSP stuff
	s_pxDSPHeap->Wipe();
	delete s_pxDSPHeap;
	s_pxDSPHeap = 0;

	delete (char*)s_pDSPMemory;
	s_pDSPMemory = 0;

	s_pReverbBus_ReverbDSP = 0;
	s_pReverbBus_AuxReturnDSP = 0;

	for( int i = 0; i < ASURA_MS_DSP_EFFECT_NUM_HANDLES; ++i )
	{
		s_iDSPHandle[i] = -1;
	}

	// Deallocate the memory we allocated in Initialise()
	if ( s_pMSSystemMemory != NULL )
	{
		Asura_DeallocateAlign(s_pMSSystemMemory);
	}
	s_pMSSystemMemory = NULL;

	int iResult = cellAudioQuit();
	Asura_Assertf( iResult == CELL_OK, ("cellAudioQuit() error\n") );

	ASURA_STATIC_ASSERT( CELL_SDK_VERSION >= 0x090002 );
	// Unload the libaudio PRX.
	iResult = cellSysmoduleUnloadModule( CELL_SYSMODULE_AUDIO );
	Asura_Assert( iResult == CELL_OK, "PRX for libaudio already unloaded?" );
}


void Asura_MultiStream_Audio::MultiStreamUpdateThread	( uint64_t param )
{
	cellAudioPortStart(s_uMSPortNum);

	// Loop until time to terminate
	while ( !s_bMSThreadExitFlag )
	{
		cellMSSystemSignalSPU();	// Note - return buffer position is unusable

		//If you want to do any operations which are syncronous with MS, do them here

		sys_timer_usleep( uASURA_MS_PPU_THREAD_SLEEP );
		cellMSSystemGenerateCallbacks();
	}

	cellAudioPortStop( s_uMSPortNum );

	sys_ppu_thread_exit(0);
}

#include "Core/Asura_MemoryManagement_DebugOff.h"	//Allow placement new

Asura_Handle Asura_MultiStream_Audio::CreateVoice ( const Asura_Hash_ID uWave )
{
	int iVoiceHandle = ASURA_INVALID_HANDLE;

	if( !Asura_MultiStream_Audio::FindBuffer( uWave ) )
	{
		Asura_Assertf( false, ( "Failed to find buffer for sound, hash %u", uWave ) );
		return ASURA_INVALID_HANDLE;
	}

	// Find a free slot to use for this voice
	for( int iVoice = 0; iVoice < iASURA_MS_MAX_VOICES; ++iVoice )
	{
		if( !s_pxVoices[ iVoice ] )
		{
			void* pMemory = s_pxVoiceHeap->Alloc( sizeof( Asura_MultiStream_Buffer_Voice ) );
			Asura_Assert( pMemory, "Ran out of memory for multistream voices" );
			if( pMemory )
			{
				Asura_MultiStream_Buffer_Voice* pxVoice = new (pMemory) Asura_MultiStream_Buffer_Voice();
				if( pxVoice )
				{
					if( pxVoice->CreateStream () )
					{
						iVoiceHandle = NextVoiceHandle();
						
						pxVoice->SetAudioSystemHandle	( iVoiceHandle );
						pxVoice->EnqueueWave			( uWave );
						
						s_pxVoices[ iVoice ] = pxVoice;
					}
					else
					{
						// Failed to create the stream
						pxVoice->FakeDestructor();
						s_pxVoiceHeap->Free( pxVoice );
					}
				}
			}
			break;
		}
	}

	return iVoiceHandle;
}


Asura_Handle Asura_MultiStream_Audio::CreateStreamingVoice()
{
	int iVoiceHandle = ASURA_INVALID_HANDLE;

	// Find a free slot to use for this voice
	for( int iVoice = 0; iVoice < iASURA_MS_MAX_VOICES; ++iVoice )
	{
		if( !s_pxVoices[ iVoice ] )
		{
			void* pMemory = s_pxVoiceHeap->Alloc( sizeof( Asura_MultiStream_MP3_Streaming_Voice ) );
			Asura_Assert( pMemory, "Ran out of memory for multistream voices" );
			if( pMemory )
			{
				Asura_MultiStream_MP3_Streaming_Voice* pxVoice = new (pMemory) Asura_MultiStream_MP3_Streaming_Voice();
				
				if( pxVoice )
				{
					if( pxVoice->CreateStream() )
					{
						iVoiceHandle = NextVoiceHandle();

						pxVoice->SetAudioSystemHandle( iVoiceHandle );

						s_pxVoices[ iVoice ] = pxVoice;
					}
					else
					{
						// Failed to create the stream
						pxVoice->FakeDestructor();
						s_pxVoiceHeap->Free( pxVoice );
					}
				}
			}
			break;
		}
	}
	return iVoiceHandle;
}


Asura_Handle Asura_MultiStream_Audio::CreateCustomStreamingVoice()
{
	int iVoiceHandle = ASURA_INVALID_HANDLE;

	// Find a free slot to use for this voice
	for( int iVoice = 0; iVoice < iASURA_MS_MAX_VOICES; ++iVoice )
	{
		if( !s_pxVoices[ iVoice ] )
		{
			void* pMemory = s_pxVoiceHeap->Alloc( sizeof( Asura_MultiStream_Custom_Streaming_Voice ) );
			Asura_Assert( pMemory, "Ran out of memory for multistream voices" );
			if( pMemory )
			{
				Asura_MultiStream_Custom_Streaming_Voice* pxVoice = new (pMemory) Asura_MultiStream_Custom_Streaming_Voice();
				
				if( pxVoice )
				{
					if( pxVoice->CreateStream() )
					{
						iVoiceHandle = NextVoiceHandle();
						pxVoice->SetAudioSystemHandle	( iVoiceHandle );
						s_pxVoices[ iVoice ] = pxVoice;
					}
					else
					{
						// Failed to create the stream
						pxVoice->FakeDestructor();
						s_pxVoiceHeap->Free( pxVoice );
					}
				}
			}
			break;
		}
	}
	return iVoiceHandle;
}


Asura_Handle Asura_MultiStream_Audio::CreateSubmixVoice ( )
{
	int iVoiceHandle = ASURA_INVALID_HANDLE;

	// Find a free slot to use for this voice
	for( int iVoice = 0; iVoice < iASURA_MS_MAX_VOICES; ++iVoice )
	{
		if( !s_pxVoices[ iVoice ] )
		{
			void* pMemory = s_pxVoiceHeap->Alloc( sizeof( Asura_MultiStream_Submix_Voice ) );
			Asura_Assert( pMemory, "Ran out of memory for multistream voices" );
			if( pMemory )
			{
				Asura_MultiStream_Submix_Voice* pxVoice = new (pMemory) Asura_MultiStream_Submix_Voice();

				if( pxVoice )
				{
					if( pxVoice->CreateStream() )
					{
						iVoiceHandle = NextVoiceHandle();
						pxVoice->SetAudioSystemHandle	( iVoiceHandle );
						s_pxVoices[ iVoice ] = pxVoice;
					}
					else
					{
						// Failed to create the stream
						pxVoice->FakeDestructor();
						s_pxVoiceHeap->Free( pxVoice );
					}
				}
			}
			break;
		}
	}
	return iVoiceHandle;
}


#include "Core/Asura_MemoryManagement_DebugOn.h"

void Asura_MultiStream_Audio::DestroyVoice ( const Asura_Handle xHandle )
{
	Asura_MultiStream_Voice* pxVoice = reinterpret_cast<Asura_MultiStream_Voice*>( GetVoice( xHandle ) );
	Asura_Assert( pxVoice, "Failed to find voice to destroy" );

	if( pxVoice )
	{
		pxVoice->Destroy();

		//Check if we can delete this voice
		if( pxVoice->CanDeleteMe() )
		{
			// we need to find the voice in our voices array so we can zero it...
			for( int iVoice = 0; iVoice < iASURA_MS_MAX_VOICES; ++iVoice )
			{
				if( s_pxVoices[ iVoice ] && ( s_pxVoices[ iVoice ] == pxVoice ) )
				{
					pxVoice->FakeDestructor();
					s_pxVoiceHeap->Free( pxVoice );
					s_pxVoices[ iVoice ] = 0;
				}
			}
		}
	}
}


Asura_Audio_Voice* Asura_MultiStream_Audio::GetVoice ( const Asura_Handle xHandle )
{
	for( int iVoice = 0; iVoice < iASURA_MS_MAX_VOICES; ++iVoice )
	{
		if( s_pxVoices[ iVoice ] && ( s_pxVoices[ iVoice ]->GetAudioSystemHandle() == xHandle ) )
		{
			return s_pxVoices[ iVoice ];
		}
	}

	return 0;
}


Asura_MultiStream_Buffer* Asura_MultiStream_Audio::CreateBuffer ( const char* const szFilename, bool& bAlreadyExists )
{
	const Asura_Hash_ID uNameHash = Asura_GetHashID( szFilename );

	//see if a buffer already exists for this filename
	Asura_MultiStream_Buffer* pxBuffer = s_xBuffers.GetResource( uNameHash );

	if( pxBuffer)
	{
		bAlreadyExists = true;
		//add a reference for the current resource bank
		s_xBuffers.AddResource(uNameHash, *pxBuffer, false);
	}
	else
	{
		bAlreadyExists = false;
		//create a new buffer and add it
		pxBuffer = new Asura_MultiStream_Buffer ();
		Asura_Assert( pxBuffer, "Failed to allocate memory for audio buffer." );

		if( pxBuffer )
		{
			if( !s_xBuffers.AddResource( uNameHash, *pxBuffer ) )
			{
				Asura_Assert( false, "Failed to add audio buffer to resource set." );
				delete pxBuffer;
				pxBuffer = NULL;
			}
		}
	}
	return pxBuffer;	
}


Asura_MultiStream_Buffer* Asura_MultiStream_Audio::FindBuffer ( Asura_Hash_ID xHash )
{
	return s_xBuffers.GetResource( xHash );
}


char* Asura_MultiStream_Audio::GetWavFilename ( Asura_Hash_ID xFileHash )
{
	Asura_MultiStream_Buffer* pxBuffer = s_xBuffers.GetResource( xFileHash );
	if( pxBuffer )
	{
		return pxBuffer->GetFilename();
	}
	return NULL;
}


void Asura_MultiStream_Audio::CreateDSPChain ( const Asura_Hash_ID xHash, const Asura_Audio_DSPChain* pxChain )
{
	// TODO: Store this DSP chain in a list
}


void Asura_MultiStream_Audio::SetMasterDSPChain ( const Asura_Hash_ID xHash )
{
	//TODO: Set our  master DSP
}


Asura_Handle Asura_MultiStream_Audio::NextVoiceHandle ()
{
	s_iNextVoiceHandle = ( s_iNextVoiceHandle + 1 ) % iASURA_MS_MAX_VOICE_HANDLE;
	return s_iNextVoiceHandle;
}


void* Asura_MultiStream_Audio::AllocateFromDSPHeap ( const int iSize, const int iAlignment )
{
	if( s_pxDSPHeap )
	{
		return s_pxDSPHeap->Alloc( iSize, iAlignment );
	}
	return 0;
}


void Asura_MultiStream_Audio::DeallocateFromDSPHeap ( void* pMemory )
{
	if( s_pxDSPHeap )
	{
		s_pxDSPHeap->Free( pMemory );
	}
}


void* Asura_MultiStream_Audio::GetReverbAuxRetBuffer ( const bool bDSPReverb)
{
	if( bDSPReverb )
	{
		return s_pReverbBus_AuxReturnNoDSP;
	}
	return s_pReverbBus_ReverbNoDSP;
}


int Asura_MultiStream_Audio::LoadDSPFromMemory (void* pDSPMemory)
{
	CellMSDSP xDSPInfo;

	// Load DSP from the temporary image we have in memory
	int iDSPHandle = cellMSDSPLoadDSPFromMemory( pDSPMemory, &xDSPInfo, CELL_MS_ALLOC_DSPHANDLE, CELL_MS_ALLOC_PAGE );
	Asura_Assert( iDSPHandle >= 0, "Failed to load dsp" );
	
	return iDSPHandle;
}


int	Asura_MultiStream_Audio::GetNextFreeSubbus ( )
{
	// Loop over our allowed sub busses and return one which is not used
	// we determine if it is in use by checking if mixing is bypassed
	for( int iBus = iASURA_MS_FIRST_FREE_SUBBUS; iBus <= iASURA_MS_LAST_FREE_SUBBUS; ++iBus )
	{
		CELL_MS_BYPASS xState = cellMSCoreGetBypass( iBus | CELL_MS_BUS_FLAG, CELL_MS_DRY );

		if( xState == CELL_MS_BYPASSED )
		{
			// This bus is free, unbypass it
			cellMSCoreSetBypass( iBus | CELL_MS_BUS_FLAG, CELL_MS_WET_AND_DRY, CELL_MS_NOTBYPASSED );
			return iBus;
		}
	}
	return ASURA_MS_INVALID_SUBBUS;
}


void Asura_MultiStream_Audio::ReleaseSubbus ( const int iBusNumber )
{
	// Set the mixing as bypassed on this subbus
	cellMSCoreSetBypass( iBusNumber | CELL_MS_BUS_FLAG, CELL_MS_DRY, CELL_MS_BYPASSED );
}


void Asura_MultiStream_Audio::PopulateCellReverbParams ( CellMSFXI3DL2Params* pCellParams, Asura_Audio_Reverb* pAsuraParams )
{
	pCellParams->Room				= static_cast<float>( pAsuraParams->m_iRoom					);
	pCellParams->Room_HF			= static_cast<float>( pAsuraParams->m_iRoomHF				);
//  pCellParams->RoomRolloffFactor  =					( pAsuraParams->m_fRoomRolloffFactor	);	// Curiously absent from the multistream struct
	pCellParams->Decay_time			=					( pAsuraParams->m_fDecayTime			);
	pCellParams->Decay_HF_ratio		=					( pAsuraParams->m_fDecayHFRatio			);
	pCellParams->Reflections		= static_cast<float>( pAsuraParams->m_iReflections			);
	pCellParams->Reflections_delay	=					( pAsuraParams->m_fReflectionsDelay		);
	pCellParams->Reverb				= static_cast<float>( pAsuraParams->m_iReverb				);
	pCellParams->Reverb_delay		= static_cast<float>( pAsuraParams->m_fReverbDelay			);
	pCellParams->Diffusion			=					( pAsuraParams->m_fDiffusion			);
	pCellParams->Density			=					( pAsuraParams->m_fDensity				);
	pCellParams->HF_reference		=					( pAsuraParams->m_fHFReference			);
	
	// extended parameters ( not in standard, and therefore complete guesses on my part )
	
	pCellParams->MixChannel					= -1;							// channel index to crossfade / mix signal with for stereo -1 == off, 0 to 7 == mix chan
	pCellParams->MixAmount					= 0.0f;							// percentage to cross-mix with MixChannel   0 to 100 %
	pCellParams->EarlyReflectionPattern		= CELL_MSFX_ROOM1_LEFT;			// No idea
	pCellParams->LateReverbPattern			= CELL_MSFX_ROOM1_LATE_LEFT;	// ...
	pCellParams->EarlyReflectionScaler		= 50.0f;						// scaler 0 -> 100% for early reflections. 0 == single reflection 100 == widely spread reflections
	pCellParams->LF_reference				= 100.0f;						// reference low frequency 20 to 20000 Hertz
	pCellParams->Room_LF					= 0.0f;							// low frequency content input in mB, at LF_reference -10000 to 0 mB
}


#ifdef ASURA_ASSERT

void Asura_MultiStream_Audio::PauseAllStreams ()
{
	// Pause every voice
	for( int iVoice = 0; iVoice < iASURA_MS_MAX_VOICES; ++iVoice )
	{
		if( s_pxVoices[ iVoice ] )
		{
			s_pxVoices[ iVoice ]->Pause();
		}
	}
}


void Asura_MultiStream_Audio::UnpauseAllStreams ()
{
	// Unpause every voice
	for( int iVoice = 0; iVoice < iASURA_MS_MAX_VOICES; ++iVoice )
	{
		if( s_pxVoices[ iVoice ] )
		{
			s_pxVoices[ iVoice ]->Unpause();
		}
	}
}

#endif


#endif //ASURA_USE_AUDIO_SYSTEM

//eof
