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

// F I L E   W R A P P E R ///////////////////////////////////////////////////

#if defined ASURA_USE_PRAGMA_ONCE
#pragma once
#endif

#ifndef __ASURA_PS3_MULTISTREAM_VOICE_H_
#define __ASURA_PS3_MULTISTREAM_VOICE_H_

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

#include "Core/Asura.h"

#ifdef ASURA_USE_AUDIO_SYSTEM

#include "Core/Asura_FixedArray.h"
#include "Sound/Asura_Audio_DSP.h"
#include "Sound/Asura_Audio_Voice.h"

// D E F I N E S /////////////////////////////////////////////////////////////

#define ASURA_PS3_MULTISTREAM_VOICE_MAX_QUEUED_WAVS		8

// F O R W A R D   D E C L A R A T I O N S ///////////////////////////////////

class Asura_MultiStream_MP3_Stream;
class Asura_MultiStream_Custom_Stream;
class Asura_PS3_Sound_Info;

// T Y P E D E F S ///////////////////////////////////////////////////////////

typedef bool (*Asura_PS3_Audio_MoreDataFunc)( char* &pData, u_int &uDataSize, void* pMoreDataCallbackArg );

// C L A S S   D E C L A R A T I O N S ///////////////////////////////////////

class Asura_MultiStream_Voice : public Asura_Audio_Voice
{
	friend class Asura_MultiStream_Audio;

	public:

		// ------------------- Functions ------------------------

								Asura_MultiStream_Voice		( );
		virtual					~Asura_MultiStream_Voice	( )															{ Asura_Assert( false, "Should not get here" );	}
				
		virtual void			EnqueueWave					( const Asura_Hash_ID uWave )								{ Asura_Assert(false, "Cannot enqueue with this type of voice"); }

		virtual void			Start						( ) = 0;
		virtual bool			IsPlaying					( ) const;

		virtual void			SeekAtPlaybackTime			( const float fSeekPosition, const float fPlaybackTime )	{ Asura_Assert(false, "Cannot seek with this type of voice"); }
		virtual bool			SeekIsPending				( )															{ Asura_Assert(false, "Cannot seek with this type of voice"); return false; }

		virtual void			SetStartOffset				( const float fStartOffset );

		virtual void			Pause						( )															{ SetPausedExternally( true );  }
		virtual void			Unpause						( )															{ SetPausedExternally( false ); }

		virtual float			GetPlayPosition				( ) const													{ Asura_Assert(false, "Cannot get the play position for this type of voice"); return 0.0f; }

		virtual void			SetVoiceDSPChain			( Asura_Hash_ID uDSPChain );

		virtual bool			SetSubmixOutputs			( const Asura_Handle* auSubmixVoices, const u_int uNumVoices );
		virtual void			ClearSubmixOutputs			( );

	protected:

		// ------------------- Functions ------------------------

		virtual void			FakeDestructor				( );

		virtual void			Update						( const bool bFirstUpdate = false );
		virtual void			UpdateVolumes				( );
		virtual void			UpdatePrimaryFilter			( );
		virtual void			UpdateSecondaryFilter		( );
		virtual void			UpdateAuxSend				( );
		
		virtual bool			CreateStream				( );
		virtual void			Destroy						( );
		
		virtual void			SetAudioSystemHandle		( const int iHandle )				{ m_iAudioSystemHandle = iHandle;				}
		virtual int				GetAudioSystemHandle		( )									{ return m_iAudioSystemHandle;					}

		virtual int				GetMSStreamHandle			( )									{ return m_iMSStreamHandle;						}

		virtual void 			SetErrorOnStream			( const bool bError		= true )	{ m_bErrorOnStream		= bError;				}
		virtual void 			SetLooping					( )									{ m_bIsLooping			= true;					}
		virtual void 			SetPlaying					( const bool bPlaying	= true )	{ m_bPlaying			= bPlaying;				}
		virtual void 			SetPausedByPitch			( const bool bPaused	= true );
		virtual void			SetPausedExternally			( const bool bPaused	= true );
		virtual void 			SetStreamCreated			( const bool bCreated	= true )	{ m_bStreamCreated		= bCreated;				}
		virtual void 			SetReadyForMoreData			( const bool bReady		= true )	{ m_bReadyForMoreData	= bReady;				}
		virtual void			SetCanDeleteMe				( const bool bCanDelete = true )	{ m_bCanDeleteMe		= bCanDelete;			}
		virtual void			SetUsingMasterDSP			( const bool bUsingDSP	= true )	{ m_bUsingMasterDSP		= bUsingDSP;			}
		virtual void			SetDestroyPending			( ) 								{ m_bDestroyPending		= true;					}

