diff options
author | icculus <icculus@edf5b092-35ff-0310-97b2-ce42778d08ea> | 2008-06-01 07:51:23 +0000 |
---|---|---|
committer | icculus <icculus@edf5b092-35ff-0310-97b2-ce42778d08ea> | 2008-06-01 07:51:23 +0000 |
commit | 10afaeef337e0d19b37cb7dbe3fccee48bf18a43 (patch) | |
tree | 365da78a39bf90a8321da9f9bbf554ff5733eae6 /code/client/cl_main.c | |
parent | 94017ca419caff91bf26841b503b5867d992ca4c (diff) | |
download | ioquake3-aero-10afaeef337e0d19b37cb7dbe3fccee48bf18a43.tar.gz ioquake3-aero-10afaeef337e0d19b37cb7dbe3fccee48bf18a43.zip |
Initial patch for in-game VoIP support!
git-svn-id: svn://svn.icculus.org/quake3/trunk@1348 edf5b092-35ff-0310-97b2-ce42778d08ea
Diffstat (limited to 'code/client/cl_main.c')
-rw-r--r-- | code/client/cl_main.c | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/code/client/cl_main.c b/code/client/cl_main.c index b9243d5..22c89c6 100644 --- a/code/client/cl_main.c +++ b/code/client/cl_main.c @@ -33,6 +33,12 @@ cvar_t *cl_useMumble; cvar_t *cl_mumbleScale; #endif +#if USE_VOIP +cvar_t *cl_voipSend; +cvar_t *cl_voipGainDuringCapture; +cvar_t *voip; +#endif + cvar_t *cl_nodelta; cvar_t *cl_debugMove; @@ -168,6 +174,200 @@ void CL_UpdateMumble(void) #endif +#if USE_VOIP +static +void CL_UpdateVoipIgnore(const char *idstr, qboolean ignore) +{ + if ((*idstr >= '0') && (*idstr <= '9')) { + const int id = atoi(idstr); + if ((id >= 0) && (id < MAX_CLIENTS)) { + clc.voipIgnore[id] = ignore; + CL_AddReliableCommand(va("voip %s %d", + ignore ? "ignore" : "unignore", id)); + Com_Printf("VoIP: %s ignoring player #%d\n", + ignore ? "Now" : "No longer", id); + } + } +} + +void CL_Voip_f( void ) +{ + const char *cmd = Cmd_Argv(1); + const char *reason = NULL; + + if (cls.state != CA_ACTIVE) + reason = "Not connected to a server"; + else if (!clc.speexInitialized) + reason = "Speex not initialized"; + else if (!cl_connectedToVoipServer) + reason = "Server doesn't support VoIP"; + else if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")) + reason = "running in single-player mode"; + + if (reason != NULL) { + Com_Printf("VoIP: command ignored: %s\n", reason); + return; + } + + if (strcmp(cmd, "ignore") == 0) { + CL_UpdateVoipIgnore(Cmd_Argv(2), qtrue); + } else if (strcmp(cmd, "unignore") == 0) { + CL_UpdateVoipIgnore(Cmd_Argv(2), qfalse); + } else if (strcmp(cmd, "muteall") == 0) { + Com_Printf("VoIP: muting incoming voice\n"); + CL_AddReliableCommand("voip muteall"); + clc.voipMuteAll = qtrue; + } else if (strcmp(cmd, "unmuteall") == 0) { + Com_Printf("VoIP: unmuting incoming voice\n"); + CL_AddReliableCommand("voip unmuteall"); + clc.voipMuteAll = qfalse; + } +} + + +/* +=============== +CL_CaptureVoip + +Record more audio from the hardware if required and encode it into Speex + data for later transmission. +=============== +*/ +static +void CL_CaptureVoip(void) +{ + qboolean initialFrame = qfalse; + qboolean finalFrame = qfalse; + +#if USE_MUMBLE + // if we're using Mumble, don't try to handle VoIP transmission ourselves. + if (cl_useMumble->integer) + return; +#endif + + if (!clc.speexInitialized) + return; // just in case this gets called at a bad time. + + if (clc.voipOutgoingDataSize > 0) + return; // packet is pending transmission, don't record more yet. + + if (cl_voipSend->modified) { + qboolean dontCapture = qfalse; + if (cls.state != CA_ACTIVE) + dontCapture = qtrue; // not connected to a server. + else if (!cl_connectedToVoipServer) + dontCapture = qtrue; // server doesn't support VoIP. + else if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")) + dontCapture = qtrue; // single player game. + + cl_voipSend->modified = qfalse; + + if (dontCapture) { + cl_voipSend->integer = 0; + return; + } + + if (cl_voipSend->integer) { + initialFrame = qtrue; + } else { + finalFrame = qtrue; + } + } + + // try to get more audio data from the sound card... + + if (initialFrame) { + float gain = cl_voipGainDuringCapture->value; + 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. + } + + if ((cl_voipSend->integer) || (finalFrame)) { // user wants to capture audio? + // !!! FIXME: 8000, MONO16, 4096 samples are hardcoded in snd_openal.c + int samples = S_AvailableCaptureSamples(); + const int mult = (finalFrame) ? 1 : 12; // 12 == 240ms of audio. + + // 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!). + static int16_t sampbuffer[4096]; // !!! FIXME: don't hardcode. + int16_t voipPower = 0; + int speexFrames = 0; + int wpos = 0; + int pos = 0; + + if (samples > (clc.speexFrameSize * 12)) + samples = (clc.speexFrameSize * 12); + + // !!! 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) { + int i, bytes; + + // Check the "power" of this packet... + for (i = 0; i < clc.speexFrameSize; i++) { + int16_t s = sampbuffer[i+pos]; + if (s < 0) + s = -s; + if (s > voipPower) + voipPower = s; // !!! FIXME: this isn't very clever. + } + + // Encode raw audio samples into Speex data... + speex_bits_reset(&clc.speexEncoderBits); + speex_encode_int(clc.speexEncoder, &sampbuffer[pos], + &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++; + } + 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 + } + } + + // User requested we stop recording, and we've now processed the last of + // any previously-buffered data. Pause the capture device, etc. + if (finalFrame) { + S_StopCapture(); + S_MasterGain(1.0f); + clc.voipPower = 0.0f; // force this value so it doesn't linger. + } +} +#endif + /* ======================================================================= @@ -905,6 +1105,25 @@ void CL_Disconnect( qboolean showMainMenu ) { } #endif +#if USE_VOIP + if (cl_voipSend->integer) { + Cvar_Set("cl_voipSend", "0"); + CL_CaptureVoip(); // clean up any state... + } + + if (clc.speexInitialized) { + int i; + speex_bits_destroy(&clc.speexEncoderBits); + speex_encoder_destroy(clc.speexEncoder); + for (i = 0; i < MAX_CLIENTS; i++) { + speex_bits_destroy(&clc.speexDecoderBits[i]); + speex_decoder_destroy(clc.speexDecoder[i]); + } + } + + Cmd_RemoveCommand ("voip"); +#endif + if ( clc.demofile ) { FS_FCloseFile( clc.demofile ); clc.demofile = 0; @@ -939,6 +1158,11 @@ void CL_Disconnect( qboolean showMainMenu ) { // not connected to a pure server anymore cl_connectedToPureServer = qfalse; +#if USE_VOIP + // not connected to voip server anymore. + cl_connectedToVoipServer = qfalse; +#endif + // Stop recording any video if( CL_VideoRecording( ) ) { // Finish rendering current frame @@ -2359,9 +2583,14 @@ void CL_Frame ( int msec ) { // update audio S_Update(); +#if USE_VOIP + CL_CaptureVoip(); +#endif + #ifdef USE_MUMBLE CL_UpdateMumble(); #endif + // advance local effects for next frame SCR_RunCinematic(); @@ -2781,6 +3010,12 @@ void CL_Init( void ) { cl_mumbleScale = Cvar_Get ("cl_mumbleScale", "0.0254", CVAR_ARCHIVE); #endif +#if USE_VOIP + cl_voipSend = Cvar_Get ("cl_voipSend", "0", 0); + cl_voipGainDuringCapture = Cvar_Get ("cl_voipGainDuringCapture", "0.2", CVAR_ARCHIVE); + voip = Cvar_Get ("voip", "0", CVAR_USERINFO | CVAR_ARCHIVE); +#endif + // userinfo Cvar_Get ("name", "UnnamedPlayer", CVAR_USERINFO | CVAR_ARCHIVE ); Cvar_Get ("rate", "3000", CVAR_USERINFO | CVAR_ARCHIVE ); |