--- libmikmod-3.2.0-beta2/drivers/drv_esd.c	2004-01-31 23:39:40.000000000 +0100
+++ libmikmod-3.2.0-beta2.new/drivers/drv_esd.c	2008-02-18 12:09:07.000000000 +0100
@@ -81,7 +81,6 @@
 
 static	int sndfd=-1;
 static	esd_format_t format;
-static	SBYTE *audiobuffer=NULL;
 static	CHAR *espeaker=NULL;
 
 #ifdef MIKMOD_DYNAMIC
@@ -100,7 +99,7 @@
 	if (libesd) return 0;
 
 	/* load libesd.so */
-	libesd=dlopen("libesd.so",RTLD_LAZY|RTLD_GLOBAL);
+	libesd=dlopen("libesd.so.0",RTLD_LAZY|RTLD_GLOBAL);
 	if (!libesd) return 1;
 
 	/* resolve function references */
@@ -124,23 +123,88 @@
 }
 #endif
 
+/* A simple chunk fifo buffer, as we cannot query to ask how much free space
+   there is with esd, we keep a fifo filled and try to write the entire fifo
+   non blocking, stopping when we get -EAGAIN */
+
+#define AUDIO_BUF_SIZE (ESD_BUF_SIZE * 4)
+
+static SBYTE audiobuffer[AUDIO_BUF_SIZE];
+static int audiobuffer_start, audiobuffer_end, audiobuffer_empty;
+
+static void audiobuffer_init(void)
+{
+	audiobuffer_empty = 1;
+	audiobuffer_start = audiobuffer_end = 0;
+}
+
+/* Get a free chunk of the fifo, the caller is expected to use all of it, so
+   all of it gets marked filled */
+static SBYTE *audiobuffer_get_free_chunk(int *size)
+{
+	int end = audiobuffer_end;
+
+	if (audiobuffer_empty)
+	{
+		audiobuffer_empty = 0;
+		*size = AUDIO_BUF_SIZE;
+	}
+	else if (audiobuffer_end > audiobuffer_start)
+	{
+		*size = AUDIO_BUF_SIZE - audiobuffer_end;
+		audiobuffer_end = 0;
+	}
+	else
+	{
+		*size = audiobuffer_start - audiobuffer_end;
+		audiobuffer_end = audiobuffer_start;
+	}
+
+	return audiobuffer + end;
+}
+
+/* Get a filled chunk from the fifo, the caller must call audiobuffer_mark_free
+   to tell the fifo how much of the chunk it has consumed */
+static SBYTE *audiobuffer_get_filled_chunk(int *size)
+{
+	if (audiobuffer_empty)
+	{
+		*size = 0;
+	}
+	else if (audiobuffer_end > audiobuffer_start)
+	{
+		*size = audiobuffer_end - audiobuffer_start;
+	}
+	else
+	{
+		*size = AUDIO_BUF_SIZE - audiobuffer_start;
+	}
+
+	return audiobuffer + audiobuffer_start;
+}
+
+/* Tell the fifo to mark size bytes as free starting from the current head of
+   the fifo */
+static void audiobuffer_mark_free(int size)
+{
+	audiobuffer_start = (audiobuffer_start + size) % AUDIO_BUF_SIZE;
+	if (audiobuffer_start == audiobuffer_end)
+	{
+		audiobuffer_empty = 1;
+		audiobuffer_start = audiobuffer_end = 0;
+	}
+}
+
 /* I hope to have this function integrated into libesd someday...*/
 static ssize_t esd_writebuf(int fd,const void *buffer,size_t count)
 {
-	ssize_t start=0;
+	ssize_t res;
 
-	while (start<count) {
-		ssize_t res;
+	res = write(fd, (char*)buffer, count);
+	if (res < 0 && errno == EAGAIN)
+		return 0;
 
-		res=write(fd,(char*)buffer+start,count-start);
-		if (res<0) {
-			/* connection lost */
-			if (errno==EPIPE)
-				return -1-start;
-		} else
-			start+=res;
-	}
-	return start;
+	return res;
 }
 
 static void ESD_CommandLine(CHAR *cmdline)
@@ -193,13 +257,14 @@
 			_mm_errno=MMERR_OPENING_AUDIO;
 			return 1;
 		}
+		fcntl(sndfd, F_SETFL, fcntl(sndfd, F_GETFL) | O_NONBLOCK);
 	} else {
 		_mm_errno=MMERR_OUT_OF_MEMORY;
 		return 1;
 	}
-
-	if (!(audiobuffer=(SBYTE*)_mm_malloc(ESD_BUF_SIZE*sizeof(char))))
-		return 1;
+	
+	/* Initialize the audiobuffer */
+	audiobuffer_init();
 
 	return VC_Init();
 }
