//////////////////////////////////////////////////////////////////////////////
//
// Asura_Sound_Mixer.h
//
// Copyright (c) 2006 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

// B E G I N   F I L E   W R A P P E R ///////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef ___ASURA_SOUND_MIXER_H___
#define ___ASURA_SOUND_MIXER_H___ 1

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

// for surround pan definition and ASURA_SOUND_INCLUDE_TOOLS_DATA definition
#include "Sound/Asura_Sound_Event.h"
#include "ProjectSettings/Asura_EngineConfiguration.h"

// F O R W A R D   D E C L A R A T I O N S ///////////////////////////////////
class Asura_Sound_Mixer;
class Asura_Sound_Mixer_Channel;
class Asura_Sound_Mixer_Channel_Settings;
class Asura_Sound_Mixer_Preset;

// D E F I N E S /////////////////////////////////////////////////////////////
static const Asura_Hash_ID uMIXER_HASHID_MASTER = 0xBF8D2A62; //Asura_GetHashID("Master")
static const Asura_Hash_ID uMIXER_HASHID_MUSIC = 0x0636EE25; //Asura_GetHashID("Music")
static const Asura_Hash_ID uMIXER_HASHID_STREAMINGSOUNDS = 0x248FD6C2; //Asura_GetHashID("Streaming Sounds")
static const Asura_Hash_ID uMIXER_HASHID_CUTSCENES = 0x92934229; //Asura_GetHashID("Cutscenes")
static const Asura_Hash_ID uMIXER_HASHID_DEFAULT = 0x5C13D641; //Asrua_GetHashID("Default")

// Maximum number of presets which can be queued
#ifndef ASURA_SOUND_MIXER_LIST_SIZE
#define ASURA_SOUND_MIXER_LIST_SIZE 6
#endif

// Font face used to render debugging text
#ifndef ASURA_SOUND_MIXER_FONT_FACE
#define ASURA_SOUND_MIXER_FONT_FACE "\\fonts\\Arial_9"
#endif

// Scale to apply to font for debugging text
#ifndef ASURA_SOUND_MIXER_FONT_SCALE
#define ASURA_SOUND_MIXER_FONT_SCALE 1.0f
#endif

// Top pixel on screen of debugging text (higher values move debugging down screen)
#ifndef ASURA_SOUND_MIXER_RENDER_TOP
#define ASURA_SOUND_MIXER_RENDER_TOP 25.0f
#endif

// Width of screen in pixels allowed for debugging text (higher values move debugging left)
#ifndef ASURA_SOUND_MIXER_RENDER_WIDTH
#define ASURA_SOUND_MIXER_RENDER_WIDTH 250.0f
#endif

// Which mixer preset system the project will use, when unset the system will be defined on first use of
// an old/new system function. This should really be defined for all projects.
#ifndef ASURA_SOUND_MIXER_PRESET_SYSTEM
#define ASURA_SOUND_MIXER_PRESET_SYSTEM ASURA_SOUND_MIXER_PRESET_SYSTEM_UNSET
#endif

// This can be overridden to allow debugging to be displayed in other types of build (i.e. retail)
#if defined(_DEBUG) || defined(ASURA_TOOLS)
#ifndef ASURA_SOUND_MIXER_RENDER_DEBUG
#define ASURA_SOUND_MIXER_RENDER_DEBUG				(1)
#endif // #ifndef ASURA_SOUND_MIXER_RENDER_DEBUG
#endif // #if defined(_DEBUG) || defined(ASURA_TOOLS)

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

// E N U M E R A T I O N S ///////////////////////////////////////////////////

enum ASURA_SOUND_MIXER_PRESET_SYSTEM_TYPE
{
	ASURA_SOUND_MIXER_PRESET_SYSTEM_UNSET,
	ASURA_SOUND_MIXER_PRESET_SYSTEM_OLD,
	ASURA_SOUND_MIXER_PRESET_SYSTEM_NEW,
};

