summaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/cl_avi.c11
-rw-r--r--src/client/cl_cgame.c36
-rw-r--r--src/client/cl_cin.c4
-rw-r--r--src/client/cl_curl.c74
-rw-r--r--src/client/cl_input.c14
-rw-r--r--src/client/cl_main.c234
-rw-r--r--src/client/cl_parse.c105
-rw-r--r--src/client/client.h21
-rw-r--r--src/client/snd_openal.c8
9 files changed, 299 insertions, 208 deletions
diff --git a/src/client/cl_avi.c b/src/client/cl_avi.c
index 723fbfa4..e4593923 100644
--- a/src/client/cl_avi.c
+++ b/src/client/cl_avi.c
@@ -124,17 +124,6 @@ static ID_INLINE void WRITE_2BYTES( int x )
/*
===============
-WRITE_1BYTES
-===============
-*/
-static ID_INLINE void WRITE_1BYTES( int x )
-{
- buffer[ bufIndex ] = x;
- bufIndex += 1;
-}
-
-/*
-===============
START_CHUNK
===============
*/
diff --git a/src/client/cl_cgame.c b/src/client/cl_cgame.c
index 1c7d91cc..23f3010c 100644
--- a/src/client/cl_cgame.c
+++ b/src/client/cl_cgame.c
@@ -956,37 +956,27 @@ void CL_FirstSnapshot( void ) {
#endif
#ifdef USE_VOIP
- if (!clc.speexInitialized) {
+ if (!clc.voipCodecInitialized) {
int i;
- speex_bits_init(&clc.speexEncoderBits);
- speex_bits_reset(&clc.speexEncoderBits);
+ int error;
- clc.speexEncoder = speex_encoder_init(&speex_nb_mode);
+ clc.opusEncoder = opus_encoder_create(48000, 1, OPUS_APPLICATION_VOIP, &error);
- speex_encoder_ctl(clc.speexEncoder, SPEEX_GET_FRAME_SIZE,
- &clc.speexFrameSize);
- speex_encoder_ctl(clc.speexEncoder, SPEEX_GET_SAMPLING_RATE,
- &clc.speexSampleRate);
-
- clc.speexPreprocessor = speex_preprocess_state_init(clc.speexFrameSize,
- clc.speexSampleRate);
-
- i = 1;
- speex_preprocess_ctl(clc.speexPreprocessor,
- SPEEX_PREPROCESS_SET_DENOISE, &i);
-
- i = 1;
- speex_preprocess_ctl(clc.speexPreprocessor,
- SPEEX_PREPROCESS_SET_AGC, &i);
+ if ( error ) {
+ Com_DPrintf("VoIP: Error opus_encoder_create %d\n", error);
+ return;
+ }
for (i = 0; i < MAX_CLIENTS; i++) {
- speex_bits_init(&clc.speexDecoderBits[i]);
- speex_bits_reset(&clc.speexDecoderBits[i]);
- clc.speexDecoder[i] = speex_decoder_init(&speex_nb_mode);
+ clc.opusDecoder[i] = opus_decoder_create(48000, 1, &error);
+ if ( error ) {
+ Com_DPrintf("VoIP: Error opus_decoder_create(%d) %d\n", i, error);
+ return;
+ }
clc.voipIgnore[i] = qfalse;
clc.voipGain[i] = 1.0f;
}
- clc.speexInitialized = qtrue;
+ clc.voipCodecInitialized = qtrue;
clc.voipMuteAll = qfalse;
Cmd_AddCommand ("voip", CL_Voip_f);
Cvar_Set("cl_voipSendTarget", "spatial");
diff --git a/src/client/cl_cin.c b/src/client/cl_cin.c
index 78a237ed..74660ed5 100644
--- a/src/client/cl_cin.c
+++ b/src/client/cl_cin.c
@@ -95,8 +95,8 @@ typedef struct {
qboolean looping, holdAtEnd, dirty, alterGameState, silent, shader;
fileHandle_t iFile;
e_status status;
- unsigned int startTime;
- unsigned int lastTime;
+ int startTime;
+ int lastTime;
long tfps;
long RoQPlayed;
long ROQSize;
diff --git a/src/client/cl_curl.c b/src/client/cl_curl.c
index 8d083877..166fb1dc 100644
--- a/src/client/cl_curl.c
+++ b/src/client/cl_curl.c
@@ -178,12 +178,20 @@ void CL_cURL_Shutdown( void )
void CL_cURL_Cleanup(void)
{
if(clc.downloadCURLM) {
+ CURLMcode result;
+
if(clc.downloadCURL) {
- qcurl_multi_remove_handle(clc.downloadCURLM,
+ result = qcurl_multi_remove_handle(clc.downloadCURLM,
clc.downloadCURL);
+ if(result != CURLM_OK) {
+ Com_DPrintf("qcurl_multi_remove_handle failed: %s\n", qcurl_multi_strerror(result));
+ }
qcurl_easy_cleanup(clc.downloadCURL);
}
- qcurl_multi_cleanup(clc.downloadCURLM);
+ result = qcurl_multi_cleanup(clc.downloadCURLM);
+ if(result != CURLM_OK) {
+ Com_DPrintf("CL_cURL_Cleanup: qcurl_multi_cleanup failed: %s\n", qcurl_multi_strerror(result));
+ }
clc.downloadCURLM = NULL;
clc.downloadCURL = NULL;
}
@@ -210,8 +218,36 @@ static size_t CL_cURL_CallbackWrite(void *buffer, size_t size, size_t nmemb,
return size*nmemb;
}
+CURLcode qcurl_easy_setopt_warn(CURL *curl, CURLoption option, ...)
+{
+ CURLcode result;
+
+ va_list argp;
+ va_start(argp, option);
+
+ if(option < CURLOPTTYPE_OBJECTPOINT) {
+ long longValue = va_arg(argp, long);
+ result = qcurl_easy_setopt(curl, option, longValue);
+ } else if(option < CURLOPTTYPE_OFF_T) {
+ void *pointerValue = va_arg(argp, void *);
+ result = qcurl_easy_setopt(curl, option, pointerValue);
+ } else {
+ curl_off_t offsetValue = va_arg(argp, curl_off_t);
+ result = qcurl_easy_setopt(curl, option, offsetValue);
+ }
+
+ if(result != CURLE_OK) {
+ Com_DPrintf("qcurl_easy_setopt failed: %s\n", qcurl_easy_strerror(result));
+ }
+ va_end(argp);
+
+ return result;
+}
+
void CL_cURL_BeginDownload( const char *localName, const char *remoteURL )
{
+ CURLMcode result;
+
clc.cURLUsed = qtrue;
Com_Printf("URL: %s\n", remoteURL);
Com_DPrintf("***** CL_cURL_BeginDownload *****\n"
@@ -247,23 +283,23 @@ void CL_cURL_BeginDownload( const char *localName, const char *remoteURL )
}
if(com_developer->integer)
- qcurl_easy_setopt(clc.downloadCURL, CURLOPT_VERBOSE, 1);
- qcurl_easy_setopt(clc.downloadCURL, CURLOPT_URL, clc.downloadURL);
- qcurl_easy_setopt(clc.downloadCURL, CURLOPT_TRANSFERTEXT, 0);
- qcurl_easy_setopt(clc.downloadCURL, CURLOPT_REFERER,
+ qcurl_easy_setopt_warn(clc.downloadCURL, CURLOPT_VERBOSE, 1);
+ qcurl_easy_setopt_warn(clc.downloadCURL, CURLOPT_URL, clc.downloadURL);
+ qcurl_easy_setopt_warn(clc.downloadCURL, CURLOPT_TRANSFERTEXT, 0);
+ qcurl_easy_setopt_warn(clc.downloadCURL, CURLOPT_REFERER,
va("Tremulous://%s", NET_AdrToString(clc.serverAddress)));
- qcurl_easy_setopt(clc.downloadCURL, CURLOPT_USERAGENT, va("%s %s",
+ qcurl_easy_setopt_warn(clc.downloadCURL, CURLOPT_USERAGENT, va("%s %s",
Q3_VERSION, qcurl_version()));
- qcurl_easy_setopt(clc.downloadCURL, CURLOPT_WRITEFUNCTION,
+ qcurl_easy_setopt_warn(clc.downloadCURL, CURLOPT_WRITEFUNCTION,
CL_cURL_CallbackWrite);
- qcurl_easy_setopt(clc.downloadCURL, CURLOPT_WRITEDATA, &clc.download);
- qcurl_easy_setopt(clc.downloadCURL, CURLOPT_NOPROGRESS, 0);
- qcurl_easy_setopt(clc.downloadCURL, CURLOPT_PROGRESSFUNCTION,
+ qcurl_easy_setopt_warn(clc.downloadCURL, CURLOPT_WRITEDATA, &clc.download);
+ qcurl_easy_setopt_warn(clc.downloadCURL, CURLOPT_NOPROGRESS, 0);
+ qcurl_easy_setopt_warn(clc.downloadCURL, CURLOPT_PROGRESSFUNCTION,
CL_cURL_CallbackProgress);
- qcurl_easy_setopt(clc.downloadCURL, CURLOPT_PROGRESSDATA, NULL);
- qcurl_easy_setopt(clc.downloadCURL, CURLOPT_FAILONERROR, 1);
- qcurl_easy_setopt(clc.downloadCURL, CURLOPT_FOLLOWLOCATION, 1);
- qcurl_easy_setopt(clc.downloadCURL, CURLOPT_MAXREDIRS, 5);
+ qcurl_easy_setopt_warn(clc.downloadCURL, CURLOPT_PROGRESSDATA, NULL);
+ qcurl_easy_setopt_warn(clc.downloadCURL, CURLOPT_FAILONERROR, 1);
+ qcurl_easy_setopt_warn(clc.downloadCURL, CURLOPT_FOLLOWLOCATION, 1);
+ qcurl_easy_setopt_warn(clc.downloadCURL, CURLOPT_MAXREDIRS, 5);
clc.downloadCURLM = qcurl_multi_init();
if(!clc.downloadCURLM) {
qcurl_easy_cleanup(clc.downloadCURL);
@@ -272,7 +308,13 @@ void CL_cURL_BeginDownload( const char *localName, const char *remoteURL )
"failed");
return;
}
- qcurl_multi_add_handle(clc.downloadCURLM, clc.downloadCURL);
+ result = qcurl_multi_add_handle(clc.downloadCURLM, clc.downloadCURL);
+ if(result != CURLM_OK) {
+ qcurl_easy_cleanup(clc.downloadCURL);
+ clc.downloadCURL = NULL;
+ Com_Error(ERR_DROP,"CL_cURL_BeginDownload: qcurl_multi_add_handle() failed: %s", qcurl_multi_strerror(result));
+ return;
+ }
if(!(clc.sv_allowDownload & DLF_NO_DISCONNECT) &&
!clc.cURLDisconnected) {
diff --git a/src/client/cl_input.c b/src/client/cl_input.c
index e09d7d75..7a917df2 100644
--- a/src/client/cl_input.c
+++ b/src/client/cl_input.c
@@ -605,10 +605,10 @@ usercmd_t CL_CreateCmd( void ) {
// draw debug graphs of turning for mouse testing
if ( cl_debugMove->integer ) {
if ( cl_debugMove->integer == 1 ) {
- SCR_DebugGraph( abs(cl.viewangles[YAW] - oldAngles[YAW]) );
+ SCR_DebugGraph( fabs(cl.viewangles[YAW] - oldAngles[YAW]) );
}
if ( cl_debugMove->integer == 2 ) {
- SCR_DebugGraph( abs(cl.viewangles[PITCH] - oldAngles[PITCH]) );
+ SCR_DebugGraph( fabs(cl.viewangles[PITCH] - oldAngles[PITCH]) );
}
}
@@ -633,6 +633,12 @@ void CL_CreateNewCommands( void ) {
frame_msec = com_frameTime - old_com_frameTime;
+ // if running over 1000fps, act as if each frame is 1ms
+ // prevents divisions by zero
+ if ( frame_msec < 1 ) {
+ frame_msec = 1;
+ }
+
// if running less than 5fps, truncate the extra time to prevent
// unexpected moves after a hitch
if ( frame_msec > 200 ) {
@@ -789,7 +795,7 @@ void CL_WritePacket( void ) {
{
if((clc.voipFlags & VOIP_SPATIAL) || Com_IsVoipTarget(clc.voipTargets, sizeof(clc.voipTargets), -1))
{
- MSG_WriteByte (&buf, clc_voip);
+ MSG_WriteByte (&buf, clc_voipOpus);
MSG_WriteByte (&buf, clc.voipOutgoingGeneration);
MSG_WriteLong (&buf, clc.voipOutgoingSequence);
MSG_WriteByte (&buf, clc.voipOutgoingDataFrames);
@@ -810,7 +816,7 @@ void CL_WritePacket( void ) {
MSG_Init (&fakemsg, fakedata, sizeof (fakedata));
MSG_Bitstream (&fakemsg);
MSG_WriteLong (&fakemsg, clc.reliableAcknowledge);
- MSG_WriteByte (&fakemsg, svc_voip);
+ MSG_WriteByte (&fakemsg, svc_voipOpus);
MSG_WriteShort (&fakemsg, clc.clientNum);
MSG_WriteByte (&fakemsg, clc.voipOutgoingGeneration);
MSG_WriteLong (&fakemsg, clc.voipOutgoingSequence);
diff --git a/src/client/cl_main.c b/src/client/cl_main.c
index 34eb95af..dc177cf7 100644
--- a/src/client/cl_main.c
+++ b/src/client/cl_main.c
@@ -45,6 +45,7 @@ cvar_t *cl_voipSendTarget;
cvar_t *cl_voipGainDuringCapture;
cvar_t *cl_voipCaptureMult;
cvar_t *cl_voipShowMeter;
+cvar_t *cl_voipProtocol;
cvar_t *cl_voip;
#endif
@@ -149,7 +150,6 @@ typedef struct serverStatus_s
} serverStatus_t;
serverStatus_t cl_serverStatusList[MAX_SERVERSTATUSREQUESTS];
-int serverStatusCount;
#if defined __USEA3D && defined __A3D_GEOM
void hA3Dg_ExportRenderGeom (refexport_t *incoming_re);
@@ -251,8 +251,8 @@ void CL_Voip_f( void )
if (clc.state != CA_ACTIVE)
reason = "Not connected to a server";
- else if (!clc.speexInitialized)
- reason = "Speex not initialized";
+ else if (!clc.voipCodecInitialized)
+ reason = "Voip codec not initialized";
else if (!clc.voipEnabled)
reason = "Server doesn't support VoIP";
@@ -305,6 +305,8 @@ void CL_VoipNewGeneration(void)
clc.voipOutgoingGeneration = 1;
clc.voipPower = 0.0f;
clc.voipOutgoingSequence = 0;
+
+ opus_encoder_ctl(clc.opusEncoder, OPUS_RESET_STATE);
}
/*
@@ -393,7 +395,7 @@ void CL_VoipParseTargets(void)
===============
CL_CaptureVoip
-Record more audio from the hardware if required and encode it into Speex
+Record more audio from the hardware if required and encode it into Opus
data for later transmission.
===============
*/
@@ -423,11 +425,12 @@ void CL_CaptureVoip(void)
Com_Printf("Until then, VoIP is disabled.\n");
Cvar_Set("cl_voip", "0");
}
+ Cvar_Set("cl_voipProtocol", cl_voip->integer ? "opus" : "");
cl_voip->modified = qfalse;
cl_rate->modified = qfalse;
}
- if (!clc.speexInitialized)
+ if (!clc.voipCodecInitialized)
return; // just in case this gets called at a bad time.
if (clc.voipOutgoingDataSize > 0)
@@ -480,80 +483,67 @@ void CL_CaptureVoip(void)
if ((cl_voipSend->integer) || (finalFrame)) { // user wants to capture audio?
int samples = S_AvailableCaptureSamples();
- const int mult = (finalFrame) ? 1 : 4; // 4 == 80ms of audio.
+ const int packetSamples = (finalFrame) ? VOIP_MAX_FRAME_SAMPLES : VOIP_MAX_PACKET_SAMPLES;
// enough data buffered in audio hardware to process yet?
- if (samples >= (clc.speexFrameSize * mult)) {
- // audio capture is always MONO16 (and that's what speex wants!).
- // 2048 will cover 12 uncompressed frames in narrowband mode.
- static int16_t sampbuffer[2048];
+ if (samples >= packetSamples) {
+ // audio capture is always MONO16.
+ static int16_t sampbuffer[VOIP_MAX_PACKET_SAMPLES];
float voipPower = 0.0f;
- int speexFrames = 0;
- int wpos = 0;
- int pos = 0;
+ int voipFrames;
+ int i, bytes;
- if (samples > (clc.speexFrameSize * 4))
- samples = (clc.speexFrameSize * 4);
+ if (samples > VOIP_MAX_PACKET_SAMPLES)
+ samples = VOIP_MAX_PACKET_SAMPLES;
// !!! FIXME: maybe separate recording from encoding, so voipPower
// !!! FIXME: updates faster than 4Hz?
- samples -= samples % clc.speexFrameSize;
- S_Capture(samples, (byte *) sampbuffer); // grab from audio card.
-
- // this will probably generate multiple speex packets each time.
- while (samples > 0) {
- int16_t *sampptr = &sampbuffer[pos];
- int i, bytes;
+ samples -= samples % VOIP_MAX_FRAME_SAMPLES;
+ if (samples != 120 && samples != 240 && samples != 480 && samples != 960 && samples != 1920 && samples != 2880 ) {
+ Com_Printf("Voip: bad number of samples %d\n", samples);
+ return;
+ }
+ voipFrames = samples / VOIP_MAX_FRAME_SAMPLES;
- // preprocess samples to remove noise...
- speex_preprocess_run(clc.speexPreprocessor, sampptr);
+ S_Capture(samples, (byte *) sampbuffer); // grab from audio card.
- // check the "power" of this packet...
- for (i = 0; i < clc.speexFrameSize; i++) {
- const float flsamp = (float) sampptr[i];
- const float s = fabs(flsamp);
- voipPower += s * s;
- sampptr[i] = (int16_t) ((flsamp) * audioMult);
- }
+ // check the "power" of this packet...
+ for (i = 0; i < samples; i++) {
+ const float flsamp = (float) sampbuffer[i];
+ const float s = fabs(flsamp);
+ voipPower += s * s;
+ sampbuffer[i] = (int16_t) ((flsamp) * audioMult);
+ }
- // encode raw audio samples into Speex data...
- speex_bits_reset(&clc.speexEncoderBits);
- speex_encode_int(clc.speexEncoder, sampptr,
- &clc.speexEncoderBits);
- bytes = speex_bits_write(&clc.speexEncoderBits,
- (char *) &clc.voipOutgoingData[wpos+1],
- sizeof (clc.voipOutgoingData) - (wpos+1));
- assert((bytes > 0) && (bytes < 256));
- clc.voipOutgoingData[wpos] = (byte) bytes;
- wpos += bytes + 1;
-
- // look at the data for the next packet...
- pos += clc.speexFrameSize;
- samples -= clc.speexFrameSize;
- speexFrames++;
+ // encode raw audio samples into Opus data...
+ bytes = opus_encode(clc.opusEncoder, sampbuffer, samples,
+ (unsigned char *) clc.voipOutgoingData,
+ sizeof (clc.voipOutgoingData));
+ if ( bytes <= 0 ) {
+ Com_DPrintf("VoIP: Error encoding %d samples\n", samples);
+ bytes = 0;
}
clc.voipPower = (voipPower / (32768.0f * 32768.0f *
- ((float) (clc.speexFrameSize * speexFrames)))) *
- 100.0f;
+ ((float) samples))) * 100.0f;
if ((useVad) && (clc.voipPower < cl_voipVADThreshold->value)) {
CL_VoipNewGeneration(); // no "talk" for at least 1/4 second.
} else {
- clc.voipOutgoingDataSize = wpos;
- clc.voipOutgoingDataFrames = speexFrames;
+ clc.voipOutgoingDataSize = bytes;
+ clc.voipOutgoingDataFrames = voipFrames;
Com_DPrintf("VoIP: Send %d frames, %d bytes, %f power\n",
- speexFrames, wpos, clc.voipPower);
+ voipFrames, bytes, clc.voipPower);
#if 0
static FILE *encio = NULL;
if (encio == NULL) encio = fopen("voip-outgoing-encoded.bin", "wb");
- if (encio != NULL) { fwrite(clc.voipOutgoingData, wpos, 1, encio); fflush(encio); }
+ if (encio != NULL) { fwrite(clc.voipOutgoingData, bytes, 1, encio); fflush(encio); }
static FILE *decio = NULL;
if (decio == NULL) decio = fopen("voip-outgoing-decoded.bin", "wb");
- if (decio != NULL) { fwrite(sampbuffer, speexFrames * clc.speexFrameSize * 2, 1, decio); fflush(decio); }
+ if (decio != NULL) { fwrite(sampbuffer, voipFrames * VOIP_MAX_FRAME_SAMPLES * 2, 1, decio); fflush(decio); }
#endif
}
}
@@ -1420,14 +1410,11 @@ void CL_Disconnect( qboolean showMainMenu ) {
cl_voipUseVAD->integer = tmp;
}
- if (clc.speexInitialized) {
+ if (clc.voipCodecInitialized) {
int i;
- speex_bits_destroy(&clc.speexEncoderBits);
- speex_encoder_destroy(clc.speexEncoder);
- speex_preprocess_state_destroy(clc.speexPreprocessor);
+ opus_encoder_destroy(clc.opusEncoder);
for (i = 0; i < MAX_CLIENTS; i++) {
- speex_bits_destroy(&clc.speexDecoderBits[i]);
- speex_decoder_destroy(clc.speexDecoder[i]);
+ opus_decoder_destroy(clc.opusDecoder[i]);
}
}
Cmd_RemoveCommand ("voip");
@@ -1732,6 +1719,50 @@ static void CL_CompleteRcon( char *args, int argNum )
}
/*
+==================
+CL_CompletePlayerName
+==================
+*/
+static void CL_CompletePlayerName( char *args, int argNum )
+{
+ if( argNum == 2 )
+ {
+ char names[MAX_CLIENTS][MAX_NAME_LENGTH];
+ char *namesPtr[MAX_CLIENTS];
+ int i;
+ int clientCount;
+ int nameCount;
+ const char *info;
+ const char *name;
+
+ //configstring
+ info = cl.gameState.stringData + cl.gameState.stringOffsets[CS_SERVERINFO];
+ clientCount = atoi( Info_ValueForKey( info, "sv_maxclients" ) );
+
+ nameCount = 0;
+
+ for( i = 0; i < clientCount; i++ ) {
+ if( i == clc.clientNum )
+ continue;
+
+ info = cl.gameState.stringData + cl.gameState.stringOffsets[CS_PLAYERS+i];
+
+ name = Info_ValueForKey( info, "n" );
+ if( name[0] == '\0' )
+ continue;
+ Q_strncpyz( names[nameCount], name, sizeof(names[nameCount]) );
+ Q_CleanStr( names[nameCount] );
+
+ namesPtr[nameCount] = names[nameCount];
+ nameCount++;
+ }
+ qsort( (void*)namesPtr, nameCount, sizeof( namesPtr[0] ), Com_strCompare );
+
+ Field_CompletePlayerName( namesPtr, nameCount );
+ }
+}
+
+/*
=====================
CL_Rcon_f
@@ -1743,7 +1774,7 @@ void CL_Rcon_f( void ) {
char message[MAX_RCON_MESSAGE];
netadr_t to;
- if ( !rcon_client_password->string ) {
+ if ( !rcon_client_password->string[0] ) {
Com_Printf ("You must set 'rconpassword' before\n"
"issuing an rcon command.\n");
return;
@@ -2312,9 +2343,9 @@ Resend a connect message if the last one has timed out
=================
*/
void CL_CheckForResend( void ) {
- int port, i;
+ int port;
char info[MAX_INFO_STRING];
- char data[MAX_INFO_STRING];
+ char data[MAX_INFO_STRING + 10];
// don't send anything if playing back a demo
if ( clc.demoplaying ) {
@@ -2355,19 +2386,8 @@ void CL_CheckForResend( void ) {
Info_SetValueForKey( info, "qport", va("%i", port ) );
Info_SetValueForKey( info, "challenge", va("%i", clc.challenge ) );
- strcpy(data, "connect ");
- // TTimo adding " " around the userinfo string to avoid truncated userinfo on the server
- // (Com_TokenizeString tokenizes around spaces)
- data[8] = '"';
-
- for(i=0;i<strlen(info);i++) {
- data[9+i] = info[i]; // + (clc.challenge)&0x3;
- }
- data[9+i] = '"';
- data[10+i] = 0;
-
- // NOTE TTimo don't forget to set the right data length!
- NET_OutOfBandData( NS_CLIENT, clc.serverAddress, (byte *) &data[0], i+10 );
+ Com_sprintf( data, sizeof(data), "connect \"%s\"", info );
+ NET_OutOfBandData( NS_CLIENT, clc.serverAddress, (byte *) data, strlen ( data ) );
// the most current userinfo has been sent, so watch for any
// newer changes to userinfo variables
cvar_modifiedFlags &= ~CVAR_USERINFO;
@@ -3474,6 +3494,56 @@ static void CL_GenerateQKey(void)
}
}
+void CL_Sayto_f( void ) {
+ char *rawname;
+ char name[MAX_NAME_LENGTH];
+ char cleanName[MAX_NAME_LENGTH];
+ const char *info;
+ int count;
+ int i;
+ int clientNum;
+ char *p;
+
+ if ( Cmd_Argc() < 3 ) {
+ Com_Printf ("sayto <player name> <text>\n");
+ return;
+ }
+
+ rawname = Cmd_Argv(1);
+
+ Com_FieldStringToPlayerName( name, MAX_NAME_LENGTH, rawname );
+
+ info = cl.gameState.stringData + cl.gameState.stringOffsets[CS_SERVERINFO];
+ count = atoi( Info_ValueForKey( info, "sv_maxclients" ) );
+
+ clientNum = -1;
+ for( i = 0; i < count; i++ ) {
+
+ info = cl.gameState.stringData + cl.gameState.stringOffsets[CS_PLAYERS+i];
+ Q_strncpyz( cleanName, Info_ValueForKey( info, "n" ), sizeof(cleanName) );
+ Q_CleanStr( cleanName );
+
+ if ( !Q_stricmp( cleanName, name ) ) {
+ clientNum = i;
+ break;
+ }
+ }
+ if( clientNum <= -1 )
+ {
+ Com_Printf ("No such player name: %s.\n", name);
+ return;
+ }
+
+ p = Cmd_ArgsFrom(2);
+
+ if ( *p == '"' ) {
+ p++;
+ p[strlen(p)-1] = 0;
+ }
+
+ CL_AddReliableCommand(va("tell %i \"%s\"", clientNum, p ), qfalse);
+}
+
/*
====================
CL_Init
@@ -3624,9 +3694,9 @@ void CL_Init( void ) {
cl_voipVADThreshold = Cvar_Get ("cl_voipVADThreshold", "0.25", CVAR_ARCHIVE);
cl_voipShowMeter = Cvar_Get ("cl_voipShowMeter", "1", CVAR_ARCHIVE);
- // This is a protocol version number.
- cl_voip = Cvar_Get ("cl_voip", "1", CVAR_USERINFO | CVAR_ARCHIVE);
+ cl_voip = Cvar_Get ("cl_voip", "1", CVAR_ARCHIVE);
Cvar_CheckRange( cl_voip, 0, 1, qtrue );
+ cl_voipProtocol = Cvar_Get ("cl_voipProtocol", cl_voip->integer ? "opus" : "", CVAR_USERINFO | CVAR_ROM);
#endif
@@ -3663,6 +3733,10 @@ void CL_Init( void ) {
Cmd_AddCommand ("model", CL_SetModel_f );
Cmd_AddCommand ("video", CL_Video_f );
Cmd_AddCommand ("stopvideo", CL_StopVideo_f );
+ if( !com_dedicated->integer ) {
+ Cmd_AddCommand ("sayto", CL_Sayto_f );
+ Cmd_SetCommandCompletionFunc( "sayto", CL_CompletePlayerName );
+ }
CL_InitRef();
SCR_Init ();
@@ -3918,11 +3992,7 @@ serverStatus_t *CL_GetServerStatus( netadr_t from ) {
oldestTime = cl_serverStatusList[i].startTime;
}
}
- if (oldest != -1) {
- return &cl_serverStatusList[oldest];
- }
- serverStatusCount++;
- return &cl_serverStatusList[serverStatusCount & (MAX_SERVERSTATUSREQUESTS-1)];
+ return &cl_serverStatusList[oldest];
}
/*
diff --git a/src/client/cl_parse.c b/src/client/cl_parse.c
index a8b68ca5..981d21c6 100644
--- a/src/client/cl_parse.c
+++ b/src/client/cl_parse.c
@@ -35,7 +35,8 @@ char *svc_strings[256] = {
"svc_download",
"svc_snapshot",
"svc_EOF",
- "svc_voip",
+ "svc_voipSpeex",
+ "svc_voipOpus",
};
void SHOWNET( msg_t *msg, char *s) {
@@ -354,8 +355,8 @@ void CL_SystemInfoChanged( void ) {
cl.serverId = atoi( Info_ValueForKey( systemInfo, "sv_serverid" ) );
#ifdef USE_VOIP
- s = Info_ValueForKey( systemInfo, "sv_voip" );
- clc.voipEnabled = atoi(s);
+ s = Info_ValueForKey( systemInfo, "sv_voipProtocol" );
+ clc.voipEnabled = !Q_stricmp(s, "opus");
#endif
// don't set any vars when playing a demo
@@ -674,13 +675,13 @@ 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,
+ S_RawSamples(sender + 1, samplecnt, 48000, 2, 1,
data, clc.voipGain[sender], -1);
}
if(flags & VOIP_SPATIAL)
{
- S_RawSamples(sender + MAX_CLIENTS + 1, samplecnt, clc.speexSampleRate, 2, 1,
+ S_RawSamples(sender + MAX_CLIENTS + 1, samplecnt, 48000, 2, 1,
data, 1.0f, sender);
}
}
@@ -693,8 +694,8 @@ A VoIP message has been received from the server
=====================
*/
static
-void CL_ParseVoip ( msg_t *msg ) {
- static short decoded[4096]; // !!! FIXME: don't hardcode.
+void CL_ParseVoip ( msg_t *msg, qboolean ignoreData ) {
+ static short decoded[VOIP_MAX_PACKET_SAMPLES*4]; // !!! FIXME: don't hard code
const int sender = MSG_ReadShort(msg);
const int generation = MSG_ReadByte(msg);
@@ -702,7 +703,8 @@ void CL_ParseVoip ( msg_t *msg ) {
const int frames = MSG_ReadByte(msg);
const int packetsize = MSG_ReadShort(msg);
const int flags = MSG_ReadBits(msg, VOIP_FLAGCNT);
- char encoded[1024];
+ unsigned char encoded[4000];
+ int numSamples;
int seqdiff;
int written = 0;
int i;
@@ -732,14 +734,15 @@ void CL_ParseVoip ( msg_t *msg ) {
return; // overlarge packet, bail.
}
- if (!clc.speexInitialized) {
- MSG_ReadData(msg, encoded, packetsize); // skip payload.
- return; // can't handle VoIP without libspeex!
+ MSG_ReadData(msg, encoded, packetsize);
+
+ if (ignoreData) {
+ return; // just ignore legacy speex voip data
+ } else if (!clc.voipCodecInitialized) {
+ return; // can't handle VoIP without libopus!
} else if (sender >= MAX_CLIENTS) {
- MSG_ReadData(msg, encoded, packetsize); // skip payload.
return; // bogus sender.
} else if (CL_ShouldIgnoreVoipSender(sender)) {
- MSG_ReadData(msg, encoded, packetsize); // skip payload.
return; // Channel is muted, bail.
}
@@ -752,70 +755,59 @@ void CL_ParseVoip ( msg_t *msg ) {
// This is a new "generation" ... a new recording started, reset the bits.
if (generation != clc.voipIncomingGeneration[sender]) {
Com_DPrintf("VoIP: new generation %d!\n", generation);
- speex_bits_reset(&clc.speexDecoderBits[sender]);
+ opus_decoder_ctl(clc.opusDecoder[sender], OPUS_RESET_STATE);
clc.voipIncomingGeneration[sender] = generation;
seqdiff = 0;
} else if (seqdiff < 0) { // we're ahead of the sequence?!
// This shouldn't happen unless the packet is corrupted or something.
Com_DPrintf("VoIP: misordered sequence! %d < %d!\n",
sequence, clc.voipIncomingSequence[sender]);
- // reset the bits just in case.
- speex_bits_reset(&clc.speexDecoderBits[sender]);
+ // reset the decoder just in case.
+ opus_decoder_ctl(clc.opusDecoder[sender], OPUS_RESET_STATE);
seqdiff = 0;
- } else if (seqdiff * clc.speexFrameSize * 2 >= sizeof (decoded)) { // dropped more than we can handle?
+ } else if (seqdiff * VOIP_MAX_PACKET_SAMPLES*2 >= sizeof (decoded)) { // dropped more than we can handle?
// just start over.
Com_DPrintf("VoIP: Dropped way too many (%d) frames from client #%d\n",
seqdiff, sender);
- speex_bits_reset(&clc.speexDecoderBits[sender]);
+ opus_decoder_ctl(clc.opusDecoder[sender], OPUS_RESET_STATE);
seqdiff = 0;
}
if (seqdiff != 0) {
Com_DPrintf("VoIP: Dropped %d frames from client #%d\n",
seqdiff, sender);
- // tell speex that we're missing frames...
+ // tell opus that we're missing frames...
for (i = 0; i < seqdiff; i++) {
- assert((written + clc.speexFrameSize) * 2 < sizeof (decoded));
- speex_decode_int(clc.speexDecoder[sender], NULL, decoded + written);
- written += clc.speexFrameSize;
+ assert((written + VOIP_MAX_PACKET_SAMPLES) * 2 < sizeof (decoded));
+ numSamples = opus_decode(clc.opusDecoder[sender], NULL, 0, decoded + written, VOIP_MAX_PACKET_SAMPLES, 0);
+ if ( numSamples <= 0 ) {
+ Com_DPrintf("VoIP: Error decoding frame %d from client #%d\n", i, sender);
+ continue;
+ }
+ written += numSamples;
}
}
- for (i = 0; i < frames; i++) {
- const int len = MSG_ReadByte(msg);
- if (len < 0) {
- Com_DPrintf("VoIP: Short packet!\n");
- break;
- }
- MSG_ReadData(msg, encoded, len);
+ numSamples = opus_decode(clc.opusDecoder[sender], encoded, packetsize, decoded + written, ARRAY_LEN(decoded) - written, 0);
- // shouldn't happen, but just in case...
- if ((written + clc.speexFrameSize) * 2 > sizeof (decoded)) {
- Com_DPrintf("VoIP: playback %d bytes, %d samples, %d frames\n",
- written * 2, written, i);
-
- CL_PlayVoip(sender, written, (const byte *) decoded, flags);
- written = 0;
- }
-
- speex_bits_read_from(&clc.speexDecoderBits[sender], encoded, len);
- speex_decode_int(clc.speexDecoder[sender],
- &clc.speexDecoderBits[sender], decoded + written);
+ if ( numSamples <= 0 ) {
+ Com_DPrintf("VoIP: Error decoding voip data from client #%d\n", sender);
+ numSamples = 0;
+ }
- #if 0
- static FILE *encio = NULL;
- if (encio == NULL) encio = fopen("voip-incoming-encoded.bin", "wb");
- if (encio != NULL) { fwrite(encoded, len, 1, encio); fflush(encio); }
- static FILE *decio = NULL;
- if (decio == NULL) decio = fopen("voip-incoming-decoded.bin", "wb");
- if (decio != NULL) { fwrite(decoded+written, clc.speexFrameSize*2, 1, decio); fflush(decio); }
- #endif
+ #if 0
+ static FILE *encio = NULL;
+ if (encio == NULL) encio = fopen("voip-incoming-encoded.bin", "wb");
+ if (encio != NULL) { fwrite(encoded, len, 1, encio); fflush(encio); }
+ static FILE *decio = NULL;
+ if (decio == NULL) decio = fopen("voip-incoming-decoded.bin", "wb");
+ if (decio != NULL) { fwrite(decoded+written, clc.speexFrameSize*2, 1, decio); fflush(decio); }
+ #endif
- written += clc.speexFrameSize;
- }
+ written += numSamples;
Com_DPrintf("VoIP: playback %d bytes, %d samples, %d frames\n",
- written * 2, written, i);
+ written * 2, written, frames);
if(written > 0)
CL_PlayVoip(sender, written, (const byte *) decoded, flags);
@@ -918,9 +910,14 @@ void CL_ParseServerMessage( msg_t *msg ) {
case svc_download:
CL_ParseDownload( msg );
break;
- case svc_voip:
+ case svc_voipSpeex:
+#ifdef USE_VOIP
+ CL_ParseVoip( msg, qtrue );
+#endif
+ break;
+ case svc_voipOpus:
#ifdef USE_VOIP
- CL_ParseVoip( msg );
+ CL_ParseVoip( msg, !clc.voipEnabled );
#endif
break;
}
diff --git a/src/client/client.h b/src/client/client.h
index 541dca40..b4016467 100644
--- a/src/client/client.h
+++ b/src/client/client.h
@@ -36,8 +36,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#endif /* USE_CURL */
#ifdef USE_VOIP
-#include "speex/speex.h"
-#include "speex/speex_preprocess.h"
+#include <opus.h>
#endif
// file full of random crap that gets used to create cl_guid
@@ -241,14 +240,11 @@ typedef struct {
#ifdef USE_VOIP
qboolean voipEnabled;
- qboolean speexInitialized;
- int speexFrameSize;
- int speexSampleRate;
+ qboolean voipCodecInitialized;
// incoming data...
// !!! FIXME: convert from parallel arrays to array of a struct.
- SpeexBits speexDecoderBits[MAX_CLIENTS];
- void *speexDecoder[MAX_CLIENTS];
+ OpusDecoder *opusDecoder[MAX_CLIENTS];
byte voipIncomingGeneration[MAX_CLIENTS];
int voipIncomingSequence[MAX_CLIENTS];
float voipGain[MAX_CLIENTS];
@@ -260,9 +256,7 @@ typedef struct {
// then we are sending to clientnum i.
uint8_t voipTargets[(MAX_CLIENTS + 7) / 8];
uint8_t voipFlags;
- SpeexPreprocessState *speexPreprocessor;
- SpeexBits speexEncoderBits;
- void *speexEncoder;
+ OpusEncoder *opusEncoder;
int voipOutgoingDataSize;
int voipOutgoingDataFrames;
int voipOutgoingSequence;
@@ -449,6 +443,13 @@ extern cvar_t *cl_voipGainDuringCapture;
extern cvar_t *cl_voipCaptureMult;
extern cvar_t *cl_voipShowMeter;
extern cvar_t *cl_voip;
+
+// 20ms at 48k
+#define VOIP_MAX_FRAME_SAMPLES ( 20 * 48 )
+
+// 3 frame is 60ms of audio, the max opus will encode at once
+#define VOIP_MAX_PACKET_FRAMES 3
+#define VOIP_MAX_PACKET_SAMPLES ( VOIP_MAX_FRAME_SAMPLES * VOIP_MAX_PACKET_FRAMES )
#endif
//=================================================
diff --git a/src/client/snd_openal.c b/src/client/snd_openal.c
index d90e6d23..b5c89918 100644
--- a/src/client/snd_openal.c
+++ b/src/client/snd_openal.c
@@ -2700,16 +2700,12 @@ qboolean S_AL_Init( soundInterface_t *si )
s_alAvailableInputDevices = Cvar_Get("s_alAvailableInputDevices", inputdevicenames, CVAR_ROM | CVAR_NORESTART);
- // !!! FIXME: 8000Hz is what Speex narrowband mode needs, but we
- // !!! FIXME: should probably open the capture device after
- // !!! FIXME: initializing Speex so we can change to wideband
- // !!! FIXME: if we like.
Com_Printf("OpenAL default capture device is '%s'\n", defaultinputdevice ? defaultinputdevice : "none");
- alCaptureDevice = qalcCaptureOpenDevice(inputdevice, 8000, AL_FORMAT_MONO16, 4096);
+ alCaptureDevice = qalcCaptureOpenDevice(inputdevice, 48000, AL_FORMAT_MONO16, VOIP_MAX_PACKET_SAMPLES*4);
if( !alCaptureDevice && inputdevice )
{
Com_Printf( "Failed to open OpenAL Input device '%s', trying default.\n", inputdevice );
- alCaptureDevice = qalcCaptureOpenDevice(NULL, 8000, AL_FORMAT_MONO16, 4096);
+ alCaptureDevice = qalcCaptureOpenDevice(NULL, 48000, AL_FORMAT_MONO16, VOIP_MAX_PACKET_SAMPLES*4);
}
Com_Printf( "OpenAL capture device %s.\n",
(alCaptureDevice == NULL) ? "failed to open" : "opened");