//////////////////////////////////////////////////////
//
// FILE : Asura_Engine_Sound.cpp
//
// Created by Andrew Weinkove - 07/09/05
//
// Class for handling engine sounds.
//
// Copyright (C) 2005 Rebellion, All Rights Reserved.
//
/////////////////////////////////////////////////////

//Includes
#include "Core/Asura.h"

#ifndef ASURA_USE_AUDIO_SYSTEM

#include "Sound/Asura_Engine_Sound.h"

#include "Sound/Asura_Sound.h"

Asura_Engine_Sound::Asura_Engine_Sound(const Asura_Engine_Sound_Point* pxSoundPointArray, u_int uNumberSoundPoints, float fSoundInnerRadius, float fSoundOuterRadius)
: m_pxSoundPointArray(pxSoundPointArray)
, m_uNumberSoundPoints(uNumberSoundPoints)
, m_iLowSoundHandle(ASURA_INVALID_HANDLE)
, m_iHighSoundHandle(ASURA_INVALID_HANDLE)
, m_fSoundInnerRadius(fSoundInnerRadius)
, m_fSoundOuterRadius(fSoundOuterRadius)
{
#ifdef ASURA_ASSERT
	// Run some checking on the data we've been given.
	Asura_Assert(pxSoundPointArray && uNumberSoundPoints, "Invalid data passed to Asura_Engine_Sound constructor - will break!");
	Asura_Assert(uNumberSoundPoints > 1, "No need to use Asura_Engine_Sound class if there's only one sound. It's massive overkill.");
	Asura_Assert(uNumberSoundPoints < 20, "Do you really need to use so many different sounds? If so, you might want to optimise the sequential search");

	for (u_int i=0;i<uNumberSoundPoints;i++)
	{
		if (i &&
			pxSoundPointArray[i].m_fPitchChange != 0.0f)
		{
			Asura_Assert(pxSoundPointArray[i].m_fCorrectRevs > pxSoundPointArray[i - 1].m_fCorrectRevs, "Sound point array must be ordered with increasing revs");
		}
		Asura_Assert(pxSoundPointArray[i].m_fCrossfadeStart > 0.0f, "Crossfade start must be more than zero.");
		Asura_Assert(pxSoundPointArray[i].m_fCrossfadeLength > 0.0f, "Crossfade length must be more than zero.");
		for (u_int j=0;j<uNumberSoundPoints;j++)
		{
			if (i == j) continue;
			Asura_Assert(pxSoundPointArray[i].m_eSoundID != pxSoundPointArray[j].m_eSoundID, "No two sound points should use the same sound ID");
		}
	}
#endif //#ifdef ASURA_ASSERT
}

Asura_Engine_Sound::~Asura_Engine_Sound()
{
	ReleaseResources();
}

void Asura_Engine_Sound::Update(const Asura_Vector_3& xPosition, float fRevs, float fVolumeScale)
{
	// Make sure we're playing the right sounds at the right volume and pitch.
	float fRunningRevTotal = 0.0f;
	for (u_int i=0;i<m_uNumberSoundPoints;i++)
	{
		fRunningRevTotal += m_pxSoundPointArray[i].m_fCrossfadeStart;
		if (fRunningRevTotal > fRevs ||
			i == (m_uNumberSoundPoints - 1))
		{
			// Create the sound.
			CreateSingleSound(i, xPosition);
			// Set the volume to 1 (and the volume of any other lingering sound to zero)
			SetVolumeBlend(0.0f, fVolumeScale);
			// Now pitch shift.
			float fPitch = 1.0f + ((fRevs - m_pxSoundPointArray[i].m_fCorrectRevs) * m_pxSoundPointArray[i].m_fPitchChange);
			if (m_iLowSoundHandle != ASURA_INVALID_HANDLE)
			{
				Asura_Sound_System::SetSoundPitch(m_iLowSoundHandle, fPitch);
			}
			break;
		}
		fRunningRevTotal += m_pxSoundPointArray[i].m_fCrossfadeLength;
		if (fRunningRevTotal > fRevs)
		{
			// Make sure both the sounds are correct
			CreateDoubleSound(i, xPosition);
			// Set the volume for both of the sounds.
			float fBlend = (fRevs - (fRunningRevTotal - m_pxSoundPointArray[i].m_fCrossfadeLength)) / m_pxSoundPointArray[i].m_fCrossfadeLength;
			SetVolumeBlend(fBlend, fVolumeScale);
			// Pitch shift the lower sound
			float fPitch = 1.0f + ((fRevs - m_pxSoundPointArray[i].m_fCorrectRevs) * m_pxSoundPointArray[i].m_fPitchChange);
			if (m_iLowSoundHandle != ASURA_INVALID_HANDLE)
			{
				Asura_Sound_System::SetSoundPitch(m_iLowSoundHandle, fPitch);
			}
			// Pitch shift the higher sound
			fPitch = 1.0f + ((fRevs - m_pxSoundPointArray[i+1].m_fCorrectRevs) * m_pxSoundPointArray[i+1].m_fPitchChange);
			if (m_iHighSoundHandle != ASURA_INVALID_HANDLE)
			{
				Asura_Sound_System::SetSoundPitch(m_iHighSoundHandle, fPitch);
			}
			break;
		}
	}
	// Now set the position.
	if (m_iLowSoundHandle != ASURA_INVALID_HANDLE)
	{
		Asura_Sound_System::SetSoundPosition(m_iLowSoundHandle, xPosition);
	}
	if (m_iHighSoundHandle != ASURA_INVALID_HANDLE)
	{
		Asura_Sound_System::SetSoundPosition(m_iHighSoundHandle, xPosition);
	}
}

