about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/engine/client/snd.c609
-rw-r--r--src/engine/datafile.c18
-rw-r--r--src/engine/external/pnglite/pnglite.h12
-rw-r--r--src/engine/interface.h2
-rw-r--r--src/engine/system.c26
-rw-r--r--src/engine/system.h2
-rw-r--r--src/game/client/game_client.cpp10
7 files changed, 327 insertions, 352 deletions
diff --git a/src/engine/client/snd.c b/src/engine/client/snd.c
index 6b490f72..7ea5e752 100644
--- a/src/engine/client/snd.c
+++ b/src/engine/client/snd.c
@@ -4,356 +4,306 @@
 
 #include <engine/external/portaudio/portaudio.h>
 #include <engine/external/wavpack/wavpack.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
 
 enum
 {
-	NUM_FRAMES_STOP = 512,
-	NUM_FRAMES_LERP = 512,
+	NUM_SOUNDS = 512,
+	NUM_VOICES = 64,
+	NUM_CHANNELS = 4,
 };
 
-static const float NUM_FRAMES_STOP_INV = 1.0f/(float)NUM_FRAMES_STOP;
-static const float NUM_FRAMES_LERP_INV = 1.0f/(float)NUM_FRAMES_LERP;
-
-static const float GLOBAL_VOLUME_SCALE = 0.75f;
-static float master_volume = 1.0f;
-
-static const float GLOBAL_SOUND_DELAY = 0.05f;
-
-// --- sound ---
-typedef struct
+enum
 {
-	/*
-public:
-	sound_data() :
-		data(0x0),
-		num_samples(0),
-		rate(0),
-		channels(0),
-		sustain_start(-1),
-		sustain_end(-1),
-		last_played(0)
-	{ }*/
+	MAX_FRAMES = 1024
+};
 
+static struct sound
+{
 	short *data;
 	int num_samples;
 	int rate;
 	int channels;
-	int sustain_start;
-	int sustain_end;
-	int64 last_played;
-} SOUND_DATA;
+	int loop_start;
+	int loop_end;
+} sounds[NUM_SOUNDS] = { {0x0, 0, 0, 0, -1, -1} };
 
-
-static float clampf(float val, float lower, float upper)
+static struct voice
 {
-	if(val > upper)
-		return upper;
-	if(val < lower)
-		return lower;
-	return val;
-}
+	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 int clampi(int val, int lower, int upper)
+static struct channel
 {
-	if(val > upper)
-		return upper;
-	if(val < lower)
-		return lower;
-	return val;
-}
-/*
-template<typename T>
-inline const T clamp(const T val, const T lower, const T upper)
+	volatile struct voice *first_voice;
+	float vol;
+	float pan;
+	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)
 {
-	if(val > upper)
-		return upper;
-	if(val < lower)
-		return lower;
-	return val;
-}*/
-
+	if(i > 0x7fff)
+		return 0x7fff;
+	else if(i < -0x7fff)
+		return -0x7fff;
+	return i;
+}
 
-typedef struct
+static inline float sgn(float f)
 {
-/*
-public:
-	channel()
-	{ data = 0; lerp = -1; stop = -1; }
-*/
-	
-	SOUND_DATA *data;
-	int tick;
-	int loop;
-	float pan;
-	float vol;
-	float old_vol;
-	float new_vol;
-	int lerp;
-	int stop;
-} MIXER_CHANNEL;
+	if(f < 0.0f)
+		return -1.0f;
+	return 1.0f;
+}
 
-enum
+static void reset_voice(struct voice *v)
 {
-	MAX_CHANNELS=32,
-	MAX_FILL_FRAMES=256,
-};
-
-static MIXER_CHANNEL channels[MAX_CHANNELS];
-static int buffer[MAX_FILL_FRAMES*2];
+	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;
+}
 
