about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/engine/client/snd.c622
-rw-r--r--src/engine/config_variables.h2
-rw-r--r--src/engine/interface.h21
-rw-r--r--src/game/client/game_client.cpp36
4 files changed, 246 insertions, 435 deletions
diff --git a/src/engine/client/snd.c b/src/engine/client/snd.c
index 0fe82e14..18bc7f6a 100644
--- a/src/engine/client/snd.c
+++ b/src/engine/client/snd.c
@@ -10,256 +10,210 @@
 
 enum
 {
-	NUM_SOUNDS = 512,
+	NUM_SAMPLES = 512,
 	NUM_VOICES = 64,
-	NUM_CHANNELS = 4,
-
+	NUM_CHANNELS = 16,
+	
 	MAX_FRAMES = 1024
 };
 
-static LOCK sound_lock = 0;
-
-static struct sound
+typedef struct
 {
 	short *data;
-	int num_samples;
+	int num_frames;
 	int rate;
 	int channels;
 	int loop_start;
 	int loop_end;
-} sounds[NUM_SOUNDS] = { {0x0, 0, 0, 0, -1, -1} };
+} SAMPLE;
 
-static struct voice
+typedef struct
 {
-	volatile struct sound *sound;
-	int tick;
-	int stop;
-	int loop;
-	float vol;
-	float pan;
-	float x;
-	float y;
-	volatile struct voice *prev;
-	volatile struct voice *next;
-} voices[NUM_VOICES] = { {0x0, 0, -1, -1, 1.0f, 0.0f, 0.0f, 0.0f, 0x0, 0x0} };
-
-#define CHANNEL_POSITION_VOLUME 1
-#define CHANNEL_POSITION_PAN 2
-
-static struct channel
+	int vol;
+	int pan;
+} CHANNEL;
+
+typedef struct VOICE_t
 {
-	volatile struct voice *first_voice;
-	float vol;
-	float pan;
+	SAMPLE *snd;
+	CHANNEL *channel;
+	int tick;
+	int vol; /* 0 - 255 */
 	int flags;
-} channels[NUM_CHANNELS] = { {0x0, 1.0f, 0.0f, 0} };
-
-static float center_x = 0.0f;
-static float center_y = 0.0f;
-static float master_vol = 1.0f;
-static float master_pan = 0.0f;
-static float pan_deadzone = 256.0f;
-static float pan_falloff = 1.0f;
-static float volume_deadzone = 256.0f;
-static float volume_falloff = 1.0f;
-
-static inline short int2short(int i)
+	int x, y;
+} VOICE;
+
+static SAMPLE samples[NUM_SAMPLES] = { {0} };
+static VOICE voices[NUM_VOICES] = { {0} };
+static CHANNEL channels[NUM_CHANNELS] = { {255, 0} };
+
+static LOCK sound_lock = 0;
+
+static int center_x = 0;
+static int center_y = 0;
+
+static int mixing_rate = 48000;
+
+void snd_set_channel(int cid, float vol, float pan)
 {
-	if(i > 0x7fff)
-		return 0x7fff;
-	else if(i < -0x7fff)
-		return -0x7fff;
-	return i;
+	channels[cid].vol = (int)(vol*255.0f);
+	channels[cid].pan = (int)(pan*255.0f);
 }
 
-static inline float sgn(float f)
+static int play(int cid, int sid, int flags, float x, float y)
 {
-	if(f < 0.0f)
-		return -1.0f;
-	return 1.0f;
+	int vid = -1;
+	int i;
+	
+	lock_wait(sound_lock);
+	
+	/* search for voice */
+	/* TODO: fix this linear search */
+	for(i = 0; i < NUM_VOICES; i++)
+	{
+		if(!voices[i].snd)
+		{
+			vid = i;
+			break;
+		}
+	}
+	
+	/* voice found, use it */
+	if(vid != -1)
+	{
+		voices[vid].snd = &samples[sid];
+		voices[vid].channel = &channels[cid];
+		voices[vid].tick = 0;
+		voices[vid].vol = 255;
+		voices[vid].flags = flags;
+		voices[vid].x = (int)x;
+		voices[vid].y = (int)y;
+	}
+	
+	lock_release(sound_lock);
+	return vid;
 }
 
