diff options
Diffstat (limited to 'src/client/cl_main.c')
-rw-r--r-- | src/client/cl_main.c | 234 |
1 files changed, 152 insertions, 82 deletions
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]; } /* |