Date: Mon, 11 Oct 2004 15:17:27 +0300 (EEST)
authorSam Lantinga <slouken@libsdl.org>
Fri, 12 Nov 2004 21:39:04 +0000
changeset 968 4675910b0b7b
parent 967 cda407d627a3
child 969 cfb9518670f4
Date: Mon, 11 Oct 2004 15:17:27 +0300 (EEST) From: Hannu Savolainen Subject: Re: SDL uses obsolete OSS features I did some work on getting OSS to work better with SDL. There have been some problems with select which should be fixed now. I'm having some problems in understanding what is the purpose of the DSP_WaitAudio() routine. I added a return to the very beginning of this routine and commendted out the define for USE_BLOCKING_WRITES. At least lbreakout2 seems to work as well as earlier. The latencies are the same. An ordinary blocking write does exactly the same thing than DSP_WaitAudio does. So I would recommend using the USE_BLOCKING_WRITES approach and removing everything from the DSP_WaitAudio routine. Also enabling USE_BLOCKING_WRITES makes it possible to simplify DSP_PlayAudio() because you don't need to handle the partial writes (the do-while loop). Attached is a patch against SDL-1.2.7. After these changes SDL will use OSS as it's designed to be used (make it as simple as possible). This code should work with all OSS implementations because it uses only the very fundamental features that have been there since the jurassic times.
src/audio/dsp/SDL_dspaudio.c
src/audio/dsp/SDL_dspaudio.h
--- a/src/audio/dsp/SDL_dspaudio.c	Fri Nov 12 21:29:52 2004 +0000
+++ b/src/audio/dsp/SDL_dspaudio.c	Fri Nov 12 21:39:04 2004 +0000
@@ -18,6 +18,9 @@
 
     Sam Lantinga
     slouken@libsdl.org
+
+    Modified in Oct 2004 by Hannu Savolainen 
+    hannu@opensound.com
 */
 
 #ifdef SAVE_RCSID
@@ -57,7 +60,6 @@
 #define DSP_DRIVER_NAME         "dsp"
 
 /* Open the audio device for playback, and don't block if busy */
-/*#define USE_BLOCKING_WRITES*/
 #define OPEN_FLAGS	(O_WRONLY|O_NONBLOCK)
 
 /* Audio driver functions */