-static void mixer_fill_mono(int *out, unsigned long frames, MIXER_CHANNEL *c, float dv)
+static inline void fill_mono(int *out, unsigned frames, struct voice *v, float fvol, float fpan)
 {
-	float pl = clampf(1.0f - c->pan, 0.0f, 1.0f);
-	float pr = clampf(1.0f + c->pan, 0.0f, 1.0f);
-	unsigned long i;
+	int ivol = (int) (31.0f * fvol);
+	int ipan = (int) (31.0f * ipan);
 
+	unsigned i;
 	for(i = 0; i < frames; i++)
 	{
-		float val = c->vol * master_volume * c->data->data[c->tick];
-
-		out[i<<1] += (int)(pl*val);
-		out[(i<<1)+1] += (int)(pr*val);
-		c->tick++;
-		c->vol += dv;
-		if(c->vol < 0.0f) c->vol = 0.0f;
+		unsigned j = i<<1;
+		int val = v->sound->data[v->tick] * ivol;
+		out[j] += val;
+		out[j+1] += val;
+		v->tick++;
 	}
 }
 
-static void mixer_fill_stereo(int *out, unsigned long frames, MIXER_CHANNEL *c, float dv)
+static inline void fill_stereo(int *out, unsigned frames, struct voice *v, float fvol, float fpan)
 {
-	float pl = clampf(1.0f - c->pan, 0.0f, 1.0f);
-	float pr = clampf(1.0f + c->pan, 0.0f, 1.0f);
-	unsigned long i;
+	int ivol = (int) (31.0f * fvol);
+	int ipan = (int) (31.0f * ipan);
 
+	unsigned i;
 	for(i = 0; i < frames; i++)
 	{
-		int vl = (int)(pl*c->vol * master_volume * c->data->data[c->tick]);
-		int vr = (int)(pr*c->vol * master_volume * c->data->data[c->tick + 1]);
-		out[i<<1] += vl;
-		out[(i<<1)+1] += vr;
-		c->tick += 2;
-		c->vol += dv;
-		if(c->vol < 0.0f) c->vol = 0.0f;
+		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;
 	}
 }
 
-static void mixer_fill(void *output, unsigned long frames)
+static void mix(short *out, unsigned frames)
 {
-	short *out = (short*)output;
+	static int main_buffer[MAX_FRAMES*2];
 
-	dbg_assert(frames <= MAX_FILL_FRAMES, "not enough fill frames in buffer");
-	unsigned long i;
-	int c;
+	dbg_assert(frames <= MAX_FRAMES, "too many frames to fill");
 
+	unsigned i;
 	for(i = 0; i < frames; i++)
 	{
-		buffer[i<<1] = 0;
-		buffer[(i<<1)+1] = 0;
+		unsigned j = i<<1;
+		main_buffer[j] = 0;
+		main_buffer[j+1] = 0;
 	}
 
-	for(c = 0; c < MAX_CHANNELS; c++)
+	unsigned cid;
+	for(cid = 0; cid < NUM_CHANNELS; cid++)
 	{
-		unsigned long filled = 0;
-		while(channels[c].data && filled < frames)
+		struct channel *c = &channels[cid];
+		struct voice *v = (struct voice*)c->first_voice;
+
+		while(v)
 		{
-			unsigned long frames_left = (channels[c].data->num_samples - channels[c].tick) >> (channels[c].data->channels-1);
-			unsigned long to_fill = frames>frames_left?frames_left:frames;
-			float dv = 0.0f;
-
-			if(channels[c].stop >= 0)
-				to_fill = (unsigned)channels[c].stop>frames_left?frames:channels[c].stop;
-			if(channels[c].loop >= 0 &&
-					channels[c].data->sustain_start >= 0)
+			unsigned filled = 0;
+			while(v->sound && filled < frames)
 			{
-				unsigned long tmp = channels[c].data->sustain_end - channels[c].tick;
-				to_fill = tmp>frames?frames:tmp;
-			}
+				// 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;
+				}
 
-			if(channels[c].lerp >= 0)
-			{
-					dv = (channels[c].new_vol - channels[c].old_vol) * NUM_FRAMES_LERP_INV;
-			}
+				// 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;
+				}
 
-			if(channels[c].data->channels == 1)
-				mixer_fill_mono(buffer, to_fill, &channels[c], dv);
-			else
-				mixer_fill_stereo(buffer, to_fill, &channels[c], dv);
+				// 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;
+				}
 