-static void reset_voice(struct voice *v)
+int snd_play_at(int cid, int sid, int flags, float x, float y)
 {
-	v->sound = 0x0;
-	v->tick = 0;
-	v->stop = -1;
-	v->loop = -1;
-	v->vol = 1.0f;
-	v->pan = 0.0f;
-	v->x = 0.0f;
-	v->y = 0.0f;
-	v->next = 0x0;
-	v->prev = 0x0;
+	return play(cid, sid, flags|SNDFLAG_POS, x, y);
 }
 
-static inline void fill_mono(int *out, unsigned frames, struct voice *v, float fvol, float fpan)
+int snd_play(int cid, int sid, int flags)
 {
-	int ivol = (int) (31.0f * fvol);
-	int ipan = (int) (31.0f * ipan);
-
-	unsigned i;
-	for(i = 0; i < frames; i++)
-	{
-		unsigned j = i<<1;
-		int val = v->sound->data[v->tick] * ivol;
-		out[j] += val;
-		out[j+1] += val;
-		v->tick++;
-	}
+	return play(cid, sid, flags, 0, 0);
 }
 
-static inline void fill_stereo(int *out, unsigned frames, struct voice *v, float fvol, float fpan)
+void snd_stop(int vid)
 {
-	int ivol = (int) (31.0f * fvol);
-	int ipan = (int) (31.0f * ipan);
-	unsigned i;
-
-	for(i = 0; i < frames; i++)
-	{
-		unsigned j = i<<1;
-		out[j] += v->sound->data[v->tick] * ivol;
-		out[j+1] += v->sound->data[v->tick+1] * ivol;
-		v->tick += 2;
-	}
+	/* TODO: a nice fade out */
+	lock_wait(sound_lock);
+	voices[vid].snd = 0;
+	lock_release(sound_lock);
 }
 
-static void mix(short *out, unsigned frames)
+/* there should be a faster way todo this */
+static short int2short(int i)
 {
-	static int main_buffer[MAX_FRAMES*2];
-	unsigned locked = 0;
-	unsigned i;
-	unsigned cid;
+	if(i > 0x7fff)
+		return 0x7fff;
+	else if(i < -0x7fff)
+		return -0x7fff;
+	return i;
+}
 
-	dbg_assert(frames <= MAX_FRAMES, "too many frames to fill");
+static int iabs(int i)
+{
+	if(i<0)
+		return -i;
+	return i;
+}
 
