summaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/cl_cgame.c47
-rw-r--r--src/client/cl_cin.c10
-rw-r--r--src/client/cl_input.c108
-rw-r--r--src/client/cl_keys.c2
-rw-r--r--src/client/cl_main.c754
-rw-r--r--src/client/cl_parse.c196
-rw-r--r--src/client/cl_scrn.c77
-rw-r--r--src/client/cl_ui.c90
-rw-r--r--src/client/client.h81
-rw-r--r--src/client/libmumblelink.c134
-rw-r--r--src/client/libmumblelink.h26
-rw-r--r--src/client/qal.c15
-rw-r--r--src/client/snd_dma.c126
-rw-r--r--src/client/snd_local.h15
-rw-r--r--src/client/snd_main.c76
-rw-r--r--src/client/snd_mix.c38
-rw-r--r--src/client/snd_openal.c262
-rw-r--r--src/client/snd_public.h12
18 files changed, 1659 insertions, 410 deletions
diff --git a/src/client/cl_cgame.c b/src/client/cl_cgame.c
index 2e28d049..6e436316 100644
--- a/src/client/cl_cgame.c
+++ b/src/client/cl_cgame.c
@@ -24,6 +24,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
// cl_cgame.c -- client system interaction with client game
#include "client.h"
+#include "libmumblelink.h"
extern qboolean loadCamera(const char *name);
extern void startCamera(int time);
@@ -955,6 +956,52 @@ void CL_FirstSnapshot( void ) {
Cbuf_AddText( cl_activeAction->string );
Cvar_Set( "activeAction", "" );
}
+
+#ifdef USE_MUMBLE
+ if ((cl_useMumble->integer) && !mumble_islinked()) {
+ int ret = mumble_link(CLIENT_WINDOW_TITLE);
+ Com_Printf("Mumble: Linking to Mumble application %s\n", ret==0?"ok":"failed");
+ }
+#endif
+
+#ifdef USE_VOIP
+ if (!clc.speexInitialized) {
+ int i;
+ speex_bits_init(&clc.speexEncoderBits);
+ speex_bits_reset(&clc.speexEncoderBits);
+
+ clc.speexEncoder = speex_encoder_init(&speex_nb_mode);
+
+ 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);
+
+ 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.voipIgnore[i] = qfalse;
+ clc.voipGain[i] = 1.0f;
+ }
+ clc.speexInitialized = qtrue;
+ clc.voipMuteAll = qfalse;
+ Cmd_AddCommand ("voip", CL_Voip_f);
+ Cvar_Set("cl_voipSendTarget", "all");
+ clc.voipTarget1 = clc.voipTarget2 = clc.voipTarget3 = 0x7FFFFFFF;
+ }
+#endif
}
/*
diff --git a/src/client/cl_cin.c b/src/client/cl_cin.c
index 32325cdf..bf8501f1 100644
--- a/src/client/cl_cin.c
+++ b/src/client/cl_cin.c
@@ -54,8 +54,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define MAX_VIDEO_HANDLES 16
extern glconfig_t glConfig;
-extern int s_paintedtime;
-extern int s_rawend;
static void RoQ_init( void );
@@ -1142,17 +1140,17 @@ redump:
case ZA_SOUND_MONO:
if (!cinTable[currentHandle].silent) {
ssize = RllDecodeMonoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags);
- S_RawSamples( ssize, 22050, 2, 1, (byte *)sbuf, 1.0f );
+ S_RawSamples( 0, ssize, 22050, 2, 1, (byte *)sbuf, 1.0f );
}
break;
case ZA_SOUND_STEREO:
if (!cinTable[currentHandle].silent) {
if (cinTable[currentHandle].numQuads == -1) {
S_Update();
- s_rawend = s_soundtime;
+ s_rawend[0] = s_soundtime;
}
ssize = RllDecodeStereoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags);
- S_RawSamples( ssize, 22050, 2, 2, (byte *)sbuf, 1.0f );
+ S_RawSamples( 0, ssize, 22050, 2, 2, (byte *)sbuf, 1.0f );
}
break;
case ROQ_QUAD_INFO:
@@ -1468,7 +1466,7 @@ int CIN_PlayCinematic( const char *arg, int x, int y, int w, int h, int systemBi
Con_Close();
- s_rawend = s_soundtime;
+ s_rawend[0] = s_soundtime;
return currentHandle;
}
diff --git a/src/client/cl_input.c b/src/client/cl_input.c
index e5c87f14..30352aa3 100644
--- a/src/client/cl_input.c
+++ b/src/client/cl_input.c
@@ -53,6 +53,10 @@ kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright;
kbutton_t in_strafe, in_speed;
kbutton_t in_up, in_down;
+#ifdef USE_VOIP
+kbutton_t in_voiprecord;
+#endif
+
kbutton_t in_buttons[16];
@@ -217,6 +221,20 @@ void IN_SpeedUp(void) {IN_KeyUp(&in_speed);}
void IN_StrafeDown(void) {IN_KeyDown(&in_strafe);}
void IN_StrafeUp(void) {IN_KeyUp(&in_strafe);}
+#ifdef USE_VOIP
+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]);}
void IN_Button0Up(void) {IN_KeyUp(&in_buttons[0]);}
void IN_Button1Down(void) {IN_KeyDown(&in_buttons[1]);}
@@ -741,6 +759,91 @@ void CL_WritePacket( void ) {
count = MAX_PACKET_USERCMDS;
Com_Printf("MAX_PACKET_USERCMDS\n");
}
+
+#ifdef USE_VOIP
+ if (clc.voipOutgoingDataSize > 0) { // only send if data.
+ // Move cl_voipSendTarget from a string to the bitmasks if needed.
+ if (cl_voipSendTarget->modified) {
+ char buffer[32];
+ const char *target = cl_voipSendTarget->string;
+
+ if (Q_stricmp(target, "attacker") == 0) {
+ int player = VM_Call( cgvm, CG_LAST_ATTACKER );
+ Com_sprintf(buffer, sizeof (buffer), "%d", player);
+ target = buffer;
+ } else if (Q_stricmp(target, "crosshair") == 0) {
+ int player = VM_Call( cgvm, CG_CROSSHAIR_PLAYER );
+ Com_sprintf(buffer, sizeof (buffer), "%d", player);
+ target = buffer;
+ }
+
+ if ((*target == '\0') || (Q_stricmp(target, "all") == 0)) {
+ const int all = 0x7FFFFFFF;
+ clc.voipTarget1 = clc.voipTarget2 = clc.voipTarget3 = all;
+ } else if (Q_stricmp(target, "none") == 0) {
+ clc.voipTarget1 = clc.voipTarget2 = clc.voipTarget3 = 0;
+ } else {
+ clc.voipTarget1 = clc.voipTarget2 = clc.voipTarget3 = 0;
+ const char *ptr = target;
+ do {
+ if ((*ptr == ',') || (*ptr == '\0')) {
+ const int val = atoi(target);
+ target = ptr + 1;
+ if ((val >= 0) && (val < 31)) {
+ clc.voipTarget1 |= (1 << (val-0));
+ } else if ((val >= 31) && (val < 62)) {
+ clc.voipTarget2 |= (1 << (val-31));
+ } else if ((val >= 62) && (val < 93)) {
+ clc.voipTarget3 |= (1 << (val-62));
+ }
+ }
+ } while (*(ptr++));
+ }
+ cl_voipSendTarget->modified = qfalse;
+ }
+
+ MSG_WriteByte (&buf, clc_EOF); // placate legacy servers.
+ MSG_WriteByte (&buf, clc_extension);
+ MSG_WriteByte (&buf, clc_voip);
+ MSG_WriteByte (&buf, clc.voipOutgoingGeneration);
+ MSG_WriteLong (&buf, clc.voipOutgoingSequence);
+ MSG_WriteByte (&buf, clc.voipOutgoingDataFrames);
+ MSG_WriteLong (&buf, clc.voipTarget1);
+ MSG_WriteLong (&buf, clc.voipTarget2);
+ MSG_WriteLong (&buf, clc.voipTarget3);
+ MSG_WriteShort (&buf, clc.voipOutgoingDataSize);
+ MSG_WriteData (&buf, clc.voipOutgoingData, clc.voipOutgoingDataSize);
+
+ // If we're recording a demo, we have to fake a server packet with
+ // this VoIP data so it gets to disk; the server doesn't send it
+ // back to us, and we might as well eliminate concerns about dropped
+ // and misordered packets here.
+ if ( clc.demorecording && !clc.demowaiting ) {
+ const int voipSize = clc.voipOutgoingDataSize;
+ msg_t fakemsg;
+ byte fakedata[MAX_MSGLEN];
+ MSG_Init (&fakemsg, fakedata, sizeof (fakedata));
+ MSG_Bitstream (&fakemsg);
+ MSG_WriteLong (&fakemsg, clc.reliableAcknowledge);
+ MSG_WriteByte (&fakemsg, svc_EOF);
+ MSG_WriteByte (&fakemsg, svc_extension);
+ MSG_WriteByte (&fakemsg, svc_voip);
+ MSG_WriteShort (&fakemsg, clc.clientNum);
+ MSG_WriteByte (&fakemsg, clc.voipOutgoingGeneration);
+ MSG_WriteLong (&fakemsg, clc.voipOutgoingSequence);
+ MSG_WriteByte (&fakemsg, clc.voipOutgoingDataFrames);
+ MSG_WriteShort (&fakemsg, clc.voipOutgoingDataSize );
+ MSG_WriteData (&fakemsg, clc.voipOutgoingData, voipSize);
+ MSG_WriteByte (&fakemsg, svc_EOF);
+ CL_WriteDemoMessage (&fakemsg, 0);
+ }
+
+ clc.voipOutgoingSequence += clc.voipOutgoingDataFrames;
+ clc.voipOutgoingDataSize = 0;
+ clc.voipOutgoingDataFrames = 0;
+ } else
+#endif
+
if ( count >= 1 ) {
if ( cl_showSend->integer ) {
Com_Printf( "(%i)", count );
@@ -898,6 +1001,11 @@ void CL_InitInput( void ) {
Cmd_AddCommand ("+mlook", IN_MLookDown);
Cmd_AddCommand ("-mlook", IN_MLookUp);
+#ifdef USE_VOIP
+ Cmd_AddCommand ("+voiprecord", IN_VoipRecordDown);
+ Cmd_AddCommand ("-voiprecord", IN_VoipRecordUp);
+#endif
+
cl_nodelta = Cvar_Get ("cl_nodelta", "0", 0);
cl_debugMove = Cvar_Get ("cl_debugMove", "0", 0);
}
diff --git a/src/client/cl_keys.c b/src/client/cl_keys.c
index d9fc9946..7c982c4b 100644
--- a/src/client/cl_keys.c
+++ b/src/client/cl_keys.c
@@ -1116,7 +1116,7 @@ void CL_KeyEvent (int key, qboolean down, unsigned time) {
{
if (keys[K_ALT].down)
{
- Key_ClearStates();
+// Key_ClearStates(); // Thilo: why should we clear the key states here?
Cvar_SetValue( "r_fullscreen",
!Cvar_VariableIntegerValue( "r_fullscreen" ) );
return;
diff --git a/src/client/cl_main.c b/src/client/cl_main.c
index 326bc5f6..444b877d 100644
--- a/src/client/cl_main.c
+++ b/src/client/cl_main.c
@@ -25,6 +25,26 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "client.h"
#include <limits.h>
+#ifdef USE_MUMBLE
+#include "libmumblelink.h"
+#endif
+
+#ifdef USE_MUMBLE
+cvar_t *cl_useMumble;
+cvar_t *cl_mumbleScale;
+#endif
+
+#ifdef USE_VOIP
+cvar_t *cl_voipUseVAD;
+cvar_t *cl_voipVADThreshold;
+cvar_t *cl_voipSend;
+cvar_t *cl_voipSendTarget;
+cvar_t *cl_voipGainDuringCapture;
+cvar_t *cl_voipCaptureMult;
+cvar_t *cl_voipShowMeter;
+cvar_t *cl_voip;
+#endif
+
cvar_t *cl_nodelta;
cvar_t *cl_debugMove;
@@ -37,7 +57,6 @@ cvar_t *rconAddress;
cvar_t *cl_timeout;
cvar_t *cl_maxpackets;
cvar_t *cl_packetdup;
-cvar_t *cl_master;
cvar_t *cl_timeNudge;
cvar_t *cl_showTimeDelta;
cvar_t *cl_freezeDemo;
@@ -123,6 +142,284 @@ void CL_CDDialog( void ) {
cls.cddialog = qtrue; // start it next frame
}
+#ifdef USE_MUMBLE
+static
+void CL_UpdateMumble(void)
+{
+ vec3_t pos, forward, up;
+ float scale = cl_mumbleScale->value;
+ float tmp;
+
+ if(!cl_useMumble->integer)
+ return;
+
+ // !!! FIXME: not sure if this is even close to correct.
+ AngleVectors( cl.snap.ps.viewangles, forward, NULL, up);
+
+ pos[0] = cl.snap.ps.origin[0] * scale;
+ pos[1] = cl.snap.ps.origin[2] * scale;
+ pos[2] = cl.snap.ps.origin[1] * scale;
+
+ tmp = forward[1];
+ forward[1] = forward[2];
+ forward[2] = tmp;
+
+ tmp = up[1];
+ up[1] = up[2];
+ up[2] = tmp;
+
+ if(cl_useMumble->integer > 1) {
+ fprintf(stderr, "%f %f %f, %f %f %f, %f %f %f\n",
+ pos[0], pos[1], pos[2],
+ forward[0], forward[1], forward[2],
+ up[0], up[1], up[2]);
+ }
+
+ mumble_update_coordinates(pos, forward, up);
+}
+#endif
+
+
+#ifdef 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);
+ }
+ }
+}
+
+static
+void CL_UpdateVoipGain(const char *idstr, float gain)
+{
+ if ((*idstr >= '0') && (*idstr <= '9')) {
+ const int id = atoi(idstr);
+ if (gain < 0.0f)
+ gain = 0.0f;
+ if ((id >= 0) && (id < MAX_CLIENTS)) {
+ clc.voipGain[id] = gain;
+ Com_Printf("VoIP: player #%d gain now set to %f\n", id, gain);
+ }
+ }
+}
+
+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";
+
+ 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, "gain") == 0) {
+ CL_UpdateVoipGain(Cmd_Argv(2), atof(Cmd_Argv(3)));
+ } 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;
+ }
+}
+
+
+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
+
+Record more audio from the hardware if required and encode it into Speex
+ data for later transmission.
+===============
+*/
+static
+void CL_CaptureVoip(void)
+{
+ const float audioMult = cl_voipCaptureMult->value;
+ const qboolean useVad = (cl_voipUseVAD->integer != 0);
+ 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_voipUseVAD->modified) {
+ Cvar_Set("cl_voipSend", (useVad) ? "1" : "0");
+ cl_voipUseVAD->modified = qfalse;
+ }
+
+ 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)
+ dontCapture = qtrue; // not connected to a server.
+ else if (!cl_connectedToVoipServer)
+ dontCapture = qtrue; // server doesn't support VoIP.
+ else if (clc.demoplaying)
+ dontCapture = qtrue; // playing back a demo.
+ else if ( cl_voip->integer == 0 )
+ dontCapture = qtrue; // client has VoIP support disabled.
+ else if ( audioMult == 0.0f )
+ dontCapture = qtrue; // basically silenced incoming audio.
+
+ 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();
+ CL_VoipNewGeneration();
+ }
+
+ if ((cl_voipSend->integer) || (finalFrame)) { // user wants to capture audio?
+ 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!).
+ // 2048 will cover 12 uncompressed frames in narrowband mode.
+ static int16_t sampbuffer[2048];
+ float voipPower = 0.0f;
+ 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) {
+ int16_t *sampptr = &sampbuffer[pos];
+ int i, bytes;
+
+ // preprocess samples to remove noise...
+ speex_preprocess_run(clc.speexPreprocessor, sampptr);
+
+ // 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);
+ }
+
+ // 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++;
+ }
+
+ clc.voipPower = (voipPower / (32768.0f * 32768.0f *
+ ((float) (clc.speexFrameSize * speexFrames)))) *
+ 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;
+
+ 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
+ }
+ }
+ }
+
+ // 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
/*
=======================================================================
@@ -825,7 +1122,7 @@ void CL_MapLoading( void ) {
Key_SetCatcher( 0 );
SCR_UpdateScreen();
clc.connectTime = -RETRANSMIT_TIMEOUT;
- NET_StringToAdr( cls.servername, &clc.serverAddress);
+ NET_StringToAdr( cls.servername, &clc.serverAddress, NA_UNSPEC);
// we don't need a challenge on the localhost
CL_CheckForResend();
@@ -853,7 +1150,7 @@ CL_UpdateGUID
update cl_guid using QKEY_FILE and optional prefix
====================
*/
-static void CL_UpdateGUID( char *prefix, int prefix_len )
+static void CL_UpdateGUID( const char *prefix, int prefix_len )
{
fileHandle_t f;
int len;
@@ -898,6 +1195,36 @@ void CL_Disconnect( qboolean showMainMenu ) {
*clc.downloadTempName = *clc.downloadName = 0;
Cvar_Set( "cl_downloadName", "" );
+#ifdef USE_MUMBLE
+ if (cl_useMumble->integer && mumble_islinked()) {
+ Com_Printf("Mumble: Unlinking from Mumble application\n");
+ mumble_unlink();
+ }
+#endif
+
+#ifdef USE_VOIP
+ if (cl_voipSend->integer) {
+ int tmp = cl_voipUseVAD->integer;
+ cl_voipUseVAD->integer = 0; // disable this for a moment.
+ clc.voipOutgoingDataSize = 0; // dump any pending VoIP transmission.
+ Cvar_Set("cl_voipSend", "0");
+ CL_CaptureVoip(); // clean up any state...
+ cl_voipUseVAD->integer = tmp;
+ }
+
+ if (clc.speexInitialized) {
+ int i;
+ speex_bits_destroy(&clc.speexEncoderBits);
+ speex_encoder_destroy(clc.speexEncoder);
+ speex_preprocess_state_destroy(clc.speexPreprocessor);
+ 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;
@@ -932,6 +1259,11 @@ void CL_Disconnect( qboolean showMainMenu ) {
// not connected to a pure server anymore
cl_connectedToPureServer = qfalse;
+#ifdef USE_VOIP
+ // not connected to voip server anymore.
+ cl_connectedToVoipServer = qfalse;
+#endif
+
// Stop recording any video
if( CL_VideoRecording( ) ) {
// Finish rendering current frame
@@ -986,7 +1318,7 @@ void CL_RequestMotd( void ) {
return;
}
Com_Printf( "Resolving %s\n", MASTER_SERVER_NAME );
- if ( !NET_StringToAdr( MASTER_SERVER_NAME, &cls.updateServer ) ) {
+ if ( !NET_StringToAdr( MASTER_SERVER_NAME, &cls.updateServer, NA_IP ) ) {
Com_Printf( "Couldn't resolve address\n" );
return;
}
@@ -1109,12 +1441,28 @@ CL_Connect_f
*/
void CL_Connect_f( void ) {
char *server;
- char serverString[ 22 ];
+ const char *serverString;
+ int argc = Cmd_Argc();
+ netadrtype_t family = NA_UNSPEC;
- if ( Cmd_Argc() != 2 ) {
- Com_Printf( "usage: connect [server]\n");
+ if ( argc != 2 && argc != 3 ) {
+ Com_Printf( "usage: connect [-4|-6] server\n");
return;
}
+
+ if(argc == 2)
+ server = Cmd_Argv(1);
+ else
+ {
+ if(!strcmp(Cmd_Argv(1), "-4"))
+ family = NA_IP;
+ else if(!strcmp(Cmd_Argv(1), "-6"))
+ family = NA_IP6;
+ else
+ Com_Printf( "warning: only -4 or -6 as address type understood.\n");
+
+ server = Cmd_Argv(2);
+ }
Cvar_Set("ui_singlePlayerActive", "0");
@@ -1124,8 +1472,6 @@ void CL_Connect_f( void ) {
// clear any previous "server full" type messages
clc.serverMessage[0] = 0;
- server = Cmd_Argv (1);
-
if ( com_sv_running->integer && !strcmp( server, "localhost" ) ) {
// if running a local server, kill it
SV_Shutdown( "Server quit" );
@@ -1138,13 +1484,9 @@ void CL_Connect_f( void ) {
CL_Disconnect( qtrue );
Con_Close();
- /* MrE: 2000-09-13: now called in CL_DownloadsComplete
- CL_FlushMemory( );
- */
-
Q_strncpyz( cls.servername, server, sizeof(cls.servername) );
- if (!NET_StringToAdr( cls.servername, &clc.serverAddress) ) {
+ if (!NET_StringToAdr(cls.servername, &clc.serverAddress, family) ) {
Com_Printf ("Bad server address\n");
cls.state = CA_DISCONNECTED;
return;
@@ -1152,12 +1494,10 @@ void CL_Connect_f( void ) {
if (clc.serverAddress.port == 0) {
clc.serverAddress.port = BigShort( PORT_SERVER );
}
- Com_sprintf( serverString, sizeof( serverString ), "%i.%i.%i.%i:%i",
- clc.serverAddress.ip[0], clc.serverAddress.ip[1],
- clc.serverAddress.ip[2], clc.serverAddress.ip[3],
- BigShort( clc.serverAddress.port ) );
-
- Com_Printf( "%s resolved to %s\n", cls.servername, serverString );
+
+ serverString = NET_AdrToStringwPort(clc.serverAddress);
+
+ Com_Printf( "%s resolved to %s\n", cls.servername, serverString);
if( cl_guidServerUniq->integer )
CL_UpdateGUID( serverString, strlen( serverString ) );
@@ -1224,7 +1564,7 @@ void CL_Rcon_f( void ) {
return;
}
- NET_StringToAdr (rconAddress->string, &to);
+ NET_StringToAdr (rconAddress->string, &to, NA_UNSPEC);
if (to.port == 0) {
to.port = BigShort (PORT_SERVER);
}
@@ -1239,21 +1579,11 @@ CL_SendPureChecksums
=================
*/
void CL_SendPureChecksums( void ) {
- const char *pChecksums;
char cMsg[MAX_INFO_VALUE];
- int i;
// if we are pure we need to send back a command with our referenced pk3 checksums
- pChecksums = FS_ReferencedPakPureChecksums();
+ Com_sprintf(cMsg, sizeof(cMsg), "cp %d %s", cl.serverId, FS_ReferencedPakPureChecksums());
- // "cp"
- // "Yf"
- Com_sprintf(cMsg, sizeof(cMsg), "Yf ");
- Q_strcat(cMsg, sizeof(cMsg), va("%d ", cl.serverId) );
- Q_strcat(cMsg, sizeof(cMsg), pChecksums);
- for (i = 0; i < 2; i++) {
- cMsg[i] += 10;
- }
CL_AddReliableCommand( cMsg );
}
@@ -1774,13 +2104,8 @@ void CL_MotdPacket( netadr_t from, const char *info ) {
CL_InitServerInfo
===================
*/
-void CL_InitServerInfo( serverInfo_t *server, serverAddress_t *address ) {
- server->adr.type = NA_IP;
- server->adr.ip[0] = address->ip[0];
- server->adr.ip[1] = address->ip[1];
- server->adr.ip[2] = address->ip[2];
- server->adr.ip[3] = address->ip[3];
- server->adr.port = address->port;
+void CL_InitServerInfo( serverInfo_t *server, netadr_t *address ) {
+ server->adr = *address;
server->clients = 0;
server->hostName[0] = '\0';
server->mapName[0] = '\0';
@@ -1801,8 +2126,8 @@ CL_ServersResponsePacket
===================
*/
void CL_ServersResponsePacket( netadr_t from, msg_t *msg ) {
- int i, count, max, total;
- serverAddress_t addresses[MAX_SERVERSPERPACKET];
+ int i, count, total;
+ netadr_t addresses[MAX_SERVERSPERPACKET];
int numservers;
byte* buffptr;
byte* buffend;
@@ -1815,71 +2140,66 @@ void CL_ServersResponsePacket( netadr_t from, msg_t *msg ) {
cls.numGlobalServerAddresses = 0;
}
- if (cls.nummplayerservers == -1) {
- cls.nummplayerservers = 0;
- }
-
// parse through server response string
numservers = 0;
buffptr = msg->data;
buffend = buffptr + msg->cursize;
- while (buffptr+1 < buffend) {
- // advance to initial token
- do {
- if (*buffptr++ == '\\')
- break;
- }
- while (buffptr < buffend);
- if ( buffptr >= buffend - 6 ) {
+ // advance to initial token
+ do
+ {
+ if(*buffptr == '\\' || *buffptr == '/')
break;
- }
+
+ buffptr++;
+ } while (buffptr < buffend);
- // parse out ip
- addresses[numservers].ip[0] = *buffptr++;
- addresses[numservers].ip[1] = *buffptr++;
- addresses[numservers].ip[2] = *buffptr++;
- addresses[numservers].ip[3] = *buffptr++;
+ while (buffptr + 1 < buffend)
+ {
+ if (*buffptr == '\\')
+ {
+ buffptr++;
+
+ if (buffend - buffptr < sizeof(addresses[numservers].ip) + sizeof(addresses[numservers].port) + 1)
+ break;
+ for(i = 0; i < sizeof(addresses[numservers].ip); i++)
+ addresses[numservers].ip[i] = *buffptr++;
+
+ addresses[numservers].type = NA_IP;
+ }
+ else
+ {
+ buffptr++;
+
+ if (buffend - buffptr < sizeof(addresses[numservers].ip6) + sizeof(addresses[numservers].port) + 1)
+ break;
+
+ for(i = 0; i < sizeof(addresses[numservers].ip6); i++)
+ addresses[numservers].ip6[i] = *buffptr++;
+
+ addresses[numservers].type = NA_IP6;
+ }
+
// parse out port
- addresses[numservers].port = (*buffptr++)<<8;
+ addresses[numservers].port = (*buffptr++) << 8;
addresses[numservers].port += *buffptr++;
addresses[numservers].port = BigShort( addresses[numservers].port );
// syntax check
- if (*buffptr != '\\') {
+ if (*buffptr != '\\' && *buffptr != '/')
break;
- }
-
- Com_DPrintf( "server: %d ip: %d.%d.%d.%d:%d\n",numservers,
- addresses[numservers].ip[0],
- addresses[numservers].ip[1],
- addresses[numservers].ip[2],
- addresses[numservers].ip[3],
- BigShort( addresses[numservers].port ) );
-
+
numservers++;
- if (numservers >= MAX_SERVERSPERPACKET) {
+ if (numservers >= MAX_SERVERSPERPACKET)
break;
- }
-
- // parse out EOT
- if (buffptr[1] == 'E' && buffptr[2] == 'O' && buffptr[3] == 'T') {
- break;
- }
}
- if (cls.masterNum == 0) {
- count = cls.numglobalservers;
- max = MAX_GLOBAL_SERVERS;
- } else {
- count = cls.nummplayerservers;
- max = MAX_OTHER_SERVERS;
- }
+ count = cls.numglobalservers;
- for (i = 0; i < numservers && count < max; i++) {
+ for (i = 0; i < numservers && count < MAX_GLOBAL_SERVERS; i++) {
// build net address
- serverInfo_t *server = (cls.masterNum == 0) ? &cls.globalServers[count] : &cls.mplayerServers[count];
+ serverInfo_t *server = &cls.globalServers[count];
CL_InitServerInfo( server, &addresses[i] );
// advance to next slot
@@ -1887,29 +2207,18 @@ void CL_ServersResponsePacket( netadr_t from, msg_t *msg ) {
}
// if getting the global list
- if (cls.masterNum == 0) {
- if ( cls.numGlobalServerAddresses < MAX_GLOBAL_SERVERS ) {
- // if we couldn't store the servers in the main list anymore
- for (; i < numservers && count >= max; i++) {
- serverAddress_t *addr;
- // just store the addresses in an additional list
- addr = &cls.globalServerAddresses[cls.numGlobalServerAddresses++];
- addr->ip[0] = addresses[i].ip[0];
- addr->ip[1] = addresses[i].ip[1];
- addr->ip[2] = addresses[i].ip[2];
- addr->ip[3] = addresses[i].ip[3];
- addr->port = addresses[i].port;
- }
+ if ( count >= MAX_GLOBAL_SERVERS && cls.numGlobalServerAddresses < MAX_GLOBAL_SERVERS )
+ {
+ // if we couldn't store the servers in the main list anymore
+ for (; i < numservers && cls.numGlobalServerAddresses < MAX_GLOBAL_SERVERS; i++)
+ {
+ // just store the addresses in an additional list
+ cls.globalServerAddresses[cls.numGlobalServerAddresses++] = addresses[i];
}
}
- if (cls.masterNum == 0) {
- cls.numglobalservers = count;
- total = count + cls.numGlobalServerAddresses;
- } else {
- cls.nummplayerservers = count;
- total = count;
- }
+ cls.numglobalservers = count;
+ total = count + cls.numGlobalServerAddresses;
Com_Printf("%d servers parsed (total %d)\n", numservers, total);
}
@@ -1936,12 +2245,12 @@ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
Q_strncpyz( c, Cmd_Argv( 0 ), BIG_INFO_STRING );
Q_strncpyz( arg1, Cmd_Argv( 1 ), BIG_INFO_STRING );
- Com_DPrintf ("CL packet %s: %s\n", NET_AdrToString(from), c);
+ Com_DPrintf ("CL packet %s: %s\n", NET_AdrToStringwPort(from), c);
// challenge from the server we are connecting to
if ( !Q_stricmp(c, "challengeResponse") ) {
if ( cls.state != CA_CONNECTING ) {
- Com_Printf( "Unwanted challenge response received. Ignored.\n" );
+ Com_DPrintf( "Unwanted challenge response received. Ignored.\n" );
} else {
// start sending challenge repsonse instead of challenge request packets
clc.challenge = atoi(arg1);
@@ -1969,8 +2278,8 @@ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
}
if ( !NET_CompareBaseAdr( from, clc.serverAddress ) ) {
Com_Printf( "connectResponse from a different address. Ignored.\n" );
- Com_Printf( "%s should have been %s\n", NET_AdrToString( from ),
- NET_AdrToString( clc.serverAddress ) );
+ Com_Printf( "%s should have been %s\n", NET_AdrToStringwPort( from ),
+ NET_AdrToStringwPort( clc.serverAddress ) );
return;
}
Netchan_Setup (NS_CLIENT, &clc.netchan, from, Cvar_VariableValue( "net_qport" ) );
@@ -2055,7 +2364,7 @@ void CL_PacketEvent( netadr_t from, msg_t *msg ) {
}
if ( msg->cursize < 4 ) {
- Com_Printf ("%s: Runt packet\n",NET_AdrToString( from ));
+ Com_Printf ("%s: Runt packet\n", NET_AdrToStringwPort( from ));
return;
}
@@ -2064,7 +2373,7 @@ void CL_PacketEvent( netadr_t from, msg_t *msg ) {
//
if ( !NET_CompareAdr( from, clc.netchan.remoteAddress ) ) {
Com_DPrintf ("%s:sequenced packet without connection\n"
- ,NET_AdrToString( from ) );
+ , NET_AdrToStringwPort( from ) );
// FIXME: send a client disconnect?
return;
}
@@ -2190,7 +2499,7 @@ void CL_Frame ( int msec ) {
#endif
if ( cls.state == CA_DISCONNECTED && !( Key_GetCatcher( ) & KEYCATCH_UI )
- && !com_sv_running->integer ) {
+ && !com_sv_running->integer && uivm ) {
// if disconnected, bring up the menu
S_StopAllSounds();
VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_MAIN );
@@ -2282,6 +2591,14 @@ void CL_Frame ( int msec ) {
// update audio
S_Update();
+#ifdef USE_VOIP
+ CL_CaptureVoip();
+#endif
+
+#ifdef USE_MUMBLE
+ CL_UpdateMumble();
+#endif
+
// advance local effects for next frame
SCR_RunCinematic();
@@ -2385,6 +2702,10 @@ void CL_StartHunkUsers( qboolean rendererOnly ) {
S_BeginRegistration();
}
+ if( com_dedicated->integer ) {
+ return;
+ }
+
if ( !cls.uiStarted ) {
cls.uiStarted = qtrue;
CL_InitUI();
@@ -2442,6 +2763,7 @@ void CL_InitRef( void ) {
ri.FS_FileExists = FS_FileExists;
ri.Cvar_Get = Cvar_Get;
ri.Cvar_Set = Cvar_Set;
+ ri.Cvar_CheckRange = Cvar_CheckRange;
// cinematic stuff
@@ -2608,7 +2930,7 @@ CL_Init
void CL_Init( void ) {
Com_Printf( "----- Client Initialization -----\n" );
- Con_Init ();
+ Con_Init ();
CL_ClearState ();
@@ -2626,7 +2948,6 @@ void CL_Init( void ) {
cl_timeout = Cvar_Get ("cl_timeout", "200", 0);
- cl_master = Cvar_Get ("cl_master", MASTER_SERVER_NAME, CVAR_ARCHIVE);
cl_timeNudge = Cvar_Get ("cl_timeNudge", "0", CVAR_TEMP );
cl_shownet = Cvar_Get ("cl_shownet", "0", CVAR_TEMP );
cl_showSend = Cvar_Get ("cl_showSend", "0", CVAR_TEMP );
@@ -2666,7 +2987,7 @@ void CL_Init( void ) {
cl_conXOffset = Cvar_Get ("cl_conXOffset", "0", 0);
#ifdef MACOS_X
- // In game video is REALLY slow in Mac OS X right now due to driver slowness
+ // In game video is REALLY slow in Mac OS X right now due to driver slowness
cl_inGameVideo = Cvar_Get ("r_inGameVideo", "0", CVAR_ARCHIVE);
#else
cl_inGameVideo = Cvar_Get ("r_inGameVideo", "1", CVAR_ARCHIVE);
@@ -2683,7 +3004,7 @@ void CL_Init( void ) {
m_forward = Cvar_Get ("m_forward", "0.25", CVAR_ARCHIVE);
m_side = Cvar_Get ("m_side", "0.25", CVAR_ARCHIVE);
#ifdef MACOS_X
- // Input is jittery on OS X w/o this
+ // Input is jittery on OS X w/o this
m_filter = Cvar_Get ("m_filter", "1", CVAR_ARCHIVE);
#else
m_filter = Cvar_Get ("m_filter", "0", CVAR_ARCHIVE);
@@ -2709,9 +3030,42 @@ void CL_Init( void ) {
Cvar_Get ("password", "", CVAR_USERINFO);
+#ifdef USE_MUMBLE
+ cl_useMumble = Cvar_Get ("cl_useMumble", "0", CVAR_ARCHIVE | CVAR_LATCH);
+ cl_mumbleScale = Cvar_Get ("cl_mumbleScale", "0.0254", CVAR_ARCHIVE);
+#endif
+
+#ifdef USE_VOIP
+ 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_voipCaptureMult = Cvar_Get ("cl_voipCaptureMult", "2.0", CVAR_ARCHIVE);
+ cl_voipUseVAD = Cvar_Get ("cl_voipUseVAD", "0", CVAR_ARCHIVE);
+ 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 | CVAR_LATCH);
+ Cvar_CheckRange( cl_voip, 0, 1, qtrue );
+
+ // If your data rate is too low, you'll get Connection Interrupted warnings
+ // when VoIP packets arrive, even if you have a broadband connection.
+ // This might work on rates lower than 25000, but for safety's sake, we'll
+ // just demand it. Who doesn't have at least a DSL line now, anyhow? If
+ // you don't, you don't need VoIP. :)
+ if ((cl_voip->integer) && (Cvar_VariableIntegerValue("rate") < 25000)) {
+ Com_Printf("Your network rate is too slow for VoIP.\n");
+ Com_Printf("Set 'Data Rate' to 'LAN/Cable/xDSL' in 'Setup/System/Network' and restart.\n");
+ Com_Printf("Until then, VoIP is disabled.\n");
+ Cvar_Set("cl_voip", "0");
+ }
+#endif
+
// cgame might not be initialized before menu is used
Cvar_Get ("cg_viewsize", "100", CVAR_ARCHIVE );
+ // Make sure cg_stereoSeparation is zero as that variable is deprecated and should not be used anymore.
+ Cvar_Get ("cg_stereoSeparation", "0", CVAR_ROM);
//
// register our commands
@@ -2744,11 +3098,11 @@ void CL_Init( void ) {
SCR_Init ();
- Cbuf_Execute ();
+// Cbuf_Execute ();
Cvar_Set( "cl_running", "1" );
- CL_GenerateQKey();
+ CL_GenerateQKey();
Cvar_Get( "cl_guid", "", CVAR_USERINFO | CVAR_ROM );
CL_UpdateGUID( NULL, 0 );
@@ -2843,12 +3197,6 @@ static void CL_SetServerInfoByAddress(netadr_t from, const char *info, int ping)
}
}
- for (i = 0; i < MAX_OTHER_SERVERS; i++) {
- if (NET_CompareAdr(from, cls.mplayerServers[i].adr)) {
- CL_SetServerInfo(&cls.mplayerServers[i], info, ping);
- }
- }
-
for (i = 0; i < MAX_GLOBAL_SERVERS; i++) {
if (NET_CompareAdr(from, cls.globalServers[i].adr)) {
CL_SetServerInfo(&cls.globalServers[i], info, ping);
@@ -2871,7 +3219,6 @@ CL_ServerInfoPacket
void CL_ServerInfoPacket( netadr_t from, msg_t *msg ) {
int i, type;
char info[MAX_INFO_STRING];
- char* str;
char *infoString;
int prot;
@@ -2902,12 +3249,12 @@ void CL_ServerInfoPacket( netadr_t from, msg_t *msg ) {
{
case NA_BROADCAST:
case NA_IP:
- str = "udp";
type = 1;
break;
-
+ case NA_IP6:
+ type = 2;
+ break;
default:
- str = "???";
type = 0;
break;
}
@@ -2959,7 +3306,7 @@ void CL_ServerInfoPacket( netadr_t from, msg_t *msg ) {
if (info[strlen(info)-1] != '\n') {
strncat(info, "\n", sizeof(info) - 1);
}
- Com_Printf( "%s: %s", NET_AdrToString( from ), info );
+ Com_Printf( "%s: %s", NET_AdrToStringwPort( from ), info );
}
}
@@ -3017,7 +3364,7 @@ int CL_ServerStatus( char *serverAddress, char *serverStatusString, int maxLen )
return qfalse;
}
// get the address
- if ( !NET_StringToAdr( serverAddress, &to ) ) {
+ if ( !NET_StringToAdr( serverAddress, &to, NA_UNSPEC) ) {
return qfalse;
}
serverStatus = CL_GetServerStatus( to );
@@ -3192,6 +3539,8 @@ void CL_LocalServers_f( void ) {
to.type = NA_BROADCAST;
NET_SendPacket( NS_CLIENT, strlen( message ), message, to );
+ to.type = NA_MULTICAST6;
+ NET_SendPacket( NS_CLIENT, strlen( message ), message, to );
}
}
}
@@ -3203,43 +3552,49 @@ CL_GlobalServers_f
*/
void CL_GlobalServers_f( void ) {
netadr_t to;
- int i;
- int count;
- char *buffptr;
- char command[1024];
+ int count, i, masterNum;
+ char command[1024], *masteraddress;
- if ( Cmd_Argc() < 3) {
- Com_Printf( "usage: globalservers <master# 0-1> <protocol> [keywords]\n");
+ if ((count = Cmd_Argc()) < 3 || (masterNum = atoi(Cmd_Argv(1))) < 0 || masterNum > 4)
+ {
+ Com_Printf( "usage: globalservers <master# 0-4> <protocol> [keywords]\n");
return;
}
- cls.masterNum = atoi( Cmd_Argv(1) );
-
- Com_Printf( "Requesting servers from the master...\n");
+ sprintf(command, "sv_master%d", masterNum + 1);
+ masteraddress = Cvar_VariableString(command);
+
+ if(!*masteraddress)
+ {
+ Com_Printf( "CL_GlobalServers_f: Error: No master server address given.\n");
+ return;
+ }
// reset the list, waiting for response
// -1 is used to distinguish a "no response"
- NET_StringToAdr( cl_master->string, &to );
-
- if( cls.masterNum == 1 ) {
- cls.nummplayerservers = -1;
- cls.pingUpdateSource = AS_MPLAYER;
- }
- else {
- cls.numglobalservers = -1;
- cls.pingUpdateSource = AS_GLOBAL;
+ i = NET_StringToAdr(masteraddress, &to, NA_UNSPEC);
+
+ if(!i)
+ {
+ Com_Printf( "CL_GlobalServers_f: Error: could not resolve address of master %s\n", masteraddress);
+ return;
}
- to.type = NA_IP;
- to.port = BigShort(PORT_MASTER);
+ else if(i == 2)
+ to.port = BigShort(PORT_MASTER);
+
+ Com_Printf("Requesting servers from master %s...\n", masteraddress);
- sprintf( command, "getservers %s", Cmd_Argv(2) );
+ cls.numglobalservers = -1;
+ cls.pingUpdateSource = AS_GLOBAL;
- // tack on keywords
- buffptr = command + strlen( command );
- count = Cmd_Argc();
- for (i=3; i<count; i++)
- buffptr += sprintf( buffptr, " %s", Cmd_Argv(i) );
+ Com_sprintf( command, sizeof(command), "getservers %s", Cmd_Argv(2) );
+
+ for (i=3; i < count; i++)
+ {
+ Q_strcat(command, sizeof(command), " ");
+ Q_strcat(command, sizeof(command), Cmd_Argv(i));
+ }
NET_OutOfBandPrint( NS_SERVER, to, "%s", command );
}
@@ -3264,7 +3619,7 @@ void CL_GetPing( int n, char *buf, int buflen, int *pingtime )
return;
}
- str = NET_AdrToString( cl_pinglist[n].adr );
+ str = NET_AdrToStringwPort( cl_pinglist[n].adr );
Q_strncpyz( buf, str, buflen );
time = cl_pinglist[n].time;
@@ -3423,17 +3778,33 @@ void CL_Ping_f( void ) {
netadr_t to;
ping_t* pingptr;
char* server;
+ int argc;
+ netadrtype_t family = NA_UNSPEC;
+
+ argc = Cmd_Argc();
- if ( Cmd_Argc() != 2 ) {
- Com_Printf( "usage: ping [server]\n");
+ if ( argc != 2 && argc != 3 ) {
+ Com_Printf( "usage: ping [-4|-6] server\n");
return;
}
+
+ if(argc == 2)
+ server = Cmd_Argv(1);
+ else
+ {
+ if(!strcmp(Cmd_Argv(1), "-4"))
+ family = NA_IP;
+ else if(!strcmp(Cmd_Argv(1), "-6"))
+ family = NA_IP6;
+ else
+ Com_Printf( "warning: only -4 or -6 as address type understood.\n");
+
+ server = Cmd_Argv(2);
+ }
Com_Memset( &to, 0, sizeof(netadr_t) );
- server = Cmd_Argv(1);
-
- if ( !NET_StringToAdr( server, &to ) ) {
+ if ( !NET_StringToAdr( server, &to, family ) ) {
return;
}
@@ -3476,10 +3847,6 @@ qboolean CL_UpdateVisiblePings_f(int source) {
server = &cls.localServers[0];
max = cls.numlocalservers;
break;
- case AS_MPLAYER :
- server = &cls.mplayerServers[0];
- max = cls.nummplayerservers;
- break;
case AS_GLOBAL :
server = &cls.globalServers[0];
max = cls.numglobalservers;
@@ -3488,6 +3855,8 @@ qboolean CL_UpdateVisiblePings_f(int source) {
server = &cls.favoriteServers[0];
max = cls.numfavoriteservers;
break;
+ default:
+ return qfalse;
}
for (i = 0; i < max; i++) {
if (server[i].visible) {
@@ -3561,32 +3930,53 @@ CL_ServerStatus_f
==================
*/
void CL_ServerStatus_f(void) {
- netadr_t to;
+ netadr_t to, *toptr = NULL;
char *server;
serverStatus_t *serverStatus;
+ int argc;
+ netadrtype_t family = NA_UNSPEC;
- Com_Memset( &to, 0, sizeof(netadr_t) );
+ argc = Cmd_Argc();
- if ( Cmd_Argc() != 2 ) {
- if ( cls.state != CA_ACTIVE || clc.demoplaying ) {
+ if ( argc != 2 && argc != 3 )
+ {
+ if (cls.state != CA_ACTIVE || clc.demoplaying)
+ {
Com_Printf ("Not connected to a server.\n");
- Com_Printf( "Usage: serverstatus [server]\n");
- return;
+ Com_Printf( "usage: serverstatus [-4|-6] server\n");
+ return;
}
- server = cls.servername;
- }
- else {
- server = Cmd_Argv(1);
+
+ toptr = &clc.serverAddress;
}
+
+ if(!toptr)
+ {
+ Com_Memset( &to, 0, sizeof(netadr_t) );
+
+ if(argc == 2)
+ server = Cmd_Argv(1);
+ else
+ {
+ if(!strcmp(Cmd_Argv(1), "-4"))
+ family = NA_IP;
+ else if(!strcmp(Cmd_Argv(1), "-6"))
+ family = NA_IP6;
+ else
+ Com_Printf( "warning: only -4 or -6 as address type understood.\n");
+
+ server = Cmd_Argv(2);
+ }
- if ( !NET_StringToAdr( server, &to ) ) {
- return;
+ toptr = &to;
+ if ( !NET_StringToAdr( server, toptr, family ) )
+ return;
}
- NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" );
+ NET_OutOfBandPrint( NS_CLIENT, *toptr, "getstatus" );
- serverStatus = CL_GetServerStatus( to );
- serverStatus->address = to;
+ serverStatus = CL_GetServerStatus( *toptr );
+ serverStatus->address = *toptr;
serverStatus->print = qtrue;
serverStatus->pending = qtrue;
}
diff --git a/src/client/cl_parse.c b/src/client/cl_parse.c
index ef57ee3f..96d4f799 100644
--- a/src/client/cl_parse.c
+++ b/src/client/cl_parse.c
@@ -33,7 +33,10 @@ char *svc_strings[256] = {
"svc_baseline",
"svc_serverCommand",
"svc_download",
- "svc_snapshot"
+ "svc_snapshot",
+ "svc_EOF",
+ "svc_extension",
+ "svc_voip",
};
void SHOWNET( msg_t *msg, char *s) {
@@ -328,6 +331,10 @@ void CL_ParseSnapshot( msg_t *msg ) {
int cl_connectedToPureServer;
int cl_connectedToCheatServer;
+#ifdef USE_VOIP
+int cl_connectedToVoipServer;
+#endif
+
/*
==================
CL_SystemInfoChanged
@@ -356,6 +363,13 @@ void CL_SystemInfoChanged( void ) {
return;
}
+#ifdef USE_VOIP
+ // in the future, (val) will be a protocol version string, so only
+ // accept explicitly 1, not generally non-zero.
+ s = Info_ValueForKey( systemInfo, "sv_voip" );
+ cl_connectedToVoipServer = (atoi( s ) == 1);
+#endif
+
s = Info_ValueForKey( systemInfo, "sv_cheats" );
cl_connectedToCheatServer = atoi( s );
if ( !cl_connectedToCheatServer ) {
@@ -622,6 +636,164 @@ void CL_ParseDownload ( msg_t *msg ) {
}
}
+#ifdef USE_VOIP
+static
+qboolean CL_ShouldIgnoreVoipSender(int sender)
+{
+ if (!cl_voip->integer)
+ return qtrue; // VoIP is disabled.
+ else if ((sender == clc.clientNum) && (!clc.demoplaying))
+ return qtrue; // ignore own voice (unless playing back a demo).
+ else if (clc.voipMuteAll)
+ return qtrue; // all channels are muted with extreme prejudice.
+ else if (clc.voipIgnore[sender])
+ return qtrue; // just ignoring this guy.
+ else if (clc.voipGain[sender] == 0.0f)
+ return qtrue; // too quiet to play.
+
+ return qfalse;
+}
+
+/*
+=====================
+CL_ParseVoip
+
+A VoIP message has been received from the server
+=====================
+*/
+static
+void CL_ParseVoip ( msg_t *msg ) {
+ static short decoded[4096]; // !!! FIXME: don't hardcode.
+
+ const int sender = MSG_ReadShort(msg);
+ const int generation = MSG_ReadByte(msg);
+ const int sequence = MSG_ReadLong(msg);
+ const int frames = MSG_ReadByte(msg);
+ const int packetsize = MSG_ReadShort(msg);
+ char encoded[1024];
+ int seqdiff = sequence - clc.voipIncomingSequence[sender];
+ int written = 0;
+ int i;
+
+ Com_DPrintf("VoIP: %d-byte packet from client %d\n", packetsize, sender);
+
+ if (sender < 0)
+ return; // short/invalid packet, bail.
+ else if (generation < 0)
+ return; // short/invalid packet, bail.
+ else if (sequence < 0)
+ return; // short/invalid packet, bail.
+ else if (frames < 0)
+ return; // short/invalid packet, bail.
+ else if (packetsize < 0)
+ return; // short/invalid packet, bail.
+
+ if (packetsize > sizeof (encoded)) { // overlarge packet?
+ int bytesleft = packetsize;
+ while (bytesleft) {
+ int br = bytesleft;
+ if (br > sizeof (encoded))
+ br = sizeof (encoded);
+ MSG_ReadData(msg, encoded, br);
+ bytesleft -= br;
+ }
+ return; // overlarge packet, bail.
+ }
+
+ if (!clc.speexInitialized) {
+ MSG_ReadData(msg, encoded, packetsize); // skip payload.
+ return; // can't handle VoIP without libspeex!
+ } 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.
+ }
+
+ // !!! FIXME: make sure data is narrowband? Does decoder handle this?
+
+ Com_DPrintf("VoIP: packet accepted!\n");
+
+ // 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]);
+ 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]);
+ seqdiff = 0;
+ } else if (seqdiff > 100) { // more than 2 seconds of audio dropped?
+ // just start over.
+ Com_DPrintf("VoIP: Dropped way too many (%d) frames from client #%d\n",
+ seqdiff, sender);
+ speex_bits_reset(&clc.speexDecoderBits[sender]);
+ seqdiff = 0;
+ }
+
+ if (seqdiff != 0) {
+ Com_DPrintf("VoIP: Dropped %d frames from client #%d\n",
+ seqdiff, sender);
+ // tell speex 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;
+ }
+ }
+
+ for (i = 0; i < frames; i++) {
+ char encoded[256];
+ const int len = MSG_ReadByte(msg);
+ if (len < 0) {
+ Com_DPrintf("VoIP: Short packet!\n");
+ break;
+ }
+ MSG_ReadData(msg, encoded, len);
+
+ // 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);
+ S_RawSamples(sender + 1, written, clc.speexSampleRate, 2, 1,
+ (const byte *) decoded, clc.voipGain[sender]);
+ written = 0;
+ }
+
+ speex_bits_read_from(&clc.speexDecoderBits[sender], encoded, len);
+ speex_decode_int(clc.speexDecoder[sender],
+ &clc.speexDecoderBits[sender], decoded + written);
+
+ #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;
+ }
+
+ Com_DPrintf("VoIP: playback %d bytes, %d samples, %d frames\n",
+ written * 2, written, i);
+
+ if (written > 0) {
+ S_RawSamples(sender + 1, written, clc.speexSampleRate, 2, 1,
+ (const byte *) decoded, clc.voipGain[sender]);
+ }
+
+ clc.voipIncomingSequence[sender] = sequence + frames;
+}
+#endif
+
+
/*
=====================
CL_ParseCommandString
@@ -683,13 +855,26 @@ void CL_ParseServerMessage( msg_t *msg ) {
cmd = MSG_ReadByte( msg );
- if ( cmd == svc_EOF) {
+ // See if this is an extension command after the EOF, which means we
+ // got data that a legacy client should ignore.
+ if ((cmd == svc_EOF) && (MSG_LookaheadByte( msg ) == svc_extension)) {
+ SHOWNET( msg, "EXTENSION" );
+ MSG_ReadByte( msg ); // throw the svc_extension byte away.
+ cmd = MSG_ReadByte( msg ); // something legacy clients can't do!
+ // sometimes you get a svc_extension at end of stream...dangling
+ // bits in the huffman decoder giving a bogus value?
+ if (cmd == -1) {
+ cmd = svc_EOF;
+ }
+ }
+
+ if (cmd == svc_EOF) {
SHOWNET( msg, "END OF MESSAGE" );
break;
}
if ( cl_shownet->integer >= 2 ) {
- if ( !svc_strings[cmd] ) {
+ if ( (cmd < 0) || (!svc_strings[cmd]) ) {
Com_Printf( "%3i:BAD CMD %i\n", msg->readcount-1, cmd );
} else {
SHOWNET( msg, svc_strings[cmd] );
@@ -715,6 +900,11 @@ void CL_ParseServerMessage( msg_t *msg ) {
case svc_download:
CL_ParseDownload( msg );
break;
+ case svc_voip:
+#ifdef USE_VOIP
+ CL_ParseVoip( msg );
+#endif
+ break;
}
}
}
diff --git a/src/client/cl_scrn.c b/src/client/cl_scrn.c
index f0a64bb4..cf89fb9d 100644
--- a/src/client/cl_scrn.c
+++ b/src/client/cl_scrn.c
@@ -322,6 +322,46 @@ int SCR_GetBigStringWidth( const char *str ) {
//===============================================================================
/*
+=================
+SCR_DrawVoipMeter
+=================
+*/
+void SCR_DrawVoipMeter( void ) {
+ char buffer[16];
+ char string[256];
+ int limit, i;
+
+ if (!cl_voipShowMeter->integer)
+ return; // player doesn't want to show meter at all.
+ else if (!cl_voipSend->integer)
+ return; // not recording at the moment.
+ else if (cls.state != CA_ACTIVE)
+ return; // not connected to a server.
+ else if (!cl_connectedToVoipServer)
+ return; // server doesn't support VoIP.
+ else if (clc.demoplaying)
+ return; // playing back a demo.
+ else if (!cl_voip->integer)
+ return; // client has VoIP support disabled.
+
+ limit = (int) (clc.voipPower * 10.0f);
+ if (limit > 10)
+ limit = 10;
+
+ for (i = 0; i < limit; i++)
+ buffer[i] = '*';
+ while (i < 10)
+ buffer[i++] = ' ';
+ buffer[i] = '\0';
+
+ sprintf( string, "VoIP: [%s]", buffer );
+ SCR_DrawStringExt( 320 - strlen( string ) * 4, 10, 8, string, g_color_table[7], qtrue, qfalse );
+}
+
+
+
+
+/*
===============================================================================
DEBUG GRAPH
@@ -452,10 +492,14 @@ void SCR_DrawScreenField( stereoFrame_t stereoFrame ) {
case CA_LOADING:
case CA_PRIMED:
// draw the game information screen and loading progress
- CL_CGameRendering( stereoFrame );
+ CL_CGameRendering(stereoFrame);
break;
case CA_ACTIVE:
- CL_CGameRendering( stereoFrame );
+ // always supply STEREO_CENTER as vieworg offset is now done by the engine.
+ CL_CGameRendering(stereoFrame);
+#ifdef USE_VOIP
+ SCR_DrawVoipMeter();
+#endif
break;
}
}
@@ -494,20 +538,25 @@ void SCR_UpdateScreen( void ) {
}
recursive = 1;
- // if running in stereo, we need to draw the frame twice
- if ( cls.glconfig.stereoEnabled ) {
- SCR_DrawScreenField( STEREO_LEFT );
- SCR_DrawScreenField( STEREO_RIGHT );
- } else {
- SCR_DrawScreenField( STEREO_CENTER );
- }
+ // If there is no VM, there are also no rendering commands issued. Stop the renderer in
+ // that case.
+ if( uivm || com_dedicated->integer )
+ {
+ // if running in stereo, we need to draw the frame twice
+ if ( cls.glconfig.stereoEnabled || Cvar_VariableIntegerValue("r_anaglyphMode")) {
+ SCR_DrawScreenField( STEREO_LEFT );
+ SCR_DrawScreenField( STEREO_RIGHT );
+ } else {
+ SCR_DrawScreenField( STEREO_CENTER );
+ }
- if ( com_speeds->integer ) {
- re.EndFrame( &time_frontend, &time_backend );
- } else {
- re.EndFrame( NULL, NULL );
+ if ( com_speeds->integer ) {
+ re.EndFrame( &time_frontend, &time_backend );
+ } else {
+ re.EndFrame( NULL, NULL );
+ }
}
-
+
recursive = 0;
}
diff --git a/src/client/cl_ui.c b/src/client/cl_ui.c
index 8aa4550f..c0d4e729 100644
--- a/src/client/cl_ui.c
+++ b/src/client/cl_ui.c
@@ -47,19 +47,17 @@ LAN_LoadCachedServers
void LAN_LoadCachedServers( void ) {
int size;
fileHandle_t fileIn;
- cls.numglobalservers = cls.nummplayerservers = cls.numfavoriteservers = 0;
+ cls.numglobalservers = cls.numfavoriteservers = 0;
cls.numGlobalServerAddresses = 0;
if (FS_SV_FOpenFileRead("servercache.dat", &fileIn)) {
FS_Read(&cls.numglobalservers, sizeof(int), fileIn);
- FS_Read(&cls.nummplayerservers, sizeof(int), fileIn);
FS_Read(&cls.numfavoriteservers, sizeof(int), fileIn);
FS_Read(&size, sizeof(int), fileIn);
- if (size == sizeof(cls.globalServers) + sizeof(cls.favoriteServers) + sizeof(cls.mplayerServers)) {
+ if (size == sizeof(cls.globalServers) + sizeof(cls.favoriteServers)) {
FS_Read(&cls.globalServers, sizeof(cls.globalServers), fileIn);
- FS_Read(&cls.mplayerServers, sizeof(cls.mplayerServers), fileIn);
FS_Read(&cls.favoriteServers, sizeof(cls.favoriteServers), fileIn);
} else {
- cls.numglobalservers = cls.nummplayerservers = cls.numfavoriteservers = 0;
+ cls.numglobalservers = cls.numfavoriteservers = 0;
cls.numGlobalServerAddresses = 0;
}
FS_FCloseFile(fileIn);
@@ -75,12 +73,10 @@ void LAN_SaveServersToCache( void ) {
int size;
fileHandle_t fileOut = FS_SV_FOpenFileWrite("servercache.dat");
FS_Write(&cls.numglobalservers, sizeof(int), fileOut);
- FS_Write(&cls.nummplayerservers, sizeof(int), fileOut);
FS_Write(&cls.numfavoriteservers, sizeof(int), fileOut);
- size = sizeof(cls.globalServers) + sizeof(cls.favoriteServers) + sizeof(cls.mplayerServers);
+ size = sizeof(cls.globalServers) + sizeof(cls.favoriteServers);
FS_Write(&size, sizeof(int), fileOut);
FS_Write(&cls.globalServers, sizeof(cls.globalServers), fileOut);
- FS_Write(&cls.mplayerServers, sizeof(cls.mplayerServers), fileOut);
FS_Write(&cls.favoriteServers, sizeof(cls.favoriteServers), fileOut);
FS_FCloseFile(fileOut);
}
@@ -101,10 +97,7 @@ static void LAN_ResetPings(int source) {
servers = &cls.localServers[0];
count = MAX_OTHER_SERVERS;
break;
- case AS_MPLAYER :
- servers = &cls.mplayerServers[0];
- count = MAX_OTHER_SERVERS;
- break;
+ case AS_MPLAYER:
case AS_GLOBAL :
servers = &cls.globalServers[0];
count = MAX_GLOBAL_SERVERS;
@@ -138,10 +131,7 @@ static int LAN_AddServer(int source, const char *name, const char *address) {
count = &cls.numlocalservers;
servers = &cls.localServers[0];
break;
- case AS_MPLAYER :
- count = &cls.nummplayerservers;
- servers = &cls.mplayerServers[0];
- break;
+ case AS_MPLAYER:
case AS_GLOBAL :
max = MAX_GLOBAL_SERVERS;
count = &cls.numglobalservers;
@@ -153,7 +143,7 @@ static int LAN_AddServer(int source, const char *name, const char *address) {
break;
}
if (servers && *count < max) {
- NET_StringToAdr( address, &adr );
+ NET_StringToAdr( address, &adr, NA_IP );
for ( i = 0; i < *count; i++ ) {
if (NET_CompareAdr(servers[i].adr, adr)) {
break;
@@ -185,10 +175,7 @@ static void LAN_RemoveServer(int source, const char *addr) {
count = &cls.numlocalservers;
servers = &cls.localServers[0];
break;
- case AS_MPLAYER :
- count = &cls.nummplayerservers;
- servers = &cls.mplayerServers[0];
- break;
+ case AS_MPLAYER:
case AS_GLOBAL :
count = &cls.numglobalservers;
servers = &cls.globalServers[0];
@@ -200,7 +187,7 @@ static void LAN_RemoveServer(int source, const char *addr) {
}
if (servers) {
netadr_t comp;
- NET_StringToAdr( addr, &comp );
+ NET_StringToAdr( addr, &comp, NA_IP );
for (i = 0; i < *count; i++) {
if (NET_CompareAdr( comp, servers[i].adr)) {
int j = i;
@@ -226,9 +213,7 @@ static int LAN_GetServerCount( int source ) {
case AS_LOCAL :
return cls.numlocalservers;
break;
- case AS_MPLAYER :
- return cls.nummplayerservers;
- break;
+ case AS_MPLAYER:
case AS_GLOBAL :
return cls.numglobalservers;
break;
@@ -248,25 +233,20 @@ static void LAN_GetServerAddressString( int source, int n, char *buf, int buflen
switch (source) {
case AS_LOCAL :
if (n >= 0 && n < MAX_OTHER_SERVERS) {
- Q_strncpyz(buf, NET_AdrToString( cls.localServers[n].adr) , buflen );
- return;
- }
- break;
- case AS_MPLAYER :
- if (n >= 0 && n < MAX_OTHER_SERVERS) {
- Q_strncpyz(buf, NET_AdrToString( cls.mplayerServers[n].adr) , buflen );
+ Q_strncpyz(buf, NET_AdrToStringwPort( cls.localServers[n].adr) , buflen );
return;
}
break;
+ case AS_MPLAYER:
case AS_GLOBAL :
if (n >= 0 && n < MAX_GLOBAL_SERVERS) {
- Q_strncpyz(buf, NET_AdrToString( cls.globalServers[n].adr) , buflen );
+ Q_strncpyz(buf, NET_AdrToStringwPort( cls.globalServers[n].adr) , buflen );
return;
}
break;
case AS_FAVORITES :
if (n >= 0 && n < MAX_OTHER_SERVERS) {
- Q_strncpyz(buf, NET_AdrToString( cls.favoriteServers[n].adr) , buflen );
+ Q_strncpyz(buf, NET_AdrToStringwPort( cls.favoriteServers[n].adr) , buflen );
return;
}
break;
@@ -289,11 +269,7 @@ static void LAN_GetServerInfo( int source, int n, char *buf, int buflen ) {
server = &cls.localServers[n];
}
break;
- case AS_MPLAYER :
- if (n >= 0 && n < MAX_OTHER_SERVERS) {
- server = &cls.mplayerServers[n];
- }
- break;
+ case AS_MPLAYER:
case AS_GLOBAL :
if (n >= 0 && n < MAX_GLOBAL_SERVERS) {
server = &cls.globalServers[n];
@@ -339,11 +315,7 @@ static int LAN_GetServerPing( int source, int n ) {
server = &cls.localServers[n];
}
break;
- case AS_MPLAYER :
- if (n >= 0 && n < MAX_OTHER_SERVERS) {
- server = &cls.mplayerServers[n];
- }
- break;
+ case AS_MPLAYER:
case AS_GLOBAL :
if (n >= 0 && n < MAX_GLOBAL_SERVERS) {
server = &cls.globalServers[n];
@@ -373,11 +345,7 @@ static serverInfo_t *LAN_GetServerPtr( int source, int n ) {
return &cls.localServers[n];
}
break;
- case AS_MPLAYER :
- if (n >= 0 && n < MAX_OTHER_SERVERS) {
- return &cls.mplayerServers[n];
- }
- break;
+ case AS_MPLAYER:
case AS_GLOBAL :
if (n >= 0 && n < MAX_GLOBAL_SERVERS) {
return &cls.globalServers[n];
@@ -520,9 +488,7 @@ static void LAN_MarkServerVisible(int source, int n, qboolean visible ) {
case AS_LOCAL :
server = &cls.localServers[0];
break;
- case AS_MPLAYER :
- server = &cls.mplayerServers[0];
- break;
+ case AS_MPLAYER:
case AS_GLOBAL :
server = &cls.globalServers[0];
count = MAX_GLOBAL_SERVERS;
@@ -544,11 +510,7 @@ static void LAN_MarkServerVisible(int source, int n, qboolean visible ) {
cls.localServers[n].visible = visible;
}
break;
- case AS_MPLAYER :
- if (n >= 0 && n < MAX_OTHER_SERVERS) {
- cls.mplayerServers[n].visible = visible;
- }
- break;
+ case AS_MPLAYER:
case AS_GLOBAL :
if (n >= 0 && n < MAX_GLOBAL_SERVERS) {
cls.globalServers[n].visible = visible;
@@ -576,11 +538,7 @@ static int LAN_ServerIsVisible(int source, int n ) {
return cls.localServers[n].visible;
}
break;
- case AS_MPLAYER :
- if (n >= 0 && n < MAX_OTHER_SERVERS) {
- return cls.mplayerServers[n].visible;
- }
- break;
+ case AS_MPLAYER:
case AS_GLOBAL :
if (n >= 0 && n < MAX_GLOBAL_SERVERS) {
return cls.globalServers[n].visible;
@@ -743,6 +701,14 @@ intptr_t CL_UISystemCalls( intptr_t *args ) {
return 0;
case UI_CMD_EXECUTETEXT:
+ if(args[1] == 0
+ && (!strncmp(VMA(2), "snd_restart", 11)
+ || !strncmp(VMA(2), "vid_restart", 11)
+ || !strncmp(VMA(2), "quit", 5)))
+ {
+ Com_Printf (S_COLOR_YELLOW "turning EXEC_NOW '%.11s' into EXEC_INSERT\n", (const char*)VMA(2));
+ args[1] = EXEC_INSERT;
+ }
Cbuf_ExecuteText( args[1], VMA(2) );
return 0;
diff --git a/src/client/client.h b/src/client/client.h
index 5e68edff..a3cbadfb 100644
--- a/src/client/client.h
+++ b/src/client/client.h
@@ -35,6 +35,11 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "cl_curl.h"
#endif /* USE_CURL */
+#ifdef USE_VOIP
+#include "speex/speex.h"
+#include "speex/speex_preprocess.h"
+#endif
+
// file full of random crap that gets used to create cl_guid
#define QKEY_FILE "qkey"
#define QKEY_SIZE 2048
@@ -226,6 +231,36 @@ typedef struct {
int timeDemoMaxDuration; // maximum frame duration
unsigned char timeDemoDurations[ MAX_TIMEDEMO_DURATIONS ]; // log of frame durations
+#ifdef USE_VOIP
+ qboolean speexInitialized;
+ int speexFrameSize;
+ int speexSampleRate;
+
+ // incoming data...
+ // !!! FIXME: convert from parallel arrays to array of a struct.
+ SpeexBits speexDecoderBits[MAX_CLIENTS];
+ void *speexDecoder[MAX_CLIENTS];
+ byte voipIncomingGeneration[MAX_CLIENTS];
+ int voipIncomingSequence[MAX_CLIENTS];
+ float voipGain[MAX_CLIENTS];
+ qboolean voipIgnore[MAX_CLIENTS];
+ qboolean voipMuteAll;
+
+ // outgoing data...
+ int voipTarget1; // these three ints make up a bit mask of 92 bits.
+ int voipTarget2; // the bits say who a VoIP pack is addressed to:
+ int voipTarget3; // (1 << clientnum). See cl_voipSendTarget cvar.
+ SpeexPreprocessState *speexPreprocessor;
+ SpeexBits speexEncoderBits;
+ void *speexEncoder;
+ int voipOutgoingDataSize;
+ int voipOutgoingDataFrames;
+ int voipOutgoingSequence;
+ byte voipOutgoingGeneration;
+ byte voipOutgoingData[1024];
+ float voipPower;
+#endif
+
// big stuff at end of structure so most offsets are 15 bits or less
netchan_t netchan;
} clientConnection_t;
@@ -264,11 +299,6 @@ typedef struct {
} serverInfo_t;
typedef struct {
- byte ip[4];
- unsigned short port;
-} serverAddress_t;
-
-typedef struct {
connstate_t state; // connection status
qboolean cddialog; // bring up the cd needed dialog next frame
@@ -295,18 +325,13 @@ typedef struct {
serverInfo_t globalServers[MAX_GLOBAL_SERVERS];
// additional global servers
int numGlobalServerAddresses;
- serverAddress_t globalServerAddresses[MAX_GLOBAL_SERVERS];
+ netadr_t globalServerAddresses[MAX_GLOBAL_SERVERS];
int numfavoriteservers;
serverInfo_t favoriteServers[MAX_OTHER_SERVERS];
- int nummplayerservers;
- serverInfo_t mplayerServers[MAX_OTHER_SERVERS];
-
int pingUpdateSource; // source currently pinging or updating
- int masterNum;
-
// update server info
netadr_t updateServer;
char updateChallenge[MAX_TOKEN_CHARS];
@@ -377,6 +402,25 @@ extern cvar_t *cl_inGameVideo;
extern cvar_t *cl_lanForcePackets;
extern cvar_t *cl_autoRecordDemo;
+#ifdef USE_MUMBLE
+extern cvar_t *cl_useMumble;
+extern cvar_t *cl_mumbleScale;
+#endif
+
+#ifdef USE_VOIP
+// 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_voipVADThreshold;
+extern cvar_t *cl_voipSend;
+extern cvar_t *cl_voipSendTarget;
+extern cvar_t *cl_voipGainDuringCapture;
+extern cvar_t *cl_voipCaptureMult;
+extern cvar_t *cl_voipShowMeter;
+extern cvar_t *cl_voip;
+#endif
+
//=================================================
//
@@ -431,6 +475,10 @@ extern kbutton_t in_mlook, in_klook;
extern kbutton_t in_strafe;
extern kbutton_t in_speed;
+#ifdef USE_VOIP
+extern kbutton_t in_voiprecord;
+#endif
+
void CL_InitInput (void);
void CL_SendCmd (void);
void CL_ClearState (void);
@@ -452,6 +500,11 @@ void Key_SetCatcher( int catcher );
extern int cl_connectedToPureServer;
extern int cl_connectedToCheatServer;
+#ifdef USE_VOIP
+extern int cl_connectedToVoipServer;
+void CL_Voip_f( void );
+#endif
+
void CL_SystemInfoChanged( void );
void CL_ParseServerMessage( msg_t *msg );
@@ -565,3 +618,9 @@ void CL_WriteAVIVideoFrame( const byte *imageBuffer, int size );
void CL_WriteAVIAudioFrame( const byte *pcmBuffer, int size );
qboolean CL_CloseAVI( void );
qboolean CL_VideoRecording( void );
+
+//
+// cl_main.c
+//
+void CL_WriteDemoMessage ( msg_t *msg, int headerBytes );
+
diff --git a/src/client/libmumblelink.c b/src/client/libmumblelink.c
new file mode 100644
index 00000000..c45e6a1d
--- /dev/null
+++ b/src/client/libmumblelink.c
@@ -0,0 +1,134 @@
+/* libmumblelink.c -- mumble link interface
+
+ Copyright (C) 2008 Ludwig Nussel <ludwig.nussel@suse.de>
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+*/
+
+#ifdef WIN32
+#include <windows.h>
+#define uint32_t UINT32
+#else
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#endif
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "libmumblelink.h"
+
+typedef struct
+{
+ uint32_t uiVersion;
+ uint32_t uiTick;
+ float fPosition[3];
+ float fFront[3];
+ float fTop[3];
+ wchar_t name[256];
+} LinkedMem;
+
+static LinkedMem *lm = NULL;
+
+#ifdef WIN32
+static HANDLE hMapObject = NULL;
+#else
+static int32_t GetTickCount(void)
+{
+ struct timeval tv;
+ gettimeofday(&tv,NULL);
+
+ return tv.tv_usec / 1000 + tv.tv_sec * 1000;
+}
+#endif
+
+int mumble_link(const char* name)
+{
+#ifdef WIN32
+ if(lm)
+ return 0;
+
+ hMapObject = OpenFileMappingW(FILE_MAP_ALL_ACCESS, FALSE, L"MumbleLink");
+ if (hMapObject == NULL)
+ return -1;
+
+ lm = (LinkedMem *) MapViewOfFile(hMapObject, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(LinkedMem));
+ if (lm == NULL) {
+ CloseHandle(hMapObject);
+ hMapObject = NULL;
+ return -1;
+ }
+#else
+ char file[256];
+ int shmfd;
+ if(lm)
+ return 0;
+
+ snprintf(file, sizeof (file), "/MumbleLink.%d", getuid());
+ shmfd = shm_open(file, O_RDWR, S_IRUSR | S_IWUSR);
+ if(shmfd < 0) {
+ return -1;
+ }
+
+ lm = (LinkedMem *) (mmap(NULL, sizeof(LinkedMem), PROT_READ | PROT_WRITE, MAP_SHARED, shmfd,0));
+ if (lm == (void *) (-1)) {
+ lm = NULL;
+ }
+ close(shmfd);
+#endif
+ mbstowcs(lm->name, name, sizeof(lm->name));
+
+ return 0;
+}
+
+void mumble_update_coordinates(float fPosition[3], float fFront[3], float fTop[3])
+{
+ if (!lm)
+ return;
+
+ memcpy(lm->fPosition, fPosition, sizeof(fPosition));
+ memcpy(lm->fFront, fFront, sizeof(fFront));
+ memcpy(lm->fTop, fTop, sizeof(fTop));
+ lm->uiVersion = 1;
+ lm->uiTick = GetTickCount();
+}
+
+void mumble_unlink()
+{
+ if(!lm)
+ return;
+#ifdef WIN32
+ UnmapViewOfFile(lm);
+ CloseHandle(hMapObject);
+ hMapObject = NULL;
+#else
+ munmap(lm, sizeof(LinkedMem));
+#endif
+ lm = NULL;
+}
+
+int mumble_islinked(void)
+{
+ return lm != NULL;
+}
diff --git a/src/client/libmumblelink.h b/src/client/libmumblelink.h
new file mode 100644
index 00000000..0623931e
--- /dev/null
+++ b/src/client/libmumblelink.h
@@ -0,0 +1,26 @@
+/* libmumblelink.h -- mumble link interface
+
+ Copyright (C) 2008 Ludwig Nussel <ludwig.nussel@suse.de>
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+*/
+
+int mumble_link(const char* name);
+int mumble_islinked(void);
+void mumble_update_coordinates(float fPosition[3], float fFront[3], float fTop[3]);
+void mumble_unlink(void);
diff --git a/src/client/qal.c b/src/client/qal.c
index a3448ba2..0596670d 100644
--- a/src/client/qal.c
+++ b/src/client/qal.c
@@ -102,6 +102,11 @@ LPALCGETPROCADDRESS qalcGetProcAddress;
LPALCGETENUMVALUE qalcGetEnumValue;
LPALCGETSTRING qalcGetString;
LPALCGETINTEGERV qalcGetIntegerv;
+LPALCCAPTUREOPENDEVICE qalcCaptureOpenDevice;
+LPALCCAPTURECLOSEDEVICE qalcCaptureCloseDevice;
+LPALCCAPTURESTART qalcCaptureStart;
+LPALCCAPTURESTOP qalcCaptureStop;
+LPALCCAPTURESAMPLES qalcCaptureSamples;
static void *OpenALLib = NULL;
@@ -230,6 +235,11 @@ qboolean QAL_Init(const char *libname)
qalcGetEnumValue = GPA("alcGetEnumValue");
qalcGetString = GPA("alcGetString");
qalcGetIntegerv = GPA("alcGetIntegerv");
+ qalcCaptureOpenDevice = GPA("alcCaptureOpenDevice");
+ qalcCaptureCloseDevice = GPA("alcCaptureCloseDevice");
+ qalcCaptureStart = GPA("alcCaptureStart");
+ qalcCaptureStop = GPA("alcCaptureStop");
+ qalcCaptureSamples = GPA("alcCaptureSamples");
if(alinit_fail)
{
@@ -324,6 +334,11 @@ void QAL_Shutdown( void )
qalcGetEnumValue = NULL;
qalcGetString = NULL;
qalcGetIntegerv = NULL;
+ qalcCaptureOpenDevice = NULL;
+ qalcCaptureCloseDevice = NULL;
+ qalcCaptureStart = NULL;
+ qalcCaptureStop = NULL;
+ qalcCaptureSamples = NULL;
}
#else
qboolean QAL_Init(const char *libname)
diff --git a/src/client/snd_dma.c b/src/client/snd_dma.c
index 749be017..d9fea8d4 100644
--- a/src/client/snd_dma.c
+++ b/src/client/snd_dma.c
@@ -91,8 +91,8 @@ cvar_t *s_mixPreStep;
static loopSound_t loopSounds[MAX_GENTITIES];
static channel_t *freelist = NULL;
-int s_rawend;
-portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES];
+int s_rawend[MAX_RAW_STREAMS];
+portable_samplepair_t s_rawsamples[MAX_RAW_STREAMS][MAX_RAW_SAMPLES];
// ====================================================================
@@ -121,6 +121,42 @@ void S_Base_SoundInfo(void) {
Com_Printf("----------------------\n" );
}
+
+#ifdef USE_VOIP
+static
+void S_Base_StartCapture( void )
+{
+ // !!! FIXME: write me.
+}
+
+static
+int S_Base_AvailableCaptureSamples( void )
+{
+ // !!! FIXME: write me.
+ return 0;
+}
+
+static
+void S_Base_Capture( int samples, byte *data )
+{
+ // !!! FIXME: write me.
+}
+
+static
+void S_Base_StopCapture( void )
+{
+ // !!! FIXME: write me.
+}
+
+static
+void S_Base_MasterGain( float val )
+{
+ // !!! FIXME: write me.
+}
+#endif
+
+
+
/*
=================
S_Base_SoundList
@@ -624,7 +660,7 @@ void S_Base_ClearSoundBuffer( void ) {
S_ChannelSetup();
- s_rawend = 0;
+ Com_Memset(s_rawend, '\0', sizeof (s_rawend));
if (dma.samplebits == 8)
clear = 0x80;
@@ -895,10 +931,6 @@ void S_ByteSwapRawSamples( int samples, int width, int s_channels, const byte *d
}
}
-portable_samplepair_t *S_GetRawSamplePointer( void ) {
- return s_rawsamples;
-}
-
/*
============
S_RawSamples
@@ -906,36 +938,42 @@ S_RawSamples
Music streaming
============
*/
-void S_Base_RawSamples( int samples, int rate, int width, int s_channels, const byte *data, float volume ) {
+void S_Base_RawSamples( int stream, int samples, int rate, int width, int s_channels, const byte *data, float volume ) {
int i;
int src, dst;
float scale;
int intVolume;
+ portable_samplepair_t *rawsamples;
if ( !s_soundStarted || s_soundMuted ) {
return;
}
+ if ( (stream < 0) || (stream >= MAX_RAW_STREAMS) ) {
+ return;
+ }
+ rawsamples = s_rawsamples[stream];
+
intVolume = 256 * volume;
- if ( s_rawend < s_soundtime ) {
- Com_DPrintf( "S_RawSamples: resetting minimum: %i < %i\n", s_rawend, s_soundtime );
- s_rawend = s_soundtime;
+ if ( s_rawend[stream] < s_soundtime ) {
+ Com_DPrintf( "S_RawSamples: resetting minimum: %i < %i\n", s_rawend[stream], s_soundtime );
+ s_rawend[stream] = s_soundtime;
}
scale = (float)rate / dma.speed;
-//Com_Printf ("%i < %i < %i\n", s_soundtime, s_paintedtime, s_rawend);
+//Com_Printf ("%i < %i < %i\n", s_soundtime, s_paintedtime, s_rawend[stream]);
if (s_channels == 2 && width == 2)
{
if (scale == 1.0)
{ // optimized case
for (i=0 ; i<samples ; i++)
{
- dst = s_rawend&(MAX_RAW_SAMPLES-1);
- s_rawend++;
- s_rawsamples[dst].left = ((short *)data)[i*2] * intVolume;
- s_rawsamples[dst].right = ((short *)data)[i*2+1] * intVolume;
+ dst = s_rawend[stream]&(MAX_RAW_SAMPLES-1);
+ s_rawend[stream]++;
+ rawsamples[dst].left = ((short *)data)[i*2] * intVolume;
+ rawsamples[dst].right = ((short *)data)[i*2+1] * intVolume;
}
}
else
@@ -945,10 +983,10 @@ void S_Base_RawSamples( int samples, int rate, int width, int s_channels, const
src = i*scale;
if (src >= samples)
break;
- dst = s_rawend&(MAX_RAW_SAMPLES-1);
- s_rawend++;
- s_rawsamples[dst].left = ((short *)data)[src*2] * intVolume;
- s_rawsamples[dst].right = ((short *)data)[src*2+1] * intVolume;
+ dst = s_rawend[stream]&(MAX_RAW_SAMPLES-1);
+ s_rawend[stream]++;
+ rawsamples[dst].left = ((short *)data)[src*2] * intVolume;
+ rawsamples[dst].right = ((short *)data)[src*2+1] * intVolume;
}
}
}
@@ -959,10 +997,10 @@ void S_Base_RawSamples( int samples, int rate, int width, int s_channels, const
src = i*scale;
if (src >= samples)
break;
- dst = s_rawend&(MAX_RAW_SAMPLES-1);
- s_rawend++;
- s_rawsamples[dst].left = ((short *)data)[src] * intVolume;
- s_rawsamples[dst].right = ((short *)data)[src] * intVolume;
+ dst = s_rawend[stream]&(MAX_RAW_SAMPLES-1);
+ s_rawend[stream]++;
+ rawsamples[dst].left = ((short *)data)[src] * intVolume;
+ rawsamples[dst].right = ((short *)data)[src] * intVolume;
}
}
else if (s_channels == 2 && width == 1)
@@ -974,10 +1012,10 @@ void S_Base_RawSamples( int samples, int rate, int width, int s_channels, const
src = i*scale;
if (src >= samples)
break;
- dst = s_rawend&(MAX_RAW_SAMPLES-1);
- s_rawend++;
- s_rawsamples[dst].left = ((char *)data)[src*2] * intVolume;
- s_rawsamples[dst].right = ((char *)data)[src*2+1] * intVolume;
+ dst = s_rawend[stream]&(MAX_RAW_SAMPLES-1);
+ s_rawend[stream]++;
+ rawsamples[dst].left = ((char *)data)[src*2] * intVolume;
+ rawsamples[dst].right = ((char *)data)[src*2+1] * intVolume;
}
}
else if (s_channels == 1 && width == 1)
@@ -989,15 +1027,15 @@ void S_Base_RawSamples( int samples, int rate, int width, int s_channels, const
src = i*scale;
if (src >= samples)
break;
- dst = s_rawend&(MAX_RAW_SAMPLES-1);
- s_rawend++;
- s_rawsamples[dst].left = (((byte *)data)[src]-128) * intVolume;
- s_rawsamples[dst].right = (((byte *)data)[src]-128) * intVolume;
+ dst = s_rawend[stream]&(MAX_RAW_SAMPLES-1);
+ s_rawend[stream]++;
+ rawsamples[dst].left = (((byte *)data)[src]-128) * intVolume;
+ rawsamples[dst].right = (((byte *)data)[src]-128) * intVolume;
}
}
- if ( s_rawend > s_soundtime + MAX_RAW_SAMPLES ) {
- Com_DPrintf( "S_RawSamples: overflowed %i > %i\n", s_rawend, s_soundtime );
+ if ( s_rawend[stream] > s_soundtime + MAX_RAW_SAMPLES ) {
+ Com_DPrintf( "S_RawSamples: overflowed %i > %i\n", s_rawend[stream], s_soundtime );
}
}
@@ -1274,7 +1312,7 @@ void S_Base_StopBackgroundTrack( void ) {
return;
S_CodecCloseStream(s_backgroundStream);
s_backgroundStream = NULL;
- s_rawend = 0;
+ s_rawend[0] = 0;
}
/*
@@ -1347,12 +1385,12 @@ void S_UpdateBackgroundTrack( void ) {
}
// see how many samples should be copied into the raw buffer
- if ( s_rawend < s_soundtime ) {
- s_rawend = s_soundtime;
+ if ( s_rawend[0] < s_soundtime ) {
+ s_rawend[0] = s_soundtime;
}
- while ( s_rawend < s_soundtime + MAX_RAW_SAMPLES ) {
- bufferSamples = MAX_RAW_SAMPLES - (s_rawend - s_soundtime);
+ while ( s_rawend[0] < s_soundtime + MAX_RAW_SAMPLES ) {
+ bufferSamples = MAX_RAW_SAMPLES - (s_rawend[0] - s_soundtime);
// decide how much data needs to be read from the file
fileSamples = bufferSamples * s_backgroundStream->info.rate / dma.speed;
@@ -1375,7 +1413,7 @@ void S_UpdateBackgroundTrack( void ) {
if(r > 0)
{
// add to raw buffer
- S_Base_RawSamples( fileSamples, s_backgroundStream->info.rate,
+ S_Base_RawSamples( 0, fileSamples, s_backgroundStream->info.rate,
s_backgroundStream->info.width, s_backgroundStream->info.channels, raw, musicVolume );
}
else
@@ -1509,5 +1547,13 @@ qboolean S_Base_Init( soundInterface_t *si ) {
si->SoundInfo = S_Base_SoundInfo;
si->SoundList = S_Base_SoundList;
+#ifdef USE_VOIP
+ si->StartCapture = S_Base_StartCapture;
+ si->AvailableCaptureSamples = S_Base_AvailableCaptureSamples;
+ si->Capture = S_Base_Capture;
+ si->StopCapture = S_Base_StopCapture;
+ si->MasterGain = S_Base_MasterGain;
+#endif
+
return qtrue;
}
diff --git a/src/client/snd_local.h b/src/client/snd_local.h
index 4f028a3d..a926e7f9 100644
--- a/src/client/snd_local.h
+++ b/src/client/snd_local.h
@@ -127,7 +127,7 @@ typedef struct
void (*StartLocalSound)( sfxHandle_t sfx, int channelNum );
void (*StartBackgroundTrack)( const char *intro, const char *loop );
void (*StopBackgroundTrack)( void );
- void (*RawSamples)(int samples, int rate, int width, int channels, const byte *data, float volume);
+ void (*RawSamples)(int stream, int samples, int rate, int width, int channels, const byte *data, float volume);
void (*StopAllSounds)( void );
void (*ClearLoopingSounds)( qboolean killall );
void (*AddLoopingSound)( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx );
@@ -143,6 +143,13 @@ typedef struct
void (*ClearSoundBuffer)( void );
void (*SoundInfo)( void );
void (*SoundList)( void );
+#ifdef USE_VOIP
+ void (*StartCapture)( void );
+ int (*AvailableCaptureSamples)( void );
+ void (*Capture)( int samples, byte *data );
+ void (*StopCapture)( void );
+ void (*MasterGain)( float gain );
+#endif
} soundInterface_t;
@@ -176,14 +183,15 @@ extern channel_t loop_channels[MAX_CHANNELS];
extern int numLoopChannels;
extern int s_paintedtime;
-extern int s_rawend;
extern vec3_t listener_forward;
extern vec3_t listener_right;
extern vec3_t listener_up;
extern dma_t dma;
#define MAX_RAW_SAMPLES 16384
-extern portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES];
+#define MAX_RAW_STREAMS 128
+extern portable_samplepair_t s_rawsamples[MAX_RAW_STREAMS][MAX_RAW_SAMPLES];
+extern int s_rawend[MAX_RAW_STREAMS];
extern cvar_t *s_volume;
extern cvar_t *s_musicVolume;
@@ -200,7 +208,6 @@ void SND_setup( void );
void S_PaintChannels(int endtime);
void S_memoryLoad(sfx_t *sfx);
-portable_samplepair_t *S_GetRawSamplePointer( void );
// spatializes a channel
void S_Spatialize(channel_t *ch);
diff --git a/src/client/snd_main.c b/src/client/snd_main.c
index b8dfdf36..d3391b23 100644
--- a/src/client/snd_main.c
+++ b/src/client/snd_main.c
@@ -64,6 +64,14 @@ static qboolean S_ValidSoundInterface( soundInterface_t *si )
if( !si->SoundInfo ) return qfalse;
if( !si->SoundList ) return qfalse;
+#ifdef USE_VOIP
+ if( !si->StartCapture ) return qfalse;
+ if( !si->AvailableCaptureSamples ) return qfalse;
+ if( !si->Capture ) return qfalse;
+ if( !si->StopCapture ) return qfalse;
+ if( !si->MasterGain ) return qfalse;
+#endif
+
return qtrue;
}
@@ -120,11 +128,11 @@ void S_StopBackgroundTrack( void )
S_RawSamples
=================
*/
-void S_RawSamples (int samples, int rate, int width, int channels,
+void S_RawSamples (int stream, int samples, int rate, int width, int channels,
const byte *data, float volume)
{
if( si.RawSamples ) {
- si.RawSamples( samples, rate, width, channels, data, volume );
+ si.RawSamples( stream, samples, rate, width, channels, data, volume );
}
}
@@ -319,6 +327,70 @@ void S_SoundList( void )
}
}
+
+#ifdef USE_VOIP
+/*
+=================
+S_StartCapture
+=================
+*/
+void S_StartCapture( void )
+{
+ if( si.StartCapture ) {
+ si.StartCapture( );
+ }
+}
+
+/*
+=================
+S_AvailableCaptureSamples
+=================
+*/
+int S_AvailableCaptureSamples( void )
+{
+ if( si.AvailableCaptureSamples ) {
+ return si.AvailableCaptureSamples( );
+ }
+ return 0;
+}
+
+/*
+=================
+S_Capture
+=================
+*/
+void S_Capture( int samples, byte *data )
+{
+ if( si.Capture ) {
+ si.Capture( samples, data );
+ }
+}
+
+/*
+=================
+S_StopCapture
+=================
+*/
+void S_StopCapture( void )
+{
+ if( si.StopCapture ) {
+ si.StopCapture( );
+ }
+}
+
+/*
+=================
+S_MasterGain
+=================
+*/
+void S_MasterGain( float gain )
+{
+ if( si.MasterGain ) {
+ si.MasterGain( gain );
+ }
+}
+#endif
+
//=============================================================================
/*
diff --git a/src/client/snd_mix.c b/src/client/snd_mix.c
index 46f3c6d0..fa2a841d 100644
--- a/src/client/snd_mix.c
+++ b/src/client/snd_mix.c
@@ -632,12 +632,12 @@ S_PaintChannels
void S_PaintChannels( int endtime ) {
int i;
int end;
+ int stream;
channel_t *ch;
sfx_t *sc;
int ltime, count;
int sampleOffset;
-
snd_vol = s_volume->value*255;
//Com_Printf ("%i to %i\n", s_paintedtime, endtime);
@@ -649,30 +649,18 @@ void S_PaintChannels( int endtime ) {
end = s_paintedtime + PAINTBUFFER_SIZE;
}
- // clear the paint buffer to either music or zeros
- if ( s_rawend < s_paintedtime ) {
- if ( s_rawend ) {
- //Com_DPrintf ("background sound underrun\n");
- }
- Com_Memset(paintbuffer, 0, (end - s_paintedtime) * sizeof(portable_samplepair_t));
- } else {
- // copy from the streaming sound source
- int s;
- int stop;
-
- stop = (end < s_rawend) ? end : s_rawend;
-
- for ( i = s_paintedtime ; i < stop ; i++ ) {
- s = i&(MAX_RAW_SAMPLES-1);
- paintbuffer[i-s_paintedtime] = s_rawsamples[s];
- }
-// if (i != end)
-// Com_Printf ("partial stream\n");
-// else
-// Com_Printf ("full stream\n");
- for ( ; i < end ; i++ ) {
- paintbuffer[i-s_paintedtime].left =
- paintbuffer[i-s_paintedtime].right = 0;
+ // clear the paint buffer and mix any raw samples...
+ Com_Memset(paintbuffer, 0, sizeof (paintbuffer));
+ for (stream = 0; stream < MAX_RAW_STREAMS; stream++) {
+ if ( s_rawend[stream] >= s_paintedtime ) {
+ // copy from the streaming sound source
+ const portable_samplepair_t *rawsamples = s_rawsamples[stream];
+ const int stop = (end < s_rawend[stream]) ? end : s_rawend[stream];
+ for ( i = s_paintedtime ; i < stop ; i++ ) {
+ const int s = i&(MAX_RAW_SAMPLES-1);
+ paintbuffer[i-s_paintedtime].left += rawsamples[s].left;
+ paintbuffer[i-s_paintedtime].right += rawsamples[s].right;
+ }
}
}
diff --git a/src/client/snd_openal.c b/src/client/snd_openal.c
index fee1d22e..5fad0a45 100644
--- a/src/client/snd_openal.c
+++ b/src/client/snd_openal.c
@@ -499,9 +499,9 @@ typedef struct src_s
} src_t;
#ifdef MACOS_X
- #define MAX_SRC 64
+ #define MAX_SRC 64
#else
- #define MAX_SRC 128
+ #define MAX_SRC 128
#endif
static src_t srcList[MAX_SRC];
static int srcCount = 0;
@@ -896,10 +896,13 @@ S_AL_UpdateEntityPosition
static
void S_AL_UpdateEntityPosition( int entityNum, const vec3_t origin )
{
- S_AL_SanitiseVector( (vec_t *)origin );
+ vec3_t sanOrigin;
+
+ VectorCopy( origin, sanOrigin );
+ S_AL_SanitiseVector( sanOrigin );
if ( entityNum < 0 || entityNum > MAX_GENTITIES )
Com_Error( ERR_DROP, "S_UpdateEntityPosition: bad entitynum %i", entityNum );
- VectorCopy( origin, entityList[entityNum].origin );
+ VectorCopy( sanOrigin, entityList[entityNum].origin );
}
/*
@@ -917,8 +920,8 @@ static qboolean S_AL_CheckInput(int entityNum, sfxHandle_t sfx)
if (sfx < 0 || sfx >= numSfx)
{
Com_Printf(S_COLOR_RED "ERROR: S_AL_CheckInput: handle %i out of range\n", sfx);
- return qtrue;
- }
+ return qtrue;
+ }
return qfalse;
}
@@ -1093,12 +1096,17 @@ S_AL_AddLoopingSound
static
void S_AL_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx )
{
+ vec3_t sanOrigin, sanVelocity;
+
if(S_AL_CheckInput(entityNum, sfx))
return;
- S_AL_SanitiseVector( (vec_t *)origin );
- S_AL_SanitiseVector( (vec_t *)velocity );
- S_AL_SrcLoop(SRCPRI_ENTITY, sfx, origin, velocity, entityNum);
+ VectorCopy( origin, sanOrigin );
+ VectorCopy( velocity, sanVelocity );
+ S_AL_SanitiseVector( sanOrigin );
+ S_AL_SanitiseVector( sanVelocity );
+
+ S_AL_SrcLoop(SRCPRI_ENTITY, sfx, sanOrigin, sanVelocity, entityNum);
}
/*
@@ -1109,20 +1117,24 @@ S_AL_AddRealLoopingSound
static
void S_AL_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx )
{
+ vec3_t sanOrigin, sanVelocity;
+
if(S_AL_CheckInput(entityNum, sfx))
return;
- S_AL_SanitiseVector( (vec_t *)origin );
- S_AL_SanitiseVector( (vec_t *)velocity );
+ VectorCopy( origin, sanOrigin );
+ VectorCopy( velocity, sanVelocity );
+ S_AL_SanitiseVector( sanOrigin );
+ S_AL_SanitiseVector( sanVelocity );
// There are certain maps (*cough* Q3:TA mpterra*) that have large quantities
// of ET_SPEAKERS in the PVS at any given time. OpenAL can't cope with mixing
// large numbers of sounds, so this culls them by distance
- if( DistanceSquared( origin, lastListenerOrigin ) > (s_alMaxDistance->value + s_alGraceDistance->value) *
+ if( DistanceSquared( sanOrigin, lastListenerOrigin ) > (s_alMaxDistance->value + s_alGraceDistance->value) *
(s_alMaxDistance->value + s_alGraceDistance->value) )
return;
- S_AL_SrcLoop(SRCPRI_AMBIENT, sfx, origin, velocity, entityNum);
+ S_AL_SrcLoop(SRCPRI_AMBIENT, sfx, sanOrigin, sanVelocity, entityNum);
}
/*
@@ -1264,35 +1276,37 @@ ALuint S_AL_SrcGet(srcHandle_t src)
//===========================================================================
-
-static srcHandle_t streamSourceHandle = -1;
-static qboolean streamPlaying = qfalse;
-static ALuint streamSource;
+static srcHandle_t streamSourceHandles[MAX_RAW_STREAMS];
+static qboolean streamPlaying[MAX_RAW_STREAMS];
+static ALuint streamSources[MAX_RAW_STREAMS];
/*
=================
S_AL_AllocateStreamChannel
=================
*/
-static void S_AL_AllocateStreamChannel( void )
+static void S_AL_AllocateStreamChannel( int stream )
{
+ if ((stream < 0) || (stream >= MAX_RAW_STREAMS))
+ return;
+
// Allocate a streamSource at high priority
- streamSourceHandle = S_AL_SrcAlloc(SRCPRI_STREAM, -2, 0);
- if(streamSourceHandle == -1)
+ streamSourceHandles[stream] = S_AL_SrcAlloc(SRCPRI_STREAM, -2, 0);
+ if(streamSourceHandles[stream] == -1)
return;
// Lock the streamSource so nobody else can use it, and get the raw streamSource
- S_AL_SrcLock(streamSourceHandle);
- streamSource = S_AL_SrcGet(streamSourceHandle);
+ S_AL_SrcLock(streamSourceHandles[stream]);
+ streamSources[stream] = S_AL_SrcGet(streamSourceHandles[stream]);
// Set some streamSource parameters
- qalSourcei (streamSource, AL_BUFFER, 0 );
- qalSourcei (streamSource, AL_LOOPING, AL_FALSE );
- qalSource3f(streamSource, AL_POSITION, 0.0, 0.0, 0.0);
- qalSource3f(streamSource, AL_VELOCITY, 0.0, 0.0, 0.0);
- qalSource3f(streamSource, AL_DIRECTION, 0.0, 0.0, 0.0);
- qalSourcef (streamSource, AL_ROLLOFF_FACTOR, 0.0 );
- qalSourcei (streamSource, AL_SOURCE_RELATIVE, AL_TRUE );
+ qalSourcei (streamSources[stream], AL_BUFFER, 0 );
+ qalSourcei (streamSources[stream], AL_LOOPING, AL_FALSE );
+ qalSource3f(streamSources[stream], AL_POSITION, 0.0, 0.0, 0.0);
+ qalSource3f(streamSources[stream], AL_VELOCITY, 0.0, 0.0, 0.0);
+ qalSource3f(streamSources[stream], AL_DIRECTION, 0.0, 0.0, 0.0);
+ qalSourcef (streamSources[stream], AL_ROLLOFF_FACTOR, 0.0 );
+ qalSourcei (streamSources[stream], AL_SOURCE_RELATIVE, AL_TRUE );
}
/*
@@ -1300,12 +1314,15 @@ static void S_AL_AllocateStreamChannel( void )
S_AL_FreeStreamChannel
=================
*/
-static void S_AL_FreeStreamChannel( void )
+static void S_AL_FreeStreamChannel( int stream )
{
+ if ((stream < 0) || (stream >= MAX_RAW_STREAMS))
+ return;
+
// Release the output streamSource
- S_AL_SrcUnlock(streamSourceHandle);
- streamSource = 0;
- streamSourceHandle = -1;
+ S_AL_SrcUnlock(streamSourceHandles[stream]);
+ streamSources[stream] = 0;
+ streamSourceHandles[stream] = -1;
}
/*
@@ -1314,20 +1331,23 @@ S_AL_RawSamples
=================
*/
static
-void S_AL_RawSamples(int samples, int rate, int width, int channels, const byte *data, float volume)
+void S_AL_RawSamples(int stream, int samples, int rate, int width, int channels, const byte *data, float volume)
{
ALuint buffer;
ALuint format;
+ if ((stream < 0) || (stream >= MAX_RAW_STREAMS))
+ return;
+
format = S_AL_Format( width, channels );
// Create the streamSource if necessary
- if(streamSourceHandle == -1)
+ if(streamSourceHandles[stream] == -1)
{
- S_AL_AllocateStreamChannel();
+ S_AL_AllocateStreamChannel(stream);
// Failed?
- if(streamSourceHandle == -1)
+ if(streamSourceHandles[stream] == -1)
{
Com_Printf( S_COLOR_RED "ERROR: Can't allocate streaming streamSource\n");
return;
@@ -1339,10 +1359,10 @@ void S_AL_RawSamples(int samples, int rate, int width, int channels, const byte
qalBufferData(buffer, format, (ALvoid *)data, (samples * width * channels), rate);
// Shove the data onto the streamSource
- qalSourceQueueBuffers(streamSource, 1, &buffer);
+ qalSourceQueueBuffers(streamSources[stream], 1, &buffer);
// Volume
- qalSourcef (streamSource, AL_GAIN, volume * s_volume->value * s_alGain->value);
+ qalSourcef (streamSources[stream], AL_GAIN, volume * s_volume->value * s_alGain->value);
}
/*
@@ -1351,40 +1371,43 @@ S_AL_StreamUpdate
=================
*/
static
-void S_AL_StreamUpdate( void )
+void S_AL_StreamUpdate( int stream )
{
int numBuffers;
ALint state;
- if(streamSourceHandle == -1)
+ if ((stream < 0) || (stream >= MAX_RAW_STREAMS))
+ return;
+
+ if(streamSourceHandles[stream] == -1)
return;
// Un-queue any buffers, and delete them
- qalGetSourcei( streamSource, AL_BUFFERS_PROCESSED, &numBuffers );
+ qalGetSourcei( streamSources[stream], AL_BUFFERS_PROCESSED, &numBuffers );
while( numBuffers-- )
{
ALuint buffer;
- qalSourceUnqueueBuffers(streamSource, 1, &buffer);
+ qalSourceUnqueueBuffers(streamSources[stream], 1, &buffer);
qalDeleteBuffers(1, &buffer);
}
// Start the streamSource playing if necessary
- qalGetSourcei( streamSource, AL_BUFFERS_QUEUED, &numBuffers );
+ qalGetSourcei( streamSources[stream], AL_BUFFERS_QUEUED, &numBuffers );
- qalGetSourcei(streamSource, AL_SOURCE_STATE, &state);
+ qalGetSourcei(streamSources[stream], AL_SOURCE_STATE, &state);
if(state == AL_STOPPED)
{
- streamPlaying = qfalse;
+ streamPlaying[stream] = qfalse;
// If there are no buffers queued up, release the streamSource
if( !numBuffers )
- S_AL_FreeStreamChannel( );
+ S_AL_FreeStreamChannel( stream );
}
- if( !streamPlaying && numBuffers )
+ if( !streamPlaying[stream] && numBuffers )
{
- qalSourcePlay( streamSource );
- streamPlaying = qtrue;
+ qalSourcePlay( streamSources[stream] );
+ streamPlaying[stream] = qtrue;
}
}
@@ -1394,14 +1417,17 @@ S_AL_StreamDie
=================
*/
static
-void S_AL_StreamDie( void )
+void S_AL_StreamDie( int stream )
{
- if(streamSourceHandle == -1)
+ if ((stream < 0) || (stream >= MAX_RAW_STREAMS))
return;
- streamPlaying = qfalse;
- qalSourceStop(streamSource);
- S_AL_FreeStreamChannel();
+ if(streamSourceHandles[stream] == -1)
+ return;
+
+ streamPlaying[stream] = qfalse;
+ qalSourceStop(streamSources[stream]);
+ S_AL_FreeStreamChannel(stream);
}
@@ -1693,6 +1719,11 @@ void S_AL_MusicUpdate( void )
static ALCdevice *alDevice;
static ALCcontext *alContext;
+#ifdef USE_VOIP
+static ALCdevice *alCaptureDevice;
+static cvar_t *s_alCapture;
+#endif
+
#ifdef _WIN32
#define ALDRIVER_DEFAULT "OpenAL32.dll"
#define ALDEVICE_DEFAULT "Generic Software"
@@ -1710,9 +1741,11 @@ S_AL_StopAllSounds
static
void S_AL_StopAllSounds( void )
{
+ int i;
S_AL_SrcShutup();
S_AL_StopBackgroundTrack();
- S_AL_StreamDie();
+ for (i = 0; i < MAX_RAW_STREAMS; i++)
+ S_AL_StreamDie(i);
}
/*
@@ -1753,11 +1786,14 @@ S_AL_Update
static
void S_AL_Update( void )
{
+ int i;
+
// Update SFX channels
S_AL_SrcUpdate();
// Update streams
- S_AL_StreamUpdate();
+ for (i = 0; i < MAX_RAW_STREAMS; i++)
+ S_AL_StreamUpdate(i);
S_AL_MusicUpdate();
// Doppler
@@ -1831,6 +1867,47 @@ void S_AL_SoundList( void )
{
}
+#ifdef USE_VOIP
+static
+void S_AL_StartCapture( void )
+{
+ if (alCaptureDevice != NULL)
+ qalcCaptureStart(alCaptureDevice);
+}
+
+static
+int S_AL_AvailableCaptureSamples( void )
+{
+ int retval = 0;
+ if (alCaptureDevice != NULL)
+ {
+ ALint samples = 0;
+ qalcGetIntegerv(alCaptureDevice, ALC_CAPTURE_SAMPLES, sizeof (samples), &samples);
+ retval = (int) samples;
+ }
+ return retval;
+}
+
+static
+void S_AL_Capture( int samples, byte *data )
+{
+ if (alCaptureDevice != NULL)
+ qalcCaptureSamples(alCaptureDevice, data, samples);
+}
+
+void S_AL_StopCapture( void )
+{
+ if (alCaptureDevice != NULL)
+ qalcCaptureStop(alCaptureDevice);
+}
+
+void S_AL_MasterGain( float gain )
+{
+ qalListenerf(AL_GAIN, gain);
+}
+#endif
+
+
/*
=================
S_AL_SoundInfo
@@ -1843,7 +1920,8 @@ void S_AL_SoundInfo( void )
Com_Printf( " Vendor: %s\n", qalGetString( AL_VENDOR ) );
Com_Printf( " Version: %s\n", qalGetString( AL_VERSION ) );
Com_Printf( " Renderer: %s\n", qalGetString( AL_RENDERER ) );
- Com_Printf( " Extensions: %s\n", qalGetString( AL_EXTENSIONS ) );
+ Com_Printf( " AL Extensions: %s\n", qalGetString( AL_EXTENSIONS ) );
+ Com_Printf( " ALC Extensions: %s\n", qalcGetString( NULL, ALC_EXTENSIONS ) );
if(qalcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT"))
{
Com_Printf(" Device: %s\n", qalcGetString(alDevice, ALC_DEVICE_SPECIFIER));
@@ -1860,7 +1938,9 @@ static
void S_AL_Shutdown( void )
{
// Shut down everything
- S_AL_StreamDie( );
+ int i;
+ for (i = 0; i < MAX_RAW_STREAMS; i++)
+ S_AL_StreamDie(i);
S_AL_StopBackgroundTrack( );
S_AL_SrcShutdown( );
S_AL_BufferShutdown( );
@@ -1868,6 +1948,21 @@ void S_AL_Shutdown( void )
qalcDestroyContext(alContext);
qalcCloseDevice(alDevice);
+#ifdef USE_VOIP
+ if (alCaptureDevice != NULL) {
+ qalcCaptureStop(alCaptureDevice);
+ qalcCaptureCloseDevice(alCaptureDevice);
+ alCaptureDevice = NULL;
+ Com_Printf( "OpenAL capture device closed.\n" );
+ }
+#endif
+
+ for (i = 0; i < MAX_RAW_STREAMS; i++) {
+ streamSourceHandles[i] = -1;
+ streamPlaying[i] = qfalse;
+ streamSources[i] = 0;
+ }
+
QAL_Shutdown();
}
@@ -1883,11 +1978,18 @@ qboolean S_AL_Init( soundInterface_t *si )
#ifdef USE_OPENAL
qboolean enumsupport, founddev = qfalse;
+ int i;
if( !si ) {
return qfalse;
}
+ for (i = 0; i < MAX_RAW_STREAMS; i++) {
+ streamSourceHandles[i] = -1;
+ streamPlaying[i] = qfalse;
+ streamSources[i] = 0;
+ }
+
// New console variables
s_alPrecache = Cvar_Get( "s_alPrecache", "1", CVAR_ARCHIVE );
s_alGain = Cvar_Get( "s_alGain", "0.4", CVAR_ARCHIVE );
@@ -1988,6 +2090,40 @@ qboolean S_AL_Init( soundInterface_t *si )
qalDopplerFactor( s_alDopplerFactor->value );
qalDopplerVelocity( s_alDopplerSpeed->value );
+#ifdef USE_VOIP
+ // !!! FIXME: some of these alcCaptureOpenDevice() values should be cvars.
+ // !!! FIXME: add support for capture device enumeration.
+ // !!! FIXME: add some better error reporting.
+ s_alCapture = Cvar_Get( "s_alCapture", "1", CVAR_ARCHIVE | CVAR_LATCH );
+ if (!s_alCapture->integer) {
+ Com_Printf("OpenAL capture support disabled by user ('+set s_alCapture 1' to enable)\n");
+#if USE_MUMBLE
+ } else if (cl_useMumble->integer) {
+ Com_Printf("OpenAL capture support disabled for Mumble support\n");
+#endif
+ } else {
+ // !!! FIXME: Apple has a 1.1-compliant OpenAL, which includes
+ // !!! FIXME: capture support, but they don't list it in the
+ // !!! FIXME: extension string. We need to check the version string,
+ // !!! FIXME: then the extension string, but that's too much trouble,
+ // !!! FIXME: so we'll just check the function pointer for now.
+ //if (qalcIsExtensionPresent(NULL, "ALC_EXT_capture")) {
+ if (qalcCaptureOpenDevice == NULL) {
+ Com_Printf("No ALC_EXT_capture support, can't record audio.\n");
+ } else {
+ // !!! 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",
+ qalcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER));
+ alCaptureDevice = qalcCaptureOpenDevice(NULL, 8000, AL_FORMAT_MONO16, 4096);
+ Com_Printf( "OpenAL capture device %s.\n",
+ (alCaptureDevice == NULL) ? "failed to open" : "opened");
+ }
+ }
+#endif
+
si->Shutdown = S_AL_Shutdown;
si->StartSound = S_AL_StartSound;
si->StartLocalSound = S_AL_StartLocalSound;
@@ -2010,6 +2146,14 @@ qboolean S_AL_Init( soundInterface_t *si )
si->SoundInfo = S_AL_SoundInfo;
si->SoundList = S_AL_SoundList;
+#ifdef USE_VOIP
+ si->StartCapture = S_AL_StartCapture;
+ si->AvailableCaptureSamples = S_AL_AvailableCaptureSamples;
+ si->Capture = S_AL_Capture;
+ si->StopCapture = S_AL_StopCapture;
+ si->MasterGain = S_AL_MasterGain;
+#endif
+
return qtrue;
#else
return qfalse;
diff --git a/src/client/snd_public.h b/src/client/snd_public.h
index d737412b..db1b8e64 100644
--- a/src/client/snd_public.h
+++ b/src/client/snd_public.h
@@ -34,7 +34,7 @@ void S_StopBackgroundTrack( void );
// cinematics and voice-over-network will send raw samples
// 1.0 volume will be direct output of source samples
-void S_RawSamples (int samples, int rate, int width, int channels,
+void S_RawSamples (int stream, int samples, int rate, int width, int channels,
const byte *data, float volume);
// stop all sounds and the background track
@@ -73,3 +73,13 @@ void S_ClearSoundBuffer( void );
void SNDDMA_Activate( void );
void S_UpdateBackgroundTrack( void );
+
+
+#ifdef USE_VOIP
+void S_StartCapture( void );
+int S_AvailableCaptureSamples( void );
+void S_Capture( int samples, byte *data );
+void S_StopCapture( void );
+void S_MasterGain( float gain );
+#endif
+