-			if(channels[c].loop >= 0 &&
-					channels[c].data->sustain_start >= 0 &&
-					channels[c].tick >= channels[c].data->sustain_end)
-				channels[c].tick = channels[c].data->sustain_start;
+				// 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)
+				{
+					if(v->next)
+						v->next->prev = v->prev;
 
-			if(channels[c].stop >= 0)
-				channels[c].stop -=  to_fill;
-			if(channels[c].tick >= channels[c].data->num_samples ||
-					channels[c].stop == 0)
-				channels[c].data = 0;
+					if(v->prev)
+						v->prev->next = v->next;
+					else
+						channels[cid].first_voice = v->next;
 
-			channels[c].lerp -= to_fill;
-			if(channels[c].lerp < 0)
-				channels[c].lerp = -1;
+					dbg_msg("snd", "sound stopped");
 
+					reset_voice(v);
+				}
+
+				filled += to_fill;
+			}
 
-			filled += to_fill;
+			v = (struct voice*)v->next;
 		}
 	}
 
+	// clamp accumulated values
 	for(i = 0; i < frames; i++)
 	{
-		out[i<<1] = (short)clampi(buffer[i<<1], -0x7fff, 0x7fff);
-		out[(i<<1)+1] = (short)clampi(buffer[(i<<1)+1], -0x7fff, 0x7fff);
-	}
-}
-
-int mixer_play(SOUND_DATA *sound, unsigned loop, float vol, float pan)
-{
-	if(time_get() - sound->last_played < (int64)(time_freq()*GLOBAL_SOUND_DELAY))
-		return -1;
+		int j = i<<1;
+		int vl = main_buffer[j];
+		int vr = main_buffer[j+1];
 
-	int c;
-	for(c = 0; c < MAX_CHANNELS; c++)
-	{
-		if(channels[c].data == 0)
-		{
-			channels[c].data = sound;
-			channels[c].tick = 0;
-			channels[c].loop = loop;
-			channels[c].vol = vol * GLOBAL_VOLUME_SCALE;
-			channels[c].pan = pan;
-			channels[c].stop = -1;
-			channels[c].lerp = -1;
-			sound->last_played = time_get();
-			return c;
-		}
+		out[j] = int2short(vl>>5);
+		out[j+1] = int2short(vr>>5);
 	}
-
-	return -1;
-}
-
-static void mixer_stop(int id)
-{
-	dbg_assert(id >= 0 && id < MAX_CHANNELS, "id out of bounds");
-	channels[id].old_vol = channels[id].vol;
-	channels[id].stop = NUM_FRAMES_STOP;
-}
-
-static void mixer_set_vol(int id, float vol)
-{
-	dbg_assert(id >= 0 && id < MAX_CHANNELS, "id out of bounds");
-	channels[id].new_vol = vol * GLOBAL_VOLUME_SCALE;
-	channels[id].old_vol = channels[id].vol;
-	channels[id].lerp = NUM_FRAMES_LERP;
 }
 
-typedef struct
-{
-	SOUND_DATA sound;
-	int next;
-} SOUND;
-
-enum
-{
-	MAX_SOUNDS = 1024,
-};
-
-static SOUND sounds[MAX_SOUNDS];
-static int first_free_sound;
-
-static PaStream *stream = 0;
-
 static int pacallback(const void *in, void *out, unsigned long frames, const PaStreamCallbackTimeInfo* time, PaStreamCallbackFlags status, void *user)
 {
-	mixer_fill(out, frames);
+	mix(out, frames);
 	return 0;
 }
 
+static PaStream *stream;
+
 int snd_init()
 {
-	int i;
-	first_free_sound = 0;
-	for(i = 0; i < MAX_SOUNDS; i++)
-		sounds[i].next = i+1;
-	sounds[MAX_SOUNDS-1].next = -1;
-	
-	// init PA
 	PaStreamParameters params;
 	PaError err = Pa_Initialize();
-	if(err != paNoError)
-	{
-		dbg_msg("audio_stream", "portaudio error: %s", Pa_GetErrorText(err));
-		return 0;
-	}
-
 	params.device = Pa_GetDefaultOutputDevice();
-	if(params.device == -1)
-	{
-		dbg_msg("audio_stream", "no default output device");
-		return 0;
-	}
 	params.channelCount = 2;
 	params.sampleFormat = paInt16;
 	params.suggestedLatency = Pa_GetDeviceInfo(params.device)->defaultLowOutputLatency;
 	params.hostApiSpecificStreamInfo = 0x0;
 
 	err = Pa_OpenStream(
-		&stream,        /* passes back stream pointer */
-		0,              /* no input channels */
-		&params,		/* pointer to parameters */
-		44100,          /* sample rate */
-		128,            /* frames per buffer */
-		paClipOff,		/* no clamping */
-		pacallback,		/* specify our custom callback */
-		0); /* pass our data through to callback */	
-
-	if(err != paNoError)
-	{
-		dbg_msg("audio_stream", "portaudio error: %s", Pa_GetErrorText(err));
-		return 0;
-	}
-	
+			&stream,        /* passes back stream pointer */
+			0,              /* no input channels */
+			&params,                /* pointer to parameters */
+			44100,          /* sample rate */
+			128,            /* frames per buffer */
+			paClipOff,              /* no clamping */
+			pacallback,             /* specify our custom callback */
+			0x0); /* pass our data through to callback */
 	err = Pa_StartStream(stream);
