#include "munga.h"
#pragma hdrstop

#include "audcmp.h"
#include "audsrc.h"
#include "random.h"
#include "controls.h"
#include "objstrm.h"
#include "subsystm.h"
#include "namelist.h"

//#############################################################################
//#########################    AudioMessageWatcher    #########################
//#############################################################################

//
//#############################################################################
//#############################################################################
//
AudioMessageWatcher::AudioMessageWatcher(
	PlugStream *stream,
	Entity *entity
):
	AudioComponent(stream),
	audioComponentSocket(NULL),
	messageTap(NULL)
{
	Check(stream);
	Check(entity);

	//
	// HACK - Get simulation pointer using names
	//
	Simulation *simulation;
	CString subsystem_name;

	MemoryStream_Read(stream, &subsystem_name);
	if ((simulation = entity->FindSubsystem(subsystem_name)) == NULL)
	{
		#if DEBUG_LEVEL>0
		{
			CString entity_name("Entity");
			if (!(subsystem_name == entity_name))
			{
				Dump(subsystem_name);
			}
			Verify(subsystem_name == entity_name);
		}
		#else
		{
			CString entity_name("Entity");
			if (!(subsystem_name == entity_name))
			{
				DEBUG_STREAM <<
					"MessageWatcher::MessageWatcher - subsystem " <<
					subsystem_name <<
					"\n";
				Fail("MessageWatcher::MessageWatcher - subsystem not found\n");
			}
		}
		#endif
		simulation = entity;
	}
	Check(simulation);

	//
	// Get message id
	//
	Receiver::MessageID message_ID;
	MemoryStream_Read(stream, &message_ID);

	//
	// Get audio component, control ID, control value
	//
	AudioComponent *audio_component;

	PlugStream_ReadObjectIDAndFindPlug(stream, &audio_component);
	MemoryStream_Read(stream, &controlID);
	MemoryStream_Read(stream, &controlValue);

	audioComponentSocket.Add(audio_component);

	//
	// Make the message tap
	//
	messageTap =
		MakeMessageTap(
			simulation->GetDerivation(),
			(ScanCallback)&AudioMessageWatcher::MessageTapScanCallback,
			message_ID,
			simulation
		);
	Register_Object(messageTap);

	entity->AddAudioComponent(this);

	#if DEBUG_LEVEL>0
		verifyReceiver = simulation;
		verifyMessageID = message_ID;
	#endif
}

//
//#############################################################################
//#############################################################################
//
AudioMessageWatcher::~AudioMessageWatcher()
{
	Unregister_Object(messageTap);
	delete messageTap;
}

//
//#############################################################################
//#############################################################################
//
void
	AudioMessageWatcher::BuildFromPage(
		PlugStream *stream,
		NameList *name_list,
		ClassID class_ID,
		ObjectID object_ID
	)
{
	AudioComponent::BuildFromPage(stream, name_list, class_ID, object_ID);

	//
	// HACK - Store simulation pointer using names
	//
	MEM_STRM_WRITE_ENTRY(*stream, name_list, CString, subsystem);

	//
	// Store message ID
	//
	MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, message_ID);

	//
	// Store audio_component, control ID, control value
	//
	CString audio_component_name("audio_component");
	PlugStream_FindEntryAndWriteObjectID(stream, name_list, audio_component_name);

	MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, control_ID);
	MEM_STRM_WRITE_ENTRY(*stream, name_list, AudioControlValue, control_value);
}

//
//#############################################################################
//#############################################################################
//
Logical
	AudioMessageWatcher::TestInstance() const
{
	Component::TestInstance();
	if (messageTap != NULL)
	{
		Check(messageTap);
	}
	Check(&audioComponentSocket);
	return True;
}

//
//#############################################################################
//#############################################################################
//
#if DEBUG_LEVEL>0
void
	AudioMessageWatcher::MessageTapScanCallback(
		Receiver::Message *message,
		Receiver *receiver
	)
#else
void
	AudioMessageWatcher::MessageTapScanCallback(
		Receiver::Message *message,
		Receiver*
	)
#endif
{
	Check(this);
	Check(message);
	Check(receiver);
	Verify(verifyReceiver == receiver);
	Verify(verifyMessageID == message->messageID);

	//
	// Send the audio control message
	//
	if (DoesMessageMatch(message))
	{
		Check(audioComponentSocket.GetCurrent());
		#if 1
		audioComponentSocket.GetCurrent()->ReceiveControl(
			controlID,
			controlValue
		);
		#else
		audioComponentSocket.GetCurrent()->PostReceiveControl(
			controlID,
			controlValue
		);
      #endif
	}
}

