Skip to content

Commit

Permalink
- Apply parts of Ben Millwood's target bitfield patch (#3787)
Browse files Browse the repository at this point in the history
- Fix Ryan's FIXME and have voip packet buffer on the server dynamically allocated via Z_Malloc and store pointers in a circular buffer
- Improve voip target parsing on top of Ben Millwood's patch
- Add new "spatial" target where speaker is spatialized in 3d space and can be heard by all clients in hearing range (s_alMaxDistance)
  (#4467)
- Decrease voip sound lengths from 240ms to 80ms per voip packet to mitigate udp packet loss and decrease latency
- Protocol version incremented to 71
  • Loading branch information
Thilo Schulz committed Jul 27, 2011
1 parent 41ac8a2 commit 2349148
Show file tree
Hide file tree
Showing 19 changed files with 407 additions and 245 deletions.
4 changes: 2 additions & 2 deletions code/client/cl_cgame.c
Original file line number Diff line number Diff line change
Expand Up @@ -952,8 +952,8 @@ void CL_FirstSnapshot( void ) {
clc.speexInitialized = qtrue;
clc.voipMuteAll = qfalse;
Cmd_AddCommand ("voip", CL_Voip_f);
Cvar_Set("cl_voipSendTarget", "all");
clc.voipTarget1 = clc.voipTarget2 = clc.voipTarget3 = 0x7FFFFFFF;
Cvar_Set("cl_voipSendTarget", "spatial");
Com_Memset(clc.voipTargets, ~0, sizeof(clc.voipTargets));
}
#endif
}
Expand Down
4 changes: 2 additions & 2 deletions code/client/cl_cin.c
Original file line number Diff line number Diff line change
Expand Up @@ -1147,7 +1147,7 @@ static void RoQInterrupt(void)
case ZA_SOUND_MONO:
if (!cinTable[currentHandle].silent) {
ssize = RllDecodeMonoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags);
S_RawSamples( 0, ssize, 22050, 2, 1, (byte *)sbuf, 1.0f );
S_RawSamples(0, ssize, 22050, 2, 1, (byte *)sbuf, 1.0f, -1);
}
break;
case ZA_SOUND_STEREO:
Expand All @@ -1157,7 +1157,7 @@ static void RoQInterrupt(void)
s_rawend[0] = s_soundtime;
}
ssize = RllDecodeStereoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags);
S_RawSamples( 0, ssize, 22050, 2, 2, (byte *)sbuf, 1.0f );
S_RawSamples(0, ssize, 22050, 2, 2, (byte *)sbuf, 1.0f, -1);
}
break;
case ROQ_QUAD_INFO:
Expand Down
116 changes: 43 additions & 73 deletions code/client/cl_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -799,83 +799,53 @@ void CL_WritePacket( void ) {
}

