//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//

#include "audio_pch.h"
#include <assert.h>
#include "voice.h"
#include "ivoicecodec.h"

#if defined( _X360 )
#include "xauddefs.h"
#endif

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

// ------------------------------------------------------------------------- //
// CAudioSourceVoice.
// This feeds the data from an incoming voice channel (a guy on the server
// who is speaking) into the sound engine.
// ------------------------------------------------------------------------- //

class CAudioSourceVoice : public CAudioSourceWave
{
public:
								CAudioSourceVoice(CSfxTable *pSfx, int iEntity);
								virtual ~CAudioSourceVoice();
	
	virtual int					GetType( void )
	{
		return AUDIO_SOURCE_VOICE;
	}
	virtual void				GetCacheData( CAudioSourceCachedInfo *info )
	{
		Assert( 0 );
	}


	virtual CAudioMixer			*CreateMixer( int initialStreamPosition = 0 );
	virtual int					GetOutputData( void **pData, int samplePosition, int sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] );
	virtual int					SampleRate( void );
	
	// Sample size is in bytes.  It will not be accurate for compressed audio.  This is a best estimate.
	// The compressed audio mixers understand this, but in general do not assume that SampleSize() * SampleCount() = filesize
	// or even that SampleSize() is 100% accurate due to compression.
	virtual int					SampleSize( void );

	// Total number of samples in this source.  NOTE: Some sources are infinite (mic input), they should return
	// a count equal to one second of audio at their current rate.
	virtual int					SampleCount( void );

	virtual bool				IsVoiceSource()				{return true;}

	virtual bool				IsLooped()					{return false;}
	virtual bool				IsStreaming()				{return true;}
	virtual bool				IsStereoWav()				{return false;}
	virtual int					GetCacheStatus()			{return AUDIO_IS_LOADED;}
	virtual void				CacheLoad()					{}
	virtual void				CacheUnload()				{}
	virtual CSentence			*GetSentence()				{return NULL;}

	virtual int					ZeroCrossingBefore( int sample )	{return sample;}
	virtual int					ZeroCrossingAfter( int sample )		{return sample;}
	
	// mixer's references
	virtual void				ReferenceAdd( CAudioMixer *pMixer );
	virtual void				ReferenceRemove( CAudioMixer *pMixer );

	// check reference count, return true if nothing is referencing this
	virtual bool				CanDelete();

	virtual void				Prefetch() {}

	// Nothing, not a cache object...
	virtual void				CheckAudioSourceCache() {} 

private:

	class CWaveDataVoice : public IWaveData
	{
	public:
							CWaveDataVoice( CAudioSourceWave &source ) : m_source(source) {}
							~CWaveDataVoice( void ) {}

		virtual CAudioSource &Source( void )
		{
			return m_source;
		}
		
		// this file is in memory, simply pass along the data request to the source
		virtual int ReadSourceData( void **pData, int sampleIndex, int sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] )
		{
			return m_source.GetOutputData( pData, sampleIndex, sampleCount, copyBuf );
		}

		virtual bool IsReadyToMix() 
		{ 
			return true; 
		}

	private:
		CAudioSourceWave		&m_source;	// pointer to source
	};


private:
	CAudioSourceVoice( const CAudioSourceVoice & );

	// Which entity's voice this is for.
	int							m_iChannel;
	
	// How many mixers are referencing us.
	int							m_refCount;
};



// ----------------------------------------------------------------------------- //
// Globals.
// ----------------------------------------------------------------------------- //

// The format we sample voice in.
extern WAVEFORMATEX g_VoiceSampleFormat;

class CVoiceSfx : public CSfxTable
{
public:
	virtual const char *getname()
	{
		return "?VoiceSfx";
	}
};

static CVoiceSfx g_CVoiceSfx[VOICE_NUM_CHANNELS];

static float	g_VoiceOverdriveDuration = 0;
static bool		g_bVoiceOverdriveOn = false;

// When voice is on, all other sounds are decreased by this factor.
static ConVar voice_overdrive( "voice_overdrive", "2" );
static ConVar voice_overdrivefadetime( "voice_overdrivefadetime", "0.4" ); // How long it takes to fade in and out of the voice overdrive.

// The sound engine uses this to lower all sound volumes.
// All non-voice sounds are multiplied by this and divided by 256.
int g_SND_VoiceOverdriveInt = 256;


extern int Voice_SamplesPerSec();
extern int Voice_AvgBytesPerSec();

// ----------------------------------------------------------------------------- //
// CAudioSourceVoice implementation.
// ----------------------------------------------------------------------------- //

CAudioSourceVoice::CAudioSourceVoice( CSfxTable *pSfx, int iChannel )
	: CAudioSourceWave( pSfx )
{
	m_iChannel = iChannel;
	m_refCount = 0;

	WAVEFORMATEX tmp = g_VoiceSampleFormat;
	tmp.nSamplesPerSec = Voice_SamplesPerSec();
	tmp.nAvgBytesPerSec = Voice_AvgBytesPerSec();
	Init((char*)&tmp, sizeof(tmp));
	m_sampleCount = tmp.nSamplesPerSec;
}

CAudioSourceVoice::~CAudioSourceVoice()
{
	Voice_OnAudioSourceShutdown( m_iChannel );
}

CAudioMixer *CAudioSourceVoice::CreateMixer( int initialStreamPosition )
{
	CWaveDataVoice *pVoice = new CWaveDataVoice(*this);
	if(!pVoice)
		return NULL;

	CAudioMixer *pMixer = CreateWaveMixer( pVoice, WAVE_FORMAT_PCM, 1, BYTES_PER_SAMPLE*8, 0 );
	if(!pMixer)
	{
		delete pVoice;
		return NULL;
	}

	return pMixer;
}

int CAudioSourceVoice::GetOutputData( void **pData, int samplePosition, int sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] )
{
	int nSamplesGotten = Voice_GetOutputData(
		m_iChannel,
		copyBuf,
		AUDIOSOURCE_COPYBUF_SIZE,
		samplePosition,
		sampleCount );
	
	// If there weren't enough bytes in the received data channel, pad it with zeros.
	if( nSamplesGotten < sampleCount )
	{
		memset( &copyBuf[nSamplesGotten], 0, (sampleCount - nSamplesGotten) * BYTES_PER_SAMPLE );
		nSamplesGotten = sampleCount;
	}

	*pData = copyBuf;
	return nSamplesGotten;
}

int CAudioSourceVoice::SampleRate()
{
	return Voice_SamplesPerSec();
}

int CAudioSourceVoice::SampleSize()
{
	return BYTES_PER_SAMPLE;
}

int CAudioSourceVoice::SampleCount()
{
	return Voice_SamplesPerSec();
}

void CAudioSourceVoice::ReferenceAdd(CAudioMixer *pMixer)
{
	m_refCount++;
}

void CAudioSourceVoice::ReferenceRemove(CAudioMixer *pMixer)
{
	m_refCount--;
	if ( m_refCount <= 0 )
		delete this;
}

bool CAudioSourceVoice::CanDelete()
{
	return m_refCount == 0;
}


// ----------------------------------------------------------------------------- //
// Interface implementation.
// ----------------------------------------------------------------------------- //

bool VoiceSE_Init()
{
	if( !snd_initialized )
		return false;

	g_SND_VoiceOverdriveInt = 256;
	return true;
}

void VoiceSE_Term()
{
	// Disable voice ducking.
	g_SND_VoiceOverdriveInt = 256;
}


void VoiceSE_Idle(float frametime)
{
	g_SND_VoiceOverdriveInt = 256;

	if( g_bVoiceOverdriveOn )
	{
		g_VoiceOverdriveDuration = min( g_VoiceOverdriveDuration+frametime, voice_overdrivefadetime.GetFloat() );
	}
	else
	{
		if(g_VoiceOverdriveDuration == 0)
			return;

		g_VoiceOverdriveDuration = max(g_VoiceOverdriveDuration-frametime, 0.f);
	}

	float percent = g_VoiceOverdriveDuration / voice_overdrivefadetime.GetFloat();
	percent = (float)(-cos(percent * 3.1415926535) * 0.5 + 0.5);		// Smooth it out..
	float voiceOverdrive = 1 + (voice_overdrive.GetFloat() - 1) * percent;
	g_SND_VoiceOverdriveInt = (int)(256 / voiceOverdrive);
}


int VoiceSE_StartChannel(
	int iChannel,	//! Which channel to start.
	int iEntity,
	bool bProximity,
	int nViewEntityIndex )
{
	Assert( iChannel >= 0 && iChannel < VOICE_NUM_CHANNELS );

	// Start the sound.
	CSfxTable *sfx = &g_CVoiceSfx[iChannel];
	sfx->pSource = NULL;
	Vector vOrigin(0,0,0);

	StartSoundParams_t params;
	params.staticsound = false;
	params.entchannel = (CHAN_VOICE_BASE+iChannel);
	params.pSfx = sfx;
	params.origin = vOrigin;
	params.fvol = 1.0f;
	params.flags = 0;
	params.pitch = PITCH_NORM;


	if ( bProximity == true )
	{
		params.bUpdatePositions = true;
		params.soundlevel = SNDLVL_TALKING;
		params.soundsource = iEntity;
	}
	else
	{
		params.soundlevel = SNDLVL_IDLE;
		params.soundsource = nViewEntityIndex;
	}


	return S_StartSound( params );
}

void VoiceSE_EndChannel(
	int iChannel,	//! Which channel to stop.
	int iEntity
	)
{
	Assert( iChannel >= 0 && iChannel < VOICE_NUM_CHANNELS );

	S_StopSound( iEntity, CHAN_VOICE_BASE+iChannel );

	// Start the sound.
	CSfxTable *sfx = &g_CVoiceSfx[iChannel];
	sfx->pSource = NULL;
}

void VoiceSE_StartOverdrive()
{
	g_bVoiceOverdriveOn = true;
}

void VoiceSE_EndOverdrive()
{
	g_bVoiceOverdriveOn = false;
}


void VoiceSE_InitMouth(int entnum)
{
}

void VoiceSE_CloseMouth(int entnum)
{
}

void VoiceSE_MoveMouth(int entnum, short *pSamples, int nSamples)
{
}


CAudioSource* Voice_SetupAudioSource( int soundsource, int entchannel )
{
	int iChannel = entchannel - CHAN_VOICE_BASE;
	if( iChannel >= 0 && iChannel < VOICE_NUM_CHANNELS )
	{
		CSfxTable *sfx = &g_CVoiceSfx[iChannel];
		return new CAudioSourceVoice( sfx, iChannel );
	}
	else
		return NULL;
}