-	for(i = 0; i < frames; i++)
-	{
-		unsigned j = i<<1;
-		main_buffer[j] = 0;
-		main_buffer[j+1] = 0;
-	}
+static void mix(short *final_out, unsigned frames)
+{
+	int mix_buffer[MAX_FRAMES*2] = {0};
+	int i, s;
 
-	for(cid = 0; cid < NUM_CHANNELS; cid++)
+	/* aquire lock while we are mixing */
+	lock_wait(sound_lock);
+	
+	for(i = 0; i < NUM_VOICES; i++)
 	{
-		struct channel *c = &channels[cid];
-		struct voice *v = (struct voice*)c->first_voice;
-
-		while(v)
+		if(voices[i].snd)
 		{
-			unsigned filled = 0;
-			unsigned step = 1;
+			/* mix voice */
+			VOICE *v = &voices[i];
+			int *out = mix_buffer;
 
-			while(v && v->sound && filled < frames)
-			{
-				/* calculate maximum frames to fill */
-				unsigned frames_left = (v->sound->num_samples - v->tick) >> (v->sound->channels-1);
-				unsigned long to_fill = frames>frames_left?frames_left:frames;
-				float vol = 1.0f;
-				float pan = 0.0f;
-
-				/* clamp to_fill if voice should stop */
-				if(v->stop >= 0)
-					to_fill = (unsigned)v->stop>frames_left?frames:v->stop;
-
-				/* clamp to_fill if we are about to loop */
-				if(v->loop >= 0 && v->sound->loop_start >= 0)
-				{
-					unsigned tmp = v->sound->loop_end - v->tick;
-					to_fill = tmp>to_fill?to_fill:tmp;
-				}
+			int step = v->snd->channels; /* setup input sources */
+			short *in_l = &v->snd->data[v->tick*step];
+			short *in_r = &v->snd->data[v->tick*step+1];
+			
+			int end = v->snd->num_frames-v->tick;
 
-				/* calculate voice volume and delta */
-				if(c->flags & CHANNEL_POSITION_VOLUME)
-				{
-					float dx = v->x - center_x;
-					float dy = v->y - center_y;
-					float dist = dx*dx + dy*dy;
-					if(dist < volume_deadzone*volume_deadzone)
-						vol = master_vol * c->vol;
-					else
-						vol = master_vol * c->vol / ((dist - volume_deadzone*volume_deadzone)*volume_falloff); /*TODO: use some fast 1/x^2 */
-				}
-				else
-				{
-					vol = master_vol * c->vol * v->vol;
-				}
+			int rvol = v->channel->vol;
+			int lvol = v->channel->vol;
 
-				/* calculate voice pan and delta */
-				if(c->flags & CHANNEL_POSITION_PAN)
-				{
-					float dx = v->x - center_x;
-					if(fabs(dx) < pan_deadzone)
-						pan = master_pan + c->pan;
-					else
-						pan = master_pan + c->pan + sgn(dx)*(fabs(dx) - pan_deadzone)/pan_falloff;
-				}
-				else
-				{
-					pan = master_pan + c->pan + v->pan;
-				}
+			/* make sure that we don't go outside the sound data */
+			if(frames < end)
+				end = frames;
+			
+			/* check if we have a mono sound */
+			if(v->snd->channels == 1)
+				in_r = in_l;
 
-				/* fill the main buffer */
-				if(v->sound->channels == 1)
-					fill_mono(&main_buffer[filled], to_fill, v, vol, pan);
-				else
-					fill_stereo(&main_buffer[filled], to_fill, v, vol, pan);
-
-				/* reset tick of we hit loop point */
-				if(v->loop >= 0 &&
-						v->sound->loop_start >= 0 &&
-						v->tick >= v->sound->loop_end)
-					v->tick = v->sound->loop_start;
-
-				/* stop sample if nessecary */
-				if(v->stop >= 0)
-					v->stop -=  to_fill;
-				if(v->tick >= v->sound->num_samples || v->stop == 0)
+			/* volume calculation */
+			if(v->flags&SNDFLAG_POS && v->channel->pan)
+			{
+				const int range = 1500; /* magic value, remove */
+				int dx = v->x - center_x;
+				int dy = v->y - center_y;
+				int dist = sqrt(dx*dx+dy*dy); /* double here. nasty */
+				int p = iabs(dx);
+				if(dist < range)
 				{
-					struct voice *vn = (struct voice *)v->next;
-					if(!locked)
-					{
-						lock_wait(sound_lock);
-						locked = 1;
-					}
-
-
-					if(v->next)
-						v->next->prev = v->prev;
-
-					if(v->prev)
-						v->prev->next = v->next;
+					/* panning */
+					if(dx > 0)
+						lvol = ((range-p)*lvol)/range;
 					else
-						channels[cid].first_voice = v->next;
-
-					dbg_msg("snd", "sound stopped");
-
-					reset_voice(v);
-					step = 0;
-					v = vn;
+						rvol = ((range-p)*rvol)/range;
+					
+					/* falloff */
+					lvol = (lvol*(range-dist))/range;
+					rvol = (rvol*(range-dist))/range;
 				}
-
-				filled += to_fill;
 			}
 
-			if(step)
-				v = (struct voice*)v->next;
+			/* process all frames */
+			for(s = 0; s < end; s++)
+			{
+				*out++ += (*in_l)*lvol;
+				*out++ += (*in_r)*rvol;
+				in_l += step;
+				in_r += step;
+				v->tick++;
+			}
+			
+			/* free voice if not used any more */
+			if(v->tick == v->snd->num_frames)
+				v->snd = 0;
+			
 		}
 	}
 
-	if(locked)
-		lock_release(sound_lock);
+	/* release the lock */
+	lock_release(sound_lock);
 
 	/* clamp accumulated values */
 	for(i = 0; i < frames; i++)
 	{
 		int j = i<<1;
-		int vl = main_buffer[j];
-		int vr = main_buffer[j+1];
+		int vl = mix_buffer[j]>>8;
+		int vr = mix_buffer[j+1]>>8;
 
-		out[j] = int2short(vl>>5);
-		out[j+1] = int2short(vr>>5);
-	}
+		final_out[j] = int2short(vl);
+		final_out[j+1] = int2short(vr);
+	}	
 }
 
 static int pacallback(const void *in, void *out, unsigned long frames, const PaStreamCallbackTimeInfo* time, PaStreamCallbackFlags status, void *user)