//
//#############################################################################
//#############################################################################
//
void
	AudioMessageWatcher::ReceiveControl(
		AudioControlID,
		AudioControlValue
	)
{
}

//#############################################################################
//#################    AudioControlsButtonMessageWatcher    ###################
//#############################################################################

//
//#############################################################################
//#############################################################################
//
AudioControlsButtonMessageWatcher::AudioControlsButtonMessageWatcher(
	PlugStream *stream,
	Entity *entity
):
	AudioMessageWatcher(stream, entity)
{
}

//
//#############################################################################
//#############################################################################
//
AudioControlsButtonMessageWatcher::~AudioControlsButtonMessageWatcher()
{
}

//
//#############################################################################
//#############################################################################
//
Logical
	AudioControlsButtonMessageWatcher::DoesMessageMatch(
		Receiver::Message *message
	)
{
	Check(this);
	Check(message);

	ReceiverDataMessageOf<ControlsButton> *controls_button_message =
		Cast_Object(
			ReceiverDataMessageOf<ControlsButton>*,
         message
		);
	Check(controls_button_message);

   return (controls_button_message->dataContents > 0);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioControlSend ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//
//#############################################################################
//#############################################################################
//
AudioControlSend::~AudioControlSend()
{
}

//
//#############################################################################
//#############################################################################
//
AudioControlSend::AudioControlSend(
	PlugStream *stream,
	Entity *entity
):
	AudioComponent(stream)
{
   AudioComponent *audio_component;
   AudioControlID control_ID;
   AudioControlValue control_value;

	PlugStream_ReadObjectIDAndFindPlug(stream, &audio_component);
	MemoryStream_Read(stream, &control_ID);
	MemoryStream_Read(stream, &control_value);

   Check(entity);
	entity->AddAudioComponent(this);

	Check(audio_component);
	#if 1
	audio_component->ReceiveControl(control_ID, control_value);
	#else
	audio_component->PostReceiveControl(control_ID, control_value);
   #endif
}

//
//#############################################################################
//#############################################################################
//
void
   AudioControlSend::BuildFromPage(
      PlugStream *stream,
      NameList *name_list,
      ClassID class_ID,
      ObjectID object_ID
   )
{
   AudioComponent::BuildFromPage(stream, name_list, class_ID, object_ID);

	//
	// Store fields
	//
	CString audio_component_name("audio_component");
	PlugStream_FindEntryAndWriteObjectID(stream, name_list, audio_component_name);

	MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, control_ID);
	MEM_STRM_WRITE_ENTRY(*stream, name_list, AudioControlValue, control_value);
}

//
//#############################################################################
//#############################################################################
//
void
	AudioControlSend::ReceiveControl(
		AudioControlID,
		AudioControlValue
	)
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioControlSplitter ~~~~~~~~~~~~~~~~~~~~~~~~~~

//
//#############################################################################
//#############################################################################
//
AudioControlSplitter::AudioControlSplitter(
   PlugStream *stream,
   Entity *entity
):
	AudioComponent(stream),
	audioComponentSocket(NULL)
{
	int i, number_of_entries;

	MemoryStream_Read(stream, &number_of_entries);
	for (i = 0; i < number_of_entries; i++)
	{
		AudioComponent *audio_component;

		PlugStream_ReadObjectIDAndFindPlug(stream, &audio_component);
		Check(audio_component);
		audioComponentSocket.Add(audio_component);
		audio_component->AddWatcher(this);
	}

	Check(entity);
	entity->AddAudioComponent(this);
}

//
//#############################################################################
//#############################################################################
//
AudioControlSplitter::~AudioControlSplitter()
{
}

