///////////////////////////////////////////////////////////////////////////////
//
// File      : Asura_PS3_MultiStream.h
// Author    : Philip Hall
// Created   : 13/09/2007
//
// Copyright (C) 2007 Rebellion. All Rights Reserved
//
///////////////////////////////////////////////////////////////////////////////

#ifndef __ASURA_PS3_MULTISTREAM_H__
#define __ASURA_PS3_MULTISTREAM_H__

#ifndef ASURA_USE_AUDIO_SYSTEM

#if defined(ASURA_PS3_USE_MULTISTREAM) && (ASURA_PS3_USE_MULTISTREAM != 0)

// Asura headers
#include "Sound/Asura_Sound.h"
#include "Core/Asura_RingBuffer.h"

// Asura PS3 Headers
#include "Sound/Asura_PS3_MultiStream_Streaming.h"

// SDK headers
#include <cell/audio.h>
#include <cell/mstream.h>
#include <cell/sysmodule.h>

// Choose a suitable reverb (I3DL2 by default)
#if !(defined ASURA_PS3_USE_I3DL2_REVERB) && !(defined ASURA_PS3_USE_PARAMETRIC_REVERB) && !(defined ASURA_PS3_USE_IR_REVERB)
	//#define ASURA_PS3_USE_PARAMETRIC_REVERB
	#define ASURA_PS3_USE_I3DL2_REVERB
	//#define ASURA_PS3_USE_IR_REVERB
#endif	

#define ASURA_PS3_SOUND_MAX_STREAMS_TO_DESTROY	64	//in one frame (basically)

enum ASURA_PS3_SOUND_DSP_EFFECT_HANDLE
{
	ASURA_PS3_SOUND_DSP_EFFECT_HANDLE_REVERB = 0,
	ASURA_PS3_SOUND_DSP_EFFECT_HANDLE_LPF,
	ASURA_PS3_SOUND_DSP_EFFECT_HANDLE_AUX_SEND,
	ASURA_PS3_SOUND_DSP_EFFECT_HANDLE_AUX_RETURN,

	ASURA_PS3_SOUND_DSP_EFFECT_NUM_HANDLES
};

class Asura_PS3_MultiStream;
class Asura_Mutex;

class Asura_PS3_SoundToDestroy
{
	public:

		int		m_iStreamHandle;
		u_int	m_uDestroyTime;	// timestamp when this sound can be destroyed.

		bool							m_bStreamed;
		Asura_PS3_FileSystem::Handle	m_xStreamingFile;
		void*							m_pStreamBuffers[2];

		void*	m_pOcclusionDSPBuffer;
		void*	m_pAuxSendDSPBuffer;
		void*	m_pDistanceDSPBuffer;
};

////////////////////////////////////////////
// PS3 MultiStream active sound
////////////////////////////////////////////
class Asura_PS3_ActiveMSSound : public Asura_PS3_ActiveSound_Common
{
public:
	
	Asura_PS3_ActiveMSSound();
	~Asura_PS3_ActiveMSSound();

	bool Create(int iLoadedSoundIndex, Asura_Handle iSoundHandle, bool bUseEQ, bool bUse3d);
	void Destroy();
	void Update();

	bool IsPlaying() const;
	bool IsPaused() const;
	bool IsStreamed() const { return m_bStreamed; }

	// Basic behaviour
	bool Play( bool bLooping );
	bool Pause();
	bool Unpause();
	bool Stop(int iDecayMS);

	// More advanced control
	bool SetUseReverb( bool bReverb );
	bool SetEQType(Asura_Sound_System::EQType eType);
	bool SetCone( const Asura_Vector_3& xDirection, float fInnerAngle, float fOuterAngle, float fVolumeOutsideCone ) { return false; }
	bool IsValid() const { return (m_iLoadedSoundIndex >= 0) && (m_iMSStreamHandle >= 0); }
	
	void UpdateDistanceFilter(float fGain);
	void UpdateOcclusionFilter(float fGain);