-	if(err != paNoError)
-	{
-		dbg_msg("audio_stream", "portaudio error: %s", Pa_GetErrorText(err));
-		return 0;
-	}
-	
-	return 1;	
+
+	return 0;
 }
 
 int snd_shutdown()
 {
 	Pa_StopStream(stream);
-	stream = NULL;
-	Pa_Terminate();	
-	return 1;
-}
+	Pa_Terminate();
 
-float snd_get_master_volume()
-{
-	return master_volume;
+	return 0;
 }
 
-void snd_set_master_volume(float val)
+void snd_set_center(int x, int y)
 {
-	if(val < 0.0f)
-		val = 0.0f;
-	else if(val > 1.0f)
-		val = 1.0f;
-
-	master_volume = val;
+	center_x = x;
+	center_y = y;
 }
 
-static int snd_alloc_sound()
+int snd_alloc_id()
 {
-	if(first_free_sound < 0)
-		return -1;
-	int id = first_free_sound;
-	first_free_sound = sounds[id].next;
-	sounds[id].next = -1;
-	return id;
+	unsigned sid;
+	for(sid = 0; sid < NUM_SOUNDS; sid++)
+	{
+		if(sounds[sid].data == 0x0)
+		{
+			return sid;
+		}
+	}
+
+	return -1;
 }
 
 static FILE *file = NULL;
@@ -365,11 +315,15 @@ static int read_data(void *buffer, int size)
 
 int snd_load_wv(const char *filename)
 {
-	SOUND_DATA snd;
-	int id = -1;
-
+	struct sound *snd;
+	int sid = -1;
 	char error[100];
 
+	sid = snd_alloc_id();
+	if(sid < 0)
+		return -1;
+	snd = &sounds[sid];
+
 	file = fopen(filename, "rb"); // TODO: use system.h stuff for this
 
 	WavpackContext *context = WavpackOpenFileInput(read_data, error);
@@ -380,18 +334,18 @@ int snd_load_wv(const char *filename)
 		unsigned int samplerate = WavpackGetSampleRate(context);
 		int channels = WavpackGetNumChannels(context);
 
-		snd.channels = channels;
-		snd.rate = samplerate;
+		snd->channels = channels;
+		snd->rate = samplerate;
 
-		if(snd.channels > 2)
+		if(snd->channels > 2)
 		{
 			dbg_msg("sound/wv", "file is not mono or stereo. filename='%s'", filename);
 			return -1;
 		}
 
-		if(snd.rate != 44100)
+		if(snd->rate != 44100)
 		{
-			dbg_msg("sound/wv", "file is %d Hz, not 44100 Hz. filename='%s'", snd.rate, filename);
+			dbg_msg("sound/wv", "file is %d Hz, not 44100 Hz. filename='%s'", snd->rate, filename);
 			return -1;
 		}
 		
@@ -405,8 +359,8 @@ int snd_load_wv(const char *filename)
 		WavpackUnpackSamples(context, data, samples); // TODO: check return value
 		int *src = data;
 		
-		snd.data = (short *)mem_alloc(2*samples*channels, 1);
-		short *dst = snd.data;
+		snd->data = (short *)mem_alloc(2*samples*channels, 1);
+		short *dst = snd->data;
 
 		int i;
 		for (i = 0; i < samples*channels; i++)
@@ -414,12 +368,9 @@ int snd_load_wv(const char *filename)
 
 		mem_free(data);
 
-		snd.num_samples = samples;
-		snd.sustain_start = -1;
-		snd.sustain_end = -1;
-		snd.last_played = 0;
-		id = snd_alloc_sound();
-		sounds[id].sound = snd;
+		snd->num_samples = samples;
+		snd->loop_start = -1;
+		snd->loop_end = -1;
 	}
 	else
 	{
@@ -429,23 +380,14 @@ int snd_load_wv(const char *filename)
 	fclose(file);
 	file = NULL;
 
-	if(id >= 0)
-	{
-		if(config.debug)
-			dbg_msg("sound/wv", "loaded %s", filename);
-	}
-	else
-	{
-		dbg_msg("sound/wv", "failed to load %s", filename);
-	}
+	if(config.debug)
+		dbg_msg("sound/wv", "loaded %s", filename);
 
-	return id;
+	return sid;
 }
 
 int snd_load_wav(const char *filename)
 {
-	SOUND_DATA snd;
-	
 	// open file for reading
 	IOHANDLE file;
 	file = io_open(filename, IOFLAG_READ);
@@ -455,7 +397,14 @@ int snd_load_wav(const char *filename)
 		return -1;
 	}
 