@@ -130,94 +132,19 @@
 /* This function waits until it is possible to write a full sound buffer */
 static void DSP_WaitAudio(_THIS)
 {
-	/* Check to see if the thread-parent process is still alive */
-	{ static int cnt = 0;
-		/* Note that this only works with thread implementations 
-		   that use a different process id for each thread.
-		*/
-		if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */
-			if ( kill(parent, 0) < 0 ) {
-				this->enabled = 0;
-			}
-		}
-	}
-
-#ifndef USE_BLOCKING_WRITES /* Not necessary when using blocking writes */
-	/* See if we need to use timed audio synchronization */
-	if ( frame_ticks ) {
-		/* Use timer for general audio synchronization */
-		Sint32 ticks;
-
-		ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS;
-		if ( ticks > 0 ) {
-			SDL_Delay(ticks);
-		}
-	} else {
-		/* Use select() for audio synchronization */
-		fd_set fdset;
-		struct timeval timeout;
-
-		FD_ZERO(&fdset);
-		FD_SET(audio_fd, &fdset);
-		timeout.tv_sec = 10;
-		timeout.tv_usec = 0;
-#ifdef DEBUG_AUDIO
-		fprintf(stderr, "Waiting for audio to get ready\n");
-#endif
-		if ( select(audio_fd+1, NULL, &fdset, NULL, &timeout) <= 0 ) {
-			const char *message =
-			"Audio timeout - buggy audio driver? (disabled)";
-			/* In general we should never print to the screen,
-			   but in this case we have no other way of letting
-			   the user know what happened.
-			*/
-			fprintf(stderr, "SDL: %s\n", message);
-			this->enabled = 0;
-			/* Don't try to close - may hang */
-			audio_fd = -1;
-#ifdef DEBUG_AUDIO
-			fprintf(stderr, "Done disabling audio\n");
-#endif
-		}
-#ifdef DEBUG_AUDIO
-		fprintf(stderr, "Ready!\n");
-#endif
-	}
-#endif /* !USE_BLOCKING_WRITES */
+	/* Not needed at all since OSS handles waiting automagically */
 }
 
 static void DSP_PlayAudio(_THIS)
 {
-	int written, p=0;
-
-	/* Write the audio data, checking for EAGAIN on broken audio drivers */
-	do {
-		written = write(audio_fd, &mixbuf[p], mixlen-p);
-		if (written>0)
-		   p += written;
-		if (written == -1 && errno != 0 && errno != EAGAIN && errno != EINTR)
-		{
-		   /* Non recoverable error has occurred. It should be reported!!! */
-		   perror("audio");
-		   break;
-		}
-
-		if ( p < written || ((written < 0) && ((errno == 0) || (errno == EAGAIN))) ) {
-			SDL_Delay(1);	/* Let a little CPU time go by */
-		}
-	} while ( p < written );
-
-	/* If timer synchronization is enabled, set the next write frame */
-	if ( frame_ticks ) {
-		next_frame += frame_ticks;
+	if (write(audio_fd, mixbuf, mixlen)==-1)
+	{
+		perror("Audio write");
+		this->enabled = 0;
 	}
 
-	/* If we couldn't write, assume fatal error for now */
-	if ( written < 0 ) {
-		this->enabled = 0;
-	}
 #ifdef DEBUG_AUDIO
-	fprintf(stderr, "Wrote %d bytes of audio data\n", written);
+	fprintf(stderr, "Wrote %d bytes of audio data\n", mixlen);
 #endif
 }
 
@@ -233,26 +160,128 @@
 		mixbuf = NULL;
 	}
 	if ( audio_fd >= 0 ) {
-		int value;
-		ioctl(audio_fd, SNDCTL_DSP_RESET, &value);
 		close(audio_fd);
 		audio_fd = -1;
 	}
 }
 
-static int DSP_ReopenAudio(_THIS, const char *audiodev, int format,
-						SDL_AudioSpec *spec)
+static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec)
 {
-	int frag_spec;
+	char audiodev[1024];
+	int format;
 	int value;
+	int frag_spec;
+	Uint16 test_format;
 
-	/* Close and then reopen the audio device */
-	close(audio_fd);
-	audio_fd = open(audiodev, O_WRONLY, 0);
+	/* Open the audio device */
+	audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
 	if ( audio_fd < 0 ) {
 		SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
 		return(-1);
 	}
+	mixbuf = NULL;
+
+	/* Make the file descriptor use blocking writes with fcntl() */
+	{ long flags;
+		flags = fcntl(audio_fd, F_GETFL);
+		flags &= ~O_NONBLOCK;
+		if ( fcntl(audio_fd, F_SETFL, flags) < 0 ) {
+			SDL_SetError("Couldn't set audio blocking mode");
+			return(-1);
+		}
+	}
+
+	/* Get a list of supported hardware formats */
+	if ( ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0 ) {
+		perror("SNDCTL_DSP_GETFMTS");
+		SDL_SetError("Couldn't get audio format list");
+		return(-1);
+	}
+
+	/* Try for a closest match on audio format */
+	format = 0;
+	for ( test_format = SDL_FirstAudioFormat(spec->format);
+						! format && test_format; ) {
+#ifdef DEBUG_AUDIO
+		fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
+#endif
+		switch ( test_format ) {
+			case AUDIO_U8:
+				if ( value & AFMT_U8 ) {
+					format = AFMT_U8;
+				}
+				break;
+			case AUDIO_S16LSB:
+				if ( value & AFMT_S16_LE ) {
+					format = AFMT_S16_LE;
+				}
+				break;
+			case AUDIO_S16MSB:
+				if ( value & AFMT_S16_BE ) {
+					format = AFMT_S16_BE;
+				}
+				break;
+#if 0
+/*
+ * These formats are not used by any real life systems so they are not 
+ * needed here.
+ */
+			case AUDIO_S8:
+				if ( value & AFMT_S8 ) {
+					format = AFMT_S8;
+				}
+				break;
+			case AUDIO_U16LSB:
+				if ( value & AFMT_U16_LE ) {
+					format = AFMT_U16_LE;
+				}
+				break;
+			case AUDIO_U16MSB:
+				if ( value & AFMT_U16_BE ) {
+					format = AFMT_U16_BE;
+				}
+				break;
+#endif
+			default:
+				format = 0;
+				break;
+		}
+		if ( ! format ) {
+			test_format = SDL_NextAudioFormat();
+		}
+	}
+	if ( format == 0 ) {
+		SDL_SetError("Couldn't find any hardware audio formats");
+		return(-1);
+	}
+	spec->format = test_format;
+
+	/* Set the audio format */
+	value = format;
+	if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
+						(value != format) ) {
+		perror("SNDCTL_DSP_SETFMT");
+		SDL_SetError("Couldn't set audio format");
+		return(-1);
+	}
+
+	/* Set the number of channels of output */
+	value = spec->channels;
+	if ( ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0 ) {
+		perror("SNDCTL_DSP_CHANNELS");
+		SDL_SetError("Cannot set the number of channels");
+		return(-1);
+	}
+	spec->channels = value;
+
+	/* Set the DSP frequency */
+	value = spec->freq;
+	if ( ioctl(audio_fd, SNDCTL_DSP_SPEED, &value) < 0 ) {
+		perror("SNDCTL_DSP_SPEED");
+		SDL_SetError("Couldn't set audio frequency");
+		return(-1);
+	}
+	spec->freq = value;
 
 	/* Calculate the final parameters for this audio specification */
 	SDL_CalculateAudioSpec(spec);
