<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">diff -rupN qrack190/cl_input.c qrack/cl_input.c
--- qrack190/cl_input.c	2008-09-29 14:15:16.000000000 +0200
+++ qrack/cl_input.c	2011-04-29 01:55:57.302064400 +0200
@@ -536,6 +536,10 @@ void CL_SendMove (usercmd_t *cmd)
 		CL_Disconnect ();
 	}
 	*/
+
+	/*Spike: Add voice data*/
+	S_Voip_Transmit(clcfte_voicechat, buf);
+
 	CL_SendLagMove();
 }
 
diff -rupN qrack190/cl_parse.c qrack/cl_parse.c
--- qrack190/cl_parse.c	2008-10-06 14:33:30.000000000 +0200
+++ qrack/cl_parse.c	2011-04-27 05:56:07.174550700 +0200
@@ -434,6 +434,33 @@ static int CL_WebDownloadProgress( doubl
 //    }
 //}
 //*/
+
+qboolean CL_ReadServerVersion(void)
+{
+	int i;
+	for (;;)
+	{
+		i = MSG_ReadLong ();
+
+		if (i == PROTOCOL_VERSION_FTE2)
+		{
+			i = MSG_ReadLong ();
+			if (i &amp; ~PROTOCOL_SUPPORTED_FTE2)
+				Host_Error ("Server is using unsupported ftepext2 flags - %x\n", i&amp;~PROTOCOL_SUPPORTED_FTE2);
+			cl.pext_fte2 = i;
+			continue;
+		}
+
+		if (i == PROTOCOL_VERSION)
+		{
+			return true;
+		}
+
+		Host_Error ("Server returned version %i(%c%c%c%c), not %i", i, (i&gt;&gt;0)&amp;0xff, (i&gt;&gt;8)&amp;0xff, (i&gt;&gt;16)&amp;0xff, (i&gt;&gt;24)&amp;0xff, PROTOCOL_VERSION);
+		return false;
+	}
+}
+
 /*
 ==================
 CL_ParseServerInfo
@@ -461,12 +488,8 @@ void CL_ParseServerInfo (void)
 	CL_ClearState ();
 
 // parse protocol version number
-	i = MSG_ReadLong ();
-	if (i != PROTOCOL_VERSION)
-	{
-		Con_Printf ("Server returned version %i, not %i", i, PROTOCOL_VERSION);
+	if (!CL_ReadServerVersion())
 		return;
-	}
 
 // parse maxclients
 	cl.maxclients = MSG_ReadByte ();
@@ -859,6 +882,9 @@ void CL_ParseServerInfo (void)
 	Hunk_Check ();			// make sure nothing is hurt
 	
 	noclip_anglehack = false;	// noclip is turned off at start
+
+	/*tell the voicechat code so it knows to ask the server to enable now*/
+	S_Voip_MapChange();
 }
 
 /*
@@ -1639,9 +1665,8 @@ void CL_ParseServerMessage (void)
 			break;
 		
 		case svc_version:
-			i = MSG_ReadLong ();
-			if (i != PROTOCOL_VERSION)
-				Host_Error ("CL_ParseServerMessage: Server is protocol %i instead of %i", i, PROTOCOL_VERSION);
+			if (!CL_ReadServerVersion())
+				break;
 			break;
 			
 		case svc_disconnect:
@@ -1722,7 +1747,10 @@ void CL_ParseServerMessage (void)
 				CL_ParseProQuakeMessage();
 			// Still want to add text, even on ProQuake messages.  This guarantees compatibility;
 			// unrecognized messages will essentially be ignored but there will be no parse errors
-			Cbuf_AddText (MSG_ReadString ());
+			str = MSG_ReadString();
+			if (!strcmp(str, "cmd pext\n"))
+				str = va("cmd pext 0x%x 0x%x"/*" 0x%x 0x%x"*/"\n", PROTOCOL_VERSION_FTE2, PROTOCOL_SUPPORTED_FTE2 /*, other version, other flags*/);
+			Cbuf_AddText (str);
 			break;
 			
 		case svc_damage:
@@ -1943,6 +1971,10 @@ void CL_ParseServerMessage (void)
 			Cvar_Set ("r_skybox", MSG_ReadString());
 			break;
 #endif
+
+		case svcfte_voicechat:
+			S_Voip_Parse();
+			break;
 		}
 	}
 }
diff -rupN qrack190/client.h qrack/client.h
--- qrack190/client.h	2008-09-29 14:15:16.000000000 +0200
+++ qrack/client.h	2011-04-27 05:07:04.701250900 +0200
@@ -291,6 +291,9 @@ typedef struct
 	double			last_loc_time;		// R00k
 	char			last_loc_name[32];	// R00k
 	double			laser_point_time;
+
+	/*Spike: protocol extensions in use*/
+	unsigned int    pext_fte2;
 } client_state_t;
 
 extern	client_state_t	cl;
diff -rupN qrack190/protocol.h qrack/protocol.h
--- qrack190/protocol.h	2008-09-29 14:15:46.000000000 +0200
+++ qrack/protocol.h	2011-04-27 05:53:10.726458500 +0200
@@ -21,6 +21,11 @@ Foundation, Inc., 59 Temple Place - Suit
 
 #define	PROTOCOL_VERSION	15
 
+/*Spike: protocol extensions layered on top of the underlying base protocol*/ 
+#define PROTOCOL_VERSION_FTE2			(('F'&lt;&lt;0) + ('T'&lt;&lt;8) + ('E'&lt;&lt;16) + ('2' &lt;&lt; 24))	//fte extensions.
+#define PROTOCOL_SUPPORTED_FTE2_VOIP    0x2
+#define PROTOCOL_SUPPORTED_FTE2         (PROTOCOL_SUPPORTED_FTE2_VOIP)
+
 // if the high bit of the servercmd is set, the low bits are fast update flags:
 #define	U_MOREBITS	(1&lt;&lt;0)
 #define	U_ORIGIN1	(1&lt;&lt;1)
@@ -136,6 +141,8 @@ Foundation, Inc., 59 Temple Place - Suit
 #define	svc_hidelmp		36	// [string] slotname
 #define	svc_skybox		37	// [string] skyname
 
+#define svcfte_voicechat	84 /*voicechat, part of FTE PEXT 2 extensions*/
+
 // client to server
 #define	clc_bad			0
 #define	clc_nop 		1
@@ -143,6 +150,8 @@ Foundation, Inc., 59 Temple Place - Suit
 #define	clc_move		3	// [usercmd_t]
 #define	clc_stringcmd	4	// [string] message
 
+#define clcfte_voicechat		83
+
 
 // JPG - added ProQuake commands
 #define pqc_nop			1
diff -rupN qrack190/sbar.c qrack/sbar.c
--- qrack190/sbar.c	2008-10-11 13:28:34.000000000 +0200
+++ qrack/sbar.c	2011-04-30 21:24:25.042367200 +0200
@@ -1457,13 +1457,14 @@ void Sbar_DeathmatchOverlay (void)
 		if (!s-&gt;name[0])
 			continue;
 
+		/*Spike: changed the scoreboard background colour so you can see who's speaking*/
 		if (k == cl.viewentity - 1)
 		{
-			Draw_AlphaFill (x - 63, y, 328 , 10, 20, scr_scoreboard_fillalpha.value);
+			Draw_AlphaFill (x - 63, y, 328 , 10, S_Voip_Speaking(k)?75:20, scr_scoreboard_fillalpha.value);
 		}
 		else
 		{
-			Draw_AlphaFill (x - 63, y, 328 , 10, 18, scr_scoreboard_fillalpha.value);	
+			Draw_AlphaFill (x - 63, y, 328 , 10, S_Voip_Speaking(k)?73:18, scr_scoreboard_fillalpha.value);	
 		}
 		
 		Draw_Fill (x - 64, y, 1, 10, 0);	//Border - Left
diff -rupN qrack190/server.h qrack/server.h
--- qrack190/server.h	2008-09-29 14:16:38.000000000 +0200
+++ qrack/server.h	2011-04-27 05:36:31.646314300 +0200
@@ -115,6 +115,28 @@ typedef struct client_s
 
 	// JPG 3.30 - allow clients to connect if they don't have the map
 	qboolean	nomap;
+
+	/* Spike - extension querying support */
+	qboolean     pext_unknown;
+	unsigned int pext_fte;
+	unsigned int pext_fte2;
+
+	/* Spike - voice chat support */
+	unsigned int voice_read;	/*place in ring*/
+	unsigned char voice_mute[MAX_SCOREBOARD/8]; /*user choice*/
+	qboolean voice_active;
+	enum
+	{
+		/*note - when recording an mvd, only 'all' will be received by non-spectating viewers. all other chat will only be heard when spectating the receiver(or sender) of said chat*/
+
+		/*should we add one to respond to the last speaker? or should that be an automagic +voip_reply instead?*/
+		VT_TEAM,
+		VT_ALL,
+		VT_NONMUTED,	/*cheap, but allows custom private channels with no external pesters*/
+		VT_PLAYERSLOT0
+		/*player0+...*/
+	} voice_target;
+	qboolean		ismuted; /*admin/server control*/
 } client_t;
 
 
@@ -253,3 +275,4 @@ void SV_CheckForNewClients (void);
 void SV_RunClients (void);
 void SV_SaveSpawnparms ();
 void SV_SpawnServer (char *server);
+void SV_SendServerinfo (client_t *client);
diff -rupN qrack190/snd_dma.c qrack/snd_dma.c
--- qrack190/snd_dma.c	2008-09-29 14:16:40.000000000 +0200
+++ qrack/snd_dma.c	2011-04-29 02:00:22.191324500 +0200
@@ -192,6 +192,9 @@ void S_Init (void)
 		Con_Printf ("loading all sounds as 8bit\n");
 	}
 
+	/*Spike: gotta call this from somewhere*/
+	S_Voip_Init();
+
 	snd_initialized = true;
 
 	S_Startup ();
diff -rupN qrack190/snd_mix.c qrack/snd_mix.c
--- qrack190/snd_mix.c	2008-09-29 14:16:40.000000000 +0200
+++ qrack/snd_mix.c	2011-04-27 05:26:33.934127200 +0200
@@ -37,6 +37,8 @@ int		snd_scaletable[32][256];
 int 	*snd_p, snd_linear_count, snd_vol;
 short	*snd_out;
 
+float voicevolumemod = 256;
+
 void Snd_WriteLinearBlastStereo16 (void);
 
 #if !id386
@@ -64,7 +66,7 @@ void S_TransferStereo16 (int endtime)
 	HRESULT	hresult;
 #endif
 
-	snd_vol = volume.value * 256;
+	snd_vol = volume.value * voicevolumemod;
 
 	snd_p = (int *)paintbuffer;
 	lpaintedtime = paintedtime;