	void CreateDistanceFilter	();
	void CreateOcclusionFilter	();
	void CreateAuxSend			();

	void SetStreamFinished		(bool bFinished)		{ m_bStreamFinished = bFinished; };
	bool IsStreamFinished		()						{ return m_bStreamFinished; }

	void SetSoundCanBeDeleted	(bool bCanBeDeleted)	{ m_bSoundCanBeDeleted = bCanBeDeleted; }
	bool SoundCanBeDeleted		()						{ return m_bSoundCanBeDeleted; }

private:
	
	void ApplySettings();
	static void StreamLocalCallback(int iStreamHandle, void* pUserData, int iCallbackType, void* pWriteBuffer, int nBufferSize);
	static void LoadDataRequest( void* pThis, u_int uSize );
	bool IsStreamValid ()	{ return ( ( m_iMSStreamHandle >= 0 ) && !m_bErrorOnStream );	}

	bool IsMono () { return (m_iNumChannels == 1); }

	friend class Asura_PS3_MultiStream;
	
	bool   m_bUseHardware3d;		// set if using 3d vol, pan and s_bHardwarePan turned on.

	bool   m_bStreamed;              // True if streamed sound
	u_int  m_uStreamingBytesRead;    // Number of bytes streamed
	int    m_iMSStreamHandle;        // Multistream stream handle
	void*  m_pStreamBuffers[2];      // 2 sound streaming buffers (dynamically allocated, should be aligned)
	Asura_PS3_FileSystem::Handle m_xStreamingFile;

	void*	m_pOcclusionDSPBuffer;			//Occlusion DSP
	void*	m_pOcclusionDSPParamAddr;
	bool	m_bOcclusionDSPActive;
	float	m_fOcclusionFilterGain;

	void*	m_pAuxSendDSPBuffer;			//Aux send DSP
	void*	m_pAuxSendDSPParamAddr;
	bool	m_bAuxSendDSPActive;

	void*	m_pDistanceDSPBuffer;			//Distance DSP
	void*	m_pDistanceDSPParamAddr;
	bool	m_bDistanceDSPActive;
	float	m_fDistanceFilterGain;

	bool	m_bUseReverb;

	int		m_iNumChannels;

	bool	m_bPausedByPitch;

	bool	m_bErrorOnStream;

	volatile bool	m_bStreamFinished;
	volatile bool	m_bSoundCanBeDeleted;

	Asura_PS3_MS_LoadDataRequest m_xLoadDataRequest;

	Asura_PS3_MultiStream*	m_pxParent;
};



////PGH/////////////////////////////////////////
// Multistream reverb information             
////////////////////////////////////////////////
class Asura_PS3_ReverbInfo
{
public:
#if defined ASURA_PS3_USE_I3DL2_REVERB
	CellMSFXI3DL2Params    m_xParams;
#elif defined ASURA_PS3_USE_PARAMETRIC_REVERB
	CellMSFXReverbParams   m_xParams;
#endif
	Asura_Sound_System::ReverbType m_eReverbType;
	float m_fWetVolumeScale;
	float m_fDryVolumeScale;
	
	Asura_PS3_ReverbInfo();
	void InterpolateTo(Asura_PS3_ReverbInfo &xResult, const Asura_PS3_ReverbInfo &xEnd, float fRatio);
	void SetReverb(Asura_Sound_System::ReverbType m_eReverbType, float fWetVolumeScale, float fDryVolumeScale);
};


////////////////////////////////////////////
// PS3 MultiStream sound system
////////////////////////////////////////////
class Asura_PS3_MultiStream
{

public:

	Asura_PS3_MultiStream();
	virtual ~Asura_PS3_MultiStream();

	bool Initialise();
	void Update();
	void Reset();
	bool Shutdown();

	void SetReverb( Asura_Sound_System::ReverbType eType, float fDryVolumeScale, float fWetVolumeScale );

