Initial patch for in-game VoIP support!

This commit is contained in:
Ryan C. Gordon 2008-06-01 07:51:23 +00:00
parent 0ee3960225
commit 12326a9eac
19 changed files with 1185 additions and 127 deletions

View file

@ -129,6 +129,10 @@ ifndef USE_MUMBLE
USE_MUMBLE=1
endif
ifndef USE_VOIP
USE_VOIP=1
endif
ifndef USE_LOCAL_HEADERS
USE_LOCAL_HEADERS=1
endif
@ -240,6 +244,10 @@ ifeq ($(PLATFORM),linux)
BASE_CFLAGS += -DUSE_MUMBLE
endif
ifeq ($(USE_VOIP),1)
BASE_CFLAGS += -DUSE_VOIP
endif
OPTIMIZE = -O3 -ffast-math -funroll-loops -fomit-frame-pointer
ifeq ($(ARCH),x86_64)
@ -295,6 +303,10 @@ ifeq ($(PLATFORM),linux)
CLIENT_LDFLAGS += -lrt
endif
ifeq ($(USE_VOIP),1)
CLIENT_LDFLAGS += -lspeex
endif
ifeq ($(ARCH),i386)
# linux32 make ...
BASE_CFLAGS += -m32
@ -362,6 +374,11 @@ ifeq ($(PLATFORM),darwin)
BASE_CFLAGS += -DUSE_MUMBLE
endif
ifeq ($(USE_VOIP),1)
BASE_CFLAGS += -DUSE_VOIP
CLIENT_LDFLAGS += -lspeex
endif
BASE_CFLAGS += -D_THREAD_SAFE=1
ifeq ($(USE_LOCAL_HEADERS),1)
@ -432,6 +449,10 @@ ifeq ($(PLATFORM),mingw32)
BASE_CFLAGS += -DUSE_MUMBLE
endif
ifeq ($(USE_VOIP),1)
BASE_CFLAGS += -DUSE_VOIP
endif
OPTIMIZE = -O3 -march=i586 -fno-omit-frame-pointer -ffast-math \
-falign-loops=2 -funroll-loops -falign-jumps=2 -falign-functions=2 \
-fstrength-reduce
@ -464,6 +485,10 @@ ifeq ($(PLATFORM),mingw32)
CLIENT_LDFLAGS += -lvorbisfile -lvorbis -logg
endif
ifeq ($(USE_VOIP),1)
CLIENT_LDFLAGS += -lspeex
endif
ifeq ($(ARCH),x86)
# build 32bit
BASE_CFLAGS += -m32
@ -521,6 +546,10 @@ ifeq ($(PLATFORM),freebsd)
BASE_CFLAGS += -DUSE_MUMBLE
endif
ifeq ($(USE_VOIP),1)
BASE_CFLAGS += -DUSE_VOIP
endif
ifeq ($(ARCH),axp)
BASE_CFLAGS += -DNO_VM_COMPILED
RELEASE_CFLAGS=$(BASE_CFLAGS) -DNDEBUG -O3 -ffast-math -funroll-loops \
@ -561,6 +590,10 @@ ifeq ($(PLATFORM),freebsd)
CLIENT_LDFLAGS += -lvorbisfile -lvorbis -logg
endif
ifeq ($(USE_VOIP),1)
CLIENT_LDFLAGS += -lspeex
endif
else # ifeq freebsd
@ -592,6 +625,10 @@ ifeq ($(PLATFORM),openbsd)
BASE_CFLAGS += -DUSE_MUMBLE
endif
ifeq ($(USE_VOIP),1)
BASE_CFLAGS += -DUSE_VOIP
endif
BASE_CFLAGS += -DNO_VM_COMPILED -I/usr/X11R6/include -I/usr/local/include
RELEASE_CFLAGS=$(BASE_CFLAGS) -DNDEBUG -O3 \
-march=pentium -fomit-frame-pointer -pipe -ffast-math \
@ -622,6 +659,10 @@ ifeq ($(PLATFORM),openbsd)
CLIENT_LDFLAGS += -lvorbisfile -lvorbis -logg
endif
ifeq ($(USE_VOIP),1)
CLIENT_LDFLAGS += -lspeex
endif
else # ifeq openbsd

View file