@@ -150,7 +152,7 @@ void S_TransferPaintBuffer(int endtime)
 	out_mask = shm-&gt;samples - 1; 
 	out_idx = paintedtime * shm-&gt;channels &amp; out_mask;
 	step = 3 - shm-&gt;channels;
-	snd_vol = volume.value*256;
+	snd_vol = volume.value*voicevolumemod;
 
 #ifdef _WIN32
 	if (pDSBuf)
diff -rupN qrack190/snd_voip.c qrack/snd_voip.c
--- qrack190/snd_voip.c	1970-01-01 01:00:00.000000000 +0100
+++ qrack/snd_voip.c	2011-04-30 22:26:13.679559400 +0200
@@ -0,0 +1,1801 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+
+/*
+authorship and stuff:
+Audio resampling functions:   Originally from DarkPlaces, presumably LordHavoc is the original author.
+Raw Audio Capture (DSound):   Spike.
+Networking:                   Spike.
+Raw Audio Playback/Streaming: qqshka: This code is taken straight from ezQuake.
+Codec:                        The Speex team.
+
+Compatibility:
+This code requires the engine to know if the peer also supports the extension.
+Servers send a 'cmd pext\n' stufftext. The client intercepts this and replaces it with a 'cmd pext KEY VALUE ...\n' command.
+When the server receives the extended pext command, it reads the key+value pairs, and tells the client the extensions that be used for the connection.
+No voice commands nor packets will be sent which the other is unable to understand. The exception is in the form of demos. Such demos will require the replaying client to understand the data.
+
+
+*/
+
+
+
+#include "quakedef.h"
+
+/*****************************************************************************************************************************/
+/*System componant (should be inside sys_win.c, only win32 supported)*/
+typedef struct {
+	void **funcptr;
+	char *name;
+} dllfunction_t;
+typedef void *dllhandle_t;
+dllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs);
+void Sys_CloseLibrary(dllhandle_t *lib);
+
+#ifdef _WIN32
+#include &lt;windows.h&gt;
+void Sys_CloseLibrary(dllhandle_t *lib)
+{
+	FreeLibrary((HMODULE)lib);
+}
+dllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs)
+{
+	int i;
+	HMODULE lib;
+
+	lib = LoadLibrary(name);
+	if (!lib)
+	{
+#ifdef _WIN64
+		lib = LoadLibrary(va("%s_64", name));
+#elif defined(_WIN32)
+		lib = LoadLibrary(va("%s_32", name));
+#endif
+		if (!lib)
+			return NULL;
+	}
+
+	if (funcs)
+	{
+		for (i = 0; funcs[i].name; i++)
+		{
+			*funcs[i].funcptr = GetProcAddress(lib, funcs[i].name);
+			if (!*funcs[i].funcptr)
+				break;
+		}
+		if (funcs[i].name)
+		{
+			Sys_CloseLibrary((dllhandle_t*)lib);
+			lib = NULL;
+		}
+	}
+
+	return (dllhandle_t*)lib;
+}
+#endif
+
+
+/*****************************************************************************************************************************/
+/*audio capture componant. only dsound supported in this implementation.*/
+
+/*sound capture driver stuff*/
+typedef struct
+{
+	void *(*Init) (int samplerate);			/*create a new context*/
+	void (*Start) (void *ctx);		/*begin grabbing new data, old data is potentially flushed*/
+	unsigned int (*Update) (void *ctx, unsigned char *buffer, unsigned int minbytes, unsigned int maxbytes);	/*grab the data into a different buffer*/
+	void (*Stop) (void *ctx);		/*stop grabbing new data, old data may remain*/
+	void (*Shutdown) (void *ctx);	/*destroy everything*/
+} snd_capture_driver_t;
+
+#include &lt;dsound.h&gt;
+static HINSTANCE hInstDSC;
+static HRESULT (WINAPI *pDirectSoundCaptureCreate)(GUID FAR *lpGUID, LPDIRECTSOUNDCAPTURE FAR *lplpDS, IUnknown FAR *pUnkOuter);
+
+typedef struct
+{
+	LPDIRECTSOUNDCAPTURE DSCapture;
+	LPDIRECTSOUNDCAPTUREBUFFER DSCaptureBuffer;
+	long lastreadpos;
+} dsndcapture_t;
+static const long bufferbytes = 1024*1024;
+
+static const long inputwidth = 2;
+
+void *DSOUND_Capture_Init (int rate)
+{
+	dsndcapture_t *result;
+	DSCBUFFERDESC bufdesc;
+
+	WAVEFORMATEX  wfxFormat;
+
+	wfxFormat.wFormatTag = WAVE_FORMAT_PCM;
+    wfxFormat.nChannels = 1;
+    wfxFormat.nSamplesPerSec = rate;
+	wfxFormat.wBitsPerSample = 8*inputwidth;
+    wfxFormat.nBlockAlign = wfxFormat.nChannels * (wfxFormat.wBitsPerSample / 8);
+	wfxFormat.nAvgBytesPerSec = wfxFormat.nSamplesPerSec * wfxFormat.nBlockAlign;
+    wfxFormat.cbSize = 0;
+
+	bufdesc.dwSize = sizeof(bufdesc);
+	bufdesc.dwBufferBytes = bufferbytes;
+	bufdesc.dwFlags = 0;
+	bufdesc.dwReserved = 0;
+	bufdesc.lpwfxFormat = &amp;wfxFormat;
+
+	/*probably already inited*/
+	if (!hInstDSC)
+	{
+		hInstDSC = LoadLibrary("dsound.dll");
+
+		if (hInstDSC == NULL)
+		{
+			Con_SafePrintf ("Couldn't load dsound.dll\n");
+			return NULL;
+		}
+	}
+	/*global pointer, used only in this function*/
+	if (!pDirectSoundCaptureCreate)
+	{
+		pDirectSoundCaptureCreate = (void *)GetProcAddress(hInstDSC, "DirectSoundCaptureCreate");
+
+		if (!pDirectSoundCaptureCreate)
+		{
+			Con_SafePrintf ("Couldn't get DS proc addr\n");
+			return NULL;
+		}
+
+//		pDirectSoundCaptureEnumerate = (void *)GetProcAddress(hInstDS,"DirectSoundCaptureEnumerateA");
+	}
+
+	result = Z_Malloc(sizeof(*result));
+	if (!FAILED(pDirectSoundCaptureCreate(NULL, &amp;result-&gt;DSCapture, NULL)))
+	{
+		if (!FAILED(IDirectSoundCapture_CreateCaptureBuffer(result-&gt;DSCapture, &amp;bufdesc, &amp;result-&gt;DSCaptureBuffer, NULL)))
+		{
+			return result;
+		}
+		IDirectSoundCapture_Release(result-&gt;DSCapture);
+		Con_SafePrintf ("Couldn't create a capture buffer\n");
+	}
+	Z_Free(result);
+	return NULL;
+}
+
+void DSOUND_Capture_Start(void *ctx)
+{
+	DWORD capturePos;
+	dsndcapture_t *c = ctx;
+	IDirectSoundCaptureBuffer_Start(c-&gt;DSCaptureBuffer, DSBPLAY_LOOPING);
+
+	c-&gt;lastreadpos = 0;
+	IDirectSoundCaptureBuffer_GetCurrentPosition(c-&gt;DSCaptureBuffer, &amp;capturePos, &amp;c-&gt;lastreadpos);
+}
+
+void DSOUND_Capture_Stop(void *ctx)
+{
+	dsndcapture_t *c = ctx;
+	IDirectSoundCaptureBuffer_Stop(c-&gt;DSCaptureBuffer);
+}
+
+void DSOUND_Capture_Shutdown(void *ctx)
+{
+	dsndcapture_t *c = ctx;
+	if (c-&gt;DSCaptureBuffer)
+	{
+		IDirectSoundCaptureBuffer_Stop(c-&gt;DSCaptureBuffer);
+		IDirectSoundCaptureBuffer_Release(c-&gt;DSCaptureBuffer);
+	}
+	if (c-&gt;DSCapture)
+	{
+		IDirectSoundCapture_Release(c-&gt;DSCapture);
+	}
+	Z_Free(ctx);
+}
+
+/*minsamples is a hint*/
+unsigned int DSOUND_Capture_Update(void *ctx, unsigned char *buffer, unsigned int minbytes, unsigned int maxbytes)
+{
+	dsndcapture_t *c = ctx;
+	HRESULT hr;
+	LPBYTE lpbuf1 = NULL;
+	LPBYTE lpbuf2 = NULL;
+	DWORD dwsize1 = 0;
+	DWORD dwsize2 = 0;
+
+	DWORD capturePos;
+	DWORD readPos;
+	long  filled;
+
+// Query to see how much data is in buffer.
+	hr = IDirectSoundCaptureBuffer_GetCurrentPosition(c-&gt;DSCaptureBuffer, &amp;capturePos, &amp;readPos);
+	if (hr != DS_OK)
+	{
+		return 0;
+	}
+	filled = readPos - c-&gt;lastreadpos;
+	if (filled &lt; 0)
+		filled += bufferbytes; // unwrap offset
+
+	if (filled &gt; maxbytes)	//figure out how much we need to empty it by, and if that's enough to be worthwhile.
+		filled = maxbytes;
+	else if (filled &lt; minbytes)
+		return 0;
+
+//	filled /= inputwidth;
+//	filled *= inputwidth;
+
+	// Lock free space in the DS
+	hr = IDirectSoundCaptureBuffer_Lock(c-&gt;DSCaptureBuffer, c-&gt;lastreadpos, filled, (void **) &amp;lpbuf1, &amp;dwsize1, (void **) &amp;lpbuf2, &amp;dwsize2, 0);
+	if (hr == DS_OK)
+	{
+		// Copy from DS to the buffer
+		memcpy(buffer, lpbuf1, dwsize1);
+		if(lpbuf2 != NULL)
+		{
+			memcpy(buffer+dwsize1, lpbuf2, dwsize2);
+		}
+		// Update our buffer offset and unlock sound buffer
+ 		c-&gt;lastreadpos = (c-&gt;lastreadpos + dwsize1 + dwsize2) % bufferbytes;
+		IDirectSoundCaptureBuffer_Unlock(c-&gt;DSCaptureBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
+	}
+	else
+	{
+		return 0;
+	}
+	return filled;
+}
+snd_capture_driver_t DSOUND_Capture =
+{
+	DSOUND_Capture_Init,
+	DSOUND_Capture_Start,
+	DSOUND_Capture_Update,
+	DSOUND_Capture_Stop,
+	DSOUND_Capture_Shutdown
+};
+
+
+/*****************************************************************************************************************************/
+
+/*Mixer stuff*/
+extern float voicevolumemod; /*normal = 256 - var defined/used in snd_mix.c*/
+
+
+
+
+
+#define LINEARUPSCALE(in, inrate, insamps, out, outrate, outlshift, outrshift) \
+		{ \
+			scale = inrate / (double)outrate; \
+			infrac = floor(scale * 65536); \
+			outsamps = insamps / scale; \
+			inaccum = 0; \
+			outnlsamps = floor(1.0 / scale); \
+			outsamps -= outnlsamps; \
+			\
+			while (outsamps) \
+			{ \
+				*out = ((0xFFFF - inaccum)*in[0] + inaccum*in[1]) &gt;&gt; (16 - outlshift + outrshift); \
+				inaccum += infrac; \
+				in += (inaccum &gt;&gt; 16); \
+				inaccum &amp;= 0xFFFF; \
+				out++; \
+				outsamps--; \
+			} \
+			while (outnlsamps) \
+			{ \
+				*out = (*in &gt;&gt; outrshift) &lt;&lt; outlshift; \
+				out++; \
+				outnlsamps--; \
+			} \
+		}
+
+#define LINEARUPSCALESTEREO(in, inrate, insamps, out, outrate, outlshift, outrshift) \
+		{ \
+			scale = inrate / (double)outrate; \
+			infrac = floor(scale * 65536); \
+			outsamps = insamps / scale; \
+			inaccum = 0; \
+			outnlsamps = floor(1.0 / scale); \
+			outsamps -= outnlsamps; \
+			\
+			while (outsamps) \
+			{ \
+				out[0] = ((0xFFFF - inaccum)*in[0] + inaccum*in[2]) &gt;&gt; (16 - outlshift + outrshift); \
+				out[1] = ((0xFFFF - inaccum)*in[1] + inaccum*in[3]) &gt;&gt; (16 - outlshift + outrshift); \
+				inaccum += infrac; \
+				in += (inaccum &gt;&gt; 16) * 2; \
+				inaccum &amp;= 0xFFFF; \
+				out += 2; \
+				outsamps--; \
+			} \
+			while (outnlsamps) \
+			{ \
+				out[0] = (in[0] &gt;&gt; outrshift) &lt;&lt; outlshift; \
+				out[1] = (in[1] &gt;&gt; outrshift) &lt;&lt; outlshift; \
+				out += 2; \
+				outnlsamps--; \
+			} \
+		}
+
+#define LINEARUPSCALESTEREOTOMONO(in, inrate, insamps, out, outrate, outlshift, outrshift) \
+		{ \
+			scale = inrate / (double)outrate; \
+			infrac = floor(scale * 65536); \
+			outsamps = insamps / scale; \
+			inaccum = 0; \
+			outnlsamps = floor(1.0 / scale); \
+			outsamps -= outnlsamps; \
+			\
+			while (outsamps) \
+			{ \
+				*out = ((((0xFFFF - inaccum)*in[0] + inaccum*in[2]) &gt;&gt; (16 - outlshift + outrshift)) + \
+				(((0xFFFF - inaccum)*in[1] + inaccum*in[3]) &gt;&gt; (16 - outlshift + outrshift))) &gt;&gt; 1; \
+				inaccum += infrac; \
+				in += (inaccum &gt;&gt; 16) * 2; \
+				inaccum &amp;= 0xFFFF; \
+				out++; \
+				outsamps--; \
+			} \
+			while (outnlsamps) \
+			{ \
+				out[0] = (((in[0] &gt;&gt; outrshift) &lt;&lt; outlshift) + ((in[1] &gt;&gt; outrshift) &lt;&lt; outlshift)) &gt;&gt; 1; \
+				out++; \
+				outnlsamps--; \
+			} \
+		}
+
+#define LINEARDOWNSCALE(in, inrate, insamps, out, outrate, outlshift, outrshift) \
+		{ \
+			scale = outrate / (double)inrate; \
+			infrac = floor(scale * 65536); \
+			inaccum = 0; \
+			insamps--; \
+			outsampleft = 0; \
+			\
+			while (insamps) \
+			{ \
+				inaccum += infrac; \
+				if (inaccum &gt;&gt; 16) \
+				{ \
+					inaccum &amp;= 0xFFFF; \
+					outsampleft += (infrac - inaccum) * (*in); \
+					*out = outsampleft &gt;&gt; (16 - outlshift + outrshift); \
+					out++; \
+					outsampleft = inaccum * (*in); \
+				} \
+				else \
+					outsampleft += infrac * (*in); \
+				in++; \
+				insamps--; \
+			} \
+			outsampleft += (0xFFFF - inaccum) * (*in);\
+			*out = outsampleft &gt;&gt; (16 - outlshift + outrshift); \
+		}
+
+#define LINEARDOWNSCALESTEREO(in, inrate, insamps, out, outrate, outlshift, outrshift) \
+		{ \
+			scale = outrate / (double)inrate; \
+			infrac = floor(scale * 65536); \
+			inaccum = 0; \
+			insamps--; \
+			outsampleft = 0; \
+			outsampright = 0; \
+			\
+			while (insamps) \
+			{ \
+				inaccum += infrac; \
+				if (inaccum &gt;&gt; 16) \
+				{ \
+					inaccum &amp;= 0xFFFF; \
+					outsampleft += (infrac - inaccum) * in[0]; \
+					outsampright += (infrac - inaccum) * in[1]; \
+					out[0] = outsampleft &gt;&gt; (16 - outlshift + outrshift); \
+					out[1] = outsampright &gt;&gt; (16 - outlshift + outrshift); \
+					out += 2; \
+					outsampleft = inaccum * in[0]; \
+					outsampright = inaccum * in[1]; \
+				} \
+				else \
+				{ \
+					outsampleft += infrac * in[0]; \
+					outsampright += infrac * in[1]; \
+				} \
+				in += 2; \
+				insamps--; \
+			} \
+			outsampleft += (0xFFFF - inaccum) * in[0];\
+			outsampright += (0xFFFF - inaccum) * in[1];\
+			out[0] = outsampleft &gt;&gt; (16 - outlshift + outrshift); \
+			out[1] = outsampright &gt;&gt; (16 - outlshift + outrshift); \
+		}
+
+#define LINEARDOWNSCALESTEREOTOMONO(in, inrate, insamps, out, outrate, outlshift, outrshift) \
+		{ \
+			scale = outrate / (double)inrate; \
+			infrac = floor(scale * 65536); \
+			inaccum = 0; \
+			insamps--; \
+			outsampleft = 0; \
+			\
+			while (insamps) \
+			{ \
+				inaccum += infrac; \
+				if (inaccum &gt;&gt; 16) \
+				{ \
+					inaccum &amp;= 0xFFFF; \
+					outsampleft += (infrac - inaccum) * ((in[0] + in[1]) &gt;&gt; 1); \
+					*out = outsampleft &gt;&gt; (16 - outlshift + outrshift); \
+					out++; \
+					outsampleft = inaccum * ((in[0] + in[1]) &gt;&gt; 1); \
+				} \
+				else \
+					outsampleft += infrac * ((in[0] + in[1]) &gt;&gt; 1); \
+				in += 2; \
+				insamps--; \
+			} \
+			outsampleft += (0xFFFF - inaccum) * ((in[0] + in[1]) &gt;&gt; 1);\
+			*out = outsampleft &gt;&gt; (16 - outlshift + outrshift); \
+		}
+
+#define STANDARDRESCALE(in, inrate, insamps, out, outrate, outlshift, outrshift) \
+		{ \
+			scale = inrate / (double)outrate; \
+			infrac = floor(scale * 65536); \
+			outsamps = insamps / scale; \
+			inaccum = 0; \
+			\
+			while (outsamps) \
+			{ \
+				*out = (*in &gt;&gt; outrshift) &lt;&lt; outlshift; \
+				inaccum += infrac; \
+				in += (inaccum &gt;&gt; 16); \
+				inaccum &amp;= 0xFFFF; \
+				out++; \
+				outsamps--; \
+			} \
+		}
+
+#define STANDARDRESCALESTEREO(in, inrate, insamps, out, outrate, outlshift, outrshift) \
+		{ \
+			scale = inrate / (double)outrate; \
+			infrac = floor(scale * 65536); \
+			outsamps = insamps / scale; \
+			inaccum = 0; \
+			\
+			while (outsamps) \
+			{ \
+				out[0] = (in[0] &gt;&gt; outrshift) &lt;&lt; outlshift; \
+				out[1] = (in[1] &gt;&gt; outrshift) &lt;&lt; outlshift; \
+				inaccum += infrac; \
+				in += (inaccum &gt;&gt; 16) * 2; \
+				inaccum &amp;= 0xFFFF; \
+				out += 2; \
+				outsamps--; \
+			} \
+		}
+
+#define STANDARDRESCALESTEREOTOMONO(in, inrate, insamps, out, outrate, outlshift, outrshift) \
+		{ \
+			scale = inrate / (double)outrate; \
+			infrac = floor(scale * 65536); \
+			outsamps = insamps / scale; \
+			inaccum = 0; \
+			\
+			while (outsamps) \
+			{ \
+				out[0] = (((in[0] &gt;&gt; outrshift) &lt;&lt; outlshift) + ((in[1] &gt;&gt; outrshift) &lt;&lt; outlshift)) &gt;&gt; 1; \
+				inaccum += infrac; \
+				in += (inaccum &gt;&gt; 16) * 2; \
+				inaccum &amp;= 0xFFFF; \
+				out++; \
+				outsamps--; \
+			} \
+		}
+
+#define QUICKCONVERT(in, insamps, out, outlshift, outrshift) \
+		{ \
+			while (insamps) \
+			{ \
+				*out = (*in &gt;&gt; outrshift) &lt;&lt; outlshift; \
+				out++; \
+				in++; \
+				insamps--; \
+			} \
+		}
+
+#define QUICKCONVERTSTEREOTOMONO(in, insamps, out, outlshift, outrshift) \
+		{ \
+			while (insamps) \
+			{ \
+				*out = (((in[0] &gt;&gt; outrshift) &lt;&lt; outlshift) + ((in[1] &gt;&gt; outrshift) &lt;&lt; outlshift)) &gt;&gt; 1; \
+				out++; \
+				in += 2; \
+				insamps--; \
+			} \
+		}
+
+// SND_ResampleStream: takes a sound stream and converts with given parameters. Limited to
+// 8-16-bit signed conversions and mono-to-mono/stereo-to-stereo conversions.
+// Not an in-place algorithm.
+void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int insamps, void *out, int outrate, int outwidth, int outchannels, int resampstyle)
+{
+	double scale;
+	signed char *in8 = (signed char *)in;
+	short *in16 = (short *)in;
+	signed char *out8 = (signed char *)out;
+	short *out16 = (short *)out;
+	int outsamps, outnlsamps, outsampleft, outsampright;
+	int infrac, inaccum;
+
+	if (insamps &lt;= 0)
+		return;
+
+	if (inchannels == outchannels &amp;&amp; inwidth == outwidth &amp;&amp; inrate == outrate)
+	{
+		memcpy(out, in, inwidth*insamps*inchannels);
+		return;
+	}
+
+	if (inchannels == 1 &amp;&amp; outchannels == 1)
+	{
+		if (inwidth == 1)
+		{
+			if (outwidth == 1)
+			{
+				if (inrate &lt; outrate) // upsample
+				{
+					if (resampstyle)
+						LINEARUPSCALE(in8, inrate, insamps, out8, outrate, 0, 0)
+					else
+						STANDARDRESCALE(in8, inrate, insamps, out8, outrate, 0, 0)
+				}
+				else // downsample
+				{
+				if (resampstyle &gt; 1)
+					LINEARDOWNSCALE(in8, inrate, insamps, out8, outrate, 0, 0)
+				else
+					STANDARDRESCALE(in8, inrate, insamps, out8, outrate, 0, 0)
+				}
+				return;
+			}
+			else
+			{
+				if (inrate == outrate) // quick convert
+					QUICKCONVERT(in8, insamps, out16, 8, 0)
+				else if (inrate &lt; outrate) // upsample
+				{
+					if (resampstyle)
+						LINEARUPSCALE(in8, inrate, insamps, out16, outrate, 8, 0)
+					else
+						STANDARDRESCALE(in8, inrate, insamps, out16, outrate, 8, 0)
+				}
+				else // downsample
+				{
+					if (resampstyle &gt; 1)
+						LINEARDOWNSCALE(in8, inrate, insamps, out16, outrate, 8, 0)
+					else
+						STANDARDRESCALE(in8, inrate, insamps, out16, outrate, 8, 0)
+				}
+				return;
+			}
+		}
+		else // 16-bit
+		{
+			if (outwidth == 2)
+			{
+				if (inrate &lt; outrate) // upsample
+				{
+					if (resampstyle)
+						LINEARUPSCALE(in16, inrate, insamps, out16, outrate, 0, 0)
+					else
+						STANDARDRESCALE(in16, inrate, insamps, out16, outrate, 0, 0)
+				}
+				else // downsample
+				{
+					if (resampstyle &gt; 1)
+						LINEARDOWNSCALE(in16, inrate, insamps, out16, outrate, 0, 0)
+					else
+						STANDARDRESCALE(in16, inrate, insamps, out16, outrate, 0, 0)
+				}
+				return;
+			}
+			else
+			{
+				if (inrate == outrate) // quick convert
+					QUICKCONVERT(in16, insamps, out8, 0, 8)
+				else if (inrate &lt; outrate) // upsample
+				{
+					if (resampstyle)
+						LINEARUPSCALE(in16, inrate, insamps, out8, outrate, 0, 8)
+					else
+						STANDARDRESCALE(in16, inrate, insamps, out8, outrate, 0, 8)
+				}
+				else // downsample
+				{
+					if (resampstyle &gt; 1)
+						LINEARDOWNSCALE(in16, inrate, insamps, out8, outrate, 0, 8)
+					else
+						STANDARDRESCALE(in16, inrate, insamps, out8, outrate, 0, 8)
+				}
+			return;
+			}
+		}
+	}
+	else if (outchannels == 2 &amp;&amp; inchannels == 2)
+	{
+		if (inwidth == 1)
+		{
+			if (outwidth == 1)
+			{
+				if (inrate &lt; outrate) // upsample
+				{
+					if (resampstyle)
+						LINEARUPSCALESTEREO(in8, inrate, insamps, out8, outrate, 0, 0)
+					else
+						STANDARDRESCALESTEREO(in8, inrate, insamps, out8, outrate, 0, 0)
+				}
+				else // downsample
+				{
+					if (resampstyle &gt; 1)
+						LINEARDOWNSCALESTEREO(in8, inrate, insamps, out8, outrate, 0, 0)
+					else
+						STANDARDRESCALESTEREO(in8, inrate, insamps, out8, outrate, 0, 0)
+				}
+			}
+			else
+			{
+				if (inrate == outrate) // quick convert
+				{
+					insamps *= 2;
+					QUICKCONVERT(in8, insamps, out16, 8, 0)
+				}
+				else if (inrate &lt; outrate) // upsample
+				{
+					if (resampstyle)
+						LINEARUPSCALESTEREO(in8, inrate, insamps, out16, outrate, 8, 0)
+					else
+						STANDARDRESCALESTEREO(in8, inrate, insamps, out16, outrate, 8, 0)
+				}
+				else // downsample
+				{
+					if (resampstyle &gt; 1)
+						LINEARDOWNSCALESTEREO(in8, inrate, insamps, out16, outrate, 8, 0)
+					else
+						STANDARDRESCALESTEREO(in8, inrate, insamps, out16, outrate, 8, 0)
+				}
+			}
+		}
+		else // 16-bit
+		{
+			if (outwidth == 2)
+			{
+				if (inrate &lt; outrate) // upsample
+				{
+					if (resampstyle)
+						LINEARUPSCALESTEREO(in16, inrate, insamps, out16, outrate, 0, 0)
+					else
+						STANDARDRESCALESTEREO(in16, inrate, insamps, out16, outrate, 0, 0)
+				}
+				else // downsample
+				{
+					if (resampstyle &gt; 1)
+						LINEARDOWNSCALESTEREO(in16, inrate, insamps, out16, outrate, 0, 0)
+					else
+						STANDARDRESCALESTEREO(in16, inrate, insamps, out16, outrate, 0, 0)
+				}
+			}
+			else
+			{
+				if (inrate == outrate) // quick convert
+				{
+					insamps *= 2;
+					QUICKCONVERT(in16, insamps, out8, 0, 8)
+				}
+				else if (inrate &lt; outrate) // upsample
+				{
+					if (resampstyle)
+						LINEARUPSCALESTEREO(in16, inrate, insamps, out8, outrate, 0, 8)
+					else
+						STANDARDRESCALESTEREO(in16, inrate, insamps, out8, outrate, 0, 8)
+				}
+				else // downsample
+				{
+					if (resampstyle &gt; 1)
+						LINEARDOWNSCALESTEREO(in16, inrate, insamps, out8, outrate, 0, 8)
+					else
+						STANDARDRESCALESTEREO(in16, inrate, insamps, out8, outrate, 0, 8)
+				}
+			}
+		}
+	}
+#if 0
+	else if (outchannels == 1 &amp;&amp; inchannels == 2)
+	{
+		if (inwidth == 1)
+		{
+			if (outwidth == 1)
+			{
+				if (inrate &lt; outrate) // upsample
+				{
+					if (resampstyle)
+						LINEARUPSCALESTEREOTOMONO(in8, inrate, insamps, out8, outrate, 0, 0)
+					else
+						STANDARDRESCALESTEREOTOMONO(in8, inrate, insamps, out8, outrate, 0, 0)
+				}
+				else // downsample
+					STANDARDRESCALESTEREOTOMONO(in8, inrate, insamps, out8, outrate, 0, 0)
+			}
+			else
+			{
+				if (inrate == outrate) // quick convert
+					QUICKCONVERTSTEREOTOMONO(in8, insamps, out16, 8, 0)
+				else if (inrate &lt; outrate) // upsample
+				{
+					if (resampstyle)
+						LINEARUPSCALESTEREOTOMONO(in8, inrate, insamps, out16, outrate, 8, 0)
+					else
+						STANDARDRESCALESTEREOTOMONO(in8, inrate, insamps, out16, outrate, 8, 0)
+				}
+				else // downsample
+					STANDARDRESCALESTEREOTOMONO(in8, inrate, insamps, out16, outrate, 8, 0)
+			}
+		}
+		else // 16-bit
+		{
+			if (outwidth == 2)
+			{
+				if (inrate &lt; outrate) // upsample
+				{
+					if (resampstyle)
+						LINEARUPSCALESTEREOTOMONO(in16, inrate, insamps, out16, outrate, 0, 0)
+					else
+						STANDARDRESCALESTEREOTOMONO(in16, inrate, insamps, out16, outrate, 0, 0)
+				}
+				else // downsample
+					STANDARDRESCALESTEREOTOMONO(in16, inrate, insamps, out16, outrate, 0, 0)
+			}
+			else
+			{
+				if (inrate == outrate) // quick convert
+					QUICKCONVERTSTEREOTOMONO(in16, insamps, out8, 0, 8)
+				else if (inrate &lt; outrate) // upsample
+				{
+					if (resampstyle)
+						LINEARUPSCALESTEREOTOMONO(in16, inrate, insamps, out8, outrate, 0, 8)
+					else
+						STANDARDRESCALESTEREOTOMONO(in16, inrate, insamps, out8, outrate, 0, 8)
+				}
+				else // downsample
+					STANDARDRESCALESTEREOTOMONO(in16, inrate, insamps, out8, outrate, 0, 8)
+			}
+		}
+	}
+#endif
+}
+
+
+
+
+
+
+#define MAX_RAW_CACHE (1024 * 32) // have no idea which size it actually should be.
+
+typedef struct
+{
+	qboolean inuse;
+	int id;
+	sfx_t sfx;
+} streaming_t;
+
+static void S_RawClearStream(streaming_t *s);
+
+#define MAX_RAW_SOURCES (MAX_SCOREBOARD+1)
+
+static streaming_t s_streamers[MAX_RAW_SOURCES] = {{0}};
+
+static void S_RawClear(void)
+{
+	int i;
+	streaming_t *s;
+
+	for (s = s_streamers, i = 0; i &lt; MAX_RAW_SOURCES; i++, s++)
+	{
+		S_RawClearStream(s);
+	}
+
+	memset(s_streamers, 0, sizeof(s_streamers));
+}
+
+// Stop playing particular stream and make it free.
+static void S_RawClearStream(streaming_t *s)
+{
+	int i;
+	sfxcache_t * currentcache;
+
+	if (!s)
+		return;
+
+	// get current cache if any.
+	currentcache = (sfxcache_t *)Cache_Check(&amp;s-&gt;sfx.cache);
+	if (currentcache)
+	{
+		currentcache-&gt;loopstart = -1; //stop mixing it
+	}
+
+	// remove link on sfx from the channels array.
+	for (i = 0; i &lt; total_channels; i++)
+	{
+		if (channels[i].sfx == &amp;s-&gt;sfx)
+		{
+		channels[i].sfx = NULL;
+		break;
+		}
+	}
+
+	// free cache.
+	if (s-&gt;sfx.cache.data)
+	Cache_Free(&amp;s-&gt;sfx.cache);
+
+	// clear whole struct.
+	memset(s, 0, sizeof(*s));
+}
+
+// Searching for free slot or re-use previous one with the same sourceid.
+static streaming_t * S_RawGetFreeStream(int sourceid)
+{
+	int i;
+	streaming_t *s, *free = NULL;
+
+	for (s = s_streamers, i = 0; i &lt; MAX_RAW_SOURCES; i++, s++)
+	{
+		if (!s-&gt;inuse)
+		{
+			if (!free)
+			{
+				free = s; // found free stream slot.
+			}
+
+			continue;
+		}
+
+		if (s-&gt;id == sourceid)
+		{
+			return s; // re-using slot.
+		}
+	}
+
+	return free;
+}
+
+// Streaming audio.
+// This is useful when there is one source, and the sound is to be played with no attenuation.
+void S_RawAudio(int sourceid, byte *data, unsigned int speed, unsigned int samples, unsigned int channelsnum, unsigned int width)
+{
+	unsigned int i;
+	int newsize;
+	int prepadl;
+	int spare;
+	int outsamples;
+	double speedfactor;
+	sfxcache_t * currentcache;
+	streaming_t * s;
+
+	// search for free slot or re-use previous one with the same sourceid.
+	s = S_RawGetFreeStream(sourceid);
+
+	if (!s)
+	{
+		Con_DPrintf("No free audio streams or stream not found\n");
+		return;
+	}
+
+	// empty data mean someone tell us to shut up particular slot.
+	if (!data)
+	{
+		S_RawClearStream(s);
+		return;
+	}
+
+	// attempting to add new stream
+	if (!s-&gt;inuse)
+	{
+		sfxcache_t * newcache;
+
+		// clear whole struct.
+		memset(s, 0, sizeof(*s));
+		// allocate cache.
+		newsize = MAX_RAW_CACHE; //sizeof(sfxcache_t)
+
+		if (newsize &lt; sizeof(sfxcache_t))
+			Sys_Error("MAX_RAW_CACHE too small %d", newsize);
+
+		newcache = Cache_Alloc(&amp;s-&gt;sfx.cache, newsize, "rawaudio");
+		if (!newcache)
+		{
+			Con_DPrintf("Cache_Alloc failed\n");
+			return;
+		}
+
+		s-&gt;inuse = true;
+		s-&gt;id = sourceid;
+		// strcpy(s-&gt;sfx.name, ""); // FIXME: probably we should put some specific tag name here?
+		s-&gt;sfx.cache.data = newcache;
+		newcache-&gt;speed = shm-&gt;speed;
+		newcache-&gt;stereo = channelsnum-1;
+		newcache-&gt;width = width;
+		newcache-&gt;loopstart = -1;
+		newcache-&gt;length = 0;
+		newcache = NULL;
+
+		// Con_Printf("Added new raw stream\n");
+	}
+
+	// get current cache if any.
+	currentcache = (sfxcache_t *)Cache_Check(&amp;s-&gt;sfx.cache);
+	if (!currentcache)
+	{
+		Con_DPrintf("Cache_Check failed\n");
+		S_RawClearStream(s);
+		return;
+	}
+
+	if ( currentcache-&gt;speed != shm-&gt;speed
+	|| currentcache-&gt;stereo != channelsnum-1
+	|| currentcache-&gt;width != width)
+	{
+		currentcache-&gt;speed = shm-&gt;speed;
+		currentcache-&gt;stereo = channelsnum-1;
+		currentcache-&gt;width = width;
+		// newcache-&gt;loopstart = -1;
+		currentcache-&gt;length = 0;
+		// Con_Printf("Restarting raw stream\n");
+	}
+
+	speedfactor = (double)speed/shm-&gt;speed;
+	outsamples = samples/speedfactor;
+
+	prepadl = 0x7fffffff;
+	// FIXME: qqshka: I have no idea that spike is doing here, really. WTF is prepadl??? PITCHSHIFT ???
+	// spike: make sure that we have a prepad.
+	for (i = 0; i &lt; total_channels; i++)
+	{
+		if (channels[i].sfx == &amp;s-&gt;sfx)
+		{
+			if (prepadl &gt; (channels[i].pos/*&gt;&gt;PITCHSHIFT*/))
+				prepadl = (channels[i].pos/*&gt;&gt;PITCHSHIFT*/);
+			break;
+		}
+	}
+
+	if (prepadl == 0x7fffffff)
+	{
+		prepadl = 0;
+		spare = 0;
+		if (spare &gt; shm-&gt;speed)
+		{
+			Con_DPrintf("Sacrificed raw sound stream\n");
+			spare = 0; //too far out. sacrifice it all
+		}
+	}
+	else
+	{
+		if (prepadl &lt; 0)
+			prepadl = 0;
+		spare = currentcache-&gt;length - prepadl;
+		if (spare &lt; 0) //remaining samples since last time
+			spare = 0;
+
+		if (spare &gt; shm-&gt;speed * 2) // more than 2 seconds of sound
+		{
+			Con_DPrintf("Sacrificed raw sound stream\n");
+			spare = 0; //too far out. sacrifice it all
+		}
+	}
+
+	newsize = sizeof(sfxcache_t) + (spare + outsamples) * (currentcache-&gt;stereo+1) * currentcache-&gt;width;
+	if (newsize &gt;= MAX_RAW_CACHE)
+	{
+		Con_DPrintf("Cache buffer overflowed\n");
+		S_RawClearStream(s);
+		return;
+	}
+	// move along spare/remaning samples in the begging of the buffer.
+	memmove(currentcache-&gt;data,
+	currentcache-&gt;data + prepadl * (currentcache-&gt;stereo+1) * currentcache-&gt;width,
+	spare * (currentcache-&gt;stereo+1) * currentcache-&gt;width);
+
+	currentcache-&gt;length = spare + outsamples;
+
+	// resample.
+	{
+		short *outpos = (short *)(currentcache-&gt;data + spare * (currentcache-&gt;stereo+1) * currentcache-&gt;width);
+		SND_ResampleStream(data,
+				speed,
+				width,
+				channelsnum,
+				samples,
+				outpos,
+				shm-&gt;speed,
+				currentcache-&gt;width,
+				currentcache-&gt;stereo+1,
+				true
+				);
+	}
+
+	currentcache-&gt;loopstart = -1;//currentcache-&gt;total_length;
+
+	for (i = 0; i &lt; total_channels; i++)
+	{
+		if (channels[i].sfx == &amp;s-&gt;sfx)
+		{
+#if 0
+			// FIXME: qqshka: hrm, should it be just like this all the time??? I think it should.
+			channels[i].pos = 0;
+			channels[i].end = paintedtime + currentcache-&gt;total_length;
+#else
+			channels[i].pos -= prepadl; // * channels[i].rate;
+			channels[i].end += outsamples;
+			channels[i].master_vol = (int) (1 * 255); // this should changed volume on alredy playing sound.
+
+			if (channels[i].end &lt; paintedtime)
+			{
+				channels[i].pos = 0;
+				channels[i].end = paintedtime + currentcache-&gt;length;
+			}
+#endif
+			break;
+		}
+	}
+
+	//this one wasn't playing, lets start it then.
+	if (i == total_channels)
+	{
+		// Con_DPrintf("start sound\n");
+		/*slight delay to try to avoid frame rate/etc stops/starts*/
+		// S_StartSoundCard(si, -1, 0, &amp;s-&gt;sfx, r_origin, 1, 32767, -shm-&gt;format.speed*0.02, 0);
+		S_StartSound(cl.viewentity, 0, &amp;s-&gt;sfx, r_origin, 256/voicevolumemod, 0);
+	}
+}
+
+/*****************************************************************************************************************************/
+/*client/coding/decoding componant*/
+
+/*the client cvars*/
+cvar_t cl_voip_send = {"cl_voip_send", "0"};
+cvar_t cl_voip_vad_threshhold = {"cl_voip_vad_threshhold", "15"};
+cvar_t cl_voip_vad_delay = {"cl_voip_vad_delay", "0.3"};
+cvar_t cl_voip_capturingvol = {"cl_voip_capturingvol", "0.5"};
+cvar_t cl_voip_showmeter = {"cl_voip_showmeter", "1"};
+
+cvar_t cl_voip_play = {"cl_voip_play", "1"};
+cvar_t cl_voip_micamp = {"cl_voip_micamp", "2"};
+
+
+
+#include &lt;speex/speex.h&gt;
+#include &lt;speex/speex_preprocess.h&gt;
+static struct
+{
+	qboolean inited;
+	qboolean loaded;
+	dllhandle_t *speexlib;
+	dllhandle_t *speexdsplib;
+
+	SpeexBits encbits;
+	void *encoder;
+	SpeexPreprocessState *preproc;
+	unsigned int framesize;
+	unsigned int samplerate;
+
+	SpeexBits decbits[MAX_SCOREBOARD];
+	void *decoder[MAX_SCOREBOARD];
+	unsigned char decseq[MAX_SCOREBOARD];	/*sender's sequence, to detect+cover minor packetloss*/
+	unsigned char decgen[MAX_SCOREBOARD];	/*last generation. if it changes, we flush speex to reset packet loss*/
+	float decamp[MAX_SCOREBOARD];	/*amplify them by this*/
+	float lastspoke[MAX_SCOREBOARD];	/*time when they're no longer considered talking. if future, they're talking*/
+
+	unsigned char capturebuf[32768]; /*pending data*/
+	unsigned int capturepos;/*amount of pending data*/
+	unsigned int encsequence;/*the outgoing sequence count*/
+	unsigned int generation;/*incremented whenever capture is restarted*/
+	qboolean wantsend;	/*set if we're capturing data to send*/
+	float voiplevel;	/*your own voice level*/
+	unsigned int dumps;	/*trigger a new generation thing after a bit*/
+	unsigned int keeps;	/*for vad_delay*/
+
+	snd_capture_driver_t *driver;/*capture driver's functions*/
+	void *driverctx;	/*capture driver context*/
+} s_speex;
+
+static const SpeexMode *(*qspeex_lib_get_mode)(int mode);
+static void (*qspeex_bits_init)(SpeexBits *bits);
+static void (*qspeex_bits_reset)(SpeexBits *bits);
+static int (*qspeex_bits_write)(SpeexBits *bits, char *bytes, int max_len);
+
+static SpeexPreprocessState *(*qspeex_preprocess_state_init)(int frame_size, int sampling_rate);
+static int (*qspeex_preprocess_ctl)(SpeexPreprocessState *st, int request, void *ptr);
+static int (*qspeex_preprocess_run)(SpeexPreprocessState *st, spx_int16_t *x);
+
+static void * (*qspeex_encoder_init)(const SpeexMode *mode);
+static int (*qspeex_encoder_ctl)(void *state, int request, void *ptr);
+static int (*qspeex_encode_int)(void *state, spx_int16_t *in, SpeexBits *bits);
+
+static void *(*qspeex_decoder_init)(const SpeexMode *mode);
+static int (*qspeex_decode_int)(void *state, SpeexBits *bits, spx_int16_t *out);
+static void (*qspeex_bits_read_from)(SpeexBits *bits, char *bytes, int len);
+
+static dllfunction_t qspeexfuncs[] =
+{
+	{(void*)&amp;qspeex_lib_get_mode, "speex_lib_get_mode"},
+	{(void*)&amp;qspeex_bits_init, "speex_bits_init"},
+	{(void*)&amp;qspeex_bits_reset, "speex_bits_reset"},
+	{(void*)&amp;qspeex_bits_write, "speex_bits_write"},
+
+	{(void*)&amp;qspeex_encoder_init, "speex_encoder_init"},
+	{(void*)&amp;qspeex_encoder_ctl, "speex_encoder_ctl"},
+	{(void*)&amp;qspeex_encode_int, "speex_encode_int"},
+
+	{(void*)&amp;qspeex_decoder_init, "speex_decoder_init"},
+	{(void*)&amp;qspeex_decode_int, "speex_decode_int"},
+	{(void*)&amp;qspeex_bits_read_from, "speex_bits_read_from"},
+
+	{NULL}
+};
+static dllfunction_t qspeexdspfuncs[] =
+{
+	{(void*)&amp;qspeex_preprocess_state_init, "speex_preprocess_state_init"},
+	{(void*)&amp;qspeex_preprocess_ctl, "speex_preprocess_ctl"},
+	{(void*)&amp;qspeex_preprocess_run, "speex_preprocess_run"},
+
+	{NULL}
+};
+
+snd_capture_driver_t DSOUND_Capture;
+snd_capture_driver_t OSS_Capture;
+
+static qboolean S_Speex_Init(void)
+{
+	int i;
+	const SpeexMode *mode;
+	if (s_speex.inited)
+		return s_speex.loaded;
+	s_speex.inited = true;
+
+	s_speex.speexlib = Sys_LoadLibrary("libspeex", qspeexfuncs);
+	if (!s_speex.speexlib)
+	{
+		Con_Printf("libspeex not found. Voice chat not available.\n");
+		return false;
+	}
+
+	s_speex.speexdsplib = Sys_LoadLibrary("libspeexdsp", qspeexdspfuncs);
+	if (!s_speex.speexdsplib)
+	{
+		Con_Printf("libspeexdsp not found. Voice chat not available.\n");
+		return false;
+	}
+
+	mode = qspeex_lib_get_mode(SPEEX_MODEID_NB);
+
+
+	qspeex_bits_init(&amp;s_speex.encbits);
+	qspeex_bits_reset(&amp;s_speex.encbits);
+
+	s_speex.encoder = qspeex_encoder_init(mode);
+
+	qspeex_encoder_ctl(s_speex.encoder, SPEEX_GET_FRAME_SIZE, &amp;s_speex.framesize);
+	qspeex_encoder_ctl(s_speex.encoder, SPEEX_GET_SAMPLING_RATE, &amp;s_speex.samplerate);
+	s_speex.samplerate = 11025;
+	qspeex_encoder_ctl(s_speex.encoder, SPEEX_SET_SAMPLING_RATE, &amp;s_speex.samplerate);
+
+	s_speex.preproc = qspeex_preprocess_state_init(s_speex.framesize, s_speex.samplerate);
+
+	i = 1;
+	qspeex_preprocess_ctl(s_speex.preproc, SPEEX_PREPROCESS_SET_DENOISE, &amp;i);
+
+	i = 1;
+	qspeex_preprocess_ctl(s_speex.preproc, SPEEX_PREPROCESS_SET_AGC, &amp;i);
+
+	for (i = 0; i &lt; MAX_SCOREBOARD; i++)
+	{
+		qspeex_bits_init(&amp;s_speex.decbits[i]);
+		qspeex_bits_reset(&amp;s_speex.decbits[i]);
+		s_speex.decoder[i] = qspeex_decoder_init(mode);
+		s_speex.decamp[i] = 1;
+	}
+	s_speex.loaded = true;
+	return s_speex.loaded;
+}
+
+void S_Voip_Parse(void)
+{
+	unsigned int sender;
+	int bytes;
+	unsigned char data[1024], *start;
+	short decodebuf[1024];
+	unsigned int decodesamps, len, newseq, drops;
+	unsigned char seq, gen;
+	float amp = 1;
+	unsigned int i;
+
+	sender = MSG_ReadByte();
+	gen = MSG_ReadByte();
+	seq = MSG_ReadByte();
+	bytes = MSG_ReadShort();
+	if (bytes &gt; sizeof(data) || !cl_voip_play.value || !S_Speex_Init() || (gen &amp; 0xf0))
+	{
+		while(bytes-- &gt; 0)
+			MSG_ReadByte();
+		return;
+	}
+	/*would be faster to do a block read*/
+	for(i = 0; i &lt; bytes; i++)
+		data[i] = MSG_ReadByte();
+
+	sender &amp;= MAX_SCOREBOARD-1;
+
+	amp = s_speex.decamp[sender];
+
+	decodesamps = 0;
+	newseq = 0;
+	drops = 0;
+	start = data;
+
+	s_speex.lastspoke[sender] = realtime + 0.5;
+	if (s_speex.decgen[sender] != gen)
+	{
+		qspeex_bits_reset(&amp;s_speex.decbits[sender]);
+		s_speex.decgen[sender] = gen;
+		s_speex.decseq[sender] = seq;
+	}
+
+	while (bytes &gt; 0)
+	{
+		if (decodesamps + s_speex.framesize &gt; sizeof(decodebuf)/sizeof(decodebuf[0]))
+		{
+			S_RawAudio(sender, (byte*)decodebuf, s_speex.samplerate, decodesamps, 1, 2);
+			decodesamps = 0;
+		}
+
+		if (s_speex.decseq[sender] != seq)
+		{
+			qspeex_decode_int(s_speex.decoder[sender], NULL, decodebuf + decodesamps);
+			s_speex.decseq[sender]++;
+			drops++;
+		}
+		else
+		{
+			bytes--;
+			len = *start++;
+			qspeex_bits_read_from(&amp;s_speex.decbits[sender], start, len);
+			bytes -= len;
+			start += len;
+			qspeex_decode_int(s_speex.decoder[sender], &amp;s_speex.decbits[sender], decodebuf + decodesamps);
+			newseq++;
+		}
+		if (amp != 1)
+		{
+			for (i = decodesamps; i &lt; decodesamps+s_speex.framesize; i++)
+				decodebuf[i] *= amp;
+		}
+		decodesamps += s_speex.framesize;
+	}
+	s_speex.decseq[sender] += newseq;
+
+	if (drops)
+		Con_DPrintf("%i dropped audio frames\n", drops);
+
+	if (decodesamps &gt; 0)
+		S_RawAudio(sender, (byte*)decodebuf, s_speex.samplerate, decodesamps, 1, 2);
+}
+
+void S_Voip_Transmit(unsigned char clc, sizebuf_t *buf)
+{
+	unsigned char outbuf[1024];
+	unsigned int outpos;//in bytes
+	unsigned int encpos;//in bytes
+	short *start;
+	unsigned char initseq;//in frames
+	unsigned int i;
+	unsigned int samps;
+	float level, f;
+	float micamp = cl_voip_micamp.value;
+	qboolean voipsendenable = true;
+
+	/*if you're sending sound, you should be prepared to accept others yelling at you to shut up*/
+	if (!cl_voip_play.value)
+		voipsendenable = false;
+	if (!(cl.pext_fte2 &amp; PROTOCOL_SUPPORTED_FTE2_VOIP))
+		voipsendenable = false;
+
+	if (!voipsendenable)
+	{
+		if (s_speex.driver)
+		{
+			if (s_speex.wantsend)
+				s_speex.driver-&gt;Stop(s_speex.driverctx);
+			s_speex.driver-&gt;Shutdown(s_speex.driverctx);
+			s_speex.driverctx = NULL;
+			s_speex.driver = NULL;
+		}
+		return;
+	}
+
+	voipsendenable = cl_voip_send.value&gt;0;
+
+	if (!s_speex.driver)
+	{
+		s_speex.voiplevel = -1;
+		/*only init the first time capturing is requested*/
+		if (!voipsendenable)
+			return;
+
+		/*Add new drivers in order of priority*/
+		if (!s_speex.driver || !s_speex.driver-&gt;Init)
+			s_speex.driver = &amp;DSOUND_Capture;
+		if (!s_speex.driver || !s_speex.driver-&gt;Init)
+			s_speex.driver = &amp;OSS_Capture;
+
+		/*no way to capture audio, give up*/
+		if (!s_speex.driver)
+			return;
+
+		/*see if we can init speex...*/
+		if (!S_Speex_Init())
+			return;
+
+		s_speex.driverctx = s_speex.driver-&gt;Init(s_speex.samplerate);
+	}
+
+	/*couldn't init a driver?*/
+	if (!s_speex.driverctx)
+	{
+		return;
+	}
+
+	if (!voipsendenable &amp;&amp; s_speex.wantsend)
+	{
+		s_speex.wantsend = false;
+		s_speex.capturepos += s_speex.driver-&gt;Update(s_speex.driverctx, (unsigned char*)s_speex.capturebuf + s_speex.capturepos, 1, sizeof(s_speex.capturebuf) - s_speex.capturepos);
+		s_speex.driver-&gt;Stop(s_speex.driverctx);
+		/*note: we still grab audio to flush everything that was captured while it was active*/
+	}
+	else if (voipsendenable &amp;&amp; !s_speex.wantsend)
+	{
+		s_speex.wantsend = true;
+		if (!s_speex.capturepos)
+		{	/*if we were actually still sending, it was probably only off for a single frame, in which case don't reset it*/
+			s_speex.dumps = 0;
+			s_speex.generation++;
+			s_speex.encsequence = 0;
+			qspeex_bits_reset(&amp;s_speex.encbits);
+		}
+		else
+		{
+			s_speex.capturepos += s_speex.driver-&gt;Update(s_speex.driverctx, (unsigned char*)s_speex.capturebuf + s_speex.capturepos, 1, sizeof(s_speex.capturebuf) - s_speex.capturepos);
+		}
+		s_speex.driver-&gt;Start(s_speex.driverctx);
+	}
+
+	s_speex.capturepos += s_speex.driver-&gt;Update(s_speex.driverctx, (unsigned char*)s_speex.capturebuf + s_speex.capturepos, s_speex.framesize*2, sizeof(s_speex.capturebuf) - s_speex.capturepos);
+
+	if (!s_speex.wantsend &amp;&amp; s_speex.capturepos &lt; s_speex.framesize*2)
+	{
+		s_speex.voiplevel = -1;
+		s_speex.capturepos = 0;
+		voicevolumemod = 256;
+		return;
+	}
+	voicevolumemod = cl_voip_capturingvol.value*256;
+
+	initseq = s_speex.encsequence;
+	level = 0;
+	samps=0;
+	for (encpos = 0, outpos = 0; s_speex.capturepos-encpos &gt;= s_speex.framesize*2 &amp;&amp; sizeof(outbuf)-outpos &gt; 64; s_speex.encsequence++)
+	{
+		start = (short*)(s_speex.capturebuf + encpos);
+
+		qspeex_preprocess_run(s_speex.preproc, start);
+
+		for (i = 0; i &lt; s_speex.framesize; i++)
+		{
+			f = start[i] * micamp;
+			start[i] = f;
+			f = fabs(start[i]);
+			level += f*f;
+		}
+		samps+=s_speex.framesize;
+
+		qspeex_bits_reset(&amp;s_speex.encbits);
+		qspeex_encode_int(s_speex.encoder, start, &amp;s_speex.encbits);
+		outbuf[outpos] = qspeex_bits_write(&amp;s_speex.encbits, outbuf+outpos+1, sizeof(outbuf) - (outpos+1));
+		outpos += 1+outbuf[outpos];
+		encpos += s_speex.framesize*2;
+	}
+	if (samps)
+	{
+		float nl;
+		nl = (3000*level) / (32767.0f*32767*samps);
+		s_speex.voiplevel = (s_speex.voiplevel*7 + nl)/8;
+		if (s_speex.voiplevel &lt; cl_voip_vad_threshhold.value &amp;&amp; !((int)cl_voip_send.value &amp; 2))
+		{
+			/*try and dump it, it was too quiet, and they're not pressing +voip*/
+			if (s_speex.keeps &gt; samps)
+			{
+				/*but not instantly*/
+				s_speex.keeps -= samps;
+			}
+			else
+			{
+				outpos = 0;
+				s_speex.dumps += samps;
+				s_speex.keeps = 0;
+			}
+		}
+		else
+			s_speex.keeps = s_speex.samplerate * cl_voip_vad_delay.value;
+		if (outpos)
+		{
+			if (s_speex.dumps &gt; s_speex.samplerate/4)
+				s_speex.generation++;
+			s_speex.dumps = 0;
+		}
+	}
+
+	if (outpos &amp;&amp; buf-&gt;maxsize - buf-&gt;cursize &gt;= outpos+4)
+	{
+		MSG_WriteByte(buf, clc);
+		MSG_WriteByte(buf, (s_speex.generation &amp; 0x0f)); /*gonna leave that nibble clear here... in this version, the client will ignore packets with those bits set. can use them for codec or something*/
+		MSG_WriteByte(buf, initseq);
+		MSG_WriteShort(buf, outpos);
+		SZ_Write(buf, outbuf, outpos);
+	}
+
+	/*remove sent data*/
+	memmove(s_speex.capturebuf, s_speex.capturebuf + encpos, s_speex.capturepos-encpos);
+	s_speex.capturepos -= encpos;
+}
+void S_Voip_Ignore(unsigned int slot, qboolean ignore)
+{
+	MSG_WriteByte (&amp;cls.message, clc_stringcmd);
+	MSG_WriteString (&amp;cls.message, va("vignore %i %i", slot, ignore));
+}
+static void S_Voip_Enable_f(void)
+{
+	Cvar_SetValue(cl_voip_send.name, (int)cl_voip_send.value | 2);
+}
+static void S_Voip_Disable_f(void)
+{
+	Cvar_SetValue(cl_voip_send.name, (int)cl_voip_send.value &amp; ~2);
+}
+static void S_Voip_f(void)
+{
+	int i;
+	if (!strcmp(Cmd_Argv(1), "maxgain"))
+	{
+		i = atoi(Cmd_Argv(2));
+		qspeex_preprocess_ctl(s_speex.preproc, SPEEX_PREPROCESS_SET_AGC_MAX_GAIN, &amp;i);
+	}
+}
+static void S_Voip_Play_Callback(cvar_t *var, char *oldval)
+{
+	if (cl.pext_fte2 &amp; PROTOCOL_SUPPORTED_FTE2_VOIP)
+	{
+		MSG_WriteByte (&amp;cls.message, clc_stringcmd);
+		if (var-&gt;value)
+			MSG_WriteString (&amp;cls.message, "unmuteall");
+		else
+			MSG_WriteString (&amp;cls.message, "muteall");
+	}
+}
+void S_Voip_MapChange(void)
+{
+	S_Voip_Play_Callback(&amp;cl_voip_play, "");
+}
+int S_Voip_Loudness(qboolean ignorevad)
+{
+	if (s_speex.voiplevel &gt; 100)
+		return 100;
+	if (!s_speex.driverctx || (!ignorevad &amp;&amp; s_speex.dumps))
+		return -1;
+	return s_speex.voiplevel;
+}
+qboolean S_Voip_Speaking(unsigned int plno)
+{
+	if (plno &gt;= MAX_SCOREBOARD)
+		return false;
+	return s_speex.lastspoke[plno] &gt; realtime;
+}
+
+void S_Voip_Init(void)
+{
+	Cvar_RegisterVariable(&amp;cl_voip_send);
+	Cvar_RegisterVariable(&amp;cl_voip_vad_threshhold);
+	Cvar_RegisterVariable(&amp;cl_voip_vad_delay);
+	Cvar_RegisterVariable(&amp;cl_voip_capturingvol);
+	Cvar_RegisterVariable(&amp;cl_voip_showmeter);
+	Cvar_RegisterVariable(&amp;cl_voip_play);
+	Cvar_RegisterVariable(&amp;cl_voip_micamp);
+
+	Cmd_AddCommand("+voip", S_Voip_Enable_f);
+	Cmd_AddCommand("-voip", S_Voip_Disable_f);
+}
+
+
+/*****************************************************************************************************************************/
+/*server componant*/
+
+cvar_t sv_voip = {"sv_voip", "1"};
+cvar_t sv_voip_echo = {"sv_voip_echo", "0"};
+
+
+/*
+Pivicy issues:
+By sending voice chat to a server, you are unsure who might be listening.
+Server could be changed to record voice.
+Voice will be saved in any demos made of the game.
+You're never quite sure if anyone might join the server and your team before you finish saying a sentance.
+You run the risk of sounds around you being recorded by quake, including but not limited to: TV channels, loved ones, phones, YouTube videos featuring certain moans.
+Default on non-team games is to broadcast.
+*/
+
+#define VOICE_RING_SIZE 512 /*POT*/
+struct
+{
+	struct voice_ring_s
+	{
+			unsigned int sender;
+			unsigned char receiver[MAX_SCOREBOARD/8];
+			unsigned char gen;
+			unsigned char seq;
+			unsigned int datalen;
+			unsigned char data[1024];
+	} ring[VOICE_RING_SIZE];
+	unsigned int write;
+} voice;
+void SV_VoiceReadPacket(client_t *client)
+{
+	unsigned int vt = client-&gt;voice_target;
+	unsigned int j;
+	struct voice_ring_s *ring;
+	unsigned short bytes;
+	client_t *cl;
+	unsigned char gen = MSG_ReadByte();
+	unsigned char seq = MSG_ReadByte();
+	/*read the data from the client*/
+	bytes = MSG_ReadShort();
+	ring = &amp;voice.ring[voice.write &amp; (VOICE_RING_SIZE-1)];
+	if (bytes &gt; sizeof(ring-&gt;data) || client-&gt;ismuted || !sv_voip.value)
+	{
+		while (bytes-- &gt; 0)
+			MSG_ReadByte();
+		return;
+	}
+	else
+	{
+		voice.write++;
+		for (j = 0; j &lt; bytes; j++)
+			ring-&gt;data[j] = MSG_ReadByte();
+	}
+
+	ring-&gt;datalen = bytes;
+	ring-&gt;sender = client - svs.clients;
+	ring-&gt;gen = gen;
+	ring-&gt;seq = seq;
+
+	/*broadcast it its to their team, and its not teamplay*/
+	if (vt == VT_TEAM &amp;&amp; !teamplay.value)
+		vt = VT_ALL;
+
+	/*figure out which team members are meant to receive it*/
+	for (j = 0; j &lt; MAX_SCOREBOARD/8; j++)
+		ring-&gt;receiver[j] = 0;
+	for (j = 0, cl = svs.clients; j &lt; svs.maxclients; j++, cl++)
+	{
+		if (!cl-&gt;spawned || !cl-&gt;active)
+			continue;
+
+		if (vt == VT_TEAM)
+		{
+			if ((cl-&gt;colors &amp; 0xf) == (client-&gt;colors &amp; 0xf))
+				continue;	// on different teams
+		}
+		else if (vt == VT_NONMUTED)
+		{
+			if (client-&gt;voice_mute[j&gt;&gt;3] &amp; (1&lt;&lt;(j&amp;3)))
+				continue;
+		}
+		else if (vt &gt;= VT_PLAYERSLOT0)
+		{
+			if (j != vt - VT_PLAYERSLOT0)
+				continue;
+		}
+
+		ring-&gt;receiver[j&gt;&gt;3] |= 1&lt;&lt;(j&amp;3);
+	}
+}
+void SV_VoiceInitClient(client_t *client)
+{
+	client-&gt;voice_target = VT_TEAM;
+	client-&gt;voice_active = false;
+	client-&gt;voice_read = voice.write;
+	memset(client-&gt;voice_mute, 0, sizeof(client-&gt;voice_mute));
+}
+void SV_VoiceSendPacket(client_t *client, sizebuf_t *buf)
+{
+	unsigned int clno;
+	qboolean send;
+	struct voice_ring_s *ring;
+
+	clno = client - svs.clients;
+
+	if (!(client-&gt;pext_fte2 &amp; PROTOCOL_SUPPORTED_FTE2_VOIP))
+		return;
+	if (!client-&gt;voice_active)
+	{
+		client-&gt;voice_read = voice.write;
+		return;
+	}
+
+	while(client-&gt;voice_read &lt; voice.write)
+	{
+		/*they might be too far behind*/
+		if (client-&gt;voice_read+VOICE_RING_SIZE &lt; voice.write)
+			client-&gt;voice_read = voice.write - VOICE_RING_SIZE;
+
+		ring = &amp;voice.ring[(client-&gt;voice_read) &amp; (VOICE_RING_SIZE-1)];
+
+		/*figure out if it was for us*/
+		send = false;
+		if (ring-&gt;receiver[clno&gt;&gt;3] &amp; (1&lt;&lt;(clno&amp;3)))
+			send = true;
+
+		if (client-&gt;voice_mute[ring-&gt;sender&gt;&gt;3] &amp; (1&lt;&lt;(ring-&gt;sender&amp;3)))
+			send = false;
+
+		if (ring-&gt;sender == clno &amp;&amp; !sv_voip_echo.value)
+			send = false;
+
+		if (send)
+		{
+			if (buf-&gt;maxsize - buf-&gt;cursize &lt; ring-&gt;datalen+5)
+				break;
+			MSG_WriteByte(buf, svcfte_voicechat);
+			MSG_WriteByte(buf, ring-&gt;sender);
+			MSG_WriteByte(buf, ring-&gt;gen);
+			MSG_WriteByte(buf, ring-&gt;seq);
+			MSG_WriteShort(buf, ring-&gt;datalen);
+			SZ_Write(buf, ring-&gt;data, ring-&gt;datalen);
+		}
+		client-&gt;voice_read++;
+	}
+}
+
+static void SV_Voice_Ignore_f(void)
+{
+	if (cmd_source == src_command)
+	{
+		if (cls.state == ca_connected)
+			Cmd_ForwardToServer ();
+	}
+	else
+	{
+		unsigned int other;
+		int type = 0;
+
+		if (Cmd_Argc() &lt; 2)
+		{
+			/*only a name = toggle*/
+			type = 0;
+		}
+		else
+		{
+			/*mute if 1, unmute if 0*/
+			if (atoi(Cmd_Argv(2)))
+				type = 1;
+			else
+				type = -1;
+		}
+		other = atoi(Cmd_Argv(1));
+		if (other &gt;= MAX_SCOREBOARD)
+			return;
+
+		switch(type)
+		{
+		case -1:
+			host_client-&gt;voice_mute[other&gt;&gt;3] &amp;= ~(1&lt;&lt;(other&amp;3));
+			break;
+		case 0:	
+			host_client-&gt;voice_mute[other&gt;&gt;3] ^= (1&lt;&lt;(other&amp;3));
+			break;
+		case 1:
+			host_client-&gt;voice_mute[other&gt;&gt;3] |= (1&lt;&lt;(other&amp;3));
+		}
+	}
+}
+static void SV_Voice_Target_f(void)
+{
+	if (cmd_source == src_command)
+	{
+		if (cls.state == ca_connected)
+			Cmd_ForwardToServer ();
+	}
+	else
+	{
+		unsigned int other;
+		char *t = Cmd_Argv(1);
+		if (!strcmp(t, "team"))
+			host_client-&gt;voice_target = VT_TEAM;
+		else if (!strcmp(t, "all"))
+			host_client-&gt;voice_target = VT_ALL;
+		else if (!strcmp(t, "nonmuted"))
+			host_client-&gt;voice_target = VT_NONMUTED;
+		else if (*t &gt;= '0' &amp;&amp; *t &lt;= '9')
+		{
+			other = atoi(t);
+			if (other &gt;= MAX_SCOREBOARD)
+				return;
+			host_client-&gt;voice_target = VT_PLAYERSLOT0 + other;
+		}
+		else
+		{
+			/*don't know who you mean, futureproofing*/
+			host_client-&gt;voice_target = VT_TEAM;
+		}
+	}
+}
+static void SV_Voice_MuteAll_f(void)
+{
+	if (cmd_source == src_command)
+	{
+		Cvar_Set("cl_voip_play", "0");
+		if (cls.state == ca_connected)
+			Cmd_ForwardToServer ();
+	}
+	else
+	{
+		host_client-&gt;voice_active = false;
+	}
+}
+static void SV_Voice_UnmuteAll_f(void)
+{
+	if (cmd_source == src_command)
+	{
+		Cvar_Set("cl_voip_play", "1");
+		if (cls.state == ca_connected)
+			Cmd_ForwardToServer ();
+	}
+	else
+	{
+		host_client-&gt;voice_active = true;
+	}
+}
+
+void SV_VoiceInit(void)
+{
+	Cvar_RegisterVariable(&amp;sv_voip);
+	Cvar_RegisterVariable(&amp;sv_voip_echo);
+
+	Cmd_AddCommand("muteall", SV_Voice_MuteAll_f);
+	Cmd_AddCommand("unmuteall", SV_Voice_UnmuteAll_f);
+	Cmd_AddCommand("vignore", SV_Voice_Ignore_f);
+	Cmd_AddCommand("voicetarg", SV_Voice_Target_f);
+}
diff -rupN qrack190/sound.h qrack/sound.h
--- qrack190/sound.h	2008-09-29 14:16:40.000000000 +0200
+++ qrack/sound.h	2011-04-29 01:59:34.002839800 +0200
@@ -174,4 +174,22 @@ void SNDDMA_Submit(void);
 void S_AmbientOff (void);
 void S_AmbientOn (void);
 
