aboutsummaryrefslogtreecommitdiff
path: root/Minecraft.Client/Orbis/Network/SonyVoiceChat_Orbis.h
blob: 950ca09a6f00b34476678a3caf6253cef6223cca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <voice_qos.h>
#include "Common/Network/Sony/SQRNetworkPlayer.h"

static const int sc_maxVoiceDataSize = 2048;


class VoicePacket
{
	static const int MAX_LOCAL_PLAYER_COUNT = 4;

public:
	struct Flags
	{
		bool m_bTalking  : 1; 
		bool m_bHasMicConnected : 1;
	};	
	
	Flags m_localPlayerFlags[MAX_LOCAL_PLAYER_COUNT];
	uint32_t m_frameSendIndex;
	uint32_t m_numFrames;
	uint32_t m_checkSum;
	uint32_t m_playerIndexFlags;
	char m_data[sc_maxVoiceDataSize];

	static int getPacketSize(int dataSize) { return (uint64_t)&((VoicePacket*)0)->m_data[dataSize];}
	void setChecksum(int dataSize)
	{
		m_checkSum = 0;
		for(int i=0;i<dataSize;i++)
			m_checkSum += m_data[i];
	}
	void verifyData(int packetSize, int frameSize) 
	{ 
		if(m_numFrames == 0)
			return;
		int dataSize = m_numFrames*frameSize; 
		assert(packetSize == getPacketSize(dataSize)); 
		int checkSum = 0;
		for(int i=0;i<dataSize;i++)
			checkSum += m_data[i];
		assert(checkSum == m_checkSum);
	}
};


//--------------------------------------------------------------------------
// read-write-safe ring buffer implementation: does not use mutex protection
// the writer thread changes pointer <buf_free>, 
// the reader thread changes pointer <buf_full>
class RingBuffer
{
public:
	RingBuffer(int sizeBytes);
	~RingBuffer()							{	delete buffer;	}
	void Reset(void)						{	buf_full = buf_free = 0;		}
	unsigned int DataSize(void)				{	return (buf_free - buf_full);	}
	int Write(char* data, int len_);
	int Read(char* data, int max_bytes_);
	int getDataSize()						{	return buf_free - buf_full; }
	void ResetByWriter(void)				{	buf_free = buf_full;	}
	void ResetByReader(void)				{	buf_full = buf_free;	}

private:
	char*   buffer;
	unsigned int buf_size;
	unsigned int buf_full;
	unsigned int buf_free;
};


static const int sc_ringBufferSize = 16384;

class SQRLocalVoiceDevice
{
public:
	uint32_t    m_headsetPort;
	uint32_t    m_microphonePort;
	uint8_t		m_localConnections[4];		// connection between this devices mic and other local player's headsets 
	bool		m_bChatRestricted;
	bool		m_bFlaggedForShutdown;
	SceUserServiceUserId m_localUserID;

public:
	SQRLocalVoiceDevice() 
		: m_headsetPort(SCE_VOICE_INVALID_PORT_ID)
		, m_microphonePort(SCE_VOICE_INVALID_PORT_ID)
		, m_localUserID(SCE_USER_SERVICE_USER_ID_INVALID)
	{
		for(int i=0;i<4;i++)
			m_localConnections[i] = 0;
	}

	void init(SceUserServiceUserId localUserID, bool bChatRestricted);
	void flagForShutdown() { m_bFlaggedForShutdown = true;}
	void shutdownWhenFlagged();
	bool isValid() { return m_localUserID != SCE_USER_SERVICE_USER_ID_INVALID; }
// 	void setBitRate()
// 	{
// 		int err = sceVoiceSetBitRate(uint32_t portId,
// 			SceVoiceBitRate bitrate
// 			);
// 	}
};

#define VOICE_ENCODED_FORMAT                  SCE_VOICE_BITRATE_7300



class SQRVoiceConnection
{
	static const int MAX_LOCAL_PLAYER_COUNT = 4;
	static const int SNP_MAX_PAYLOAD = 1346;				// This is the default RUDP payload size - if we want to change this we'll need to use cellRudpSetOption to set something else & adjust segment size
	class QueuedSendBlock
	{
	public:
		unsigned char *start;
		unsigned char *end;
		unsigned char *current;
	};

