//===========================================================================//
// File:	l4audio.hh                                                          //
// Project: MUNGA           Brick: Audio Renderer									  //
// Contents: 																					  //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 01/30/95  ECH  Initial coding.                                            //
//---------------------------------------------------------------------------//
// Copyright (C) 1994-1995, Virtual World Entertainment, Inc.                //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#if !defined(L4AUDIO_HPP)
#	define L4AUDIO_HPP

#	if !defined(L4AUDHDW_HPP)
#		include <l4audhdw.hpp>
#	endif

#	if !defined(AUDIO_HPP)
#		include <audio.hpp>
#	endif

#	if !defined(AUDLOC_HPP)
#		include <audloc.hpp>
#	endif

#	if !defined(AUDSRC_HPP)
#		include <audsrc.hpp>
#	endif

	//##########################################################################
	//#######################    L4AudioSpatialization    ######################
	//##########################################################################

	class L4AudioSpatialization SIGNATURED
   {
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Public types
	//
   public:
		enum Quadrant
		{
			Quadrant1,
			Quadrant2,
			Quadrant3,
			Quadrant4
		};

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Contstruction, Destruction, & Testing
	//
   public:
   	L4AudioSpatialization();
      ~L4AudioSpatialization();

      Logical
      	TestInstance() const;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Spatialization calculations & results
	//
   public:
      void
      	CalculateSpatialization(Radian azimuth_of_source);

      Radian
      	azimuthOfSource;

		Scalar
      	frontLeftScale,
      	frontRightScale,
      	rearLeftScale,
      	rearRightScale;

      Scalar
			frontLeftDelay,
			frontRightDelay,
			rearLeftDelay,
			rearRightDelay;

      Quadrant
      	quadrant;
   };

	//~~~~~~~~~~~~~~~~~~~~~~~ L4AudioSpatialization inlines ~~~~~~~~~~~~~~~~~~~~

	inline MemoryStream&
		MemoryStream_Read(
			MemoryStream* stream,
			L4AudioSpatialization::Quadrant *ptr
		)
	{
		return stream->ReadBytes(ptr, sizeof(*ptr));
	}
	
	inline MemoryStream&
		MemoryStream_Write(
			MemoryStream* stream,
			const L4AudioSpatialization::Quadrant *ptr
		)
	{
		return stream->WriteBytes(ptr, sizeof(*ptr));
	}

	//##########################################################################
	//#########################    L4AudioLocation    ##########################
	//##########################################################################

	class L4AudioLocation:
		public AudioLocation
	{
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Construction, Destruction, Testing
	//
	public:
		L4AudioLocation(
      	PlugStream *stream,
         Entity *entity
      );
      void
         L4AudioLocationX();
		~L4AudioLocation();

		Logical
			TestInstance() const;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Spatialization Accessors
	//
	public:
		void
			GetSpatialization(L4AudioSpatialization *the_spatialization)
				{*the_spatialization = spatialization;}

		L4AudioSpatialization::Quadrant
			GetQuadrant()
				{return spatialization.quadrant;}

		Scalar
			GetFrontLeftScale()
				{return spatialization.frontLeftScale;}
		Scalar
			GetFrontRightScale()
         	{return spatialization.frontRightScale;}
		Scalar
      	GetRearLeftScale()
         	{return spatialization.rearLeftScale;}
		Scalar
      	GetRearRightScale()
         	{return spatialization.rearRightScale;}

		Scalar
      	GetFrontLeftITDPitchOffset()
				{return frontLeftITDPitchOffset;}
		Scalar
      	GetFrontRightITDPitchOffset()
         	{return frontRightITDPitchOffset;}
		Scalar
      	GetRearLeftITDPitchOffset()
         	{return rearLeftITDPitchOffset;}
		Scalar
      	GetRearRightITDPitchOffset()
				{return rearRightITDPitchOffset;}

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Spatialization Implementation
	//
	public:
		void
			UpdateSpatialModelImplementation(AudioHead *audio_head);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Private data
	//
	private:
		L4AudioSpatialization
			spatialization;

		Time
      	lastITDFrameTime;

      Scalar
			currentFrontLeftDelay,
			currentFrontRightDelay,
			currentRearLeftDelay,
			currentRearRightDelay;

      AudioPitchCents
         frontLeftITDPitchOffset,
         frontRightITDPitchOffset,
         rearLeftITDPitchOffset,
         rearRightITDPitchOffset;
	};

	//##########################################################################
	//#########################    AudioChannelSet    ##########################
	//##########################################################################

	#define AUDIO_CHANNEL_SET_LENGTH (4)

	typedef int AudioChannelSetIndex;
	const AudioChannelSetIndex AudioChannelSetSize = AUDIO_CHANNEL_SET_LENGTH;

	class AudioChannelSet SIGNATURED
	{
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Construction, Destruction, & Testing
	//
	public:
		AudioChannelSet();
		~AudioChannelSet() {}

		Logical
			TestInstance() const;

   	static void
      	BuildFromPage(
         	PlugStream *stream,
         	NameList *name_list,
            RegisteredClass::ClassID class_ID,
            ObjectID object_ID
         );

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Channel accesors
	//
	public:
		void
			SetFrontLeft(AudioChannel *audio_channel);
		void
			SetFrontRight(AudioChannel *audio_channel);
		void
			SetRearLeft(AudioChannel *audio_channel);
		void
			SetRearRight(AudioChannel *audio_channel);

		AudioChannel*
			GetFrontLeft();
		AudioChannel*
			GetFrontRight();
		AudioChannel*
			GetRearLeft();
		AudioChannel*
			GetRearRight();

		void
			SetNth(
				AudioChannelSetIndex index,
				AudioChannel *audio_channel
			);
		AudioChannel*
			GetNth(AudioChannelSetIndex index);

		void
			ReleaseAll();

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Enable accesors
	//
	public:
		void
			EnableFrontLeft(Logical flag);
		void
			EnableFrontRight(Logical flag);
		void
			EnableRearLeft(Logical flag);
		void
			EnableRearRight(Logical flag);

		Logical
			IsFrontLeftEnabled();
		Logical
			IsFrontRightEnabled();
		Logical
			IsRearLeftEnabled();
		Logical
			IsRearRightEnabled();

		void
			EnableNth(
				AudioChannelSetIndex index,
				Logical flag
			);
		void
      	EnableAll();
		Logical
			IsNthEnabled(AudioChannelSetIndex index);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Null channel set
	//
	public:
		static AudioChannelSet
			Null;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Private data
	//
	private:
		AudioChannel
			*channels[AUDIO_CHANNEL_SET_LENGTH];
		Logical
			enables[AUDIO_CHANNEL_SET_LENGTH];
	};

	//##########################################################################
	//##########################    L4AudioSource    ###########################
	//##########################################################################

	class L4AudioSource:
		public AudioSource
	{
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Destruction, Testing
	//
	public:
		~L4AudioSource();

		Logical
			TestInstance() const;

      static void
         BuildFromPage(
            PlugStream *stream,
            NameList *name_list,
            ClassID class_ID,
            ObjectID object_ID
			);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// AudioChannelSet methods
	//
	public:
		void
			SetAudioChannelSet(const AudioChannelSet &audio_channel_set);
		const AudioChannelSet*
			GetAudioChannelSet();

		void
			ReleaseChannels();

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Protected Constuction
	//
	protected:
		L4AudioSource(
			PlugStream *stream,
			Entity *entity
		);
		void
			L4AudioSourceX();

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Protected Implementations
	//
	protected:
		void
			StopMessageImplementation();

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Protected Data
	//
	protected:
		AudioChannelSet
			channelSet;
	};

	//##########################################################################
	//########################    DirectPatchSource    #########################
	//##########################################################################

	enum DirectPatchPosition
	{
		FrontDirectPatchPosition = 0,
		RearDirectPatchPosition = 1,
		FrontLeftDirectPatchPosition = 2,
		FrontRightDirectPatchPosition = 3,
		RearLeftDirectPatchPosition = 4,
		RearRightDirectPatchPosition = 5
	};

	class DirectPatchSource:
		public L4AudioSource
	{
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Construction, Destruction, Testing
	//
	public:
		DirectPatchSource(
			PlugStream *stream,
			Entity *entity
		);
		~DirectPatchSource();

		Logical
			TestInstance() const;

		static void
      	BuildFromPage(
				PlugStream *stream,
         	NameList *name_list,
				ClassID class_ID,
            ObjectID object_ID
			);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// State implementations
	//
	public:
		void
			StartImplementation();
		void
			StopImplementation();

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Protected Implementations
	//
	protected:
		void
			StartMessageImplementation();

		void
      	ExecuteModel(Logical force_update);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Private data
	//
	private:
		DirectPatchPosition
			audioPosition;
		AudioChannel
			*channel;

		MIDIValue
			lastMIDIVolume;
      MIDIPitchBend
			lastMIDIPitchBend;
      MIDINRPNValue
			lastMIDIFilterCutoff;
	};

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~ DirectPatchSource inlines ~~~~~~~~~~~~~~~~~~~~

	inline MemoryStream&
		MemoryStream_Read(
			MemoryStream *stream,
			DirectPatchPosition *ptr
		)
	{
		return stream->ReadBytes(ptr, sizeof(*ptr));
	}
		
	inline MemoryStream&
		MemoryStream_Write(
			MemoryStream *stream,
			const DirectPatchPosition *ptr
		)
	{
		return stream->WriteBytes(ptr, sizeof(*ptr));
	}

	//##########################################################################
	//######################    Dynamic3DPatchSource    ########################
	//##########################################################################

	class Dynamic3DPatchSource:
		public L4AudioSource
	{
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Construction, Destruction, Testing
	//
	public:
		Dynamic3DPatchSource(
			PlugStream *stream,
         Entity *entity
		);
		~Dynamic3DPatchSource();

		Logical
			TestInstance() const;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Mix levels
	//
	public:
      AudioControlValue
      	CalculateSourceVolumeScale();

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// State implementations
	//
	public:
		void
			StartImplementation();
		void
			StopImplementation();

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Protected implementations
	//
	protected:
		void
			StartMessageImplementation();

		void
			ExecuteModel(Logical force_update);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Private data
	//
	private:
      MIDIValue
      	lastFrontLeftMIDIVolume,
			lastFrontRightMIDIVolume,
      	lastRearLeftMIDIVolume,
      	lastRearRightMIDIVolume;

      MIDIPitchBend
      	lastFrontLeftMIDIPitchBend,
      	lastFrontRightMIDIPitchBend,
      	lastRearLeftMIDIPitchBend,
      	lastRearRightMIDIPitchBend;

      MIDINRPNValue
      	lastMIDIFilterCutoff;
	};

	//##########################################################################
	//#######################    Static3DPatchSource    ########################
	//##########################################################################

	class Static3DPatchSource:
		public L4AudioSource
	{
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Construction, Destruction, Testing
	//
	public:
		Static3DPatchSource(
			PlugStream *stream,
         Entity *entity
		);
		~Static3DPatchSource();

		Logical
			TestInstance() const;

		static void
      	BuildFromPage(
				PlugStream *stream,
         	NameList *name_list,
				ClassID class_ID,
            ObjectID object_ID
			);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// SetPosition
	//
	public:
      void
      	SetPosition(const Vector3D &vector3D);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// State implementations
	//
	public:
		void
			StartImplementation();
		void
			StopImplementation();

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Protected implementations
	//
	protected:
		void
			StartMessageImplementation();

		void
			ExecuteModel(Logical force_update);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Private Methods
	//
	private:
		L4AudioSpatialization::Quadrant
			GetQuadrant();
		Radian
			GetAzimuthOfSource();

		Scalar
			GetFrontLeftScale();
		Scalar
			GetFrontRightScale();
		Scalar
			GetRearLeftScale();
		Scalar
			GetRearRightScale();

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Private data
	//
	private:
      Vector3D
      	position;

		Logical
			useInternalSpatialization;
		L4AudioSpatialization
			internalSpatialization;

      MIDIValue
      	lastFrontLeftMIDIVolume,
      	lastFrontRightMIDIVolume,
      	lastRearLeftMIDIVolume,
      	lastRearRightMIDIVolume;

      MIDIPitchBend
      	lastMIDIPitchBend;

      MIDINRPNValue
      	lastMIDIFilterCutoff;
	};

	//~~~~~~~~~~~~~~~~~~~~~~~~ Static3DPatchSource inlines ~~~~~~~~~~~~~~~~~~~~~

	inline L4AudioSpatialization::Quadrant
		Static3DPatchSource::GetQuadrant()
	{
		return internalSpatialization.quadrant;
	}

	inline Radian
		Static3DPatchSource::GetAzimuthOfSource()
	{
		return internalSpatialization.azimuthOfSource;
	}

	inline Scalar
		Static3DPatchSource::GetFrontLeftScale()
	{
		return internalSpatialization.frontLeftScale;
	}

	inline Scalar
		Static3DPatchSource::GetFrontRightScale()
	{
		return internalSpatialization.frontRightScale;
	}

	inline Scalar
		Static3DPatchSource::GetRearLeftScale()
	{
		return internalSpatialization.rearLeftScale;
	}

	inline Scalar
		Static3DPatchSource::GetRearRightScale()
	{
		return internalSpatialization.rearRightScale;
	}

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioChannelSet inlines ~~~~~~~~~~~~~~~~~~~~~~

	inline void
		AudioChannelSet::SetFrontLeft(AudioChannel *audio_channel)
	{
		Check(this);
		channels[0] = audio_channel;
	}

	inline void
		AudioChannelSet::SetFrontRight(AudioChannel *audio_channel)
	{
		Check(this);
		channels[1] = audio_channel;
	}

	inline void
		AudioChannelSet::SetRearLeft(AudioChannel *audio_channel)
	{
		Check(this);
		channels[2] = audio_channel;
	}

	inline void
		AudioChannelSet::SetRearRight(AudioChannel *audio_channel)
	{
		Check(this);
		channels[3] = audio_channel;
	}

	inline AudioChannel*
		AudioChannelSet::GetFrontLeft()
	{
		Check(this);
		return channels[0];
	}

	inline AudioChannel*
		AudioChannelSet::GetFrontRight()
	{
		Check(this);
		return channels[1];
	}

	inline AudioChannel*
		AudioChannelSet::GetRearLeft()
	{
		Check(this);
		return channels[2];
	}

	inline AudioChannel*
		AudioChannelSet::GetRearRight()
	{
		Check(this);
		return channels[3];
	}

	inline void
		AudioChannelSet::SetNth(
			AudioChannelSetIndex index,
			AudioChannel *audio_channel
		)
	{
		Check(this);
		Verify(index >=0 && index < AUDIO_CHANNEL_SET_LENGTH);
		channels[index] = audio_channel;
	}

	inline AudioChannel*
		AudioChannelSet::GetNth(AudioChannelSetIndex index)
	{
		Check(this);
		Verify(index >=0 && index < AUDIO_CHANNEL_SET_LENGTH);
		return channels[index];
	}

	inline void
		AudioChannelSet::EnableFrontLeft(Logical flag)
	{
		Check(this);
		enables[0] = flag;
	}

	inline void
		AudioChannelSet::EnableFrontRight(Logical flag)
	{
		Check(this);
		enables[1] = flag;
	}

	inline void
		AudioChannelSet::EnableRearLeft(Logical flag)
	{
		Check(this);
		enables[2] = flag;
	}

	inline void
		AudioChannelSet::EnableRearRight(Logical flag)
	{
		Check(this);
		enables[3] = flag;
	}

	inline Logical
		AudioChannelSet::IsFrontLeftEnabled()
	{
		Check(this);
		return enables[0];
	}

	inline Logical
		AudioChannelSet::IsFrontRightEnabled()
	{
		Check(this);
		return enables[1];
	}

	inline Logical
		AudioChannelSet::IsRearLeftEnabled()
	{
		Check(this);
		return enables[2];
	}

	inline Logical
		AudioChannelSet::IsRearRightEnabled()
	{
		Check(this);
		return enables[3];
	}

	inline void
		AudioChannelSet::EnableNth(
			AudioChannelSetIndex index,
			Logical flag
		)
	{
		Check(this);
		Verify(index >=0 && index < AUDIO_CHANNEL_SET_LENGTH);
		enables[index] = flag;
	}

	inline Logical
		AudioChannelSet::IsNthEnabled(AudioChannelSetIndex index)
	{
		Check(this);
		Verify(index >=0 && index < AUDIO_CHANNEL_SET_LENGTH);
		return enables[index];
	}

#endif

