//===========================================================================//
// File:	audsrc.cpp                                                          //
// Project: MUNGA           Brick: Audio manager									  //
// 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               //
//===========================================================================//

#include <munga.hpp>
#pragma hdrstop

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

#if !defined(AUDREND_HPP)
#	include <audrend.hpp>
#endif

#if !defined(OBJSTRM_HPP)
	#include <objstrm.hpp>
#endif

#if !defined(APP_HPP)
	#include <app.hpp>
#endif

#if !defined(NAMELIST_HPP)
	#include <namelist.hpp>
#endif

//~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioSourceStartState ~~~~~~~~~~~~~~~~~~~~~~~~~~~

//
//#############################################################################
//#############################################################################
//

#define DEFAULT_NOTE (60)

AudioSourceStartState::AudioSourceStartState()
{
	noteValue = DEFAULT_NOTE;

	isDurationSet = False;
	hasDuration = False;
	duration = 0.0f;
	startTime = 0.0f;
	endTime = 0.0f;
}

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

//
//#############################################################################
//#############################################################################
//
Logical
	AudioSourceStartState::TestInstance() const
{
	if (isDurationSet || hasDuration)
	{
		Verify(duration >= 0.0f);
	}
	if (hasDuration)
	{
		Verify(endTime >= startTime);
	}
   return True;
}

//
//#############################################################################
//#############################################################################
//
void
	AudioSourceStartState::CalculateDuration(AudioControlValue control_value)
{
	//
	// Calculate duration
	//
	if (control_value > 0.0f)
	{
		hasDuration = True;
		endTime = AudioTime::Now();
		endTime += control_value;
	}
	else if (isDurationSet)
	{
		hasDuration = True;
		endTime = AudioTime::Now();
		endTime += duration;
		isDurationSet = False;
	}
	else
	{
		hasDuration = False;
		endTime = 0.0f;
	}
}

#if 0
//
//#############################################################################
//#############################################################################
//
AudioTime
	AudioSourceStartState::GetCurrentRunningTime()
{
	Check(this);
	AudioTime current_duration = AudioTime::Now();
	return (current_duration -= startTime);
}
#endif

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ AudioSource ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//
//#############################################################################
//#############################################################################
//
const Receiver::HandlerEntry
	AudioSource::MessageHandlerEntries[]=
{
	{
		AudioSource::StartMessageID,
		"Start",
		(AudioSource::Handler)&AudioSource::StartMessageHandler
	},
	{
		AudioSource::StopMessageID,
		"Stop",
		(AudioSource::Handler)&AudioSource::StopMessageHandler
	}
};

AudioSource::MessageHandlerSet
	AudioSource::MessageHandlers(
		ELEMENTS(AudioSource::MessageHandlerEntries),
		AudioSource::MessageHandlerEntries,
		AudioComponent::MessageHandlers
	);

//
//#############################################################################
//#############################################################################
//
Derivation
	AudioSource::ClassDerivations(
		AudioComponent::ClassDerivations,
		"AudioSource"
	);

AudioSource::SharedData
	AudioSource::DefaultData(
		AudioSource::ClassDerivations,
		AudioSource::MessageHandlers
	);

//
//#############################################################################
//#############################################################################
//
AudioSource::AudioSource(
	PlugStream *stream,
	Entity *entity
):
	AudioComponent(stream, DefaultData)
{
	AudioResource *audio_resource;
	AudioLocation *audio_location;
	AudioSourcePriority priority;
	AudioSourceMixPresence mix_presence;
   AudioControlValue volume_mix_level;
   AudioPitchCents pitch_mix_offset;
	AudioControlValue brightness_mix_level;
	Logical has_compression_curve;
	Scalar seconds;
	AudioTime compression_duration;
	Logical use_brightness_scale;
	Logical use_attack_time_scale;

	PlugStream_ReadObjectIDAndFindPlug(stream, &audio_resource);
	PlugStream_ReadObjectIDAndFindPlug(stream, &audio_location);
	MemoryStream_Read(stream, &priority);
	MemoryStream_Read(stream, &mix_presence);
	MemoryStream_Read(stream, &volume_mix_level);
	MemoryStream_Read(stream, &pitch_mix_offset);
	MemoryStream_Read(stream, &brightness_mix_level);
	MemoryStream_Read(stream, &has_compression_curve);
	MemoryStream_Read(stream, &seconds);
	compression_duration = seconds;
	MemoryStream_Read(stream, &use_brightness_scale);
	MemoryStream_Read(stream, &use_attack_time_scale);

	AudioSourceX(
      audio_resource,
		audio_location,
      entity,
      priority,
		mix_presence,
      volume_mix_level,
		pitch_mix_offset,
		brightness_mix_level,
		has_compression_curve,
		compression_duration,
		use_brightness_scale,
		use_attack_time_scale
	);
}

//
//#############################################################################
//#############################################################################
//
void
   AudioSource::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_resource_name("resource");
   PlugStream_FindEntryAndWriteObjectID(stream, name_list, audio_resource_name);

   CString audio_location_name("location");
	PlugStream_FindEntryAndWriteObjectID(stream, name_list, audio_location_name);

	MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, priority);
	MEM_STRM_WRITE_ENTRY(*stream, name_list, Enumeration, mix_presence);
	MEM_STRM_WRITE_ENTRY(*stream, name_list, AudioControlValue, volume_mix_level);
	MEM_STRM_WRITE_ENTRY(*stream, name_list, AudioPitchCents, pitch_mix_offset);
	MEM_STRM_WRITE_ENTRY(*stream, name_list, AudioControlValue, brightness_mix_level);
	MEM_STRM_WRITE_ENTRY(*stream, name_list, Logical, has_compression_curve);
	MEM_STRM_WRITE_ENTRY(*stream, name_list, Scalar, compression_duration);
	MEM_STRM_WRITE_ENTRY(*stream, name_list, Logical, use_brightness_scale);
	MEM_STRM_WRITE_ENTRY(*stream, name_list, Logical, use_attack_time_scale);
}

//
//#############################################################################
//#############################################################################
//
void
	AudioSource::AudioSourceX(
      AudioResource *audio_resource,
      AudioLocation *audio_location,
      Entity *entity,
      AudioSourcePriority priority,
      AudioSourceMixPresence mix_presence,
      AudioControlValue volume_mix_level,
      AudioPitchCents pitch_mix_offset,
		AudioControlValue brightness_mix_level,
		Logical has_compression_curve,
		const AudioTime &compression_duration,
		Logical use_brightness_scale,
		Logical use_attack_time_scale
	)
{
	Check(audio_resource);
	Check(audio_location);
	Check(entity);

	audioLocation = audio_location;
	audioResource = audio_resource;

	audioSourceState = StoppedAudioSourceState;

	attackVolumeScale = 1.0f;
	useAttackTimeScale = use_attack_time_scale;
   attackTimeScale = 0.0f;

	audioSourcePriority = priority;

	audioSourceMixPresence = mix_presence;
	volumeCompressionScale = 1.0f;

	hasCompressionCurve = has_compression_curve;
	compressionDuration = compression_duration;

	volumeMixScale = volume_mix_level;
	pitchMixOffset = pitch_mix_offset;
	useBrightnessScale = use_brightness_scale;
	brightnessMixScale = brightness_mix_level;

	volumeScale = 1.0f;
	pitchOffset = 0.0f;
	brightnessScale = 1.0f;

	nextExecuteFrame = NullAudioFrameCount;
	suspendFinishedFrame = NullAudioFrameCount;

	entity->AddAudioComponent(this);
}

//
//#############################################################################
//#############################################################################
//
AudioSource::~AudioSource()
{
	Verify(audioSourceState == StoppedAudioSourceState);
}

//
//#############################################################################
//#############################################################################
//
Logical
	AudioSource::TestInstance() const
{
	AudioComponent::TestInstance();
	return True;
}

