- Apply parts of Ben Millwood's target bitfield patch (#3787)

- 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
This commit is contained in:
Thilo Schulz 2011-07-27 15:47:29 +00:00
parent 41ac8a232a
commit 2349148cf1
19 changed files with 403 additions and 241 deletions

View file

@ -952,8 +952,8 @@ void CL_FirstSnapshot( void ) {
clc.speexInitialized = qtrue; clc.speexInitialized = qtrue;
clc.voipMuteAll = qfalse; clc.voipMuteAll = qfalse;
Cmd_AddCommand ("voip", CL_Voip_f); Cmd_AddCommand ("voip", CL_Voip_f);
Cvar_Set("cl_voipSendTarget", "all"); Cvar_Set("cl_voipSendTarget", "spatial");
clc.voipTarget1 = clc.voipTarget2 = clc.voipTarget3 = 0x7FFFFFFF; Com_Memset(clc.voipTargets, ~0, sizeof(clc.voipTargets));
} }
#endif #endif
} }

View file

@ -1147,7 +1147,7 @@ redump:
case ZA_SOUND_MONO: case ZA_SOUND_MONO:
if (!cinTable[currentHandle].silent) { if (!cinTable[currentHandle].silent) {
ssize = RllDecodeMonoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags); 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; break;
case ZA_SOUND_STEREO: case ZA_SOUND_STEREO:
@ -1157,7 +1157,7 @@ redump:
s_rawend[0] = s_soundtime; s_rawend[0] = s_soundtime;
} }
ssize = RllDecodeStereoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags); 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; break;
case ROQ_QUAD_INFO: case ROQ_QUAD_INFO:

View file

