You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
378 lines
9.0 KiB
378 lines
9.0 KiB
5 years ago
|
//========= 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( ©Buf[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;
|
||
|
}
|
||
|
|
||
|
|