--- a/src/audio/alsa/SDL_alsa_audio.c Mon Apr 15 04:53:41 2002 +0000
+++ b/src/audio/alsa/SDL_alsa_audio.c Mon Apr 15 07:38:54 2002 +0000
@@ -44,96 +44,41 @@
/* The tag name used by ALSA audio */
#define DRIVER_NAME "alsa"
-/* default card and device numbers as listed in dev/snd */
-static int card_no = 0;
-static int device_no = 0;
-
-/* default channel communication parameters */
-#define DEFAULT_CPARAMS_RATE 22050
-#define DEFAULT_CPARAMS_VOICES 1
-#define DEFAULT_CPARAMS_FRAG_SIZE 512
-#define DEFAULT_CPARAMS_FRAGS_MIN 1
-#define DEFAULT_CPARAMS_FRAGS_MAX -1
-
-/* Open the audio device for playback, and don't block if busy */
-#define OPEN_FLAGS (SND_PCM_OPEN_PLAYBACK|SND_PCM_OPEN_NONBLOCK)
+/* The default ALSA audio driver */
+#define DEFAULT_DEVICE "plughw:0,0"
/* Audio driver functions */
-static int PCM_OpenAudio(_THIS, SDL_AudioSpec *spec);
-static void PCM_WaitAudio(_THIS);
-static void PCM_PlayAudio(_THIS);
-static Uint8 *PCM_GetAudioBuf(_THIS);
-static void PCM_CloseAudio(_THIS);
-
-/* PCM transfer channel parameters initialize function */
-static void init_pcm_cparams(snd_pcm_channel_params_t* cparams)
-{
- memset(cparams,0,sizeof(snd_pcm_channel_params_t));
+static int ALSA_OpenAudio(_THIS, SDL_AudioSpec *spec);
+static void ALSA_WaitAudio(_THIS);
+static void ALSA_PlayAudio(_THIS);
+static Uint8 *ALSA_GetAudioBuf(_THIS);
+static void ALSA_CloseAudio(_THIS);
- cparams->channel = SND_PCM_CHANNEL_PLAYBACK;
- cparams->mode = SND_PCM_MODE_BLOCK;
- cparams->start_mode = SND_PCM_START_DATA; //_FULL
- cparams->stop_mode = SND_PCM_STOP_STOP;
- cparams->format.format = SND_PCM_SFMT_S16_LE;
- cparams->format.interleave = 1;
- cparams->format.rate = DEFAULT_CPARAMS_RATE;
- cparams->format.voices = DEFAULT_CPARAMS_VOICES;
- cparams->buf.block.frag_size = DEFAULT_CPARAMS_FRAG_SIZE;
- cparams->buf.block.frags_min = DEFAULT_CPARAMS_FRAGS_MIN;
- cparams->buf.block.frags_max = DEFAULT_CPARAMS_FRAGS_MAX;
+static const char *get_audio_device()
+{
+ const char *device;
+
+ device = getenv("AUDIODEV"); /* Is there a standard variable name? */
+ if ( device == NULL ) {
+ device = DEFAULT_DEVICE;
+ }
+ return device;
}
/* Audio driver bootstrap functions */
static int Audio_Available(void)
-/*
- See if we can open a nonblocking channel.
- Return value '1' means we can.
- Return value '0' means we cannot.
-*/
{
int available;
- int rval;
+ int status;
snd_pcm_t *handle;
- snd_pcm_channel_params_t cparams;
-#ifdef DEBUG_AUDIO
- snd_pcm_channel_status_t cstatus;
-#endif
available = 0;
- handle = NULL;
-
- init_pcm_cparams(&cparams);
-
- rval = snd_pcm_open(&handle, card_no, device_no, OPEN_FLAGS);
- if (rval >= 0)
- {
- rval = snd_pcm_plugin_params(handle, &cparams);
-
-#ifdef DEBUG_AUDIO
- snd_pcm_plugin_status(handle, &cstatus);
- printf("status after snd_pcm_plugin_params call = %d\n",cstatus.status);
-#endif
- if (rval >= 0)
- {
- available = 1;
- }
- else
- {
- SDL_SetError("snd_pcm_channel_params failed: %s\n", snd_strerror (rval));
- }
-
- if ((rval = snd_pcm_close(handle)) < 0)
- {
- SDL_SetError("snd_pcm_close failed: %s\n",snd_strerror(rval));
- available = 0;
- }
+ status = snd_pcm_open(&handle, get_audio_device(), SND_PCM_STREAM_PLAYBACK, 0);
+ if ( status >= 0 ) {
+ available = 1;
+ snd_pcm_close(handle);
}
- else
- {
- SDL_SetError("snd_pcm_open failed: %s\n", snd_strerror(rval));
- }
-
return(available);
}
@@ -162,14 +107,13 @@
return(0);
}
memset(this->hidden, 0, (sizeof *this->hidden));
- audio_handle = NULL;
/* Set the function pointers */
- this->OpenAudio = PCM_OpenAudio;
- this->WaitAudio = PCM_WaitAudio;
- this->PlayAudio = PCM_PlayAudio;
- this->GetAudioBuf = PCM_GetAudioBuf;
- this->CloseAudio = PCM_CloseAudio;
+ this->OpenAudio = ALSA_OpenAudio;
+ this->WaitAudio = ALSA_WaitAudio;
+ this->PlayAudio = ALSA_PlayAudio;
+ this->GetAudioBuf = ALSA_GetAudioBuf;
+ this->CloseAudio = ALSA_CloseAudio;
this->free = Audio_DeleteDevice;
@@ -177,14 +121,13 @@
}
AudioBootStrap ALSA_bootstrap = {
- DRIVER_NAME, "ALSA PCM audio",
+ DRIVER_NAME, "ALSA 0.9 PCM audio",
Audio_Available, Audio_CreateDevice
};
/* This function waits until it is possible to write a full sound buffer */
-static void PCM_WaitAudio(_THIS)
+static void ALSA_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
@@ -196,322 +139,177 @@
}
}
}
-
- /* 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
- }
}
-static snd_pcm_channel_status_t cstatus;
-
-static void PCM_PlayAudio(_THIS)
+static void ALSA_PlayAudio(_THIS)
{
- int written, rval;
-
- /* Write the audio data, checking for EAGAIN (buffer full) and underrun */
- do {
- written = snd_pcm_plugin_write(audio_handle, pcm_buf, pcm_len);
-#ifdef DEBUG_AUDIO
- fprintf(stderr, "written = %d pcm_len = %d\n",written,pcm_len);
-#endif
- if (written != pcm_len)
- {
- if (errno == EAGAIN)
- {
- SDL_Delay(1); /* Let a little CPU time go by and try to write again */
-#ifdef DEBUG_AUDIO
- fprintf(stderr, "errno == EAGAIN\n");
-#endif
- }
- else
- {
- if( (rval = snd_pcm_plugin_status(audio_handle, &cstatus)) < 0 )
- {
- SDL_SetError("snd_pcm_plugin_status failed: %s\n", snd_strerror(rval));
- return;
- }
- if ( (cstatus.status == SND_PCM_STATUS_UNDERRUN)
- ||(cstatus.status == SND_PCM_STATUS_READY) )
- {
-#ifdef DEBUG_AUDIO
- fprintf(stderr, "buffer underrun\n");
-#endif
- if ( (rval = snd_pcm_plugin_prepare (audio_handle,SND_PCM_CHANNEL_PLAYBACK)) < 0 )
- {
- SDL_SetError("snd_pcm_plugin_prepare failed: %s\n",snd_strerror(rval) );
- return;
- }
- /* if we reach here, try to write again */
- }
+ int status;
+ int sample_len;
+ signed short *sample_buf;
+
+ sample_len = this->spec.samples;
+ sample_buf = (signed short *)mixbuf;
+ while ( sample_len > 0 ) {
+ status = snd_pcm_writei(pcm_handle, sample_buf, sample_len);
+ if ( status < 0 ) {
+ if ( status == -EAGAIN ) {
+ continue;
+ }
+ if ( status == -ESTRPIPE ) {
+ do {
+ status = snd_pcm_resume(pcm_handle);
+ } while ( status == -EAGAIN );
+ }
+ if ( status < 0 ) {
+ status = snd_pcm_prepare(pcm_handle);
+ }
+ if ( status < 0 ) {
+ /* Hmm, not much we can do - abort */
+ this->enabled = 0;
+ return;
}
}
- } while ( (written < 0) && ((errno == 0) || (errno == EAGAIN)) );
-
- /* Set the next write frame */
- if ( frame_ticks ) {
- next_frame += frame_ticks;
+ sample_buf += status * this->spec.channels;
+ sample_len -= status;
}
-
- /* If we couldn't write, assume fatal error for now */
- if ( written < 0 ) {
- this->enabled = 0;
- }
- return;
}
-static Uint8 *PCM_GetAudioBuf(_THIS)
+static Uint8 *ALSA_GetAudioBuf(_THIS)
{
- return(pcm_buf);
+ return(mixbuf);
}
-static void PCM_CloseAudio(_THIS)
+static void ALSA_CloseAudio(_THIS)
{
- int rval;
-
- if ( pcm_buf != NULL ) {
- free(pcm_buf);
- pcm_buf = NULL;
+ if ( mixbuf != NULL ) {
+ SDL_FreeAudioMem(mixbuf);
+ mixbuf = NULL;
}
- if ( audio_handle != NULL ) {
- if ((rval = snd_pcm_plugin_flush(audio_handle,SND_PCM_CHANNEL_PLAYBACK)) < 0)
- {
- SDL_SetError("snd_pcm_plugin_flush failed: %s\n",snd_strerror(rval));
- return;
- }
- if ((rval = snd_pcm_close(audio_handle)) < 0)
- {
- SDL_SetError("snd_pcm_close failed: %s\n",snd_strerror(rval));
- return;
- }
- audio_handle = NULL;
+ if ( pcm_handle ) {
+ snd_pcm_close(pcm_handle);
+ pcm_handle = NULL;
}
}
-static int PCM_OpenAudio(_THIS, SDL_AudioSpec *spec)
+static int ALSA_OpenAudio(_THIS, SDL_AudioSpec *spec)
{
- int rval;
- snd_pcm_channel_params_t cparams;
- snd_pcm_channel_setup_t csetup;
- int format;
- Uint16 test_format;
- int twidth;
-
- /* initialize channel transfer parameters to default */
- init_pcm_cparams(&cparams);
-
- /* Reset the timer synchronization flag */
- frame_ticks = 0.0;
+ int status;
+ snd_pcm_hw_params_t *params;
+ snd_pcm_format_t format;
+ snd_pcm_uframes_t frames;
+ Uint16 test_format;
/* Open the audio device */
-
- rval = snd_pcm_open(&audio_handle, card_no, device_no, OPEN_FLAGS);
- if ( rval < 0 ) {
- SDL_SetError("snd_pcm_open failed: %s\n", snd_strerror(rval));
+ status = snd_pcm_open(&pcm_handle, get_audio_device(), SND_PCM_STREAM_PLAYBACK, 0);
+ if ( status < 0 ) {
+ SDL_SetError("Couldn't open audio device: %s", snd_strerror(status));
+ return(-1);
+ }
+
+ /* Figure out what the hardware is capable of */
+ snd_pcm_hw_params_alloca(¶ms);
+ status = snd_pcm_hw_params_any(pcm_handle, params);
+ if ( status < 0 ) {
+ SDL_SetError("Couldn't get hardware config: %s", snd_strerror(status));
+ ALSA_CloseAudio(this);
+ return(-1);
+ }
+
+ /* SDL only uses interleaved sample output */
+ status = snd_pcm_hw_params_set_access(pcm_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
+ if ( status < 0 ) {
+ SDL_SetError("Couldn't set interleaved access: %s", snd_strerror(status));
+ ALSA_CloseAudio(this);
return(-1);
}
-#ifdef PLUGIN_DISABLE_MMAP /* This is gone in newer versions of ALSA? */
- /* disable count status parameter */
- if ((rval = snd_plugin_set_disable(audio_handle, PLUGIN_DISABLE_MMAP))<0)
- {
- SDL_SetError("snd_plugin_set_disable failed: %s\n", snd_strerror(rval));
- return(-1);
- }
-#endif
-
- pcm_buf = NULL;
-
/* Try for a closest match on audio format */
- format = 0;
+ status = -1;
for ( test_format = SDL_FirstAudioFormat(spec->format);
- ! format && test_format; )
- {
-#ifdef DEBUG_AUDIO
- fprintf(stderr, "Trying format 0x%4.4x spec->samples %d\n", test_format,spec->samples);
-#endif
- /* if match found set format to equivalent ALSA format */
- switch ( test_format ) {
+ test_format && (status < 0); ) {
+ switch ( test_format ) {
case AUDIO_U8:
- format = SND_PCM_SFMT_U8;
- cparams.buf.block.frag_size = spec->samples * spec->channels;
+ format = SND_PCM_FORMAT_U8;
break;
case AUDIO_S8:
- format = SND_PCM_SFMT_S8;
- cparams.buf.block.frag_size = spec->samples * spec->channels;
+ format = SND_PCM_FORMAT_S8;
break;
case AUDIO_S16LSB:
- format = SND_PCM_SFMT_S16_LE;
- cparams.buf.block.frag_size = spec->samples*2 * spec->channels;
+ format = SND_PCM_FORMAT_S16_LE;
break;
case AUDIO_S16MSB:
- format = SND_PCM_SFMT_S16_BE;
- cparams.buf.block.frag_size = spec->samples*2 * spec->channels;
+ format = SND_PCM_FORMAT_S16_BE;
break;
case AUDIO_U16LSB:
- format = SND_PCM_SFMT_U16_LE;
- cparams.buf.block.frag_size = spec->samples*2 * spec->channels;
+ format = SND_PCM_FORMAT_U16_LE;
break;
case AUDIO_U16MSB:
- format = SND_PCM_SFMT_U16_BE;
- cparams.buf.block.frag_size = spec->samples*2 * spec->channels;
+ format = SND_PCM_FORMAT_U16_BE;
break;
default:
+ format = 0;
break;
}
- if ( ! format ) {
+ if ( format != 0 ) {
+ status = snd_pcm_hw_params_set_format(pcm_handle, params, format);
+ }
+ if ( status < 0 ) {
test_format = SDL_NextAudioFormat();
}
}
- if ( format == 0 ) {
+ if ( status < 0 ) {
SDL_SetError("Couldn't find any hardware audio formats");
+ ALSA_CloseAudio(this);
return(-1);
}
spec->format = test_format;
- /* Set the audio format */
- cparams.format.format = format;
+ /* Set the number of channels */
+ status = snd_pcm_hw_params_set_channels(pcm_handle, params, spec->channels);
+ if ( status < 0 ) {
+ status = snd_pcm_hw_params_get_channels(params);
+ if ( (status <= 0) || (status > 2) ) {
+ SDL_SetError("Couldn't set audio channels");
+ ALSA_CloseAudio(this);
+ return(-1);
+ }
+ spec->channels = status;
+ }
- /* Set mono or stereo audio (currently only two channels supported) */
- cparams.format.voices = spec->channels;
-
- #ifdef DEBUG_AUDIO
- printf("intializing channels %d\n", cparams.format.voices);
- #endif
-
- /* Set rate */
- cparams.format.rate = spec->freq ;
+ /* Set the audio rate */
+ status = snd_pcm_hw_params_set_rate_near(pcm_handle, params, spec->freq, NULL);
+ if ( status < 0 ) {
+ SDL_SetError("Couldn't set audio frequency: %s", snd_strerror(status));
+ ALSA_CloseAudio(this);
+ return(-1);
+ }
+ spec->freq = status;
- /* Setup the transfer parameters according to cparams */
- rval = snd_pcm_plugin_params(audio_handle, &cparams);
- if (rval < 0) {
- SDL_SetError("snd_pcm_channel_params failed: %s\n", snd_strerror (rval));
+ /* Set the buffer size, in samples */
+ frames = spec->samples;
+ frames = snd_pcm_hw_params_set_period_size_near(pcm_handle, params, frames, NULL);
+ spec->samples = frames;
+ snd_pcm_hw_params_set_periods_near(pcm_handle, params, 2, NULL);
+
+ /* "set" the hardware with the desired parameters */
+ status = snd_pcm_hw_params(pcm_handle, params);
+ if ( status < 0 ) {
+ SDL_SetError("Couldn't set audio parameters: %s", snd_strerror(status));
+ ALSA_CloseAudio(this);
return(-1);
}
- /* Make sure channel is setup right one last time */
- memset( &csetup, 0, sizeof( csetup ) );
- csetup.channel = SND_PCM_CHANNEL_PLAYBACK;
- if ( snd_pcm_plugin_setup( audio_handle, &csetup ) < 0 )
- {
- SDL_SetError("Unable to setup playback channel\n" );
- return(-1);
- }
-
-#ifdef DEBUG_AUDIO
- else
- {
- fprintf(stderr,"requested format: %d\n",cparams.format.format);
- fprintf(stderr,"requested frag size: %d\n",cparams.buf.block.frag_size);
- fprintf(stderr,"requested max frags: %d\n\n",cparams.buf.block.frags_max);
-
- fprintf(stderr,"real format: %d\n", csetup.format.format );
- fprintf(stderr,"real frag size : %d\n", csetup.buf.block.frag_size );
- fprintf(stderr,"real max frags : %d\n", csetup.buf.block.frags_max );
- }
-#endif // DEBUG_AUDIO
+ /* Calculate the final parameters for this audio specification */
+ SDL_CalculateAudioSpec(spec);
- /* Allocate memory to the audio buffer and initialize with silence
- (Note that buffer size must be a multiple of fragment size, so find closest multiple)
- */
-
- twidth = snd_pcm_format_width(format);
- if (twidth < 0) {
- printf("snd_pcm_format_width failed\n");
- twidth = 0;
- }
-#ifdef DEBUG_AUDIO
- printf("format is %d bits wide\n",twidth);
-#endif
-
- pcm_len = csetup.buf.block.frag_size * (twidth/8) * csetup.format.voices ;
-
-#ifdef DEBUG_AUDIO
- printf("pcm_len set to %d\n", pcm_len);
-#endif
-
- if (pcm_len == 0)
- {
- pcm_len = csetup.buf.block.frag_size;
- }
-
- pcm_buf = (Uint8*)malloc(pcm_len);
- if (pcm_buf == NULL) {
- SDL_SetError("pcm_buf malloc failed\n");
- return(-1);
- }
- memset(pcm_buf,spec->silence,pcm_len);
-
-#ifdef DEBUG_AUDIO
- fprintf(stderr,"pcm_buf malloced and silenced.\n");
-#endif
-
- /* get the file descriptor */
- if( (audio_fd = snd_pcm_file_descriptor(audio_handle, device_no)) < 0)
- {
- fprintf(stderr, "snd_pcm_file_descriptor failed with error code: %d\n", audio_fd);
- }
-
- /* Trigger audio playback */
- rval = snd_pcm_plugin_prepare( audio_handle, SND_PCM_CHANNEL_PLAYBACK);
- if (rval < 0) {
- SDL_SetError("snd_pcm_plugin_prepare failed: %s\n", snd_strerror (rval));
- return(-1);
+ /* Allocate mixing buffer */
+ mixlen = spec->size;
+ mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
+ if ( mixbuf == NULL ) {
+ ALSA_CloseAudio(this);
+ return(-1);
}
- rval = snd_pcm_playback_go(audio_handle);
- if (rval < 0) {
- SDL_SetError("snd_pcm_playback_go failed: %s\n", snd_strerror (rval));
- return(-1);
- }
-
- /* 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;
- }
- }
+ memset(mixbuf, spec->silence, spec->size);
/* Get the parent process id (we're the parent of the audio thread) */
parent = getpid();