// C L A S S E S /////////////////////////////////////////////////////////////
class Asura_Sound_Mixer_System
{
	friend class Asura_Sound_Mixer;
	friend class Asura_Sound_Mixer_Channel;
	friend class Asura_Chunk_SoundEvent_Mixer;

public:
	static void Initialise();
	// call Update once per frame to set the actual runtime values for each channel.
	static void Update();
	static void Reset();
	static void Shutdown();

#ifdef ASURA_SOUND_MIXER_RENDER_DEBUG
	static void	DebuggingRender();
	//Allow app to override font and scale of default Debugging Font.
	static Asura_Handle GetDebugFontHandle() {return m_uDebugFontHandle;}
	static void SetDebugFontHandle(Asura_Handle uHandle) {m_uDebugFontHandle = uHandle;}
	static float GetDebugFontScale(){return m_fDebugFontScale;}
	static void SetDebugFontScale(float fScale){m_fDebugFontScale = fScale;}
private:
	static Asura_Handle		m_uDebugFontHandle;
	static float			m_fDebugFontScale;
public:
#else
	static void DebuggingRender() {}
#endif

	static Asura_Sound_Mixer_Channel* FindChannel(Asura_Hash_ID uHashID) {return FindChannelInternal(uHashID);}	// TODO make const
	static const Asura_Sound_Mixer_Preset* FindPreset(Asura_Hash_ID uHashID) {return FindPresetInternal(uHashID);}

	// SetPreset sets the channel settings from the preset with the given hash ID. 
	// It returns true iff the preset was found.
	static bool SetPreset(Asura_Hash_ID uHashID, float fFadeTime = 0.0f);

	static bool QueuePreset( Asura_Hash_ID uPreset, float fFadeInTimeOverride = -1.0f );
	static bool UnqueuePreset( Asura_Hash_ID uPreset, float fFadeOutTimeOverride = -1.0f );

	// IsIgnoreGameSpeedPitch() returns ignore game speed pitch flag (false if channel is NULL)
	static bool IsIgnoreGameSpeedPitch( const Asura_Sound_Mixer_Channel* const pxChannel );
	static bool GetMixerUseReverb( const Asura_Sound_Mixer_Channel* const pxChannel );
	static bool GetMixerUseDistanceFilter( const Asura_Sound_Mixer_Channel* const pxChannel );
	static bool GetMixerUseDoppler( const Asura_Sound_Mixer_Channel* const pxChannel );
	static bool GetMixerUseOcclusion( const Asura_Sound_Mixer_Channel* const pxChannel );
	static bool GetMixerUseMasterDSP( const Asura_Sound_Mixer_Channel* const pxChannel );

	// GetMixerVolume() returns master volume if channel is NULL
	static float GetMixerVolume( const Asura_Sound_Mixer_Channel* const pxChannel );

	static void WriteToStream	(Asura_Chunk_Stream& xStream);
	static void ReadFromStream	(Asura_Chunk_Stream& xStream);

#ifdef ASURA_TOOLS
	//Tools accessors for the channels
	//Note that new channels aren't added to the end of the 'indexed' list , so
	//it isn't safe to store an index value for any length of time.
	//RAR 17/07/2006
	static u_int GetNumberOfChannels();
	static Asura_Sound_Mixer_Channel* GetChannelByHash(Asura_Hash_ID uHashID) {return FindChannelInternal(uHashID);}
	static Asura_Sound_Mixer_Channel* GetChannelByIndex(u_int uIndex);

	static u_int GetNumberOfPresets();
	static Asura_Sound_Mixer_Preset* GetPresetByHash(Asura_Hash_ID uHashID) {return FindPresetInternal(uHashID);}
	static Asura_Sound_Mixer_Preset* GetPresetByIndex(u_int uIndex);

	static bool RenameChannel( const char* const szOldName, const char* const szNewName );
	//For muting the channels in tools builds. The hash ID of 'Master' can be used
	//to control the fake master channel
	static void SetToolsMute(Asura_Hash_ID uHashID, bool bMute);

#endif