//
//#############################################################################
//#############################################################################
//
void
	AudioControlSplitter::BuildFromPage(
		PlugStream *stream,
		NameList *name_list,
		ClassID class_ID,
		ObjectID object_ID
	)
{
	AudioComponent::BuildFromPage(stream, name_list, class_ID, object_ID);

	//
	// Count number of entries
	//
	int number_of_entries = 0;
	NameList::Entry *entry;

	Check(name_list);
	entry = name_list->GetFirstEntry();
	while (entry != NULL)
	{
		Check(entry);
		if (entry->IsName("audio_component"))
			number_of_entries++;
		entry = entry->GetNextEntry();
	}

	//
	// Store entries
	//
	MemoryStream_Write(stream, &number_of_entries);

	Check(name_list);
	entry = name_list->GetFirstEntry();
	while (entry != NULL)
	{
		Check(entry);
		if (entry->IsName("audio_component"))
		{
			CString object_name;
			ObjectID object_ID;

			Check_Pointer(entry->GetChar());
			object_name = entry->GetChar();

			Check(stream);
			object_ID = stream->FindObjectID(object_name);
			if (object_ID == NullObjectID)
			{
				std::cout << "AudioControlSplitter::BuildFromPage - object_name == ";
				std::cout << object_name << "\n";
				Fail("AudioControlSplitter::BuildFromPage - object_ID == NullObjectID");
			}
			Verify(object_ID != NullObjectID);
			MemoryStream_Write(stream, &object_ID);
		}
		entry = entry->GetNextEntry();
	}
}

//
//#############################################################################
//#############################################################################
//
Logical
	AudioControlSplitter::TestInstance() const
{
	AudioComponent::TestInstance();
	Check(&audioComponentSocket);
	return True;
}

//
//#############################################################################
//#############################################################################
//
void
	AudioControlSplitter::ReceiveControl(
		AudioControlID control_ID,
		AudioControlValue control_value
	)
{
	Check(this);

	//
	// Send to all of the components in the socket
	//
	SChainIteratorOf<AudioComponent*> iterator(&audioComponentSocket);
   AudioComponent *audio_component;

	Check(&iterator);
	while ((audio_component = iterator.ReadAndNext()) != NULL)
	{
		Check(audio_component);
		audio_component->ReceiveControl(control_ID, control_value);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioControlMixer ~~~~~~~~~~~~~~~~~~~~~~~~~~~

//
//#############################################################################
//#############################################################################
//
AudioControlMixer::~AudioControlMixer()
{
}

//
//#############################################################################
//#############################################################################
//
AudioControlMixer::AudioControlMixer(
   PlugStream *stream,
   Entity *entity
):
	AudioComponent(stream),
   audioComponentSocket(NULL)
{
   AudioComponent *audio_component;
   AudioControlID first_input_control_ID;
   AudioControlID last_input_control_ID;
   AudioControlID output_control_ID;

   PlugStream_ReadObjectIDAndFindPlug(stream, &audio_component);
	MemoryStream_Read(stream, &first_input_control_ID);
	MemoryStream_Read(stream, &last_input_control_ID);
	MemoryStream_Read(stream, &output_control_ID);

   AudioControlMixerX(
      audio_component,
      entity,
      first_input_control_ID,
      last_input_control_ID,
      output_control_ID
	);
}

//
//#############################################################################
//#############################################################################
//
void
   AudioControlMixer::BuildFromPage(
      PlugStream *stream,
      NameList *name_list,
      ClassID class_ID,
      ObjectID object_ID
   )
{
   AudioComponent::BuildFromPage(stream, name_list, class_ID, object_ID);

   //
   // Store fields
   //
   CString audio_component_name("audio_component");
   PlugStream_FindEntryAndWriteObjectID(stream, name_list, audio_component_name);

	MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, first_input_control_ID);
	MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, last_input_control_ID);
	MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, output_control_ID);
}

//
//#############################################################################
//#############################################################################
//
void
   AudioControlMixer::AudioControlMixerX(
      AudioComponent *audio_component,
      Entity *entity,
      AudioControlID first_input_control_ID,
		AudioControlID last_input_control_ID,
      AudioControlID output_control_ID
	)
{
   //
   // Set up the control mix parameters
   //
   firstInputControlID = first_input_control_ID;
   outputControlID = output_control_ID;

   Verify(last_input_control_ID > first_input_control_ID);
   numberOfInputs = last_input_control_ID - first_input_control_ID + 1;
   Verify(numberOfInputs <= AUDIO_CONTROL_MIXER_MAX_CONTROLS);

   for (int i = 0; i < numberOfInputs; i++)
   {
      Verify(i >= 0 && i < AUDIO_CONTROL_MIXER_MAX_CONTROLS);
   	controlValueArray[i] = 0.0f;
   }

   //
   // Remember the audio component to mix to and add this
   // component to the entity
   //
	Check(audio_component);
	audioComponentSocket.Add(audio_component);
   audio_component->AddWatcher(this);

   Check(entity);
	entity->AddAudioComponent(this);
}