+
+struct client_s;
+
+/*Spike: externally visible voice chat functions*/
+void S_Voip_Transmit(unsigned char clc, sizebuf_t *buf);   /*generate clc_voicechat messages as required*/
+void S_Voip_Parse(void);                                   /*handle an svc_voicechat message*/
+void S_Voip_Ignore(unsigned int slot, qboolean ignore);    /*ignore/unignore a player using whatever ignore system that may exist in the engine already*/
+void S_Voip_MapChange(void);                               /*call at map start so the client knows to ask the server to activate voice*/
+int S_Voip_Loudness(qboolean ignorevad);                   /*returns how loud the voice is right now. ignorevad false causes return value 0 when voice is not activated by voice*/
+qboolean S_Voip_Speaking(unsigned int plno);               /*returns a boolean to say if a player slot is talking at the time*/
+void S_Voip_Init(void);
+
+void SV_VoiceReadPacket(struct client_s *client);                 /*handle a clc_voicechat message*/
+void SV_VoiceSendPacket(struct client_s *client, sizebuf_t *buf); /*generate svc_voicechat messages*/
+void SV_VoiceInitClient(struct client_s *client);                 /*resets a client. should be called at map start and connect*/
+void SV_VoiceInit(void);                                   /*registers server voip commands/cvars*/
+
+
 #endif