	static void MuteMaster();	
	static void UnmuteMaster();
	static bool IsMasterMuted()	{	return s_bMuteMaster;	}

	static bool MixerChangedThisFrame() { return s_bMixerChangedThisFrame; }

private:
	static Asura_Sound_Mixer_Channel* FindChannelInternal(Asura_Hash_ID uHashID);
	static Asura_Sound_Mixer_Preset* FindPresetInternal(Asura_Hash_ID uHashID);
	static bool SetPresetInternal(Asura_Hash_ID uHashID, float fFadeTime);

	// private functions for friend access
	static void AddSolo();
	static void RemoveSolo();

	static void AddChannelToTree( Asura_Sound_Mixer_Channel& pxChannel );
	static void RemoveChannelFromTree( Asura_Sound_Mixer_Channel& pxChannel );

	static void SetChanged();

	//tree traversal callbacks
	static void DeleteChannel(Asura_Hash_ID uHash,Asura_Sound_Mixer_Channel** ppxChannel);
	static void DeletePreset(Asura_Hash_ID uHash, Asura_Sound_Mixer_Preset** ppxPreset);

#ifdef ASURA_SOUND_MIXER_RENDER_DEBUG
	static bool ChannelTreeCallback(Asura_Hash_ID uHash, Asura_Sound_Mixer_Channel** ppxChannel, void* pUserData);
#endif

#ifdef ASURA_ASSERT
	static bool CheckSystem( ASURA_SOUND_MIXER_PRESET_SYSTEM_TYPE eRequiredSystem );
#endif //__#ifdef ASURA_ASSERT__

	// private static data
	static Asura_Sound_Mixer* s_pxInstance;
	#ifdef ASURA_TOOLS
	static bool s_bIsLoading; //set while loading a mixer chunk
	#endif

	static ASURA_SOUND_MIXER_PRESET_SYSTEM_TYPE s_eMixerPresetSystem;

	class PresetListItem
	{
	public:
		PresetListItem() { Reset(); }
		void Reset() { m_uHashID = ASURA_HASH_ID_UNSET; m_fExpiryTime = 0.0f; }

		Asura_Hash_ID m_uHashID;
		float m_fExpiryTime;
	};

	static PresetListItem* s_pxTopPreset;
	static PresetListItem s_axPresetList[ASURA_SOUND_MIXER_LIST_SIZE];

#ifdef ASURA_SOUND_MIXER_RENDER_DEBUG
	static bool s_bRenderDebug;
	static Asura_Sound_Mixer_Channel** s_ppxChannelList;
	static u_int s_uChannelListSize;
#endif //__#ifdef ASURA_SOUND_MIXER_RENDER_DEBUG__

	static bool s_bMuteMaster;

	static bool s_bMixerChangedThisFrame;
};


class Asura_Sound_Mixer
{
	friend class Asura_Chunk_SoundEvent_Mixer;

public:

	Asura_Sound_Mixer();

	// call Update once per frame to set the actual runtime values for each channel.
	void Update();
	void Reset();

	void AddSolo();
	void RemoveSolo();

	// GetMixerVolume() returns master volume if channel is NULL
	float GetMixerVolume( const Asura_Sound_Mixer_Channel* const pxChannel );

	// IsIgnoreGameSpeedPitch() returns ignore game speed pitch flag (false if channel is NULL)
	bool IsIgnoreGameSpeedPitch( const Asura_Sound_Mixer_Channel* const pxChannel );
	bool GetUseDistanceFilter( const Asura_Sound_Mixer_Channel* const pxChannel );
	bool GetUseReverb( const Asura_Sound_Mixer_Channel* const pxChannel );
	bool GetUseDoppler( const Asura_Sound_Mixer_Channel* const pxChannel );
	bool GetUseOcclusion( const Asura_Sound_Mixer_Channel* const pxChannel );
	bool GetUseMasterDSP( const Asura_Sound_Mixer_Channel* const pxChannel );

	void SetChanged() { m_bChanged = true; }

#ifdef ASURA_TOOLS
	void AddChannel( Asura_Sound_Mixer_Channel* pxChannel );
	void RemoveChannel( Asura_Sound_Mixer_Channel* pxChannel );

	void SetToolsMute(bool bMute);
#endif
	u_int GetNumChannels() const { return m_xChannelsArray.Size(); }
	const Asura_Sound_Mixer_Channel* GetChannel( u_int uChannel ) const { return m_xChannelsArray[uChannel]; }
	Asura_Sound_Mixer_Channel* GetChannel( u_int uChannel ) { return m_xChannelsArray[uChannel]; }

	bool MixerChangedThisFrame () const	{ return m_bMixerUpdatedThisFrame; }

private:

	// array is only resized or reordered in tools
	Asura_HeapArray<Asura_Sound_Mixer_Channel*> m_xChannelsArray;

	float m_fMasterVolume;
	u_int m_uNumSolo;
	u_int m_uUpdateFrameStamp;
	bool m_bChanged;

	#ifdef ASURA_TOOLS
	bool m_bToolsMute;
	#endif

	bool m_bMixerUpdatedThisFrame;
};

class Asura_Sound_Mixer_Channel_Settings
{
public:
	
	Asura_Sound_Mixer_Channel_Settings();

	// user settings (ones exposed in the GUI)
	float				m_fVolume;
	Asura_SurroundPan	m_xSurroundPan;

	bool				m_bPanOverride;				// all inputs use this pan rather than it being a modifier
	bool				m_bMute;					// mute this channel
	bool				m_bSolo;					// only play this channel (and any others with solo set)
	// JAT 26/02/07 
	bool				m_bIgnoreGameSpeedPitch;	// all inputs ignore any pitch changes because of game speed variations

	bool				m_bUseReverb;
	bool				m_bUseDistanceFilter;
	bool				m_bUseDoppler;
	bool				m_bUseOcclusion;
	bool				m_bUseMasterDSP;
};

class Asura_Sound_Mixer_Channel
{
	friend class Asura_Sound_Mixer;
	friend class Asura_Sound_Mixer_System;
	friend class Asura_Chunk_SoundEvent_Mixer;

public:

	Asura_Sound_Mixer_Channel(const char* szName);// name must be unique
	// the destructor needs to check pointer references by going through the trees
	// of sound events and of mixer channels and checking the stored pointers, warning 
	// if any are set to this channel and then clearing them.
	~Asura_Sound_Mixer_Channel();

	// GetCurrentVolume and GetCurrentPan() return or fill out current runtime values. 
	float GetCurrentVolume() const
	{
		return m_fCurrentVolume;
	}
	void GetCurrentPan(Asura_SurroundPan& xSurroundPanOut) const
	{
		xSurroundPanOut = m_xSettings.m_xSurroundPan;
	}

	// GetVolume and GetPan() return or fill out stored values as displayed in GUI. 
	float GetVolume() const
	{
		return m_xSettings.m_fVolume;
	}
	void GetPan(Asura_SurroundPan& xSurroundPan) const
	{
		xSurroundPan = m_xSettings.m_xSurroundPan;
	}

	void SetVolume( float fVolume )
	{
		m_xSettings.m_fVolume = fVolume;
		m_fFadeEndVolume = fVolume;
		m_fVolumeFadeTimer = 1.0f;
		Asura_Sound_Mixer_System::SetChanged();
	}
	void SetPan(const Asura_SurroundPan& xSurroundPan)
	{
		m_xSettings.m_xSurroundPan = xSurroundPan;
		Asura_Sound_Mixer_System::SetChanged();
	}
	void SetSolo( bool bIsSolo );
	void SetMute( bool bIsMute )
	{ 
		m_xSettings.m_bMute = bIsMute; 
		m_bFadeToMute = bIsMute;
		m_fMuteFadeTimer = bIsMute ? 0.0f : 1.0f;
		Asura_Sound_Mixer_System::SetChanged();
	}