-	int id = -1;
+	struct sound *snd;
+	int sid = -1;
+
+	sid = snd_alloc_id();
+	if(sid < 0)
+		return -1;
+	snd = &sounds[sid];
+
 	int state = 0;
 	while(1)
 	{
@@ -503,8 +452,8 @@ int snd_load_wav(const char *filename)
 				
 				// 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);
+				snd->channels = fmt[2] | (fmt[3]<<8);
+				snd->rate = fmt[4] | (fmt[5]<<8) | (fmt[6]<<16) | (fmt[7]<<24);
 
 				if(compression_code != 1)
 				{
@@ -512,15 +461,15 @@ int snd_load_wav(const char *filename)
 					return -1;
 				}
 				
-				if(snd.channels > 2)
+				if(snd->channels > 2)
 				{
 					dbg_msg("sound/wav", "file is not mono or stereo. filename='%s'", filename);
 					return -1;
 				}
 
-				if(snd.rate != 44100)
+				if(snd->rate != 44100)
 				{
-					dbg_msg("sound/wav", "file is %d Hz, not 44100 Hz. filename='%s'", snd.rate, filename);
+					dbg_msg("sound/wav", "file is %d Hz, not 44100 Hz. filename='%s'", snd->rate, filename);
 					return -1;
 				}
 				
@@ -542,21 +491,14 @@ int snd_load_wav(const char *filename)
 			// 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);
+				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)
-				for(unsigned i = 0; i < (unsigned)snd.num_samples; i++)
-				{
-					unsigned j = i << 1;
-					snd.data[i] = ((short)((char*)snd.data)[j]) + ((short)((char*)snd.data)[j+1] << 8);
-				}
+				swap_endian(snd->data, sizeof(short), snd->num_samples);
 #endif
-				snd.sustain_start = -1;
-				snd.sustain_end = -1;
-				snd.last_played = 0;
-				id = snd_alloc_sound();
-				sounds[id].sound = snd;
+				snd->loop_start = -1;
+				snd->loop_end = -1;
 				state++;
 			}
 			else
@@ -580,8 +522,8 @@ int snd_load_wav(const char *filename)
 					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));
-					sounds[id].sound.sustain_start = start * sounds[id].sound.channels;
-					sounds[id].sound.sustain_end = end * sounds[id].sound.channels;
+					snd->loop_start = start * snd->channels;
+					snd->loop_end = end * snd->channels;
 				}
 
 				if(num_loops > 1)
@@ -597,38 +539,47 @@ int snd_load_wav(const char *filename)
 			io_skip(file, chunk_size);
 	}
 
-	if(id >= 0)
-	{
-		if(config.debug)
-			dbg_msg("sound/wav", "loaded %s", filename);
-	}
-	else
-		dbg_msg("sound/wav", "failed to load %s", filename);
+	if(config.debug)
+		dbg_msg("sound/wav", "loaded %s", filename);
 
-	return id;
+	return sid;
 }
 
-int snd_play(int id, int loop, float vol, float pan)
+int snd_play(int cid, int sid, int loop, int x, int y)
 {
-	if(id < 0)
+	int vid;
+	for(vid = 0; vid < NUM_VOICES; vid++)
 	{
-		dbg_msg("snd", "bad sound id");
-		return -1;
+		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;
+
+			// add voice to channel last, to avoid threding errors
+			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];
+			return vid;
+		}
 	}
 
