From e9f14bee6055b9a52b44074452f7e37b7f901ca9 Mon Sep 17 00:00:00 2001 From: icculus Date: Sat, 7 Jun 2008 14:40:59 +0000 Subject: VoIP: initial shot at voice activation. git-svn-id: svn://svn.icculus.org/quake3/trunk@1375 edf5b092-35ff-0310-97b2-ce42778d08ea --- code/client/cl_cgame.c | 4 +++ code/client/cl_input.c | 21 +++++++------- code/client/cl_main.c | 79 +++++++++++++++++++++++++++++++++++--------------- code/client/client.h | 1 + 4 files changed, 71 insertions(+), 34 deletions(-) (limited to 'code') diff --git a/code/client/cl_cgame.c b/code/client/cl_cgame.c index 18a9630..d0624c9 100644 --- a/code/client/cl_cgame.c +++ b/code/client/cl_cgame.c @@ -937,6 +937,10 @@ void CL_FirstSnapshot( void ) { speex_preprocess_ctl(clc.speexPreprocessor, SPEEX_PREPROCESS_SET_DENOISE, &i); + i = (cl_voipUseVAD->integer != 0); + speex_preprocess_ctl(clc.speexPreprocessor, + SPEEX_PREPROCESS_SET_VAD, &i); + for (i = 0; i < MAX_CLIENTS; i++) { speex_bits_init(&clc.speexDecoderBits[i]); speex_bits_reset(&clc.speexDecoderBits[i]); diff --git a/code/client/cl_input.c b/code/client/cl_input.c index e2ee6f6..5eab1a2 100644 --- a/code/client/cl_input.c +++ b/code/client/cl_input.c @@ -221,8 +221,17 @@ void IN_StrafeDown(void) {IN_KeyDown(&in_strafe);} void IN_StrafeUp(void) {IN_KeyUp(&in_strafe);} #if USE_VOIP -void IN_VoipRecordDown(void) {IN_KeyDown(&in_voiprecord);} -void IN_VoipRecordUp(void) {IN_KeyUp(&in_voiprecord);} +void IN_VoipRecordDown(void) +{ + IN_KeyDown(&in_voiprecord); + Cvar_Set("cl_voipSend", "1"); +} + +void IN_VoipRecordUp(void) +{ + IN_KeyUp(&in_voiprecord); + Cvar_Set("cl_voipSend", "0"); +} #endif void IN_Button0Down(void) {IN_KeyDown(&in_buttons[0]);} @@ -556,14 +565,6 @@ usercmd_t CL_CreateCmd( void ) { // get basic movement from joystick CL_JoystickMove( &cmd ); -#if USE_VOIP - if ( ( in_voiprecord.active ) && ( !cl_voipSend->integer ) ) { - Cvar_Set("cl_voipSend", "1"); - } else if ( ( !in_voiprecord.active ) && ( cl_voipSend->integer ) ) { - Cvar_Set("cl_voipSend", "0"); - } -#endif - // check to make sure the angles haven't wrapped if ( cl.viewangles[PITCH] - oldAngles[PITCH] > 90 ) { cl.viewangles[PITCH] = oldAngles[PITCH] + 90; diff --git a/code/client/cl_main.c b/code/client/cl_main.c index 8d72ed4..334b6a5 100644 --- a/code/client/cl_main.c +++ b/code/client/cl_main.c @@ -34,6 +34,7 @@ cvar_t *cl_mumbleScale; #endif #if USE_VOIP +cvar_t *cl_voipUseVAD; cvar_t *cl_voipSend; cvar_t *cl_voipSendTarget; cvar_t *cl_voipGainDuringCapture; @@ -242,6 +243,18 @@ void CL_Voip_f( void ) } +static +void CL_VoipNewGeneration(void) +{ + // don't have a zero generation so new clients won't match, and don't + // wrap to negative so MSG_ReadLong() doesn't "fail." + clc.voipOutgoingGeneration++; + if (clc.voipOutgoingGeneration <= 0) + clc.voipOutgoingGeneration = 1; + clc.voipPower = 0.0f; + clc.voipOutgoingSequence = 0; +} + /* =============== CL_CaptureVoip @@ -253,6 +266,7 @@ Record more audio from the hardware if required and encode it into Speex static void CL_CaptureVoip(void) { + const qboolean useVad = (cl_voipUseVAD->integer != 0); qboolean initialFrame = qfalse; qboolean finalFrame = qfalse; @@ -268,6 +282,17 @@ void CL_CaptureVoip(void) if (clc.voipOutgoingDataSize > 0) return; // packet is pending transmission, don't record more yet. + if (cl_voipUseVAD->modified) { + int useVadi = (int) useVad; + speex_preprocess_ctl(clc.speexPreprocessor, + SPEEX_PREPROCESS_SET_VAD, &useVadi); + cl_voipUseVAD->modified = qfalse; + Cvar_Set("cl_voipSend", (useVad) ? "1" : "0"); + } + + if ((useVad) && (!cl_voipSend->integer)) + Cvar_Set("cl_voipSend", "1"); // lots of things reset this. + if (cl_voipSend->modified) { qboolean dontCapture = qfalse; if (cls.state != CA_ACTIVE) @@ -302,11 +327,7 @@ void CL_CaptureVoip(void) if (gain < 0.0f) gain = 0.0f; else if (gain >= 1.0f) gain = 1.0f; S_MasterGain(cl_voipGainDuringCapture->value); S_StartCapture(); - clc.voipPower = 0.0f; - clc.voipOutgoingSequence = 0; - clc.voipOutgoingGeneration++; - if (clc.voipOutgoingGeneration == 0) // don't have a zero generation... - clc.voipOutgoingGeneration = 1; // ...so new clients won't match. + CL_VoipNewGeneration(); } if ((cl_voipSend->integer) || (finalFrame)) { // user wants to capture audio? @@ -318,6 +339,7 @@ void CL_CaptureVoip(void) // 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]; + qboolean isVoice = qfalse; int16_t voipPower = 0; int speexFrames = 0; int wpos = 0; @@ -334,22 +356,25 @@ void CL_CaptureVoip(void) // this will probably generate multiple speex packets each time. while (samples > 0) { + int16_t *sampptr = &sampbuffer[pos]; int i, bytes; - // Check the "power" of this packet... + // check the "power" of this packet... for (i = 0; i < clc.speexFrameSize; i++) { - int16_t s = sampbuffer[i+pos]; + int16_t s = sampptr[i]; if (s < 0) s = -s; if (s > voipPower) voipPower = s; // !!! FIXME: this isn't very clever. } - speex_preprocess_run(clc.speexPreprocessor, &sampbuffer[pos]); + // preprocess samples to remove noise, check for voice... + if (speex_preprocess_run(clc.speexPreprocessor, sampptr)) + isVoice = qtrue; // player is probably speaking. - // Encode raw audio samples into Speex data... + // encode raw audio samples into Speex data... speex_bits_reset(&clc.speexEncoderBits); - speex_encode_int(clc.speexEncoder, &sampbuffer[pos], + speex_encode_int(clc.speexEncoder, sampptr, &clc.speexEncoderBits); bytes = speex_bits_write(&clc.speexEncoderBits, (char *) &clc.voipOutgoingData[wpos+1], @@ -363,21 +388,26 @@ void CL_CaptureVoip(void) samples -= clc.speexFrameSize; speexFrames++; } - clc.voipPower = ((float) voipPower) / 32767.0f; - clc.voipOutgoingDataSize = wpos; - clc.voipOutgoingDataFrames = speexFrames; - Com_DPrintf("Outgoing VoIP data: %d frames, %d bytes, %f power\n", - speexFrames, wpos, clc.voipPower); - - #if 0 - static FILE *encio = NULL; - if (encio == NULL) encio = fopen("outgoing-encoded.bin", "wb"); - if (encio != NULL) { fwrite(clc.voipOutgoingData, wpos, 1, encio); fflush(encio); } - static FILE *decio = NULL; - if (decio == NULL) decio = fopen("outgoing-decoded.bin", "wb"); - if (decio != NULL) { fwrite(sampbuffer, speexFrames * clc.speexFrameSize * 2, 1, decio); fflush(decio); } - #endif + if ((useVad) && (!isVoice)) { + CL_VoipNewGeneration(); // no talk for at least 1/4 second. + } else { + clc.voipPower = ((float) voipPower) / 32767.0f; + clc.voipOutgoingDataSize = wpos; + clc.voipOutgoingDataFrames = speexFrames; + + Com_DPrintf("VoIP: Send %d frames, %d bytes, %f power\n", + speexFrames, wpos, 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); } + 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); } + #endif + } } } @@ -3057,6 +3087,7 @@ void CL_Init( void ) { cl_voipSend = Cvar_Get ("cl_voipSend", "0", 0); cl_voipSendTarget = Cvar_Get ("cl_voipSendTarget", "all", 0); cl_voipGainDuringCapture = Cvar_Get ("cl_voipGainDuringCapture", "0.2", CVAR_ARCHIVE); + cl_voipUseVAD = Cvar_Get ("cl_voipUseVAD", "0", CVAR_ARCHIVE); voip = Cvar_Get ("voip", "1", CVAR_USERINFO | CVAR_ARCHIVE | CVAR_LATCH); // This is a protocol version number. diff --git a/code/client/client.h b/code/client/client.h index 3df4874..1514582 100644 --- a/code/client/client.h +++ b/code/client/client.h @@ -411,6 +411,7 @@ extern cvar_t *cl_mumbleScale; // cl_voipSendTarget is a string: "all" to broadcast to everyone, "none" to // send to no one, or a comma-separated list of client numbers: // "0,7,2,23" ... an empty string is treated like "all". +extern cvar_t *cl_voipUseVAD; extern cvar_t *cl_voipSend; extern cvar_t *cl_voipSendTarget; extern cvar_t *cl_voipGainDuringCapture; -- cgit v1.2.3