|
|
//========= Copyright � 1996-2005, 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, int skipInitialSamples, bool bUpdateDelayForChoreo, SoundError &soundError, struct hrtf_info_t* pHRTFVec ); virtual int GetOutputData( void **pData, int64 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 IsPlayerVoice() {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 GetQuality() { return 0; }
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, int64 sampleIndex, int sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] ) { return m_source.GetOutputData( pData, sampleIndex, sampleCount, copyBuf ); }
virtual bool IsReadyToMix() { return true; }
void UpdateLoopPosition( int nLoopPosition ) { // Should not be necessary in this implementation...
Assert( false ); }
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( char *pBuf, size_t bufLen ) { const char *pName = "?VoiceSfx"; V_strncpy( pBuf, pName, bufLen ); return pBuf; } };
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, int skipInitialSamples, bool bUpdateDelayForChoreo, SoundError &soundError, struct hrtf_info_t* pHRTFVec ) { soundError = SE_OK; CWaveDataVoice *pVoice = new CWaveDataVoice(*this); if(!pVoice) { Assert( false ); soundError = SE_CANT_CREATE_MIXER; return NULL; }
CAudioMixer *pMixer = CreateWaveMixer( pVoice, WAVE_FORMAT_PCM, 1, BYTES_PER_SAMPLE*8, initialStreamPosition, skipInitialSamples, bUpdateDelayForChoreo ); if(!pMixer) { delete pVoice; soundError = SE_CANT_CREATE_MIXER; return NULL; }
ReferenceAdd( pMixer ); return pMixer; }
int CAudioSourceVoice::GetOutputData( void **pData, int64 samplePosition, int sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] ) { int nSamplesGotten = Voice_GetOutputData( m_iChannel, copyBuf, AUDIOSOURCE_COPYBUF_SIZE, (int)samplePosition, sampleCount ); // If there weren't enough bytes in the received data channel, pad it with zeros.
if( nSamplesGotten < sampleCount ) { if ( copyBuf != NULL ) { 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); }
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; }
|