2021-07-24 21:11:47 -07:00

954 lines
27 KiB
C++

#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<const char*>(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