@@ -274,6 +228,8 @@ int snd_init()
 {
 	PaStreamParameters params;
 	PaError err = Pa_Initialize();
+	
+	mixing_rate = config.snd_rate;
 
 	sound_lock = lock_create();
 
@@ -290,7 +246,7 @@ int snd_init()
 			&stream,        /* passes back stream pointer */
 			0,              /* no input channels */
 			&params,                /* pointer to parameters */
-			44100,          /* sample rate */
+			mixing_rate,          /* sample rate */
 			128,            /* frames per buffer */
 			paClipOff,              /* no clamping */
 			pacallback,             /* specify our custom callback */
@@ -310,26 +266,59 @@ int snd_shutdown()
 	return 0;
 }
 
-void snd_set_center(int x, int y)
-{
-	center_x = x;
-	center_y = y;
-}
-
 int snd_alloc_id()
 {
+	/* TODO: linear search, get rid of it */
 	unsigned sid;
-	for(sid = 0; sid < NUM_SOUNDS; sid++)
+	for(sid = 0; sid < NUM_SAMPLES; sid++)
 	{
-		if(sounds[sid].data == 0x0)
-		{
+		if(samples[sid].data == 0x0)
 			return sid;
-		}
 	}
 
 	return -1;
 }
 
+static void rate_convert(int sid)
+{
+	SAMPLE *snd = &samples[sid];
+	int num_frames = 0;
+	short *new_data = 0;
+	int i;
+	
+	/* make sure that we need to convert this sound */
+	if(!snd->data || snd->rate == mixing_rate)
+		return;
+
+	/* allocate new data */
+	num_frames = (int)((snd->num_frames/(float)snd->rate)*mixing_rate);
+	new_data = mem_alloc(num_frames*snd->channels*sizeof(short), 1);
+	
+	for(i = 0; i < num_frames; i++)
+	{
+		/* resample TODO: this should be done better, like linear atleast */
+		float a = i/(float)num_frames;
+		int f = (int)(a*snd->num_frames);
+		if(f >= snd->num_frames)
+			f = snd->num_frames-1;
+		
+		/* set new data */
+		if(snd->channels == 1)
+			new_data[i] = snd->data[f];
+		else if(snd->channels == 2)
+		{
+			new_data[i*2] = snd->data[f*2];
+			new_data[i*2+1] = snd->data[f*2+1];
+		}
+	}
+	
+	/* free old data and apply new */
+	mem_free(snd->data);
+	snd->data = new_data;
+	snd->num_frames = num_frames;
+}
+
+
 static FILE *file = NULL;
 
 static int read_data(void *buffer, int size)
@@ -339,7 +328,7 @@ static int read_data(void *buffer, int size)
 
 int snd_load_wv(const char *filename)
 {
-	struct sound *snd;
+	SAMPLE *snd;
 	int sid = -1;
 	char error[100];
 	WavpackContext *context;
@@ -347,7 +336,7 @@ int snd_load_wv(const char *filename)
 	sid = snd_alloc_id();
 	if(sid < 0)
 		return -1;
-	snd = &sounds[sid];
+	snd = &samples[sid];
 
 	file = fopen(filename, "rb"); /* TODO: use system.h stuff for this */
 
@@ -396,7 +385,7 @@ int snd_load_wv(const char *filename)
 
 		mem_free(data);
 
-		snd->num_samples = samples;
+		snd->num_frames = samples;
 		snd->loop_start = -1;
 		snd->loop_end = -1;
 	}
@@ -411,218 +400,17 @@ int snd_load_wv(const char *filename)
 	if(config.debug)
 		dbg_msg("sound/wv", "loaded %s", filename);
 
+	rate_convert(sid);
 	return sid;
 }
 
