Date: Mon, 11 Oct 2004 15:17:27 +0300 (EEST)
authorSam Lantinga
Fri, 12 Nov 2004 21:39:04 +0000
changeset 9684675910b0b7b
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
     1.1 --- a/src/audio/dsp/SDL_dspaudio.c	Fri Nov 12 21:29:52 2004 +0000
     1.2 +++ b/src/audio/dsp/SDL_dspaudio.c	Fri Nov 12 21:39:04 2004 +0000
     1.3 @@ -18,6 +18,9 @@
     1.4  
     1.5      Sam Lantinga
     1.6      slouken@libsdl.org
     1.7 +
     1.8 +    Modified in Oct 2004 by Hannu Savolainen 
     1.9 +    hannu@opensound.com
    1.10  */
    1.11  
    1.12  #ifdef SAVE_RCSID
    1.13 @@ -57,7 +60,6 @@
    1.14  #define DSP_DRIVER_NAME         "dsp"
    1.15  
    1.16  /* Open the audio device for playback, and don't block if busy */
    1.17 -/*#define USE_BLOCKING_WRITES*/
    1.18  #define OPEN_FLAGS	(O_WRONLY|O_NONBLOCK)
    1.19  
    1.20  /* Audio driver functions */
    1.21 @@ -130,94 +132,19 @@
    1.22  /* This function waits until it is possible to write a full sound buffer */
    1.23  static void DSP_WaitAudio(_THIS)
    1.24  {
    1.25 -	/* Check to see if the thread-parent process is still alive */
    1.26 -	{ static int cnt = 0;
    1.27 -		/* Note that this only works with thread implementations 
    1.28 -		   that use a different process id for each thread.
    1.29 -		*/
    1.30 -		if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */
    1.31 -			if ( kill(parent, 0) < 0 ) {
    1.32 -				this->enabled = 0;
    1.33 -			}
    1.34 -		}
    1.35 -	}
    1.36 -
    1.37 -#ifndef USE_BLOCKING_WRITES /* Not necessary when using blocking writes */
    1.38 -	/* See if we need to use timed audio synchronization */
    1.39 -	if ( frame_ticks ) {
    1.40 -		/* Use timer for general audio synchronization */
    1.41 -		Sint32 ticks;
    1.42 -
    1.43 -		ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS;
    1.44 -		if ( ticks > 0 ) {
    1.45 -			SDL_Delay(ticks);
    1.46 -		}
    1.47 -	} else {
    1.48 -		/* Use select() for audio synchronization */
    1.49 -		fd_set fdset;
    1.50 -		struct timeval timeout;
    1.51 -
    1.52 -		FD_ZERO(&fdset);
    1.53 -		FD_SET(audio_fd, &fdset);
    1.54 -		timeout.tv_sec = 10;
    1.55 -		timeout.tv_usec = 0;
    1.56 -#ifdef DEBUG_AUDIO
    1.57 -		fprintf(stderr, "Waiting for audio to get ready\n");
    1.58 -#endif
    1.59 -		if ( select(audio_fd+1, NULL, &fdset, NULL, &timeout) <= 0 ) {
    1.60 -			const char *message =
    1.61 -			"Audio timeout - buggy audio driver? (disabled)";
    1.62 -			/* In general we should never print to the screen,
    1.63 -			   but in this case we have no other way of letting
    1.64 -			   the user know what happened.
    1.65 -			*/
    1.66 -			fprintf(stderr, "SDL: %s\n", message);
    1.67 -			this->enabled = 0;
    1.68 -			/* Don't try to close - may hang */
    1.69 -			audio_fd = -1;
    1.70 -#ifdef DEBUG_AUDIO
    1.71 -			fprintf(stderr, "Done disabling audio\n");
    1.72 -#endif
    1.73 -		}
    1.74 -#ifdef DEBUG_AUDIO
    1.75 -		fprintf(stderr, "Ready!\n");
    1.76 -#endif
    1.77 -	}
    1.78 -#endif /* !USE_BLOCKING_WRITES */
    1.79 +	/* Not needed at all since OSS handles waiting automagically */
    1.80  }
    1.81  
    1.82  static void DSP_PlayAudio(_THIS)
    1.83  {
    1.84 -	int written, p=0;
    1.85 -
    1.86 -	/* Write the audio data, checking for EAGAIN on broken audio drivers */
    1.87 -	do {
    1.88 -		written = write(audio_fd, &mixbuf[p], mixlen-p);
    1.89 -		if (written>0)
    1.90 -		   p += written;
    1.91 -		if (written == -1 && errno != 0 && errno != EAGAIN && errno != EINTR)
    1.92 -		{
    1.93 -		   /* Non recoverable error has occurred. It should be reported!!! */
    1.94 -		   perror("audio");
    1.95 -		   break;
    1.96 -		}
    1.97 -
    1.98 -		if ( p < written || ((written < 0) && ((errno == 0) || (errno == EAGAIN))) ) {
    1.99 -			SDL_Delay(1);	/* Let a little CPU time go by */
   1.100 -		}
   1.101 -	} while ( p < written );
   1.102 -
   1.103 -	/* If timer synchronization is enabled, set the next write frame */
   1.104 -	if ( frame_ticks ) {
   1.105 -		next_frame += frame_ticks;
   1.106 +	if (write(audio_fd, mixbuf, mixlen)==-1)
   1.107 +	{
   1.108 +		perror("Audio write");
   1.109 +		this->enabled = 0;
   1.110  	}
   1.111  
   1.112 -	/* If we couldn't write, assume fatal error for now */
   1.113 -	if ( written < 0 ) {
   1.114 -		this->enabled = 0;
   1.115 -	}
   1.116  #ifdef DEBUG_AUDIO
   1.117 -	fprintf(stderr, "Wrote %d bytes of audio data\n", written);
   1.118 +	fprintf(stderr, "Wrote %d bytes of audio data\n", mixlen);
   1.119  #endif
   1.120  }
   1.121  
   1.122 @@ -233,26 +160,128 @@
   1.123  		mixbuf = NULL;
   1.124  	}
   1.125  	if ( audio_fd >= 0 ) {
   1.126 -		int value;
   1.127 -		ioctl(audio_fd, SNDCTL_DSP_RESET, &value);
   1.128  		close(audio_fd);
   1.129  		audio_fd = -1;
   1.130  	}
   1.131  }
   1.132  
   1.133 -static int DSP_ReopenAudio(_THIS, const char *audiodev, int format,
   1.134 -						SDL_AudioSpec *spec)
   1.135 +static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec)
   1.136  {
   1.137 -	int frag_spec;
   1.138 +	char audiodev[1024];
   1.139 +	int format;
   1.140  	int value;
   1.141 +	int frag_spec;
   1.142 +	Uint16 test_format;
   1.143  
   1.144 -	/* Close and then reopen the audio device */
   1.145 -	close(audio_fd);
   1.146 -	audio_fd = open(audiodev, O_WRONLY, 0);
   1.147 +	/* Open the audio device */
   1.148 +	audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
   1.149  	if ( audio_fd < 0 ) {
   1.150  		SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
   1.151  		return(-1);
   1.152  	}
   1.153 +	mixbuf = NULL;
   1.154 +
   1.155 +	/* Make the file descriptor use blocking writes with fcntl() */
   1.156 +	{ long flags;
   1.157 +		flags = fcntl(audio_fd, F_GETFL);
   1.158 +		flags &= ~O_NONBLOCK;
   1.159 +		if ( fcntl(audio_fd, F_SETFL, flags) < 0 ) {
   1.160 +			SDL_SetError("Couldn't set audio blocking mode");
   1.161 +			return(-1);
   1.162 +		}
   1.163 +	}
   1.164 +
   1.165 +	/* Get a list of supported hardware formats */
   1.166 +	if ( ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0 ) {
   1.167 +		perror("SNDCTL_DSP_GETFMTS");
   1.168 +		SDL_SetError("Couldn't get audio format list");
   1.169 +		return(-1);
   1.170 +	}
   1.171 +
   1.172 +	/* Try for a closest match on audio format */
   1.173 +	format = 0;
   1.174 +	for ( test_format = SDL_FirstAudioFormat(spec->format);
   1.175 +						! format && test_format; ) {
   1.176 +#ifdef DEBUG_AUDIO
   1.177 +		fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
   1.178 +#endif
   1.179 +		switch ( test_format ) {
   1.180 +			case AUDIO_U8:
   1.181 +				if ( value & AFMT_U8 ) {
   1.182 +					format = AFMT_U8;
   1.183 +				}
   1.184 +				break;
   1.185 +			case AUDIO_S16LSB:
   1.186 +				if ( value & AFMT_S16_LE ) {
   1.187 +					format = AFMT_S16_LE;
   1.188 +				}
   1.189 +				break;
   1.190 +			case AUDIO_S16MSB:
   1.191 +				if ( value & AFMT_S16_BE ) {
   1.192 +					format = AFMT_S16_BE;
   1.193 +				}
   1.194 +				break;
   1.195 +#if 0
   1.196 +/*
   1.197 + * These formats are not used by any real life systems so they are not 
   1.198 + * needed here.
   1.199 + */
   1.200 +			case AUDIO_S8:
   1.201 +				if ( value & AFMT_S8 ) {
   1.202 +					format = AFMT_S8;
   1.203 +				}
   1.204 +				break;
   1.205 +			case AUDIO_U16LSB:
   1.206 +				if ( value & AFMT_U16_LE ) {
   1.207 +					format = AFMT_U16_LE;
   1.208 +				}
   1.209 +				break;
   1.210 +			case AUDIO_U16MSB:
   1.211 +				if ( value & AFMT_U16_BE ) {
   1.212 +					format = AFMT_U16_BE;
   1.213 +				}
   1.214 +				break;
   1.215 +#endif
   1.216 +			default:
   1.217 +				format = 0;
   1.218 +				break;
   1.219 +		}
   1.220 +		if ( ! format ) {
   1.221 +			test_format = SDL_NextAudioFormat();
   1.222 +		}
   1.223 +	}
   1.224 +	if ( format == 0 ) {
   1.225 +		SDL_SetError("Couldn't find any hardware audio formats");
   1.226 +		return(-1);
   1.227 +	}
   1.228 +	spec->format = test_format;
   1.229 +
   1.230 +	/* Set the audio format */
   1.231 +	value = format;
   1.232 +	if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
   1.233 +						(value != format) ) {
   1.234 +		perror("SNDCTL_DSP_SETFMT");
   1.235 +		SDL_SetError("Couldn't set audio format");
   1.236 +		return(-1);
   1.237 +	}
   1.238 +
   1.239 +	/* Set the number of channels of output */
   1.240 +	value = spec->channels;
   1.241 +	if ( ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0 ) {
   1.242 +		perror("SNDCTL_DSP_CHANNELS");
   1.243 +		SDL_SetError("Cannot set the number of channels");
   1.244 +		return(-1);
   1.245 +	}
   1.246 +	spec->channels = value;
   1.247 +
   1.248 +	/* Set the DSP frequency */
   1.249 +	value = spec->freq;
   1.250 +	if ( ioctl(audio_fd, SNDCTL_DSP_SPEED, &value) < 0 ) {
   1.251 +		perror("SNDCTL_DSP_SPEED");
   1.252 +		SDL_SetError("Couldn't set audio frequency");
   1.253 +		return(-1);
   1.254 +	}
   1.255 +	spec->freq = value;
   1.256  
   1.257  	/* Calculate the final parameters for this audio specification */
   1.258  	SDL_CalculateAudioSpec(spec);
   1.259 @@ -271,6 +300,7 @@
   1.260  		(frag_spec >> 16), 1<<(frag_spec&0xFFFF));
   1.261  #endif
   1.262  	if ( ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0 ) {
   1.263 +		perror("SNDCTL_DSP_SETFRAGMENT");
   1.264  		fprintf(stderr, "Warning: Couldn't set audio fragment size\n");
   1.265  	}
   1.266  #ifdef DEBUG_AUDIO
   1.267 @@ -283,160 +313,6 @@
   1.268  	}
   1.269  #endif
   1.270  
   1.271 -	/* Set the audio format */
   1.272 -	value = format;
   1.273 -	if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
   1.274 -						(value != format) ) {
   1.275 -		SDL_SetError("Couldn't set audio format");
   1.276 -		return(-1);
   1.277 -	}
   1.278 -
   1.279 -	/* Set the number of channels of output */
   1.280 -	value = spec->channels;
   1.281 -#ifdef SNDCTL_DSP_CHANNELS
   1.282 -	if ( ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0 ) {
   1.283 -#endif
   1.284 -		value = (spec->channels > 1);
   1.285 -		ioctl(audio_fd, SNDCTL_DSP_STEREO, &value);
   1.286 -		value = (value ? 2 : 1);
   1.287 -#ifdef SNDCTL_DSP_CHANNELS
   1.288 -	}
   1.289 -#endif
   1.290 -	if ( value != spec->channels ) {
   1.291 -		SDL_SetError("Couldn't set audio channels");
   1.292 -		return(-1);
   1.293 -	}
   1.294 -
   1.295 -	/* Set the DSP frequency */
   1.296 -	value = spec->freq;
   1.297 -	if ( ioctl(audio_fd, SNDCTL_DSP_SPEED, &value) < 0 ) {
   1.298 -		SDL_SetError("Couldn't set audio frequency");
   1.299 -		return(-1);
   1.300 -	}
   1.301 -	spec->freq = value;
   1.302 -
   1.303 -	/* We successfully re-opened the audio */
   1.304 -	return(0);
   1.305 -}
   1.306 -
   1.307 -static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec)
   1.308 -{
   1.309 -	char audiodev[1024];
   1.310 -	int format;
   1.311 -	int value;
   1.312 -	Uint16 test_format;
   1.313 -
   1.314 -	/* Reset the timer synchronization flag */
   1.315 -	frame_ticks = 0.0;
   1.316 -
   1.317 -	/* Open the audio device */
   1.318 -	audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
   1.319 -	if ( audio_fd < 0 ) {
   1.320 -		SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
   1.321 -		return(-1);
   1.322 -	}
   1.323 -	mixbuf = NULL;
   1.324 -
   1.325 -#ifdef USE_BLOCKING_WRITES
   1.326 -	/* Make the file descriptor use blocking writes with fcntl() */
   1.327 -	{ long flags;
   1.328 -		flags = fcntl(audio_fd, F_GETFL);
   1.329 -		flags &= ~O_NONBLOCK;
   1.330 -		if ( fcntl(audio_fd, F_SETFL, flags) < 0 ) {
   1.331 -			SDL_SetError("Couldn't set audio blocking mode");
   1.332 -			return(-1);
   1.333 -		}
   1.334 -	}
   1.335 -#endif
   1.336 -
   1.337 -	/* Get a list of supported hardware formats */
   1.338 -	if ( ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0 ) {
   1.339 -		SDL_SetError("Couldn't get audio format list");
   1.340 -		return(-1);
   1.341 -	}
   1.342 -
   1.343 -	/* Try for a closest match on audio format */
   1.344 -	format = 0;
   1.345 -	for ( test_format = SDL_FirstAudioFormat(spec->format);
   1.346 -						! format && test_format; ) {
   1.347 -#ifdef DEBUG_AUDIO
   1.348 -		fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
   1.349 -#endif
   1.350 -		switch ( test_format ) {
   1.351 -			case AUDIO_U8:
   1.352 -				if ( value & AFMT_U8 ) {
   1.353 -					format = AFMT_U8;
   1.354 -				}
   1.355 -				break;
   1.356 -			case AUDIO_S8:
   1.357 -				if ( value & AFMT_S8 ) {
   1.358 -					format = AFMT_S8;
   1.359 -				}
   1.360 -				break;
   1.361 -			case AUDIO_S16LSB:
   1.362 -				if ( value & AFMT_S16_LE ) {
   1.363 -					format = AFMT_S16_LE;
   1.364 -				}
   1.365 -				break;
   1.366 -			case AUDIO_S16MSB:
   1.367 -				if ( value & AFMT_S16_BE ) {
   1.368 -					format = AFMT_S16_BE;
   1.369 -				}
   1.370 -				break;
   1.371 -			case AUDIO_U16LSB:
   1.372 -				if ( value & AFMT_U16_LE ) {
   1.373 -					format = AFMT_U16_LE;
   1.374 -				}
   1.375 -				break;
   1.376 -			case AUDIO_U16MSB:
   1.377 -				if ( value & AFMT_U16_BE ) {
   1.378 -					format = AFMT_U16_BE;
   1.379 -				}
   1.380 -				break;
   1.381 -			default:
   1.382 -				format = 0;
   1.383 -				break;
   1.384 -		}
   1.385 -		if ( ! format ) {
   1.386 -			test_format = SDL_NextAudioFormat();
   1.387 -		}
   1.388 -	}
   1.389 -	if ( format == 0 ) {
   1.390 -		SDL_SetError("Couldn't find any hardware audio formats");
   1.391 -		return(-1);
   1.392 -	}
   1.393 -	spec->format = test_format;
   1.394 -
   1.395 -	/* Set the audio format */
   1.396 -	value = format;
   1.397 -	if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
   1.398 -						(value != format) ) {
   1.399 -		SDL_SetError("Couldn't set audio format");
   1.400 -		return(-1);
   1.401 -	}
   1.402 -
   1.403 -	/* Set the number of channels of output */
   1.404 -	value = spec->channels;
   1.405 -#ifdef SNDCTL_DSP_CHANNELS
   1.406 -	if ( ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0 ) {
   1.407 -#endif
   1.408 -		value = (spec->channels > 1);
   1.409 -		ioctl(audio_fd, SNDCTL_DSP_STEREO, &value);
   1.410 -		value = (value ? 2 : 1);
   1.411 -#ifdef SNDCTL_DSP_CHANNELS
   1.412 -	}
   1.413 -#endif
   1.414 -	spec->channels = value;
   1.415 -
   1.416 -	/* Because some drivers don't allow setting the buffer size
   1.417 -	   after setting the format, we must re-open the audio device
   1.418 -	   once we know what format and channels are supported
   1.419 -	 */
   1.420 -	if ( DSP_ReopenAudio(this, audiodev, format, spec) < 0 ) {
   1.421 -		/* Error is set by DSP_ReopenAudio() */
   1.422 -		return(-1);
   1.423 -	}
   1.424 -
   1.425  	/* Allocate mixing buffer */
   1.426  	mixlen = spec->size;
   1.427  	mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
   1.428 @@ -445,17 +321,6 @@
   1.429  	}
   1.430  	memset(mixbuf, spec->silence, spec->size);
   1.431  
   1.432 -#ifndef USE_BLOCKING_WRITES
   1.433 -	/* Check to see if we need to use select() workaround */
   1.434 -	{ char *workaround;
   1.435 -		workaround = getenv("SDL_DSP_NOSELECT");
   1.436 -		if ( workaround ) {
   1.437 -			frame_ticks = (float)(spec->samples*1000)/spec->freq;
   1.438 -			next_frame = SDL_GetTicks()+frame_ticks;
   1.439 -		}
   1.440 -	}
   1.441 -#endif /* !USE_BLOCKING_WRITES */
   1.442 -
   1.443  	/* Get the parent process id (we're the parent of the audio thread) */
   1.444  	parent = getpid();
   1.445  
     2.1 --- a/src/audio/dsp/SDL_dspaudio.h	Fri Nov 12 21:29:52 2004 +0000
     2.2 +++ b/src/audio/dsp/SDL_dspaudio.h	Fri Nov 12 21:39:04 2004 +0000
     2.3 @@ -43,10 +43,6 @@
     2.4  	/* Raw mixing buffer */
     2.5  	Uint8 *mixbuf;
     2.6  	int    mixlen;
     2.7 -
     2.8 -	/* Support for audio timing using a timer, in addition to select() */
     2.9 -	float frame_ticks;
    2.10 -	float next_frame;
    2.11  };
    2.12  #define FUDGE_TICKS	10	/* The scheduler overhead ticks per frame */
    2.13