#include "audio_pch.h" #include "matchmaking/imatchframework.h" #include "tier2/tier2.h" #include "fmtstr.h" #include "audio/public/voice.h" #if !defined( DEDICATED ) && ( defined( OSX ) || defined( _WIN32 ) || defined( LINUX ) ) && !defined( NO_STEAM ) #include "cl_steamauth.h" #include "client.h" #if defined( PS3SDK_INSTALLED ) #define PS3_CROSS_PLAY #endif #if !defined( CSTRIKE15 ) #if defined( PS3_CROSS_PLAY ) || defined ( _PS3 ) #include "ps3sceCelp8.h" #define SOUND_PC_CELP_ENABLED #endif #endif // CSTRIKE15 extern IVEngineClient *engineClient; #endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" CEngineVoiceStub *Audio_GetEngineVoiceStub() { static CEngineVoiceStub s_EngineVoiceStub; return &s_EngineVoiceStub; } #if !defined( DEDICATED ) && ( defined( OSX ) || defined( _WIN32 ) || defined( LINUX ) ) && !defined( NO_STEAM ) ConVar snd_voice_echo( "snd_voice_echo", "0", FCVAR_DEVELOPMENTONLY ); // #define SND_VOICE_LOG_DEBUG #ifdef SND_VOICE_LOG_DEBUG ConVar snd_voice_log( "snd_voice_log", "0", FCVAR_DEVELOPMENTONLY ); // 1 = record, 2 = playback enum SndVoiceLog_t { SND_VOICE_LOG_RECORD_REMOTE = 1, SND_VOICE_LOG_TEST_PLAYBACK_LOG = 2, SND_VOICE_LOG_RECORD_REMOTE_11025 = 4, SND_VOICE_LOG_RECORD_LOCAL = 8, SND_VOICE_LOG_RECORD_LOCAL_11025 = 16, }; CUtlBuffer g_bufSndVoiceLog; CUtlBuffer g_bufSndVoiceLog11025; CON_COMMAND( snd_voice_log_commit, "Commit voice log to file" ) { if ( ( args.ArgC() <= 1 ) || ( g_bufSndVoiceLog.TellMaxPut() <= 0 ) ) { Msg( "Voice log size: %u/%u\n", g_bufSndVoiceLog.TellMaxPut(), g_bufSndVoiceLog11025.TellMaxPut() ); return; } if ( !strcmp( "0", args.Arg( 1 ) ) ) { Msg( "Voice log discarded\n" ); g_bufSndVoiceLog.Purge(); g_bufSndVoiceLog11025.Purge(); return; } if ( g_pFullFileSystem->WriteFile( args.Arg( 1 ), NULL, g_bufSndVoiceLog ) ) { Msg( "Voice log committed to file '%s', %u bytes\n", args.Arg(1), g_bufSndVoiceLog.TellMaxPut() ); g_bufSndVoiceLog.Purge(); } else { Warning( "Failed to commit voice log to file '%s', keeping %u bytes\n", args.Arg(1), g_bufSndVoiceLog.TellMaxPut() ); } if ( g_pFullFileSystem->WriteFile( CFmtStr( "%s.11025", args.Arg( 1 ) ), NULL, g_bufSndVoiceLog11025 ) ) { Msg( "Voice log committed to file '%s.11025', %u bytes\n", args.Arg(1), g_bufSndVoiceLog11025.TellMaxPut() ); g_bufSndVoiceLog11025.Purge(); } else { Warning( "Failed to commit voice log to file '%s.11025', keeping %u bytes\n", args.Arg(1), g_bufSndVoiceLog11025.TellMaxPut() ); } } CON_COMMAND( snd_voice_log_load, "Load voice log file" ) { g_bufSndVoiceLog.Purge(); if ( !g_pFullFileSystem->ReadFile( args.Arg( 1 ), NULL, g_bufSndVoiceLog ) ) { Warning( "Failed to read voice log from file '%s'\n", args.Arg( 1 ) ); } else { Msg( "Loaded voice log from file '%s'\n", args.Arg( 1 ) ); } } CON_COMMAND( snd_voice_log_setsin, "Set voice log sin-wave" ) { g_bufSndVoiceLog.Purge(); const int kNumSamples = 500000; g_bufSndVoiceLog.EnsureCapacity( kNumSamples * sizeof( int16 ) ); g_bufSndVoiceLog.SeekPut( CUtlBuffer::SEEK_HEAD, kNumSamples * sizeof( int16 ) ); float flSinFreq = 2.0/128.0; if ( args.ArgC() > 1 ) { flSinFreq = atof( args.Arg( 1 ) ); if ( flSinFreq <= 0.00001 ) flSinFreq = 0.00001; } // PURELY FOR EXAMPLE: a basic sine wave test if ( 1 ) { for ( int q = 0 ; q < kNumSamples; ++ q ) { float f = sin( flSinFreq * M_PI * q ) * 20000; ( ( int16 * ) g_bufSndVoiceLog.Base() )[q] = f; } } } #endif // SND_VOICE_LOG_DEBUG #define SOUND_PC_CELP_FREQ 8000 template < int nSOURCE, int nTARGET > struct ResampleGeneric_t { public: ResampleGeneric_t() : m_sampLeftover( 0 ), m_flFractionalSamples( 0.0f ) { } uint32 Resample( const int16 *piSamples, uint32 numInSamples, int16 *poSamples ) { if ( !poSamples ) return ( 1 + ( numInSamples / nSOURCE ) ) * nTARGET; if ( !numInSamples ) return 0; int numOutSamples = 0; const int16 *piSamplesEnd = piSamples + numInSamples; for ( int16 nextSamp = *( piSamples ++ ); ; m_flFractionalSamples += ((float) nSOURCE)/((float) nTARGET) ) { // if we've passed a sample boundary, go onto the next sample while ( m_flFractionalSamples >= 1.0f ) { m_flFractionalSamples -= 1.0f; m_sampLeftover = nextSamp; if ( piSamples < piSamplesEnd ) { nextSamp = *( piSamples ++ ); } else { return numOutSamples; } } *( poSamples ++ ) = Lerp( m_flFractionalSamples, m_sampLeftover, nextSamp ); // left ++ numOutSamples; } } private: float m_flFractionalSamples; int16 m_sampLeftover; }; typedef ResampleGeneric_t< SOUND_PC_CELP_FREQ, VOICE_OUTPUT_SAMPLE_RATE > Resample_CELP_to_PC_t; // 320 CELP samples = 441 PC samples typedef ResampleGeneric_t< VOICE_OUTPUT_SAMPLE_RATE, SOUND_PC_CELP_FREQ > Resample_PC_to_CELP_t; // 441 PC samples = 320 PC samples #ifdef SND_VOICE_LOG_DEBUG CON_COMMAND( snd_voice_log_resample, "Resample voice log file" ) { g_bufSndVoiceLog.Purge(); CUtlBuffer bufRaw; if ( !g_pFullFileSystem->ReadFile( args.Arg( 1 ), NULL, bufRaw ) ) { Warning( "Failed to read voice log from file '%s'\n", args.Arg( 1 ) ); return; } else { Msg( "Loaded voice log from file '%s'\n", args.Arg( 1 ) ); } Resample_CELP_to_PC_t rcpt; g_bufSndVoiceLog.EnsureCapacity( bufRaw.TellPut() * 2 ); uint32 numResamples = rcpt.Resample( (int16*)bufRaw.Base(), bufRaw.TellPut()/2, (int16*) g_bufSndVoiceLog.Base() ); g_bufSndVoiceLog.SeekPut( CUtlBuffer::SEEK_HEAD, numResamples*2 ); Msg( "Resampled voice log from %d to %d samples\n", bufRaw.TellPut()/2, numResamples ); } CON_COMMAND( snd_voice_log_resample44, "Resample voice log file all the way up to 44100" ) { g_bufSndVoiceLog.Purge(); CUtlBuffer bufRaw; if ( !g_pFullFileSystem->ReadFile( args.Arg( 1 ), NULL, bufRaw ) ) { Warning( "Failed to read voice log from file '%s'\n", args.Arg( 1 ) ); return; } else { Msg( "Loaded voice log from file '%s'\n", args.Arg( 1 ) ); } Resample_CELP_to_PC_t rcpt; CUtlBuffer buf11025; buf11025.EnsureCapacity( bufRaw.TellPut() * 2 ); uint32 numResamples = rcpt.Resample( (int16*)bufRaw.Base(), bufRaw.TellPut()/2, (int16*) buf11025.Base() ); buf11025.SeekPut( CUtlBuffer::SEEK_HEAD, numResamples*2 ); Msg( "Resampled voice log from %d to %d @11025 samples\n", bufRaw.TellPut()/2, numResamples ); g_bufSndVoiceLog.EnsureCapacity( buf11025.TellPut() * 4 ); int16 *pIn = (int16*) buf11025.Base(); int16 *pInEnd = pIn + numResamples; int16 *pOut = (int16*) g_bufSndVoiceLog.Base(); while ( pIn < pInEnd ) { int16 a = *( pIn++ ); int16 b = ( pIn < pInEnd ) ? *pIn : a; for ( int jj = 0; jj < 4; ++ jj ) { *( pOut ++ ) = ( double( b ) * ( jj ) + double( a ) * ( 4 - jj ) ) / 4.0; } } g_bufSndVoiceLog.SeekPut( CUtlBuffer::SEEK_HEAD, numResamples*8 ); } #endif // SND_VOICE_LOG_DEBUG class CEngineVoiceSteam : public IEngineVoice { public: CEngineVoiceSteam(); ~CEngineVoiceSteam(); public: virtual bool IsHeadsetPresent( int iController ); virtual bool IsLocalPlayerTalking( int iController ); virtual void AddPlayerToVoiceList( XUID xPlayer, int iController, uint64 uiFlags ); virtual void RemovePlayerFromVoiceList( XUID xPlayer, int iController ); virtual void GetRemoteTalkers( int *pNumTalkers, XUID *pRemoteTalkers ); virtual bool VoiceUpdateData( int iController ); virtual void GetVoiceData( int iController, const byte **ppvVoiceDataBuffer, unsigned int *pnumVoiceDataBytes ); virtual void VoiceResetLocalData( int iController ); virtual void SetPlaybackPriority( XUID remoteTalker, int iController, int iAllowPlayback ); virtual void PlayIncomingVoiceData( XUID xuid, const byte *pbData, unsigned int dwDataSize, const bool *bAudiblePlayers = NULL ); virtual void RemoveAllTalkers(); protected: void AudioInitializationUpdate(); void UpdateHUDVoiceStatus(); bool IsPlayerTalking( XUID xuid ); public: bool m_bLocalVoice[ XUSER_MAX_COUNT ]; struct RemoteTalker_t { XUID m_xuid; uint64 m_uiFlags; float m_flLastTalkTimestamp; }; CUtlVector< RemoteTalker_t > m_arrRemoteVoice; bool m_bVoiceForPs3; int FindRemoteTalker( XUID xuid ) { for ( int k = 0; k < m_arrRemoteVoice.Count(); ++ k ) if ( m_arrRemoteVoice[k].m_xuid == xuid ) return k; return m_arrRemoteVoice.InvalidIndex(); } bool m_bInitializedAudio; byte m_pbVoiceData[ 18*1024 * XUSER_MAX_COUNT ]; float m_flLastTalkingTimestamp; #if defined( PS3_CROSS_PLAY ) || defined ( _PS3 ) #ifdef SOUND_PC_CELP_ENABLED sceCelp8encHandle m_sceCelp8encHandle; sceCelp8decHandle m_sceCelp8decHandle; CUtlVectorFixed< byte, SCE_CELP8ENC_INPUT_SIZE > m_bufEncLeftover; CUtlVectorFixed< byte, SCE_CELP8DEC_INPUT_SIZE > m_bufDecLeftover; #endif // SOUND_PC_CELP_ENABLED Resample_CELP_to_PC_t m_resampleCelp2Pc; Resample_PC_to_CELP_t m_resamplePc2Celp; #endif }; CEngineVoiceSteam::CEngineVoiceSteam() { memset( m_bLocalVoice, 0, sizeof( m_bLocalVoice ) ); memset( m_pbVoiceData, 0, sizeof( m_pbVoiceData ) ); m_bInitializedAudio = false; m_bVoiceForPs3 = false; m_flLastTalkingTimestamp = 0.0f; #if defined( PS3_CROSS_PLAY ) || defined ( _PS3 ) #ifdef SOUND_PC_CELP_ENABLED m_sceCelp8encHandle = NULL; sceCelp8encAttr encQueryAttr; if ( sceCelp8encQueryAttr( &encQueryAttr ) < 0 ) { Warning( "ERROR: Failed to configure PS3 voice encoder!\n" ); } else { DevMsg( "PS3 voice encoder version %d.%d.%d.%d [%u]\n", (encQueryAttr.verNumber&0xff000000)>>24, (encQueryAttr.verNumber&0xff0000)>>16, (encQueryAttr.verNumber&0xff00)>>8, (encQueryAttr.verNumber&0xff), encQueryAttr.memSize ); m_sceCelp8encHandle = malloc( encQueryAttr.memSize ); if ( m_sceCelp8encHandle ) sceCelp8encInitInstance( m_sceCelp8encHandle ); } #endif #ifdef SOUND_PC_CELP_ENABLED m_sceCelp8decHandle = NULL; sceCelp8decAttr decQueryAttr; if ( sceCelp8decQueryAttr( &decQueryAttr ) < 0 ) { Warning( "ERROR: Failed to configure PS3 voice decoder!\n" ); } else { DevMsg( "PS3 voice decoder version %d.%d.%d.%d [%u]\n", (decQueryAttr.verNumber&0xff000000)>>24, (decQueryAttr.verNumber&0xff0000)>>16, (decQueryAttr.verNumber&0xff00)>>8, (decQueryAttr.verNumber&0xff), decQueryAttr.memSize ); m_sceCelp8decHandle = malloc( decQueryAttr.memSize ); if ( m_sceCelp8decHandle ) sceCelp8decInitInstance( m_sceCelp8decHandle ); } #endif #endif } CEngineVoiceSteam::~CEngineVoiceSteam() { #if defined( PS3_CROSS_PLAY ) || defined ( _PS3 ) #ifdef SOUND_PC_CELP_ENABLED if ( m_sceCelp8encHandle ) free( m_sceCelp8encHandle ); m_sceCelp8encHandle = NULL; if ( m_sceCelp8decHandle ) free( m_sceCelp8decHandle ); m_sceCelp8decHandle = NULL; #endif // SOUND_PC_CELP_ENABLED #endif } bool CEngineVoiceSteam::IsHeadsetPresent( int iController ) { return false; } bool CEngineVoiceSteam::IsLocalPlayerTalking( int iController ) { #ifdef _PS3 EVoiceResult res = Steam3Client().SteamUser()->GetAvailableVoice( NULL, NULL ); #else EVoiceResult res = Steam3Client().SteamUser()->GetAvailableVoice( NULL, NULL, 0 ); #endif switch ( res ) { case k_EVoiceResultOK: case k_EVoiceResultNoData: return true; default: return ( ( Plat_FloatTime() - m_flLastTalkingTimestamp ) <= 0.2f ); } } bool CEngineVoiceSteam::IsPlayerTalking( XUID uid ) { if ( !g_pMatchFramework->GetMatchSystem()->GetMatchVoice()->CanPlaybackTalker( uid ) ) return false; for ( int k = 0; k < m_arrRemoteVoice.Count(); ++ k ) { if ( m_arrRemoteVoice[k].m_xuid == uid ) { return ( ( Plat_FloatTime() - m_arrRemoteVoice[k].m_flLastTalkTimestamp ) < 0.2 ); } } return false; } void CEngineVoiceSteam::AddPlayerToVoiceList( XUID xPlayer, int iController, uint64 uiFlags ) { if ( !xPlayer && iController >= 0 && iController < XUSER_MAX_COUNT ) { // Add local player m_bLocalVoice[ iController ] = true; AudioInitializationUpdate(); if ( snd_voice_echo.GetBool() ) { #if defined( PS3_CROSS_PLAY ) || defined ( _PS3 ) m_bVoiceForPs3 = (snd_voice_echo.GetInt() == 2); #endif Steam3Client().SteamUser()->StartVoiceRecording(); } } if ( xPlayer ) { if ( FindRemoteTalker( xPlayer ) == m_arrRemoteVoice.InvalidIndex() ) { RemoteTalker_t rt = { xPlayer, uiFlags, 0 }; m_arrRemoteVoice.AddToTail( rt ); #if defined( PS3_CROSS_PLAY ) || defined ( _PS3 ) m_bVoiceForPs3 = !!(uiFlags & ENGINE_VOICE_FLAG_PS3); #endif AudioInitializationUpdate(); // Steam3Client().SteamUser()->StartVoiceRecording(); } } } void CEngineVoiceSteam::RemovePlayerFromVoiceList( XUID xPlayer, int iController ) { if ( !xPlayer && iController >= 0 && iController < XUSER_MAX_COUNT ) { // Remove local player m_bLocalVoice[ iController ] = false; AudioInitializationUpdate(); Steam3Client().SteamUser()->StopVoiceRecording(); } if ( xPlayer ) { int idx = FindRemoteTalker( xPlayer ); if ( idx != m_arrRemoteVoice.InvalidIndex() ) { m_arrRemoteVoice.FastRemove( idx ); AudioInitializationUpdate(); } } } void CEngineVoiceSteam::GetRemoteTalkers( int *pNumTalkers, XUID *pRemoteTalkers ) { if ( pNumTalkers ) *pNumTalkers = m_arrRemoteVoice.Count(); if ( pRemoteTalkers ) { for ( int k = 0; k < m_arrRemoteVoice.Count(); ++ k ) pRemoteTalkers[k] = m_arrRemoteVoice[k].m_xuid; } } bool CEngineVoiceSteam::VoiceUpdateData( int iController ) { #ifdef SND_VOICE_LOG_DEBUG if ( snd_voice_log.GetInt() == SND_VOICE_LOG_TEST_PLAYBACK_LOG ) { PlayIncomingVoiceData( 2, NULL, 0, NULL ); return false; } #endif // SND_VOICE_LOG_DEBUG #ifdef _PS3 EVoiceResult res = Steam3Client().SteamUser()->GetAvailableVoice( NULL, NULL ); #else EVoiceResult res = Steam3Client().SteamUser()->GetAvailableVoice( NULL, NULL, 0 ); #endif bool bResult = ( res == k_EVoiceResultOK ); if ( bResult ) m_flLastTalkingTimestamp = Plat_FloatTime(); UpdateHUDVoiceStatus(); return bResult; } void CEngineVoiceSteam::UpdateHUDVoiceStatus( void ) { for ( int iClient = 0; iClient < GetBaseLocalClient().m_nMaxClients; iClient++ ) { // Information about local client if it's a local client speaking bool bLocalClient = false; int iSsSlot = -1; int iCtrlr = -1; // Detection if it's a local client for ( DWORD k = 0; k < XBX_GetNumGameUsers(); ++ k ) { CClientState &cs = GetLocalClient( k ); if ( cs.m_nPlayerSlot == iClient ) { bLocalClient = true; iSsSlot = k; iCtrlr = XBX_GetUserId( iSsSlot ); } } // Convert into index and XUID int iIndex = iClient + 1; XUID xid = NULL; player_info_t infoClient; if ( engineClient->GetPlayerInfo( iIndex, &infoClient ) ) { xid = infoClient.xuid; } if ( !xid ) // No XUID means no VOIP { g_pSoundServices->OnChangeVoiceStatus( iIndex, -1, false ); if ( bLocalClient ) g_pSoundServices->OnChangeVoiceStatus( iIndex, iSsSlot, false ); continue; } // Determine talking status bool bTalking = false; if ( bLocalClient ) { //Make sure the player's own "remote" label is not on. g_pSoundServices->OnChangeVoiceStatus( iIndex, -1, false ); iIndex = -1; // issue notification as ent=-1 bTalking = IsLocalPlayerTalking( iCtrlr ); } else { bTalking = IsPlayerTalking( xid ); } g_pSoundServices->OnChangeVoiceStatus( iIndex, iSsSlot, bTalking ); } } void CEngineVoiceSteam::GetVoiceData( int iController, const byte **ppvVoiceDataBuffer, unsigned int *pnumVoiceDataBytes ) { const int size = ARRAYSIZE( m_pbVoiceData ) / XUSER_MAX_COUNT; byte *pbVoiceData = m_pbVoiceData + iController * ARRAYSIZE( m_pbVoiceData ) / XUSER_MAX_COUNT; *ppvVoiceDataBuffer = pbVoiceData; EVoiceResult res = k_EVoiceResultOK; if ( !m_bVoiceForPs3 ) { #ifdef _PS3 res = Steam3Client().SteamUser()->GetVoice( true, pbVoiceData, size, pnumVoiceDataBytes, false, NULL, 0, NULL ); #else res = Steam3Client().SteamUser()->GetVoice( true, pbVoiceData, size, pnumVoiceDataBytes, false, NULL, 0, NULL, 0 ); #endif } #if defined( PS3_CROSS_PLAY ) || defined ( _PS3 ) else { #ifdef _PS3 res = Steam3Client().SteamUser()->GetVoice( true, pbVoiceData, size, pnumVoiceDataBytes, false, NULL, 0, NULL ); #else res = Steam3Client().SteamUser()->GetVoice( true, pbVoiceData, size, pnumVoiceDataBytes, false, NULL, 0, NULL, 0 ); #endif #if defined( SOUND_PC_CELP_ENABLED ) if ( !m_sceCelp8encHandle ) res = k_EVoiceResultNotRecording; #endif int16 *pbUncompressedVoiceData = ( int16* ) stackalloc( 11025*2 ); unsigned int numUncompressedVoiceBytes = 11025*2; if ( res == k_EVoiceResultOK ) { #ifdef _PS3 res = Steam3Client().SteamUser()->DecompressVoice( pbVoiceData, *pnumVoiceDataBytes, pbUncompressedVoiceData, numUncompressedVoiceBytes, &numUncompressedVoiceBytes ); #else res = Steam3Client().SteamUser()->DecompressVoice( pbVoiceData, *pnumVoiceDataBytes, pbUncompressedVoiceData, numUncompressedVoiceBytes, &numUncompressedVoiceBytes, 11025 ); #endif } if ( res == k_EVoiceResultOK ) { uint32 numOutSamples = m_resamplePc2Celp.Resample( pbUncompressedVoiceData, numUncompressedVoiceBytes/2, ( int16* ) pbVoiceData ); *pnumVoiceDataBytes = numOutSamples * 2; #ifdef SND_VOICE_LOG_DEBUG if ( snd_voice_log.GetInt() & SND_VOICE_LOG_RECORD_LOCAL_11025 ) { g_bufSndVoiceLog11025.Put( pbUncompressedVoiceData, numUncompressedVoiceBytes ); } if ( snd_voice_log.GetInt() & SND_VOICE_LOG_RECORD_LOCAL ) { g_bufSndVoiceLog.Put( pbVoiceData, numOutSamples * 2 ); } #endif // SND_VOICE_LOG_DEBUG #if defined( SOUND_PC_CELP_ENABLED ) byte *pbSrc = pbVoiceData; byte *pbDst = pbVoiceData; byte *pbSrcEnd = pbSrc + *pnumVoiceDataBytes; while ( pbSrc < pbSrcEnd ) { // Copy src data into encoding buffer, advance src int numBytesRoomForEncode = SCE_CELP8ENC_INPUT_SIZE - m_bufEncLeftover.Count(); numBytesRoomForEncode = MIN( numBytesRoomForEncode, pbSrcEnd - pbSrc ); m_bufEncLeftover.AddMultipleToTail( numBytesRoomForEncode, pbSrc ); pbSrc += numBytesRoomForEncode; // If we have sufficient number of bytes for encoding, then encode, advance dst if ( m_bufEncLeftover.Count() == SCE_CELP8ENC_INPUT_SIZE ) { byte encBuffer[ SCE_CELP8ENC_OUTPUT_SIZE ] = {0}; int numBytesGenerated = 0; int encResult = sceCelp8encEncode( m_sceCelp8encHandle, m_bufEncLeftover.Base(), encBuffer, &numBytesGenerated ); if ( encResult < 0 ) numBytesGenerated = 0; if ( numBytesGenerated > 0 ) { V_memcpy( pbDst, encBuffer, numBytesGenerated ); pbDst += numBytesGenerated; } m_bufEncLeftover.RemoveAll(); } else break; } // Set the number of bytes after encoding process *pnumVoiceDataBytes = pbDst - pbVoiceData; #endif } if ( res != k_EVoiceResultOK ) { *pnumVoiceDataBytes = 0; *ppvVoiceDataBuffer = NULL; return; } } #endif // On PC respect user push-to-talk setting and don't transmit voice // if push-to-talk key is not held static ConVarRef voice_system_enable( "voice_system_enable" ); // voice system is initialized static ConVarRef voice_enable( "voice_enable" ); // mute all player voice data, and don't send voice data for this player static ConVarRef voice_vox( "voice_vox" ); // open mic static ConVarRef voice_ptt( "voice_ptt" ); // mic ptt release time if ( !voice_system_enable.GetBool() || !voice_enable.GetBool() ) goto prevent_voice_comm; if ( !voice_vox.GetInt() ) { float flPttReleaseTime = voice_ptt.GetFloat(); if ( flPttReleaseTime && ( ( Plat_FloatTime() - flPttReleaseTime ) > 1.0f ) ) { // User is in push-to-talk mode and released the talk key over a second ago // don't transmit any voice in this case goto prevent_voice_comm; } } switch ( res ) { case k_EVoiceResultNoData: case k_EVoiceResultOK: return; default: prevent_voice_comm: *pnumVoiceDataBytes = 0; *ppvVoiceDataBuffer = NULL; return; } } void CEngineVoiceSteam::VoiceResetLocalData( int iController ) { const int size = ARRAYSIZE( m_pbVoiceData ) / XUSER_MAX_COUNT; byte *pbVoiceData = m_pbVoiceData + iController * ARRAYSIZE( m_pbVoiceData ) / XUSER_MAX_COUNT; memset( pbVoiceData, 0, size ); } void CEngineVoiceSteam::SetPlaybackPriority( XUID remoteTalker, int iController, int iAllowPlayback ) { ; } void CEngineVoiceSteam::PlayIncomingVoiceData( XUID xuid, const byte *pbData, unsigned int dwDataSize, const bool *bAudiblePlayers /* = NULL */ ) { int idxRemoteTalker = 0; for ( DWORD dwSlot = 0; dwSlot < XBX_GetNumGameUsers(); ++ dwSlot ) { #ifdef _GAMECONSOLE int iCtrlr = XBX_GetUserId( dwSlot ); #else int iCtrlr = dwSlot; #endif IPlayerLocal *pPlayer = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( iCtrlr ); if ( pPlayer && pPlayer->GetXUID() == xuid ) { //Hack: Don't play stuff that comes from ourselves. if ( snd_voice_echo.GetBool() ) { if ( !m_arrRemoteVoice.Count() ) { RemoteTalker_t rt = { 0, m_bVoiceForPs3 ? ENGINE_VOICE_FLAG_PS3 : 0, 0 }; m_arrRemoteVoice.AddToTail( rt ); } goto playvoice; } return; } } #ifdef SND_VOICE_LOG_DEBUG if ( snd_voice_log.GetInt() == SND_VOICE_LOG_TEST_PLAYBACK_LOG ) { if ( !m_arrRemoteVoice.Count() ) { RemoteTalker_t rt = { 0, m_bVoiceForPs3 ? ENGINE_VOICE_FLAG_PS3 : 0, 0 }; m_arrRemoteVoice.AddToTail( rt ); } } else #endif { // Make sure voice playback is allowed for the specified user if ( !g_pMatchFramework->GetMatchSystem()->GetMatchVoice()->CanPlaybackTalker( xuid ) ) return; // Find registered remote talker idxRemoteTalker = FindRemoteTalker( xuid ); if ( idxRemoteTalker == m_arrRemoteVoice.InvalidIndex() ) return; } playvoice: // Uncompress the voice data char pbUncompressedVoice[ 11025 * 2 ]; uint32 numUncompressedBytes = 0; #ifdef SND_VOICE_LOG_DEBUG if ( snd_voice_log.GetInt() == SND_VOICE_LOG_TEST_PLAYBACK_LOG ) { const int nVoiceLogFreq = 11025; const int nVoiceLogSamples = 441; static float s_flLastTime = 0; static int s_nBufferPosition = 0; if ( g_bufSndVoiceLog.TellPut() <= nVoiceLogSamples ) return; if ( !s_flLastTime ) { s_flLastTime = Plat_FloatTime(); return; } // See how many bytes we can send, assuming 16,000 bytes/sec (8000Hz), rounding to nearest 320 bytes float flCurTime = Plat_FloatTime(); if ( flCurTime - s_flLastTime > 1.0f ) { // drop frames s_flLastTime = Plat_FloatTime(); return; } int numSoundFrames = nVoiceLogFreq * ( flCurTime - s_flLastTime ) / nVoiceLogSamples; if ( numSoundFrames <= 0 ) return; // Advance time pointer s_flLastTime += numSoundFrames * nVoiceLogSamples / float( nVoiceLogFreq ); int16 *piWritePos = ( int16 * ) pbUncompressedVoice; while ( numSoundFrames --> 0 ) { // See if we need to reset buffer position if ( s_nBufferPosition + nVoiceLogSamples > g_bufSndVoiceLog.TellPut() ) s_nBufferPosition = 0; // uint32 numOutSamples = m_resampleCelp2Pc.Resample( ( ( const int16 * ) g_bufSndVoiceLog.Base() ) + ( s_nBufferPosition / 2 ), 160, piWritePos ); uint32 numOutSamples = nVoiceLogSamples; V_memcpy( piWritePos, ( ( const int16 * ) g_bufSndVoiceLog.Base() ) + ( s_nBufferPosition / sizeof( int16 ) ), nVoiceLogSamples* sizeof( int16 ) ); piWritePos += numOutSamples; numUncompressedBytes += numOutSamples * 2; s_nBufferPosition += nVoiceLogSamples * sizeof( int16 ); } if ( !numUncompressedBytes ) return; } else #endif // SND_VOICE_LOG_DEBUG if ( !(m_arrRemoteVoice[idxRemoteTalker].m_uiFlags & ENGINE_VOICE_FLAG_PS3) ) { #ifdef _PS3 EVoiceResult res = Steam3Client().SteamUser()->DecompressVoice( const_cast< byte * >( pbData ), dwDataSize, pbUncompressedVoice, sizeof( pbUncompressedVoice ), &numUncompressedBytes ); #else EVoiceResult res = Steam3Client().SteamUser()->DecompressVoice( const_cast< byte * >( pbData ), dwDataSize, pbUncompressedVoice, sizeof( pbUncompressedVoice ), &numUncompressedBytes, 11025 ); #endif if ( res != k_EVoiceResultOK ) return; } #if defined( PS3_CROSS_PLAY ) || defined ( _PS3 ) #ifdef SOUND_PC_CELP_ENABLED else if ( m_sceCelp8decHandle ) { int numCelpCompressedDecodedBufferMaxBytes = SOUND_PC_CELP_FREQ * 2; byte *pbCelpCompressedDecodedBuffer = ( byte * ) stackalloc( numCelpCompressedDecodedBufferMaxBytes ); byte *pbSrc = const_cast< byte * >( pbData ), *pbDst = pbCelpCompressedDecodedBuffer; byte *pbSrcEnd = pbSrc + dwDataSize, *pbDstEnd = pbDst + numCelpCompressedDecodedBufferMaxBytes; while ( pbSrc < pbSrcEnd ) { // Copy src data into decoding buffer, advance src int numBytesRoomForDecode = SCE_CELP8DEC_INPUT_SIZE - m_bufDecLeftover.Count(); numBytesRoomForDecode = MIN( numBytesRoomForDecode, pbSrcEnd - pbSrc ); m_bufDecLeftover.AddMultipleToTail( numBytesRoomForDecode, pbSrc ); pbSrc += numBytesRoomForDecode; // If we have sufficient number of bytes for encoding, then encode, advance dst if ( m_bufDecLeftover.Count() == SCE_CELP8DEC_INPUT_SIZE ) { byte decBuffer[ SCE_CELP8DEC_OUTPUT_SIZE ] = {0}; int numBytesGenerated = SCE_CELP8DEC_OUTPUT_SIZE; #ifdef SOUND_PC_CELP_ENABLED int decResult = sceCelp8decDecode( m_sceCelp8decHandle, m_bufDecLeftover.Base(), decBuffer, 0 ); #else int decResult = -1; #endif #ifdef SND_VOICE_LOG_DEBUG if ( snd_voice_log.GetInt() & SND_VOICE_LOG_RECORD_REMOTE ) { g_bufSndVoiceLog.Put( decBuffer, numBytesGenerated ); } #endif if ( decResult < 0 ) numBytesGenerated = 0; if ( ( numBytesGenerated > 0 ) && ( pbDst + numBytesGenerated <= pbDstEnd ) ) { // even if we have no room to store decoded data, keep decoding // to keep decoder state consistent V_memcpy( pbDst, decBuffer, numBytesGenerated ); pbDst += numBytesGenerated; } m_bufDecLeftover.RemoveAll(); } else break; } // Set the number of bytes after encoding process numUncompressedBytes = pbDst - pbCelpCompressedDecodedBuffer; uint32 numOutSamples = m_resampleCelp2Pc.Resample( ( const int16 * ) pbCelpCompressedDecodedBuffer, numUncompressedBytes/2, ( int16 * ) pbUncompressedVoice ); numUncompressedBytes = numOutSamples * 2; if ( !numOutSamples ) return; #ifdef SND_VOICE_LOG_DEBUG if ( snd_voice_log.GetInt() & SND_VOICE_LOG_RECORD_REMOTE_11025 ) { g_bufSndVoiceLog11025.Put( pbUncompressedVoice, numUncompressedBytes ); } #endif // SND_VOICE_LOG_DEBUG } #endif // SOUND_PC_CELP_ENABLED #endif // PS3_CROSS_PLAY || _PS3 // Voice channel index int idxVoiceChan = idxRemoteTalker; int nChannel = Voice_GetChannel( idxVoiceChan ); if ( nChannel == VOICE_CHANNEL_ERROR ) { // Create a channel in the voice engine and a channel in the sound engine for this guy. nChannel = Voice_AssignChannel( idxVoiceChan, false, 0.0f ); } // Give the voice engine the data (it in turn gives it to the mixer for the sound engine). if ( nChannel != VOICE_CHANNEL_ERROR ) { Voice_AddIncomingData( nChannel, reinterpret_cast(pbData), dwDataSize, 0, 0, 0, VoiceFormat_Steam ); m_arrRemoteVoice[idxRemoteTalker].m_flLastTalkTimestamp = Plat_FloatTime(); } } void CEngineVoiceSteam::RemoveAllTalkers() { memset( m_bLocalVoice, 0, sizeof( m_bLocalVoice ) ); m_arrRemoteVoice.RemoveAll(); AudioInitializationUpdate(); } void CEngineVoiceSteam::AudioInitializationUpdate() { bool bHasTalkers = ( m_arrRemoteVoice.Count() > 0 ); for ( int k = 0; k < ARRAYSIZE( m_bLocalVoice ); ++ k ) { if ( m_bLocalVoice[k] ) { bHasTalkers = true; break; } } // Initialized already if ( bHasTalkers == m_bInitializedAudio ) return; // Clear out voice buffers memset( m_pbVoiceData, 0, sizeof( m_pbVoiceData ) ); // Init or deinit voice system if ( bHasTalkers ) { Voice_ForceInit(); } else { Voice_Deinit(); } m_bInitializedAudio = bHasTalkers; } IEngineVoice *Audio_GetEngineVoiceSteam() { static CEngineVoiceSteam s_EngineVoiceSteam; return &s_EngineVoiceSteam; } #else IEngineVoice *Audio_GetEngineVoiceSteam() { return Audio_GetEngineVoiceStub(); } #endif