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.
377 lines
9.0 KiB
377 lines
9.0 KiB
//========= 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; |
|
} |
|
|
|
|
|
|