-#if 0
-int snd_load_wav(const char *filename)
-{
-	/* open file for reading */
-	IOHANDLE file;
-	struct sound *snd;
-	int sid = -1;
-	int state = 0;
-
-	file = io_open(filename, IOFLAG_READ);
-	if(!file)
-	{
-		dbg_msg("sound/wav", "failed to open file. filename='%s'", filename);
-		return -1;
-	}
-
-	sid = snd_alloc_id();
-	if(sid < 0)
-		return -1;
-	snd = &sounds[sid];
-
-	while(1)
-	{
-		/* read chunk header */
-		unsigned char head[8];
-		int chunk_size;
-		if(io_read(file, head, sizeof(head)) != 8)
-		{
-			break;
-		}
-		
-		chunk_size = head[4] | (head[5]<<8) | (head[6]<<16) | (head[7]<<24);
-		head[4] = 0;
-			
-		if(state == 0)
-		{
-			unsigned char type[4];
-
-			/* read the riff and wave headers */
-			if(head[0] != 'R' || head[1] != 'I' || head[2] != 'F' || head[3] != 'F')
-			{
-				dbg_msg("sound/wav", "not a RIFF file. filename='%s'", filename);
-				return -1;
-			}
-			
-			io_read(file, type, 4);
-
-			if(type[0] != 'W' || type[1] != 'A' || type[2] != 'V' || type[3] != 'E')
-			{
-				dbg_msg("sound/wav", "RIFF file is not a WAVE. filename='%s'", filename);
-				return -1;
-			}
-			
-			state++;
-		}
-		else if(state == 1)
-		{
-			/* read the format chunk */
-			if(head[0] == 'f' && head[1] == 'm' && head[2] == 't' && head[3] == ' ')
-			{
-				unsigned char fmt[16];
-				if(io_read(file, fmt, sizeof(fmt)) !=  sizeof(fmt))
-				{
-					dbg_msg("sound/wav", "failed to read format. filename='%s'", filename);
-					return -1;
-				}
-				
-				/* decode format */
-				int compression_code = fmt[0] | (fmt[1]<<8);
-				snd->channels = fmt[2] | (fmt[3]<<8);
-				snd->rate = fmt[4] | (fmt[5]<<8) | (fmt[6]<<16) | (fmt[7]<<24);
-
-				if(compression_code != 1)
-				{
-					dbg_msg("sound/wav", "file is not uncompressed. filename='%s'", filename);
-					return -1;
-				}
-				
-				if(snd->channels > 2)
-				{
-					dbg_msg("sound/wav", "file is not mono or stereo. filename='%s'", filename);
-					return -1;
-				}
-
-				if(snd->rate != 44100)
-				{
-					dbg_msg("sound/wav", "file is %d Hz, not 44100 Hz. filename='%s'", snd->rate, filename);
-					return -1;
-				}
-				
-				int bps = fmt[14] | (fmt[15]<<8);
-				if(bps != 16)
-				{
-					dbg_msg("sound/wav", "bps is %d, not 16, filname='%s'", bps, filename);
-					return -1;
-				}
-				
-				/* next state */
-				state++;
-			}
-			else
-				io_skip(file, chunk_size);
-		}
-		else if(state == 2)
-		{
-			/* read the data */
-			if(head[0] == 'd' && head[1] == 'a' && head[2] == 't' && head[3] == 'a')
-			{
-				snd->data = (short*)mem_alloc(chunk_size, 1);
-				io_read(file, snd->data, chunk_size);
-				snd->num_samples = chunk_size/(2);
-#if defined(CONF_ARCH_ENDIAN_BIG)
-				swap_endian(snd->data, sizeof(short), snd->num_samples);
-#endif
-				snd->loop_start = -1;
-				snd->loop_end = -1;
-				state++;
-			}
-			else
-				io_skip(file, chunk_size);
-		}
-		else if(state == 3)
-		{
-			if(head[0] == 's' && head[1] == 'm' && head[2] == 'p' && head[3] == 'l')
-			{
-				unsigned char smpl[36];
-				unsigned char loop[24];
-				if(config.debug)
-					dbg_msg("sound/wav", "got sustain");
-
-				io_read(file, smpl, sizeof(smpl));
-				unsigned num_loops = (smpl[28] | (smpl[29]<<8) | (smpl[30]<<16) | (smpl[31]<<24));
-				unsigned skip = (smpl[32] | (smpl[33]<<8) | (smpl[34]<<16) | (smpl[35]<<24));
-
-				if(num_loops > 0)
-				{
-					io_read(file, loop, sizeof(loop));
-					unsigned start = (loop[8] | (loop[9]<<8) | (loop[10]<<16) | (loop[11]<<24));
-					unsigned end = (loop[12] | (loop[13]<<8) | (loop[14]<<16) | (loop[15]<<24));
-					snd->loop_start = start * snd->channels;
-					snd->loop_end = end * snd->channels;
-				}
-
-				if(num_loops > 1)
-					io_skip(file, (num_loops-1) * sizeof(loop));
-
-				io_skip(file, skip);
-				state++;
-			}
-			else
-				io_skip(file, chunk_size);
-		}
-		else
-			io_skip(file, chunk_size);
-	}
-
-	if(config.debug)
-		dbg_msg("sound/wav", "loaded %s", filename);
-
-	return sid;
-}
-#endif
-
-
-int snd_play(int cid, int sid, int loop, float x, float y)
-{
-	int vid;
-	dbg_msg("snd", "try adding sound");
-	for(vid = 0; vid < NUM_VOICES; vid++)
-	{
-		if(voices[vid].sound == 0x0)
-		{
-			voices[vid].tick = 0;
-			voices[vid].x = x;
-			voices[vid].y = y;
-			voices[vid].sound = &sounds[sid];
-			if(loop == SND_LOOP)
-				voices[vid].loop = voices[vid].sound->loop_end;
-			else
-				voices[vid].loop = -1;
-
-			lock_wait(sound_lock);
-			dbg_msg("snd", "sound added");
-			voices[vid].next = channels[cid].first_voice;
-			if(channels[cid].first_voice)
-				channels[cid].first_voice->prev = &voices[vid];
-			channels[cid].first_voice = &voices[vid];
-			lock_release(sound_lock);
-			return vid;
-		}
-	}
-
-	dbg_msg("snd", "failed");
-	return -1;
-}
-
-void snd_set_master_volume(float val)
-{
-	master_vol = val;
-}
-
-void snd_stop(int vid)
+void snd_set_master_volume(float vol)
 {
-	/*TODO: lerp volume to 0*/
-	voices[vid].stop = 0;
+	/*master_vol = vol;*/
 }
 
 void snd_set_listener_pos(float x, float y)
 {
-	center_x = x;
-	center_y = y;
+	center_x = (int)x;
+	center_y = (int)y;
 }