//
//#############################################################################
//#############################################################################
//
Logical
	AudioControlMixer::TestInstance() const
{
	AudioComponent::TestInstance();
   Check(&audioComponentSocket);
   return True;
}

//
//#############################################################################
//#############################################################################
//
void
   AudioControlMixer::ReceiveControl(
      AudioControlID control_ID,
      AudioControlValue control_value
   )
{
	Check(this);

   //
   // Index the controller into the mix table
   //
   int index = control_ID - firstInputControlID;

	if (index >= 0 && index < numberOfInputs)
   {
		//
      // Set the control value
      //
   	Verify(index >= 0 && index < AUDIO_CONTROL_MIXER_MAX_CONTROLS);
      controlValueArray[index] = control_value;

      //
      // Mix the new control value
      //
      AudioControlValue mixed_value = 0.0f;

		for (int i = 0; i < numberOfInputs; i++)
      {
			Verify(i >= 0 && i < AUDIO_CONTROL_MIXER_MAX_CONTROLS);
      	mixed_value += controlValueArray[i];
      }

		Check(audioComponentSocket.GetCurrent());
      audioComponentSocket.GetCurrent()->ReceiveControl(
      	outputControlID,
         mixed_value
      );
   }
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioControlMultiplier ~~~~~~~~~~~~~~~~~~~~~~

//
//#############################################################################
//#############################################################################
//
AudioControlMultiplier::~AudioControlMultiplier()
{
}

//
//#############################################################################
//#############################################################################
//
AudioControlMultiplier::AudioControlMultiplier(
   PlugStream *stream,
   Entity *entity
):
	AudioComponent(stream),
   audioComponentSocket(NULL)
{
	AudioComponent *audio_component;
   AudioControlID first_input_control_ID;
   AudioControlID last_input_control_ID;
   AudioControlID output_control_ID;

   PlugStream_ReadObjectIDAndFindPlug(stream, &audio_component);
	MemoryStream_Read(stream, &first_input_control_ID);
	MemoryStream_Read(stream, &last_input_control_ID);
	MemoryStream_Read(stream, &output_control_ID);

   AudioControlMultiplierX(
      audio_component,
      entity,
      first_input_control_ID,
      last_input_control_ID,
      output_control_ID
	);
}

//
//#############################################################################
//#############################################################################
//
void
   AudioControlMultiplier::BuildFromPage(
      PlugStream *stream,
      NameList *name_list,
      ClassID class_ID,
      ObjectID object_ID
   )
{
   AudioComponent::BuildFromPage(stream, name_list, class_ID, object_ID);

   //
   // Store fields
   //
   CString audio_component_name("audio_component");
   PlugStream_FindEntryAndWriteObjectID(stream, name_list, audio_component_name);

	MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, first_input_control_ID);
	MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, last_input_control_ID);
	MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, output_control_ID);
}

//
//#############################################################################
//#############################################################################
//
void
	AudioControlMultiplier::AudioControlMultiplierX(
      AudioComponent *audio_component,
      Entity *entity,
      AudioControlID first_input_control_ID,
		AudioControlID last_input_control_ID,
		AudioControlID output_control_ID
   )
{
   //
   // Set up the control mix parameters
   //
   firstInputControlID = first_input_control_ID;
   outputControlID = output_control_ID;

   Verify(last_input_control_ID > first_input_control_ID);
   numberOfInputs = last_input_control_ID - first_input_control_ID + 1;
   Verify(numberOfInputs <= AUDIO_CONTROL_MULTIPLIER_MAX_CONTROLS);

   for (int i = 0; i < numberOfInputs; i++)
   {
      Verify(i >= 0 && i < AUDIO_CONTROL_MULTIPLIER_MAX_CONTROLS);
   	controlValueArray[i] = 1.0f;
   }

   //
   // Remember the audio component to mix to and add this
   // component to the entity
   //
	Check(audio_component);
	audioComponentSocket.Add(audio_component);
   audio_component->AddWatcher(this);

   Check(entity);
	entity->AddAudioComponent(this);
}

//
//#############################################################################
//#############################################################################
//
Logical
	AudioControlMultiplier::TestInstance() const
{
	AudioComponent::TestInstance();
   Check(&audioComponentSocket);
   return True;
}

