mirror of
https://github.com/ioquake/ioq3.git
synced 2024-11-10 07:11:46 +00:00
Initial patch for in-game VoIP support!
This commit is contained in:
parent
0ee3960225
commit
12326a9eac
19 changed files with 1185 additions and 127 deletions
41
Makefile
41
Makefile
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
//=============================================================================
|
||||
|
||||
/*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -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
|
||||
//
|
||||
|
|
|
@ -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) );
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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) );
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue