//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include "audio_pch.h" #include #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; }