summaryrefslogtreecommitdiff
path: root/engine/audio/private/snd_dev_xaudio.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engine/audio/private/snd_dev_xaudio.cpp')
-rw-r--r--engine/audio/private/snd_dev_xaudio.cpp872
1 files changed, 872 insertions, 0 deletions
diff --git a/engine/audio/private/snd_dev_xaudio.cpp b/engine/audio/private/snd_dev_xaudio.cpp
new file mode 100644
index 0000000..1601701
--- /dev/null
+++ b/engine/audio/private/snd_dev_xaudio.cpp
@@ -0,0 +1,872 @@
+//========= 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<linearCount; i+=2 )
+ {
+ // L Channel
+ val = ( pFront[i] * volumeFactor ) >> 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] );
+ }
+ }
+} \ No newline at end of file