	static int	GetMaxConcurrentSounds() { return iASURA_PS3_SOUND_MAX_VOICES; }
	static int	GetMaxConcurrentSoundsWithEQ() { return ASURA_PS3_SOUND_MAX_SUB_BUSES; }
	static int	GetMaxVoiceChannels() { return iASURA_PS3_SOUND_MAX_VOICE_CHANNELS; }
	static int	GetUsedVoiceChannels();

	static Asura_PS3_MultiStream_Streaming& GetStreamingAPI() { return s_xStreamingAPI; }

	void ReloadDSPEffects (); 

	int		GetDSPHandle (ASURA_PS3_SOUND_DSP_EFFECT_HANDLE uEffect) { return m_iDSPHandle[uEffect]; }
	void*	GetDSPMemory (ASURA_PS3_SOUND_DSP_EFFECT_HANDLE uEffect) { return m_pDSPBuffer[uEffect]; }

	static void StreamGlobalCallback(int iStreamHandle, void* pUserData, int iCallbackType, void* pWriteBuffer, int nBufferSize);

	//These functions are thread safe accesors for the list of sounds to destroy
	bool	GetNextSoundToDestroy	(Asura_PS3_SoundToDestroy* pxOut);	
	void	QueueSoundToDestroy		(Asura_PS3_SoundToDestroy pxIn);

	#ifdef ASURA_PS3_SUPPORT_SULPHA
	void	BeginSulphaCapture		()	{ cellMSSulphaDECI3Start(); }
	#endif

private:

	//---------------- Private Functions ---------------------

	bool InitialiseAudio();
	bool InitialiseSPURS();
	bool InitDSPEffects();
	void UpdateDSPReverb(Asura_PS3_ReverbInfo &xReverbInfo);
	void ShutdownDSPEffects();
	
	static void MultiStream_Update_Thread(uint64_t param);

#if defined ASURA_PS3_USE_I3DL2_REVERB
	void*  m_pI3DL2ParamAddr;
	bool InitI3DL2ReverbDSP();

#elif defined ASURA_PS3_USE_IR_REVERB
	// Impulse response reverb
	bool InitImpulseResponseDSP();

#elif defined ASURA_PS3_USE_PARAMETRIC_REVERB
	// Time domain parametric reverb
	bool InitParametricReverbDSP();
#endif

	//---------------- Private Data ---------------------

	void*                      m_pMSSystemMemory;      // Aligned memory allocated for MultiStream
	sys_ppu_thread_t           m_xMultiStreamPuThread; // PU thread for multistream/libaudio updates
	unsigned int               m_uMSPortNum;           // Multistream output port

	// Reverb setup
	Asura_PS3_ReverbInfo       m_xCurrentReverb;       // Current global reverb settings
	Asura_PS3_ReverbInfo       m_xNextReverb;          // Next global reverb to uses
	float                      m_fReverbIPolRatio;     // Reverb interpolation end time

	static Asura_PS3_MultiStream_Streaming s_xStreamingAPI;

	//DSP effects
	int		m_iDSPHandle [ASURA_PS3_SOUND_DSP_EFFECT_NUM_HANDLES];	//List of handles to DSP effects
	void*	m_pDSPBuffer [ASURA_PS3_SOUND_DSP_EFFECT_NUM_HANDLES];	//Memory allocated for DSP effects
	
	Asura_Mutex									m_xDestroyListMutex;
	Asura_RingBuffer<Asura_PS3_SoundToDestroy>	m_xStreamsToDestroy;	//List of sounds to destroy in the multistream callback

	#ifdef ASURA_PS3_SUPPORT_SULPHA
	void*	m_pSulphaMemory;
	#endif

	ASURA_DECLARE_NO_COPYING(Asura_PS3_MultiStream);
};


#endif	// ASURA_PS3_USE_MULTISTREAM

#endif // #ifndef ASURA_USE_AUDIO_SYSTEM

#endif


