//===========================================================================//
// File:	l4audhdw.hh																			  //
// Project: MUNGA           Brick: Audio Manager									  //
// Contents: Spec for audio manager														  //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 01/25/95  ECH  Initial coding.                                            //
//---------------------------------------------------------------------------//
// Copyright (C) 1994, Virtual World Entertainment, Inc.                     //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#if !defined(L4AUDHDW_HPP)
#	define L4AUDHDW_HPP

#	define AUDIO_HARDWARE

#	if !defined(STYLE_HPP)
#		include <style.hpp>
#	endif

#	ifdef AUDIO_HARDWARE
#		ifndef  _SOS_MIDI_DEFINED
#			include <sosm.h>
#		endif
#	else
		typedef Byte BYTE;
		typedef Word WORD;		
#	endif

#	if !defined(NODE_HPP)
#		include <node.hpp>
#	endif

#	if !defined(SCHAIN_HPP)
#		include <schain.hpp>
#	endif

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

	#if defined(TRACE_AUDIO_RENDERER_MIDI)
		extern BitTrace Audio_Renderer_MIDI("Audio Renderer MIDI");
		#define SET_AUDIO_RENDERER_MIDI() Audio_Renderer_MIDI.Set()
		#define CLEAR_AUDIO_RENDERER_MIDI() Audio_Renderer_MIDI.Clear()
	#else
		#define SET_AUDIO_RENDERER_MIDI()
		#define CLEAR_AUDIO_RENDERER_MIDI()
	#endif

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MIDI types ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	typedef unsigned char   MIDIStatus;
	typedef unsigned char   MIDIChannel;
	typedef unsigned char   MIDIValue;
	typedef int					MIDIPitchBend;
	typedef int					MIDINRPNValue;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MIDI status ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

   #define MIDI_CHANNEL_COUNT					(16)

	#define MIDI_NOTE_OFF_STATUS				((MIDIValue)0x8) // 8
	#define MIDI_NOTE_ON_STATUS				((MIDIValue)0x9) // 9
	#define MIDI_POLYKEY_STATUS				((MIDIValue)0xa) // 10
	#define MIDI_CONTROLLER_STATUS			((MIDIValue)0xb) // 11
	#define MIDI_PROGRAM_CHANGE_STATUS		((MIDIValue)0xc) // 12
	#define MIDI_CHANNEL_PRESSURE_STATUS	((MIDIValue)0xd) // 13
	#define MIDI_PITCH_BEND_STATUS			((MIDIValue)0xe) // 14

	//~~~~~~~~~~~~~~~~~~~~~~~~~~ MIDI controllers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	#define MIDI_MAX_CONTROL_VALUE			((MIDIValue)127)
	#define MIDI_MIN_CONTROL_VALUE			((MIDIValue)0)

   #define MIDI_LEFT_PAN_VALUE				((MIDIValue)0)
   #define MIDI_CENTER_PAN_VALUE				((MIDIValue)63)
   #define MIDI_RIGHT_PAN_VALUE				((MIDIValue)127)

   #define MIDI_VOLUME_CONTROL				((MIDIValue)7)
	#define MIDI_PAN_CONTROL					((MIDIValue)10)
   #define MIDI_REVERB_CONTROL				((MIDIValue)91)
	#define MIDI_CHORUS_CONTROL				((MIDIValue)93)
	#define MIDI_CONTROL_RESET					((MIDIValue)121)

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MIDI notes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

   #define MIDI_DEFAULT_NOTE					((MIDIValue)60)

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MIDI pitch ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

   #define MIDI_PITCH_BEND_MAX				((MIDIPitchBend)8191)
   #define MIDI_PITCH_BEND_RANGE				((MIDIValue)24)
	#define MIDI_PITCH_BEND_CENTS				(24*100)
	#define MIDI_PITCH_PER_CENTS				((Scalar)MIDI_PITCH_BEND_MAX / \
															(Scalar)MIDI_PITCH_BEND_CENTS)

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AWE NRPN ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

   #define AWE_VOL_ATTACK_TIME_NRPN			((MIDIValue)11)
   #define AWE_PITCH_NRPN						((MIDIValue)16)
   #define AWE_FILTER_CUTOFF_NRPN			((MIDIValue)21)

   #define AWE_VOL_ATTACK_TIME_RANGE		((MIDINRPNValue)5940) // 0...5.9 secs
   #define AWE_FILTER_CUTOFF_RANGE			((MIDINRPNValue)127) // 100...8000 Hz

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AWE effects ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	typedef enum {
		ChorusMIDIEffectType,
		ReverbMIDIEffectType
	} MIDIEffectType;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~ AWE resources ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	#define AWE_VOICE_COUNT						(32)
	#define AWE_CHANNEL_COUNT					(16)
   #define AWE_PERCUSSIVE_CHANNEL			(9)

	//
	// Driver type to use
	//
	#define _MIDI_DRIVER_TYPE _MIDI_AWE32

	//
	// Path to the driver file
	//
	#define _SOS_DRIVER_PATH _NULL

	//
	// AWE environment variables
	//
	#define FRONT_CARD_ENV_VAR		"AWE_FRONT"
	#define REAR_CARD_ENV_VAR		"AWE_REAR"

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MIDIMessage ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	#define MIDI_MESSAGE_SIZE (3)

	class MIDIMessage SIGNATURED
	{
		friend class AudioCard;
		friend ostream&
			operator << (
				ostream &strm,
				const MIDIMessage &midi_message
			);

	public:
		MIDIMessage(
			MIDIStatus status,
			MIDIChannel channel,
			MIDIValue byte1,
			MIDIValue byte2 = 0
		);
		~MIDIMessage() {}

		Logical
			TestInstance() const;

	private:
		MIDIValue
			message[MIDI_MESSAGE_SIZE];
	};

   inline MIDIMessage::MIDIMessage(
      MIDIStatus status,
      MIDIChannel channel,
      MIDIValue byte1,
      MIDIValue byte2
   )
	{
      message[0] = (MIDIValue)((status << 4) | channel);
      message[1] = byte1;
      message[2] = byte2;
   }

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioChannel ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	typedef enum
	{
		InstrumentAudioChannelType = 0,
		PercussionAudioChannelType
	} AudioChannelType;

	class AudioCard;

	class AudioChannel:
		public Plug
	{
	public:
		//
		//--------------------------------------------------------------------
		// Constructor, Destructor, Testing
		//--------------------------------------------------------------------
		//
		AudioChannel(
			AudioChannelType audio_channel_type,
			MIDIChannel midi_channel,
         AudioCard *audio_card
		);
		~AudioChannel();

		Logical
			TestInstance() const;

		//
		//--------------------------------------------------------------------
		// Accessors
		//--------------------------------------------------------------------
		//
		AudioChannelType
			GetAudioChannelType()
				{return audioChannelType;}
		MIDIChannel
			GetChannelNumber()
				{return midiChannel;}
		void
			SetVoicesUsed(AudioVoiceCount voices_used)
         	{voicesUsed = voices_used;}
		AudioVoiceCount
			GetVoicesUsed()
         	{return voicesUsed;}

		//
		//--------------------------------------------------------------------
		// MIDI methods
		//--------------------------------------------------------------------
		//
		void
			SendNoteOn(
				MIDIValue key,
				MIDIValue velocity
			);
		void
			SendNoteOff(
				MIDIValue key,
				MIDIValue velocity
			);
		void
			SendProgramChange(
				MIDIValue program
			);
		void
			SendController(
				MIDIValue controller,
				MIDIValue value
			);
		void
			SendPolyKeyPressure(
				MIDIValue key,
				MIDIValue value
			);
		void
			SendChannelPressure(
				MIDIValue value
			);
		void
			SendPitchBend(
				MIDIPitchBend pitch_bend
			);
      void
         SendPitchBendRange(
				MIDIValue sensitivity
         );
      void
      	SendNRPN(
				MIDIValue paramNumber,
				MIDINRPNValue value
         );
		void
			SelectEffect(
				MIDIEffectType type,
				MIDIValue effect
			);
		void
			SelectBank(
				MIDIValue bank
			);

		//
		//--------------------------------------------------------------------
		// Release
		//--------------------------------------------------------------------
		//
		void
			Release();

	private:
		//
		//--------------------------------------------------------------------
		// Private data
		//--------------------------------------------------------------------
		//
		AudioChannelType
      	audioChannelType;
		MIDIChannel
			midiChannel;
		AudioVoiceCount
			voicesUsed;
		AudioCard
			*audioCard;
	};

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioCard ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	class AudioCard:
		public Node
	{
		friend class AudioHardware;

	public:
		//
		//--------------------------------------------------------------------
		// Constructor, Destructor, Testing
		//--------------------------------------------------------------------
		//
		AudioCard();
		~AudioCard();

		Logical
			TestInstance() const;

		//
		//--------------------------------------------------------------------
		// Public methods
		//--------------------------------------------------------------------
		//
		void
			Initialize();
		void
			Close();
		void
			GetEnvironmentSettings(char *env_str);

		Logical
			InitMIDIReceive();
		void
			CloseMIDIReceive();
		Logical
			IsMIDIAvailable();
		MIDIValue
			GetMIDIByte();

		void
			InitializeChannels();
		int
			LoadSBK(const char *file_name);
		void
			ReleaseAllBanks();

		//
		//--------------------------------------------------------------------
		// MIDI methods
		//--------------------------------------------------------------------
		//
		void
			SendNoteOn(
				MIDIChannel channel,
				MIDIValue key,
				MIDIValue velocity
			);
		void
			SendNoteOff(
				MIDIChannel channel,
				MIDIValue key,
				MIDIValue velocity
			);
		void
			SendProgramChange(
				MIDIChannel channel,
				MIDIValue program
			);
		void
			SendController(
				MIDIChannel channel,
				MIDIValue controller,
				MIDIValue value
			);
		void
			SendPolyKeyPressure(
				MIDIChannel channel,
				MIDIValue key,
				MIDIValue value
			);
		void
			SendChannelPressure(
				MIDIChannel channel,
				MIDIValue value
			);
		void
         SendPitchBendRange(
            MIDIChannel channel,
            MIDIValue sensitivity
         );
		void
			SendPitchBend(
				MIDIChannel channel,
				MIDIPitchBend pitch_bend
			);
		void
			SendSysex(
				void *data,
				size_t length
			);
		void
			SendNRPN(
				MIDIChannel channel,
				MIDIValue paramNumber,
				MIDINRPNValue value
			);
		void
			SelectEffect(
				MIDIEffectType type,
				MIDIValue effect
			);
		void
			SelectBank(
				MIDIChannel channel,
				MIDIValue bank
			);
		void
			SendMIDIMessage(
				MIDIMessage *midi_message, 
				Word length
			);

		//
		//--------------------------------------------------------------------
		// Channel methods
		//--------------------------------------------------------------------
		//
		AudioChannel*
			RequestAudioChannel(AudioVoiceCount voice_count);
		void
			ReleaseAudioChannel(AudioChannel *audio_channel);

	private:
		//
		//--------------------------------------------------------------------
		// Private methods
		//--------------------------------------------------------------------
		//
		WORD
			GetHexWord(const char ** s);

		//
		//--------------------------------------------------------------------
		// Private data
		//--------------------------------------------------------------------
		//

		//
		// Card addresses
		//
		WORD srcAddx;
		WORD irqInterrupt;
		WORD dmaChannel;
		WORD mpuAddx;
		WORD emuAddx;

		//
		// Structure for the MIDI driver initialization function
		// Structure for the MIDI driver hardware
		//
		#ifdef AUDIO_HARDWARE
			_SOS_MIDI_INIT_DRIVER sSOSMIDIInitDriver;
			_SOS_MIDI_HARDWARE sSOSMIDIHardware;
		#endif

		//
		// Handle for the loaded MIDI driver
		//
		WORD wDriverHandle;

		//
		// Number of free voices
		//
		AudioVoiceCount
      	idleVoices;

		//
		// Idle channels
		//
		ChainOf<AudioChannel*>
			idleChannelSocket;
		ChainOf<AudioChannel*>
			percussionChannelSocket;
	};

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioHardware ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	class AudioHardware:
		public Node
	{
	public:
		//
		//--------------------------------------------------------------------
		// Constructor, Destructor, Testing
		//--------------------------------------------------------------------
		//
		AudioHardware();
		~AudioHardware();

		Logical
			TestInstance() const;
		static Logical
			TestClass();
		static Logical
			ProfileClass();

		//
		//--------------------------------------------------------------------
		// Public methods
		//--------------------------------------------------------------------
		//
		void
			Initialize();
		void
			Close();

		//
		//--------------------------------------------------------------------
		// Card Access
		//--------------------------------------------------------------------
		//
		AudioCard*
			GetFrontCard();

		AudioCard*
			GetRearCard();

	private:
		//
		//--------------------------------------------------------------------
		// Private methods
		//--------------------------------------------------------------------
		//

		//
		//--------------------------------------------------------------------
		// Private data
		//--------------------------------------------------------------------
		//
		AudioCard
			frontCard;
		AudioCard
			rearCard;
	};

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioCard inlines ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	inline void
		AudioCard::SendMIDIMessage(
			MIDIMessage *midi_message,
			Word length			
		)
	{
	#ifdef AUDIO_HARDWARE
		SET_AUDIO_RENDERER_MIDI();

		Check_Signature(midi_message);
		#if DEBUG_LEVEL>0
			switch (midi_message->message[0] >> 4) 
			{
			case MIDI_NOTE_ON_STATUS:
			case MIDI_NOTE_OFF_STATUS:
			case MIDI_POLYKEY_STATUS:
			case MIDI_CONTROLLER_STATUS:
			case MIDI_PITCH_BEND_STATUS:
				Verify(length == 3);
				break;

			case MIDI_PROGRAM_CHANGE_STATUS:
			case MIDI_CHANNEL_PRESSURE_STATUS:
				Verify(length == 2);
				break;
			default:
				Fail("Should never reach here");
				break;
			}
		#endif				

		#if DEBUG_LEVEL>0
			{
				Word wError = sosMIDISendMIDIData(
					wDriverHandle,
					(char*)midi_message->message,
					length
				);
				Verify(wError == _ERR_NO_ERROR);
			}
		#else
			sosMIDISendMIDIData(
				wDriverHandle,
				(char*)midi_message->message,
				length
			);
		#endif
		CLEAR_AUDIO_RENDERER_MIDI();
	#endif
	}

	//
	//--------------------------------------------------------------------
	// MIDI methods
	//--------------------------------------------------------------------
	//
	inline void
		AudioCard::SendNoteOn(
			MIDIChannel channel,
			MIDIValue key,
			MIDIValue velocity
		)
   {
      MIDIMessage message(MIDI_NOTE_ON_STATUS, channel, key, velocity);
      SendMIDIMessage(&message, 3);
   }

	inline void
		AudioCard::SendNoteOff(
			MIDIChannel channel,
			MIDIValue key,
			MIDIValue velocity
		)
   {
      MIDIMessage message(MIDI_NOTE_OFF_STATUS, channel, key, velocity);
      SendMIDIMessage(&message, 3);
   }

	inline void
		AudioCard::SendProgramChange(
			MIDIChannel channel,
			MIDIValue program
		)
   {
      MIDIMessage message(MIDI_PROGRAM_CHANGE_STATUS, channel, program);
      SendMIDIMessage(&message, 2);
   }

	inline void
		AudioCard::SendController(
			MIDIChannel channel,
			MIDIValue controller,
			MIDIValue value
		)
   {
      MIDIMessage message(MIDI_CONTROLLER_STATUS, channel, controller, value);
      SendMIDIMessage(&message, 3);
   }

	inline void
		AudioCard::SendPolyKeyPressure(
			MIDIChannel channel,
			MIDIValue key,
			MIDIValue value
		)
   {
      MIDIMessage message(MIDI_POLYKEY_STATUS, channel, key, value);
      SendMIDIMessage(&message, 3);
   }

	inline void
		AudioCard::SendChannelPressure(
			MIDIChannel channel,
			MIDIValue value
		)
   {
      MIDIMessage message(MIDI_CHANNEL_PRESSURE_STATUS, channel, value);
      SendMIDIMessage(&message, 2);
   }

	inline void
		AudioCard::SelectBank(
			MIDIChannel channel,
			MIDIValue bank
		)
   {
      SendController(channel, (MIDIValue)0, bank);
   }

	//~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioChannel inlines ~~~~~~~~~~~~~~~~~~~~~~~~~~

	//
	//--------------------------------------------------------------------
	// MIDI methods
	//--------------------------------------------------------------------
	//
	inline void
		AudioChannel::SendNoteOn(
			MIDIValue key,
			MIDIValue velocity
		)
   {
      Check(audioCard);
      audioCard->SendNoteOn(midiChannel,key,velocity);
   }

	inline void
		AudioChannel::SendNoteOff(
			MIDIValue key,
			MIDIValue velocity
		)
   {
      Check(audioCard);
      audioCard->SendNoteOff(midiChannel,key,velocity);
   }

	inline void
		AudioChannel::SendProgramChange(
			MIDIValue program
		)
   {
      Check(audioCard);
      audioCard->SendProgramChange(midiChannel,program);
   }

	inline void
		AudioChannel::SendController(
			MIDIValue controller,
			MIDIValue value
		)
   {
      Check(audioCard);
      audioCard->SendController(midiChannel,controller,value);
   }

	inline void
		AudioChannel::SendPolyKeyPressure(
			MIDIValue key,
			MIDIValue value
		)
   {
      Check(audioCard);
      audioCard->SendPolyKeyPressure(midiChannel,key,value);
   }

	inline void
		AudioChannel::SendChannelPressure(
			MIDIValue value
		)
   {
      Check(audioCard);
      audioCard->SendChannelPressure(midiChannel,value);
   }

	inline void
		AudioChannel::SendPitchBend(
			MIDIPitchBend pitch_bend
		)
   {
      Check(audioCard);
      audioCard->SendPitchBend(midiChannel,pitch_bend);
   }

   inline void
      AudioChannel::SendPitchBendRange(
         MIDIValue sensitivity
      )
   {
      Check(audioCard);
      audioCard->SendPitchBendRange(midiChannel,sensitivity);
   }

	inline void
		AudioChannel::SelectEffect(
			MIDIEffectType type,
			MIDIValue effect
		)
   {
      Check(audioCard);
      audioCard->SelectEffect(type,effect);
   }

   inline void
      AudioChannel::SendNRPN(
         MIDIValue paramNumber,
         MIDINRPNValue value
      )
   {
      Check(audioCard);
      audioCard->SendNRPN(midiChannel,paramNumber,value);
   }

	inline void
		AudioChannel::SelectBank(
			MIDIValue bank
		)
   {
      Check(audioCard);
      audioCard->SelectBank(midiChannel,bank);
   }

	inline void
		AudioChannel::Release()
   {
      Check(audioCard);
      audioCard->ReleaseAudioChannel(this);
   }

	//~~~~~~~~~~~~~~~~~~~~~~~~~ AudioHardware inlines ~~~~~~~~~~~~~~~~~~~~~~~~~~

   inline AudioCard*
      AudioHardware::GetFrontCard()
   {
      Check(this);
      return &frontCard;
   }

   inline AudioCard*
      AudioHardware::GetRearCard()
   {
      Check(this);
      return &rearCard;
   }

#endif

