From 8b57bcb1deffad227731484e52d631277fe3d521 Mon Sep 17 00:00:00 2001
From: havoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Date: Wed, 23 Nov 2005 03:00:34 +0000
Subject: [PATCH] implemented 7.1 audio, only works with SDL (attempted ALSA
 support but ALSA doesn't seem to like mmap access to 4/6/8 channel buffers)

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@5818 d7cf8633-e32d-0410-b094-e92efae38249
---
 snd_alsa.c | 341 +++++++++++++-----------------
 snd_bsd.c  |   3 +-
 snd_main.c | 310 +++++++++++++++++++--------
 snd_main.h |   9 +-
 snd_mix.c  | 600 ++++++++++++++++++++++++++++++++++++++---------------
 snd_oss.c  |   3 +-
 snd_sdl.c  |  88 ++++----
 snd_win.c  |   2 +
 8 files changed, 864 insertions(+), 492 deletions(-)

diff --git a/snd_alsa.c b/snd_alsa.c
index 95310d84..4328a603 100644
--- a/snd_alsa.c
+++ b/snd_alsa.c
@@ -39,232 +39,177 @@ static snd_pcm_t   *pcm;
 
 qboolean SNDDMA_Init (void)
 {
-	int					 err, i;
-	int					 bps = -1, stereo = -1;
-	unsigned int		 rate = 0;
+	int					err, i, j;
+	int					width;
+	int					channels;
+	unsigned int		rate;
 	snd_pcm_hw_params_t	*hw;
 	snd_pcm_sw_params_t	*sw;
-	snd_pcm_uframes_t	 frag_size;
+	snd_pcm_uframes_t	frag_size;
 
 	snd_pcm_hw_params_alloca (&hw);
 	snd_pcm_sw_params_alloca (&sw);
 
-// COMMANDLINEOPTION: Linux ALSA Sound: -sndpcm <devicename> selects which pcm device to us, default is "default"
-	if ((i=COM_CheckParm("-sndpcm"))!=0)
-		pcmname=com_argv[i+1];
-	if (!pcmname)
-		pcmname = "default";
-
 // COMMANDLINEOPTION: Linux ALSA Sound: -sndbits <number> sets sound precision to 8 or 16 bit (email me if you want others added)
+	width = 2;
 	if ((i=COM_CheckParm("-sndbits")) != 0)
 	{
-		bps = atoi(com_argv[i+1]);
-		if (bps != 16 && bps != 8)
-		{
-			Con_Printf("Error: invalid sample bits: %d\n", bps);
-			return false;
-		}
+		j = atoi(com_argv[i+1]);
+		if (j == 16 || j == 8)
+			width = j / 8;
+		else
+			Con_Printf("Error: invalid sample bits: %d\n", j);
 	}
 
 // COMMANDLINEOPTION: Linux ALSA Sound: -sndspeed <hz> chooses 44100 hz, 22100 hz, or 11025 hz sound output rate
+	rate = 44100;
 	if ((i=COM_CheckParm("-sndspeed")) != 0)
 	{
-		rate = atoi(com_argv[i+1]);
-		if (rate!=44100 && rate!=22050 && rate!=11025)
-		{
+		j = atoi(com_argv[i+1]);
+		if (j >= 1)
+			rate = j;
+		else
 			Con_Printf("Error: invalid sample rate: %d\n", rate);
-			return false;
-		}
 	}
 
+	for (channels = 8;channels >= 1;channels--)
+	{
+		if ((channels & 1) && channels != 1)
+			continue;
 // COMMANDLINEOPTION: Linux ALSA Sound: -sndmono sets sound output to mono
-	if ((i=COM_CheckParm("-sndmono")) != 0)
-		stereo=0;
+		if ((i=COM_CheckParm("-sndmono")) != 0)
+			if (channels != 1)
+				continue;
 // COMMANDLINEOPTION: Linux ALSA Sound: -sndstereo sets sound output to stereo
-	if ((i=COM_CheckParm("-sndstereo")) != 0)
-		stereo=1;
+		if ((i=COM_CheckParm("-sndstereo")) != 0)
+			if (channels != 2)
+				continue;
 
-	err = snd_pcm_open (&pcm, pcmname, SND_PCM_STREAM_PLAYBACK,
-						  SND_PCM_NONBLOCK);
-	if (0 > err) {
-		Con_Printf ("Error: audio open error: %s\n", snd_strerror (err));
-		return 0;
-	}
-	Con_Printf ("ALSA: Using PCM %s.\n", pcmname);
+// COMMANDLINEOPTION: Linux ALSA Sound: -sndpcm <devicename> selects which pcm device to us, default is "default"
+		if (channels == 8)
+			pcmname = "surround71";
+		else if (channels == 6)
+			pcmname = "surround51";
+		else if (channels == 4)
+			pcmname = "surround40";
+		else
+			pcmname = "default";
+		if ((i=COM_CheckParm("-sndpcm"))!=0)
+			pcmname = com_argv[i+1];
+
+		Con_Printf ("ALSA: Trying PCM %s.\n", pcmname);
+
+		err = snd_pcm_open (&pcm, pcmname, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+		if (0 > err)
+		{
+			Con_Printf ("Error: audio open error: %s\n", snd_strerror (err));
+			continue;
+		}
 
-	err = snd_pcm_hw_params_any (pcm, hw);
-	if (0 > err) {
-		Con_Printf ("ALSA: error setting hw_params_any. %s\n",
-					snd_strerror (err));
-		goto error;
-	}
+		err = snd_pcm_hw_params_any (pcm, hw);
+		if (0 > err)
+		{
+			Con_Printf ("ALSA: error setting hw_params_any. %s\n", snd_strerror (err));
+			snd_pcm_close (pcm);
+			continue;
+		}
 
-	err = snd_pcm_hw_params_set_access (pcm, hw,
-										  SND_PCM_ACCESS_MMAP_INTERLEAVED);
-	if (0 > err) {
-		Con_Printf ("ALSA: Failure to set noninterleaved PCM access. %s\n"
-					"Note: Interleaved is not supported\n",
-					snd_strerror (err));
-		goto error;
-	}
+		err = snd_pcm_hw_params_set_access (pcm, hw, SND_PCM_ACCESS_MMAP_INTERLEAVED);
+		if (0 > err)
+		{
+			Con_Printf ("ALSA: Failure to set interleaved mmap PCM access. %s\n", snd_strerror (err));
+			snd_pcm_close (pcm);
+			continue;
+		}
 
-	switch (bps) {
-		case -1:
-			err = snd_pcm_hw_params_set_format (pcm, hw,
-												  SND_PCM_FORMAT_S16);
-			if (0 <= err) {
-				bps = 16;
-			} else if (0 <= (err = snd_pcm_hw_params_set_format (pcm, hw,
-														 SND_PCM_FORMAT_U8))) {
-				bps = 8;
-			} else {
-				Con_Printf ("ALSA: no useable formats. %s\n",
-							snd_strerror (err));
-				goto error;
-			}
-			break;
-		case 8:
-		case 16:
-			err = snd_pcm_hw_params_set_format (pcm, hw, bps == 8 ?
-												  SND_PCM_FORMAT_U8 :
-												  SND_PCM_FORMAT_S16);
-			if (0 > err) {
-				Con_Printf ("ALSA: no usable formats. %s\n",
-							snd_strerror (err));
-				goto error;
-			}
-			break;
-		default:
-			Con_Printf ("ALSA: desired format not supported\n");
-			goto error;
-	}
+		err = snd_pcm_hw_params_set_format (pcm, hw, width == 1 ? SND_PCM_FORMAT_U8 : SND_PCM_FORMAT_S16);
+		if (0 > err)
+		{
+			Con_Printf ("ALSA: desired bits %i not supported by driver.  %s\n", width * 8, snd_strerror (err));
+			snd_pcm_close (pcm);
+			continue;
+		}
 
-	switch (stereo) {
-		case -1:
-			err = snd_pcm_hw_params_set_channels (pcm, hw, 2);
-			if (0 <= err) {
-				stereo = 1;
-			} else if (0 <= (err = snd_pcm_hw_params_set_channels (pcm, hw,
-																	 1))) {
-				stereo = 0;
-			} else {
-				Con_Printf ("ALSA: no usable channels. %s\n",
-							snd_strerror (err));
-				goto error;
-			}
-			break;
-		case 0:
-		case 1:
-			err = snd_pcm_hw_params_set_channels (pcm, hw, stereo ? 2 : 1);
-			if (0 > err) {
-				Con_Printf ("ALSA: no usable channels. %s\n",
-							snd_strerror (err));
-				goto error;
-			}
-			break;
-		default:
-			Con_Printf ("ALSA: desired channels not supported\n");
-			goto error;
-	}
+		err = snd_pcm_hw_params_set_channels (pcm, hw, channels);
+		if (0 > err)
+		{
+			Con_Printf ("ALSA: no usable channels. %s\n", snd_strerror (err));
+			snd_pcm_close (pcm);
+			continue;
+		}
 
-	switch (rate) {
-		case 0:
-			rate = 44100;
-			err = snd_pcm_hw_params_set_rate_near (pcm, hw, &rate, 0);
-			if (0 <= err) {
-				frag_size = 32 * bps;
-			} else {
-				rate = 22050;
-				err = snd_pcm_hw_params_set_rate_near (pcm, hw, &rate, 0);
-				if (0 <= err) {
-					frag_size = 16 * bps;
-				} else {
-					rate = 11025;
-					err = snd_pcm_hw_params_set_rate_near (pcm, hw, &rate,
-															 0);
-					if (0 <= err) {
-						frag_size = 8 * bps;
-					} else {
-						Con_Printf ("ALSA: no usable rates. %s\n",
-									snd_strerror (err));
-						goto error;
-					}
-				}
-			}
-			break;
-		case 11025:
-		case 22050:
-		case 44100:
-			err = snd_pcm_hw_params_set_rate_near (pcm, hw, &rate, 0);
-			if (0 > err) {
-				Con_Printf ("ALSA: desired rate %i not supported. %s\n", rate,
-							snd_strerror (err));
-				goto error;
-			}
-			frag_size = 8 * bps * rate / 11025;
-			break;
-		default:
-			Con_Printf ("ALSA: desired rate %i not supported.\n", rate);
-			goto error;
-	}
+		err = snd_pcm_hw_params_set_rate_near (pcm, hw, &rate, 0);
+		if (0 > err)
+		{
+			Con_Printf ("ALSA: desired rate %i not supported by driver. %s\n", rate, snd_strerror (err));
+			snd_pcm_close (pcm);
+			continue;
+		}
 
-	err = snd_pcm_hw_params_set_period_size_near (pcm, hw, &frag_size, 0);
-	if (0 > err) {
-		Con_Printf ("ALSA: unable to set period size near %i. %s\n",
-					(int) frag_size, snd_strerror (err));
-		goto error;
-	}
-	err = snd_pcm_hw_params (pcm, hw);
-	if (0 > err) {
-		Con_Printf ("ALSA: unable to install hw params: %s\n",
-					snd_strerror (err));
-		goto error;
-	}
-	err = snd_pcm_sw_params_current (pcm, sw);
-	if (0 > err) {
-		Con_Printf ("ALSA: unable to determine current sw params. %s\n",
-					snd_strerror (err));
-		goto error;
-	}
-	err = snd_pcm_sw_params_set_start_threshold (pcm, sw, ~0U);
-	if (0 > err) {
-		Con_Printf ("ALSA: unable to set playback threshold. %s\n",
-					snd_strerror (err));
-		goto error;
-	}
-	err = snd_pcm_sw_params_set_stop_threshold (pcm, sw, ~0U);
-	if (0 > err) {
-		Con_Printf ("ALSA: unable to set playback stop threshold. %s\n",
-					snd_strerror (err));
-		goto error;
-	}
-	err = snd_pcm_sw_params (pcm, sw);
-	if (0 > err) {
-		Con_Printf ("ALSA: unable to install sw params. %s\n",
-					snd_strerror (err));
-		goto error;
-	}
+		frag_size = 64 * width * rate / 11025;
+		err = snd_pcm_hw_params_set_period_size_near (pcm, hw, &frag_size, 0);
+		if (0 > err)
+		{
+			Con_Printf ("ALSA: unable to set period size near %i. %s\n", (int) frag_size, snd_strerror (err));
+			snd_pcm_close (pcm);
+			continue;
+		}
+		err = snd_pcm_hw_params (pcm, hw);
+		if (0 > err)
+		{
+			Con_Printf ("ALSA: unable to install hw params: %s\n", snd_strerror (err));
+			snd_pcm_close (pcm);
+			continue;
+		}
+		err = snd_pcm_sw_params_current (pcm, sw);
+		if (0 > err)
+		{
+			Con_Printf ("ALSA: unable to determine current sw params. %s\n", snd_strerror (err));
+			snd_pcm_close (pcm);
+			continue;
+		}
+		err = snd_pcm_sw_params_set_start_threshold (pcm, sw, ~0U);
+		if (0 > err)
+		{
+			Con_Printf ("ALSA: unable to set playback threshold. %s\n", snd_strerror (err));
+			snd_pcm_close (pcm);
+			continue;
+		}
+		err = snd_pcm_sw_params_set_stop_threshold (pcm, sw, ~0U);
+		if (0 > err)
+		{
+			Con_Printf ("ALSA: unable to set playback stop threshold. %s\n", snd_strerror (err));
+			snd_pcm_close (pcm);
+			continue;
+		}
+		err = snd_pcm_sw_params (pcm, sw);
+		if (0 > err)
+		{
+			Con_Printf ("ALSA: unable to install sw params. %s\n", snd_strerror (err));
+			snd_pcm_close (pcm);
+			continue;
+		}
 
-	shm->format.channels = stereo + 1;
-	shm->samplepos = 0;
-	shm->format.width = bps / 8;
+		err = snd_pcm_hw_params_get_buffer_size (hw, &buffer_size);
+		if (0 > err)
+		{
+			Con_Printf ("ALSA: unable to get buffer size. %s\n", snd_strerror (err));
+			snd_pcm_close (pcm);
+			continue;
+		}
 
-	err = snd_pcm_hw_params_get_buffer_size (hw, &buffer_size);
-	if (0 > err) {
-		Con_Printf ("ALSA: unable to get buffer size. %s\n",
-					snd_strerror (err));
-		goto error;
+		memset( (void*) shm, 0, sizeof(*shm) );
+		shm->format.channels = channels;
+		shm->format.width = width;
+		shm->format.speed = rate;
+		shm->samplepos = 0;
+		shm->sampleframes = buffer_size;
+		shm->samples = shm->sampleframes * shm->format.channels;
+		SNDDMA_GetDMAPos ();		// sets shm->buffer
+
+		snd_inited = 1;
+		return true;
 	}
-
-	shm->samples = buffer_size * shm->format.channels;		// mono samples in buffer
-	shm->format.speed = rate;
-	SNDDMA_GetDMAPos ();		// sets shm->buffer
-
-	snd_inited = 1;
-	return true;
-
-error:
-	snd_pcm_close (pcm);
 	return false;
 }
 
@@ -272,7 +217,7 @@ int SNDDMA_GetDMAPos (void)
 {
 	const snd_pcm_channel_area_t *areas;
 	snd_pcm_uframes_t offset;
-	snd_pcm_uframes_t nframes = shm->samples/shm->format.channels;
+	snd_pcm_uframes_t nframes = shm->sampleframes;
 
 	if (!snd_inited)
 		return 0;
diff --git a/snd_bsd.c b/snd_bsd.c
index caa7ee0c..ef22f5bc 100644
--- a/snd_bsd.c
+++ b/snd_bsd.c
@@ -111,7 +111,8 @@ qboolean SNDDMA_Init (void)
 				(info.play.channels == 2) ? "stereo" : "mono",
 				info.play.sample_rate);
 
-	shm->samples = sizeof (dma_buffer) / shm->format.width;
+	shm->sampleframes = sizeof (dma_buffer) / shm->format.width / shm->format.channels;
+	shm->samples = shm->sampleframes * shm->format.channels;
 	shm->samplepos = 0;
 	shm->buffer = dma_buffer;
 
diff --git a/snd_main.c b/snd_main.c
index 7a39a48a..0708a15b 100644
--- a/snd_main.c
+++ b/snd_main.c
@@ -24,6 +24,27 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "snd_main.h"
 #include "snd_ogg.h"
 
+#if SND_LISTENERS != 8
+#error this data only supports up to 8 channel, update it!
+#endif
+typedef struct listener_s
+{
+	float yawangle;
+	float dotscale;
+	float dotbias;
+	float ambientvolume;
+}
+listener_t;
+typedef struct speakerlayout_s
+{
+	const char *name;
+	unsigned int channels;
+	qboolean headphones;
+	listener_t listeners[SND_LISTENERS];
+}
+speakerlayout_t;
+
+static speakerlayout_t snd_speakerlayout;
 
 void S_Play(void);
 void S_PlayVol(void);
@@ -42,12 +63,13 @@ unsigned int total_channels;
 int snd_blocked = 0;
 cvar_t snd_initialized = { CVAR_READONLY, "snd_initialized", "0"};
 cvar_t snd_streaming = { CVAR_SAVE, "snd_streaming", "1"};
+cvar_t snd_headphones = { CVAR_SAVE, "snd_headphones", "0"};
 
 volatile dma_t *shm = 0;
 volatile dma_t sn;
 
 vec3_t listener_origin;
-matrix4x4_t listener_matrix;
+matrix4x4_t listener_matrix[SND_LISTENERS];
 vec_t sound_nominal_clip_dist=1000.0;
 mempool_t *snd_mempool;
 
@@ -98,7 +120,8 @@ void S_SoundInfo_f(void)
 		return;
 	}
 
-	Con_Printf("%5d stereo\n", shm->format.channels - 1);
+	Con_Printf("%5d speakers\n", shm->format.channels);
+	Con_Printf("%5d frames\n", shm->sampleframes);
 	Con_Printf("%5d samples\n", shm->samples);
 	Con_Printf("%5d samplepos\n", shm->samplepos);
 	Con_Printf("%5d samplebits\n", shm->format.width * 8);
@@ -122,9 +145,10 @@ void S_Startup(void)
 		shm->format.width = 2;
 		shm->format.speed = 22050;
 		shm->format.channels = 2;
-		shm->samples = 32768;
+		shm->sampleframes = 16384;
+		shm->samples = shm->sampleframes * shm->format.channels;
 		shm->samplepos = 0;
-		shm->buffer = (unsigned char *)Mem_Alloc(snd_mempool, shm->format.channels * shm->samples * shm->format.width);
+		shm->buffer = (unsigned char *)Mem_Alloc(snd_mempool, shm->samples * shm->format.width);
 	}
 	else
 	{
@@ -177,6 +201,7 @@ void S_Init(void)
 	Cvar_RegisterVariable(&volume);
 	Cvar_RegisterVariable(&bgmvolume);
 	Cvar_RegisterVariable(&snd_staticvolume);
+	Cvar_RegisterVariable(&snd_headphones);
 
 // COMMANDLINEOPTION: Sound: -nosound disables sound (including CD audio)
 	if (COM_CheckParm("-nosound") || COM_CheckParm("-safe"))
@@ -459,22 +484,36 @@ channel_t *SND_PickChannel(int entnum, int entchannel)
 	for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++)
 	{
 		ch = &channels[ch_idx];
-		if (entchannel != 0		// channel 0 never overrides
-		&& ch->entnum == entnum
-		&& (ch->entchannel == entchannel || entchannel == -1) )
-		{	// always override sound from same entity
-			first_to_die = ch_idx;
-			break;
+		if (entchannel != 0)
+		{
+			// try to override an existing channel
+			if (ch->entnum == entnum && (ch->entchannel == entchannel || entchannel == -1) )
+			{
+				// always override sound from same entity
+				first_to_die = ch_idx;
+				break;
+			}
+		}
+		else
+		{
+			if (!ch->sfx)
+			{
+				// no sound on this channel
+				first_to_die = ch_idx;
+				break;
+			}
 		}
 
-		// don't let monster sounds override player sounds
-		if (ch->entnum == cl.viewentity && entnum != cl.viewentity && ch->sfx)
-			continue;
+		if (ch->sfx)
+		{
+			// don't let monster sounds override player sounds
+			if (ch->entnum == cl.viewentity && entnum != cl.viewentity)
+				continue;
 
-		// don't override looped sounds
-		if ((ch->flags & CHANNELFLAG_FORCELOOP) != 0 ||
-			(ch->sfx != NULL && ch->sfx->loopstart >= 0))
-			continue;
+			// don't override looped sounds
+			if ((ch->flags & CHANNELFLAG_FORCELOOP) || ch->sfx->loopstart >= 0)
+				continue;
+		}
 
 		if (ch->end - paintedtime < life_left)
 		{
@@ -500,48 +539,54 @@ Spatializes a channel
 */
 void SND_Spatialize(channel_t *ch, qboolean isstatic)
 {
-	vec_t dist, scale, pan;
+	int i;
+	vec_t dist, mastervol, intensity, vol;
 	vec3_t source_vec;
 
+	// update sound origin if we know about the entity
+	if (ch->entnum > 0 && cls.state == ca_connected && cl_entities[ch->entnum].state_current.active)
+	{
+		//Con_Printf("-- entnum %i origin %f %f %f neworigin %f %f %f\n", ch->entnum, ch->origin[0], ch->origin[1], ch->origin[2], cl_entities[ch->entnum].state_current.origin[0], cl_entities[ch->entnum].state_current.origin[1], cl_entities[ch->entnum].state_current.origin[2]);
+		VectorCopy(cl_entities[ch->entnum].state_current.origin, ch->origin);
+		if (cl_entities[ch->entnum].state_current.modelindex && cl.model_precache[cl_entities[ch->entnum].state_current.modelindex] && cl.model_precache[cl_entities[ch->entnum].state_current.modelindex]->soundfromcenter)
+			VectorMAMAM(1.0f, ch->origin, 0.5f, cl.model_precache[cl_entities[ch->entnum].state_current.modelindex]->normalmins, 0.5f, cl.model_precache[cl_entities[ch->entnum].state_current.modelindex]->normalmaxs, ch->origin);
+	}
+
+	mastervol = ch->master_vol;
+	// Adjust volume of static sounds
+	if (isstatic)
+		mastervol *= snd_staticvolume.value;
+
 	// anything coming from the view entity will always be full volume
 	// LordHavoc: make sounds with ATTN_NONE have no spatialization
 	if (ch->entnum == cl.viewentity || ch->dist_mult == 0)
 	{
-		ch->leftvol = ch->master_vol;
-		ch->rightvol = ch->master_vol;
+		for (i = 0;i < SND_LISTENERS;i++)
+		{
+			vol = mastervol * snd_speakerlayout.listeners[i].ambientvolume;
+			ch->listener_volume[i] = bound(0, vol, 255);
+		}
 	}
 	else
 	{
-		// update sound origin if we know about the entity
-		if (ch->entnum > 0 && cls.state == ca_connected && cl_entities[ch->entnum].render.model)
+		// calculate stereo seperation and distance attenuation
+		VectorSubtract(listener_origin, ch->origin, source_vec);
+		dist = VectorLength(source_vec);
+		intensity = mastervol * (1.0 - dist * ch->dist_mult);
+		if (intensity > 0)
 		{
-			//Con_Printf("-- entnum %i origin %f %f %f neworigin %f %f %f\n", ch->entnum, ch->origin[0], ch->origin[1], ch->origin[2], cl_entities[ch->entnum].persistent.trail_origin[0], cl_entities[ch->entnum].persistent.trail_origin[1], cl_entities[ch->entnum].persistent.trail_origin[2]);
-			VectorCopy(cl_entities[ch->entnum].persistent.trail_origin, ch->origin);
+			for (i = 0;i < SND_LISTENERS;i++)
+			{
+				Matrix4x4_Transform(&listener_matrix[i], ch->origin, source_vec);
+				VectorNormalize(source_vec);
+				vol = intensity * max(0, source_vec[0] * snd_speakerlayout.listeners[i].dotscale + snd_speakerlayout.listeners[i].dotbias);
+				ch->listener_volume[i] = bound(0, vol, 255);
+			}
 		}
-
-		// calculate stereo seperation and distance attenuation
-		Matrix4x4_Transform(&listener_matrix, ch->origin, source_vec);
-		dist = VectorNormalizeLength(source_vec);
-		// distance
-		scale = ch->master_vol * (1.0 - (dist * ch->dist_mult));
-		// panning
-		pan = scale * source_vec[1];
-		// calculate the volumes
-		ch->leftvol = (int) (scale + pan);
-		ch->rightvol = (int) (scale - pan);
-		//Con_Printf("%f %f %f:%f %f %f:%f %f:%d %d\n", ch->origin[0], ch->origin[1], ch->origin[2], source_vec[0], source_vec[1], source_vec[2], scale, pan, ch->leftvol, ch->rightvol);
-	}
-
-	// Adjust volume of static sounds
-	if (isstatic)
-	{
-		ch->leftvol *= snd_staticvolume.value;
-		ch->rightvol *= snd_staticvolume.value;
+		else
+			for (i = 0;i < SND_LISTENERS;i++)
+				ch->listener_volume[i] = 0;
 	}
-
-	// clamp volumes
-	ch->leftvol = bound(0, ch->leftvol, 255);
-	ch->rightvol = bound(0, ch->rightvol, 255);
 }
 
 
@@ -777,15 +822,15 @@ S_UpdateAmbientSounds
 */
 void S_UpdateAmbientSounds (void)
 {
+	int			i;
 	float		vol;
 	int			ambient_channel;
 	channel_t	*chan;
 	unsigned char		ambientlevels[NUM_AMBIENTS];
 
-	if (ambient_level.value <= 0 || !cl.worldmodel || !cl.worldmodel->brush.AmbientSoundLevelsForPoint)
-		return;
-
-	cl.worldmodel->brush.AmbientSoundLevelsForPoint(cl.worldmodel, listener_origin, ambientlevels, sizeof(ambientlevels));
+	memset(ambientlevels, 0, sizeof(ambientlevels));
+	if (cl.worldmodel && cl.worldmodel->brush.AmbientSoundLevelsForPoint)
+		cl.worldmodel->brush.AmbientSoundLevelsForPoint(cl.worldmodel, listener_origin, ambientlevels, sizeof(ambientlevels));
 
 	// Calc ambient sound levels
 	for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++)
@@ -794,7 +839,7 @@ void S_UpdateAmbientSounds (void)
 		if (chan->sfx == NULL || chan->sfx->fetcher == NULL)
 			continue;
 
-		vol = ambient_level.value * ambientlevels[ambient_channel];
+		vol = ambientlevels[ambient_channel];
 		if (vol < 8)
 			vol = 0;
 
@@ -812,10 +857,84 @@ void S_UpdateAmbientSounds (void)
 				chan->master_vol = vol;
 		}
 
-		chan->leftvol = chan->rightvol = chan->master_vol;
+		for (i = 0;i < SND_LISTENERS;i++)
+			chan->listener_volume[i] = (int)(chan->master_vol * ambient_level.value * snd_speakerlayout.listeners[i].ambientvolume);
 	}
 }
 
+#define SND_SPEAKERLAYOUTS 5
+static speakerlayout_t snd_speakerlayouts[SND_SPEAKERLAYOUTS] =
+{
+	{
+		"surround71", 8, false,
+		{
+			{45, 0.2, 0.2, 0.5}, // front left
+			{315, 0.2, 0.2, 0.5}, // front right
+			{135, 0.2, 0.2, 0.5}, // rear left
+			{225, 0.2, 0.2, 0.5}, // rear right
+			{0, 0.2, 0.2, 0.5}, // front center
+			{0, 0, 0, 0}, // lfe (we don't have any good lfe sound sources and it would take some filtering work to generate them (and they'd probably still be wrong), so...  no lfe)
+			{90, 0.2, 0.2, 0.5}, // side left
+			{180, 0.2, 0.2, 0.5}, // side right
+		}
+	},
+	{
+		"surround51", 6, false,
+		{
+			{45, 0.2, 0.2, 0.5}, // front left
+			{315, 0.2, 0.2, 0.5}, // front right
+			{135, 0.2, 0.2, 0.5}, // rear left
+			{225, 0.2, 0.2, 0.5}, // rear right
+			{0, 0.2, 0.2, 0.5}, // front center
+			{0, 0, 0, 0}, // lfe (we don't have any good lfe sound sources and it would take some filtering work to generate them (and they'd probably still be wrong), so...  no lfe)
+			{0, 0, 0, 0},
+			{0, 0, 0, 0},
+		}
+	},
+	{
+		// these systems sometimes have a subwoofer as well, but it has no
+		// channel of its own
+		"surround40", 4, false,
+		{
+			{45, 0.3, 0.3, 0.8}, // front left
+			{315, 0.3, 0.3, 0.8}, // front right
+			{135, 0.3, 0.3, 0.8}, // rear left
+			{225, 0.3, 0.3, 0.8}, // rear right
+			{0, 0, 0, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0},
+		}
+	},
+	{
+		"headphones", 2, true,
+		{
+			{90, 0.5, 0.5, 1}, // side left
+			{270, 0.5, 0.5, 1}, // side right
+			{0, 0, 0, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0},
+		}
+	},
+	{
+		// these systems sometimes have a subwoofer as well, but it has no
+		// channel of its own
+		"stereo", 2, false,
+		{
+			{45, 0.5, 0.5, 1}, // front left
+			{315, 0.5, 0.5, 1}, // front right
+			{0, 0, 0, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0},
+			{0, 0, 0, 0},
+		}
+	}
+};
 
 /*
 ============
@@ -828,13 +947,27 @@ void S_Update(const matrix4x4_t *listenermatrix)
 {
 	unsigned int i, j, total;
 	channel_t *ch, *combine;
+	matrix4x4_t basematrix, rotatematrix;
 
 	if (!snd_initialized.integer || (snd_blocked > 0))
 		return;
 
-	Matrix4x4_Invert_Simple(&listener_matrix, listenermatrix);
+	Matrix4x4_Invert_Simple(&basematrix, listenermatrix);
 	Matrix4x4_OriginFromMatrix(listenermatrix, listener_origin);
 
+	// select speaker layout
+	for (i = 0;i < SND_SPEAKERLAYOUTS - 1;i++)
+		if (snd_speakerlayouts[i].channels == shm->format.channels && (!snd_speakerlayouts[i].headphones || snd_headphones.integer))
+			break;
+	snd_speakerlayout = snd_speakerlayouts[i];
+
+	// calculate the current matrices
+	for (j = 0;j < SND_LISTENERS;j++)
+	{
+		Matrix4x4_CreateFromQuakeEntity(&rotatematrix, 0, 0, 0, 0, snd_speakerlayout.listeners[j].yawangle, 0, 1);
+		Matrix4x4_Concat(&listener_matrix[j], &basematrix, &rotatematrix);
+	}
+
 	// update general area ambient sound sources
 	S_UpdateAmbientSounds ();
 
@@ -846,39 +979,41 @@ void S_Update(const matrix4x4_t *listenermatrix)
 	{
 		if (!ch->sfx)
 			continue;
-		SND_Spatialize(ch, i >= MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS);         // respatialize channel
-		if (!ch->leftvol && !ch->rightvol)
-			continue;
+
+		// respatialize channel
+		SND_Spatialize(ch, i >= MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS);
 
 		// try to combine static sounds with a previous channel of the same
 		// sound effect so we don't mix five torches every frame
 		if (i > MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS)
 		{
-			// see if it can just use the last one
-			if (combine && combine->sfx == ch->sfx)
-			{
-				combine->leftvol += ch->leftvol;
-				combine->rightvol += ch->rightvol;
-				ch->leftvol = ch->rightvol = 0;
-				continue;
-			}
-			// search for one
-			combine = channels+MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS;
-			for (j=MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS ; j<i; j++, combine++)
-				if (combine->sfx == ch->sfx)
+			// no need to merge silent channels
+			for (j = 0;j < SND_LISTENERS;j++)
+				if (ch->listener_volume[j])
 					break;
-
-			if (j == total_channels)
+			if (j == SND_LISTENERS)
+				continue;
+			// if the last combine chosen isn't suitable, find a new one
+			if (!(combine && combine != ch && combine->sfx == ch->sfx))
+			{
+				// search for one
 				combine = NULL;
-			else
+				for (j = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS;j < i;j++)
+				{
+					if (channels[j].sfx == ch->sfx)
+					{
+						combine = channels + j;
+						break;
+					}
+				}
+			}
+			if (combine && combine != ch && combine->sfx == ch->sfx)
 			{
-				if (combine != ch)
+				for (j = 0;j < SND_LISTENERS;j++)
 				{
-					combine->leftvol += ch->leftvol;
-					combine->rightvol += ch->rightvol;
-					ch->leftvol = ch->rightvol = 0;
+					combine->listener_volume[j] += ch->listener_volume[j];
+					ch->listener_volume[j] = 0;
 				}
-				continue;
 			}
 		}
 	}
@@ -891,8 +1026,16 @@ void S_Update(const matrix4x4_t *listenermatrix)
 		total = 0;
 		ch = channels;
 		for (i=0 ; i<total_channels; i++, ch++)
-			if (ch->sfx && (ch->leftvol || ch->rightvol) )
-				total++;
+		{
+			if (ch->sfx)
+			{
+				for (j = 0;j < SND_LISTENERS;j++)
+					if (ch->listener_volume[j])
+						break;
+				if (j < SND_LISTENERS)
+					total++;
+			}
+		}
 
 		Con_Printf("----(%u)----\n", total);
 	}
@@ -907,7 +1050,7 @@ void GetSoundtime(void)
 	static	int		oldsamplepos;
 	int		fullsamples;
 
-	fullsamples = shm->samples / shm->format.channels;
+	fullsamples = shm->sampleframes;
 
 	// it is possible to miscount buffers if it has wrapped twice between
 	// calls to S_Update.  Oh well.
@@ -940,7 +1083,6 @@ void S_ExtraUpdate (void)
 void S_Update_(void)
 {
 	unsigned        endtime;
-	int				samps;
 
 	if (!sound_started || (snd_blocked > 0))
 		return;
@@ -954,9 +1096,7 @@ void S_Update_(void)
 
 	// mix ahead of current position
 	endtime = soundtime + _snd_mixahead.value * shm->format.speed;
-	samps = shm->samples >> (shm->format.channels - 1);
-	if (endtime > (unsigned int)(soundtime + samps))
-		endtime = soundtime + samps;
+	endtime = min(endtime, (unsigned int)(soundtime + shm->sampleframes));
 
 	S_PaintChannels (endtime);
 
@@ -974,7 +1114,7 @@ console functions
 static void S_Play_Common(float fvol, float attenuation)
 {
 	int 	i, ch_ind;
-	char name[256];
+	char name[MAX_QPATH];
 	sfx_t	*sfx;
 
 	i = 1;
diff --git a/snd_main.h b/snd_main.h
index fbf2f3e4..ec097d22 100644
--- a/snd_main.h
+++ b/snd_main.h
@@ -67,19 +67,24 @@ struct sfx_s
 typedef struct dma_s
 {
 	snd_format_t	format;
+	int				sampleframes;	// frames in buffer (frame = samples for all speakers)
 	int				samples;		// mono samples in buffer
 	int				samplepos;		// in mono samples
 	unsigned char	*buffer;
 	int				bufferlength;	// used only by certain drivers
 } dma_t;
 
+// maximum supported speakers constant
+#define SND_LISTENERS 8
+
 typedef struct channel_s
 {
+	int pad[8];
 	sfx_t			*sfx;			// sfx number
+	int pad2[8];
 	unsigned int	flags;			// cf CHANNELFLAG_* defines
 	int				master_vol;		// 0-255 master volume
-	int				leftvol;		// 0-255 volume
-	int				rightvol;		// 0-255 volume
+	short			listener_volume[SND_LISTENERS];		// 0-255 volume per speaker
 	int				end;			// end time in global paintsamples
 	int				lastptime;		// last time this channel was painted
 	int				pos;			// sample position in sfx
diff --git a/snd_mix.c b/snd_mix.c
index d2329f40..f4a857d3 100644
--- a/snd_mix.c
+++ b/snd_mix.c
@@ -24,18 +24,17 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 typedef struct portable_samplepair_s
 {
-	int left;
-	int right;
-} portable_samplepair_t;
+	int sample[SND_LISTENERS];
+} portable_sampleframe_t;
 
 // LordHavoc: was 512, expanded to 2048
 #define	PAINTBUFFER_SIZE 2048
-portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE];
+portable_sampleframe_t paintbuffer[PAINTBUFFER_SIZE];
 
 // FIXME: this desyncs with the video too easily
 extern qboolean cl_capturevideo_active;
 extern void SCR_CaptureVideo_SoundFrame(unsigned char *bufstereo16le, size_t length, int rate);
-void S_CaptureAVISound(portable_samplepair_t *buf, size_t length)
+void S_CaptureAVISound(portable_sampleframe_t *buf, size_t length)
 {
 	int n;
 	size_t i;
@@ -45,11 +44,11 @@ void S_CaptureAVISound(portable_samplepair_t *buf, size_t length)
 	// write the sound buffer as little endian 16bit interleaved stereo
 	for(i = 0;i < length;i++)
 	{
-		n = buf[i].left;
+		n = buf[i].sample[0];
 		n = bound(-32768, n, 32767);
 		out[i*4+0] = n & 0xFF;
 		out[i*4+1] = (n >> 8) & 0xFF;
-		n = buf[i].right;
+		n = buf[i].sample[1];
 		n = bound(-32768, n, 32767);
 		out[i*4+2] = n & 0xFF;
 		out[i*4+3] = (n >> 8) & 0xFF;
@@ -61,134 +60,253 @@ void S_CaptureAVISound(portable_samplepair_t *buf, size_t length)
 void S_TransferPaintBuffer(int endtime)
 {
 	void *pbuf;
+	int i;
+	portable_sampleframe_t *snd_p;
+	int lpaintedtime;
+	int snd_frames;
+	int val;
 	if ((pbuf = S_LockBuffer()))
 	{
-		int i;
-		int *snd_p;
-		int lpaintedtime;
-		int snd_linear_count;
-		int val;
-		snd_p = (int *) paintbuffer;
+		snd_p = paintbuffer;
 		lpaintedtime = paintedtime;
-		if (shm->format.width == 2)
+		for (lpaintedtime = paintedtime;lpaintedtime < endtime;lpaintedtime += snd_frames)
 		{
-			// 16bit
-			short *snd_out;
-			if (shm->format.channels == 2)
+			// handle recirculating buffer issues
+			i = lpaintedtime & (shm->sampleframes - 1);
+			snd_frames = shm->sampleframes - i;
+			if (snd_frames > endtime - lpaintedtime)
+				snd_frames = endtime - lpaintedtime;
+			if (shm->format.width == 2)
 			{
-				// 16bit 2 channels (stereo)
-				while (lpaintedtime < endtime)
+				// 16bit
+				short *snd_out = (short *) pbuf + i * shm->format.channels;
+				if (shm->format.channels == 8)
 				{
-					// handle recirculating buffer issues
-					i = lpaintedtime & ((shm->samples >> 1) - 1);
-					snd_out = (short *) pbuf + (i << 1);
-					snd_linear_count = (shm->samples >> 1) - i;
-					if (snd_linear_count > endtime - lpaintedtime)
-						snd_linear_count = endtime - lpaintedtime;
-					snd_linear_count <<= 1;
+					// 7.1 surround
 					if (snd_swapstereo.value)
 					{
-						for (i = 0;i < snd_linear_count;i += 2)
+						for (i = 0;i < snd_frames;i++, snd_p++)
 						{
-							snd_out[i    ] = bound(-32768, snd_p[i + 1], 32767);
-							snd_out[i + 1] = bound(-32768, snd_p[i    ], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[1], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[0], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[3], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[2], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[4], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[5], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[6], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[7], 32767);
 						}
 					}
 					else
 					{
-						for (i = 0;i < snd_linear_count;i += 2)
+						for (i = 0;i < snd_frames;i++, snd_p++)
 						{
-							snd_out[i    ] = bound(-32768, snd_p[i    ], 32767);
-							snd_out[i + 1] = bound(-32768, snd_p[i + 1], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[0], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[1], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[2], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[3], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[4], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[5], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[6], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[7], 32767);
 						}
 					}
-					snd_p += snd_linear_count;
-					lpaintedtime += (snd_linear_count >> 1);
 				}
-			}
-			else
-			{
-				// 16bit 1 channel (mono)
-				while (lpaintedtime < endtime)
+				else if (shm->format.channels == 6)
 				{
-					// handle recirculating buffer issues
-					i = lpaintedtime & (shm->samples - 1);
-					snd_out = (short *) pbuf + i;
-					snd_linear_count = shm->samples - i;
-					if (snd_linear_count > endtime - lpaintedtime)
-						snd_linear_count = endtime - lpaintedtime;
-					for (i = 0;i < snd_linear_count;i++)
+					// 5.1 surround
+					if (snd_swapstereo.value)
 					{
-						val = (snd_p[i * 2 + 0] + snd_p[i * 2 + 1]) >> 1;
-						snd_out[i] = bound(-32768, val, 32767);
+						for (i = 0;i < snd_frames;i++, snd_p++)
+						{
+							*snd_out++ = bound(-32768, snd_p->sample[1], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[0], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[3], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[2], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[4], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[5], 32767);
+						}
+					}
+					else
+					{
+						for (i = 0;i < snd_frames;i++, snd_p++)
+						{
+							*snd_out++ = bound(-32768, snd_p->sample[0], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[1], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[2], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[3], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[4], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[5], 32767);
+						}
 					}
-					snd_p += snd_linear_count << 1;
-					lpaintedtime += snd_linear_count;
 				}
-			}
-		}
-		else
-		{
-			// 8bit
-			unsigned char *snd_out;
-			if (shm->format.channels == 2)
-			{
-				// 8bit 2 channels (stereo)
-				while (lpaintedtime < endtime)
+				else if (shm->format.channels == 4)
+				{
+					// 4.0 surround
+					if (snd_swapstereo.value)
+					{
+						for (i = 0;i < snd_frames;i++, snd_p++)
+						{
+							*snd_out++ = bound(-32768, snd_p->sample[1], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[0], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[3], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[2], 32767);
+						}
+					}
+					else
+					{
+						for (i = 0;i < snd_frames;i++, snd_p++)
+						{
+							*snd_out++ = bound(-32768, snd_p->sample[0], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[1], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[2], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[3], 32767);
+						}
+					}
+				}
+				else if (shm->format.channels == 2)
 				{
-					// handle recirculating buffer issues
-					i = lpaintedtime & ((shm->samples >> 1) - 1);
-					snd_out = (unsigned char *) pbuf + (i << 1);
-					snd_linear_count = (shm->samples >> 1) - i;
-					if (snd_linear_count > endtime - lpaintedtime)
-						snd_linear_count = endtime - lpaintedtime;
-					snd_linear_count <<= 1;
+					// 2.0 stereo
 					if (snd_swapstereo.value)
 					{
-						for (i = 0;i < snd_linear_count;i += 2)
+						for (i = 0;i < snd_frames;i++, snd_p++)
 						{
-							val = (snd_p[i + 1] >> 8) + 128;
-							snd_out[i    ] = bound(0, val, 255);
-							val = (snd_p[i    ] >> 8) + 128;
-							snd_out[i + 1] = bound(0, val, 255);
+							*snd_out++ = bound(-32768, snd_p->sample[1], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[0], 32767);
 						}
 					}
 					else
 					{
-						for (i = 0;i < snd_linear_count;i += 2)
+						for (i = 0;i < snd_frames;i++, snd_p++)
 						{
-							val = (snd_p[i    ] >> 8) + 128;
-							snd_out[i    ] = bound(0, val, 255);
-							val = (snd_p[i + 1] >> 8) + 128;
-							snd_out[i + 1] = bound(0, val, 255);
+							*snd_out++ = bound(-32768, snd_p->sample[0], 32767);
+							*snd_out++ = bound(-32768, snd_p->sample[1], 32767);
 						}
 					}
-					snd_p += snd_linear_count;
-					lpaintedtime += (snd_linear_count >> 1);
+				}
+				else if (shm->format.channels == 1)
+				{
+					// 1.0 mono
+					for (i = 0;i < snd_frames;i++, snd_p++)
+						*snd_out++ = bound(-32768, (snd_p->sample[0] + snd_p->sample[1]) >> 1, 32767);
 				}
 			}
 			else
 			{
-				// 8bit 1 channel (mono)
-				while (lpaintedtime < endtime)
+				// 8bit
+				unsigned char *snd_out = (unsigned char *) pbuf + i * shm->format.channels;
+				if (shm->format.channels == 8)
+				{
+					// 7.1 surround
+					if (snd_swapstereo.value)
+					{
+						for (i = 0;i < snd_frames;i++, snd_p++)
+						{
+							val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[3] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[2] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[4] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[5] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[6] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[7] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+						}
+					}
+					else
+					{
+						for (i = 0;i < snd_frames;i++, snd_p++)
+						{
+							val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[2] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[3] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[4] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[5] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[6] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[7] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+						}
+					}
+				}
+				else if (shm->format.channels == 6)
+				{
+					// 5.1 surround
+					if (snd_swapstereo.value)
+					{
+						for (i = 0;i < snd_frames;i++, snd_p++)
+						{
+							val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[3] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[2] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[4] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[5] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+						}
+					}
+					else
+					{
+						for (i = 0;i < snd_frames;i++, snd_p++)
+						{
+							val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[2] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[3] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[4] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[5] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+						}
+					}
+				}
+				else if (shm->format.channels == 4)
+				{
+					// 4.0 surround
+					if (snd_swapstereo.value)
+					{
+						for (i = 0;i < snd_frames;i++, snd_p++)
+						{
+							val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[3] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[2] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+						}
+					}
+					else
+					{
+						for (i = 0;i < snd_frames;i++, snd_p++)
+						{
+							val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[2] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[3] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+						}
+					}
+				}
+				else if (shm->format.channels == 2)
 				{
-					// handle recirculating buffer issues
-					i = lpaintedtime & (shm->samples - 1);
-					snd_out = (unsigned char *) pbuf + i;
-					snd_linear_count = shm->samples - i;
-					if (snd_linear_count > endtime - lpaintedtime)
-						snd_linear_count = endtime - lpaintedtime;
-					for (i = 0;i < snd_linear_count;i++)
+					// 2.0 stereo
+					if (snd_swapstereo.value)
 					{
-						val = ((snd_p[i * 2] + snd_p[i * 2 + 1]) >> 9) + 128;
-						snd_out[i    ] = bound(0, val, 255);
+						for (i = 0;i < snd_frames;i++, snd_p++)
+						{
+							val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+						}
 					}
-					snd_p += snd_linear_count << 1;
-					lpaintedtime += snd_linear_count;
+					else
+					{
+						for (i = 0;i < snd_frames;i++, snd_p++)
+						{
+							val = (snd_p->sample[0] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+							val = (snd_p->sample[1] >> 8) + 128;*snd_out++ = bound(0, val, 255);
+						}
+					}
+				}
+				else if (shm->format.channels == 1)
+				{
+					// 1.0 mono
+					for (i = 0;i < snd_frames;i++, snd_p++)
+						val = ((snd_p->sample[0]+snd_p->sample[1]) >> 9) + 128;*snd_out++ = bound(0, val, 255);
 				}
 			}
 		}
-
 		S_UnlockBuffer();
 	}
 }
@@ -202,12 +320,11 @@ CHANNEL MIXING
 ===============================================================================
 */
 
-qboolean SND_PaintChannelFrom8 (channel_t *ch, int endtime);
-qboolean SND_PaintChannelFrom16 (channel_t *ch, int endtime);
+qboolean SND_PaintChannel (channel_t *ch, int endtime);
 
 void S_PaintChannels(int endtime)
 {
-	unsigned int i;
+	unsigned int i, j;
 	int end;
 	channel_t *ch;
 	sfx_t *sfx;
@@ -230,7 +347,12 @@ void S_PaintChannels(int endtime)
 			sfx = ch->sfx;
 			if (!sfx)
 				continue;
-			if (!ch->leftvol && !ch->rightvol)
+			if (sfx->fetcher)
+				j++;
+			for (j = 0;j < SND_LISTENERS;j++)
+				if (ch->listener_volume[j])
+					break;
+			if (j == SND_LISTENERS)
 				continue;
 			if (!S_LoadSound (sfx, true))
 				continue;
@@ -283,15 +405,10 @@ void S_PaintChannels(int endtime)
 
 				if (count > 0)
 				{
-					if (ch->leftvol > 255)
-						ch->leftvol = 255;
-					if (ch->rightvol > 255)
-						ch->rightvol = 255;
+					for (j = 0;j < SND_LISTENERS;j++)
+						ch->listener_volume[j] = bound(0, ch->listener_volume[j], 255);
 
-					if (sfx->format.width == 1)
-						stop_paint = !SND_PaintChannelFrom8 (ch, count);
-					else
-						stop_paint = !SND_PaintChannelFrom16 (ch, count);
+					stop_paint = !SND_PaintChannel (ch, count);
 
 					if (!stop_paint)
 					{
@@ -331,11 +448,9 @@ void S_PaintChannels(int endtime)
 }
 
 
-// TODO: Try to merge SND_PaintChannelFrom8 and SND_PaintChannelFrom16
-qboolean SND_PaintChannelFrom8 (channel_t *ch, int count)
+qboolean SND_PaintChannel (channel_t *ch, int count)
 {
-	int snd_vol, leftvol, rightvol;
-	const signed char *sfx;
+	int snd_vol, vol[SND_LISTENERS];
 	const sfxbuffer_t *sb;
 	int i;
 
@@ -345,78 +460,235 @@ qboolean SND_PaintChannelFrom8 (channel_t *ch, int count)
 	else
 		snd_vol = volume.value * 256;
 
-	leftvol = ch->leftvol * snd_vol;
-	rightvol = ch->rightvol * snd_vol;
+	for (i = 0;i < SND_LISTENERS;i++)
+		vol[i] = ch->listener_volume[i] * snd_vol;
 
 	sb = ch->sfx->fetcher->getsb (ch, ch->pos, count);
 	if (sb == NULL)
 		return false;
 
-	// Stereo sound support
-	if (ch->sfx->format.channels == 2)
+#if SND_LISTENERS != 8
+#error this code only supports up to 8 channels, update it
+#endif
+
+	if (ch->sfx->format.width == 1)
 	{
-		sfx = (signed char *)sb->data + (ch->pos - sb->offset) * 2;
-		for (i = 0;i < count;i++)
+		const signed char *sfx = (signed char *)sb->data + (ch->pos - sb->offset) * ch->sfx->format.channels;
+		// Stereo sound support
+		if (ch->sfx->format.channels == 2)
 		{
-			paintbuffer[i].left += (*sfx++ * leftvol) >> 8;
-			paintbuffer[i].right += (*sfx++ * rightvol) >> 8;
+			if (vol[6] + vol[7] > 0)
+			{
+				for (i = 0;i < count;i++)
+				{
+					paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8;
+					paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 8;
+					paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 8;
+					paintbuffer[i].sample[3] += (sfx[1] * vol[3]) >> 8;
+					paintbuffer[i].sample[4] += ((sfx[0]+sfx[1]) * vol[4]) >> 9;
+					paintbuffer[i].sample[5] += ((sfx[0]+sfx[1]) * vol[5]) >> 9;
+					paintbuffer[i].sample[6] += (sfx[0] * vol[6]) >> 8;
+					paintbuffer[i].sample[7] += (sfx[1] * vol[7]) >> 8;
+					sfx += 2;
+				}
+			}
+			else if (vol[4] + vol[5] > 0)
+			{
+				for (i = 0;i < count;i++)
+				{
+					paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8;
+					paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 8;
+					paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 8;
+					paintbuffer[i].sample[3] += (sfx[1] * vol[3]) >> 8;
+					paintbuffer[i].sample[4] += ((sfx[0]+sfx[1]) * vol[4]) >> 9;
+					paintbuffer[i].sample[5] += ((sfx[0]+sfx[1]) * vol[5]) >> 9;
+					sfx += 2;
+				}
+			}
+			else if (vol[2] + vol[3] > 0)
+			{
+				for (i = 0;i < count;i++)
+				{
+					paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8;
+					paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 8;
+					paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 8;
+					paintbuffer[i].sample[3] += (sfx[1] * vol[3]) >> 8;
+					sfx += 2;
+				}
+			}
+			else if (vol[0] + vol[1] > 0)
+			{
+				for (i = 0;i < count;i++)
+				{
+					paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8;
+					paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 8;
+					sfx += 2;
+				}
+			}
 		}
-	}
-	else
-	{
-		sfx = (signed char *)sb->data + ch->pos - sb->offset;
-		for (i = 0;i < count;i++)
+		else if (ch->sfx->format.channels == 1)
 		{
-			paintbuffer[i].left += (*sfx * leftvol) >> 8;
-			paintbuffer[i].right += (*sfx++ * rightvol) >> 8;
+			if (vol[6] + vol[7] > 0)
+			{
+				for (i = 0;i < count;i++)
+				{
+					paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8;
+					paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 8;
+					paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 8;
+					paintbuffer[i].sample[3] += (sfx[0] * vol[3]) >> 8;
+					paintbuffer[i].sample[4] += (sfx[0] * vol[4]) >> 8;
+					paintbuffer[i].sample[5] += (sfx[0] * vol[5]) >> 8;
+					paintbuffer[i].sample[6] += (sfx[0] * vol[6]) >> 8;
+					paintbuffer[i].sample[7] += (sfx[0] * vol[7]) >> 8;
+					sfx += 1;
+				}
+			}
+			else if (vol[4] + vol[5] > 0)
+			{
+				for (i = 0;i < count;i++)
+				{
+					paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8;
+					paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 8;
+					paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 8;
+					paintbuffer[i].sample[3] += (sfx[0] * vol[3]) >> 8;
+					paintbuffer[i].sample[4] += (sfx[0] * vol[4]) >> 8;
+					paintbuffer[i].sample[5] += (sfx[0] * vol[5]) >> 8;
+					sfx += 1;
+				}
+			}
+			else if (vol[2] + vol[3] > 0)
+			{
+				for (i = 0;i < count;i++)
+				{
+					paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8;
+					paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 8;
+					paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 8;
+					paintbuffer[i].sample[3] += (sfx[0] * vol[3]) >> 8;
+					sfx += 1;
+				}
+			}
+			else if (vol[0] + vol[1] > 0)
+			{
+				for (i = 0;i < count;i++)
+				{
+					paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 8;
+					paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 8;
+					sfx += 1;
+				}
+			}
 		}
+		else
+			return true; // unsupported number of channels in sound
 	}
-	ch->pos += count;
-	return true;
-}
-
-qboolean SND_PaintChannelFrom16 (channel_t *ch, int count)
-{
-	int snd_vol, leftvol, rightvol;
-	signed short *sfx;
-	const sfxbuffer_t *sb;
-	int i;
-
-	// If this channel manages its own volume
-	if (ch->flags & CHANNELFLAG_FULLVOLUME)
-		snd_vol = 256;
-	else
-		snd_vol = volume.value * 256;
-
-	leftvol = ch->leftvol * snd_vol;
-	rightvol = ch->rightvol * snd_vol;
-
-	sb = ch->sfx->fetcher->getsb (ch, ch->pos, count);
-	if (sb == NULL)
-		return false;
-
-	// Stereo sound support
-	if (ch->sfx->format.channels == 2)
+	else if (ch->sfx->format.width == 2)
 	{
-		sfx = (signed short *)sb->data + (ch->pos - sb->offset) * 2;
-
-		for (i=0 ; i<count ; i++)
+		const signed short *sfx = (signed short *)sb->data + (ch->pos - sb->offset) * ch->sfx->format.channels;
+		// Stereo sound support
+		if (ch->sfx->format.channels == 2)
 		{
-			paintbuffer[i].left += (*sfx++ * leftvol) >> 16;
-			paintbuffer[i].right += (*sfx++ * rightvol) >> 16;
+			if (vol[6] + vol[7] > 0)
+			{
+				for (i = 0;i < count;i++)
+				{
+					paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16;
+					paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 16;
+					paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 16;
+					paintbuffer[i].sample[3] += (sfx[1] * vol[3]) >> 16;
+					paintbuffer[i].sample[4] += ((sfx[0]+sfx[1]) * vol[4]) >> 17;
+					paintbuffer[i].sample[5] += ((sfx[0]+sfx[1]) * vol[5]) >> 17;
+					paintbuffer[i].sample[6] += (sfx[0] * vol[6]) >> 16;
+					paintbuffer[i].sample[7] += (sfx[1] * vol[7]) >> 16;
+					sfx += 2;
+				}
+			}
+			else if (vol[4] + vol[5] > 0)
+			{
+				for (i = 0;i < count;i++)
+				{
+					paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16;
+					paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 16;
+					paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 16;
+					paintbuffer[i].sample[3] += (sfx[1] * vol[3]) >> 16;
+					paintbuffer[i].sample[4] += ((sfx[0]+sfx[1]) * vol[4]) >> 17;
+					paintbuffer[i].sample[5] += ((sfx[0]+sfx[1]) * vol[5]) >> 17;
+					sfx += 2;
+				}
+			}
+			else if (vol[2] + vol[3] > 0)
+			{
+				for (i = 0;i < count;i++)
+				{
+					paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16;
+					paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 16;
+					paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 16;
+					paintbuffer[i].sample[3] += (sfx[1] * vol[3]) >> 16;
+					sfx += 2;
+				}
+			}
+			else if (vol[0] + vol[1] > 0)
+			{
+				for (i = 0;i < count;i++)
+				{
+					paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16;
+					paintbuffer[i].sample[1] += (sfx[1] * vol[1]) >> 16;
+					sfx += 2;
+				}
+			}
 		}
-	}
-	else
-	{
-		sfx = (signed short *)sb->data + ch->pos - sb->offset;
-
-		for (i=0 ; i<count ; i++)
+		else if (ch->sfx->format.channels == 1)
 		{
-			paintbuffer[i].left += (*sfx * leftvol) >> 16;
-			paintbuffer[i].right += (*sfx++ * rightvol) >> 16;
+			if (vol[6] + vol[7] > 0)
+			{
+				for (i = 0;i < count;i++)
+				{
+					paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16;
+					paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 16;
+					paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 16;
+					paintbuffer[i].sample[3] += (sfx[0] * vol[3]) >> 16;
+					paintbuffer[i].sample[4] += (sfx[0] * vol[4]) >> 16;
+					paintbuffer[i].sample[5] += (sfx[0] * vol[5]) >> 16;
+					paintbuffer[i].sample[6] += (sfx[0] * vol[6]) >> 16;
+					paintbuffer[i].sample[7] += (sfx[0] * vol[7]) >> 16;
+					sfx += 1;
+				}
+			}
+			else if (vol[4] + vol[5] > 0)
+			{
+				for (i = 0;i < count;i++)
+				{
+					paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16;
+					paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 16;
+					paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 16;
+					paintbuffer[i].sample[3] += (sfx[0] * vol[3]) >> 16;
+					paintbuffer[i].sample[4] += (sfx[0] * vol[4]) >> 16;
+					paintbuffer[i].sample[5] += (sfx[0] * vol[5]) >> 16;
+					sfx += 1;
+				}
+			}
+			else if (vol[2] + vol[3] > 0)
+			{
+				for (i = 0;i < count;i++)
+				{
+					paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16;
+					paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 16;
+					paintbuffer[i].sample[2] += (sfx[0] * vol[2]) >> 16;
+					paintbuffer[i].sample[3] += (sfx[0] * vol[3]) >> 16;
+					sfx += 1;
+				}
+			}
+			else if (vol[0] + vol[1] > 0)
+			{
+				for (i = 0;i < count;i++)
+				{
+					paintbuffer[i].sample[0] += (sfx[0] * vol[0]) >> 16;
+					paintbuffer[i].sample[1] += (sfx[0] * vol[1]) >> 16;
+					sfx += 1;
+				}
+			}
 		}
+		else
+			return true; // unsupported number of channels in sound
 	}
-
 	ch->pos += count;
 	return true;
 }
diff --git a/snd_oss.c b/snd_oss.c
index eef8e62c..b1503200 100644
--- a/snd_oss.c
+++ b/snd_oss.c
@@ -189,7 +189,8 @@ qboolean SNDDMA_Init(void)
 		return 0;
 	}
 
-	shm->samples = info.fragstotal * info.fragsize / shm->format.width;
+	shm->sampleframes = info.fragstotal * info.fragsize / shm->format.width / shm->format.channels;
+	shm->samples = shm->sampleframes * shm->format.channels;
 
 	// memory map the dma buffer
 	shm->bufferlength = info.fragstotal * info.fragsize;
diff --git a/snd_sdl.c b/snd_sdl.c
index dbeb3ced..1b9ef7d6 100644
--- a/snd_sdl.c
+++ b/snd_sdl.c
@@ -22,12 +22,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 /*
 Info:
-One SDL sample consists of x channel samples
-The mixer supposes that the driver has one channel entry/sample though it has x channels/sample
-like the SDL
+SDL samples are really frames (full set of samples for all speakers)
 */
 
-#define AUDIO_SDL_SAMPLES		4096
+#define AUDIO_SDL_SAMPLEFRAMES		4096
 #define AUDIO_LOCALFACTOR		4
 
 typedef struct AudioState_s
@@ -81,8 +79,9 @@ Returns false if nothing is found.
 
 qboolean SNDDMA_Init(void)
 {
-	SDL_AudioSpec spec;
+	SDL_AudioSpec wantspec;
 	int i;
+	int channels;
 
 	// Init the SDL Audio subsystem
 	if( SDL_InitSubSystem( SDL_INIT_AUDIO ) ) {
@@ -90,48 +89,55 @@ qboolean SNDDMA_Init(void)
 		return false;
 	}
 
-	// Init the shm structure
-	memset( (void*) shm, 0, sizeof(*shm) );
-
-	shm->format.channels = 2; //stereo
-	shm->format.width = 2;
-
-// COMMANDLINEOPTION: SDL Sound: -sndspeed <hz> chooses 44100 hz, 22100 hz, or 11025 hz sound output rate
-	i = COM_CheckParm( "-sndspeed" );
-	if( i && i != ( com_argc - 1 ) )
-		shm->format.speed = atoi( com_argv[ i+1 ] );
-	else
-		shm->format.speed = 44100;
-
-	shm->samplepos = 0;
-	shm->samples = AUDIO_SDL_SAMPLES * AUDIO_LOCALFACTOR;
-	shm->bufferlength = shm->samples * shm->format.width;
-	shm->buffer = (unsigned char *)Mem_Alloc( snd_mempool, shm->bufferlength );
-
-	// Init the as structure
-	as.buffer = shm->buffer;
-	as.width = shm->format.width;
-	as.pos = 0;
-	as.size = shm->bufferlength;
-
-	// Init the SDL Audio subsystem
-	spec.callback = Buffer_Callback;
-	spec.channels = shm->format.channels;
-	spec.format = AUDIO_S16SYS;
-	spec.freq = shm->format.speed;
-	spec.userdata = NULL;
-	spec.samples = AUDIO_SDL_SAMPLES;
-
-	if( SDL_OpenAudio( &spec, NULL ) ) {
+	for (channels = 8;channels >= 2;channels -= 2)
+	{
+		// Init the SDL Audio subsystem
+		wantspec.callback = Buffer_Callback;
+		wantspec.userdata = NULL;
+		wantspec.freq = 44100;
+		// COMMANDLINEOPTION: SDL Sound: -sndspeed <hz> chooses sound output rate (try values such as 44100, 48000, 22050, 11025 (quake), 24000, 32000, 96000, 192000, etc)
+		i = COM_CheckParm( "-sndspeed" );
+		if( i && i != ( com_argc - 1 ) )
+			wantspec.freq = atoi( com_argv[ i+1 ] );
+		wantspec.format = AUDIO_S16SYS;
+		wantspec.channels = channels;
+		wantspec.samples = AUDIO_SDL_SAMPLEFRAMES;
+
+		if( SDL_OpenAudio( &wantspec, NULL ) )
+		{
+			Con_Printf("%s\n", SDL_GetError());
+			continue;
+		}
+
+		// Init the shm structure
+		memset( (void*) shm, 0, sizeof(*shm) );
+		shm->format.channels = wantspec.channels;
+		shm->format.width = 2;
+		shm->format.speed = wantspec.freq;
+
+		shm->samplepos = 0;
+		shm->sampleframes = wantspec.samples * AUDIO_LOCALFACTOR;
+		shm->samples = shm->sampleframes * shm->format.channels;
+		shm->bufferlength = shm->samples * shm->format.width;
+		shm->buffer = (unsigned char *)Mem_Alloc( snd_mempool, shm->bufferlength );
+
+		// Init the as structure
+		as.buffer = shm->buffer;
+		as.width = shm->format.width;
+		as.pos = 0;
+		as.size = shm->bufferlength;
+		break;
+	}
+	if (channels < 2)
+	{
 		Con_Print( "Failed to open the audio device!\n" );
 		Con_DPrintf(
 			"Audio Specification:\n"
 			"\tChannels  : %i\n"
 			"\tFormat    : %x\n"
 			"\tFrequency : %i\n"
-			"\tBuffersize: %i Bytes(%i Samples)\n",
-			spec.channels, spec.format, spec.freq, shm->bufferlength , spec.samples );
-		Mem_Free( shm->buffer );
+			"\tSamples   : %i\n",
+			wantspec.channels, wantspec.format, wantspec.freq, wantspec.samples );
 		return false;
 	}
 
diff --git a/snd_win.c b/snd_win.c
index 2752446f..0f62112c 100644
--- a/snd_win.c
+++ b/snd_win.c
@@ -397,6 +397,7 @@ sndinitstat SNDDMA_InitDirect (void)
 	pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
 
 	shm->samples = gSndBufSize / shm->format.width;
+	shm->sampleframes = shm->samples / shm->format.channels;
 	shm->samplepos = 0;
 	shm->buffer = (unsigned char *) lpData;
 
@@ -527,6 +528,7 @@ qboolean SNDDMA_InitWav (void)
 	}
 
 	shm->samples = gSndBufSize / shm->format.width;
+	shm->sampleframes = shm->samples / shm->format.channels;
 	shm->samplepos = 0;
 	shm->buffer = (unsigned char *) lpData;
 
-- 
2.39.5