diff options
Diffstat (limited to 'code/client')
| -rw-r--r-- | code/client/cl_cgame.c | 20 | ||||
| -rw-r--r-- | code/client/cl_cin.c | 10 | ||||
| -rw-r--r-- | code/client/cl_input.c | 40 | ||||
| -rw-r--r-- | code/client/cl_main.c | 235 | ||||
| -rw-r--r-- | code/client/cl_parse.c | 177 | ||||
| -rw-r--r-- | code/client/client.h | 43 | ||||
| -rw-r--r-- | code/client/snd_dma.c | 126 | ||||
| -rw-r--r-- | code/client/snd_local.h | 15 | ||||
| -rw-r--r-- | code/client/snd_main.c | 76 | ||||
| -rw-r--r-- | code/client/snd_mix.c | 38 | ||||
| -rw-r--r-- | code/client/snd_openal.c | 220 | ||||
| -rw-r--r-- | code/client/snd_public.h | 12 | 
12 files changed, 887 insertions, 125 deletions
| diff --git a/code/client/cl_cgame.c b/code/client/cl_cgame.c index 334294e..7ea1b1d 100644 --- a/code/client/cl_cgame.c +++ b/code/client/cl_cgame.c @@ -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  }  /* diff --git a/code/client/cl_cin.c b/code/client/cl_cin.c index 0e44eee..bb5be7e 100644 --- a/code/client/cl_cin.c +++ b/code/client/cl_cin.c @@ -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;  	} diff --git a/code/client/cl_input.c b/code/client/cl_input.c index f7e3920..a972536 100644 --- a/code/client/cl_input.c +++ b/code/client/cl_input.c @@ -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);  } diff --git a/code/client/cl_main.c b/code/client/cl_main.c index b9243d5..22c89c6 100644 --- a/code/client/cl_main.c +++ b/code/client/cl_main.c @@ -33,6 +33,12 @@ cvar_t	*cl_useMumble;  cvar_t	*cl_mumbleScale;  #endif +#if USE_VOIP +cvar_t	*cl_voipSend; +cvar_t	*cl_voipGainDuringCapture; +cvar_t	*voip; +#endif +  cvar_t	*cl_nodelta;  cvar_t	*cl_debugMove; @@ -168,6 +174,200 @@ void CL_UpdateMumble(void)  #endif +#if USE_VOIP +static +void CL_UpdateVoipIgnore(const char *idstr, qboolean ignore) +{ +	if ((*idstr >= '0') && (*idstr <= '9')) { +		const int id = atoi(idstr); +		if ((id >= 0) && (id < MAX_CLIENTS)) { +			clc.voipIgnore[id] = ignore; +			CL_AddReliableCommand(va("voip %s %d", +			                         ignore ? "ignore" : "unignore", id)); +			Com_Printf("VoIP: %s ignoring player #%d\n", +			            ignore ? "Now" : "No longer", id); +		} +	} +} + +void CL_Voip_f( void ) +{ +	const char *cmd = Cmd_Argv(1); +	const char *reason = NULL; + +	if (cls.state != CA_ACTIVE) +		reason = "Not connected to a server"; +	else if (!clc.speexInitialized) +		reason = "Speex not initialized"; +	else if (!cl_connectedToVoipServer) +		reason = "Server doesn't support VoIP"; +	else if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")) +		reason = "running in single-player mode"; + +	if (reason != NULL) { +		Com_Printf("VoIP: command ignored: %s\n", reason); +		return; +	} + +	if (strcmp(cmd, "ignore") == 0) { +		CL_UpdateVoipIgnore(Cmd_Argv(2), qtrue); +	} else if (strcmp(cmd, "unignore") == 0) { +		CL_UpdateVoipIgnore(Cmd_Argv(2), qfalse); +	} else if (strcmp(cmd, "muteall") == 0) { +		Com_Printf("VoIP: muting incoming voice\n"); +		CL_AddReliableCommand("voip muteall"); +		clc.voipMuteAll = qtrue; +	} else if (strcmp(cmd, "unmuteall") == 0) { +		Com_Printf("VoIP: unmuting incoming voice\n"); +		CL_AddReliableCommand("voip unmuteall"); +		clc.voipMuteAll = qfalse; +	} +} + + +/* +=============== +CL_CaptureVoip + +Record more audio from the hardware if required and encode it into Speex + data for later transmission. +=============== +*/ +static +void CL_CaptureVoip(void) +{ +	qboolean initialFrame = qfalse; +	qboolean finalFrame = qfalse; + +#if USE_MUMBLE +	// if we're using Mumble, don't try to handle VoIP transmission ourselves. +	if (cl_useMumble->integer) +		return; +#endif + +	if (!clc.speexInitialized) +		return;  // just in case this gets called at a bad time. + +	if (clc.voipOutgoingDataSize > 0) +		return;  // packet is pending transmission, don't record more yet. + +	if (cl_voipSend->modified) { +		qboolean dontCapture = qfalse; +		if (cls.state != CA_ACTIVE) +			dontCapture = qtrue;  // not connected to a server. +		else if (!cl_connectedToVoipServer) +			dontCapture = qtrue;  // server doesn't support VoIP. +		else if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")) +			dontCapture = qtrue;  // single player game. + +		cl_voipSend->modified = qfalse; + +		if (dontCapture) { +			cl_voipSend->integer = 0; +			return; +		} + +		if (cl_voipSend->integer) { +			initialFrame = qtrue; +		} else { +			finalFrame = qtrue; +		} +	} + +	// try to get more audio data from the sound card... + +	if (initialFrame) { +		float gain = cl_voipGainDuringCapture->value; +		if (gain < 0.0f) gain = 0.0f; else if (gain >= 1.0f) gain = 1.0f; +		S_MasterGain(cl_voipGainDuringCapture->value); +		S_StartCapture(); +		clc.voipPower = 0.0f; +		clc.voipOutgoingSequence = 0; +		clc.voipOutgoingGeneration++; +		if (clc.voipOutgoingGeneration == 0) // don't have a zero generation... +			clc.voipOutgoingGeneration = 1;  //  ...so new clients won't match. +	} + +	if ((cl_voipSend->integer) || (finalFrame)) { // user wants to capture audio? +		// !!! FIXME: 8000, MONO16, 4096 samples are hardcoded in snd_openal.c +		int samples = S_AvailableCaptureSamples(); +		const int mult = (finalFrame) ? 1 : 12; // 12 == 240ms of audio. + +		// enough data buffered in audio hardware to process yet? +		if (samples >= (clc.speexFrameSize * mult)) { +			// audio capture is always MONO16 (and that's what speex wants!). +			static int16_t sampbuffer[4096];  // !!! FIXME: don't hardcode. +			int16_t voipPower = 0; +			int speexFrames = 0; +			int wpos = 0; +			int pos = 0; + +			if (samples > (clc.speexFrameSize * 12)) +				samples = (clc.speexFrameSize * 12); + +			// !!! FIXME: maybe separate recording from encoding, so voipPower +			// !!! FIXME:  updates faster than 4Hz? + +			samples -= samples % clc.speexFrameSize; +			S_Capture(samples, (byte *) sampbuffer);  // grab from audio card. + +			// this will probably generate multiple speex packets each time. +			while (samples > 0) { +				int i, bytes; + +				// Check the "power" of this packet... +				for (i = 0; i < clc.speexFrameSize; i++) { +					int16_t s = sampbuffer[i+pos]; +					if (s < 0) +						s = -s; +					if (s > voipPower) +						voipPower = s;  // !!! FIXME: this isn't very clever. +				} + +				// Encode raw audio samples into Speex data... +				speex_bits_reset(&clc.speexEncoderBits); +				speex_encode_int(clc.speexEncoder, &sampbuffer[pos], +				                 &clc.speexEncoderBits); +				bytes = speex_bits_write(&clc.speexEncoderBits, +				                         (char *) &clc.voipOutgoingData[wpos+1], +				                         sizeof (clc.voipOutgoingData) - (wpos+1)); +				assert((bytes > 0) && (bytes < 256)); +				clc.voipOutgoingData[wpos] = (byte) bytes; +				wpos += bytes + 1; + +				// look at the data for the next packet... +				pos += clc.speexFrameSize; +				samples -= clc.speexFrameSize; +				speexFrames++; +			} +			clc.voipPower = ((float) voipPower) / 32767.0f; +			clc.voipOutgoingDataSize = wpos; +			clc.voipOutgoingDataFrames = speexFrames; + +			Com_DPrintf("Outgoing VoIP data: %d frames, %d bytes, %f power\n", +			            speexFrames, wpos, clc.voipPower); + +			#if 0 +			static FILE *encio = NULL; +			if (encio == NULL) encio = fopen("outgoing-encoded.bin", "wb"); +			if (encio != NULL) { fwrite(clc.voipOutgoingData, wpos, 1, encio); fflush(encio); } +			static FILE *decio = NULL; +			if (decio == NULL) decio = fopen("outgoing-decoded.bin", "wb"); +			if (decio != NULL) { fwrite(sampbuffer, speexFrames * clc.speexFrameSize * 2, 1, decio); fflush(decio); } +			#endif +		} +	} + +	// User requested we stop recording, and we've now processed the last of +	//  any previously-buffered data. Pause the capture device, etc. +	if (finalFrame) { +		S_StopCapture(); +		S_MasterGain(1.0f); +		clc.voipPower = 0.0f;  // force this value so it doesn't linger. +	} +} +#endif +  /*  ======================================================================= @@ -905,6 +1105,25 @@ void CL_Disconnect( qboolean showMainMenu ) {  	}  #endif +#if USE_VOIP +	if (cl_voipSend->integer) { +		Cvar_Set("cl_voipSend", "0"); +		CL_CaptureVoip();  // clean up any state... +	} + +	if (clc.speexInitialized) { +		int i; +		speex_bits_destroy(&clc.speexEncoderBits); +		speex_encoder_destroy(clc.speexEncoder); +		for (i = 0; i < MAX_CLIENTS; i++) { +			speex_bits_destroy(&clc.speexDecoderBits[i]); +			speex_decoder_destroy(clc.speexDecoder[i]); +		} +	} + +	Cmd_RemoveCommand ("voip"); +#endif +  	if ( clc.demofile ) {  		FS_FCloseFile( clc.demofile );  		clc.demofile = 0; @@ -939,6 +1158,11 @@ void CL_Disconnect( qboolean showMainMenu ) {  	// not connected to a pure server anymore  	cl_connectedToPureServer = qfalse; +#if USE_VOIP +	// not connected to voip server anymore. +	cl_connectedToVoipServer = qfalse; +#endif +  	// Stop recording any video  	if( CL_VideoRecording( ) ) {  		// Finish rendering current frame @@ -2359,9 +2583,14 @@ void CL_Frame ( int msec ) {  	// update audio  	S_Update(); +#if USE_VOIP +	CL_CaptureVoip(); +#endif +  #ifdef USE_MUMBLE  	CL_UpdateMumble();  #endif +  	// advance local effects for next frame  	SCR_RunCinematic(); @@ -2781,6 +3010,12 @@ void CL_Init( void ) {  	cl_mumbleScale = Cvar_Get ("cl_mumbleScale", "0.0254", CVAR_ARCHIVE);  #endif +#if USE_VOIP +	cl_voipSend = Cvar_Get ("cl_voipSend", "0", 0); +	cl_voipGainDuringCapture = Cvar_Get ("cl_voipGainDuringCapture", "0.2", CVAR_ARCHIVE); +	voip = Cvar_Get ("voip", "0", CVAR_USERINFO | CVAR_ARCHIVE); +#endif +  	// userinfo  	Cvar_Get ("name", "UnnamedPlayer", CVAR_USERINFO | CVAR_ARCHIVE );  	Cvar_Get ("rate", "3000", CVAR_USERINFO | CVAR_ARCHIVE ); diff --git a/code/client/cl_parse.c b/code/client/cl_parse.c index 72ae6ed..167b035 100644 --- a/code/client/cl_parse.c +++ b/code/client/cl_parse.c @@ -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  		}  	}  } diff --git a/code/client/client.h b/code/client/client.h index b064404..802e99f 100644 --- a/code/client/client.h +++ b/code/client/client.h @@ -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 ); diff --git a/code/client/snd_dma.c b/code/client/snd_dma.c index c1fb41e..0848fe4 100644 --- a/code/client/snd_dma.c +++ b/code/client/snd_dma.c @@ -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;  } diff --git a/code/client/snd_local.h b/code/client/snd_local.h index 3b45a99..1a8b5dc 100644 --- a/code/client/snd_local.h +++ b/code/client/snd_local.h @@ -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); diff --git a/code/client/snd_main.c b/code/client/snd_main.c index 2526336..3da9fd0 100644 --- a/code/client/snd_main.c +++ b/code/client/snd_main.c @@ -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 +  //=============================================================================  /* diff --git a/code/client/snd_mix.c b/code/client/snd_mix.c index 62e6841..cf0a999 100644 --- a/code/client/snd_mix.c +++ b/code/client/snd_mix.c @@ -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 { -			// copy from the streaming sound source -			int		s; -			int		stop; - -			stop = (end < s_rawend) ? end : s_rawend; - -			for ( i = s_paintedtime ; i < stop ; i++ ) { -				s = i&(MAX_RAW_SAMPLES-1); -				paintbuffer[i-s_paintedtime] = s_rawsamples[s]; -			} -//		if (i != end) -//			Com_Printf ("partial stream\n"); -//		else -//			Com_Printf ("full stream\n"); -			for ( ; i < end ; i++ ) { -				paintbuffer[i-s_paintedtime].left = -				paintbuffer[i-s_paintedtime].right = 0; +		// clear the paint buffer and mix any raw samples... +		Com_Memset(paintbuffer, 0, sizeof (paintbuffer)); +		for (stream = 0; stream < MAX_RAW_STREAMS; stream++) { +			if ( s_rawend[stream] >= s_paintedtime ) { +				// copy from the streaming sound source +				const portable_samplepair_t *rawsamples = s_rawsamples[stream]; +				const int stop = (end < s_rawend[stream]) ? end : s_rawend[stream]; +				for ( i = s_paintedtime ; i < stop ; i++ ) { +					const int s = i&(MAX_RAW_SAMPLES-1); +					paintbuffer[i-s_paintedtime].left += rawsamples[s].left; +					paintbuffer[i-s_paintedtime].right += rawsamples[s].right; +				}  			}  		} diff --git a/code/client/snd_openal.c b/code/client/snd_openal.c index 74be8f2..3295e3e 100644 --- a/code/client/snd_openal.c +++ b/code/client/snd_openal.c @@ -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; + +	if(streamSourceHandles[stream] == -1)  		return; -	streamPlaying = qfalse; -	qalSourceStop(streamSource); -	S_AL_FreeStreamChannel(); +	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; diff --git a/code/client/snd_public.h b/code/client/snd_public.h index 030c292..e1c2309 100644 --- a/code/client/snd_public.h +++ b/code/client/snd_public.h @@ -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 + | 
