//===========================================================================//
// File:	AudioRenderer.cpp                                                //
// Project: MUNGA           Brick: Video Renderer 	                         //
// Contents: Abstracted Base Video Renderer								     //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 03/04/97  JTR  Initial coding.                                            //
//---------------------------------------------------------------------------//
// Copyright (C) 1997, Virtual World Entertainment, Inc.                     //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#include "AdeptHeaders.hpp"
#include "AudioSample.hpp"
#include "AudioChannel.hpp"
#include "AudioCommand.hpp"
#include "AudioRenderer.hpp"

//##########################################################################
//############################    AudioSample    ###########################
//##########################################################################

AudioSample::ClassData* AudioSample::DefaultData = NULL;

static bool g_SummarizeSamples;
static bool __stdcall CheckSummarizeSamples() {return g_SummarizeSamples;}
static void __stdcall EnableSummarizeSamples() {g_SummarizeSamples = !g_SummarizeSamples;}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioSample::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			AudioSampleClassID,
			"Adept::AudioSample",
			BaseClass::DefaultData
		);
	Register_Object(DefaultData);

	g_SummarizeSamples = false;
	AddDebuggerMenuItem("Libraries\\Sound\\Spew Sample Summaries", CheckSummarizeSamples, EnableSummarizeSamples, 0);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioSample::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AudioSample::AudioSample(MemoryStream *stream):
	Plug(DefaultData),
	m_timesPlayed(0),
	m_referenceCount(0),
	m_highestReferenceCount(0)
{
	Check_Pointer(this);
	AUDIO_LOAD("Sample Loading");

	//
	//---------------------------------------------
	// Find the resource file but don't load it yet
	//---------------------------------------------
	//
	ResourceID sample_id;
	*stream >> sample_id;
	m_sampleResource.FindID(sample_id, false);
	Verify(m_sampleResource.DoesResourceExist());

	//
	//------------------------------
	// Read the data from the stream
	//------------------------------
	//
	int gos_type;
	*stream >> gos_type >> m_cached >> m_3D;
	m_gosType = static_cast<gosAudio_ResourceType>(gos_type);
	m_gosSample = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AudioSample::~AudioSample()
{
	Check_Object(this);
	Verify(m_commands.IsEmpty());
	Verify(m_activeChannels.IsEmpty());

	if (m_timesPlayed>0)
	{
		ChainIteratorOf<AudioChannel*> channels(&m_inactiveChannels);
		AudioChannel *channel;
		while ((channel = channels.ReadAndNext()) != NULL)
		{
			Check_Object(channel);
			channel->m_sample = NULL;
		}
		#if !defined(NO_STATS)
			if (g_SummarizeSamples)
				SPEWALWAYS((
					"",
					"%s\t%d\t%d\t%d",
					m_sampleResource.GetName(),
					m_sampleInfo.dwSizeInBytes,
					m_timesPlayed,
					m_highestReferenceCount
				));
		#endif
		gosAudio_DestroyResource(&m_gosSample);
	}
	else
	{
		#if !defined(NO_STATS)
			if (g_SummarizeSamples)
				SPEWALWAYS((
					"",
					"%s\t0\t%d\t%d",
					m_sampleResource.GetName(),
					m_timesPlayed,
					m_highestReferenceCount
				));
		#endif
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioSample::Load()
{
	Check_Object(this);

	//
	//---------------------------------------
	// Construct the appropriate gos resource
	//---------------------------------------
	//
	if (!m_timesPlayed)
	{
		switch (m_gosType)
		{
		case gosAudio_UserMemoryDecode:
			m_sampleResource.LoadData();
			gosAudio_CreateResource(
				&m_gosSample,
				gosAudio_UserMemoryDecode,
				m_sampleResource.GetName(),
				NULL,
				m_sampleResource.GetPointer(),
				m_sampleResource.GetSize(),
				!m_3D
			);
			m_sampleResource.UnloadData();
			break;

		case gosAudio_StreamedFP:
			{
				Check_Object(ResourceManager::Instance);
				ResourceFile *file =
					ResourceManager::Instance->GetResourceFile(m_sampleResource.GetResourceID().GetFileID());
				Check_Object(file);
				gosAudio_CreateResource(
					&m_gosSample,
					m_sampleResource.GetName(),
					file->GetFileHandle(),
					m_sampleResource.GetDiskOffset(),
					!m_3D
				);
			}
			break;

		default:
			STOP(("Unsupported audio type"));
			break;
		}
		gosAudio_GetResourceInfo(m_gosSample, &m_sampleInfo);

		//
		//---------------------------------------------------------------------------
		// Make gos tell us about the sound, then set the restart delay appropriately
		//---------------------------------------------------------------------------
		//
		if (m_cached)
		{
			Verify(m_gosType == gosAudio_UserMemoryDecode);
			AudioRenderer::Instance->m_cacheSize += m_sampleInfo.dwSizeInBytes;
			AudioRenderer::Instance->FlushUnusedCache();
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioSample::SetChannelActive(AudioChannel *channel)
{
	Check_Object(this);
	Check_Object(channel);

	//
	//-------------------------------------------
	// Attach to the inactive chain in the sample
	//-------------------------------------------
	//
	if (m_inactiveChannels.IsPlugMember(channel))
		m_inactiveChannels.RemovePlug(channel);
	Verify(!m_activeChannels.IsPlugMember(channel));
	m_activeChannels.Add(channel);
	++m_timesPlayed;
	if (++m_referenceCount > m_highestReferenceCount)
		m_highestReferenceCount = m_referenceCount;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void AudioSample::SetChannelInactive(AudioChannel *channel)
{
	Check_Object(this);
	Check_Object(channel);

	//
	//---------------------------------------------------------------------------------
	// If this sample is not cached, kill it, and set the channel time to zero to force
	// it to the top of the reuse chain since there is no point in trying to save its
	// sample setting
	//---------------------------------------------------------------------------------
	//
	--m_referenceCount;
	Verify(m_referenceCount >= 0);
	Verify(m_activeChannels.IsPlugMember(channel));
	m_activeChannels.RemovePlug(channel);
	if (!m_cached && m_commands.IsEmpty())
	{
		channel->m_startTime = 0.0;
		channel->m_sample = NULL;
		gosAudio_AssignResourceToChannel(channel->m_channel, NULL);
		delete this;
		return;
	}

	//
	//----------------------------------------------------------------------------------
	// Attach to the inactive chain in the sample.  If the sample is a stream and is not
	// play once, delete it
	//----------------------------------------------------------------------------------
	//
	Verify(!m_inactiveChannels.IsPlugMember(channel));
	m_inactiveChannels.Add(channel);
}