@@ -271,6 +300,7 @@
 		(frag_spec >> 16), 1<<(frag_spec&0xFFFF));
 #endif
 	if ( ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0 ) {
+		perror("SNDCTL_DSP_SETFRAGMENT");
 		fprintf(stderr, "Warning: Couldn't set audio fragment size\n");
 	}
 #ifdef DEBUG_AUDIO
@@ -283,160 +313,6 @@
 	}
 #endif
 
-	/* Set the audio format */
-	value = format;
-	if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
-						(value != format) ) {
-		SDL_SetError("Couldn't set audio format");
-		return(-1);
-	}
-
-	/* Set the number of channels of output */
-	value = spec->channels;
-#ifdef SNDCTL_DSP_CHANNELS
-	if ( ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0 ) {
-#endif
-		value = (spec->channels > 1);
-		ioctl(audio_fd, SNDCTL_DSP_STEREO, &value);
-		value = (value ? 2 : 1);
-#ifdef SNDCTL_DSP_CHANNELS
-	}
-#endif
-	if ( value != spec->channels ) {
-		SDL_SetError("Couldn't set audio channels");
-		return(-1);
-	}
-
-	/* Set the DSP frequency */
-	value = spec->freq;
-	if ( ioctl(audio_fd, SNDCTL_DSP_SPEED, &value) < 0 ) {
-		SDL_SetError("Couldn't set audio frequency");
-		return(-1);
-	}
-	spec->freq = value;
-
-	/* We successfully re-opened the audio */
-	return(0);
-}
-
-static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec)
-{
-	char audiodev[1024];
-	int format;
-	int value;
-	Uint16 test_format;
-
-	/* Reset the timer synchronization flag */
-	frame_ticks = 0.0;
-
-	/* Open the audio device */
-	audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
-	if ( audio_fd < 0 ) {
-		SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
-		return(-1);
-	}
-	mixbuf = NULL;
-
-#ifdef USE_BLOCKING_WRITES
-	/* Make the file descriptor use blocking writes with fcntl() */
-	{ long flags;
-		flags = fcntl(audio_fd, F_GETFL);
-		flags &= ~O_NONBLOCK;
-		if ( fcntl(audio_fd, F_SETFL, flags) < 0 ) {
-			SDL_SetError("Couldn't set audio blocking mode");
-			return(-1);
-		}
-	}
-#endif
-
-	/* Get a list of supported hardware formats */
-	if ( ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0 ) {
-		SDL_SetError("Couldn't get audio format list");
-		return(-1);
-	}
-
-	/* Try for a closest match on audio format */
-	format = 0;
-	for ( test_format = SDL_FirstAudioFormat(spec->format);
-						! format && test_format; ) {
-#ifdef DEBUG_AUDIO
-		fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
-#endif
-		switch ( test_format ) {
-			case AUDIO_U8:
-				if ( value & AFMT_U8 ) {
-					format = AFMT_U8;
-				}
-				break;
-			case AUDIO_S8:
-				if ( value & AFMT_S8 ) {
-					format = AFMT_S8;
-				}
-				break;
-			case AUDIO_S16LSB:
-				if ( value & AFMT_S16_LE ) {
-					format = AFMT_S16_LE;
-				}
-				break;
-			case AUDIO_S16MSB:
-				if ( value & AFMT_S16_BE ) {
-					format = AFMT_S16_BE;
-				}
-				break;
-			case AUDIO_U16LSB:
-				if ( value & AFMT_U16_LE ) {
-					format = AFMT_U16_LE;
-				}
-				break;
-			case AUDIO_U16MSB:
-				if ( value & AFMT_U16_BE ) {
-					format = AFMT_U16_BE;
-				}
-				break;
-			default:
-				format = 0;
-				break;
-		}
-		if ( ! format ) {
-			test_format = SDL_NextAudioFormat();
-		}
-	}
-	if ( format == 0 ) {
-		SDL_SetError("Couldn't find any hardware audio formats");
-		return(-1);
-	}
-	spec->format = test_format;
-
-	/* Set the audio format */
-	value = format;
-	if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
-						(value != format) ) {
-		SDL_SetError("Couldn't set audio format");
-		return(-1);
-	}
-
-	/* Set the number of channels of output */
-	value = spec->channels;
-#ifdef SNDCTL_DSP_CHANNELS
-	if ( ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0 ) {
-#endif
-		value = (spec->channels > 1);
-		ioctl(audio_fd, SNDCTL_DSP_STEREO, &value);
-		value = (value ? 2 : 1);
-#ifdef SNDCTL_DSP_CHANNELS
-	}
-#endif
-	spec->channels = value;
-
-	/* Because some drivers don't allow setting the buffer size
-	   after setting the format, we must re-open the audio device
-	   once we know what format and channels are supported
-	 */
-	if ( DSP_ReopenAudio(this, audiodev, format, spec) < 0 ) {
-		/* Error is set by DSP_ReopenAudio() */
-		return(-1);
-	}
-
 	/* Allocate mixing buffer */
 	mixlen = spec->size;
 	mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
@@ -445,17 +321,6 @@
 	}
 	memset(mixbuf, spec->silence, spec->size);
 
-#ifndef USE_BLOCKING_WRITES
-	/* Check to see if we need to use select() workaround */
-	{ char *workaround;
-		workaround = getenv("SDL_DSP_NOSELECT");
-		if ( workaround ) {
-			frame_ticks = (float)(spec->samples*1000)/spec->freq;
-			next_frame = SDL_GetTicks()+frame_ticks;
-		}
-	}
-#endif /* !USE_BLOCKING_WRITES */
-
 	/* Get the parent process id (we're the parent of the audio thread) */
 	parent = getpid();
 
--- a/src/audio/dsp/SDL_dspaudio.h	Fri Nov 12 21:29:52 2004 +0000
+++ b/src/audio/dsp/SDL_dspaudio.h	Fri Nov 12 21:39:04 2004 +0000
@@ -43,10 +43,6 @@
 	/* Raw mixing buffer */
 	Uint8 *mixbuf;
 	int    mixlen;
-
-	/* Support for audio timing using a timer, in addition to select() */
-	float frame_ticks;
-	float next_frame;
 };
 #define FUDGE_TICKS	10	/* The scheduler overhead ticks per frame */