@@ -218,7 +283,6 @@
 static void ESD_Exit_internal(void)
 {
 	VC_Exit();
-	_mm_free(audiobuffer);
 	if (sndfd>=0) {
 		esd_closestream(sndfd);
 		sndfd=-1;
@@ -234,19 +298,50 @@
 #endif
 }
 
-static void ESD_Update_internal(int count)
+typedef ULONG (*VC_WriteBytesFunc)(SBYTE*, ULONG);
+
+static void ESD_Update_internal(VC_WriteBytesFunc WriteBytes)
 {
 	static time_t losttime;
 
 	if (sndfd>=0) {
-		if (esd_writebuf(sndfd,audiobuffer,count)<0) {
-			/* if we lost our connection with esd, clean up and work as the
-			   nosound driver until we can reconnect */
-			esd_closestream(sndfd);
-			sndfd=-1;
-			signal(SIGPIPE,SIG_DFL);
-			losttime=time(NULL);
+		SBYTE *chunk;
+		int size;
+		ssize_t written;
+		
+		/* Fill fifo */
+		chunk = audiobuffer_get_free_chunk(&size);
+		while (size)
+		{
+			WriteBytes(chunk, size);
+			chunk = audiobuffer_get_free_chunk(&size);
 		}
+
+		/* And write untill fifo empty, or we would block */
+		chunk = audiobuffer_get_filled_chunk(&size);
+		while (size)
+		{
+			written = esd_writebuf(sndfd, chunk, size);
+			if (written < 0)
+			{
+				/* if we lost our connection with esd, clean up
+				   and work as the nosound driver until we can
+				   reconnect */
+				esd_closestream(sndfd);
+				sndfd = -1;
+				signal(SIGPIPE, SIG_DFL);
+				losttime = time(NULL);
+				break;
+			}
+			
+			/* Stop if the buffer is full */
+			if (written == 0)
+				break;
+
+			audiobuffer_mark_free(written);
+
+			chunk = audiobuffer_get_filled_chunk(&size);
+		}		
 	} else {
 		/* an alarm would be better, but then the library user could not use
 		   alarm(2) himself... */
@@ -255,8 +350,11 @@
 			/* Attempt to reconnect every 5 seconds */
 			if (!(SETENV))
 				if ((sndfd=esd_playstream(format,md_mixfreq,espeaker,"libmikmod"))>=0) {
-					VC_SilenceBytes(audiobuffer,ESD_BUF_SIZE);
-					esd_writebuf(sndfd,audiobuffer,ESD_BUF_SIZE);
+					/* reconnected, clear fifo (contains old sound) and recurse
+					   to play sound */
+					audiobuffer_init();
+					fcntl(sndfd, F_SETFL, fcntl(sndfd, F_GETFL) | O_NONBLOCK);
+					ESD_Update_internal(WriteBytes);
 				}
 		}
 	}
@@ -264,22 +362,24 @@
 
 static void ESD_Update(void)
 {
-	ESD_Update_internal(VC_WriteBytes(audiobuffer,ESD_BUF_SIZE));
+	ESD_Update_internal(VC_WriteBytes);
 }
 
 static void ESD_Pause(void)
 {
-	ESD_Update_internal(VC_SilenceBytes(audiobuffer,ESD_BUF_SIZE));
+	ESD_Update_internal(VC_SilenceBytes);
 }
 
 static BOOL ESD_PlayStart(void)
 {
 	if (sndfd<0)
-		if (!(SETENV))
+		if (!(SETENV)) {
 			if ((sndfd=esd_playstream(format,md_mixfreq,espeaker,"libmikmod"))<0) {
 				_mm_errno=MMERR_OPENING_AUDIO;
 				return 1;
 			}
+			fcntl(sndfd, F_SETFL, fcntl(sndfd, F_GETFL) | O_NONBLOCK);
+		}
 	/* since the default behaviour of SIGPIPE on most Unices is to kill the
 	   program, we'll prefer handle EPIPE ourselves should the esd die - recent
 	   esdlib use a do-nothing handler, which prevents us from receiving EPIPE,
@@ -289,6 +389,7 @@
 	/* silence buffers */
 	VC_SilenceBytes(audiobuffer,ESD_BUF_SIZE);
 	esd_writebuf(sndfd,audiobuffer,ESD_BUF_SIZE);
+	audiobuffer_init();
 
 	return VC_PlayStart();
 }
@@ -299,6 +400,7 @@
 		/* silence buffers */
 		VC_SilenceBytes(audiobuffer,ESD_BUF_SIZE);
 		esd_writebuf(sndfd,audiobuffer,ESD_BUF_SIZE);
+		audiobuffer_init();
 
 		signal(SIGPIPE,SIG_DFL);
 	}