	#ifdef ASURA_TOOLS
	void SetToolsMute(bool bMute)
	{
		m_bToolsMute = bMute;
		Asura_Sound_Mixer_System::SetChanged();
	}
	#endif

	void SetPanOverride( bool bSet )
	{
		m_xSettings.m_bPanOverride = bSet;
		Asura_Sound_Mixer_System::SetChanged();
	}

	// JAT 26/02/07 ignore game speed pitch setting
	void SetIgnoreGameSpeedPitch( const bool bIgnore )
	{
		m_xSettings.m_bIgnoreGameSpeedPitch = bIgnore;
		Asura_Sound_Mixer_System::SetChanged();
	}

	void SetUseReverb( const bool bUseReverb )
	{ 
		m_xSettings.m_bUseReverb = bUseReverb; 
		Asura_Sound_Mixer_System::SetChanged();
	}

	void SetUseDistanceFilter( const bool bUseDistanceFilter )
	{ 
		m_xSettings.m_bUseDistanceFilter = bUseDistanceFilter;
		Asura_Sound_Mixer_System::SetChanged();
	}

	void SetUseDoppler( const bool bUseDoppler )
	{ 
		m_xSettings.m_bUseDoppler = bUseDoppler; 
		Asura_Sound_Mixer_System::SetChanged();
	}

	void SetUseOcclusion( const bool bUseOcclusion )
	{ 
		m_xSettings.m_bUseOcclusion = bUseOcclusion;
		Asura_Sound_Mixer_System::SetChanged();
	}

	bool IsSolo() const { return m_xSettings.m_bSolo; }
	#ifdef ASURA_TOOLS
	bool IsMute() const { return m_xSettings.m_bMute || m_bToolsMute; }
	#else
	bool IsMute() const { return m_xSettings.m_bMute; }
	#endif
	bool IsPanOverride() const {return m_xSettings.m_bPanOverride;}

	// JAT 26/02/07 ignore game speed pitch setting
	bool IsIgnoreGameSpeedPitch() const
	{ 
		if ( m_pxOutput )
		{
			return m_xSettings.m_bIgnoreGameSpeedPitch || m_pxOutput->IsIgnoreGameSpeedPitch();
		}

		return m_xSettings.m_bIgnoreGameSpeedPitch;
	}

	bool GetUseReverb() const
	{
		if ( m_pxOutput )
		{
			return m_xSettings.m_bUseReverb && m_pxOutput->GetUseReverb();
		}

		return m_xSettings.m_bUseReverb;
	}

	bool GetUseDistanceFilter() const
	{ 
		if ( m_pxOutput )
		{
			return m_xSettings.m_bUseDistanceFilter && m_pxOutput->GetUseDistanceFilter();
		}

		return m_xSettings.m_bUseDistanceFilter; 
	}

	bool GetUseDoppler() const
	{ 
		if ( m_pxOutput )
		{
			return m_xSettings.m_bUseDoppler && m_pxOutput->GetUseDoppler();
		}

		return m_xSettings.m_bUseDoppler; 
	}

	bool GetUseOcclusion() const
	{ 
		if ( m_pxOutput )
		{
			return m_xSettings.m_bUseOcclusion && m_pxOutput->GetUseOcclusion();
		}

		return m_xSettings.m_bUseOcclusion; 
	}

	bool GetUseMasterDSP() const
	{
		if ( m_pxOutput )
		{
			return m_xSettings.m_bUseMasterDSP && m_pxOutput->GetUseMasterDSP();
		}

		return m_xSettings.m_bUseMasterDSP;
	}

	const Asura_Sound_Mixer_Channel_Settings& GetSettings() const { return m_xSettings; }
	void Set(const Asura_Sound_Mixer_Channel_Settings& xSettings,float fFadeTime);