		virtual bool 			IsStreamValid				( )	const							{ return !m_bErrorOnStream;						}
		virtual bool 			IsReadyForMoreData			( )	const							{ return m_bReadyForMoreData;					}
		virtual bool 			IsStreamCreated				( )	const							{ return m_bStreamCreated;						}
		virtual bool 			IsPausedByPitch				( )	const							{ return m_bPausedByPitch;						}
		virtual bool 			IsPausedExternally			( )	const							{ return m_bPausedExternally;					}
		virtual bool			CanDeleteMe					( )	const							{ return m_bCanDeleteMe;						}
		virtual bool			IsLooping					( )	const							{ return m_bIsLooping;							}
		virtual bool			IsMono						( )	const							{ return ( m_iNumChannels == 1 );				}
		virtual bool			IsUsingMasterDSP			( )	const							{ return m_bUsingMasterDSP;						}
		virtual bool			IsUsingStreamDSP			( )	const							{ return ( m_uDSPChain != ASURA_HASH_ID_UNSET );}
		virtual bool			IsDestroyPending			( ) const							{ return m_bDestroyPending;						}
		

		static void				StreamCallback				( int	iStreamHandle
															, void* pUserData
															, int	iCallbackType
															, void* pWriteBuffer
															, int	iBufferSize	);

		void					CreatePrimaryLPFilter		( );	// Distance + Occlusion filtering
		void					CreateSecondaryLPFilter		( );	// Obstruction filtering
		void					CreateAuxSend				( );	// Send to reverb bus
		void					CreateAsuraDSPChainFX		( );	// Any DSP effects specified by the Asura DSP chain on this voice
		
		void					CreateBaseDSPEffects		( );	// Do all of the above
		void					DestroyBaseDSPEffects		( );
		
		void					SetupStreamRouting			( );	// Route the stream to the appropriate busses

		// ------------------- Variables ------------------------

		int				m_iMSStreamHandle;
		int				m_iNumChannels;
		int				m_iAudioSystemHandle;
		float			m_fStartOffset;
				
		void*			m_pPrimaryFilterBuffer;										// Primary filter DSP
		void*			m_pPrimaryFilterParamAddr;
		float			m_fPrimaryFilterGain;

		void*			m_pSecondaryFilterBuffer;									// Secondary filter DSP
		void*			m_pSecondaryFilterParamAddr;
		float			m_fSecondaryFilterGain;

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

		void*			m_apChainDSPBuffers	[ ASURA_DSP_MAX_EFFECTS_PER_CHAIN ];	// Asura voice chain DSP

		Asura_Hash_ID	m_uDSPChain;
		int				m_iMultiChannelBus;

		float			m_fDeathTimer;

		bool			m_bUsingMasterDSP;
		bool			m_bPrimaryFilterActive;
		bool			m_bSecondaryFilterActive;
		bool			m_bAuxSendDSPActive;
		bool			m_bDestroyPending;
		bool			m_bStreamCreated;
		bool			m_bPausedByPitch;
		bool			m_bPausedExternally;
		bool			m_bErrorOnStream;
		bool			m_bIsLooping;
		volatile bool	m_bCanDeleteMe;
		volatile bool	m_bPlaying;
		volatile bool	m_bReadyForMoreData;
		
	private:

		// ------------------- Functions ------------------------

		Asura_MultiStream_Voice(const Asura_MultiStream_Voice &xCopy);
		Asura_MultiStream_Voice& operator=(const Asura_MultiStream_Voice &xCopy);
};

//_________________________________________________________________________________________________________

class Asura_MultiStream_Buffer_Voice : public Asura_MultiStream_Voice
{
	friend class Asura_MultiStream_Audio;

	public:

		// ------------------- Functions ------------------------

						Asura_MultiStream_Buffer_Voice	( );
		virtual			~Asura_MultiStream_Buffer_Voice	( )									{ Asura_Assert( false, "Should not get here" );	}
		
		virtual void	EnqueueWave						( const Asura_Hash_ID uWave );
		
		virtual void	Start							( );

	protected:

		// ------------------- Functions ------------------------

		virtual void			Update					( const bool bFirstUpdate = false );
		
		virtual Asura_Hash_ID	PopNextWaveToPlay		( );

		// ------------------- Variables ------------------------