@ -916,6 +916,26 @@ void CL_FirstSnapshot( void ) {
Com_Printf("Mumble: Linking to Mumble application %s\n", ret==0?"ok":"failed");
}
#endif
#if 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);
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;
}
speex_encoder_ctl(clc.speexEncoder, SPEEX_GET_FRAME_SIZE,
&clc.speexFrameSize);
clc.speexInitialized = qtrue;
clc.voipMuteAll = qfalse;
Cmd_AddCommand ("voip", CL_Voip_f);
}
#endif
}
/*

View file

@ -53,8 +53,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 );
@ -1141,17 +1139,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:
@ -1478,7 +1476,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;
}

View file

@ -52,6 +52,10 @@ kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright;
kbutton_t in_strafe, in_speed;
kbutton_t in_up, in_down;
#if USE_VOIP
kbutton_t in_voiprecord;
#endif
kbutton_t in_buttons[16];
@ -216,6 +220,11 @@ void IN_SpeedUp(void) {IN_KeyUp(&in_speed);}
void IN_StrafeDown(void) {IN_KeyDown(&in_strafe);}
void IN_StrafeUp(void) {IN_KeyUp(&in_strafe);}
#if USE_VOIP
void IN_VoipRecordDown(void) {IN_KeyDown(&in_voiprecord);}
void IN_VoipRecordUp(void) {IN_KeyUp(&in_voiprecord);}
#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]);}
@ -547,6 +556,14 @@ usercmd_t CL_CreateCmd( void ) {
// get basic movement from joystick
CL_JoystickMove( &cmd );
#if USE_VOIP
if ( ( in_voiprecord.active ) && ( !cl_voipSend->integer ) ) {
Cvar_Set("cl_voipSend", "1");
} else if ( ( !in_voiprecord.active ) && ( cl_voipSend->integer ) ) {
Cvar_Set("cl_voipSend", "0");
}
#endif
// check to make sure the angles haven't wrapped
if ( cl.viewangles[PITCH] - oldAngles[PITCH] > 90 ) {
cl.viewangles[PITCH] = oldAngles[PITCH] + 90;
@ -740,6 +757,24 @@ void CL_WritePacket( void ) {
count = MAX_PACKET_USERCMDS;
Com_Printf("MAX_PACKET_USERCMDS\n");
}
#if USE_VOIP
if (clc.voipOutgoingDataSize > 0) { // only send if data.
MSG_WriteByte (&buf, clc_voip);
MSG_WriteByte (&buf, clc.voipOutgoingGeneration);
MSG_WriteLong (&buf, clc.voipOutgoingSequence);
MSG_WriteByte (&buf, clc.voipOutgoingDataFrames);
MSG_WriteLong (&buf, 0x7FFFFFFF); // !!! FIXME: send to specific people.
MSG_WriteLong (&buf, 0x7FFFFFFF); // !!! FIXME: send to specific people.
MSG_WriteLong (&buf, 0x7FFFFFFF); // !!! FIXME: send to specific people.
MSG_WriteShort (&buf, clc.voipOutgoingDataSize);
MSG_WriteData (&buf, clc.voipOutgoingData, clc.voipOutgoingDataSize);
clc.voipOutgoingSequence += clc.voipOutgoingDataFrames;
clc.voipOutgoingDataSize = 0;
clc.voipOutgoingDataFrames = 0;
} else
#endif
if ( count >= 1 ) {
if ( cl_showSend->integer ) {
Com_Printf( "(%i)", count );
@ -897,6 +932,11 @@ void CL_InitInput( void ) {
Cmd_AddCommand ("+mlook", IN_MLookDown);
Cmd_AddCommand ("-mlook", IN_MLookUp);
#if 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);
}

View file

@ -33,6 +33,12 @@ cvar_t *cl_useMumble;
cvar_t *cl_mumbleScale;
#endif
#if USE_VOIP
cvar_t *cl_voipSend;
cvar_t *cl_voipGainDuringCapture;
cvar_t *voip;
#endif
cvar_t *cl_nodelta;
cvar_t *cl_debugMove;
@ -168,6 +174,200 @@ void CL_UpdateMumble(void)
#endif
#if USE_VOIP
static
void CL_UpdateVoipIgnore(const char *idstr, qboolean ignore)
{
if ((*idstr >= '0') && (*idstr <= '9')) {
const int id = atoi(idstr);
if ((id >= 0) && (id < MAX_CLIENTS)) {
clc.voipIgnore[id] = ignore;
CL_AddReliableCommand(va("voip %s %d",
ignore ? "ignore" : "unignore", id));
Com_Printf("VoIP: %s ignoring player #%d\n",
ignore ? "Now" : "No longer", id);
}
}
}
void CL_Voip_f( void )
{
const char *cmd = Cmd_Argv(1);
const char *reason = NULL;
if (cls.state != CA_ACTIVE)
reason = "Not connected to a server";
else if (!clc.speexInitialized)
reason = "Speex not initialized";
else if (!cl_connectedToVoipServer)
reason = "Server doesn't support VoIP";
else if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive"))
reason = "running in single-player mode";
if (reason != NULL) {
Com_Printf("VoIP: command ignored: %s\n", reason);
return;
}
if (strcmp(cmd, "ignore") == 0) {
CL_UpdateVoipIgnore(Cmd_Argv(2), qtrue);
} else if (strcmp(cmd, "unignore") == 0) {
CL_UpdateVoipIgnore(Cmd_Argv(2), qfalse);
} else if (strcmp(cmd, "muteall") == 0) {
Com_Printf("VoIP: muting incoming voice\n");
CL_AddReliableCommand("voip muteall");
clc.voipMuteAll = qtrue;
} else if (strcmp(cmd, "unmuteall") == 0) {
Com_Printf("VoIP: unmuting incoming voice\n");
CL_AddReliableCommand("voip unmuteall");
clc.voipMuteAll = qfalse;
}
}
/*
===============
CL_CaptureVoip
Record more audio from the hardware if required and encode it into Speex
data for later transmission.
===============
*/
static
void CL_CaptureVoip(void)
{
qboolean initialFrame = qfalse;
qboolean finalFrame = qfalse;
#if USE_MUMBLE
// if we're using Mumble, don't try to handle VoIP transmission ourselves.
if (cl_useMumble->integer)
return;
#endif
if (!clc.speexInitialized)
return; // just in case this gets called at a bad time.
if (clc.voipOutgoingDataSize > 0)
return; // packet is pending transmission, don't record more yet.
if (cl_voipSend->modified) {
qboolean dontCapture = qfalse;
if (cls.state != CA_ACTIVE)
dontCapture = qtrue; // not connected to a server.
else if (!cl_connectedToVoipServer)
dontCapture = qtrue; // server doesn't support VoIP.
else if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive"))
dontCapture = qtrue; // single player game.
cl_voipSend->modified = qfalse;
if (dontCapture) {
cl_voipSend->integer = 0;
return;
}
if (cl_voipSend->integer) {
initialFrame = qtrue;
} else {
finalFrame = qtrue;
}
}
// try to get more audio data from the sound card...
if (initialFrame) {
float gain = cl_voipGainDuringCapture->value;
if (gain < 0.0f) gain = 0.0f; else if (gain >= 1.0f) gain = 1.0f;
S_MasterGain(cl_voipGainDuringCapture->value);
S_StartCapture();
clc.voipPower = 0.0f;
clc.voipOutgoingSequence = 0;
clc.voipOutgoingGeneration++;
if (clc.voipOutgoingGeneration == 0) // don't have a zero generation...
clc.voipOutgoingGeneration = 1; // ...so new clients won't match.
}
if ((cl_voipSend->integer) || (finalFrame)) { // user wants to capture audio?
// !!! FIXME: 8000, MONO16, 4096 samples are hardcoded in snd_openal.c
int samples = S_AvailableCaptureSamples();
const int mult = (finalFrame) ? 1 : 12; // 12 == 240ms of audio.
// enough data buffered in audio hardware to process yet?
if (samples >= (clc.speexFrameSize * mult)) {
// audio capture is always MONO16 (and that's what speex wants!).
static int16_t sampbuffer[4096]; // !!! FIXME: don't hardcode.
int16_t voipPower = 0;
int speexFrames = 0;
int wpos = 0;
int pos = 0;
if (samples > (clc.speexFrameSize * 12))
samples = (clc.speexFrameSize * 12);
// !!! FIXME: maybe separate recording from encoding, so voipPower
// !!! FIXME: updates faster than 4Hz?
samples -= samples % clc.speexFrameSize;
S_Capture(samples, (byte *) sampbuffer); // grab from audio card.
// this will probably generate multiple speex packets each time.
while (samples > 0) {
int i, bytes;
// Check the "power" of this packet...
for (i = 0; i < clc.speexFrameSize; i++) {
int16_t s = sampbuffer[i+pos];
if (s < 0)
s = -s;
if (s > voipPower)
voipPower = s; // !!! FIXME: this isn't very clever.
}
// Encode raw audio samples into Speex data...
speex_bits_reset(&clc.speexEncoderBits);
speex_encode_int(clc.speexEncoder, &sampbuffer[pos],
&clc.speexEncoderBits);
bytes = speex_bits_write(&clc.speexEncoderBits,
(char *) &clc.voipOutgoingData[wpos+1],
sizeof (clc.voipOutgoingData) - (wpos+1));
assert((bytes > 0) && (bytes < 256));
clc.voipOutgoingData[wpos] = (byte) bytes;
wpos += bytes + 1;
// look at the data for the next packet...
pos += clc.speexFrameSize;
samples -= clc.speexFrameSize;
speexFrames++;
}
clc.voipPower = ((float) voipPower) / 32767.0f;
clc.voipOutgoingDataSize = wpos;
clc.voipOutgoingDataFrames = speexFrames;
Com_DPrintf("Outgoing VoIP data: %d frames, %d bytes, %f power\n",
speexFrames, wpos, clc.voipPower);
#if 0
static FILE *encio = NULL;
if (encio == NULL) encio = fopen("outgoing-encoded.bin", "wb");
if (encio != NULL) { fwrite(clc.voipOutgoingData, wpos, 1, encio); fflush(encio); }
static FILE *decio = NULL;
if (decio == NULL) decio = fopen("outgoing-decoded.bin", "wb");
if (decio != NULL) { fwrite(sampbuffer, speexFrames * clc.speexFrameSize * 2, 1, decio); fflush(decio); }
#endif
}
}
// User requested we stop recording, and we've now processed the last of
// any previously-buffered data. Pause the capture device, etc.
if (finalFrame) {
S_StopCapture();
S_MasterGain(1.0f);
clc.voipPower = 0.0f; // force this value so it doesn't linger.
}
}
#endif
/*
=======================================================================
@ -905,6 +1105,25 @@ void CL_Disconnect( qboolean showMainMenu ) {
}
#endif
#if USE_VOIP
if (cl_voipSend->integer) {
Cvar_Set("cl_voipSend", "0");
CL_CaptureVoip(); // clean up any state...
}
if (clc.speexInitialized) {
int i;
speex_bits_destroy(&clc.speexEncoderBits);
speex_encoder_destroy(clc.speexEncoder);
for (i = 0; i < MAX_CLIENTS; i++) {
speex_bits_destroy(&clc.speexDecoderBits[i]);
speex_decoder_destroy(clc.speexDecoder[i]);
}
}
Cmd_RemoveCommand ("voip");
#endif
if ( clc.demofile ) {
FS_FCloseFile( clc.demofile );
clc.demofile = 0;
@ -939,6 +1158,11 @@ void CL_Disconnect( qboolean showMainMenu ) {
// not connected to a pure server anymore
cl_connectedToPureServer = qfalse;
#if USE_VOIP
// not connected to voip server anymore.
cl_connectedToVoipServer = qfalse;
#endif
// Stop recording any video
if( CL_VideoRecording( ) ) {
// Finish rendering current frame
@ -2359,9 +2583,14 @@ void CL_Frame ( int msec ) {
// update audio
S_Update();
#if USE_VOIP
CL_CaptureVoip();
#endif
#ifdef USE_MUMBLE
CL_UpdateMumble();
#endif
// advance local effects for next frame
SCR_RunCinematic();
@ -2781,6 +3010,12 @@ void CL_Init( void ) {
cl_mumbleScale = Cvar_Get ("cl_mumbleScale", "0.0254", CVAR_ARCHIVE);
#endif
#if USE_VOIP
cl_voipSend = Cvar_Get ("cl_voipSend", "0", 0);
cl_voipGainDuringCapture = Cvar_Get ("cl_voipGainDuringCapture", "0.2", CVAR_ARCHIVE);
voip = Cvar_Get ("voip", "0", CVAR_USERINFO | CVAR_ARCHIVE);
#endif
// userinfo
Cvar_Get ("name", "UnnamedPlayer", CVAR_USERINFO | CVAR_ARCHIVE );
Cvar_Get ("rate", "3000", CVAR_USERINFO | CVAR_ARCHIVE );

View file

@ -32,7 +32,12 @@ char *svc_strings[256] = {
"svc_baseline",
"svc_serverCommand",
"svc_download",
"svc_snapshot"
"svc_snapshot",
"svc_EOF",
#if USE_VOIP
"svc_voip"
#endif
};
void SHOWNET( msg_t *msg, char *s) {
@ -327,6 +332,10 @@ void CL_ParseSnapshot( msg_t *msg ) {
int cl_connectedToPureServer;
int cl_connectedToCheatServer;
#if USE_VOIP
int cl_connectedToVoipServer;
#endif
/*
==================
CL_SystemInfoChanged
@ -355,6 +364,11 @@ void CL_SystemInfoChanged( void ) {
return;
}
#if USE_VOIP
s = Info_ValueForKey( systemInfo, "sv_voip" );
cl_connectedToVoipServer = atoi( s );
#endif
s = Info_ValueForKey( systemInfo, "sv_cheats" );
cl_connectedToCheatServer = atoi( s );
if ( !cl_connectedToCheatServer ) {
@ -621,6 +635,162 @@ void CL_ParseDownload ( msg_t *msg ) {
}
}
#if USE_VOIP
static
qboolean CL_ShouldIgnoreVoipSender(int sender)
{
if (!voip->integer)
return qtrue; // VoIP is disabled.
else if (sender == clc.clientNum)
return qtrue; // this is us, don't output our own voice.
else if (clc.voipMuteAll)
return qtrue; // all channels are muted with extreme prejudice.
else if (clc.voipIgnore[sender])
return qtrue; // just ignoring this guy.
return qfalse; // !!! FIXME: implement per-channel muting.
}
/*
=====================
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, 8000, 2, 1,
(const byte *) decoded, 1.0f); // !!! FIXME: hardcoding!
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("incoming-encoded.bin", "wb");
if (encio != NULL) { fwrite(encoded, len, 1, encio); fflush(encio); }
static FILE *decio = NULL;
if (decio == NULL) decio = fopen("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, 8000, 2, 1,
(const byte *) decoded, 1.0f); // !!! FIXME: hardcoding!
}
clc.voipIncomingSequence[sender] = sequence + frames;
}
#endif
/*
=====================
CL_ParseCommandString
@ -714,6 +884,11 @@ void CL_ParseServerMessage( msg_t *msg ) {
case svc_download:
CL_ParseDownload( msg );
break;
#if USE_VOIP
case svc_voip:
CL_ParseVoip( msg );
break;
#endif
}
}
}

View file

@ -34,6 +34,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "cl_curl.h"
#endif /* USE_CURL */
#if USE_VOIP
#include "speex/speex.h"
#endif
// file full of random crap that gets used to create cl_guid
#define QKEY_FILE "qkey"
#define QKEY_SIZE 2048
@ -225,6 +229,30 @@ typedef struct {
int timeDemoMaxDuration; // maximum frame duration
unsigned char timeDemoDurations[ MAX_TIMEDEMO_DURATIONS ]; // log of frame durations
#if USE_VOIP
qboolean speexInitialized;
int speexFrameSize;
// 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];
qboolean voipIgnore[MAX_CLIENTS];
qboolean voipMuteAll;
// outgoing data...
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;
@ -372,6 +400,12 @@ extern cvar_t *cl_useMumble;
extern cvar_t *cl_mumbleScale;
#endif
#if USE_VOIP
extern cvar_t *cl_voipSend;
extern cvar_t *cl_voipGainDuringCapture;
extern cvar_t *voip;
#endif
//=================================================
//
@ -426,6 +460,10 @@ extern kbutton_t in_mlook, in_klook;
extern kbutton_t in_strafe;
extern kbutton_t in_speed;
#if USE_VOIP
extern kbutton_t in_voiprecord;
#endif
void CL_InitInput (void);
void CL_SendCmd (void);
void CL_ClearState (void);
@ -447,6 +485,11 @@ void Key_SetCatcher( int catcher );
extern int cl_connectedToPureServer;
extern int cl_connectedToCheatServer;
#if USE_VOIP
extern int cl_connectedToVoipServer;
void CL_Voip_f( void );
#endif
void CL_SystemInfoChanged( void );
void CL_ParseServerMessage( msg_t *msg );

View file

@ -90,8 +90,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];
// ====================================================================
@ -120,6 +120,42 @@ void S_Base_SoundInfo(void) {
Com_Printf("----------------------\n" );
}
#if 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
@ -608,7 +644,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;
@ -879,10 +915,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
@ -890,36 +922,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
@ -929,10 +967,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;
}
}
}
@ -943,10 +981,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)
@ -958,10 +996,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)
@ -973,15 +1011,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 );
}
}
@ -1258,7 +1296,7 @@ void S_Base_StopBackgroundTrack( void ) {
return;
S_CodecCloseStream(s_backgroundStream);
s_backgroundStream = NULL;
s_rawend = 0;
s_rawend[0] = 0;
}
/*
@ -1331,12 +1369,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;
@ -1359,7 +1397,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
@ -1492,5 +1530,13 @@ qboolean S_Base_Init( soundInterface_t *si ) {
si->SoundInfo = S_Base_SoundInfo;
si->SoundList = S_Base_SoundList;
#if 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;
}

View file

@ -125,7 +125,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 );
@ -140,6 +140,13 @@ typedef struct
void (*ClearSoundBuffer)( void );
void (*SoundInfo)( void );
void (*SoundList)( void );
#if USE_VOIP
void (*StartCapture)( void );
int (*AvailableCaptureSamples)( void );
void (*Capture)( int samples, byte *data );
void (*StopCapture)( void );
void (*MasterGain)( float gain );
#endif
} soundInterface_t;
@ -173,14 +180,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;
@ -197,7 +205,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);

View file

@ -62,6 +62,14 @@ static qboolean S_ValidSoundInterface( soundInterface_t *si )
if( !si->SoundInfo ) return qfalse;
if( !si->SoundList ) return qfalse;
#if 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;
}
@ -118,11 +126,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 );
}
}
@ -304,6 +312,70 @@ void S_SoundList( void )
}
}
#if 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
//=============================================================================
/*

View file

@ -631,12 +631,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);
@ -648,30 +648,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 {
// 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
int s;
int stop;
stop = (end < s_rawend) ? end : s_rawend;
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++ ) {
s = i&(MAX_RAW_SAMPLES-1);
paintbuffer[i-s_paintedtime] = s_rawsamples[s];
const int s = i&(MAX_RAW_SAMPLES-1);
paintbuffer[i-s_paintedtime].left += rawsamples[s].left;
paintbuffer[i-s_paintedtime].right += rawsamples[s].right;
}
// 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;
}
}

View file

@ -1253,35 +1253,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 );
}
/*
@ -1289,12 +1291,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;
}
/*
@ -1303,20 +1308,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;
@ -1328,10 +1336,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);
}
/*
@ -1340,40 +1348,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;
}
}
@ -1383,14 +1394,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);
}
@ -1682,6 +1696,11 @@ void S_AL_MusicUpdate( void )
static ALCdevice *alDevice;
static ALCcontext *alContext;
#if USE_VOIP
static ALCdevice *alCaptureDevice;
static cvar_t *s_alCapture;
#endif
#ifdef _WIN32
#define ALDRIVER_DEFAULT "OpenAL32.dll"
#define ALDEVICE_DEFAULT "Generic Software"
@ -1699,9 +1718,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);
}
/*
@ -1742,11 +1763,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
@ -1820,6 +1844,47 @@ void S_AL_SoundList( void )
{
}
#if 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
@ -1832,7 +1897,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));
@ -1849,7 +1915,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( );
@ -1857,6 +1925,21 @@ void S_AL_Shutdown( void )
qalcDestroyContext(alContext);
qalcCloseDevice(alDevice);
#if 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();
}
@ -1872,11 +1955,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 );
@ -1977,6 +2067,36 @@ qboolean S_AL_Init( soundInterface_t *si )
qalDopplerFactor( s_alDopplerFactor->value );
qalDopplerVelocity( s_alDopplerSpeed->value );
#if 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 );
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 {
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;
@ -1998,6 +2118,14 @@ qboolean S_AL_Init( soundInterface_t *si )
si->SoundInfo = S_AL_SoundInfo;
si->SoundList = S_AL_SoundList;
#if 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;

View file

@ -33,7 +33,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
@ -70,3 +70,13 @@ void S_ClearSoundBuffer( void );
void SNDDMA_Activate( void );
void S_UpdateBackgroundTrack( void );
#if 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

View file

@ -274,7 +274,11 @@ enum svc_ops_e {
svc_serverCommand, // [string] to be executed by client game module
svc_download, // [short] size [size bytes]
svc_snapshot,
svc_EOF
svc_EOF,
#if USE_VOIP
svc_voip
#endif
};
@ -287,7 +291,11 @@ enum clc_ops_e {
clc_move, // [[usercmd_t]
clc_moveNoDelta, // [[usercmd_t]
clc_clientCommand, // [string] message
clc_EOF
clc_EOF,
#if USE_VOIP
clc_voip, // packet of voice data.
#endif
};
/*

View file

@ -33,6 +33,18 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define MAX_ENT_CLUSTERS 16
#if USE_VOIP
typedef struct voipServerPacket_s
{
int generation;
int sequence;
int frames;
int len;
int sender;
byte data[1024];
} voipServerPacket_t;
#endif
typedef struct svEntity_s {
struct worldSector_s *worldSector;
struct svEntity_s *nextEntityInWorldSector;
@ -167,6 +179,14 @@ typedef struct client_s {
netchan_buffer_t *netchan_start_queue;
netchan_buffer_t **netchan_end_queue;
#if USE_VOIP
qboolean hasVoip;
qboolean muteAllVoip;
qboolean ignoreVoipFromClient[MAX_CLIENTS];
voipServerPacket_t voipPacket[64]; // !!! FIXME: WAY too much memory!
int queuedVoipPackets;
#endif
int oldServerTime;
qboolean csUpdated[MAX_CONFIGSTRINGS+1];
} client_t;
@ -264,6 +284,11 @@ extern cvar_t *sv_strictAuth;
extern serverBan_t serverBans[SERVER_MAXBANS];
extern int serverBansCount;
#if USE_VOIP
extern cvar_t *sv_voip;
#endif
//===========================================================
//
@ -320,6 +345,11 @@ void SV_ClientThink (client_t *cl, usercmd_t *cmd);
void SV_WriteDownloadToClient( client_t *cl , msg_t *msg );
#if USE_VOIP
void SV_WriteVoipToClient( client_t *cl, msg_t *msg );
#endif
//
// sv_ccmds.c
//

View file

@ -1083,6 +1083,50 @@ void SV_WriteDownloadToClient( client_t *cl , msg_t *msg )
}
}
#if USE_VOIP
/*
==================
SV_WriteVoipToClient
Check to see if there is any VoIP queued for a client, and send if there is.
==================
*/
void SV_WriteVoipToClient( client_t *cl, msg_t *msg )
{
voipServerPacket_t *packet = &cl->voipPacket[0];
int totalbytes = 0;
int i;
if (*cl->downloadName) {
cl->queuedVoipPackets = 0;
return; // no VoIP allowed if download is going, to save bandwidth.
}
// Write as many VoIP packets as we reasonably can...
for (i = 0; i < cl->queuedVoipPackets; i++, packet++) {
totalbytes += packet->len;
if (totalbytes > MAX_DOWNLOAD_BLKSIZE)
break;
MSG_WriteByte( msg, svc_voip );
MSG_WriteShort( msg, packet->sender );
MSG_WriteByte( msg, (byte) packet->generation );
MSG_WriteLong( msg, packet->sequence );
MSG_WriteByte( msg, packet->frames );
MSG_WriteShort( msg, packet->len );
MSG_WriteData( msg, packet->data, packet->len );
}
// !!! FIXME: I hate this queue system.
cl->queuedVoipPackets -= i;
if (cl->queuedVoipPackets > 0) {
memmove( &cl->voipPacket[0], &cl->voipPacket[i],
sizeof (voipServerPacket_t) * i);
}
}
#endif
/*
=================
SV_Disconnect_f
@ -1326,6 +1370,11 @@ void SV_UserinfoChanged( client_t *cl ) {
cl->snapshotMsec = 50;
}
#if USE_VOIP
val = Info_ValueForKey (cl->userinfo, "voip");
cl->hasVoip = (strlen(val) && atoi(val)) ? qtrue : qfalse;
#endif
// TTimo
// maintain the IP information
// the banning code relies on this being consistently present
@ -1361,6 +1410,39 @@ static void SV_UpdateUserinfo_f( client_t *cl ) {
VM_Call( gvm, GAME_CLIENT_USERINFO_CHANGED, cl - svs.clients );
}
#if USE_VOIP
static
void SV_UpdateVoipIgnore(client_t *cl, const char *idstr, qboolean ignore)
{
if ((*idstr >= '0') && (*idstr <= '9')) {
const int id = atoi(idstr);
if ((id >= 0) && (id < MAX_CLIENTS)) {
cl->ignoreVoipFromClient[id] = ignore;
}
}
}
/*
==================
SV_UpdateUserinfo_f
==================
*/
static void SV_Voip_f( client_t *cl ) {
const char *cmd = Cmd_Argv(1);
if (strcmp(cmd, "ignore") == 0) {
SV_UpdateVoipIgnore(cl, Cmd_Argv(2), qtrue);
} else if (strcmp(cmd, "unignore") == 0) {
SV_UpdateVoipIgnore(cl, Cmd_Argv(2), qfalse);
} else if (strcmp(cmd, "muteall") == 0) {
cl->muteAllVoip = qtrue;
} else if (strcmp(cmd, "unmuteall") == 0) {
cl->muteAllVoip = qfalse;
}
}
#endif
typedef struct {
char *name;
void (*func)( client_t *cl );
@ -1376,6 +1458,10 @@ static ucmd_t ucmds[] = {
{"stopdl", SV_StopDownload_f},
{"donedl", SV_DoneDownload_f},
#if USE_VOIP
{"voip", SV_Voip_f},
#endif
{NULL, NULL}
};
@ -1596,6 +1682,118 @@ static void SV_UserMove( client_t *cl, msg_t *msg, qboolean delta ) {
}
#if USE_VOIP
static
qboolean SV_ShouldIgnoreVoipSender(const client_t *cl)
{
if (!sv_voip->integer)
return qtrue; // VoIP disabled on this server.
else if (!cl->hasVoip) // client doesn't have VoIP support?!
return qtrue;
// !!! FIXME: implement player blacklist.
return qfalse; // don't ignore.
}
static
void SV_UserVoip( client_t *cl, msg_t *msg ) {
const int sender = (int) (cl - svs.clients);
const int generation = MSG_ReadByte(msg);
const int sequence = MSG_ReadLong(msg);
const int frames = MSG_ReadByte(msg);
const int recip1 = MSG_ReadLong(msg);
const int recip2 = MSG_ReadLong(msg);
const int recip3 = MSG_ReadLong(msg);
const int packetsize = MSG_ReadShort(msg);
byte encoded[sizeof (cl->voipPacket[0].data)];
client_t *client = NULL;
voipServerPacket_t *packet = NULL;
int i;
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 (recip1 < 0)
return; // short/invalid packet, bail.
else if (recip2 < 0)
return; // short/invalid packet, bail.
else if (recip3 < 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.
}
MSG_ReadData(msg, encoded, packetsize);
if (SV_ShouldIgnoreVoipSender(cl))
return; // Blacklisted, disabled, etc.
// !!! FIXME: see if we read past end of msg...
// !!! FIXME: reject if not speex narrowband codec.
// !!! FIXME: decide if this is bogus data?
// (the three recip* values are 31 bits each (ignores sign bit so we can
// get a -1 error from MSG_ReadLong() ... ), allowing for 93 clients.)
assert( sv_maxclients->integer < 93 );
// decide who needs this VoIP packet sent to them...
for (i = 0, client = svs.clients; i < sv_maxclients->integer ; i++, client++) {
if (client->state != CS_ACTIVE)
continue; // not in the game yet, don't send to this guy.
else if (i == sender)
continue; // don't send voice packet back to original author.
else if (!client->hasVoip)
continue; // no VoIP support, or support disabled.
else if (client->muteAllVoip)
continue; // client is ignoring everyone.
else if (client->ignoreVoipFromClient[sender])
continue; // client is ignoring this talker.
else if (*cl->downloadName) // !!! FIXME: possible to DoS?
continue; // no VoIP allowed if downloading, to save bandwidth.
else if ( ((i >= 0) && (i < 31)) && ((recip1 & (1 << (i-0))) == 0) )
continue; // not addressed to this player.
else if ( ((i >= 31) && (i < 62)) && ((recip2 & (1 << (i-31))) == 0) )
continue; // not addressed to this player.
else if ( ((i >= 62) && (i < 93)) && ((recip3 & (1 << (i-62))) == 0) )
continue; // not addressed to this player.
// Transmit this packet to the client.
// !!! FIXME: I don't like this queueing system.
if (client->queuedVoipPackets >= (sizeof (client->voipPacket) / sizeof (client->voipPacket[0]))) {
Com_Printf("Too many VoIP packets queued for client #%d\n", i);
continue; // no room for another packet right now.
}
packet = &client->voipPacket[client->queuedVoipPackets];
packet->sender = sender;
packet->frames = frames;
packet->len = packetsize;
packet->generation = generation;
packet->sequence = sequence;
memcpy(packet->data, encoded, packetsize);
client->queuedVoipPackets++;
}
}
#endif
/*
===========================================================================
@ -1699,6 +1897,10 @@ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) {
SV_UserMove( cl, msg, qtrue );
} else if ( c == clc_moveNoDelta ) {
SV_UserMove( cl, msg, qfalse );
#if USE_VOIP
} else if ( c == clc_voip ) {
SV_UserVoip( cl, msg );
#endif
} else if ( c != clc_EOF ) {
Com_Printf( "WARNING: bad command byte for client %i\n", (int) (cl - svs.clients) );
}

View file

@ -654,6 +654,9 @@ void SV_Init (void) {
Cvar_Get ("sv_cheats", "1", CVAR_SYSTEMINFO | CVAR_ROM );
sv_serverid = Cvar_Get ("sv_serverid", "0", CVAR_SYSTEMINFO | CVAR_ROM );
sv_pure = Cvar_Get ("sv_pure", "1", CVAR_SYSTEMINFO );
#if USE_VOIP
sv_voip = Cvar_Get ("sv_voip", "1", CVAR_SYSTEMINFO );
#endif
Cvar_Get ("sv_paks", "", CVAR_SYSTEMINFO | CVAR_ROM );
Cvar_Get ("sv_pakNames", "", CVAR_SYSTEMINFO | CVAR_ROM );
Cvar_Get ("sv_referencedPaks", "", CVAR_SYSTEMINFO | CVAR_ROM );

View file

@ -22,6 +22,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "server.h"
#if USE_VOIP
cvar_t *sv_voip;
#endif
serverStatic_t svs; // persistant server info
server_t sv; // local server
vm_t *gvm = NULL; // game virtual machine
@ -407,6 +411,10 @@ void SVC_Info( netadr_t from ) {
Info_SetValueForKey( infostring, "gametype", va("%i", sv_gametype->integer ) );
Info_SetValueForKey( infostring, "pure", va("%i", sv_pure->integer ) );
#if USE_VOIP
Info_SetValueForKey( infostring, "voip", va("%i", sv_voip->integer ) );
#endif
if( sv_minPing->integer ) {
Info_SetValueForKey( infostring, "minPing", va("%i", sv_minPing->integer) );
}

View file

@ -653,6 +653,10 @@ void SV_SendClientSnapshot( client_t *client ) {
// Add any download data if the client is downloading
SV_WriteDownloadToClient( client, &msg );
#if USE_VOIP
SV_WriteVoipToClient( client, &msg );
#endif
// check for overflow
if ( msg.overflowed ) {
Com_Printf ("WARNING: msg overflowed for %s\n", client->name);