	Asura_Hash_ID GetHashID() const { return m_uHashID; }
	bool IsCodeMixer() const { return m_bCodeMixer; }

#if defined( ASURA_SOUND_INCLUDE_TOOLS_DATA ) || defined( ASURA_SOUND_MIXER_RENDER_DEBUG )
	const char* GetName() const {return m_xName.GetString();}
	const char* GetOutputMixerName() const {return m_xOutputMixer.GetString();}
#endif

	void SetOutputMixerName(const char* szOutputMixer);


private:
	
	// non-copyable
	Asura_Sound_Mixer_Channel(const Asura_Sound_Mixer_Channel&);
	Asura_Sound_Mixer_Channel& operator= (const Asura_Sound_Mixer_Channel&);

	// Update() uses a framestamp to see if update is needed and calls Update on output if necessary
	void Update(float fNonSoloScale, u_int uFrameStamp);
	//Upades the fade timers (done before the update in case it changes the solo status of any channels)
	void UpdateFade();

	// user settings (ones exposed in the GUI)
	Asura_Sound_Mixer_Channel_Settings m_xSettings;

	Asura_Sound_Mixer_Channel* m_pxOutput;	// NULL if master channel

	bool				m_bCodeMixer;  // is this mixer ignored by presets and only set by code? (for volume sliders).

	// runtime settings (updated once per frame taking account of linked list of outputs)
	float				m_fCurrentVolume;
	u_int				m_uUpdateFrameStamp;

	bool				m_bFadeToSolo; //target solo status for current fade
	bool				m_bFadeToMute;//target mute status for current fade

	float				m_fSoloFadeTimer;//scale value for fade from 0 to 1. Has reached solo when this hits 0
	float				m_fMuteFadeTimer;//scale value for fade from 0 to 1. Has reached mute when this is 0

	float				m_fFadeStartVolume;//start volume for fade
	float				m_fFadeEndVolume;//end volume for fade
	float				m_fVolumeFadeTimer;//scale value for fade from 0 to 1. Has reached target at 1.

	float				m_fFadeDuration;//duration of fade in seconds

#ifdef ASURA_TOOLS
	bool				m_bToolsMute;//Extra setting for forcing the mixer to be muted while in tools builds
#endif

	// tools data
#if defined( ASURA_SOUND_INCLUDE_TOOLS_DATA ) || defined( ASURA_SOUND_MIXER_RENDER_DEBUG )
	Asura_String		m_xName;
	Asura_String		m_xOutputMixer;
#endif
	Asura_Hash_ID		m_uHashID;
};


// Asura_Sound_Mixer_ChannelPreset contains the data to set a single channel
class Asura_Sound_Mixer_ChannelPreset
{
	friend class Asura_Chunk_SoundEvent_Mixer;

public:

#ifdef ASURA_SOUND_INCLUDE_TOOLS_DATA
	Asura_Sound_Mixer_ChannelPreset( const Asura_Sound_Mixer_Channel_Settings& xSettings, const char* szChannelName );
#else
	Asura_Sound_Mixer_ChannelPreset( const Asura_Sound_Mixer_Channel_Settings& xSettings, Asura_Hash_ID uChannelHashID );
#endif

	const Asura_Sound_Mixer_Channel_Settings& GetSettings() const { return m_xSettings; }
#ifdef ASURA_TOOLS
	void SetSettings( const Asura_Sound_Mixer_Channel_Settings xSettings ) { m_xSettings = xSettings; }
#endif
	Asura_Hash_ID GetChannelHashID() const { return m_uChannelHashID; }
#if defined( ASURA_SOUND_INCLUDE_TOOLS_DATA ) || defined( ASURA_SOUND_MIXER_RENDER_DEBUG )
	const char* GetChannelName() const { return m_xChannelName.GetString(); }
#endif

#ifdef ASURA_TOOLS
	void SetChannelName(const char* szName);
#endif

private:
	