//
//#############################################################################
//#############################################################################
//
void
   AudioControlMultiplier::ReceiveControl(
      AudioControlID control_ID,
      AudioControlValue control_value
   )
{
	Check(this);

   //
   // Index the controller into the mix table
   //
   int index = control_ID - firstInputControlID;

	if (index >= 0 && index < numberOfInputs)
   {
		//
      // Set the control value
      //
   	Verify(index >= 0 && index < AUDIO_CONTROL_MULTIPLIER_MAX_CONTROLS);
      controlValueArray[index] = control_value;

      //
      // Multiply the new control value
      //
      AudioControlValue mult_value = 1.0f;

		for (int i = 0; i < numberOfInputs; i++)
		{
         Verify(i >= 0 && i < AUDIO_CONTROL_MULTIPLIER_MAX_CONTROLS);
      	mult_value *= controlValueArray[i];
      }

		Check(audioComponentSocket.GetCurrent());
      audioComponentSocket.GetCurrent()->ReceiveControl(
      	outputControlID,
         mult_value
      );
   }
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioControlSmoother ~~~~~~~~~~~~~~~~~~~~~~~~

//
//#############################################################################
//#############################################################################
//
AudioControlSmoother::~AudioControlSmoother()
{
}

//
//#############################################################################
//#############################################################################
//
AudioControlSmoother::AudioControlSmoother(
   PlugStream *stream,
   Entity *entity
):
	AudioComponent(stream),
   audioComponentSocket(NULL)
{
	//
	// Get audio component
	//
	AudioComponent *audio_component;

	PlugStream_ReadObjectIDAndFindPlug(stream, &audio_component);

	//
	// Get audio control id, sample size, and initial fill value
	//
	size_t number_of_samples;
	AudioControlValue initial_fill_value;
	AudioControlID control_ID;

	MemoryStream_Read(stream, &control_ID);
	MemoryStream_Read(stream, &number_of_samples);
	MemoryStream_Read(stream, &initial_fill_value);
	audioControlAverage.SetSize(number_of_samples, initial_fill_value);

	AudioControlSmootherX(
      audio_component,
      entity,
      control_ID
	);
}

//
//#############################################################################
//#############################################################################
//
void
   AudioControlSmoother::BuildFromPage(
      PlugStream *stream,
      NameList *name_list,
      ClassID class_ID,
      ObjectID object_ID
   )
{
   AudioComponent::BuildFromPage(stream, name_list, class_ID, object_ID);

   //
	// Store audio component
	//
	CString audio_component_name("audio_component");
	PlugStream_FindEntryAndWriteObjectID(stream, name_list, audio_component_name);

	//
	// Store audio control id, sample size, and initial fill value
	//
	MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, control_ID);
	MEM_STRM_WRITE_ENTRY(*stream, name_list, size_t, number_of_samples);

	AudioControlValue initial_fill_value = 0.0f;
	if (name_list->FindData("initial_fill_value") != NULL)
	{
		Check_Pointer(name_list->FindData("initial_fill_value"));
		Convert_From_Ascii(
			(const char *)name_list->FindData("initial_fill_value"),
			&initial_fill_value
		);
	}
	MemoryStream_Write(stream, &initial_fill_value);
}

//
//#############################################################################
//#############################################################################
//
void
   AudioControlSmoother::AudioControlSmootherX(
      AudioComponent *audio_component,
      Entity *entity,
      AudioControlID control_ID
   )
{
   controlID = control_ID;

   //
	// Remember the audio component and add this
   // component to the entity
   //
	Check(audio_component);
	audioComponentSocket.Add(audio_component);
   audio_component->AddWatcher(this);

   Check(entity);
	entity->AddAudioComponent(this);
}

//
//#############################################################################
//#############################################################################
//
Logical
   AudioControlSmoother::TestInstance() const
{
	AudioComponent::TestInstance();
   Check(&audioComponentSocket);
   Check(&audioControlAverage);
   return True;
}