-	dbg_assert(sounds[id].sound.data != 0, "null sound");
-	dbg_assert(sounds[id].next == -1, "sound isn't allocated");
-	return mixer_play(&sounds[id].sound, loop, vol, pan);
+	return -1;
 }
 
-void snd_stop(int id)
+void snd_set_master_volume(float val)
 {
-	if(id >= 0)
-		mixer_stop(id);
+	master_vol = val;
 }
 
-void snd_set_vol(int id, float vol)
+void snd_stop(int vid)
 {
-	if(id >= 0)
-		mixer_set_vol(id, vol);
+	//TODO: lerp volume to 0
+	voices[vid].stop = 0;
 }
diff --git a/src/engine/datafile.c b/src/engine/datafile.c
index fddd522d..c11082ac 100644
--- a/src/engine/datafile.c
+++ b/src/engine/datafile.c
@@ -92,8 +92,10 @@ DATAFILE *datafile_load(const char *filename)
 			return 0;
 		}
 	}
-	
-	/* TODO: swap the header */
+
+#if defined(CONF_ARCH_ENDIAN_BIG)
+	swap_endian(&header, sizeof(int), sizeof(header)/sizeof(int));	
+#endif
 	if(header.version != 3 && header.version != 4)
 	{
 		dbg_msg("datafile", "wrong version. version=%x", header.version);
@@ -129,16 +131,10 @@ DATAFILE *datafile_load(const char *filename)
 		dbg_msg("datafile", "couldn't load the whole thing, wanted=%d got=%d", size, readsize);
 		return 0;
 	}
-/*
+
 #if defined(CONF_ARCH_ENDIAN_BIG)
-	unsigned *dst = (unsigned*)df;
-	unsigned char *src = (unsigned char*)df;
-	for(unsigned i = 0; i < swapsize; i++)
-	{
-		unsigned j = i << 2;
-		dst[i] = src[j] | src[j+1]<<8 | src[j+2]<<16 | src[j+3]<<24;
-	}
-#endif*/
+	swap_endian(df->data, sizeof(int), header.swaplen);
+#endif
 	
 	if(DEBUG)
 		dbg_msg("datafile", "item_size=%d", df->header.item_size);
diff --git a/src/engine/external/pnglite/pnglite.h b/src/engine/external/pnglite/pnglite.h
index f464c46b..72ff1c52 100644
--- a/src/engine/external/pnglite/pnglite.h
+++ b/src/engine/external/pnglite/pnglite.h
@@ -70,10 +70,10 @@ enum
 	Typedefs for callbacks.

 */

 

-typedef unsigned (*png_write_callback_t)(void* input, size_t size, size_t numel, void* user_pointer);

-typedef unsigned (*png_read_callback_t)(void* output, size_t size, size_t numel, void* user_pointer);

+typedef unsigned (*png_write_callback_t)(void* input, unsigned long size, unsigned long numel, void* user_pointer);

+typedef unsigned (*png_read_callback_t)(void* output, unsigned long size, unsigned long numel, void* user_pointer);

 typedef void (*png_free_t)(void* p);

-typedef void * (*png_alloc_t)(size_t s);

