This code was not tested and breaks in Release builds, reverting to restore
functionality of the nightly. All in-game menus do not work and generating
a world crashes.
This reverts commit a9be52c41a.
1106 lines
31 KiB
C++
1106 lines
31 KiB
C++
#include "stdafx.h"
|
|
#include <libsysmodule.h>
|
|
#include <rudp.h>
|
|
#include <audioin.h>
|
|
#include <audioout.h>
|
|
#include <fios2.h>
|
|
|
|
#include "SonyVoiceChat_Orbis.h"
|
|
|
|
std::vector<SQRVoiceConnection*> SonyVoiceChat_Orbis::m_remoteConnections;
|
|
bool SonyVoiceChat_Orbis::m_bVoiceStarted = false;
|
|
int SonyVoiceChat_Orbis::m_numLocalDevicesConnected = 0;
|
|
SQRLocalVoiceDevice SonyVoiceChat_Orbis::m_localVoiceDevices[MAX_LOCAL_PLAYER_COUNT];
|
|
uint32_t SonyVoiceChat_Orbis::m_voiceOutPort;
|
|
bool SonyVoiceChat_Orbis::m_forceSendPacket = false; // force a packet across the network, even if there's no data, so we can update flags
|
|
RingBuffer SonyVoiceChat_Orbis::m_recordRingBuffer(sc_ringBufferSize);
|
|
VoicePacket::Flags SonyVoiceChat_Orbis::m_localPlayerFlags[MAX_LOCAL_PLAYER_COUNT];
|
|
bool SonyVoiceChat_Orbis::m_bInitialised = false;
|
|
CRITICAL_SECTION SonyVoiceChat_Orbis::m_csRemoteConnections;
|
|
|
|
// sample related variables
|
|
SceVoiceStartParam startParam;
|
|
int32_t playSize = 0;
|
|
|
|
static const int sc_thresholdValue = 100;
|
|
|
|
static const bool sc_verbose = false;
|
|
|
|
// #define _USE_PCM_AUDIO_
|
|
//#define USE_PCM_MIC_DATA
|
|
|
|
|
|
|
|
int g_loadedPCMVoiceDataSizes[4];
|
|
int g_loadedPCMVoiceDataPos[4];
|
|
char* g_loadedPCMVoiceData[4];
|
|
|
|
static void CreatePort(uint32_t *portId, const SceVoicePortParam *pArg)
|
|
{
|
|
C4JThread::PushAffinityAllCores(); // PS4 only
|
|
|
|
int err = sceVoiceCreatePort( portId, pArg );
|
|
assert(err == SCE_OK);
|
|
assert(*portId != SCE_VOICE_INVALID_PORT_ID);
|
|
C4JThread::PopAffinity(); // PS4 only
|
|
}
|
|
|
|
static void DeletePort(uint32_t& port)
|
|
{
|
|
int32_t result;
|
|
if (port != SCE_VOICE_INVALID_PORT_ID)
|
|
{
|
|
result = sceVoiceDeletePort( port );
|
|
if (result != SCE_OK)
|
|
{
|
|
app.DebugPrintf("sceVoiceDeletePort failed %0x\n", result);
|
|
assert(0);
|
|
}
|
|
port = SCE_VOICE_INVALID_PORT_ID;
|
|
}
|
|
}
|
|
|
|
|
|
void LoadPCMVoiceData()
|
|
{
|
|
for(int i=0;i<4;i++)
|
|
{
|
|
char filename[64];
|
|
sprintf(filename, "voice%d.pcm", i+1);
|
|
HANDLE file = CreateFile(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
DWORD dwHigh=0;
|
|
g_loadedPCMVoiceDataSizes[i] = GetFileSize(file,&dwHigh);
|
|
|
|
if(g_loadedPCMVoiceDataSizes[i]!=0)
|
|
{
|
|
g_loadedPCMVoiceData[i] = new char[g_loadedPCMVoiceDataSizes[i]];
|
|
DWORD bytesRead;
|
|
BOOL bSuccess = ReadFile(file, g_loadedPCMVoiceData[i], g_loadedPCMVoiceDataSizes[i], &bytesRead, NULL);
|
|
assert(bSuccess);
|
|
}
|
|
g_loadedPCMVoiceDataPos[i] = 0;
|
|
}
|
|
}
|
|
|
|
|
|
void SonyVoiceChat_Orbis::init()
|
|
{
|
|
int returnCode = SCE_OK;
|
|
SceUserServiceUserId initialUserId;
|
|
|
|
if (returnCode < 0)
|
|
{
|
|
app.DebugPrintf("Error: sceSysmoduleLoadModule(SCE_SYSMODULE_VOICE), ret 0x%08x\n", returnCode);
|
|
assert(0);
|
|
}
|
|
|
|
|
|
SceVoiceInitParam params;
|
|
SceVoicePortParam portArgs;
|
|
memset( ¶ms, 0, sizeof(params) );
|
|
params.appType = SCE_VOICE_APPTYPE_GAME;
|
|
params.onEvent = 0;
|
|
returnCode = sceVoiceInit( ¶ms , SCE_VOICE_VERSION_100);
|
|
if (returnCode < 0)
|
|
{
|
|
app.DebugPrintf("Error: sceVoiceInit(), ret 0x%08x\n", returnCode);
|
|
assert(0);
|
|
}
|
|
|
|
#ifdef _USE_PCM_AUDIO_
|
|
portArgs.portType = SCE_VOICE_PORTTYPE_OUT_PCMAUDIO;
|
|
portArgs.bMute = false;
|
|
portArgs.threshold = 0;
|
|
portArgs.volume = 1.0f;
|
|
portArgs.pcmaudio.format.dataType = SCE_VOICE_PCM_SHORT_LITTLE_ENDIAN;
|
|
portArgs.pcmaudio.format.sampleRate = SCE_VOICE_SAMPLINGRATE_16000;
|
|
portArgs.pcmaudio.bufSize = 4096;
|
|
#else
|
|
portArgs.portType = SCE_VOICE_PORTTYPE_OUT_VOICE;
|
|
portArgs.bMute = false;
|
|
portArgs.threshold = 0;
|
|
portArgs.volume = 1.0f;
|
|
portArgs.voice.bitrate = VOICE_ENCODED_FORMAT;
|
|
#endif
|
|
CreatePort( &m_voiceOutPort, &portArgs );
|
|
|
|
start();
|
|
m_bInitialised = true;
|
|
InitializeCriticalSection(&m_csRemoteConnections);
|
|
}
|
|
|
|
|
|
void SonyVoiceChat_Orbis::start()
|
|
{
|
|
if( m_bVoiceStarted == false)
|
|
{
|
|
startParam.container = malloc(SCE_VOICE_MEMORY_CONTAINER_SIZE);
|
|
startParam.memSize = SCE_VOICE_MEMORY_CONTAINER_SIZE;
|
|
|
|
int err;
|
|
|
|
C4JThread::PushAffinityAllCores(); // PS4 only
|
|
err = sceVoiceStart(&startParam);
|
|
assert(err == SCE_OK);
|
|
C4JThread::PopAffinity(); // PS4 only
|
|
|
|
m_bVoiceStarted = true;
|
|
}
|
|
}
|
|
|
|
void SonyVoiceChat_Orbis::checkFinished()
|
|
{
|
|
EnterCriticalSection(&m_csRemoteConnections);
|
|
|
|
for(int i=0;i<m_remoteConnections.size();i++)
|
|
{
|
|
assert(m_remoteConnections[i]->m_bFlaggedForShutdown);
|
|
}
|
|
// assert(m_numLocalDevicesConnected == 0);
|
|
|
|
LeaveCriticalSection(&m_csRemoteConnections);
|
|
}
|
|
|
|
|
|
void SonyVoiceChat_Orbis::shutdown()
|
|
{
|
|
m_bInitialised = false;
|
|
int32_t result;
|
|
result = sceVoiceStop();
|
|
assert(result == SCE_OK);
|
|
result = sceVoiceEnd();
|
|
assert(result == SCE_OK);
|
|
free(startParam.container);
|
|
|
|
int returnCode = sceSysmoduleUnloadModule(SCE_SYSMODULE_VOICE);
|
|
if (returnCode < 0)
|
|
{
|
|
app.DebugPrintf("Error: sceSysmoduleUnloadModule(SCE_SYSMODULE_VOICE), ret 0x%08x\n", returnCode);
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
void SonyVoiceChat_Orbis::setEnabled( bool bEnabled )
|
|
{
|
|
}
|
|
|
|
|
|
|
|
// Internal send function. This attempts to send as many elements in the queue as possible until the write function tells us that we can't send any more. This way,
|
|
// we are guaranteed that if there *is* anything more in the queue left to send, we'll get a CELL_RUDP_CONTEXT_EVENT_WRITABLE event when whatever we've managed to
|
|
// send here is complete, and can continue on.
|
|
void SQRVoiceConnection::SendMoreInternal()
|
|
{
|
|
EnterCriticalSection(&m_csQueue);
|
|
bool keepSending;
|
|
do
|
|
{
|
|
keepSending = false;
|
|
if( m_sendQueue.size() > 0)
|
|
{
|
|
// Attempt to send the full data in the first element in our queue
|
|
unsigned char *data= m_sendQueue.front().current;
|
|
int dataSize = m_sendQueue.front().end - m_sendQueue.front().current;
|
|
int ret = sceRudpWrite( m_rudpCtx, data, dataSize, 0);//CELL_RUDP_MSG_LATENCY_CRITICAL );
|
|
int wouldBlockFlag = SCE_RUDP_ERROR_WOULDBLOCK;
|
|
|
|
if( ret == dataSize )
|
|
{
|
|
// Fully sent, remove from queue - will loop in the while loop to see if there's anything else in the queue we could send
|
|
delete [] m_sendQueue.front().start;
|
|
m_sendQueue.pop();
|
|
if( m_sendQueue.size() )
|
|
{
|
|
keepSending = true;
|
|
}
|
|
}
|
|
else if( ( ret >= 0 ) || ( ret == wouldBlockFlag ) )
|
|
{
|
|
|
|
|
|
// Things left to send - adjust this element in the queue
|
|
int remainingBytes;
|
|
if( ret >= 0 )
|
|
{
|
|
// Only ret bytes sent so far
|
|
remainingBytes = dataSize - ret;
|
|
assert(remainingBytes > 0 );
|
|
}
|
|
else
|
|
{
|
|
// Is CELL_RUDP_ERROR_WOULDBLOCK, nothing has yet been sent
|
|
remainingBytes = dataSize;
|
|
}
|
|
m_sendQueue.front().current = m_sendQueue.front().end - remainingBytes;
|
|
}
|
|
}
|
|
} while (keepSending);
|
|
LeaveCriticalSection(&m_csQueue);
|
|
}
|
|
|
|
void SQRVoiceConnection::SendInternal(const void *data, unsigned int dataSize)
|
|
{
|
|
EnterCriticalSection(&m_csQueue);
|
|
|
|
QueuedSendBlock sendBlock;
|
|
|
|
unsigned char *dataCurrent = (unsigned char *)data;
|
|
unsigned int dataRemaining = dataSize;
|
|
|
|
while( dataRemaining )
|
|
{
|
|
int dataSize = dataRemaining;
|
|
if( dataSize > SNP_MAX_PAYLOAD ) dataSize = SNP_MAX_PAYLOAD;
|
|
sendBlock.start = new unsigned char [dataSize];
|
|
sendBlock.end = sendBlock.start + dataSize;
|
|
sendBlock.current = sendBlock.start;
|
|
memcpy( sendBlock.start, dataCurrent, dataSize);
|
|
m_sendQueue.push(sendBlock);
|
|
dataRemaining -= dataSize;
|
|
dataCurrent += dataSize;
|
|
}
|
|
|
|
// app.DebugPrintf("voice sent %d bytes\n", dataSize);
|
|
|
|
// Now try and send as much as we can
|
|
SendMoreInternal();
|
|
|
|
LeaveCriticalSection(&m_csQueue);
|
|
}
|
|
|
|
void SQRVoiceConnection::readRemoteData()
|
|
{
|
|
unsigned int dataSize = sceRudpGetSizeReadable(m_rudpCtx);
|
|
if( dataSize > 0 )
|
|
{
|
|
VoicePacket packet;
|
|
int bytesRead = sceRudpRead( m_rudpCtx, &packet, dataSize, 0, NULL );
|
|
unsigned int writeSize;
|
|
if( bytesRead > 0 )
|
|
{
|
|
// app.DebugPrintf("voice received %d bytes\n", bytesRead);
|
|
writeSize = bytesRead;
|
|
packet.verifyData(bytesRead, 19);
|
|
addPacket(packet);
|
|
// m_playRingBuffer.Write((char*)data, writeSize);
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SQRVoiceConnection::SQRVoiceConnection( int rudpCtx, SceNpMatching2RoomMemberId remoteRoomMemberId )
|
|
: m_rudpCtx(rudpCtx)
|
|
, m_remoteRoomMemberId(remoteRoomMemberId)
|
|
, m_bConnected(false)
|
|
, m_headsetConnectionMask(0)
|
|
, m_playRingBuffer(sc_ringBufferSize)
|
|
{
|
|
InitializeCriticalSection(&m_csQueue);
|
|
InitializeCriticalSection(&m_csPacketQueue);
|
|
|
|
SceVoiceInitParam params;
|
|
SceVoicePortParam portArgs;
|
|
#ifdef _USE_PCM_AUDIO_
|
|
portArgs.portType = SCE_VOICE_PORTTYPE_IN_PCMAUDIO;
|
|
portArgs.bMute = false;
|
|
portArgs.threshold = 100;
|
|
portArgs.volume = 1.0f;
|
|
portArgs.pcmaudio.format.sampleRate= SCE_VOICE_SAMPLINGRATE_16000;
|
|
portArgs.pcmaudio.format.dataType = SCE_VOICE_PCM_SHORT_LITTLE_ENDIAN;
|
|
portArgs.pcmaudio.bufSize = 4096;
|
|
#else
|
|
portArgs.portType = SCE_VOICE_PORTTYPE_IN_VOICE;
|
|
portArgs.bMute = false;
|
|
portArgs.threshold = sc_thresholdValue; // compensate network jitter
|
|
portArgs.volume = 1.0f;
|
|
portArgs.voice.bitrate = VOICE_ENCODED_FORMAT;
|
|
#endif
|
|
CreatePort( &m_voiceInPort, &portArgs );
|
|
m_nextExpectedFrameIndex = 0;
|
|
m_bFlaggedForShutdown = false;
|
|
}
|
|
|
|
SQRVoiceConnection::~SQRVoiceConnection()
|
|
{
|
|
DeleteCriticalSection(&m_csQueue);
|
|
DeleteCriticalSection(&m_csPacketQueue);
|
|
extern int g_numRUDPContextsBound;
|
|
int err = sceRudpTerminate( m_rudpCtx );
|
|
app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpTerminate\n" );
|
|
|
|
app.DebugPrintf("-----------------------------\n");
|
|
if(err<0)
|
|
app.DebugPrintf("Voice rudp context failed to delete!!! %d\n", m_rudpCtx);
|
|
else
|
|
{
|
|
g_numRUDPContextsBound--;
|
|
app.DebugPrintf("Voice rudp context deleted %d\n", m_rudpCtx);
|
|
}
|
|
app.DebugPrintf("-----------------------------\n");
|
|
|
|
DeletePort(m_voiceInPort);
|
|
|
|
}
|
|
|
|
bool SQRVoiceConnection::getNextPacket( VoicePacket& packet )
|
|
{
|
|
EnterCriticalSection(&m_csPacketQueue);
|
|
bool retVal = false;
|
|
if(m_receivedVoicePackets.size() > 0)
|
|
{
|
|
retVal = true;
|
|
packet = m_receivedVoicePackets.front();
|
|
m_receivedVoicePackets.pop();
|
|
}
|
|
LeaveCriticalSection(&m_csPacketQueue);
|
|
return retVal;
|
|
}
|
|
|
|
void SQRVoiceConnection::addPacket( VoicePacket& packet )
|
|
{
|
|
EnterCriticalSection(&m_csPacketQueue);
|
|
m_receivedVoicePackets.push(packet);
|
|
LeaveCriticalSection(&m_csPacketQueue);
|
|
}
|
|
|
|
int g_frameNum = 0;
|
|
bool g_bRecording = false;
|
|
|
|
|
|
uint32_t frameSendIndex = 0;
|
|
uint32_t lastReadFrameCnt = 0;
|
|
|
|
|
|
void PrintAllOutputVoiceStates( std::vector<SQRVoiceConnection*>& connections)
|
|
{
|
|
for(int rIdx=0;rIdx<connections.size(); rIdx++)
|
|
{
|
|
SQRVoiceConnection* pVoice = connections[rIdx];
|
|
SceVoiceBasePortInfo portInfo;
|
|
int result = sceVoiceGetPortInfo(pVoice->m_voiceInPort, &portInfo );
|
|
static SceVoicePortState lastPortState = SCE_VOICE_PORTSTATE_IDLE;
|
|
if(portInfo.state != lastPortState)
|
|
{
|
|
lastPortState = portInfo.state;
|
|
switch(portInfo.state)
|
|
{
|
|
case SCE_VOICE_PORTSTATE_IDLE:
|
|
app.DebugPrintf(" ----- SCE_VOICE_PORTSTATE_IDLE\n");
|
|
break;
|
|
case SCE_VOICE_PORTSTATE_BUFFERING:
|
|
app.DebugPrintf(" ----- SCE_VOICE_PORTSTATE_BUFFERING\n");
|
|
break;
|
|
case SCE_VOICE_PORTSTATE_RUNNING:
|
|
app.DebugPrintf(" ----- SCE_VOICE_PORTSTATE_RUNNING\n");
|
|
break;
|
|
case SCE_VOICE_PORTSTATE_READY:
|
|
app.DebugPrintf(" ----- SCE_VOICE_PORTSTATE_READY\n");
|
|
break;
|
|
case SCE_VOICE_PORTSTATE_NULL:
|
|
default:
|
|
app.DebugPrintf(" ----- SCE_VOICE_PORTSTATE_NULL\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void SonyVoiceChat_Orbis::sendPCMMicData()
|
|
{
|
|
int32_t result;
|
|
uint32_t outputPortBytes;
|
|
VoicePacket packetToSend;
|
|
uint32_t readSize;
|
|
SceVoiceBasePortInfo portInfo;
|
|
memset( &portInfo, 0, sizeof(portInfo) );
|
|
uint16_t frameGap = 0;
|
|
|
|
DWORD tick = GetTickCount();
|
|
static DWORD lastTick = 0;
|
|
int numFrames = ceilf((tick - lastTick)/16.0f);
|
|
lastTick = tick;
|
|
readSize = 512 * numFrames;
|
|
|
|
if(g_loadedPCMVoiceDataPos[0] + readSize < g_loadedPCMVoiceDataSizes[0])
|
|
{
|
|
for(int i=0;i<MAX_LOCAL_PLAYER_COUNT;i++)
|
|
{
|
|
if(m_localVoiceDevices[i].isValid())
|
|
{
|
|
result = sceVoiceWriteToIPort(m_localVoiceDevices[i].m_microphonePort, &g_loadedPCMVoiceData[0][g_loadedPCMVoiceDataPos[0]], &readSize, 0);
|
|
}
|
|
}
|
|
}
|
|
g_loadedPCMVoiceDataPos[0] += readSize;
|
|
if(g_loadedPCMVoiceDataPos[0] > (g_loadedPCMVoiceDataSizes[0] + 8192))
|
|
g_loadedPCMVoiceDataPos[0] = 0;
|
|
|
|
|
|
}
|
|
|
|
void SonyVoiceChat_Orbis::sendAllVoiceData()
|
|
{
|
|
int32_t result;
|
|
uint32_t outputPortBytes;
|
|
VoicePacket packetToSend;
|
|
uint32_t readSize;
|
|
SceVoiceBasePortInfo portInfo;
|
|
memset( &portInfo, 0, sizeof(portInfo) );
|
|
uint16_t frameGap = 0;
|
|
|
|
VoicePacket::Flags lastPlayerFlags[MAX_LOCAL_PLAYER_COUNT];
|
|
|
|
for(int i=0; i<MAX_LOCAL_PLAYER_COUNT;i++)
|
|
lastPlayerFlags[i] = m_localPlayerFlags[i];
|
|
|
|
bool flagsChanged = false;
|
|
|
|
|
|
|
|
// grab the status of all the local voices
|
|
for(int i=0; i<MAX_LOCAL_PLAYER_COUNT;i++)
|
|
{
|
|
if(m_localVoiceDevices[i].isValid())
|
|
{
|
|
bool bChatRestricted = false;
|
|
ProfileManager.GetChatAndContentRestrictions(i,true,&bChatRestricted,NULL,NULL);
|
|
|
|
if(bChatRestricted)
|
|
{
|
|
m_localPlayerFlags[i].m_bHasMicConnected = false;
|
|
}
|
|
else
|
|
{
|
|
/* Obtain port state */
|
|
int32_t state;
|
|
int err = sceVoiceGetPortAttr(m_localVoiceDevices[i].m_microphonePort, SCE_VOICE_ATTR_AUDIOINPUT_SILENT_STATE, &state,sizeof(state));
|
|
if(err == SCE_OK)
|
|
{
|
|
if (state == 0)
|
|
m_localPlayerFlags[i].m_bHasMicConnected = true;
|
|
else
|
|
m_localPlayerFlags[i].m_bHasMicConnected = false;
|
|
}
|
|
else
|
|
m_localPlayerFlags[i].m_bHasMicConnected = false;
|
|
|
|
}
|
|
|
|
if(m_localPlayerFlags[i].m_bHasMicConnected)
|
|
{
|
|
SceVoiceBasePortInfo portInfo;
|
|
int result = sceVoiceGetPortInfo(m_localVoiceDevices[i].m_microphonePort, &portInfo );
|
|
assert(result == SCE_OK);
|
|
switch(portInfo.state)
|
|
{
|
|
case SCE_VOICE_PORTSTATE_READY:
|
|
case SCE_VOICE_PORTSTATE_BUFFERING:
|
|
case SCE_VOICE_PORTSTATE_IDLE:
|
|
m_localPlayerFlags[i].m_bTalking = false;
|
|
break;
|
|
case SCE_VOICE_PORTSTATE_RUNNING:
|
|
m_localPlayerFlags[i].m_bTalking = true;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_localPlayerFlags[i].m_bTalking = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_localPlayerFlags[i].m_bHasMicConnected = false;
|
|
m_localPlayerFlags[i].m_bTalking = false;
|
|
}
|
|
packetToSend.m_localPlayerFlags[i] = m_localPlayerFlags[i];
|
|
if(m_localPlayerFlags[i].m_bHasMicConnected != lastPlayerFlags[i].m_bHasMicConnected ||
|
|
m_localPlayerFlags[i].m_bTalking != lastPlayerFlags[i].m_bTalking)
|
|
flagsChanged = true;
|
|
}
|
|
|
|
|
|
if(sc_verbose)
|
|
{
|
|
EnterCriticalSection(&m_csRemoteConnections);
|
|
PrintAllOutputVoiceStates(m_remoteConnections);
|
|
LeaveCriticalSection(&m_csRemoteConnections);
|
|
}
|
|
result = sceVoiceGetPortInfo(m_voiceOutPort, &portInfo );
|
|
if (result != SCE_OK)
|
|
{
|
|
app.DebugPrintf("sceVoiceGetPortInfo failed %x\n", result);
|
|
assert(0);
|
|
}
|
|
|
|
|
|
|
|
|
|
outputPortBytes = portInfo.numByte;
|
|
outputPortBytes = (outputPortBytes>sizeof(packetToSend.m_data))?sizeof(packetToSend.m_data):outputPortBytes;
|
|
if( outputPortBytes || flagsChanged || m_forceSendPacket)
|
|
{
|
|
frameSendIndex += lastReadFrameCnt;
|
|
if(outputPortBytes)
|
|
{
|
|
readSize = outputPortBytes;
|
|
result = sceVoiceReadFromOPort(m_voiceOutPort, packetToSend.m_data, &readSize );
|
|
if (result != SCE_OK)
|
|
{
|
|
app.DebugPrintf("sceVoiceReadFromOPort failed %0x\n", result);
|
|
assert(0);
|
|
return;
|
|
}
|
|
lastReadFrameCnt = readSize/portInfo.frameSize;
|
|
assert(readSize%portInfo.frameSize == 0);
|
|
|
|
packetToSend.m_numFrames = lastReadFrameCnt;
|
|
packetToSend.m_frameSendIndex = frameSendIndex;
|
|
packetToSend.setChecksum(readSize);
|
|
}
|
|
else
|
|
{
|
|
readSize = 0;
|
|
packetToSend.m_numFrames = 0;
|
|
packetToSend.m_frameSendIndex = frameSendIndex;
|
|
packetToSend.setChecksum(readSize);
|
|
|
|
}
|
|
|
|
|
|
int packetSize = packetToSend.getPacketSize(readSize);
|
|
|
|
EnterCriticalSection(&m_csRemoteConnections);
|
|
|
|
// send this packet out to all our remote connections
|
|
for(int rIdx=0;rIdx<m_remoteConnections.size(); rIdx++)
|
|
{
|
|
SQRVoiceConnection* pVoice = m_remoteConnections[rIdx];
|
|
if(pVoice->m_bConnected)
|
|
m_remoteConnections[rIdx]->SendInternal(&packetToSend, packetSize);
|
|
}
|
|
|
|
LeaveCriticalSection(&m_csRemoteConnections);
|
|
}
|
|
m_forceSendPacket = false;
|
|
}
|
|
|
|
bool g_bPlaying = false;
|
|
|
|
void SonyVoiceChat_Orbis::playAllReceivedData()
|
|
{
|
|
EnterCriticalSection(&m_csRemoteConnections);
|
|
// write all the incoming data from the network to each of the input voices
|
|
for(int rIdx=0;rIdx<m_remoteConnections.size(); rIdx++)
|
|
{
|
|
SQRVoiceConnection* pVoice = m_remoteConnections[rIdx];
|
|
VoicePacket packet;
|
|
while(pVoice->getNextPacket(packet)) // MGH - changed to a while loop, so all the packets are sent to the voice port, and it can handle delayed packets due to the size of it's internal buffer
|
|
{
|
|
int frameGap;
|
|
if (pVoice->m_nextExpectedFrameIndex == packet.m_frameSendIndex)
|
|
{
|
|
// no voice frame drop, continuous frames
|
|
frameGap = 0;
|
|
app.DebugPrintf(sc_verbose, "index@%d gets expected frame\n",pVoice->m_nextExpectedFrameIndex);
|
|
pVoice->m_nextExpectedFrameIndex = packet.m_frameSendIndex + packet.m_numFrames;
|
|
}
|
|
else if (pVoice->m_nextExpectedFrameIndex < packet.m_frameSendIndex)
|
|
{
|
|
// has voice frame drop, dropped forwarding frames
|
|
frameGap = packet.m_frameSendIndex - pVoice->m_nextExpectedFrameIndex;
|
|
app.DebugPrintf(sc_verbose, "index@%d gets dropped forwarding frames %d\n",pVoice->m_nextExpectedFrameIndex, frameGap);
|
|
pVoice->m_nextExpectedFrameIndex = packet.m_frameSendIndex + packet.m_numFrames;
|
|
}
|
|
else if (pVoice->m_nextExpectedFrameIndex > packet.m_frameSendIndex)
|
|
{
|
|
// has voice frame drop, dropped preceding frames, no reset on pVoice->m_nextExpectedFrameIndex
|
|
frameGap = packet.m_frameSendIndex - pVoice->m_nextExpectedFrameIndex;
|
|
app.DebugPrintf(sc_verbose, "index@%d gets dropped forwarding frames %d\n", pVoice->m_nextExpectedFrameIndex, frameGap);
|
|
}
|
|
|
|
SceVoiceBasePortInfo portInfo;
|
|
int result = sceVoiceGetPortInfo(pVoice->m_voiceInPort, &portInfo );
|
|
if (result != SCE_OK)
|
|
{
|
|
app.DebugPrintf(sc_verbose, "sceVoiceGetPortInfo LoopbackVoiceInPort failed %x\n", result);
|
|
assert(0);
|
|
LeaveCriticalSection(&m_csRemoteConnections);
|
|
return;
|
|
}
|
|
uint32_t writeSize = packet.m_numFrames * portInfo.frameSize;
|
|
int inputPortBytes = portInfo.numByte;
|
|
inputPortBytes = (inputPortBytes>writeSize)?writeSize:inputPortBytes;
|
|
writeSize = inputPortBytes;
|
|
result = sceVoiceWriteToIPort(pVoice->m_voiceInPort, packet.m_data, &writeSize, frameGap);
|
|
if (result != SCE_OK)
|
|
{
|
|
app.DebugPrintf(sc_verbose, "sceVoiceWriteToIPort failed %0x\n", result);
|
|
assert(0);
|
|
LeaveCriticalSection(&m_csRemoteConnections);
|
|
return;
|
|
}
|
|
if (writeSize != inputPortBytes)
|
|
{
|
|
// libvoice internal voice in port buffer fulls
|
|
app.DebugPrintf(sc_verbose, "internal voice in port buffer fulls. \n");
|
|
}
|
|
packet.m_numFrames = 0;
|
|
|
|
// copy the flags
|
|
for(int flagIndex=0;flagIndex<MAX_LOCAL_PLAYER_COUNT;flagIndex++)
|
|
pVoice->m_remotePlayerFlags[flagIndex] = packet.m_localPlayerFlags[flagIndex];
|
|
}
|
|
}
|
|
LeaveCriticalSection(&m_csRemoteConnections);
|
|
|
|
}
|
|
|
|
void SonyVoiceChat_Orbis::tick()
|
|
{
|
|
if(m_bInitialised)
|
|
{
|
|
// DWORD tick = GetTickCount();
|
|
// static DWORD lastTick = 0;
|
|
// app.DebugPrintf("Time since last voice tick : %d ms\n", tick - lastTick);
|
|
// lastTick = tick;
|
|
g_frameNum++;
|
|
#ifdef USE_PCM_MIC_DATA
|
|
sendPCMMicData();
|
|
#endif
|
|
sendAllVoiceData();
|
|
playAllReceivedData();
|
|
|
|
EnterCriticalSection(&m_csRemoteConnections);
|
|
|
|
for(int i=m_remoteConnections.size()-1;i>=0;i--)
|
|
{
|
|
if(m_remoteConnections[i]->m_bFlaggedForShutdown)
|
|
{
|
|
delete m_remoteConnections[i];
|
|
m_remoteConnections.erase(m_remoteConnections.begin() + i);
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&m_csRemoteConnections);
|
|
|
|
// MGH - added to hopefully fix a lockup with shutdowns happening on different threads, when more than 1 player leaves the game
|
|
for(int i=0;i<MAX_LOCAL_PLAYER_COUNT;i++)
|
|
{
|
|
if(m_localVoiceDevices[i].m_bFlaggedForShutdown)
|
|
{
|
|
m_localVoiceDevices[i].shutdownWhenFlagged();
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
bool SonyVoiceChat_Orbis::hasMicConnected(SQRNetworkPlayer* pNetPlayer)
|
|
{
|
|
if(pNetPlayer->IsLocal())
|
|
{
|
|
return m_localPlayerFlags[pNetPlayer->GetLocalPlayerIndex()].m_bHasMicConnected;
|
|
}
|
|
else
|
|
{
|
|
EnterCriticalSection(&m_csRemoteConnections);
|
|
for(int i=0;i<m_remoteConnections.size();i++)
|
|
{
|
|
SQRVoiceConnection* pVoice = m_remoteConnections[i];
|
|
if(pVoice->m_remoteRoomMemberId == pNetPlayer->m_roomMemberId)
|
|
{
|
|
bool bMicConnected = pVoice->m_remotePlayerFlags[pNetPlayer->GetLocalPlayerIndex()].m_bHasMicConnected;
|
|
LeaveCriticalSection(&m_csRemoteConnections);
|
|
return bMicConnected;
|
|
}
|
|
}
|
|
LeaveCriticalSection(&m_csRemoteConnections);
|
|
}
|
|
// if we get here we've not found the player, panic!!
|
|
assert(0);
|
|
return false;
|
|
}
|
|
|
|
void SonyVoiceChat_Orbis::mute( bool bMute )
|
|
{
|
|
}
|
|
|
|
void SonyVoiceChat_Orbis::mutePlayer( const SceNpMatching2RoomMemberId member_id, bool bMute ) /*Turn chat audio from a specified player on or off */
|
|
{
|
|
}
|
|
|
|
void SonyVoiceChat_Orbis::muteLocalPlayer( bool bMute ) /*Turn microphone input on or off */
|
|
{
|
|
}
|
|
|
|
bool SonyVoiceChat_Orbis::isMuted()
|
|
{
|
|
}
|
|
|
|
bool SonyVoiceChat_Orbis::isMutedPlayer( const PlayerUID& memberUID)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool SonyVoiceChat_Orbis::isMutedLocalPlayer()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
bool SonyVoiceChat_Orbis::isTalking(SQRNetworkPlayer* pNetPlayer)
|
|
{
|
|
if(pNetPlayer->IsLocal())
|
|
{
|
|
return m_localPlayerFlags[pNetPlayer->GetLocalPlayerIndex()].m_bTalking;
|
|
}
|
|
else
|
|
{
|
|
EnterCriticalSection(&m_csRemoteConnections);
|
|
for(int i=0;i<m_remoteConnections.size();i++)
|
|
{
|
|
SQRVoiceConnection* pVoice = m_remoteConnections[i];
|
|
if(pVoice->m_remoteRoomMemberId == pNetPlayer->m_roomMemberId)
|
|
{
|
|
bool bTalking = pVoice->m_remotePlayerFlags[pNetPlayer->GetLocalPlayerIndex()].m_bTalking;
|
|
LeaveCriticalSection(&m_csRemoteConnections);
|
|
return bTalking;
|
|
}
|
|
}
|
|
LeaveCriticalSection(&m_csRemoteConnections);
|
|
}
|
|
// if we get here we've not found the player, panic!!
|
|
assert(0);
|
|
return false;
|
|
}
|
|
|
|
|
|
void SQRLocalVoiceDevice::init(SceUserServiceUserId localUserID, bool bChatRestricted)
|
|
{
|
|
SceVoiceInitParam params;
|
|
SceVoicePortParam portArgs;
|
|
|
|
int returnCode = 0;
|
|
m_bChatRestricted = bChatRestricted;
|
|
|
|
#ifdef USE_PCM_MIC_DATA
|
|
portArgs.portType = SCE_VOICE_PORTTYPE_IN_PCMAUDIO;
|
|
portArgs.bMute = false;
|
|
portArgs.threshold = 100;
|
|
portArgs.volume = 1.0f;
|
|
portArgs.pcmaudio.format.sampleRate= SCE_VOICE_SAMPLINGRATE_16000;
|
|
portArgs.pcmaudio.format.dataType = SCE_VOICE_PCM_SHORT_LITTLE_ENDIAN;
|
|
portArgs.pcmaudio.bufSize = 4096;
|
|
CreatePort( &m_microphonePort, &portArgs );
|
|
#else
|
|
portArgs.portType = SCE_VOICE_PORTTYPE_IN_DEVICE;
|
|
portArgs.bMute = false;
|
|
portArgs.threshold = 0;
|
|
portArgs.volume = 1.0f;
|
|
portArgs.device.userId = localUserID;
|
|
portArgs.device.type = SCE_AUDIO_IN_TYPE_VOICE;
|
|
portArgs.device.index = 0;
|
|
CreatePort( &m_microphonePort, &portArgs );
|
|
|
|
#endif
|
|
portArgs.portType = SCE_VOICE_PORTTYPE_OUT_DEVICE;
|
|
portArgs.bMute = false;
|
|
portArgs.threshold = 0;
|
|
portArgs.volume = 1.0f;
|
|
portArgs.device.userId = localUserID;
|
|
portArgs.device.type = SCE_AUDIO_OUT_PORT_TYPE_VOICE;
|
|
portArgs.device.index = 0;
|
|
CreatePort( &m_headsetPort, &portArgs );
|
|
|
|
m_localUserID = localUserID;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SQRLocalVoiceDevice::shutdownWhenFlagged()
|
|
{
|
|
|
|
assert(isValid());
|
|
m_localUserID = SCE_USER_SERVICE_USER_ID_INVALID;
|
|
DeletePort(m_microphonePort);
|
|
DeletePort(m_headsetPort);
|
|
m_bFlaggedForShutdown = false;
|
|
}
|
|
|
|
|
|
|
|
SQRVoiceConnection* SonyVoiceChat_Orbis::addRemoteConnection( int RudpCxt, SceNpMatching2RoomMemberId peerMemberId)
|
|
{
|
|
EnterCriticalSection(&m_csRemoteConnections);
|
|
SQRVoiceConnection* pConn = new SQRVoiceConnection(RudpCxt, peerMemberId);
|
|
m_remoteConnections.push_back(pConn);
|
|
m_forceSendPacket = true; // new connection, so we'll force a packet through for the flags
|
|
LeaveCriticalSection(&m_csRemoteConnections);
|
|
|
|
return pConn;
|
|
}
|
|
|
|
void SonyVoiceChat_Orbis::connectPorts(uint32_t inPort, uint32_t outPort)
|
|
{
|
|
int returnCode = sceVoiceConnectIPortToOPort(inPort, outPort);
|
|
if (returnCode != SCE_OK )
|
|
{
|
|
app.DebugPrintf("sceVoiceConnectIPortToOPort failed (0x%08x), inPort 0x%08x, outPort 0x%08x\n", returnCode, inPort, outPort);
|
|
assert(0);
|
|
}
|
|
}
|
|
void SonyVoiceChat_Orbis::disconnectPorts(uint32_t inPort, uint32_t outPort)
|
|
{
|
|
int returnCode = sceVoiceDisconnectIPortFromOPort(inPort, outPort);
|
|
if (returnCode != SCE_OK )
|
|
{
|
|
app.DebugPrintf("sceVoiceDisconnectIPortFromOPort failed (0x%08x), inPort 0x%08x, outPort 0x%08x\n", returnCode, inPort, outPort);
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
|
|
void SonyVoiceChat_Orbis::makeLocalConnections()
|
|
{
|
|
// connect all mics to other devices headsets, for local chat
|
|
for(int i=0;i<MAX_LOCAL_PLAYER_COUNT;i++)
|
|
{
|
|
SQRLocalVoiceDevice* pConnectFrom = &m_localVoiceDevices[i];
|
|
if(pConnectFrom->isValid())
|
|
{
|
|
for(int j=0;j<MAX_LOCAL_PLAYER_COUNT;j++)
|
|
{
|
|
SQRLocalVoiceDevice* pConnectTo = &m_localVoiceDevices[j];
|
|
if( (pConnectFrom!=pConnectTo) && pConnectTo->isValid())
|
|
{
|
|
if(pConnectFrom->m_localConnections[j] == false)
|
|
{
|
|
if(pConnectTo->m_bChatRestricted == false && pConnectFrom->m_bChatRestricted == false)
|
|
{
|
|
connectPorts(pConnectFrom->m_microphonePort, pConnectTo->m_headsetPort);
|
|
pConnectFrom->m_localConnections[j] = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SonyVoiceChat_Orbis::breakLocalConnections(int playerIdx)
|
|
{
|
|
// break any connections with devices that are no longer valid
|
|
for(int i=0;i<MAX_LOCAL_PLAYER_COUNT;i++)
|
|
{
|
|
SQRLocalVoiceDevice* pConnectedFrom = &m_localVoiceDevices[i];
|
|
for(int j=0;j<MAX_LOCAL_PLAYER_COUNT;j++)
|
|
{
|
|
if(pConnectedFrom->m_localConnections[j] == true)
|
|
{
|
|
SQRLocalVoiceDevice* pConnectedTo = &m_localVoiceDevices[j];
|
|
if(i==playerIdx || j==playerIdx)
|
|
{
|
|
if(pConnectedTo->m_bChatRestricted == false && pConnectedFrom->m_bChatRestricted == false)
|
|
{
|
|
disconnectPorts(pConnectedFrom->m_microphonePort, pConnectedTo->m_headsetPort);
|
|
pConnectedFrom->m_localConnections[j] = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void SonyVoiceChat_Orbis::initLocalPlayer(int playerIndex)
|
|
{
|
|
if(m_localVoiceDevices[playerIndex].isValid() == false)
|
|
{
|
|
bool chatRestricted = false;
|
|
ProfileManager.GetChatAndContentRestrictions(ProfileManager.GetPrimaryPad(),false,&chatRestricted,NULL,NULL);
|
|
|
|
// create all device ports required
|
|
m_localVoiceDevices[playerIndex].init(ProfileManager.getUserID(playerIndex), chatRestricted);
|
|
m_numLocalDevicesConnected++;
|
|
if(m_localVoiceDevices[playerIndex].m_bChatRestricted == false)
|
|
{
|
|
connectPorts(m_localVoiceDevices[playerIndex].m_microphonePort, m_voiceOutPort);
|
|
}
|
|
m_forceSendPacket = true; // new local device, so we'll force a packet through for the flags
|
|
|
|
}
|
|
makeLocalConnections();
|
|
}
|
|
|
|
void SonyVoiceChat_Orbis::connectPlayer(SQRVoiceConnection* pConnection, int playerIndex)
|
|
{
|
|
if((pConnection->m_headsetConnectionMask & (1 << playerIndex)) == 0)
|
|
{
|
|
initLocalPlayer(playerIndex); // added this as we can get a client->client connection coming in first, and the network player hasn't been created yet (so this hasn't been initialised)
|
|
if(m_localVoiceDevices[playerIndex].m_bChatRestricted == false)
|
|
{
|
|
connectPorts(pConnection->m_voiceInPort, m_localVoiceDevices[playerIndex].m_headsetPort);
|
|
}
|
|
pConnection->m_headsetConnectionMask |= (1 << playerIndex);
|
|
app.DebugPrintf("Connecting player %d to rudp context %d\n", playerIndex, pConnection->m_rudpCtx);
|
|
m_forceSendPacket = true; // new connection, so we'll force a packet through for the flags
|
|
}
|
|
}
|
|
|
|
SQRVoiceConnection* SonyVoiceChat_Orbis::GetVoiceConnectionFromRudpCtx( int RudpCtx )
|
|
{
|
|
for(int i=0;i<m_remoteConnections.size();i++)
|
|
{
|
|
if(m_remoteConnections[i]->m_rudpCtx == RudpCtx)
|
|
return m_remoteConnections[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void SonyVoiceChat_Orbis::connectPlayerToAll( int playerIndex )
|
|
{
|
|
EnterCriticalSection(&m_csRemoteConnections);
|
|
|
|
for(int i=0;i<m_remoteConnections.size();i++)
|
|
{
|
|
SonyVoiceChat_Orbis::connectPlayer(m_remoteConnections[i], playerIndex);
|
|
}
|
|
|
|
LeaveCriticalSection(&m_csRemoteConnections);
|
|
}
|
|
|
|
SQRVoiceConnection* SonyVoiceChat_Orbis::getVoiceConnectionFromRoomMemberID( SceNpMatching2RoomMemberId roomMemberID )
|
|
{
|
|
for(int i=0;i<m_remoteConnections.size();i++)
|
|
{
|
|
if(m_remoteConnections[i]->m_remoteRoomMemberId == roomMemberID)
|
|
{
|
|
return m_remoteConnections[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void SonyVoiceChat_Orbis::disconnectLocalPlayer( int localIdx )
|
|
{
|
|
EnterCriticalSection(&m_csRemoteConnections);
|
|
|
|
if(m_localVoiceDevices[localIdx].m_bChatRestricted == false)
|
|
{
|
|
disconnectPorts(m_localVoiceDevices[localIdx].m_microphonePort, m_voiceOutPort);
|
|
|
|
for(int i=0;i<m_remoteConnections.size();i++)
|
|
{
|
|
disconnectPorts(m_remoteConnections[i]->m_voiceInPort, m_localVoiceDevices[localIdx].m_headsetPort);
|
|
m_remoteConnections[i]->m_headsetConnectionMask &= (~(1 << localIdx));
|
|
app.DebugPrintf("disconnecting player %d from rudp context %d\n", localIdx, m_remoteConnections[i]->m_rudpCtx);
|
|
}
|
|
}
|
|
LeaveCriticalSection(&m_csRemoteConnections);
|
|
|
|
breakLocalConnections(localIdx);
|
|
m_localVoiceDevices[localIdx].flagForShutdown();
|
|
m_numLocalDevicesConnected--;
|
|
|
|
if(m_numLocalDevicesConnected == 0) // no more local players, kill all the remote connections
|
|
{
|
|
for(int i=0;i<m_remoteConnections.size();i++)
|
|
{
|
|
delete m_remoteConnections[i];
|
|
}
|
|
m_remoteConnections.clear();
|
|
}
|
|
}
|
|
|
|
|
|
void SonyVoiceChat_Orbis::disconnectRemoteConnection( SQRVoiceConnection* pVoice )
|
|
{
|
|
EnterCriticalSection(&m_csRemoteConnections);
|
|
|
|
int voiceIdx = -1;
|
|
for(int i=0;i<m_remoteConnections.size();i++)
|
|
{
|
|
if(m_remoteConnections[i] == pVoice)
|
|
voiceIdx = i;
|
|
}
|
|
assert(voiceIdx>=0);
|
|
if(voiceIdx>=0)
|
|
{
|
|
m_remoteConnections[voiceIdx]->m_bFlaggedForShutdown = true;
|
|
}
|
|
|
|
LeaveCriticalSection(&m_csRemoteConnections);
|
|
|
|
}
|
|
|
|
void SonyVoiceChat_Orbis::setConnected( int RudpCtx )
|
|
{
|
|
SQRVoiceConnection* pVoice = GetVoiceConnectionFromRudpCtx(RudpCtx);
|
|
if(pVoice)
|
|
{
|
|
pVoice->m_bConnected = true;
|
|
m_forceSendPacket = true;
|
|
}
|
|
else
|
|
{
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
RingBuffer::RingBuffer( int sizeBytes )
|
|
{
|
|
buffer = new char[sizeBytes];
|
|
buf_size = sizeBytes;
|
|
buf_full = buf_free = 0;
|
|
}
|
|
|
|
|
|
int RingBuffer::Write( char* data, int len_ )
|
|
{
|
|
if (len_ <= 0) return len_;
|
|
unsigned int len = (unsigned int)len_;
|
|
unsigned int data_size = buf_size - (buf_free - buf_full);
|
|
if (len > data_size)
|
|
len = data_size;
|
|
data_size = buf_size - (buf_free % buf_size);
|
|
if (data_size > len)
|
|
data_size = len;
|
|
memcpy(buffer + (buf_free % buf_size), data, data_size);
|
|
if (data_size != len)
|
|
memcpy(buffer, data + data_size, len - data_size);
|
|
buf_free += len;
|
|
return len;
|
|
}
|
|
|
|
int RingBuffer::Read( char* data, int max_bytes_ )
|
|
{
|
|
if (max_bytes_ <= 0) return max_bytes_;
|
|
unsigned int max_bytes = (unsigned int)max_bytes_;
|
|
unsigned int result = buf_free - buf_full;
|
|
if (result > max_bytes)
|
|
result = max_bytes;
|
|
unsigned int chunk = buf_size - (buf_full % buf_size);
|
|
if (chunk > result)
|
|
chunk = result;
|
|
memcpy(data, buffer + (buf_full % buf_size), chunk);
|
|
if (chunk != result)
|
|
memcpy(data + chunk, buffer, result - chunk);
|
|
buf_full += result;
|
|
return result;
|
|
}
|