@ -799,54 +799,16 @@ void CL_WritePacket( void ) {
} }
#ifdef USE_VOIP #ifdef USE_VOIP
if (clc.voipOutgoingDataSize > 0) { // only send if data. if (clc.voipOutgoingDataSize > 0)
// Move cl_voipSendTarget from a string to the bitmasks if needed. {
if (cl_voipSendTarget->modified) { if((clc.voipFlags & VOIP_SPATIAL) || Com_IsVoipTarget(clc.voipTargets, sizeof(clc.voipTargets), -1))
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 ((*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;
}
MSG_WriteByte (&buf, clc_voip); MSG_WriteByte (&buf, clc_voip);
MSG_WriteByte (&buf, clc.voipOutgoingGeneration); MSG_WriteByte (&buf, clc.voipOutgoingGeneration);
MSG_WriteLong (&buf, clc.voipOutgoingSequence); MSG_WriteLong (&buf, clc.voipOutgoingSequence);
MSG_WriteByte (&buf, clc.voipOutgoingDataFrames); MSG_WriteByte (&buf, clc.voipOutgoingDataFrames);
MSG_WriteLong (&buf, clc.voipTarget1); MSG_WriteData (&buf, clc.voipTargets, sizeof(clc.voipTargets));
MSG_WriteLong (&buf, clc.voipTarget2); MSG_WriteByte(&buf, clc.voipFlags);
MSG_WriteLong (&buf, clc.voipTarget3);
MSG_WriteShort (&buf, clc.voipOutgoingDataSize); MSG_WriteShort (&buf, clc.voipOutgoingDataSize);
MSG_WriteData (&buf, clc.voipOutgoingData, clc.voipOutgoingDataSize); MSG_WriteData (&buf, clc.voipOutgoingData, clc.voipOutgoingDataSize);
@ -854,7 +816,8 @@ void CL_WritePacket( void ) {
// this VoIP data so it gets to disk; the server doesn't send it // 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 // back to us, and we might as well eliminate concerns about dropped
// and misordered packets here. // and misordered packets here.
if ( clc.demorecording && !clc.demowaiting ) { if(clc.demorecording && !clc.demowaiting)
{
const int voipSize = clc.voipOutgoingDataSize; const int voipSize = clc.voipOutgoingDataSize;
msg_t fakemsg; msg_t fakemsg;
byte fakedata[MAX_MSGLEN]; byte fakedata[MAX_MSGLEN];
@ -875,7 +838,14 @@ void CL_WritePacket( void ) {
clc.voipOutgoingSequence += clc.voipOutgoingDataFrames; clc.voipOutgoingSequence += clc.voipOutgoingDataFrames;
clc.voipOutgoingDataSize = 0; clc.voipOutgoingDataSize = 0;
clc.voipOutgoingDataFrames = 0; clc.voipOutgoingDataFrames = 0;
} else }
else
{
// We have data, but no targets. Silently discard all data
clc.voipOutgoingDataSize = 0;
clc.voipOutgoingDataFrames = 0;
}
}
#endif #endif
if ( count >= 1 ) { if ( count >= 1 ) {

View file

@ -291,6 +291,88 @@ void CL_VoipNewGeneration(void)
clc.voipOutgoingSequence = 0; 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 CL_CaptureVoip
@ -342,8 +424,9 @@ void CL_CaptureVoip(void)
cl_voipSend->modified = qfalse; cl_voipSend->modified = qfalse;
if (dontCapture) { if(dontCapture)
cl_voipSend->integer = 0; {
Cvar_Set("cl_voipSend", "0");
return; return;
} }
@ -362,11 +445,12 @@ void CL_CaptureVoip(void)
S_MasterGain(cl_voipGainDuringCapture->value); S_MasterGain(cl_voipGainDuringCapture->value);
S_StartCapture(); S_StartCapture();
CL_VoipNewGeneration(); CL_VoipNewGeneration();
CL_VoipParseTargets();
} }
if ((cl_voipSend->integer) || (finalFrame)) { // user wants to capture audio? if ((cl_voipSend->integer) || (finalFrame)) { // user wants to capture audio?
int samples = S_AvailableCaptureSamples(); 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? // enough data buffered in audio hardware to process yet?
if (samples >= (clc.speexFrameSize * mult)) { if (samples >= (clc.speexFrameSize * mult)) {
@ -378,8 +462,8 @@ void CL_CaptureVoip(void)
int wpos = 0; int wpos = 0;
int pos = 0; int pos = 0;
if (samples > (clc.speexFrameSize * 12)) if (samples > (clc.speexFrameSize * 4))
samples = (clc.speexFrameSize * 12); samples = (clc.speexFrameSize * 4);
// !!! FIXME: maybe separate recording from encoding, so voipPower // !!! FIXME: maybe separate recording from encoding, so voipPower
// !!! FIXME: updates faster than 4Hz? // !!! FIXME: updates faster than 4Hz?
@ -3420,7 +3504,7 @@ void CL_Init( void ) {
#ifdef USE_VOIP #ifdef USE_VOIP
cl_voipSend = Cvar_Get ("cl_voipSend", "0", 0); 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_voipGainDuringCapture = Cvar_Get ("cl_voipGainDuringCapture", "0.2", CVAR_ARCHIVE);
cl_voipCaptureMult = Cvar_Get ("cl_voipCaptureMult", "2.0", CVAR_ARCHIVE); cl_voipCaptureMult = Cvar_Get ("cl_voipCaptureMult", "2.0", CVAR_ARCHIVE);
cl_voipUseVAD = Cvar_Get ("cl_voipUseVAD", "0", CVAR_ARCHIVE); cl_voipUseVAD = Cvar_Get ("cl_voipUseVAD", "0", CVAR_ARCHIVE);

View file

@ -663,6 +663,29 @@ qboolean CL_ShouldIgnoreVoipSender(int sender)
return qfalse; 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 CL_ParseVoip
@ -679,6 +702,7 @@ void CL_ParseVoip ( msg_t *msg ) {
const int sequence = MSG_ReadLong(msg); const int sequence = MSG_ReadLong(msg);
const int frames = MSG_ReadByte(msg); const int frames = MSG_ReadByte(msg);
const int packetsize = MSG_ReadShort(msg); const int packetsize = MSG_ReadShort(msg);
const int flags = MSG_ReadBits(msg, VOIP_FLAGCNT);
char encoded[1024]; char encoded[1024];
int seqdiff = sequence - clc.voipIncomingSequence[sender]; int seqdiff = sequence - clc.voipIncomingSequence[sender];
int written = 0; int written = 0;
@ -769,8 +793,8 @@ void CL_ParseVoip ( msg_t *msg ) {
if ((written + clc.speexFrameSize) * 2 > sizeof (decoded)) { if ((written + clc.speexFrameSize) * 2 > sizeof (decoded)) {
Com_DPrintf("VoIP: playback %d bytes, %d samples, %d frames\n", Com_DPrintf("VoIP: playback %d bytes, %d samples, %d frames\n",
written * 2, written, i); 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; written = 0;
} }
@ -793,10 +817,8 @@ void CL_ParseVoip ( msg_t *msg ) {
Com_DPrintf("VoIP: playback %d bytes, %d samples, %d frames\n", Com_DPrintf("VoIP: playback %d bytes, %d samples, %d frames\n",
written * 2, written, i); written * 2, written, i);
if (written > 0) { if(written > 0)
S_RawSamples(sender + 1, written, clc.speexSampleRate, 2, 1, CL_PlayVoip(sender, written, (const byte *) decoded, flags);
(const byte *) decoded, clc.voipGain[sender]);
}
clc.voipIncomingSequence[sender] = sequence + frames; clc.voipIncomingSequence[sender] = sequence + frames;
} }

View file

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

View file

@ -916,7 +916,8 @@ S_Base_RawSamples
Music streaming Music streaming
============ ============
*/ */
void S_Base_RawSamples( int stream, int samples, int rate, int width, int s_channels, const byte *data, float volume ) { void S_Base_RawSamples( int stream, int samples, int rate, int width, int s_channels, const byte *data, float volume, int entityNum)
{
int i; int i;
int src, dst; int src, dst;
float scale; float scale;
@ -927,9 +928,16 @@ void S_Base_RawSamples( int stream, int samples, int rate, int width, int s_chan
return; return;
} }
if(entityNum >= 0)
{
// FIXME: support spatialized raw streams, e.g. for VoIP
return;
}
if ( (stream < 0) || (stream >= MAX_RAW_STREAMS) ) { if ( (stream < 0) || (stream >= MAX_RAW_STREAMS) ) {
return; return;
} }
rawsamples = s_rawsamples[stream]; rawsamples = s_rawsamples[stream];
if(s_muted->integer) if(s_muted->integer)
@ -1396,7 +1404,7 @@ void S_UpdateBackgroundTrack( void ) {
{ {
// add to raw buffer // add to raw buffer
S_Base_RawSamples(0, fileSamples, s_backgroundStream->info.rate, S_Base_RawSamples(0, fileSamples, s_backgroundStream->info.rate,
s_backgroundStream->info.width, s_backgroundStream->info.channels, raw, s_musicVolume->value ); s_backgroundStream->info.width, s_backgroundStream->info.channels, raw, s_musicVolume->value, -1);
} }
else else
{ {

View file

@ -125,7 +125,7 @@ typedef struct
void (*StartLocalSound)( sfxHandle_t sfx, int channelNum ); void (*StartLocalSound)( sfxHandle_t sfx, int channelNum );
void (*StartBackgroundTrack)( const char *intro, const char *loop ); void (*StartBackgroundTrack)( const char *intro, const char *loop );
void (*StopBackgroundTrack)( void ); void (*StopBackgroundTrack)( void );
void (*RawSamples)(int stream, int samples, int rate, int width, int channels, const byte *data, float volume); void (*RawSamples)(int stream, int samples, int rate, int width, int channels, const byte *data, float volume, int entityNum);
void (*StopAllSounds)( void ); void (*StopAllSounds)( void );
void (*ClearLoopingSounds)( qboolean killall ); void (*ClearLoopingSounds)( qboolean killall );
void (*AddLoopingSound)( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ); void (*AddLoopingSound)( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx );
@ -186,7 +186,7 @@ extern vec3_t listener_up;
extern dma_t dma; extern dma_t dma;
#define MAX_RAW_SAMPLES 16384 #define MAX_RAW_SAMPLES 16384
#define MAX_RAW_STREAMS 128 #define MAX_RAW_STREAMS (MAX_CLIENTS * 2 + 1)
extern portable_samplepair_t s_rawsamples[MAX_RAW_STREAMS][MAX_RAW_SAMPLES]; extern portable_samplepair_t s_rawsamples[MAX_RAW_STREAMS][MAX_RAW_SAMPLES];
extern int s_rawend[MAX_RAW_STREAMS]; extern int s_rawend[MAX_RAW_STREAMS];

View file

@ -129,11 +129,10 @@ S_RawSamples
================= =================
*/ */
void S_RawSamples (int stream, int samples, int rate, int width, int channels, void S_RawSamples (int stream, int samples, int rate, int width, int channels,
const byte *data, float volume) const byte *data, float volume, int entityNum)
{ {
if( si.RawSamples ) { if(si.RawSamples)
si.RawSamples( stream, samples, rate, width, channels, data, volume ); si.RawSamples(stream, samples, rate, width, channels, data, volume, entityNum);
}
} }
/* /*

View file

@ -521,6 +521,7 @@ typedef struct src_s
qboolean isLocked; // This is locked (un-allocatable) qboolean isLocked; // This is locked (un-allocatable)
qboolean isLooping; // Is this a looping effect (attached to an entity) qboolean isLooping; // Is this a looping effect (attached to an entity)
qboolean isTracking; // Is this object tracking its owner qboolean isTracking; // Is this object tracking its owner
qboolean isStream; // Is this source a stream
float curGain; // gain employed if source is within maxdistance. float curGain; // gain employed if source is within maxdistance.
float scaleGain; // Last gain value for this source. 0 if muted. float scaleGain; // Last gain value for this source. 0 if muted.
@ -744,13 +745,8 @@ S_AL_SrcSetup
static void S_AL_SrcSetup(srcHandle_t src, sfxHandle_t sfx, alSrcPriority_t priority, static void S_AL_SrcSetup(srcHandle_t src, sfxHandle_t sfx, alSrcPriority_t priority,
int entity, int channel, qboolean local) int entity, int channel, qboolean local)
{ {
ALuint buffer;
src_t *curSource; src_t *curSource;
// Mark the SFX as used, and grab the raw AL buffer
S_AL_BufferUse(sfx);
buffer = S_AL_BufferGet(sfx);
// Set up src struct // Set up src struct
curSource = &srcList[src]; curSource = &srcList[src];
@ -763,12 +759,19 @@ static void S_AL_SrcSetup(srcHandle_t src, sfxHandle_t sfx, alSrcPriority_t prio
curSource->isLocked = qfalse; curSource->isLocked = qfalse;
curSource->isLooping = qfalse; curSource->isLooping = qfalse;
curSource->isTracking = qfalse; curSource->isTracking = qfalse;
curSource->isStream = qfalse;
curSource->curGain = s_alGain->value * s_volume->value; curSource->curGain = s_alGain->value * s_volume->value;
curSource->scaleGain = curSource->curGain; curSource->scaleGain = curSource->curGain;
curSource->local = local; curSource->local = local;
// Set up OpenAL source // Set up OpenAL source
qalSourcei(curSource->alSource, AL_BUFFER, buffer); if(sfx >= 0)
{
// Mark the SFX as used, and grab the raw AL buffer
S_AL_BufferUse(sfx);
qalSourcei(curSource->alSource, AL_BUFFER, S_AL_BufferGet(sfx));
}
qalSourcef(curSource->alSource, AL_PITCH, 1.0f); qalSourcef(curSource->alSource, AL_PITCH, 1.0f);
S_AL_Gain(curSource->alSource, curSource->curGain); S_AL_Gain(curSource->alSource, curSource->curGain);
qalSourcefv(curSource->alSource, AL_POSITION, vec3_origin); qalSourcefv(curSource->alSource, AL_POSITION, vec3_origin);
@ -1320,7 +1323,7 @@ static void S_AL_SrcLoop( alSrcPriority_t priority, sfxHandle_t sfx,
VectorClear(sorigin); VectorClear(sorigin);
qalSourcefv(curSource->alSource, AL_POSITION, sorigin); qalSourcefv(curSource->alSource, AL_POSITION, sorigin);
qalSourcefv(curSource->alSource, AL_VELOCITY, sorigin); qalSourcefv(curSource->alSource, AL_VELOCITY, vec3_origin);
} }
else else
{ {
@ -1344,7 +1347,7 @@ static void S_AL_SrcLoop( alSrcPriority_t priority, sfxHandle_t sfx,
VectorClear(svelocity); VectorClear(svelocity);
qalSourcefv(curSource->alSource, AL_POSITION, (ALfloat *) sorigin); qalSourcefv(curSource->alSource, AL_POSITION, (ALfloat *) sorigin);
qalSourcefv( curSource->alSource, AL_VELOCITY, (ALfloat *)velocity ); qalSourcefv(curSource->alSource, AL_VELOCITY, (ALfloat *) svelocity);
} }
} }
@ -1557,6 +1560,8 @@ void S_AL_SrcUpdate( void )
continue; continue;
} }
if(!curSource->isStream)
{
// Check if it's done, and flag it // Check if it's done, and flag it
qalGetSourcei(curSource->alSource, AL_SOURCE_STATE, &state); qalGetSourcei(curSource->alSource, AL_SOURCE_STATE, &state);
if(state == AL_STOPPED) if(state == AL_STOPPED)
@ -1565,6 +1570,7 @@ void S_AL_SrcUpdate( void )
S_AL_SrcKill(i); S_AL_SrcKill(i);
continue; continue;
} }
}
// Query relativity of source, don't move if it's true // Query relativity of source, don't move if it's true
qalGetSourcei(curSource->alSource, AL_SOURCE_RELATIVE, &state); qalGetSourcei(curSource->alSource, AL_SOURCE_RELATIVE, &state);
@ -1614,32 +1620,57 @@ static ALuint streamSources[MAX_RAW_STREAMS];
S_AL_AllocateStreamChannel S_AL_AllocateStreamChannel
================= =================
*/ */
static void S_AL_AllocateStreamChannel( int stream ) static void S_AL_AllocateStreamChannel(int stream, int entityNum)
{ {
srcHandle_t cursrc;
ALuint alsrc;
if ((stream < 0) || (stream >= MAX_RAW_STREAMS)) if ((stream < 0) || (stream >= MAX_RAW_STREAMS))
return; return;
// Allocate a streamSource at high priority if(entityNum >= 0)
streamSourceHandles[stream] = S_AL_SrcAlloc(SRCPRI_STREAM, -2, 0); {
if(streamSourceHandles[stream] == -1) // This is a stream that tracks an entity
// Allocate a streamSource at normal priority
cursrc = S_AL_SrcAlloc(SRCPRI_ENTITY, entityNum, 0);
if(cursrc < 0)
return; return;
S_AL_SrcSetup(cursrc, -1, SRCPRI_ENTITY, entityNum, 0, qfalse);
alsrc = S_AL_SrcGet(cursrc);
srcList[cursrc].isTracking = qtrue;
srcList[cursrc].isStream = qtrue;
}
else
{
// Unspatialized stream source
// Allocate a streamSource at high priority
cursrc = S_AL_SrcAlloc(SRCPRI_STREAM, -2, 0);
if(cursrc < 0)
return;
alsrc = S_AL_SrcGet(cursrc);
// Lock the streamSource so nobody else can use it, and get the raw streamSource // Lock the streamSource so nobody else can use it, and get the raw streamSource
S_AL_SrcLock(streamSourceHandles[stream]); S_AL_SrcLock(cursrc);
streamSources[stream] = S_AL_SrcGet(streamSourceHandles[stream]);
// make sure that after unmuting the S_AL_Gain in S_Update() does not turn // make sure that after unmuting the S_AL_Gain in S_Update() does not turn
// volume up prematurely for this source // volume up prematurely for this source
srcList[streamSourceHandles[stream]].scaleGain = 0.0f; srcList[cursrc].scaleGain = 0.0f;
// Set some streamSource parameters // Set some streamSource parameters
qalSourcei (streamSources[stream], AL_BUFFER, 0 ); qalSourcei (alsrc, AL_BUFFER, 0 );
qalSourcei (streamSources[stream], AL_LOOPING, AL_FALSE ); qalSourcei (alsrc, AL_LOOPING, AL_FALSE );
qalSource3f(streamSources[stream], AL_POSITION, 0.0, 0.0, 0.0); qalSource3f(alsrc, AL_POSITION, 0.0, 0.0, 0.0);
qalSource3f(streamSources[stream], AL_VELOCITY, 0.0, 0.0, 0.0); qalSource3f(alsrc, AL_VELOCITY, 0.0, 0.0, 0.0);
qalSource3f(streamSources[stream], AL_DIRECTION, 0.0, 0.0, 0.0); qalSource3f(alsrc, AL_DIRECTION, 0.0, 0.0, 0.0);
qalSourcef (streamSources[stream], AL_ROLLOFF_FACTOR, 0.0 ); qalSourcef (alsrc, AL_ROLLOFF_FACTOR, 0.0 );
qalSourcei (streamSources[stream], AL_SOURCE_RELATIVE, AL_TRUE ); qalSourcei (alsrc, AL_SOURCE_RELATIVE, AL_TRUE );
}
streamSourceHandles[stream] = cursrc;
streamSources[stream] = alsrc;
} }
/* /*
@ -1654,6 +1685,7 @@ static void S_AL_FreeStreamChannel( int stream )
// Release the output streamSource // Release the output streamSource
S_AL_SrcUnlock(streamSourceHandles[stream]); S_AL_SrcUnlock(streamSourceHandles[stream]);
S_AL_SrcKill(streamSourceHandles[stream]);
streamSources[stream] = 0; streamSources[stream] = 0;
streamSourceHandles[stream] = -1; streamSourceHandles[stream] = -1;
} }
@ -1664,7 +1696,7 @@ S_AL_RawSamples
================= =================
*/ */
static static
void S_AL_RawSamples(int stream, int samples, int rate, int width, int channels, const byte *data, float volume) void S_AL_RawSamples(int stream, int samples, int rate, int width, int channels, const byte *data, float volume, int entityNum)
{ {
ALuint buffer; ALuint buffer;
ALuint format; ALuint format;
@ -1677,7 +1709,7 @@ void S_AL_RawSamples(int stream, int samples, int rate, int width, int channels,
// Create the streamSource if necessary // Create the streamSource if necessary
if(streamSourceHandles[stream] == -1) if(streamSourceHandles[stream] == -1)
{ {
S_AL_AllocateStreamChannel(stream); S_AL_AllocateStreamChannel(stream, entityNum);
// Failed? // Failed?
if(streamSourceHandles[stream] == -1) if(streamSourceHandles[stream] == -1)
@ -1694,9 +1726,12 @@ void S_AL_RawSamples(int stream, int samples, int rate, int width, int channels,
// Shove the data onto the streamSource // Shove the data onto the streamSource
qalSourceQueueBuffers(streamSources[stream], 1, &buffer); qalSourceQueueBuffers(streamSources[stream], 1, &buffer);
if(entityNum < 0)
{
// Volume // Volume
S_AL_Gain (streamSources[stream], volume * s_volume->value * s_alGain->value); S_AL_Gain (streamSources[stream], volume * s_volume->value * s_alGain->value);
} }
}
/* /*
================= =================
@ -2106,7 +2141,6 @@ S_AL_Respatialize
static static
void S_AL_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater ) void S_AL_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater )
{ {
float velocity[3] = {0.0f, 0.0f, 0.0f};
float orientation[6]; float orientation[6];
vec3_t sorigin; vec3_t sorigin;
@ -2124,7 +2158,7 @@ void S_AL_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int
// Set OpenAL listener paramaters // Set OpenAL listener paramaters
qalListenerfv(AL_POSITION, (ALfloat *)sorigin); qalListenerfv(AL_POSITION, (ALfloat *)sorigin);
qalListenerfv(AL_VELOCITY, velocity); qalListenerfv(AL_VELOCITY, vec3_origin);
qalListenerfv(AL_ORIENTATION, orientation); qalListenerfv(AL_ORIENTATION, orientation);
} }

View file

@ -34,7 +34,7 @@ void S_StopBackgroundTrack( void );
// cinematics and voice-over-network will send raw samples // cinematics and voice-over-network will send raw samples
// 1.0 volume will be direct output of source samples // 1.0 volume will be direct output of source samples
void S_RawSamples(int stream, int samples, int rate, int width, int channels, void S_RawSamples(int stream, int samples, int rate, int width, int channels,
const byte *data, float volume); const byte *data, float volume, int entityNum);
// stop all sounds and the background track // stop all sounds and the background track
void S_StopAllSounds( void ); void S_StopAllSounds( void );

View file

@ -3585,3 +3585,33 @@ void Com_RandomBytes( byte *string, int len )
string[i] = (unsigned char)( rand() % 255 ); string[i] = (unsigned char)( rand() % 255 );
} }
/*
==================
Com_IsVoipTarget
Returns non-zero if given clientNum is enabled in voipTargets, zero otherwise.
If clientNum is negative return if any bit is set.
==================
*/
qboolean Com_IsVoipTarget(uint8_t *voipTargets, int voipTargetsSize, int clientNum)
{
int index;
if(clientNum < 0)
{
for(index = 0; index < voipTargetsSize; index++)
{
if(voipTargets[index])
return qtrue;
}
return qfalse;
}
index = clientNum >> 3;
if(index < voipTargetsSize)
return (voipTargets[index] & (1 << (clientNum & 0x07)));
return qfalse;
}

View file

@ -928,6 +928,23 @@ typedef struct {
char string[MAX_CVAR_VALUE_STRING]; char string[MAX_CVAR_VALUE_STRING];
} vmCvar_t; } vmCvar_t;
/*
==============================================================
VoIP
==============================================================
*/
// if you change the count of flags be sure to also change VOIP_FLAGNUM
#define VOIP_SPATIAL 0x01 // spatialized voip message
#define VOIP_DIRECT 0x02 // non-spatialized voip message
// number of flags voip knows. You will have to bump protocol version number if you
// change this.
#define VOIP_FLAGCNT 2
/* /*
============================================================== ==============================================================

View file

@ -252,7 +252,7 @@ PROTOCOL
============================================================== ==============================================================
*/ */
#define PROTOCOL_VERSION 70 #define PROTOCOL_VERSION 71
#define PROTOCOL_LEGACY_VERSION 68 #define PROTOCOL_LEGACY_VERSION 68
// 1.31 - 67 // 1.31 - 67
@ -834,6 +834,8 @@ int Com_RealTime(qtime_t *qtime);
qboolean Com_SafeMode( void ); qboolean Com_SafeMode( void );
void Com_RunAndTimeServerPacket(netadr_t *evFrom, msg_t *buf); void Com_RunAndTimeServerPacket(netadr_t *evFrom, msg_t *buf);
qboolean Com_IsVoipTarget(uint8_t *voipTargets, int voipTargetsSize, int clientNum);
void Com_StartupVariable( const char *match ); void Com_StartupVariable( const char *match );
// checks for and removes command line "+set var arg" constructs // checks for and removes command line "+set var arg" constructs
// if match is NULL, all set commands will be executed, otherwise // if match is NULL, all set commands will be executed, otherwise

View file

@ -43,6 +43,7 @@ typedef struct voipServerPacket_s
int frames; int frames;
int len; int len;
int sender; int sender;
int flags;
byte data[1024]; byte data[1024];
} voipServerPacket_t; } voipServerPacket_t;
#endif #endif
@ -362,10 +363,6 @@ int SV_WriteDownloadToClient(client_t *cl , msg_t *msg);
int SV_SendDownloadMessages(void); int SV_SendDownloadMessages(void);
int SV_SendQueuedMessages(void); int SV_SendQueuedMessages(void);
#ifdef USE_VOIP
void SV_WriteVoipToClient( client_t *cl, msg_t *msg );
#endif
// //
// sv_ccmds.c // sv_ccmds.c

View file

@ -1176,56 +1176,6 @@ int SV_SendDownloadMessages(void)
return numDLs; return numDLs;
} }
#ifdef USE_VOIP
/*
==================
SV_WriteVoipToClient
Check to see if there is any VoIP queued for a client, and send if there is.
==================
*/
void SV_WriteVoipToClient( client_t *cl, msg_t *msg )
{
if(*cl->downloadName)
{
cl->queuedVoipPackets = 0;
return; // no VoIP allowed if download is going, to save bandwidth.
}
if(cl->queuedVoipPackets)
{
int totalbytes = 0;
int i;
voipServerPacket_t *packet;
// Write as many VoIP packets as we reasonably can...
for(i = cl->queuedVoipIndex; i < cl->queuedVoipPackets; i++)
{
packet = cl->voipPacket[i % ARRAY_LEN(cl->voipPacket)];
totalbytes += packet->len;
if (totalbytes > (msg->maxsize - msg->cursize) / 2)
break;
MSG_WriteByte(msg, svc_voip);
MSG_WriteShort(msg, packet->sender);
MSG_WriteByte(msg, (byte) packet->generation);
MSG_WriteLong(msg, packet->sequence);
MSG_WriteByte(msg, packet->frames);
MSG_WriteShort(msg, packet->len);
MSG_WriteData(msg, packet->data, packet->len);
Z_Free(packet);
}
cl->queuedVoipPackets -= i;
cl->queuedVoipIndex += i;
cl->queuedVoipIndex %= ARRAY_LEN(cl->voipPacket);
}
}
#endif
/* /*
================= =================
SV_Disconnect_f SV_Disconnect_f
@ -1800,8 +1750,15 @@ static void SV_UserMove( client_t *cl, msg_t *msg, qboolean delta ) {
#ifdef USE_VOIP #ifdef USE_VOIP
static /*
qboolean SV_ShouldIgnoreVoipSender(const client_t *cl) ==================
SV_ShouldIgnoreVoipSender
Blocking of voip packets based on source client
==================
*/
static qboolean SV_ShouldIgnoreVoipSender(const client_t *cl)
{ {
if (!sv_voip->integer) if (!sv_voip->integer)
return qtrue; // VoIP disabled on this server. return qtrue; // VoIP disabled on this server.
@ -1814,33 +1771,25 @@ qboolean SV_ShouldIgnoreVoipSender(const client_t *cl)
} }
static static
void SV_UserVoip( client_t *cl, msg_t *msg ) { void SV_UserVoip(client_t *cl, msg_t *msg)
const int sender = (int) (cl - svs.clients); {
const int generation = MSG_ReadByte(msg); int sender, generation, sequence, frames, packetsize;
const int sequence = MSG_ReadLong(msg); uint8_t recips[(MAX_CLIENTS + 7) / 8];
const int frames = MSG_ReadByte(msg); int flags;
const int recip1 = MSG_ReadLong(msg);
const int recip2 = MSG_ReadLong(msg);
const int recip3 = MSG_ReadLong(msg);
const int packetsize = MSG_ReadShort(msg);
byte encoded[sizeof(cl->voipPacket[0]->data)]; byte encoded[sizeof(cl->voipPacket[0]->data)];
client_t *client = NULL; client_t *client = NULL;
voipServerPacket_t *packet = NULL; voipServerPacket_t *packet = NULL;
int i; int i;
if (generation < 0) sender = cl - svs.clients;
return; // short/invalid packet, bail. generation = MSG_ReadByte(msg);
else if (sequence < 0) sequence = MSG_ReadLong(msg);
return; // short/invalid packet, bail. frames = MSG_ReadByte(msg);
else if (frames < 0) MSG_ReadData(msg, recips, sizeof(recips));
return; // short/invalid packet, bail. flags = MSG_ReadByte(msg);
else if (recip1 < 0) packetsize = MSG_ReadShort(msg);
return; // short/invalid packet, bail.
else if (recip2 < 0) if (msg->readcount > msg->cursize)
return; // short/invalid packet, bail.
else if (recip3 < 0)
return; // short/invalid packet, bail.
else if (packetsize < 0)
return; // short/invalid packet, bail. return; // short/invalid packet, bail.
if (packetsize > sizeof (encoded)) { // overlarge packet? if (packetsize > sizeof (encoded)) { // overlarge packet?
@ -1865,10 +1814,6 @@ void SV_UserVoip( client_t *cl, msg_t *msg ) {
// !!! FIXME: reject if not speex narrowband codec. // !!! FIXME: reject if not speex narrowband codec.
// !!! FIXME: decide if this is bogus data? // !!! FIXME: decide if this is bogus data?
// (the three recip* values are 31 bits each (ignores sign bit so we can
// get a -1 error from MSG_ReadLong() ... ), allowing for 93 clients.)
assert( sv_maxclients->integer < 93 );
// decide who needs this VoIP packet sent to them... // decide who needs this VoIP packet sent to them...
for (i = 0, client = svs.clients; i < sv_maxclients->integer ; i++, client++) { for (i = 0, client = svs.clients; i < sv_maxclients->integer ; i++, client++) {
if (client->state != CS_ACTIVE) if (client->state != CS_ACTIVE)
@ -1876,18 +1821,20 @@ void SV_UserVoip( client_t *cl, msg_t *msg ) {
else if (i == sender) else if (i == sender)
continue; // don't send voice packet back to original author. continue; // don't send voice packet back to original author.
else if (!client->hasVoip) else if (!client->hasVoip)
continue; // no VoIP support, or support disabled. continue; // no VoIP support, or unsupported protocol
else if (client->muteAllVoip) else if (client->muteAllVoip)
continue; // client is ignoring everyone. continue; // client is ignoring everyone.
else if (client->ignoreVoipFromClient[sender]) else if (client->ignoreVoipFromClient[sender])
continue; // client is ignoring this talker. continue; // client is ignoring this talker.
else if (*cl->downloadName) // !!! FIXME: possible to DoS? else if (*cl->downloadName) // !!! FIXME: possible to DoS?
continue; // no VoIP allowed if downloading, to save bandwidth. continue; // no VoIP allowed if downloading, to save bandwidth.
else if ( ((i >= 0) && (i < 31)) && ((recip1 & (1 << (i-0))) == 0) )
continue; // not addressed to this player. if(Com_IsVoipTarget(recips, sizeof(recips), i))
else if ( ((i >= 31) && (i < 62)) && ((recip2 & (1 << (i-31))) == 0) ) flags |= VOIP_DIRECT;
continue; // not addressed to this player. else
else if ( ((i >= 62) && (i < 93)) && ((recip3 & (1 << (i-62))) == 0) ) flags &= ~VOIP_DIRECT;
if (!(flags & (VOIP_SPATIAL | VOIP_DIRECT)))
continue; // not addressed to this player. continue; // not addressed to this player.
// Transmit this packet to the client. // Transmit this packet to the client.
@ -1902,6 +1849,7 @@ void SV_UserVoip( client_t *cl, msg_t *msg ) {
packet->len = packetsize; packet->len = packetsize;
packet->generation = generation; packet->generation = generation;
packet->sequence = sequence; packet->sequence = sequence;
packet->flags = flags;
memcpy(packet->data, encoded, packetsize); memcpy(packet->data, encoded, packetsize);
client->voipPacket[(client->queuedVoipIndex + client->queuedVoipPackets) % ARRAY_LEN(client->voipPacket)] = packet; client->voipPacket[(client->queuedVoipIndex + client->queuedVoipPackets) % ARRAY_LEN(client->voipPacket)] = packet;

View file

@ -521,6 +521,53 @@ static void SV_BuildClientSnapshot( client_t *client ) {
} }
} }
#ifdef USE_VOIP
/*
==================
SV_WriteVoipToClient
Check to see if there is any VoIP queued for a client, and send if there is.
==================
*/
static void SV_WriteVoipToClient(client_t *cl, msg_t *msg)
{
int totalbytes = 0;
int i;
voipServerPacket_t *packet;
if(cl->queuedVoipPackets)
{
// Write as many VoIP packets as we reasonably can...
for(i = 0; i < cl->queuedVoipPackets; i++)
{
packet = cl->voipPacket[(i + cl->queuedVoipIndex) % ARRAY_LEN(cl->voipPacket)];
if(!*cl->downloadName)
{
totalbytes += packet->len;
if (totalbytes > (msg->maxsize - msg->cursize) / 2)
break;
MSG_WriteByte(msg, svc_voip);
MSG_WriteShort(msg, packet->sender);
MSG_WriteByte(msg, (byte) packet->generation);
MSG_WriteLong(msg, packet->sequence);
MSG_WriteByte(msg, packet->frames);
MSG_WriteShort(msg, packet->len);
MSG_WriteBits(msg, packet->flags, VOIP_FLAGCNT);
MSG_WriteData(msg, packet->data, packet->len);
}
Z_Free(packet);
}
cl->queuedVoipPackets -= i;
cl->queuedVoipIndex += i;
cl->queuedVoipIndex %= ARRAY_LEN(cl->voipPacket);
}
}
#endif
/* /*
======================= =======================
SV_SendMessageToClient SV_SendMessageToClient

View file

@ -49,9 +49,12 @@ s_alCapture: set to "1" (the default) to have the audio layer open an OpenAL
cl_voipSendTarget: a string: "all" to broadcast to everyone, "none" to send cl_voipSendTarget: a string: "all" to broadcast to everyone, "none" to send
to no one, "attacker" to send to the last person that hit to no one, "attacker" to send to the last person that hit
you, "crosshair" to send to the people currently in your you, "crosshair" to send to the people currently in your
crosshair, or a comma-separated list of client numbers, like crosshair, "spatial" to talk to all people in hearing
"0,7,2,23" ... an empty string is treated like "all". This range or a comma-separated list of client numbers, like
is reset to "all" when connecting to a new server. "0,7,2,23" ... an empty string is treated like "spatial".
You can also use a mixed string like
"0, spatial, 2, crosshair".
This is reset to "spatial" when connecting to a new server.
Presumably mods will manage this cvar, not people, but Presumably mods will manage this cvar, not people, but
keybind could be useful for the general cases. To send to keybind could be useful for the general cases. To send to
just your team, or the opposing team, or a buddy list, you just your team, or the opposing team, or a buddy list, you