	// private ctor for chunk creation
	Asura_Sound_Mixer_ChannelPreset() {}

	Asura_Sound_Mixer_Channel_Settings m_xSettings;
	Asura_Hash_ID m_uChannelHashID;	// ID of channel these settings apply to
#if defined( ASURA_SOUND_INCLUDE_TOOLS_DATA ) || defined( ASURA_SOUND_MIXER_RENDER_DEBUG )
	Asura_String m_xChannelName;	// name of channel these settings apply to
#endif
};

// Asura_Sound_Mixer_Preset holds a set of channel presets.
class Asura_Sound_Mixer_Preset
{
	friend class Asura_Chunk_SoundEvent_Mixer;

public:

	// name must be unique
	Asura_Sound_Mixer_Preset( const char* szName );
	~Asura_Sound_Mixer_Preset();

	// Reset() clears out all the channel presets
	void Reset();

#ifdef ASURA_TOOLS
	void AddChannel( const Asura_Sound_Mixer_Channel_Settings& xSettings, const char* szChannelName );
	void RemoveChannel( Asura_Hash_ID uHashID );
	void ReplaceChannel( const char* szOldName, const char* szNewName);
	void SetPriority( u_char ucPriority ) { m_ucPriority = ucPriority; }
	void SetFadeInTime( float fFadeInTime ) { m_fFadeInTime = fFadeInTime; }
	void SetFadeOutTime( float fFadeOutTime ) { m_fFadeOutTime = fFadeOutTime; }
	void SetLifeTime( float fLifeTime ) { m_fLifeTime = fLifeTime; }
	void SetDSPChain( Asura_Hash_ID uDSPChain ) { m_uDSPChain = uDSPChain; }
	Asura_Sound_Mixer_ChannelPreset* FindChannel( const Asura_Hash_ID uHash ) const;
#endif

	// general accessors
	u_int GetNumChannels() const { return m_xChannelsArray.Size(); }
	const Asura_Sound_Mixer_ChannelPreset* GetChannel( u_int uChannel ) const { return m_xChannelsArray[uChannel]; }
	Asura_Sound_Mixer_ChannelPreset* GetChannel( u_int uChannel ) { return m_xChannelsArray[uChannel]; }
		
	Asura_Hash_ID GetHashID() const { return m_uHashID; }
	u_char GetPriority() const { return m_ucPriority; }
	float GetFadeInTime() const { return m_fFadeInTime; }
	float GetFadeOutTime() const { return m_fFadeOutTime; }
	float GetLifeTime() const { return m_fLifeTime; }

#if defined( ASURA_SOUND_INCLUDE_TOOLS_DATA ) || defined( ASURA_SOUND_MIXER_RENDER_DEBUG )
	const char* GetName() const { return m_xName.GetString(); }
#endif //  #if defined( ASURA_SOUND_INCLUDE_TOOLS_DATA ) || defined( ASURA_SOUND_MIXER_RENDER_DEBUG )

	Asura_Hash_ID GetDSPChain() const { return m_uDSPChain; }

private:
	// array is only resized or reordered in tools
	Asura_HeapArray<Asura_Sound_Mixer_ChannelPreset*> m_xChannelsArray;
	float m_fMasterVolume;	// master volume applies iff non-zero

	Asura_Hash_ID m_uHashID;	// ID of this preset
#if defined( ASURA_SOUND_INCLUDE_TOOLS_DATA ) || defined( ASURA_SOUND_MIXER_RENDER_DEBUG )
	Asura_String m_xName;		// name of this preset
#endif

	u_char	m_ucPriority;
	float	m_fFadeInTime;
	float	m_fFadeOutTime;
	float	m_fLifeTime;

	Asura_Hash_ID	m_uDSPChain;
};

class Asura_Message_SoundMixerPreset
{
public:
	Asura_Hash_ID m_uHashID;
	float m_fFadeTime;
};

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

#endif // #ifndef ___ASURA_SOUND_MIXER_H___