		Asura_Hash_ID	m_xCurrentWav;
		
		Asura_FixedArray<Asura_Hash_ID, ASURA_PS3_MULTISTREAM_VOICE_MAX_QUEUED_WAVS> m_xWaveQueue;

	private:

		// ------------------- Functions ------------------------

		Asura_MultiStream_Buffer_Voice(const Asura_MultiStream_Voice &xCopy);
		Asura_MultiStream_Buffer_Voice& operator=(const Asura_MultiStream_Voice &xCopy);
};


//_________________________________________________________________________________________________________

class Asura_MultiStream_MP3_Streaming_Voice : public Asura_MultiStream_Voice
{
	friend class Asura_MultiStream_Audio;

	public:

		// ------------------- Functions ------------------------

		Asura_MultiStream_MP3_Streaming_Voice			( );
		virtual ~Asura_MultiStream_MP3_Streaming_Voice	( )															{ Asura_Assert( false, "Should not get here" );	}

		virtual void			Prepare					( const Asura_Hash_ID xWavHash );

		virtual void			Start					( );
				
		virtual void			SeekAtPlaybackTime		( const float fSeekPosition, const float fPlaybackTime );
		virtual bool			SeekIsPending			( );

		virtual float			GetPlayPosition			( ) const;

		virtual bool			IsPreparing				( ) const { return m_bPreparing; }

	protected:

		// ------------------- Functions ------------------------

		virtual void			Start_Internal			( );
		
		virtual void			FakeDestructor			( );

		virtual void			Update					( const bool bFirstUpdate = false );

		//virtual const Asura_PS3_Sound_Info* GetSoundInfo( );

	private:

		enum SeekState
		{
			SEEK_STATE_NONE				= 0,	// No seek operation
			SEEK_STATE_PENDING			,		// Waiting to seek
			SEEK_STATE_NEXT_BUFFER		,		// The next read will follow a seek
			SEEK_STATE_REPORT_SEEKING	,		// This is mainly for reporting if a seek is pending, 
												// we stay in this state until the seek time has passed
		};

		// ------------------- Functions ------------------------

		static void			ThreadFunc_Prepare			( void* pData, u_int uDataSize );

		// ------------------- Variables ------------------------

		Asura_MultiStream_MP3_Stream*	m_pxStreamer;
		SeekState						m_eSeekState;
		float							m_fTimeToSeek;
		float							m_fSeekPosition;
				
		float							m_fCurrentBufferFinishTime;

		float							m_fPlayTime;
		bool							m_bPlayAfterPrepared;
		volatile bool					m_bPreparing;
};

//_________________________________________________________________________________________________________

class Asura_MultiStream_Custom_Streaming_Voice : public Asura_MultiStream_Voice
{
	friend class Asura_MultiStream_Audio;

	public:

		// ------------------- Functions ------------------------

								Asura_MultiStream_Custom_Streaming_Voice	( );
		virtual					~Asura_MultiStream_Custom_Streaming_Voice	( )													{ Asura_Assert( false, "Should not get here" );	}

		virtual void			Prepare										( Asura_PS3_Audio_MoreDataFunc pfnMoreDataFunc
																			, const int iSampleFormat
																			, const int iSampleRate
																			, const int iNumChannels
																			, void* pMoreDataCallbackArg = NULL);

		virtual void			Start										( );

	protected:

		// ------------------- Functions ------------------------

		virtual void			Update					( const bool bFirstUpdate = false );


		// ------------------- Variables ------------------------

		Asura_MultiStream_Custom_Stream*	m_pxStream;

		int									m_iSampleFormat;
		int									m_iSampleRate;
};

//_________________________________________________________________________________________________________

class Asura_MultiStream_Submix_Voice : public Asura_MultiStream_Voice
{
	friend class Asura_MultiStream_Audio;

	public:

		// ------------------- Functions ------------------------

		Asura_MultiStream_Submix_Voice			( );
		virtual	~Asura_MultiStream_Submix_Voice	( )												{ Asura_Assert( false, "Should not get here" );	}

		virtual void			Start			( )												{ Asura_Assert( false, "Do not call 'Start' on submix voices"); }
		virtual void			Prepare			( );

	protected:

		// ------------------- Functions ------------------------

		virtual void			Update					( const bool bFirstUpdate = false );


		// ------------------- Variables ------------------------

		
};


#endif	//ASURA_USE_AUDIO_SYSTEM

#endif	//__ASURA_PS3_MULTISTREAM_VOICE_H_

//eof