diff -rupN qrack190/sv_main.c qrack/sv_main.c
--- qrack190/sv_main.c	2008-09-29 14:16:40.000000000 +0200
+++ qrack/sv_main.c	2011-04-29 02:05:28.734167300 +0200
@@ -54,6 +54,9 @@ void SV_Init (void)
 	Cvar_RegisterVariable (&amp;pq_fullpitch);	// JPG 2.01
 	Cvar_RegisterVariable (&amp;cl_fullpitch);	// JPG 2.01
 
+	/*Spike: Gotta call this from somewhere*/
+	SV_VoiceInit();
+
 	if ((i = COM_CheckParm("-cheatfree")))
 		pq_cheatfree = true;
 	else
@@ -185,11 +188,29 @@ void SV_SendServerinfo (client_t *client
 {
 	char	**s, message[2048];
 
+	/*Spike: voice chat. disable voice until they ask to switch it on again after the serverinfo*/
+	SV_VoiceInitClient(client);
+
+	/*Spike: Support for protocol extension querying*/
+	if (client-&gt;pext_unknown)
+	{
+		MSG_WriteByte (&amp;client-&gt;message, svc_stufftext);
+		MSG_WriteString (&amp;client-&gt;message, "cmd pext\n");
+
+		client-&gt;sendsignon = true;
+		return;
+	}
+
 	MSG_WriteByte (&amp;client-&gt;message, svc_print);
 	sprintf (message, "%c\nVERSION %4.2f SERVER (%i CRC)\n", 2, SERVER_VERSION, pr_crc);	// 2000-01-08 Missing linefeeds fix by Maddes
 	MSG_WriteString (&amp;client-&gt;message, message);
 
 	MSG_WriteByte (&amp;client-&gt;message, svc_serverinfo);
+	if (client-&gt;pext_fte2)
+	{
+		MSG_WriteLong (&amp;client-&gt;message, PROTOCOL_VERSION_FTE2);
+		MSG_WriteLong (&amp;client-&gt;message, client-&gt;pext_fte2);
+	}
 	MSG_WriteLong (&amp;client-&gt;message, PROTOCOL_VERSION);
 	MSG_WriteByte (&amp;client-&gt;message, svs.maxclients);
 
@@ -276,6 +297,9 @@ void SV_ConnectClient (int clientnum)
 	client-&gt;message.maxsize = sizeof(client-&gt;msgbuf);
 	client-&gt;message.allowoverflow = true;		// we can catch it
 
+	/*Spike: so we know to ask them*/
+	client-&gt;pext_unknown = true;
+
 	if (sv.loadgame)
 	{
 		memcpy (client-&gt;spawn_parms, spawn_parms, sizeof(spawn_parms));
@@ -804,6 +828,9 @@ qboolean SV_SendClientDatagram (client_t
 	if (msg.cursize + sv.datagram.cursize &lt; msg.maxsize)
 		SZ_Write (&amp;msg, sv.datagram.data, sv.datagram.cursize);
 
+	/*Spike: add voice chat info if there's space for that too*/
+	SV_VoiceSendPacket(client, &amp;msg);
+
 // send the datagram
 	if (NET_SendUnreliableMessage (client-&gt;netconnection, &amp;msg) == -1)
 	{
diff -rupN qrack190/sv_user.c qrack/sv_user.c
--- qrack190/sv_user.c	2008-09-29 14:16:40.000000000 +0200
+++ qrack/sv_user.c	2011-04-29 02:14:09.585366800 +0200
@@ -457,6 +457,32 @@ void SV_ReadClientMove (usercmd_t *move)
 		host_client-&gt;edict-&gt;v.impulse = i;
 }
 
+/*Spike: function to check client's reported protocol extensions*/
+void SV_ParsePext(char *s)
+{
+	int i;
+	char *tag;
+	char *val;
+
+	if (!host_client-&gt;pext_unknown)
+		return;
+	host_client-&gt;pext_unknown = false;
+
+	Cmd_TokenizeString(s);
+	for (i = 1; i &lt; Cmd_Argc(); )
+	{
+		tag = Cmd_Argv(i++);
+		val = Cmd_Argv(i++);
+		switch(strtoul(tag, NULL, 0))
+		{
+		case PROTOCOL_VERSION_FTE2:
+			host_client-&gt;pext_fte2 = strtoul(val, NULL, 0) &amp; PROTOCOL_SUPPORTED_FTE2;
+			break;
+		}
+	}
+	SV_SendServerinfo(host_client);
+}
+
 /*
 ===================
 SV_ReadClientMessage
@@ -511,7 +537,9 @@ nextmsg:
 			case clc_stringcmd:
 				s = MSG_ReadString ();
 				ret = 0;
-		
+				if (!Q_strncasecmp(s, "pext", 4))/*this one added by Spike*/
+					SV_ParsePext(s);
+				else
 				if (	!Q_strncasecmp(s, "status", 6)	||
 					!Q_strncasecmp(s, "name", 4)	||
 					!Q_strncasecmp(s, "say", 3)	||
@@ -536,7 +564,12 @@ nextmsg:
 					!Q_strncasecmp(s, "monster", 7)	||
 					!Q_strncasecmp(s, "scrag", 5)	||
 					!Q_strncasecmp(s, "gimme", 5)	||
-					!Q_strncasecmp(s, "wraith", 6)
+					!Q_strncasecmp(s, "wraith", 6)	||
+				/*Spike: voice chat features*/
+					!Q_strncasecmp(s, "muteall", 7)	||
+					!Q_strncasecmp(s, "unmuteall", 9)	||
+					!Q_strncasecmp(s, "vignore", 7)	||
+					!Q_strncasecmp(s, "voicetarg", 9)
 					)
 				{
 					ret = 1;
@@ -554,6 +587,10 @@ nextmsg:
 			case clc_move:
 				SV_ReadClientMove (&amp;host_client-&gt;cmd);
 				break;
+
+			case clcfte_voicechat:
+				SV_VoiceReadPacket(host_client);
+				break;
 			}
 		}
 	} while (ret == 1);
</pre></body></html>