void Asura_Engine_Sound::CreateSingleSound(u_int uIndex, const Asura_Vector_3& xPosition)
{
	Asura_Assert(uIndex < m_uNumberSoundPoints, "Sanity check");
	const Asura_Engine_Sound_Point* pxSoundPoint = &m_pxSoundPointArray[uIndex];
	// We only need to have one of our sounds playing. It will play at full volume.
	if (m_iLowSoundHandle == ASURA_INVALID_HANDLE ||
		pxSoundPoint->m_eSoundID != Asura_Sound_System::GetSoundID(m_iLowSoundHandle))
	{
		// Seems we're not already playing this sound in the correct handle. Check the
		// other one.
		if (m_iHighSoundHandle != ASURA_INVALID_HANDLE &&
			pxSoundPoint->m_eSoundID == Asura_Sound_System::GetSoundID(m_iHighSoundHandle))
		{
			// We are playing the correct sound but in the wrong slot. Switch the slots.
			if (m_iLowSoundHandle != ASURA_INVALID_HANDLE)
			{
				Asura_Sound_System::DestroySound(m_iLowSoundHandle);
			}
			m_iLowSoundHandle = m_iHighSoundHandle;
			m_iHighSoundHandle = ASURA_INVALID_HANDLE;
		}
		else
		{
			// Not already playing this sound. Release any current sounds and create it.
			ReleaseResources();
			if (pxSoundPoint->m_eSoundID != ASURA_SID_NO_SOUND)
			{
				m_iLowSoundHandle = Asura_Sound_System::Create3dSound(pxSoundPoint->m_eSoundID, &xPosition, m_fSoundOuterRadius, m_fSoundInnerRadius);
				Asura_Sound_System::PlaySound(m_iLowSoundHandle, true);
			}
		}
	}
}

void Asura_Engine_Sound::CreateDoubleSound(u_int uIndex, const Asura_Vector_3& xPosition)
{
	// Get the low sound setup first.
	CreateSingleSound(uIndex, xPosition);

	// Now setup the higher sound.
	Asura_Assert(uIndex < m_uNumberSoundPoints - 1, "Sanity check");
	const Asura_Engine_Sound_Point* pxSoundPoint = &m_pxSoundPointArray[uIndex + 1];
	if (m_iHighSoundHandle == ASURA_INVALID_HANDLE ||
		pxSoundPoint->m_eSoundID != Asura_Sound_System::GetSoundID(m_iHighSoundHandle))
	{
		if (m_iHighSoundHandle != ASURA_INVALID_HANDLE)
		{
			Asura_Sound_System::DestroySound(m_iHighSoundHandle);
		}
		if (pxSoundPoint->m_eSoundID != ASURA_SID_NO_SOUND)
		{
			m_iHighSoundHandle = Asura_Sound_System::Create3dSound(pxSoundPoint->m_eSoundID, &xPosition, m_fSoundOuterRadius, m_fSoundInnerRadius);
			Asura_Sound_System::PlaySound(m_iHighSoundHandle, true);
		}
	}
}

void Asura_Engine_Sound::SetVolumeBlend(float fBlend, float fVolumeScale)
{
	// Using a non-linear crossfade. 
	if (m_iLowSoundHandle != ASURA_INVALID_HANDLE)
	{
		float fVolume1 = 1.0f - (fBlend * fBlend);
		Asura_Sound_System::SetSoundVolume(m_iLowSoundHandle, fVolume1 * fVolumeScale);
	}
	if (m_iHighSoundHandle != ASURA_INVALID_HANDLE)
	{
		float fVolume2 = 1.0f - ((1.0f - fBlend) * (1.0f - fBlend));
		if (fBlend == 0.0f)
		{
			fVolume2 = 0.0f;
		}
		Asura_Sound_System::SetSoundVolume(m_iHighSoundHandle, fVolume2 * fVolumeScale);
	}
}

void Asura_Engine_Sound::ReleaseResources()
{
	if (m_iLowSoundHandle != ASURA_INVALID_HANDLE)
	{
		Asura_Sound_System::DestroySound(m_iLowSoundHandle);
	}
	if (m_iHighSoundHandle != ASURA_INVALID_HANDLE)
	{
		Asura_Sound_System::DestroySound(m_iHighSoundHandle);
	}
}

#endif

//eof