+typedef void * (*png_alloc_t)(unsigned long s);

 

 typedef struct

 {

@@ -100,7 +100,7 @@ typedef struct
 

 	This function initializes pnglite. The parameters can be used to set your own memory allocation routines following these formats:

 

-	> void* (*custom_alloc)(size_t s)

+	> void* (*custom_alloc)(unsigned long s)

 	> void (*custom_free)(void* p)

 	Parameters:

 		pngalloc - Pointer to custom allocation routine. If 0 is passed, malloc from libc will be used.

@@ -136,8 +136,8 @@ int png_open_file_write(png_t *png, const char* filename);
 

 	This function reads or writes a png from/to the specified callback. The callbacks should be of the format:

 

-	> size_t (*png_write_callback_t)(void* input, size_t size, size_t numel, void* user_pointer);

-	> size_t (*png_read_callback_t)(void* output, size_t size, size_t numel, void* user_pointer).

+	> unsigned long (*png_write_callback_t)(void* input, unsigned long size, unsigned long numel, void* user_pointer);

+	> unsigned long (*png_read_callback_t)(void* output, unsigned long size, unsigned long numel, void* user_pointer).

 

 	Only one callback has to be specified. The read callback in case of PNG reading, otherwise the write callback.

 

diff --git a/src/engine/interface.h b/src/engine/interface.h
index b2412de7..fb2ebe6c 100644
--- a/src/engine/interface.h
+++ b/src/engine/interface.h
@@ -341,7 +341,7 @@ float snd_get_master_volume();
 void snd_set_master_volume(float val);
 int snd_load_wav(const char *filename);
 int snd_load_wv(const char *filename);
-int snd_play(int sound, int loop, float vol, float pan);
+int snd_play(int cid, int sid, int loop, int x, int y);
 void snd_stop(int id);
 void snd_set_vol(int id, float vol);
 int snd_shutdown();
diff --git a/src/engine/system.c b/src/engine/system.c
index 6226d4d2..45c648ab 100644
--- a/src/engine/system.c
+++ b/src/engine/system.c
@@ -567,3 +567,29 @@ int fs_listdir(const char *dir, fs_listdir_callback cb, void *user)
 	return 0;
 #endif
 }
+
+void swap_endian(void *data, unsigned elem_size, unsigned num)
+{
+	char *src = (char*) data;
+	char *dst = src + (elem_size - 1);
+
+	while(num)
+	{
+		unsigned n = elem_size>>1;
+		char tmp;
+		while(n)
+		{
+			tmp = *src;
+			*src = *dst;
+			*dst = tmp;
+
+			src++;
+			dst--;
+			n--;
+		}
+
+		src = src + (elem_size>>1);
+		dst = src + (elem_size - 1);
+		num--;
+	}
+}
diff --git a/src/engine/system.h b/src/engine/system.h
index 255a518c..692aae06 100644
--- a/src/engine/system.h
+++ b/src/engine/system.h
@@ -515,6 +515,8 @@ int net_addr4_cmp(const NETADDR4 *a, const NETADDR4 *b);
 
 void mem_debug_dump();
 int mem_allocated();
+
+void swap_endian(void *data, unsigned elem_size, unsigned num);
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/game/client/game_client.cpp b/src/game/client/game_client.cpp
index 301bfdd8..983a84b6 100644
--- a/src/game/client/game_client.cpp
+++ b/src/game/client/game_client.cpp
@@ -53,7 +53,7 @@ void snd_play_random(int setid, float vol, float pan)
 		
 	if(set->num_sounds == 1)
 	{
-		snd_play(set->sounds[0].id, SND_PLAY_ONCE, vol, pan);
+		snd_play(0, set->sounds[0].id, SND_PLAY_ONCE, 0, 0);
 		return;
 	}
 	
@@ -62,7 +62,7 @@ void snd_play_random(int setid, float vol, float pan)
 	do {
 		id = rand() % set->num_sounds;
 	} while(id == set->last);
-	snd_play(set->sounds[id].id, SND_PLAY_ONCE, vol, pan);
+	snd_play(0, set->sounds[id].id, SND_PLAY_ONCE, 0, 1);
 	set->last = id;
 }
 
@@ -2149,7 +2149,7 @@ 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(music_menu, SND_LOOP, 1.0f, 0.0f);
+			music_menu_id = snd_play(0, music_menu, SND_LOOP, 0, 0);
 		
 		//netaddr4 server_address;
 		if(modmenu_render(false) == -1)
@@ -2182,9 +2182,9 @@ extern "C" void modc_message(int msg)
 		chat_add_line(cid, message);
 
 		if(cid >= 0)
-			snd_play(data->sounds[SOUND_CHAT_CLIENT].sounds[0].id, SND_PLAY_ONCE, 1.0f, 0.0f);
+			snd_play(0, data->sounds[SOUND_CHAT_CLIENT].sounds[0].id, SND_PLAY_ONCE, 0, 0);
 		else
-			snd_play(data->sounds[SOUND_CHAT_SERVER].sounds[0].id, SND_PLAY_ONCE, 1.0f, 0.0f);
+			snd_play(0, data->sounds[SOUND_CHAT_SERVER].sounds[0].id, SND_PLAY_ONCE, 0, 0);
 	}
 	else if(msg == MSG_SETNAME)
 	{