#ifdef USE_VOIP
if (clc.voipOutgoingDataSize > 0) { // only send if data.
// Move cl_voipSendTarget from a string to the bitmasks if needed.
if (cl_voipSendTarget->modified) {
char buffer[32];
const char *target = cl_voipSendTarget->string;

if (Q_stricmp(target, "attacker") == 0) {
int player = VM_Call( cgvm, CG_LAST_ATTACKER );
Com_sprintf(buffer, sizeof (buffer), "%d", player);
target = buffer;
} else if (Q_stricmp(target, "crosshair") == 0) {
int player = VM_Call( cgvm, CG_CROSSHAIR_PLAYER );
Com_sprintf(buffer, sizeof (buffer), "%d", player);
target = buffer;
if (clc.voipOutgoingDataSize > 0)
{
if((clc.voipFlags & VOIP_SPATIAL) || Com_IsVoipTarget(clc.voipTargets, sizeof(clc.voipTargets), -1))
{
MSG_WriteByte (&buf, clc_voip);
MSG_WriteByte (&buf, clc.voipOutgoingGeneration);
MSG_WriteLong (&buf, clc.voipOutgoingSequence);
MSG_WriteByte (&buf, clc.voipOutgoingDataFrames);
MSG_WriteData (&buf, clc.voipTargets, sizeof(clc.voipTargets));
MSG_WriteByte(&buf, clc.voipFlags);
MSG_WriteShort (&buf, clc.voipOutgoingDataSize);
MSG_WriteData (&buf, clc.voipOutgoingData, clc.voipOutgoingDataSize);

// If we're recording a demo, we have to fake a server packet with
// this VoIP data so it gets to disk; the server doesn't send it
// back to us, and we might as well eliminate concerns about dropped
// and misordered packets here.
if(clc.demorecording && !clc.demowaiting)
{
const int voipSize = clc.voipOutgoingDataSize;
msg_t fakemsg;
byte fakedata[MAX_MSGLEN];
MSG_Init (&fakemsg, fakedata, sizeof (fakedata));
MSG_Bitstream (&fakemsg);
MSG_WriteLong (&fakemsg, clc.reliableAcknowledge);
MSG_WriteByte (&fakemsg, svc_voip);
MSG_WriteShort (&fakemsg, clc.clientNum);
MSG_WriteByte (&fakemsg, clc.voipOutgoingGeneration);
MSG_WriteLong (&fakemsg, clc.voipOutgoingSequence);
MSG_WriteByte (&fakemsg, clc.voipOutgoingDataFrames);
MSG_WriteShort (&fakemsg, clc.voipOutgoingDataSize );
MSG_WriteData (&fakemsg, clc.voipOutgoingData, voipSize);
MSG_WriteByte (&fakemsg, svc_EOF);
CL_WriteDemoMessage (&fakemsg, 0);
}

if ((*target == '\0') || (Q_stricmp(target, "all") == 0)) {
const int all = 0x7FFFFFFF;
clc.voipTarget1 = clc.voipTarget2 = clc.voipTarget3 = all;
} else if (Q_stricmp(target, "none") == 0) {
clc.voipTarget1 = clc.voipTarget2 = clc.voipTarget3 = 0;
} else {
const char *ptr = target;
clc.voipTarget1 = clc.voipTarget2 = clc.voipTarget3 = 0;
do {
if ((*ptr == ',') || (*ptr == '\0')) {
const int val = atoi(target);
target = ptr + 1;
if ((val >= 0) && (val < 31)) {
clc.voipTarget1 |= (1 << (val-0));
} else if ((val >= 31) && (val < 62)) {
clc.voipTarget2 |= (1 << (val-31));
} else if ((val >= 62) && (val < 93)) {
clc.voipTarget3 |= (1 << (val-62));
}
}
} while (*(ptr++));
}
cl_voipSendTarget->modified = qfalse;
clc.voipOutgoingSequence += clc.voipOutgoingDataFrames;
clc.voipOutgoingDataSize = 0;
clc.voipOutgoingDataFrames = 0;
}

MSG_WriteByte (&buf, clc_voip);
MSG_WriteByte (&buf, clc.voipOutgoingGeneration);
MSG_WriteLong (&buf, clc.voipOutgoingSequence);
MSG_WriteByte (&buf, clc.voipOutgoingDataFrames);
MSG_WriteLong (&buf, clc.voipTarget1);
MSG_WriteLong (&buf, clc.voipTarget2);
MSG_WriteLong (&buf, clc.voipTarget3);
MSG_WriteShort (&buf, clc.voipOutgoingDataSize);
MSG_WriteData (&buf, clc.voipOutgoingData, clc.voipOutgoingDataSize);

// If we're recording a demo, we have to fake a server packet with
// this VoIP data so it gets to disk; the server doesn't send it
// back to us, and we might as well eliminate concerns about dropped
// and misordered packets here.
if ( clc.demorecording && !clc.demowaiting ) {
const int voipSize = clc.voipOutgoingDataSize;
msg_t fakemsg;
byte fakedata[MAX_MSGLEN];
MSG_Init (&fakemsg, fakedata, sizeof (fakedata));
MSG_Bitstream (&fakemsg);
MSG_WriteLong (&fakemsg, clc.reliableAcknowledge);
MSG_WriteByte (&fakemsg, svc_voip);
MSG_WriteShort (&fakemsg, clc.clientNum);
MSG_WriteByte (&fakemsg, clc.voipOutgoingGeneration);
MSG_WriteLong (&fakemsg, clc.voipOutgoingSequence);
MSG_WriteByte (&fakemsg, clc.voipOutgoingDataFrames);
MSG_WriteShort (&fakemsg, clc.voipOutgoingDataSize );
MSG_WriteData (&fakemsg, clc.voipOutgoingData, voipSize);
MSG_WriteByte (&fakemsg, svc_EOF);
CL_WriteDemoMessage (&fakemsg, 0);
else
{
// We have data, but no targets. Silently discard all data
clc.voipOutgoingDataSize = 0;
clc.voipOutgoingDataFrames = 0;
}

clc.voipOutgoingSequence += clc.voipOutgoingDataFrames;
clc.voipOutgoingDataSize = 0;
clc.voipOutgoingDataFrames = 0;
} else
}
#endif

if ( count >= 1 ) {
Expand Down
96 changes: 90 additions & 6 deletions code/client/cl_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,88 @@ void CL_VoipNewGeneration(void)
clc.voipOutgoingSequence = 0;
}

/*
===============
CL_VoipParseTargets
sets clc.voipTargets according to cl_voipSendTarget
Generally we don't want who's listening to change during a transmission,
so this is only called when the key is first pressed
===============
*/
void CL_VoipParseTargets(void)
{
const char *target = cl_voipSendTarget->string;
char *end;
int val;

Com_Memset(clc.voipTargets, 0, sizeof(clc.voipTargets));
clc.voipFlags &= ~VOIP_SPATIAL;

while(target)
{
while(*target == ',' || *target == ' ')
target++;

if(!*target)
break;

if(isdigit(*target))
{
val = strtol(target, &end, 10);
target = end;
}
else
{
if(!Q_stricmpn(target, "all", 3))
{
Com_Memset(clc.voipTargets, ~0, sizeof(clc.voipTargets));
return;
}
if(!Q_stricmpn(target, "spatial", 7))
{
clc.voipFlags |= VOIP_SPATIAL;
target += 7;
continue;
}
else
{
if(!Q_stricmpn(target, "attacker", 8))
{
val = VM_Call(cgvm, CG_LAST_ATTACKER);
target += 8;
}
else if(!Q_stricmpn(target, "crosshair", 9))
{
val = VM_Call(cgvm, CG_CROSSHAIR_PLAYER);
target += 9;
}
else
{
while(*target && *target != ',' && *target != ' ')
target++;

continue;
}

if(val < 0)
continue;
}
}

if(val < 0 || val >= MAX_CLIENTS)
{
Com_Printf(S_COLOR_YELLOW "WARNING: VoIP "
"target %d is not a valid client "
"number\n", val);

continue;
}

clc.voipTargets[val / 8] |= 1 << (val % 8);
}
}

/*
===============
CL_CaptureVoip
Expand Down Expand Up @@ -342,8 +424,9 @@ void CL_CaptureVoip(void)

cl_voipSend->modified = qfalse;

if (dontCapture) {
cl_voipSend->integer = 0;
if(dontCapture)
{
Cvar_Set("cl_voipSend", "0");
return;
}

Expand All @@ -362,11 +445,12 @@ void CL_CaptureVoip(void)
S_MasterGain(cl_voipGainDuringCapture->value);
S_StartCapture();
CL_VoipNewGeneration();
CL_VoipParseTargets();
}

if ((cl_voipSend->integer) || (finalFrame)) { // user wants to capture audio?
int samples = S_AvailableCaptureSamples();
const int mult = (finalFrame) ? 1 : 12; // 12 == 240ms of audio.
const int mult = (finalFrame) ? 1 : 4; // 4 == 80ms of audio.

// enough data buffered in audio hardware to process yet?
if (samples >= (clc.speexFrameSize * mult)) {
Expand All @@ -378,8 +462,8 @@ void CL_CaptureVoip(void)
int wpos = 0;
int pos = 0;

if (samples > (clc.speexFrameSize * 12))
samples = (clc.speexFrameSize * 12);
if (samples > (clc.speexFrameSize * 4))
samples = (clc.speexFrameSize * 4);

// !!! FIXME: maybe separate recording from encoding, so voipPower
// !!! FIXME: updates faster than 4Hz?
Expand Down Expand Up @@ -3420,7 +3504,7 @@ void CL_Init( void ) {

#ifdef USE_VOIP
cl_voipSend = Cvar_Get ("cl_voipSend", "0", 0);
cl_voipSendTarget = Cvar_Get ("cl_voipSendTarget", "all", 0);
cl_voipSendTarget = Cvar_Get ("cl_voipSendTarget", "spatial", 0);
cl_voipGainDuringCapture = Cvar_Get ("cl_voipGainDuringCapture", "0.2", CVAR_ARCHIVE);
cl_voipCaptureMult = Cvar_Get ("cl_voipCaptureMult", "2.0", CVAR_ARCHIVE);
cl_voipUseVAD = Cvar_Get ("cl_voipUseVAD", "0", CVAR_ARCHIVE);
Expand Down
34 changes: 28 additions & 6 deletions code/client/cl_parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,29 @@ qboolean CL_ShouldIgnoreVoipSender(int sender)
return qfalse;
}

/*
=====================
CL_PlayVoip
Play raw data
=====================
*/

static void CL_PlayVoip(int sender, int samplecnt, const byte *data, int flags)
{
if(flags & VOIP_DIRECT)
{
S_RawSamples(sender + 1, samplecnt, clc.speexSampleRate, 2, 1,
data, clc.voipGain[sender], -1);
}

if(flags & VOIP_SPATIAL)
{
S_RawSamples(sender + MAX_CLIENTS + 1, samplecnt, clc.speexSampleRate, 2, 1,
data, 1.0f, sender);
}
}

/*
=====================
CL_ParseVoip
Expand All @@ -679,6 +702,7 @@ void CL_ParseVoip ( msg_t *msg ) {
const int sequence = MSG_ReadLong(msg);
const int frames = MSG_ReadByte(msg);
const int packetsize = MSG_ReadShort(msg);
const int flags = MSG_ReadBits(msg, VOIP_FLAGCNT);
char encoded[1024];
int seqdiff = sequence - clc.voipIncomingSequence[sender];
int written = 0;
Expand Down Expand Up @@ -769,8 +793,8 @@ void CL_ParseVoip ( msg_t *msg ) {
if ((written + clc.speexFrameSize) * 2 > sizeof (decoded)) {
Com_DPrintf("VoIP: playback %d bytes, %d samples, %d frames\n",
written * 2, written, i);
S_RawSamples(sender + 1, written, clc.speexSampleRate, 2, 1,
(const byte *) decoded, clc.voipGain[sender]);

CL_PlayVoip(sender, written, (const byte *) decoded, flags);
written = 0;
}

Expand All @@ -793,10 +817,8 @@ void CL_ParseVoip ( msg_t *msg ) {
Com_DPrintf("VoIP: playback %d bytes, %d samples, %d frames\n",
written * 2, written, i);

if (written > 0) {
S_RawSamples(sender + 1, written, clc.speexSampleRate, 2, 1,
(const byte *) decoded, clc.voipGain[sender]);
}
if(written > 0)
CL_PlayVoip(sender, written, (const byte *) decoded, flags);

clc.voipIncomingSequence[sender] = sequence + frames;
}
Expand Down
7 changes: 4 additions & 3 deletions code/client/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -250,9 +250,10 @@ typedef struct {
qboolean voipMuteAll;

// outgoing data...
int voipTarget1; // these three ints make up a bit mask of 92 bits.
int voipTarget2; // the bits say who a VoIP pack is addressed to:
int voipTarget3; // (1 << clientnum). See cl_voipSendTarget cvar.
// if voipTargets[i / 8] & (1 << (i % 8)),
// then we are sending to clientnum i.
uint8_t voipTargets[(MAX_CLIENTS + 7) / 8];
uint8_t voipFlags;
SpeexPreprocessState *speexPreprocessor;
SpeexBits speexEncoderBits;
void *speexEncoder;
Expand Down
Loading

0 comments on commit 2349148

Please sign in to comment.