//
//#############################################################################
//#############################################################################
//
void
   AudioControlSmoother::ReceiveControl(
      AudioControlID control_ID,
      AudioControlValue control_value
   )
{
	Check(this);

   if (control_ID == controlID)
	{
      audioControlAverage.Add(control_value);

      Check(audioComponentSocket.GetCurrent());
      audioComponentSocket.GetCurrent()->ReceiveControl(
         controlID,
         audioControlAverage.CalculateOlympicAverage()
      );
   }
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioResourceSelector ~~~~~~~~~~~~~~~~~~~~~~~

//
//#############################################################################
//#############################################################################
//
AudioResourceSelector::~AudioResourceSelector()
{
}

//
//#############################################################################
//#############################################################################
//
AudioResourceSelector::AudioResourceSelector(
   PlugStream *stream,
	Entity *entity
):
	AudioComponent(stream),
   audioSourceSocket(NULL)
{
	AudioSource *audio_source;
   AudioResourceIndex *audio_resource_index;
   AudioControlID control_ID;
   AudioControlValue min_control_value;
	AudioControlValue max_control_value;
	Logical dump_value;

   PlugStream_ReadObjectIDAndFindPlug(stream, &audio_source);
   PlugStream_ReadObjectIDAndFindPlug(stream, &audio_resource_index);
	MemoryStream_Read(stream, &control_ID);
	MemoryStream_Read(stream, &min_control_value);
	MemoryStream_Read(stream, &max_control_value);
	MemoryStream_Read(stream, &dump_value);

   AudioResourceSelectorX(
      audio_source,
		entity,
      audio_resource_index,
		control_ID,
      min_control_value,
		max_control_value,
		dump_value
	);
}

//
//#############################################################################
//#############################################################################
//
void
   AudioResourceSelector::BuildFromPage(
      PlugStream *stream,
      NameList *name_list,
      ClassID class_ID,
      ObjectID object_ID
   )
{
   AudioComponent::BuildFromPage(stream, name_list, class_ID, object_ID);

   //
   // Store fields
   //
   CString audio_source_name("audio_source");
   PlugStream_FindEntryAndWriteObjectID(stream, name_list, audio_source_name);

   CString audio_resource_index_name("audio_resource_index");
   PlugStream_FindEntryAndWriteObjectID(stream, name_list, audio_resource_index_name);

	MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, control_ID);
	MEM_STRM_WRITE_ENTRY(*stream, name_list, AudioControlValue, min_control_value);
	MEM_STRM_WRITE_ENTRY(*stream, name_list, AudioControlValue, max_control_value);
	MEM_STRM_WRITE_ENTRY(*stream, name_list, Logical, dump_value);
}

//
//#############################################################################
//#############################################################################
//
void
   AudioResourceSelector::AudioResourceSelectorX(
      AudioSource *audio_source,
		Entity *entity,
      AudioResourceIndex *audio_resource_index,
      AudioControlID control_ID,
      AudioControlValue min_control_value,
		AudioControlValue max_control_value,
		Logical dump_value
   )
{
   controlID = control_ID;
   minControlValue = min_control_value;
	maxControlValue = max_control_value;
   dumpValue = dump_value;

   Check(audio_resource_index);
   audioResourceIndex = audio_resource_index;

	Check(audio_source);
	audioSourceSocket.Add(audio_source);
   audio_source->AddWatcher(this);

   Check(entity);
	entity->AddAudioComponent(this);
}

//
//#############################################################################
//#############################################################################
//
Logical
	AudioResourceSelector::TestInstance() const
{
	AudioComponent::TestInstance();
   Check(&audioSourceSocket);
   Check(audioResourceIndex);
   return True;
}

