Skip to content
Permalink
Browse files

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.
  • Loading branch information
slouken committed Nov 12, 2004
1 parent 33db353 commit 16a601bbbfed1dc2a14dcb1ab635a09659eef93e
Showing with 62 additions and 201 deletions.
  1. +62 −197 src/audio/dsp/SDL_dspaudio.c
  2. +0 −4 src/audio/dsp/SDL_dspaudio.h
@@ -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 @@ static char rcsid =
#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 @@ AudioBootStrap DSP_bootstrap = {
/* 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 we couldn't write, assume fatal error for now */
if ( written < 0 ) {
if (write(audio_fd, mixbuf, mixlen)==-1)
{
perror("Audio write");
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,102 +160,19 @@ static void DSP_CloseAudio(_THIS)
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)
{
int frag_spec;
int value;

/* Close and then reopen the audio device */
close(audio_fd);
audio_fd = open(audiodev, O_WRONLY, 0);
if ( audio_fd < 0 ) {
SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
return(-1);
}

/* Calculate the final parameters for this audio specification */
SDL_CalculateAudioSpec(spec);

/* Determine the power of two of the fragment size */
for ( frag_spec = 0; (0x01<<frag_spec) < spec->size; ++frag_spec );
if ( (0x01<<frag_spec) != spec->size ) {
SDL_SetError("Fragment size must be a power of two");
return(-1);
}
frag_spec |= 0x00020000; /* two fragments, for low latency */

/* Set the audio buffering parameters */
#ifdef DEBUG_AUDIO
fprintf(stderr, "Requesting %d fragments of size %d\n",
(frag_spec >> 16), 1<<(frag_spec&0xFFFF));
#endif
if ( ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0 ) {
fprintf(stderr, "Warning: Couldn't set audio fragment size\n");
}
#ifdef DEBUG_AUDIO
{ audio_buf_info info;
ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info);
fprintf(stderr, "fragments = %d\n", info.fragments);
fprintf(stderr, "fragstotal = %d\n", info.fragstotal);
fprintf(stderr, "fragsize = %d\n", info.fragsize);
fprintf(stderr, "bytes = %d\n", info.bytes);
}
#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;
int frag_spec;
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 ) {
@@ -337,7 +181,6 @@ static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec)
}
mixbuf = NULL;

#ifdef USE_BLOCKING_WRITES
/* Make the file descriptor use blocking writes with fcntl() */
{ long flags;
flags = fcntl(audio_fd, F_GETFL);
@@ -347,10 +190,10 @@ static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec)
return(-1);
}
}
#endif

/* 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);
}
@@ -368,11 +211,6 @@ static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec)
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;
@@ -383,6 +221,16 @@ static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec)
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;
@@ -393,6 +241,7 @@ static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec)
format = AFMT_U16_BE;
}
break;
#endif
default:
format = 0;
break;
@@ -411,31 +260,58 @@ static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec)
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;
#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
perror("SNDCTL_DSP_CHANNELS");
SDL_SetError("Cannot set the number of channels");
return(-1);
}
#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() */
/* 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);

/* Determine the power of two of the fragment size */
for ( frag_spec = 0; (0x01<<frag_spec) < spec->size; ++frag_spec );
if ( (0x01<<frag_spec) != spec->size ) {
SDL_SetError("Fragment size must be a power of two");
return(-1);
}
frag_spec |= 0x00020000; /* two fragments, for low latency */

/* Set the audio buffering parameters */
#ifdef DEBUG_AUDIO
fprintf(stderr, "Requesting %d fragments of size %d\n",
(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
{ audio_buf_info info;
ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info);
fprintf(stderr, "fragments = %d\n", info.fragments);
fprintf(stderr, "fragstotal = %d\n", info.fragstotal);
fprintf(stderr, "fragsize = %d\n", info.fragsize);
fprintf(stderr, "bytes = %d\n", info.bytes);
}
#endif

/* Allocate mixing buffer */
mixlen = spec->size;
@@ -445,17 +321,6 @@ static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec)
}
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();

@@ -43,10 +43,6 @@ struct SDL_PrivateAudioData {
/* 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 */

0 comments on commit 16a601b

Please sign in to comment.