diff --git a/src/engine/config_variables.h b/src/engine/config_variables.h
index 492d35fb..dfe638d0 100644
--- a/src/engine/config_variables.h
+++ b/src/engine/config_variables.h
@@ -20,6 +20,8 @@ MACRO_CONFIG_INT(b_filter_pw, 0, 1, 0)
 MACRO_CONFIG_INT(b_sort, 0, 0, 0)
 MACRO_CONFIG_INT(b_max_requests, 10, 0, 0)
 
+MACRO_CONFIG_INT(snd_rate, 48000, 0, 0)
+
 MACRO_CONFIG_INT(gfx_screen_width, 800, 0, 0)
 MACRO_CONFIG_INT(gfx_screen_height, 600, 0, 0)
 MACRO_CONFIG_INT(gfx_fullscreen, 1, 0, 1)
diff --git a/src/engine/interface.h b/src/engine/interface.h
index 94201ea0..635bd757 100644
--- a/src/engine/interface.h
+++ b/src/engine/interface.h
@@ -25,6 +25,9 @@ enum
 	MASK_SET,
 	MASK_ZERO,
 
+	SNDFLAG_LOOP=1,
+	SNDFLAG_POS=2,
+	SNDFLAG_ALL=3,
 	
 	CLIENTSTATE_OFFLINE=0,
 	CLIENTSTATE_CONNECTING,