//
//#############################################################################
//#############################################################################
//
void
   AudioResourceSelector::ReceiveControl(
      AudioControlID control_ID,
      AudioControlValue control_value
   )
{
	Check(this);

   if (control_ID == controlID)
   {
      AudioComponent *audio_component;
      AudioSource *audio_source;

      audio_component = audioSourceSocket.GetCurrent();
		audio_source = Cast_Object(AudioSource*, audio_component);
      Check(audio_source);

      if (audio_source->GetAudioSourceState() == StoppedAudioSourceState)
      {
         AudioResource *audio_resource;
         int index;

         Clamp(control_value, minControlValue, maxControlValue);
         Verify(!Small_Enough(maxControlValue - minControlValue));

			index =
				( ((Scalar)audioResourceIndex->GetSize()-1.0f) *
						(control_value - minControlValue) /
							(maxControlValue - minControlValue) ) + 0.5f;
         #if DEBUG_LEVEL>0
				if (!(index >= 0 && index < audioResourceIndex->GetSize()))
            {
               Dump(control_value);
            	Dump(index);
					Dump(audioResourceIndex->GetSize());
				}
			#endif
			Verify(index >= 0 && index < audioResourceIndex->GetSize());

			#if DEBUG_LEVEL>0
			if (dumpValue)
			{
				Tell("index:" << index);
				Tell("; size: " << audioResourceIndex->GetSize() << "\n");
			}
			#endif

			audio_resource = audioResourceIndex->GetAudioResource(index);
         Check(audio_resource);
         audio_source->SetAudioResource(audio_resource);
      }
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioSampleAndHold ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//
//#############################################################################
//#############################################################################
//
AudioSampleAndHold::AudioSampleAndHold(
	PlugStream *stream,
	Entity *entity
):
	AudioComponent(stream),
	audioComponentSocket(NULL)
{
	AudioComponent *audio_component;
	AudioControlID control_ID;
	Scalar seconds;
	AudioTime sample_duration;
	Scalar min_value;
	Scalar max_value;

	//
	// audio_component
	//
	PlugStream_ReadObjectIDAndFindPlug(stream, &audio_component);

	//
	// control_ID, sample_duration
	//
	MemoryStream_Read(stream, &control_ID);
	MemoryStream_Read(stream, &seconds);
	sample_duration = seconds;

	//
	// min_value, max_value
	//
	MemoryStream_Read(stream, &min_value);
	MemoryStream_Read(stream, &max_value);

	AudioSampleAndHoldX(
		audio_component,
		entity,
		control_ID,
		sample_duration,
		min_value,
		max_value
	);
}

//
//#############################################################################
//#############################################################################
//
void
	AudioSampleAndHold::BuildFromPage(
		PlugStream *stream,
		NameList *name_list,
		ClassID class_ID,
		ObjectID object_ID
	)
{
	AudioComponent::BuildFromPage(stream, name_list, class_ID, object_ID);

	//
	// audio_component
	//
	CString audio_component_name("audio_component");
	PlugStream_FindEntryAndWriteObjectID(stream, name_list, audio_component_name);

	//
	// control_ID, sample_duration
	//
	MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, control_ID);
	MEM_STRM_WRITE_ENTRY(*stream, name_list, Scalar, sample_duration);

	//
	// min_value, max_value
	//
	Scalar min_value = 0.0f;
	Scalar max_value = 1.0f;

	if (name_list->FindData("min_value") != NULL)
	{
		Convert_From_Ascii(
			(const char *)name_list->FindData("min_value"),
			&min_value
		);
	}
	if (name_list->FindData("max_value") != NULL)
	{
		Convert_From_Ascii(
			(const char *)name_list->FindData("max_value"),
			&max_value
		);
	}
	Verify(max_value > min_value);
	MemoryStream_Write(stream, &min_value);
	MemoryStream_Write(stream, &max_value);
}

//
//#############################################################################
//#############################################################################
//
void
	AudioSampleAndHold::AudioSampleAndHoldX(
		AudioComponent *audio_component,
		Entity *entity,
		AudioControlID audio_control_ID,
		const AudioTime &sample_duration,
		Scalar min_value,
		Scalar max_value
	)
{
	audioControlID = audio_control_ID;
	currentValue = 0.0f;
	nextSampleTime = AudioTime::Now();
	sampleDuration = sample_duration;
	Verify(max_value > min_value);
	minValue = min_value;
	maxValue = max_value;

	Check(audio_component);
	audioComponentSocket.Add(audio_component);
	audio_component->AddWatcher(this);

	Check(entity);
	entity->AddAudioComponent(this);
}

//
//#############################################################################
//#############################################################################
//
AudioSampleAndHold::~AudioSampleAndHold()
{
}

//
//#############################################################################
//#############################################################################
//
void
	AudioSampleAndHold::Execute()
{
	Check(this);

	AudioComponent::Execute();

	if (AudioTime::Now() > nextSampleTime)
	{
		SampleAndHold();
	}
}

//
//#############################################################################
//#############################################################################
//
void
	AudioSampleAndHold::ReceiveControl(
		AudioControlID control_ID,
		AudioControlValue
	)
{
	Check(this);

	if (control_ID == StartAudioControlID)
	{
		SampleAndHold();
	}
}

//
//#############################################################################
//#############################################################################
//
void
	AudioSampleAndHold::SampleAndHold()
{
	Check(this);

	Scalar random_sample = Random;

	Verify(0.0f <= random_sample && random_sample <= 1.0f);
	Verify(maxValue > minValue);
	currentValue = minValue + random_sample * (maxValue - minValue);

	nextSampleTime = AudioTime::Now();
	nextSampleTime += sampleDuration;

	Check(audioComponentSocket.GetCurrent());
	audioComponentSocket.GetCurrent()->ReceiveControl(
		audioControlID,
		currentValue
	);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioLFO ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//
//#############################################################################
//#############################################################################
//
AudioLFO::AudioLFO(
	PlugStream *stream,
	Entity *entity
):
	AudioComponent(stream),
	audioComponentSocket(NULL)
{
	AudioComponent *audio_component;
	AudioControlID control_ID;
	AudioLFOWaveForm wave_form;
	Scalar the_period;
	AudioControlValue min_value;
	AudioControlValue max_value;
	Logical dump_value;

	//
	// audio_component
	//
	PlugStream_ReadObjectIDAndFindPlug(stream, &audio_component);

	//
	// control_ID, wave_form, period
	//
	MemoryStream_Read(stream, &control_ID);
	MemoryStream_Read(stream, &wave_form);
	MemoryStream_Read(stream, &the_period);

	//
	// min_value, max_value, dump_value
	//
	MemoryStream_Read(stream, &min_value);
	MemoryStream_Read(stream, &max_value);
	MemoryStream_Read(stream, &dump_value);

	AudioLFOX(
		audio_component,
		entity,
		control_ID,
		wave_form,
		the_period,
		min_value,
		max_value,
		dump_value
	);
}

//
//#############################################################################
//#############################################################################
//
void
	AudioLFO::BuildFromPage(
		PlugStream *stream,
		NameList *name_list,
		ClassID class_ID,
		ObjectID object_ID
	)
{
	AudioComponent::BuildFromPage(stream, name_list, class_ID, object_ID);

	//
	// audio_component
	//
	CString audio_component_name("audio_component");
	PlugStream_FindEntryAndWriteObjectID(stream, name_list, audio_component_name);

	//
	// control_ID, wave_form, period
	//
	MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, control_ID);
	MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, wave_form);
	MEM_STRM_WRITE_ENTRY(*stream, name_list, Scalar, period);

	//
	// min_value, max_value
	//
	MEM_STRM_WRITE_ENTRY(*stream, name_list, Scalar, min_value);
	MEM_STRM_WRITE_ENTRY(*stream, name_list, Scalar, max_value);
	MEM_STRM_WRITE_ENTRY(*stream, name_list, Logical, dump_value);
}