	std::queue<QueuedSendBlock>	m_sendQueue;
	CRITICAL_SECTION			m_csQueue;

public:
	int			m_rudpCtx;
	bool		m_bConnected;
	uint32_t    m_voiceInPort;		// 1 input port per connection, incoming UDP packets are written to each of these, and then they're connected out to all headsets
	int			m_headsetConnectionMask;		// 1 bit per player, if the headset connection has been made
	RingBuffer	m_playRingBuffer;
	SceNpMatching2RoomMemberId m_remoteRoomMemberId; // Assigned by Matching2 lib, we can use to indicate which machine this player belongs to (note - 16 bits)
	std::queue<VoicePacket> m_receivedVoicePackets;
	CRITICAL_SECTION			m_csPacketQueue;
	VoicePacket::Flags m_remotePlayerFlags[MAX_LOCAL_PLAYER_COUNT];
	uint32_t	m_nextExpectedFrameIndex;
	bool		m_bFlaggedForShutdown;
	SQRVoiceConnection(int rudpCtx, SceNpMatching2RoomMemberId remoteRoomMemberId);
	~SQRVoiceConnection();

	void SendInternal(const void *data, unsigned int dataSize);
	void SendMoreInternal();
	void readRemoteData();
	bool getNextPacket(VoicePacket& packet);
	void addPacket(VoicePacket& packet);


};

class SonyVoiceChat_Orbis
{
public:

	static void init();
	static void start();
	static void shutdown();
	static void tick();
	static void checkFinished();
	static void setEnabled(bool bEnabled);
	static bool hasMicConnected(SQRNetworkPlayer* pNetPlayer);
	static bool isTalking(SQRNetworkPlayer* pNetPlayer);
	static void mute(bool bMute);			//Turn chat audio on or off
	static void mutePlayer(const SceNpMatching2RoomMemberId member_id, bool bMute);		//Turn chat audio from a specified player on or off;
	static void muteLocalPlayer(bool bMute); //Turn microphone input on or off;

	static bool isMuted();
	static bool isMutedPlayer(const PlayerUID& memberUID);
	static bool isMutedLocalPlayer(); //Turn microphone input on or off;

	static void initLocalPlayer(int playerIndex);

	static SQRVoiceConnection* addRemoteConnection(int RudpCxt, SceNpMatching2RoomMemberId peerMemberId);
	static void connectPlayer(SQRVoiceConnection* pConnection, int playerIndex);
	static void connectPlayerToAll(int playerIndex);
	static void disconnectLocalPlayer(int localIdx);
	static void disconnectRemoteConnection( SQRVoiceConnection* pVoice );

	static void VoiceEventCallback( SceVoiceEventData* pEvent );

	static std::vector<SQRVoiceConnection*> m_remoteConnections;
	static void connectPorts(uint32_t inPort, uint32_t outPort);
	static void disconnectPorts(uint32_t inPort, uint32_t outPort);
	static void makeLocalConnections();
	static void breakLocalConnections(int playerIdx);

	static void sendAllVoiceData();
	static void playAllReceivedData();

	static void sendPCMMicData();
	static SQRVoiceConnection* getVoiceConnectionFromRoomMemberID(SceNpMatching2RoomMemberId roomMemberID);

	static SQRVoiceConnection* GetVoiceConnectionFromRudpCtx(int RudpCtx);
	static void setConnected(int RudpCtx);

private:
	static const int MAX_LOCAL_PLAYER_COUNT = 4;

	static bool m_bVoiceStarted;
	static int					m_numLocalDevicesConnected;
	static SQRLocalVoiceDevice	m_localVoiceDevices[MAX_LOCAL_PLAYER_COUNT];

	static uint32_t     m_voiceOutPort;		// single output port that all local devices are mixed to, and then sent out to all other remote machines

	static RingBuffer m_recordRingBuffer;
	static RingBuffer m_playRingBuffer;
	static VoicePacket::Flags m_localPlayerFlags[MAX_LOCAL_PLAYER_COUNT];
	static bool	m_forceSendPacket; // force a packet across the network, even if there's no data, so we can update flags
	static bool m_bInitialised;
	static CRITICAL_SECTION			m_csRemoteConnections;


};