//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: X360 XAudio Version // //=====================================================================================// #include "audio_pch.h" #include "snd_dev_xaudio.h" #include "UtlLinkedList.h" #include "session.h" #include "server.h" #include "client.h" #include "matchmaking.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" // The outer code mixes in PAINTBUFFER_SIZE (# of samples) chunks (see MIX_PaintChannels), we will never need more than // that many samples in a buffer. This ends up being about 20ms per buffer #define XAUDIO2_BUFFER_SAMPLES 8192 // buffer return has a latency, so need a decent pool #define MAX_XAUDIO2_BUFFERS 32 #define SURROUND_HEADPHONES 0 #define SURROUND_STEREO 2 #define SURROUND_DIGITAL5DOT1 5 // 5.1 means there are a max of 6 channels #define MAX_DEVICE_CHANNELS 6 ConVar snd_xaudio_spew_packets( "snd_xaudio_spew_packets", "0", 0, "Spew XAudio packet delivery" ); //----------------------------------------------------------------------------- // Implementation of XAudio //----------------------------------------------------------------------------- class CAudioXAudio : public CAudioDeviceBase { public: ~CAudioXAudio( void ); bool IsActive( void ) { return true; } bool Init( void ); void Shutdown( void ); void Pause( void ); void UnPause( void ); int PaintBegin( float mixAheadTime, int soundtime, int paintedtime ); int GetOutputPosition( void ); void ClearBuffer( void ); void TransferSamples( int end ); const char *DeviceName( void ); int DeviceChannels( void ) { return m_deviceChannels; } int DeviceSampleBits( void ) { return m_deviceSampleBits; } int DeviceSampleBytes( void ) { return m_deviceSampleBits/8; } int DeviceDmaSpeed( void ) { return m_deviceDmaSpeed; } int DeviceSampleCount( void ) { return m_deviceSampleCount; } void XAudioPacketCallback( int hCompletedBuffer ); static CAudioXAudio *m_pSingleton; CXboxVoice *GetVoiceData( void ) { return &m_VoiceData; } IXAudio2 *GetXAudio2( void ) { return m_pXAudio2; } private: int TransferStereo( const portable_samplepair_t *pFront, int paintedTime, int endTime, char *pOutptuBuffer ); int TransferSurroundInterleaved( const portable_samplepair_t *pFront, const portable_samplepair_t *pRear, const portable_samplepair_t *pCenter, int paintedTime, int endTime, char *pOutputBuffer ); int m_deviceChannels; // channels per hardware output buffer (1 for quad/5.1, 2 for stereo) int m_deviceSampleBits; // bits per sample (16) int m_deviceSampleCount; // count of mono samples in output buffer int m_deviceDmaSpeed; // samples per second per output buffer int m_clockDivider; IXAudio2 *m_pXAudio2; IXAudio2MasteringVoice *m_pMasteringVoice; IXAudio2SourceVoice *m_pSourceVoice; XAUDIO2_BUFFER m_Buffers[MAX_XAUDIO2_BUFFERS]; BYTE *m_pOutputBuffer; int m_bufferSizeBytes; // size of a single hardware output buffer, in bytes CInterlockedUInt m_BufferTail; CInterlockedUInt m_BufferHead; CXboxVoice m_VoiceData; }; CAudioXAudio *CAudioXAudio::m_pSingleton = NULL; //----------------------------------------------------------------------------- // XAudio packet completion callback //----------------------------------------------------------------------------- class XAudio2VoiceCallback : public IXAudio2VoiceCallback { public: XAudio2VoiceCallback() {} ~XAudio2VoiceCallback() {} void OnStreamEnd() {} void OnVoiceProcessingPassEnd() {} void OnVoiceProcessingPassStart( UINT32 SamplesRequired ) {} void OnBufferEnd( void *pBufferContext ) { CAudioXAudio::m_pSingleton->XAudioPacketCallback( (int)pBufferContext ); } void OnBufferStart( void *pBufferContext ) {} void OnLoopEnd( void *pBufferContext ) {} void OnVoiceError( void *pBufferContext, HRESULT Error ) {} }; XAudio2VoiceCallback s_XAudio2VoiceCallback; //----------------------------------------------------------------------------- // Create XAudio Device //----------------------------------------------------------------------------- IAudioDevice *Audio_CreateXAudioDevice( void ) { MEM_ALLOC_CREDIT(); if ( !CAudioXAudio::m_pSingleton ) { CAudioXAudio::m_pSingleton = new CAudioXAudio; } if ( !CAudioXAudio::m_pSingleton->Init() ) { delete CAudioXAudio::m_pSingleton; CAudioXAudio::m_pSingleton = NULL; } return CAudioXAudio::m_pSingleton; } CXboxVoice *Audio_GetXVoice( void ) { if ( CAudioXAudio::m_pSingleton ) { return CAudioXAudio::m_pSingleton->GetVoiceData(); } return NULL; } IXAudio2 *Audio_GetXAudio2( void ) { if ( CAudioXAudio::m_pSingleton ) { return CAudioXAudio::m_pSingleton->GetXAudio2(); } return NULL; } //----------------------------------------------------------------------------- // Destructor //----------------------------------------------------------------------------- CAudioXAudio::~CAudioXAudio( void ) { m_pSingleton = NULL; } //----------------------------------------------------------------------------- // Initialize XAudio //----------------------------------------------------------------------------- bool CAudioXAudio::Init( void ) { XAUDIOSPEAKERCONFIG xAudioConfig = 0; XAudioGetSpeakerConfig( &xAudioConfig ); snd_surround.SetValue( ( xAudioConfig & XAUDIOSPEAKERCONFIG_DIGITAL_DOLBYDIGITAL ) ? SURROUND_DIGITAL5DOT1 : SURROUND_STEREO ); m_bHeadphone = false; m_bSurround = false; m_bSurroundCenter = false; switch ( snd_surround.GetInt() ) { case SURROUND_HEADPHONES: m_bHeadphone = true; m_deviceChannels = 2; break; default: case SURROUND_STEREO: m_deviceChannels = 2; break; case SURROUND_DIGITAL5DOT1: m_bSurround = true; m_bSurroundCenter = true; m_deviceChannels = 6; break; } m_deviceSampleBits = 16; m_deviceDmaSpeed = SOUND_DMA_SPEED; // initialize the XAudio Engine // Both threads on core 2 m_pXAudio2 = NULL; HRESULT hr = XAudio2Create( &m_pXAudio2, 0, XboxThread5 ); if ( FAILED( hr ) ) return false; // create the mastering voice, this will upsample to the devices target hw output rate m_pMasteringVoice = NULL; hr = m_pXAudio2->CreateMasteringVoice( &m_pMasteringVoice ); if ( FAILED( hr ) ) return false; // 16 bit PCM WAVEFORMATEX waveFormatEx = { 0 }; waveFormatEx.wFormatTag = WAVE_FORMAT_PCM; waveFormatEx.nChannels = m_deviceChannels; waveFormatEx.nSamplesPerSec = m_deviceDmaSpeed; waveFormatEx.wBitsPerSample = 16; waveFormatEx.nBlockAlign = ( waveFormatEx.nChannels * waveFormatEx.wBitsPerSample ) / 8; waveFormatEx.nAvgBytesPerSec = waveFormatEx.nSamplesPerSec * waveFormatEx.nBlockAlign; waveFormatEx.cbSize = 0; m_pSourceVoice = NULL; hr = m_pXAudio2->CreateSourceVoice( &m_pSourceVoice, &waveFormatEx, 0, XAUDIO2_DEFAULT_FREQ_RATIO, &s_XAudio2VoiceCallback, NULL, NULL ); if ( FAILED( hr ) ) return false; float volumes[MAX_DEVICE_CHANNELS]; for ( int i = 0; i < MAX_DEVICE_CHANNELS; i++ ) { if ( !m_bSurround && i >= 2 ) { volumes[i] = 0; } else { volumes[i] = 1.0; } } m_pSourceVoice->SetChannelVolumes( m_deviceChannels, volumes ); m_bufferSizeBytes = XAUDIO2_BUFFER_SAMPLES * (m_deviceSampleBits/8) * m_deviceChannels; m_pOutputBuffer = new BYTE[MAX_XAUDIO2_BUFFERS * m_bufferSizeBytes]; ClearBuffer(); V_memset( m_Buffers, 0, MAX_XAUDIO2_BUFFERS * sizeof( XAUDIO2_BUFFER ) ); for ( int i = 0; i < MAX_XAUDIO2_BUFFERS; i++ ) { m_Buffers[i].pAudioData = m_pOutputBuffer + i*m_bufferSizeBytes; m_Buffers[i].pContext = (LPVOID)i; } m_BufferHead = 0; m_BufferTail = 0; // number of mono samples output buffer may hold m_deviceSampleCount = MAX_XAUDIO2_BUFFERS * (m_bufferSizeBytes/(DeviceSampleBytes())); // NOTE: This really shouldn't be tied to the # of bufferable samples. // This just needs to be large enough so that it doesn't fake out the sampling in // GetSoundTime(). Basically GetSoundTime() assumes a cyclical time stamp and finds wraparound cases // but that means it needs to get called much more often than once per cycle. So this number should be // much larger than the framerate in terms of output time m_clockDivider = m_deviceSampleCount / DeviceChannels(); // not really part of XAudio2, but mixer xma lacks one-time init, so doing it here XMAPlaybackInitialize(); hr = m_pSourceVoice->Start( 0 ); if ( FAILED( hr ) ) return false; DevMsg( "XAudio Device Initialized:\n" ); DevMsg( " %s\n" " %d channel(s)\n" " %d bits/sample\n" " %d samples/sec\n", DeviceName(), DeviceChannels(), DeviceSampleBits(), DeviceDmaSpeed() ); m_VoiceData.VoiceInit(); // success return true; } //----------------------------------------------------------------------------- // Shutdown XAudio //----------------------------------------------------------------------------- void CAudioXAudio::Shutdown( void ) { if ( m_pSourceVoice ) { m_pSourceVoice->Stop( 0 ); m_pSourceVoice->DestroyVoice(); m_pSourceVoice = NULL; delete[] m_pOutputBuffer; } if ( m_pMasteringVoice ) { m_pMasteringVoice->DestroyVoice(); m_pMasteringVoice = NULL; } // need to release ref to XAudio2 m_VoiceData.VoiceShutdown(); if ( m_pXAudio2 ) { m_pXAudio2->Release(); m_pXAudio2 = NULL; } if ( this == CAudioXAudio::m_pSingleton ) { CAudioXAudio::m_pSingleton = NULL; } } //----------------------------------------------------------------------------- // XAudio has completed a packet. Assuming these are sequential //----------------------------------------------------------------------------- void CAudioXAudio::XAudioPacketCallback( int hCompletedBuffer ) { // packet completion expected to be sequential Assert( hCompletedBuffer == (int)( m_PacketTail % MAX_XAUDIO2_BUFFERS ) ); m_BufferTail++; if ( snd_xaudio_spew_packets.GetBool() ) { if ( m_BufferTail == m_BufferHead ) { Warning( "XAudio: Starved\n" ); } else { Msg( "XAudio: Packet Callback, Submit: %2d, Free: %2d\n", m_BufferHead - m_BufferTail, MAX_XAUDIO2_BUFFERS - ( m_BufferHead - m_BufferTail ) ); } } if ( m_BufferTail == m_BufferHead ) { // very bad, out of packets, xaudio is starving // mix thread didn't keep up with audio clock and submit packets // submit a silent buffer to keep stream playing and audio clock running int head = m_BufferHead++; XAUDIO2_BUFFER *pBuffer = &m_Buffers[head % MAX_XAUDIO2_BUFFERS]; V_memset( pBuffer->pAudioData, 0, m_bufferSizeBytes ); pBuffer->AudioBytes = m_bufferSizeBytes; m_pSourceVoice->SubmitSourceBuffer( pBuffer ); } } //----------------------------------------------------------------------------- // Return the "write" cursor. Used to clock the audio mixing. // The actual hw write cursor and the number of samples it fetches is unknown. //----------------------------------------------------------------------------- int CAudioXAudio::GetOutputPosition( void ) { XAUDIO2_VOICE_STATE state; state.SamplesPlayed = 0; m_pSourceVoice->GetState( &state ); return ( state.SamplesPlayed % m_clockDivider ); } //----------------------------------------------------------------------------- // Pause playback //----------------------------------------------------------------------------- void CAudioXAudio::Pause( void ) { if ( m_pSourceVoice ) { m_pSourceVoice->Stop( 0 ); } } //----------------------------------------------------------------------------- // Resume playback //----------------------------------------------------------------------------- void CAudioXAudio::UnPause( void ) { if ( m_pSourceVoice ) { m_pSourceVoice->Start( 0 ); } } //----------------------------------------------------------------------------- // Calc the paint position //----------------------------------------------------------------------------- int CAudioXAudio::PaintBegin( float mixAheadTime, int soundtime, int paintedtime ) { // soundtime = total full samples that have been played out to hardware at dmaspeed // paintedtime = total full samples that have been mixed at speed // endtime = target for full samples in mixahead buffer at speed int mixaheadtime = mixAheadTime * DeviceDmaSpeed(); int endtime = soundtime + mixaheadtime; if ( endtime <= paintedtime ) { return endtime; } int fullsamps = DeviceSampleCount() / DeviceChannels(); if ( ( endtime - soundtime ) > fullsamps ) { endtime = soundtime + fullsamps; } if ( ( endtime - paintedtime ) & 0x03 ) { // The difference between endtime and painted time should align on // boundaries of 4 samples. This is important when upsampling from 11khz -> 44khz. endtime -= ( endtime - paintedtime ) & 0x03; } return endtime; } //----------------------------------------------------------------------------- // Fill the output buffers with silence //----------------------------------------------------------------------------- void CAudioXAudio::ClearBuffer( void ) { V_memset( m_pOutputBuffer, 0, MAX_XAUDIO2_BUFFERS * m_bufferSizeBytes ); } //----------------------------------------------------------------------------- // Fill the output buffer with L/R samples //----------------------------------------------------------------------------- int CAudioXAudio::TransferStereo( const portable_samplepair_t *pFrontBuffer, int paintedTime, int endTime, char *pOutputBuffer ) { int linearCount; int i; int val; int volumeFactor = S_GetMasterVolume() * 256; int *pFront = (int *)pFrontBuffer; short *pOutput = (short *)pOutputBuffer; // get size of output buffer in full samples (LR pairs) // number of sequential sample pairs that can be wrriten linearCount = g_AudioDevice->DeviceSampleCount() >> 1; // clamp output count to requested number of samples if ( linearCount > endTime - paintedTime ) { linearCount = endTime - paintedTime; } // linearCount is now number of mono 16 bit samples (L and R) to xfer. linearCount <<= 1; // transfer mono 16bit samples multiplying each sample by volume. for ( i=0; i> 8; *pOutput++ = CLIP( val ); // R Channel val = ( pFront[i+1] * volumeFactor ) >> 8; *pOutput++ = CLIP( val ); } return linearCount * DeviceSampleBytes(); } //----------------------------------------------------------------------------- // Fill the output buffer with interleaved surround samples //----------------------------------------------------------------------------- int CAudioXAudio::TransferSurroundInterleaved( const portable_samplepair_t *pFrontBuffer, const portable_samplepair_t *pRearBuffer, const portable_samplepair_t *pCenterBuffer, int paintedTime, int endTime, char *pOutputBuffer ) { int linearCount; int i, j; int val; int volumeFactor = S_GetMasterVolume() * 256; int *pFront = (int *)pFrontBuffer; int *pRear = (int *)pRearBuffer; int *pCenter = (int *)pCenterBuffer; short *pOutput = (short *)pOutputBuffer; // number of mono samples per channel // number of sequential samples that can be wrriten linearCount = m_bufferSizeBytes/( DeviceSampleBytes() * DeviceChannels() ); // clamp output count to requested number of samples if ( linearCount > endTime - paintedTime ) { linearCount = endTime - paintedTime; } for ( i = 0, j = 0; i < linearCount; i++, j += 2 ) { // FL val = ( pFront[j] * volumeFactor ) >> 8; *pOutput++ = CLIP( val ); // FR val = ( pFront[j+1] * volumeFactor ) >> 8; *pOutput++ = CLIP( val ); // Center val = ( pCenter[j] * volumeFactor) >> 8; *pOutput++ = CLIP( val ); // Let the hardware mix the sub from the main channels since // we don't have any sub-specific sounds, or direct sub-addressing *pOutput++ = 0; // RL val = ( pRear[j] * volumeFactor ) >> 8; *pOutput++ = CLIP( val ); // RR val = ( pRear[j+1] * volumeFactor ) >> 8; *pOutput++ = CLIP( val ); } return linearCount * DeviceSampleBytes() * DeviceChannels(); } //----------------------------------------------------------------------------- // Transfer up to a full paintbuffer (PAINTBUFFER_SIZE) of samples out to the xaudio buffer(s). //----------------------------------------------------------------------------- void CAudioXAudio::TransferSamples( int endTime ) { XAUDIO2_BUFFER *pBuffer; if ( m_BufferHead - m_BufferTail >= MAX_XAUDIO2_BUFFERS ) { DevWarning( "XAudio: No Free Buffers!\n" ); return; } int sampleCount = endTime - g_paintedtime; if ( sampleCount > XAUDIO2_BUFFER_SAMPLES ) { DevWarning( "XAudio: Overflowed mix buffer!\n" ); endTime = g_paintedtime + XAUDIO2_BUFFER_SAMPLES; } unsigned int nBuffer = m_BufferHead++; pBuffer = &m_Buffers[nBuffer % MAX_XAUDIO2_BUFFERS]; if ( !m_bSurround ) { pBuffer->AudioBytes = TransferStereo( PAINTBUFFER, g_paintedtime, endTime, (char *)pBuffer->pAudioData ); } else { pBuffer->AudioBytes = TransferSurroundInterleaved( PAINTBUFFER, REARPAINTBUFFER, CENTERPAINTBUFFER, g_paintedtime, endTime, (char *)pBuffer->pAudioData ); } // submit buffer m_pSourceVoice->SubmitSourceBuffer( pBuffer ); } //----------------------------------------------------------------------------- // Get our device name //----------------------------------------------------------------------------- const char *CAudioXAudio::DeviceName( void ) { if ( m_bSurround ) { return "XAudio: 5.1 Channel Surround"; } return "XAudio: Stereo"; } CXboxVoice::CXboxVoice() { m_pXHVEngine = NULL; } //----------------------------------------------------------------------------- // Initialize Voice //----------------------------------------------------------------------------- void CXboxVoice::VoiceInit( void ) { if ( !m_pXHVEngine ) { // Set the processing modes XHV_PROCESSING_MODE rgMode = XHV_VOICECHAT_MODE; // Set up parameters for the voice chat engine XHV_INIT_PARAMS xhvParams = {0}; xhvParams.dwMaxRemoteTalkers = MAX_PLAYERS; xhvParams.dwMaxLocalTalkers = XUSER_MAX_COUNT; xhvParams.localTalkerEnabledModes = &rgMode; xhvParams.remoteTalkerEnabledModes = &rgMode; xhvParams.dwNumLocalTalkerEnabledModes = 1; xhvParams.dwNumRemoteTalkerEnabledModes = 1; xhvParams.pXAudio2 = CAudioXAudio::m_pSingleton->GetXAudio2(); // Create the engine HRESULT hr = XHV2CreateEngine( &xhvParams, NULL, &m_pXHVEngine ); if ( hr != S_OK ) { Warning( "Couldn't load XHV engine!\n" ); } } VoiceResetLocalData( ); } void CXboxVoice::VoiceShutdown( void ) { if ( !m_pXHVEngine ) return; m_pXHVEngine->Release(); m_pXHVEngine = NULL; } void CXboxVoice::AddPlayerToVoiceList( CClientInfo *pClient, bool bLocal ) { XHV_PROCESSING_MODE local_proc_mode = XHV_VOICECHAT_MODE; for ( int i = 0; i < pClient->m_cPlayers; ++i ) { if ( pClient->m_xuids[i] == 0 ) continue; if ( bLocal == true ) { if ( m_pXHVEngine->RegisterLocalTalker( pClient->m_iControllers[i] ) == S_OK ) { m_pXHVEngine->StartLocalProcessingModes( pClient->m_iControllers[i], &local_proc_mode, 1 ); } } else { if ( m_pXHVEngine->RegisterRemoteTalker( pClient->m_xuids[i], NULL, NULL, NULL ) == S_OK ) { m_pXHVEngine->StartRemoteProcessingModes( pClient->m_xuids[i], &local_proc_mode, 1 ); } } } } void CXboxVoice::RemovePlayerFromVoiceList( CClientInfo *pClient, bool bLocal ) { for ( int i = 0; i < pClient->m_cPlayers; ++i ) { if ( pClient->m_xuids[i] == 0 ) continue; if ( bLocal == true ) { m_pXHVEngine->UnregisterLocalTalker( pClient->m_iControllers[i] ); } else { m_pXHVEngine->UnregisterRemoteTalker( pClient->m_xuids[i] ); } } } void CXboxVoice::PlayIncomingVoiceData( XUID xuid, const byte *pbData, DWORD pdwSize ) { XUID localXUID; XUserGetXUID( XBX_GetPrimaryUserId(), &localXUID ); //Hack: Don't play stuff that comes from ourselves. if ( localXUID == xuid ) return; m_pXHVEngine->SubmitIncomingChatData( xuid, pbData, &pdwSize ); } void CXboxVoice::UpdateHUDVoiceStatus( void ) { for ( int iClient = 0; iClient < cl.m_nMaxClients; iClient++ ) { bool bSelf = (cl.m_nPlayerSlot == iClient); int iIndex = iClient + 1; XUID id = g_pMatchmaking->PlayerIdToXuid( iIndex ); if ( id != 0 ) { bool bTalking = false; if ( bSelf == true ) { //Make sure the player's own label is not on. g_pSoundServices->OnChangeVoiceStatus( iIndex, false ); iIndex = -1; if ( IsPlayerTalking( XBX_GetPrimaryUserId(), true ) ) { bTalking = true; } } else { if ( IsPlayerTalking( id, false ) ) { bTalking = true; } } g_pSoundServices->OnChangeVoiceStatus( iIndex, bTalking ); } else { g_pSoundServices->OnChangeVoiceStatus( iIndex, false ); } } } bool CXboxVoice::VoiceUpdateData( void ) { DWORD dwNumPackets = 0; DWORD dwBytes = 0; WORD wVoiceBytes = 0; bool bShouldSend = false; DWORD dwVoiceFlags = m_pXHVEngine->GetDataReadyFlags(); //Update UI stuff. UpdateHUDVoiceStatus(); for ( uint i = 0; i < XUSER_MAX_COUNT; ++i ) { // We currently only allow one player per console if ( i != XBX_GetPrimaryUserId() ) { continue; } if ( IsHeadsetPresent( i ) == false ) continue; if ( !(dwVoiceFlags & ( 1 << i )) ) continue; dwBytes = m_ChatBufferSize - m_wLocalDataSize; if( dwBytes < XHV_VOICECHAT_MODE_PACKET_SIZE ) { bShouldSend = true; } else { m_pXHVEngine->GetLocalChatData( i, m_ChatBuffer + m_wLocalDataSize, &dwBytes, &dwNumPackets ); m_wLocalDataSize += ((WORD)dwBytes) & MAXWORD; if( m_wLocalDataSize > ( ( m_ChatBufferSize * 7 ) / 10 ) ) { bShouldSend = true; } } wVoiceBytes += m_wLocalDataSize & MAXWORD; break; } return bShouldSend || ( wVoiceBytes && ( GetTickCount() - m_dwLastVoiceSend ) > MAX_VOICE_BUFFER_TIME ); } void CXboxVoice::SetPlaybackPriority( XUID remoteTalker, DWORD dwUserIndex, XHV_PLAYBACK_PRIORITY playbackPriority ) { m_pXHVEngine->SetPlaybackPriority( remoteTalker, dwUserIndex, playbackPriority ); } void CXboxVoice::GetRemoteTalkers( int *pNumTalkers, XUID *pRemoteTalkers ) { m_pXHVEngine->GetRemoteTalkers( (DWORD*)pNumTalkers, pRemoteTalkers ); } void CXboxVoice::GetVoiceData( CLC_VoiceData *pMessage ) { byte *puchVoiceData = NULL; pMessage->m_nLength = m_wLocalDataSize; XUserGetXUID( XBX_GetPrimaryUserId(), &pMessage->m_xuid ); puchVoiceData = m_ChatBuffer; pMessage->m_DataOut.StartWriting( puchVoiceData, pMessage->m_nLength ); pMessage->m_nLength *= 8; pMessage->m_DataOut.SeekToBit( pMessage->m_nLength ); // set correct writing position } void CXboxVoice::VoiceSendData( INetChannel *pChannel ) { CLC_VoiceData voiceMsg; GetVoiceData( &voiceMsg ); if ( pChannel ) { pChannel->SendNetMsg( voiceMsg, false, true ); VoiceResetLocalData(); } } void CXboxVoice::VoiceResetLocalData( void ) { m_wLocalDataSize = 0; m_dwLastVoiceSend = GetTickCount(); Q_memset( m_ChatBuffer, 0, m_ChatBufferSize ); } bool CXboxVoice::IsPlayerTalking( XUID uid, bool bLocal ) { if ( bLocal == true ) { return m_pXHVEngine->IsLocalTalking( XBX_GetPrimaryUserId() ); } else { return !g_pMatchmaking->IsPlayerMuted( XBX_GetPrimaryUserId(), uid ) && m_pXHVEngine->IsRemoteTalking( uid ); } return false; } bool CXboxVoice::IsHeadsetPresent( int id ) { return m_pXHVEngine->IsHeadsetPresent( id ); } void CXboxVoice::RemoveAllTalkers( CClientInfo *pLocal ) { int numRemoteTalkers; XUID remoteTalkers[MAX_PLAYERS]; GetRemoteTalkers( &numRemoteTalkers, remoteTalkers ); for ( int iRemote = 0; iRemote < numRemoteTalkers; iRemote++ ) { m_pXHVEngine->UnregisterRemoteTalker( remoteTalkers[iRemote] ); } if ( pLocal ) { for ( int i = 0; i < pLocal->m_cPlayers; ++i ) { if ( pLocal->m_xuids[i] == 0 ) continue; m_pXHVEngine->UnregisterLocalTalker( pLocal->m_iControllers[i] ); } } }