244 lines
7.2 KiB
C++
244 lines
7.2 KiB
C++
//========= Copyright © 1996-2010, Valve LLC, All rights reserved. ============
|
|
//
|
|
// Purpose:Class for P2P voice chat
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================
|
|
|
|
#include "stdafx.h"
|
|
#include "voicechat.h"
|
|
|
|
|
|
CVoiceChat::CVoiceChat( IGameEngine *pGameEngine )
|
|
{
|
|
m_pGameEngine = pGameEngine;
|
|
m_bIsActive = false;
|
|
m_ulLastTimeTalked = 0;
|
|
m_hVoiceLoopback = 0;
|
|
}
|
|
|
|
|
|
CVoiceChat::~CVoiceChat()
|
|
{
|
|
m_pGameEngine = NULL;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CVoiceChat::RunFrame()
|
|
{
|
|
if ( m_bIsActive )
|
|
{
|
|
// read local microphone input
|
|
uint32 nBytesAvailable = 0;
|
|
EVoiceResult res = SteamUser()->GetAvailableVoice( &nBytesAvailable, NULL, 0 );
|
|
|
|
if ( res == k_EVoiceResultOK && nBytesAvailable > 0 )
|
|
{
|
|
uint32 nBytesWritten = 0;
|
|
MsgVoiceChatData_t msg;
|
|
|
|
// don't send more then 1 KB at a time
|
|
uint8 buffer[ 1024+sizeof(msg) ];
|
|
|
|
res = SteamUser()->GetVoice( true, buffer+sizeof(msg), 1024, &nBytesWritten, false, NULL, 0, NULL, 0 );
|
|
|
|
if ( res == k_EVoiceResultOK && nBytesWritten > 0 )
|
|
{
|
|
// assemble message. note that we don't fill in the SteamID
|
|
// here. The server will know who sent
|
|
msg.SetDataLength( nBytesWritten );
|
|
memcpy( buffer, &msg, sizeof(msg) );
|
|
|
|
// Send a message to the server with the data, server will broadcast this data on to all other clients.
|
|
SteamNetworkingSockets()->SendMessageToConnection( m_hConnServer, buffer, sizeof(msg)+nBytesWritten, k_nSteamNetworkingSend_UnreliableNoDelay, nullptr );
|
|
|
|
m_ulLastTimeTalked = m_pGameEngine->GetGameTickCount();
|
|
|
|
// if local voice loopback is enabled, play it back now
|
|
if ( m_hVoiceLoopback != 0 )
|
|
{
|
|
// Uncompress the voice data, buffer holds up to 1 second of data
|
|
uint32 numUncompressedBytes = 0;
|
|
const uint8* pVoiceData = (const uint8*) buffer;
|
|
pVoiceData += sizeof(MsgVoiceChatData_t);
|
|
|
|
res = SteamUser()->DecompressVoice( pVoiceData , nBytesWritten,
|
|
m_ubUncompressedVoice, sizeof( m_ubUncompressedVoice ), &numUncompressedBytes, VOICE_OUTPUT_SAMPLE_RATE );
|
|
|
|
if ( res == k_EVoiceResultOK && numUncompressedBytes > 0 )
|
|
{
|
|
m_pGameEngine->AddVoiceData( m_hVoiceLoopback, m_ubUncompressedVoice, numUncompressedBytes );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CVoiceChat::HandleVoiceChatData( const void *pMessage )
|
|
{
|
|
const MsgVoiceChatData_t *pMsgVoiceData = (const MsgVoiceChatData_t *) pMessage;
|
|
CSteamID fromSteamID = pMsgVoiceData->GetSteamID();
|
|
|
|
std::map< uint64, VoiceChatConnection_t >::iterator iter;
|
|
iter = m_MapConnections.find( fromSteamID.ConvertToUint64() );
|
|
if (iter == m_MapConnections.end())
|
|
return;
|
|
|
|
VoiceChatConnection_t &chatClient = iter->second;
|
|
chatClient.ulLastReceiveVoiceTime = m_pGameEngine->GetGameTickCount();
|
|
|
|
// Uncompress the voice data, buffer holds up to 1 second of data
|
|
uint8 pbUncompressedVoice[ VOICE_OUTPUT_SAMPLE_RATE * BYTES_PER_SAMPLE ];
|
|
uint32 numUncompressedBytes = 0;
|
|
const uint8* pVoiceData = (const uint8*) pMessage;
|
|
pVoiceData += sizeof(MsgVoiceChatData_t);
|
|
|
|
EVoiceResult res = SteamUser()->DecompressVoice( pVoiceData , pMsgVoiceData->GetDataLength(),
|
|
pbUncompressedVoice, sizeof( pbUncompressedVoice ), &numUncompressedBytes, VOICE_OUTPUT_SAMPLE_RATE );
|
|
|
|
if ( res == k_EVoiceResultOK && numUncompressedBytes > 0 )
|
|
{
|
|
// play it again Sam
|
|
if ( chatClient.hVoiceChannel == 0 )
|
|
{
|
|
chatClient.hVoiceChannel = m_pGameEngine->HCreateVoiceChannel();
|
|
}
|
|
|
|
m_pGameEngine->AddVoiceData( chatClient.hVoiceChannel, pbUncompressedVoice, numUncompressedBytes );
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CVoiceChat::MarkAllPlayersInactive()
|
|
{
|
|
std::map< uint64, VoiceChatConnection_t >::iterator iter;
|
|
for( iter = m_MapConnections.begin(); iter != m_MapConnections.end(); ++iter )
|
|
{
|
|
iter->second.bActive = false;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CVoiceChat::MarkPlayerAsActive( CSteamID steamID )
|
|
{
|
|
if ( !m_bIsActive )
|
|
return;
|
|
|
|
if ( m_SteamIDLocalUser == steamID )
|
|
return;
|
|
|
|
std::map< uint64, VoiceChatConnection_t >::iterator iter;
|
|
iter = m_MapConnections.find( steamID.ConvertToUint64() );
|
|
if ( iter != m_MapConnections.end() )
|
|
{
|
|
// player already has a session object, no new object created
|
|
iter->second.bActive = true;
|
|
return;
|
|
}
|
|
|
|
/*char szText[100];
|
|
sprintf_safe(szText, "CVoiceChat::AddPlayerToSession: %s.\n", SteamFriends()->GetFriendPersonaName( steamID ) );
|
|
OutputDebugString( szText ); */
|
|
|
|
VoiceChatConnection_t session;
|
|
session.ulLastReceiveVoiceTime = 0;
|
|
session.hVoiceChannel = 0;
|
|
session.bActive = true;
|
|
|
|
m_MapConnections[ steamID.ConvertToUint64() ] = session;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CVoiceChat::IsPlayerTalking( CSteamID steamID )
|
|
{
|
|
|
|
if ( steamID == m_SteamIDLocalUser )
|
|
{
|
|
// thats ourself
|
|
if ( m_ulLastTimeTalked + 250 > m_pGameEngine->GetGameTickCount() )
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
std::map< uint64, VoiceChatConnection_t >::iterator iter;
|
|
iter = m_MapConnections.find( steamID.ConvertToUint64() );
|
|
if ( iter != m_MapConnections.end() )
|
|
{
|
|
if ( (iter->second.ulLastReceiveVoiceTime + 250) > m_pGameEngine->GetGameTickCount() )
|
|
{
|
|
// user talked less then 250msec ago, assume still active
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CVoiceChat::StartVoiceChat()
|
|
{
|
|
if ( !m_bIsActive )
|
|
{
|
|
m_SteamIDLocalUser = SteamUser()->GetSteamID();
|
|
|
|
SteamUser()->StartVoiceRecording();
|
|
|
|
m_bIsActive = true;
|
|
|
|
// here you can enable optional local voice loopback:
|
|
// m_hVoiceLoopback = m_pGameEngine->HCreateVoiceChannel();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CVoiceChat::StopVoiceChat()
|
|
{
|
|
if ( m_bIsActive )
|
|
{
|
|
std::map< uint64, VoiceChatConnection_t >::iterator iter;
|
|
for( iter = m_MapConnections.begin(); iter != m_MapConnections.end(); ++iter )
|
|
{
|
|
CSteamID steamID( iter->first );
|
|
m_pGameEngine->DestroyVoiceChannel( iter->second.hVoiceChannel );
|
|
}
|
|
|
|
m_MapConnections.clear();
|
|
|
|
if ( m_hVoiceLoopback )
|
|
{
|
|
m_pGameEngine->DestroyVoiceChannel( m_hVoiceLoopback );
|
|
m_hVoiceLoopback = 0;
|
|
}
|
|
|
|
SteamUser()->StopVoiceRecording();
|
|
|
|
m_bIsActive = false;
|
|
}
|
|
}
|