diff options
Diffstat (limited to 'src/client')
-rw-r--r-- | src/client/cl_cgame.c | 47 | ||||
-rw-r--r-- | src/client/cl_cin.c | 10 | ||||
-rw-r--r-- | src/client/cl_input.c | 108 | ||||
-rw-r--r-- | src/client/cl_keys.c | 2 | ||||
-rw-r--r-- | src/client/cl_main.c | 754 | ||||
-rw-r--r-- | src/client/cl_parse.c | 196 | ||||
-rw-r--r-- | src/client/cl_scrn.c | 77 | ||||
-rw-r--r-- | src/client/cl_ui.c | 90 | ||||
-rw-r--r-- | src/client/client.h | 81 | ||||
-rw-r--r-- | src/client/libmumblelink.c | 134 | ||||
-rw-r--r-- | src/client/libmumblelink.h | 26 | ||||
-rw-r--r-- | src/client/qal.c | 15 | ||||
-rw-r--r-- | src/client/snd_dma.c | 126 | ||||
-rw-r--r-- | src/client/snd_local.h | 15 | ||||
-rw-r--r-- | src/client/snd_main.c | 76 | ||||
-rw-r--r-- | src/client/snd_mix.c | 38 | ||||
-rw-r--r-- | src/client/snd_openal.c | 262 | ||||
-rw-r--r-- | src/client/snd_public.h | 12 |
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 + |