//
//#############################################################################
//#############################################################################
//
void
	AudioLFO::AudioLFOX(
		AudioComponent *audio_component,
		Entity *entity,
		AudioControlID audio_control_ID,
		AudioLFOWaveForm wave_form,
		Scalar the_period,
		AudioControlValue min_value,
		AudioControlValue max_value,
		Logical dump_value
	)
{
	Check(audio_component);
	audioComponentSocket.Add(audio_component);
	audio_component->AddWatcher(this);

	audioControlID = audio_control_ID;
	waveForm = wave_form;
	minValue = min_value;
	maxValue = max_value;
	Verify(max_value > min_value);

	period = the_period;
	startTime = AudioTime::Now();

   dumpValue = dump_value;

	Check(entity);
	entity->AddAudioComponent(this);
}

//
//#############################################################################
//#############################################################################
//
AudioLFO::~AudioLFO()
{
}

//
//#############################################################################
//#############################################################################
//
void
	AudioLFO::Execute()
{
	Check(this);
	AudioComponent::Execute();
	Generate();
}

//
//#############################################################################
//#############################################################################
//
void
	AudioLFO::ReceiveControl(
		AudioControlID control_ID,
		AudioControlValue
	)
{
	Check(this);
	if (control_ID == StartAudioControlID)
	{
		Generate();
	}
}

//
//#############################################################################
//#############################################################################
//
void
	AudioLFO::Generate()
{
	Check(this);

	//
	// HACK - For now, everything is sinusoidal
	//
	// res = sin( (now-start) * (PI/period) )
   // val = min + ( 0.5 * (res+1) ) * (max - min)
	//
	Scalar time_factor;
	Scalar sample;
	Scalar current_value;

	time_factor = AudioTime::Now();
	Verify(!Small_Enough((Scalar)period));
   time_factor = (time_factor - (Scalar)startTime) / (Scalar)period;

	sample = (sin(time_factor * PI) + 1.0f) * 0.5f;
	Verify(0.0f <= sample && sample <= 1.0f);
	Verify(maxValue > minValue);
	current_value = minValue + sample * (maxValue - minValue);

	#if DEBUG_LEVEL>0
	if (dumpValue)
	{
		Dump(current_value);
	}
	#endif

	Check(audioComponentSocket.GetCurrent());
	audioComponentSocket.GetCurrent()->ReceiveControl(
		audioControlID,
		current_value
	);
}