@@ -366,21 +369,21 @@ void gfx_quads_draw_freeform(
 void gfx_quads_text(float x, float y, float size, const char *text);
 
 /* sound (client) */
-enum
-{
-	SND_PLAY_ONCE = 0,
-	SND_LOOP
-};
-	
 int snd_init();
+
 float snd_get_master_volume();
 void snd_set_master_volume(float val);
-int snd_load_wav(const char *filename);
+
+void snd_set_channel(int cid, float vol, float pan);
+
 int snd_load_wv(const char *filename);
-int snd_play(int cid, int sid, int loop, float x, float y);
+
+int snd_play_at(int cid, int sid, int flags, float x, float y);
+int snd_play(int cid, int sid, int flags);
+
 void snd_stop(int id);
-void snd_set_vol(int id, float vol);
 void snd_set_listener_pos(float x, float y);
+
 int snd_shutdown();
 
 /*
diff --git a/src/game/client/game_client.cpp b/src/game/client/game_client.cpp
index dc135509..ad0aff9c 100644
--- a/src/game/client/game_client.cpp
+++ b/src/game/client/game_client.cpp
@@ -16,6 +16,14 @@ extern "C" {
 #include "data.h"
 #include "menu.h"
 
+// sound channels
+enum
+{
+	CHN_GUI=0,
+	CHN_MUSIC,
+	CHN_WORLD,
+};
+
 data_container *data = 0x0;
 
 static int charids[16] = {2,10,0,4,12,6,9,1,3,15,13,11,7,5,8,14};
@@ -52,7 +60,7 @@ struct client_data
 
 inline float frandom() { return rand()/(float)(RAND_MAX); }
 
-void snd_play_random(int setid, float vol, float pan)
+void snd_play_random(int chn, int setid, float vol, vec2 pos)
 {
 	soundset *set = &data->sounds[setid];
 	
@@ -61,7 +69,7 @@ void snd_play_random(int setid, float vol, float pan)
 		
 	if(set->num_sounds == 1)
 	{
-		snd_play(0, set->sounds[0].id, SND_PLAY_ONCE, 0, 0);
+		snd_play_at(chn, set->sounds[0].id, 0, pos.x, pos.y);
 		return;
 	}
 	
@@ -70,7 +78,7 @@ void snd_play_random(int setid, float vol, float pan)
 	do {
 		id = rand() % set->num_sounds;
 	} while(id == set->last);
-	snd_play(0, set->sounds[id].id, SND_PLAY_ONCE, 0, 1);
+	snd_play_at(chn, set->sounds[id].id, 0, pos.x, pos.y);
 	set->last = id;
 }
 
@@ -465,6 +473,11 @@ static void render_loading(float percent)
 
 extern "C" void modc_init()
 {
+	// setup sound channels
+	snd_set_channel(CHN_GUI, 1.0f, 0.0f);
+	snd_set_channel(CHN_MUSIC, 1.0f, 0.0f);
+	snd_set_channel(CHN_WORLD, 1.0f, 1.0f);
+	
 	// load the data container
 	data = load_data_from_memory(internal_data);
 
@@ -653,14 +666,14 @@ static void process_events(int s)
 			int soundid = ev->sound; //(ev->sound & SOUND_MASK);
 			//bool bstartloop = (ev->sound & SOUND_LOOPFLAG_STARTLOOP) != 0;
 			//bool bstoploop = (ev->sound & SOUND_LOOPFLAG_STOPLOOP) != 0;
-			float vol, pan;
-			sound_vol_pan(p, &vol, &pan);
+			//float vol, pan;
+			//sound_vol_pan(p, &vol, &pan);
 			
 			if(soundid >= 0 && soundid < NUM_SOUNDS)
 			{
 				// TODO: we need to control the volume of the diffrent sounds
 				// 		depening on the category
-				snd_play_random(soundid, vol, pan);
+				snd_play_random(CHN_WORLD, soundid, 1.0f, p);
 			}
 		}
 	}
@@ -1833,6 +1846,9 @@ void render_game()
 	local_player_pos = mix(predicted_prev_player.pos, predicted_player.pos, client_intrapredtick());
 	if(local_player && local_player->team == -1)
 		spectate = true;
+
+	// set listner pos
+	snd_set_listener_pos(local_player_pos.x, local_player_pos.y);
 		
 	animstate idlestate;
 	anim_eval(&data->animations[ANIM_BASE], 0, &idlestate);
@@ -2412,7 +2428,9 @@ extern "C" void modc_render()
 	else // if (client_state() != CLIENTSTATE_CONNECTING && client_state() != CLIENTSTATE_LOADING)
 	{
 		if (music_menu_id == -1)
-			music_menu_id = snd_play(0, music_menu, SND_LOOP, 0, 0);
+		{
+			music_menu_id = snd_play(CHN_MUSIC, music_menu, SNDFLAG_LOOP);
+		}
 		
 		//netaddr4 server_address;
 		if(modmenu_render(false) == -1)
@@ -2446,9 +2464,9 @@ extern "C" void modc_message(int msg)
 		chat_add_line(cid, team, message);
 
 		if(cid >= 0)
-			snd_play(0, data->sounds[SOUND_CHAT_CLIENT].sounds[0].id, SND_PLAY_ONCE, 0, 0);
+			snd_play(CHN_GUI, data->sounds[SOUND_CHAT_CLIENT].sounds[0].id, 0);
 		else
-			snd_play(0, data->sounds[SOUND_CHAT_SERVER].sounds[0].id, SND_PLAY_ONCE, 0, 0);
+			snd_play(CHN_GUI, data->sounds[SOUND_CHAT_SERVER].sounds[0].id, 0);
 	}
 	else if(msg == MSG_SETNAME)
 	{