//
// Resource methods
//

//
//#############################################################################
//#############################################################################
//
void
	AudioSource::SetAudioResource(AudioResource *audio_resource)
{
	Check(this);
	Check(audio_resource);

	Verify(audioSourceState == StoppedAudioSourceState);
	Check(audioResource);
	audioResource = audio_resource;
}

//
// Current State/Status Accessors
//

//
//#############################################################################
//#############################################################################
//
void
	AudioSource::AssignAudioSourceState(AudioSourceState audio_source_state)
{
	Check(this);

	//
	// Initialize the next start state if the source is now running
	//
	if (audio_source_state == RunningAudioSourceState)
	{
		AudioSourceStartState null_state;
		nextStartState = null_state;
	}
	audioSourceState = audio_source_state;
}

//
// Mixing & Logical compression methods
//

//
//#############################################################################
//#############################################################################
//
AudioControlValue
	AudioSource::CalculateSourceCompressionEffect()
{
	Check(this);

	//
	// Get current volume level
	//
	AudioControlValue current_volume_level;

	current_volume_level = CalculateSourceVolumeScale();
	Verify(current_volume_level >= 0.0f && current_volume_level <= 1.0f);

	//
	// Calculate the amount of this to apply to compression
	//
	if (hasCompressionCurve)
	{
		//
		// scale = 1 / (1 + ((1/comp_dur) * duration)^2)
		//
		Scalar current_running_time = GetCurrentRunningTime();
		Scalar compression_duration = compressionDuration;
		Scalar duration_factor;
		Scalar denominator;
		AudioControlValue compression_effect;

		Verify(!Small_Enough(compression_duration));
		duration_factor =
			pow((1.0f/compression_duration) * current_running_time, 2.0);
		// Warn(!(duration_factor > 0.0f));

		denominator = 1.0f + duration_factor;
		Verify(!Small_Enough(denominator));
		compression_effect = 1.0f / denominator;
		Verify(compression_effect >= 0.0f && compression_effect <=1.0f);

		return compression_effect * current_volume_level;
	}
	return current_volume_level;
}

//
//#############################################################################
//#############################################################################
//
AudioControlValue
	AudioSource::CalculateSourceVolumeScale()
{
	Check(this);
	
	//
	// Execute the watchers that will update the volume related
	// member parameters
	//
	ExecuteWatchers();

	//
	// Calculate the resulting volume scale
	//
	AudioControlValue 
		volume_scale;
		
	volume_scale = volumeScale * volumeMixScale * volumeCompressionScale;
	Clamp(volume_scale, MinAudioVolume, MaxAudioVolume);
	return volume_scale;
}

//
//#############################################################################
//#############################################################################
//
AudioControlValue
	AudioSource::CalculateSourceBrightnessScale()
{
	Check(this);

	AudioControlValue brightness_mix_scale;

	brightness_mix_scale = brightnessScale * brightnessMixScale;
	Clamp(brightness_mix_scale, MinAudioBrightness, MaxAudioBrightness);

	return brightness_mix_scale;
}

//
//#############################################################################
//#############################################################################
//
void
	AudioSource::ReceiveControl(
		AudioControlID control_ID,
		AudioControlValue control_value
	)
{
	Check(this);
	switch (control_ID)
	{
	//
	//----------------------------------------------------------------------
	// StartAudioControlID
	//----------------------------------------------------------------------
	//
	case StartAudioControlID:
		{
			StartMessage message(control_value);

			Check(application);
			Check(application->GetAudioRenderer());
			application->GetAudioRenderer()->PostAudioRequestMessage(
				this,
				&message
			);
		}
		break;

	//
	//----------------------------------------------------------------------
	// StopAudioControlID
	//----------------------------------------------------------------------
	//
	case StopAudioControlID:
		{
			StopMessage message;

			Check(application);
			Check(application->GetAudioRenderer());
			application->GetAudioRenderer()->PostAudioRequestMessage(
				this,
				&message
			);
		}
		break;

	//
	//----------------------------------------------------------------------
	// Misc...
	//----------------------------------------------------------------------
	//
	case VolumeAudioControlID:
		VolumeAudioHandler(control_value);
		break;

	case PitchAudioControlID:
		PitchAudioHandler(control_value);
		break;

	case BrightnessAudioControlID:
		BrightnessAudioHandler(control_value);
		break;

	case AttackVolumeAudioControlID:
		AttackVolumeAudioHandler(control_value);
		break;

	case AttackTimeAudioControlID:
		AttackTimeAudioHandler(control_value);
		break;

	case NoteAudioControlID:
		NoteAudioHandler(control_value);
		break;

	case DurationAudioControlID:
		DurationAudioHandler(control_value);
		break;

	case FlushMessagesAudioControlID:
		Check(application);
		Check(application->GetAudioRenderer());
		application->GetAudioRenderer()->FlushAudioMessages(this);
		break;

	case NullAudioControlID:
	case IdleAudioControlID:
		break;

	default:
		Fail("AudioSource::ReceiveControl - Should never reach here");
		break;
	}
}

//
//#############################################################################
//#############################################################################
//
void
	AudioSource::StartMessageHandler(StartMessage *message)
{
	SET_AUDIO_RENDERER();
	SET_AUDIO_RENDERER_START_HANDLER();
	
	Check(this);
	Check(message);
	Verify(message->messageID == StartMessageID);

	//
	//--------------------------------------------------------------------------
	// if double trigger condition exists
	// then return
	//--------------------------------------------------------------------------
	//
	AudioTime double_trigger_cutoff(AudioDoubleTriggerCutoff);

	#if 0
	double_trigger_cutoff = AudioDoubleTriggerCutoff;
	#endif
	if (
		audioSourceState == RunningAudioSourceState &&
		GetCurrentRunningTime() <= double_trigger_cutoff
	)
	{
		#ifdef LAB_ONLY
			Check(application);
			Check(application->GetAudioRenderer());
			application->GetAudioRenderer()->doubleTriggerCount++;
		#endif
		CLEAR_AUDIO_RENDERER_START_HANDLER();
		CLEAR_AUDIO_RENDERER();
		return;
	}

	//
	//--------------------------------------------------------------------------
	// If the source is not stopped then stop it
	//--------------------------------------------------------------------------
	//
	if (audioSourceState != StoppedAudioSourceState)
	{
		StopMessage stop_message;
		StopMessageHandler(&stop_message);
	}
		
	//
	//--------------------------------------------------------------------------
	// Verify that the state is stopped
	// Copy the next start state into the current start state
	// Calculate duration
	//--------------------------------------------------------------------------
	//
	Verify(audioSourceState == StoppedAudioSourceState);
	currentStartState = nextStartState;
	currentStartState.CalculateDuration(message->controlValue);

	//
	//--------------------------------------------------------------------------
	// Implementation specific start request
	//--------------------------------------------------------------------------
	//
	StartMessageImplementation();		

	CLEAR_AUDIO_RENDERER_START_HANDLER();
	CLEAR_AUDIO_RENDERER();
}

//
//#############################################################################
//#############################################################################
//
void
#if DEBUG_LEVEL>0
	AudioSource::StopMessageHandler(StopMessage *message)
#else
	AudioSource::StopMessageHandler(StopMessage*)
#endif
{
	SET_AUDIO_RENDERER();
	SET_AUDIO_RENDERER_STOP_HANDLER();

	Check(this);
	Check(message);
	Verify(message->messageID == StopMessageID);

	//
	// if the source is stopped
	// then return
	//
	if (audioSourceState == StoppedAudioSourceState)
	{
		CLEAR_AUDIO_RENDERER_STOP_HANDLER();
		CLEAR_AUDIO_RENDERER();
		return;
	}
		
	//
	// Implementation specific stop request
	//	
	StopMessageImplementation();	

	CLEAR_AUDIO_RENDERER_STOP_HANDLER();
	CLEAR_AUDIO_RENDERER();
}

//
//#############################################################################
//#############################################################################
//
void
	AudioSource::VolumeAudioHandler(AudioControlValue control_value)
{
	Check(this);
	Clamp(control_value, MinAudioVolume, MaxAudioVolume);
	volumeScale = control_value / (MaxAudioVolume - MinAudioVolume);
}

//
//#############################################################################
//#############################################################################
//
void
	AudioSource::PitchAudioHandler(AudioControlValue control_value)
{
	Check(this);
	pitchOffset = control_value;
}

//
//#############################################################################
//#############################################################################
//
void
	AudioSource::BrightnessAudioHandler(AudioControlValue control_value)
{
	Check(this);
	Clamp(control_value, MinAudioBrightness, MaxAudioBrightness);
	brightnessScale = control_value / (MaxAudioBrightness - MinAudioBrightness);
}

//
//#############################################################################
//#############################################################################
//
void
	AudioSource::AttackVolumeAudioHandler(AudioControlValue control_value)
{
	Check(this);
	Clamp(control_value, MinAudioAttack, MaxAudioAttack);
	attackVolumeScale = control_value / (MaxAudioAttack - MinAudioAttack);
}

//
//#############################################################################
//#############################################################################
//
void
	AudioSource::AttackTimeAudioHandler(AudioControlValue control_value)
{
	Check(this);
	Clamp(control_value, MinAudioAttack, MaxAudioAttack);
	attackTimeScale = control_value / (MaxAudioAttack - MinAudioAttack);
}

//
//#############################################################################
//#############################################################################
//
void
	AudioSource::NoteAudioHandler(AudioControlValue control_value)
{
	Check(this);
	nextStartState.SetNoteValue(control_value);
}

//
//#############################################################################
//#############################################################################
//
void
	AudioSource::DurationAudioHandler(AudioControlValue control_value)
{
	Check(this);
	nextStartState.SetDurationValue(control_value);
}

//
//#############################################################################
//#############################################################################
//
void
	AudioSource::StartImplementation()
{
	currentStartState.Start();
}

//
//#############################################################################
//#############################################################################
//
void
	AudioSource::StopImplementation()
{
	Fail("AudioSource::StopImplementation - Should never reach here");
}

//
//#############################################################################
//#############################################################################
//
void
	AudioSource::SuspendImplementation()
{
	StopImplementation();
	Check(application);
	Check(application->GetAudioRenderer());
	Check(audioResource);
	suspendFinishedFrame = 
		application->GetAudioRenderer()->GetAudioFrameCount() + 
			audioResource->GetSuspendDelay();
	#ifdef LAB_ONLY
		application->GetAudioRenderer()->sourceSuspendCount++;
	#endif
}

//
//#############################################################################
//#############################################################################
//
void
	AudioSource::ResumeImplementation()
{
	StartImplementation();
	suspendFinishedFrame = NullAudioFrameCount;
	#ifdef LAB_ONLY
		Check(application);
		Check(application->GetAudioRenderer());
		application->GetAudioRenderer()->sourceResumeCount++;
	#endif
}

//
//#############################################################################
//#############################################################################
//
void
	AudioSource::Execute()
{
	Check(this);
	
	//
	// Execute source if this is the next execution frame
	//
	AudioRenderer 
		*audio_renderer;
		
	Check(application);	
	audio_renderer = application->GetAudioRenderer();
	Check(audio_renderer);
	if (nextExecuteFrame <= audio_renderer->GetAudioFrameCount())
	{
		nextExecuteFrame = 
			audio_renderer->GetAudioFrameCount() + DefaultAudioFrameDelay; 
		
		//
		// Update the sources spatial model
		// Execute inherited method
		// Execute this sources sound model
		//
		UpdateSpatialModel(audio_renderer->GetAudioHead());
		AudioComponent::Execute();
		ExecuteModel(False);
	}
}
		
