aboutsummaryrefslogtreecommitdiffstats
path: root/code/client/cl_main.c
diff options
context:
space:
mode:
authoricculus <icculus@edf5b092-35ff-0310-97b2-ce42778d08ea>2008-06-01 07:51:23 +0000
committericculus <icculus@edf5b092-35ff-0310-97b2-ce42778d08ea>2008-06-01 07:51:23 +0000
commit10afaeef337e0d19b37cb7dbe3fccee48bf18a43 (patch)
tree365da78a39bf90a8321da9f9bbf554ff5733eae6 /code/client/cl_main.c
parent94017ca419caff91bf26841b503b5867d992ca4c (diff)
downloadioquake3-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.c235
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 );