Mercurial > sdl-ios-xcode
diff src/audio/alsa/SDL_alsa_audio.c @ 2049:5f6550e5184f
Merged SDL-ryan-multiple-audio-device branch r2803:2871 into the trunk.
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Tue, 17 Oct 2006 09:15:21 +0000 |
parents | adf732f1f016 |
children | 866052b01ee5 |
line wrap: on
line diff
--- a/src/audio/alsa/SDL_alsa_audio.c Sun Oct 01 16:10:41 2006 +0000 +++ b/src/audio/alsa/SDL_alsa_audio.c Tue Oct 17 09:15:21 2006 +0000 @@ -25,6 +25,9 @@ #include <sys/types.h> #include <signal.h> /* For kill() */ +#include <dlfcn.h> +#include <errno.h> +#include <string.h> #include "SDL_timer.h" #include "SDL_audio.h" @@ -32,14 +35,6 @@ #include "../SDL_audio_c.h" #include "SDL_alsa_audio.h" -#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC -#include <dlfcn.h> -#include "SDL_name.h" -#include "SDL_loadso.h" -#else -#define SDL_NAME(X) X -#endif - /* The tag name used by ALSA audio */ #define DRIVER_NAME "alsa" @@ -47,182 +42,138 @@ /* The default ALSA audio driver */ #define DEFAULT_DEVICE "default" -/* Audio driver functions */ -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); +static int (*ALSA_snd_pcm_open) + (snd_pcm_t **, const char *, snd_pcm_stream_t, int); +static int (*ALSA_snd_pcm_close)(snd_pcm_t * pcm); +static snd_pcm_sframes_t(*ALSA_snd_pcm_writei) + (snd_pcm_t *,const void *, snd_pcm_uframes_t); +static int (*ALSA_snd_pcm_resume)(snd_pcm_t *); +static int (*ALSA_snd_pcm_prepare)(snd_pcm_t *); +static int (*ALSA_snd_pcm_drain)(snd_pcm_t *); +static const char *(*ALSA_snd_strerror)(int); +static size_t(*ALSA_snd_pcm_hw_params_sizeof)(void); +static size_t(*ALSA_snd_pcm_sw_params_sizeof)(void); +static int (*ALSA_snd_pcm_hw_params_any)(snd_pcm_t *, snd_pcm_hw_params_t *); +static int (*ALSA_snd_pcm_hw_params_set_access) + (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_access_t); +static int (*ALSA_snd_pcm_hw_params_set_format) + (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_format_t); +static int (*ALSA_snd_pcm_hw_params_set_channels) + (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int); +static int (*ALSA_snd_pcm_hw_params_get_channels)(const snd_pcm_hw_params_t *); +static unsigned int (*ALSA_snd_pcm_hw_params_set_rate_near) + (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int, int *); +static snd_pcm_uframes_t (*ALSA_snd_pcm_hw_params_set_period_size_near) + (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_uframes_t, int *); +static snd_pcm_sframes_t (*ALSA_snd_pcm_hw_params_get_period_size) + (const snd_pcm_hw_params_t *); +static unsigned int (*ALSA_snd_pcm_hw_params_set_periods_near) + (snd_pcm_t *,snd_pcm_hw_params_t *, unsigned int, int *); +static int (*ALSA_snd_pcm_hw_params_get_periods)(snd_pcm_hw_params_t *); +static int (*ALSA_snd_pcm_hw_params)(snd_pcm_t *, snd_pcm_hw_params_t *); +static int (*ALSA_snd_pcm_sw_params_current)(snd_pcm_t*, snd_pcm_sw_params_t*); +static int (*ALSA_snd_pcm_sw_params_set_start_threshold) + (snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t); +static int (*ALSA_snd_pcm_sw_params_set_avail_min) + (snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t); +static int (*ALSA_snd_pcm_sw_params)(snd_pcm_t *, snd_pcm_sw_params_t *); +static int (*ALSA_snd_pcm_nonblock)(snd_pcm_t *, int); +#define snd_pcm_hw_params_sizeof ALSA_snd_pcm_hw_params_sizeof +#define snd_pcm_sw_params_sizeof ALSA_snd_pcm_sw_params_sizeof + #ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC static const char *alsa_library = SDL_AUDIO_DRIVER_ALSA_DYNAMIC; static void *alsa_handle = NULL; -static int alsa_loaded = 0; + +static int +load_alsa_sym(const char *fn, void **addr) +{ + /* + * !!! FIXME: + * Eventually, this will deal with fallbacks, version changes, and + * missing symbols we can workaround. But for now, it doesn't. + */ -static int (*SDL_snd_pcm_open) (snd_pcm_t ** pcm, const char *name, - snd_pcm_stream_t stream, int mode); -static int (*SDL_NAME(snd_pcm_open)) (snd_pcm_t ** pcm, const char *name, - snd_pcm_stream_t stream, int mode); -static int (*SDL_NAME(snd_pcm_close)) (snd_pcm_t * pcm); -static snd_pcm_sframes_t(*SDL_NAME(snd_pcm_writei)) (snd_pcm_t * pcm, - const void *buffer, - snd_pcm_uframes_t size); -static int (*SDL_NAME(snd_pcm_resume)) (snd_pcm_t * pcm); -static int (*SDL_NAME(snd_pcm_prepare)) (snd_pcm_t * pcm); -static int (*SDL_NAME(snd_pcm_drain)) (snd_pcm_t * pcm); -static const char *(*SDL_NAME(snd_strerror)) (int errnum); -static size_t(*SDL_NAME(snd_pcm_hw_params_sizeof)) (void); -static size_t(*SDL_NAME(snd_pcm_sw_params_sizeof)) (void); -static int (*SDL_NAME(snd_pcm_hw_params_any)) (snd_pcm_t * pcm, - snd_pcm_hw_params_t * params); -static int (*SDL_NAME(snd_pcm_hw_params_set_access)) (snd_pcm_t * pcm, - snd_pcm_hw_params_t * - params, - snd_pcm_access_t - access); -static int (*SDL_NAME(snd_pcm_hw_params_set_format)) (snd_pcm_t * pcm, - snd_pcm_hw_params_t * - params, - snd_pcm_format_t val); -static int (*SDL_NAME(snd_pcm_hw_params_set_channels)) (snd_pcm_t * pcm, - snd_pcm_hw_params_t * - params, - unsigned int val); -static int (*SDL_NAME(snd_pcm_hw_params_get_channels)) (const - snd_pcm_hw_params_t * - params); -static unsigned int - (*SDL_NAME(snd_pcm_hw_params_set_rate_near)) (snd_pcm_t * - pcm, - snd_pcm_hw_params_t - * params, - unsigned int val, int *dir); -static snd_pcm_uframes_t - (*SDL_NAME(snd_pcm_hw_params_set_period_size_near)) (snd_pcm_t * pcm, - snd_pcm_hw_params_t - * params, - snd_pcm_uframes_t - val, int *dir); -static snd_pcm_sframes_t - (*SDL_NAME(snd_pcm_hw_params_get_period_size)) (const - snd_pcm_hw_params_t - * params); -static unsigned int - (*SDL_NAME(snd_pcm_hw_params_set_periods_near)) (snd_pcm_t * pcm, - snd_pcm_hw_params_t - * params, - unsigned int val, - int *dir); -static int (*SDL_NAME(snd_pcm_hw_params_get_periods)) (snd_pcm_hw_params_t * - params); -static int (*SDL_NAME(snd_pcm_hw_params)) (snd_pcm_t * pcm, - snd_pcm_hw_params_t * params); -/* -*/ -static int (*SDL_NAME(snd_pcm_sw_params_current)) (snd_pcm_t * pcm, - snd_pcm_sw_params_t * - swparams); -static int (*SDL_NAME(snd_pcm_sw_params_set_start_threshold)) (snd_pcm_t * - pcm, - snd_pcm_sw_params_t - * params, - snd_pcm_uframes_t - val); -static int (*SDL_NAME(snd_pcm_sw_params_set_avail_min)) (snd_pcm_t * pcm, - snd_pcm_sw_params_t - * params, - snd_pcm_uframes_t - val); -static int (*SDL_NAME(snd_pcm_sw_params)) (snd_pcm_t * pcm, - snd_pcm_sw_params_t * params); -static int (*SDL_NAME(snd_pcm_nonblock)) (snd_pcm_t * pcm, int nonblock); -#define snd_pcm_hw_params_sizeof SDL_NAME(snd_pcm_hw_params_sizeof) -#define snd_pcm_sw_params_sizeof SDL_NAME(snd_pcm_sw_params_sizeof) +#if HAVE_DLVSYM + *addr = dlvsym(alsa_handle, fn, "ALSA_0.9"); + if (*addr == NULL) +#endif + { + *addr = dlsym(alsa_handle, fn); + if (*addr == NULL) { + SDL_SetError("dlsym('%s') failed: %s", fn, strerror(errno)); + return 0; + } + } + + return 1; +} /* cast funcs to char* first, to please GCC's strict aliasing rules. */ -static struct +#define SDL_ALSA_SYM(x) \ + if (!load_alsa_sym(#x, (void **) (char *) &ALSA_##x)) return -1 +#else +#define SDL_ALSA_SYM(x) ALSA_##x = x +#endif + +static int load_alsa_syms(void) { - const char *name; - void **func; -} alsa_functions[] = { - { - "snd_pcm_open", (void **) (char *) &SDL_NAME(snd_pcm_open)}, { - "snd_pcm_close", (void **) (char *) &SDL_NAME(snd_pcm_close)}, { - "snd_pcm_writei", (void **) (char *) &SDL_NAME(snd_pcm_writei)}, { - "snd_pcm_resume", (void **) (char *) &SDL_NAME(snd_pcm_resume)}, { - "snd_pcm_prepare", (void **) (char *) &SDL_NAME(snd_pcm_prepare)}, { - "snd_pcm_drain", (void **) (char *) &SDL_NAME(snd_pcm_drain)}, { - "snd_strerror", (void **) (char *) &SDL_NAME(snd_strerror)}, { - "snd_pcm_hw_params_sizeof", - (void **) (char *) &SDL_NAME(snd_pcm_hw_params_sizeof)}, { - "snd_pcm_sw_params_sizeof", - (void **) (char *) &SDL_NAME(snd_pcm_sw_params_sizeof)}, { - "snd_pcm_hw_params_any", - (void **) (char *) &SDL_NAME(snd_pcm_hw_params_any)}, { - "snd_pcm_hw_params_set_access", - (void **) (char *) &SDL_NAME(snd_pcm_hw_params_set_access)}, { - "snd_pcm_hw_params_set_format", - (void **) (char *) &SDL_NAME(snd_pcm_hw_params_set_format)}, { - "snd_pcm_hw_params_set_channels", - (void **) (char *) &SDL_NAME(snd_pcm_hw_params_set_channels)}, { - "snd_pcm_hw_params_get_channels", - (void **) (char *) &SDL_NAME(snd_pcm_hw_params_get_channels)}, { - "snd_pcm_hw_params_set_rate_near", - (void **) (char *) &SDL_NAME(snd_pcm_hw_params_set_rate_near)}, { - "snd_pcm_hw_params_set_period_size_near", (void **) (char *) - &SDL_NAME(snd_pcm_hw_params_set_period_size_near)}, { - "snd_pcm_hw_params_get_period_size", - (void **) (char *) &SDL_NAME(snd_pcm_hw_params_get_period_size)}, - { - "snd_pcm_hw_params_set_periods_near", (void **) (char *) - &SDL_NAME(snd_pcm_hw_params_set_periods_near)}, { - "snd_pcm_hw_params_get_periods", - (void **) (char *) &SDL_NAME(snd_pcm_hw_params_get_periods)}, { - "snd_pcm_hw_params", (void **) (char *) &SDL_NAME(snd_pcm_hw_params)}, { - "snd_pcm_sw_params_current", - (void **) (char *) &SDL_NAME(snd_pcm_sw_params_current)}, { - "snd_pcm_sw_params_set_start_threshold", (void **) (char *) - &SDL_NAME(snd_pcm_sw_params_set_start_threshold)}, { - "snd_pcm_sw_params_set_avail_min", - (void **) (char *) &SDL_NAME(snd_pcm_sw_params_set_avail_min)}, { - "snd_pcm_sw_params", (void **) (char *) &SDL_NAME(snd_pcm_sw_params)}, { -"snd_pcm_nonblock", (void **) (char *) &SDL_NAME(snd_pcm_nonblock)},}; + SDL_ALSA_SYM(snd_pcm_open); + SDL_ALSA_SYM(snd_pcm_close); + SDL_ALSA_SYM(snd_pcm_writei); + SDL_ALSA_SYM(snd_pcm_resume); + SDL_ALSA_SYM(snd_pcm_prepare); + SDL_ALSA_SYM(snd_pcm_drain); + SDL_ALSA_SYM(snd_strerror); + SDL_ALSA_SYM(snd_pcm_hw_params_sizeof); + SDL_ALSA_SYM(snd_pcm_sw_params_sizeof); + SDL_ALSA_SYM(snd_pcm_hw_params_any); + SDL_ALSA_SYM(snd_pcm_hw_params_set_access); + SDL_ALSA_SYM(snd_pcm_hw_params_set_format); + SDL_ALSA_SYM(snd_pcm_hw_params_set_channels); + SDL_ALSA_SYM(snd_pcm_hw_params_get_channels); + SDL_ALSA_SYM(snd_pcm_hw_params_set_rate_near); + SDL_ALSA_SYM(snd_pcm_hw_params_set_period_size_near); + SDL_ALSA_SYM(snd_pcm_hw_params_get_period_size); + SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_near); + SDL_ALSA_SYM(snd_pcm_hw_params_get_periods); + SDL_ALSA_SYM(snd_pcm_hw_params); + SDL_ALSA_SYM(snd_pcm_sw_params_current); + SDL_ALSA_SYM(snd_pcm_sw_params_set_start_threshold); + SDL_ALSA_SYM(snd_pcm_sw_params_set_avail_min); + SDL_ALSA_SYM(snd_pcm_sw_params); + SDL_ALSA_SYM(snd_pcm_nonblock); + return 0; +} +#undef SDL_ALSA_SYM + +#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC static void UnloadALSALibrary(void) { - if (alsa_loaded) { -/* SDL_UnloadObject(alsa_handle);*/ + if (alsa_handle != NULL) { dlclose(alsa_handle); alsa_handle = NULL; - alsa_loaded = 0; } } static int LoadALSALibrary(void) { - int i, retval = -1; - -/* alsa_handle = SDL_LoadObject(alsa_library);*/ - alsa_handle = dlopen(alsa_library, RTLD_NOW); - if (alsa_handle) { - alsa_loaded = 1; - retval = 0; - for (i = 0; i < SDL_arraysize(alsa_functions); i++) { -/* *alsa_functions[i].func = SDL_LoadFunction(alsa_handle,alsa_functions[i].name);*/ -#if HAVE_DLVSYM - *alsa_functions[i].func = - dlvsym(alsa_handle, alsa_functions[i].name, "ALSA_0.9"); - if (!*alsa_functions[i].func) -#endif - *alsa_functions[i].func = - dlsym(alsa_handle, alsa_functions[i].name); - if (!*alsa_functions[i].func) { - retval = -1; + int retval = 0; + if (alsa_handle == NULL) { + alsa_handle = dlopen(alsa_library, RTLD_NOW); + if (alsa_handle == NULL) { + retval = -1; + SDL_SetError("ALSA: dlopen('%s') failed: %s\n", + alsa_library, strerror(errno)); + } else { + retval = load_alsa_syms(); + if (retval < 0) { UnloadALSALibrary(); - break; } } } @@ -234,12 +185,12 @@ static void UnloadALSALibrary(void) { - return; } static int LoadALSALibrary(void) { + load_alsa_syms(); return 0; } @@ -262,80 +213,10 @@ return device; } -/* Audio driver bootstrap functions */ - -static int -Audio_Available(void) -{ - int available; - int status; - snd_pcm_t *handle; - - available = 0; - if (LoadALSALibrary() < 0) { - return available; - } - status = - SDL_NAME(snd_pcm_open) (&handle, get_audio_device(2), - SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); - if (status >= 0) { - available = 1; - SDL_NAME(snd_pcm_close) (handle); - } - UnloadALSALibrary(); - return (available); -} - -static void -Audio_DeleteDevice(SDL_AudioDevice * device) -{ - SDL_free(device->hidden); - SDL_free(device); - UnloadALSALibrary(); -} - -static SDL_AudioDevice * -Audio_CreateDevice(int devindex) -{ - SDL_AudioDevice *this; - - /* Initialize all variables that we clean on shutdown */ - LoadALSALibrary(); - this = (SDL_AudioDevice *) SDL_malloc(sizeof(SDL_AudioDevice)); - if (this) { - SDL_memset(this, 0, (sizeof *this)); - this->hidden = (struct SDL_PrivateAudioData *) - SDL_malloc((sizeof *this->hidden)); - } - if ((this == NULL) || (this->hidden == NULL)) { - SDL_OutOfMemory(); - if (this) { - SDL_free(this); - } - return (0); - } - SDL_memset(this->hidden, 0, (sizeof *this->hidden)); - - /* Set the function pointers */ - this->OpenAudio = ALSA_OpenAudio; - this->WaitAudio = ALSA_WaitAudio; - this->PlayAudio = ALSA_PlayAudio; - this->GetAudioBuf = ALSA_GetAudioBuf; - this->CloseAudio = ALSA_CloseAudio; - - this->free = Audio_DeleteDevice; - - return this; -} - -AudioBootStrap ALSA_bootstrap = { - 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 -ALSA_WaitAudio(_THIS) +ALSA_WaitDevice(_THIS) { /* Check to see if the thread-parent process is still alive */ { @@ -343,8 +224,9 @@ /* 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) { + /* Check every 10 loops */ + if (this->hidden->parent && (((++cnt) % 10) == 0)) { + if (kill(this->hidden->parent, 0) < 0) { this->enabled = 0; } } @@ -352,13 +234,14 @@ } +/* !!! FIXME: is there a channel swizzler in alsalib instead? */ /* * http://bugzilla.libsdl.org/show_bug.cgi?id=110 * "For Linux ALSA, this is FL-FR-RL-RR-C-LFE * and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-RL-RR" */ #define SWIZ6(T) \ - T *ptr = (T *) mixbuf; \ + T *ptr = (T *) this->hidden->mixbuf; \ const Uint32 count = (this->spec.samples / 6); \ Uint32 i; \ for (i = 0; i < count; i++, ptr += 6) { \ @@ -392,8 +275,8 @@ /* - * Called right before feeding this->mixbuf to the hardware. Swizzle channels - * from Windows/Mac order to the format alsalib will want. + * Called right before feeding this->hidden->mixbuf to the hardware. Swizzle + * channels from Windows/Mac order to the format alsalib will want. */ static __inline__ void swizzle_alsa_channels(_THIS) @@ -415,7 +298,7 @@ static void -ALSA_PlayAudio(_THIS) +ALSA_PlayDevice(_THIS) { int status; int sample_len; @@ -424,11 +307,12 @@ swizzle_alsa_channels(this); sample_len = this->spec.samples; - sample_buf = (signed short *) mixbuf; + sample_buf = (signed short *) this->hidden->mixbuf; while (sample_len > 0) { - status = - SDL_NAME(snd_pcm_writei) (pcm_handle, sample_buf, sample_len); + status = ALSA_snd_pcm_writei(this->hidden->pcm_handle, + sample_buf, sample_len); + if (status < 0) { if (status == -EAGAIN) { SDL_Delay(1); @@ -437,11 +321,11 @@ if (status == -ESTRPIPE) { do { SDL_Delay(1); - status = SDL_NAME(snd_pcm_resume) (pcm_handle); + status = ALSA_snd_pcm_resume(this->hidden->pcm_handle); } while (status == -EAGAIN); } if (status < 0) { - status = SDL_NAME(snd_pcm_prepare) (pcm_handle); + status = ALSA_snd_pcm_prepare(this->hidden->pcm_handle); } if (status < 0) { /* Hmm, not much we can do - abort */ @@ -456,74 +340,89 @@ } static Uint8 * -ALSA_GetAudioBuf(_THIS) +ALSA_GetDeviceBuf(_THIS) { - return (mixbuf); + return (this->hidden->mixbuf); } static void -ALSA_CloseAudio(_THIS) +ALSA_CloseDevice(_THIS) { - if (mixbuf != NULL) { - SDL_FreeAudioMem(mixbuf); - mixbuf = NULL; - } - if (pcm_handle) { - SDL_NAME(snd_pcm_drain) (pcm_handle); - SDL_NAME(snd_pcm_close) (pcm_handle); - pcm_handle = NULL; + if (this->hidden != NULL) { + if (this->hidden->mixbuf != NULL) { + SDL_FreeAudioMem(this->hidden->mixbuf); + this->hidden->mixbuf = NULL; + } + if (this->hidden->pcm_handle) { + ALSA_snd_pcm_drain(this->hidden->pcm_handle); + ALSA_snd_pcm_close(this->hidden->pcm_handle); + this->hidden->pcm_handle = NULL; + } + SDL_free(this->hidden); + this->hidden = NULL; } } static int -ALSA_OpenAudio(_THIS, SDL_AudioSpec * spec) +ALSA_OpenDevice(_THIS, const char *devname, int iscapture) { - int status; - snd_pcm_hw_params_t *hwparams; - snd_pcm_sw_params_t *swparams; - snd_pcm_format_t format; - snd_pcm_uframes_t frames; - SDL_AudioFormat test_format; + int status = 0; + snd_pcm_t *pcm_handle = NULL; + snd_pcm_hw_params_t *hwparams = NULL; + snd_pcm_sw_params_t *swparams = NULL; + snd_pcm_format_t format = 0; + snd_pcm_uframes_t frames = 0; + SDL_AudioFormat test_format = 0; + + /* Initialize all variables that we clean on shutdown */ + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + if (this->hidden == NULL) { + SDL_OutOfMemory(); + return 0; + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); /* Open the audio device */ /* Name of device should depend on # channels in spec */ - status = - SDL_NAME(snd_pcm_open) (&pcm_handle, - get_audio_device(spec->channels), - SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + status = ALSA_snd_pcm_open(&pcm_handle, + get_audio_device(this->spec.channels), + SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); if (status < 0) { - SDL_SetError("Couldn't open audio device: %s", - SDL_NAME(snd_strerror) (status)); - return (-1); + ALSA_CloseDevice(this); + SDL_SetError("ALSA: Couldn't open audio device: %s", + ALSA_snd_strerror(status)); + return 0; } + this->hidden->pcm_handle = pcm_handle; + /* Figure out what the hardware is capable of */ snd_pcm_hw_params_alloca(&hwparams); - status = SDL_NAME(snd_pcm_hw_params_any) (pcm_handle, hwparams); + status = ALSA_snd_pcm_hw_params_any(pcm_handle, hwparams); if (status < 0) { - SDL_SetError("Couldn't get hardware config: %s", - SDL_NAME(snd_strerror) (status)); - ALSA_CloseAudio(this); - return (-1); + ALSA_CloseDevice(this); + SDL_SetError("ALSA: Couldn't get hardware config: %s", + ALSA_snd_strerror(status)); + return 0; } /* SDL only uses interleaved sample output */ - status = - SDL_NAME(snd_pcm_hw_params_set_access) (pcm_handle, hwparams, - SND_PCM_ACCESS_RW_INTERLEAVED); + status = ALSA_snd_pcm_hw_params_set_access(pcm_handle, hwparams, + SND_PCM_ACCESS_RW_INTERLEAVED); if (status < 0) { - SDL_SetError("Couldn't set interleaved access: %s", - SDL_NAME(snd_strerror) (status)); - ALSA_CloseAudio(this); - return (-1); + ALSA_CloseDevice(this); + SDL_SetError("ALSA: Couldn't set interleaved access: %s", + ALSA_snd_strerror(status)); + return 0; } /* Try for a closest match on audio format */ status = -1; - for (test_format = SDL_FirstAudioFormat(spec->format); + for (test_format = SDL_FirstAudioFormat(this->spec.format); test_format && (status < 0);) { - status = 0; /* if we can't support a format, it'll become -1. */ + status = 0; /* if we can't support a format, it'll become -1. */ switch (test_format) { case AUDIO_U8: format = SND_PCM_FORMAT_U8; @@ -560,131 +459,151 @@ break; } if (status >= 0) { - status = - SDL_NAME(snd_pcm_hw_params_set_format) (pcm_handle, - hwparams, format); + status = ALSA_snd_pcm_hw_params_set_format(pcm_handle, + hwparams, format); } if (status < 0) { test_format = SDL_NextAudioFormat(); } } if (status < 0) { - SDL_SetError("Couldn't find any hardware audio formats"); - ALSA_CloseAudio(this); - return (-1); + ALSA_CloseDevice(this); + SDL_SetError("ALSA: Couldn't find any hardware audio formats"); + return 0; } - spec->format = test_format; + this->spec.format = test_format; /* Set the number of channels */ - status = - SDL_NAME(snd_pcm_hw_params_set_channels) (pcm_handle, hwparams, - spec->channels); + status = ALSA_snd_pcm_hw_params_set_channels(pcm_handle, hwparams, + this->spec.channels); if (status < 0) { - status = SDL_NAME(snd_pcm_hw_params_get_channels) (hwparams); + status = ALSA_snd_pcm_hw_params_get_channels(hwparams); if ((status <= 0) || (status > 2)) { - SDL_SetError("Couldn't set audio channels"); - ALSA_CloseAudio(this); - return (-1); + ALSA_CloseDevice(this); + SDL_SetError("ALSA: Couldn't set audio channels"); + return 0; } - spec->channels = status; + this->spec.channels = status; } /* Set the audio rate */ - status = - SDL_NAME(snd_pcm_hw_params_set_rate_near) (pcm_handle, hwparams, - spec->freq, NULL); + status = ALSA_snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, + this->spec.freq, NULL); if (status < 0) { - SDL_SetError("Couldn't set audio frequency: %s", - SDL_NAME(snd_strerror) (status)); - ALSA_CloseAudio(this); - return (-1); + ALSA_CloseDevice(this); + SDL_SetError("ALSA: Couldn't set audio frequency: %s", + ALSA_snd_strerror(status)); + return 0; } - spec->freq = status; + this->spec.freq = status; /* Set the buffer size, in samples */ - frames = spec->samples; - frames = - SDL_NAME(snd_pcm_hw_params_set_period_size_near) (pcm_handle, - hwparams, frames, - NULL); - spec->samples = frames; - SDL_NAME(snd_pcm_hw_params_set_periods_near) (pcm_handle, hwparams, 2, - NULL); + frames = this->spec.samples; + frames = ALSA_snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, + frames, NULL); + this->spec.samples = frames; + ALSA_snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, 2, NULL); /* "set" the hardware with the desired parameters */ - status = SDL_NAME(snd_pcm_hw_params) (pcm_handle, hwparams); + status = ALSA_snd_pcm_hw_params(pcm_handle, hwparams); if (status < 0) { - SDL_SetError("Couldn't set hardware audio parameters: %s", - SDL_NAME(snd_strerror) (status)); - ALSA_CloseAudio(this); - return (-1); + ALSA_CloseDevice(this); + SDL_SetError("ALSA: Couldn't set hardware audio parameters: %s", + ALSA_snd_strerror(status)); + return 0; } -/* This is useful for debugging... */ -/* -{ snd_pcm_sframes_t bufsize; int fragments; - bufsize = SDL_NAME(snd_pcm_hw_params_get_period_size)(hwparams); - fragments = SDL_NAME(snd_pcm_hw_params_get_periods)(hwparams); - - fprintf(stderr, "ALSA: bufsize = %ld, fragments = %d\n", bufsize, fragments); +#if AUDIO_DEBUG +{ + snd_pcm_sframes_t bufsize; + int fragments; + bufsize = ALSA_snd_pcm_hw_params_get_period_size(hwparams); + fragments = ALSA_snd_pcm_hw_params_get_periods(hwparams); + fprintf(stderr,"ALSA: bufsize = %ld, fragments = %d\n",bufsize,fragments); } -*/ +#endif /* Set the software parameters */ snd_pcm_sw_params_alloca(&swparams); - status = SDL_NAME(snd_pcm_sw_params_current) (pcm_handle, swparams); + status = ALSA_snd_pcm_sw_params_current(pcm_handle, swparams); if (status < 0) { - SDL_SetError("Couldn't get software config: %s", - SDL_NAME(snd_strerror) (status)); - ALSA_CloseAudio(this); - return (-1); - } - status = - SDL_NAME(snd_pcm_sw_params_set_start_threshold) (pcm_handle, - swparams, 0); - if (status < 0) { - SDL_SetError("Couldn't set start threshold: %s", - SDL_NAME(snd_strerror) (status)); - ALSA_CloseAudio(this); - return (-1); + ALSA_CloseDevice(this); + SDL_SetError("ALSA: Couldn't get software config: %s", + ALSA_snd_strerror(status)); + return 0; } - status = - SDL_NAME(snd_pcm_sw_params_set_avail_min) (pcm_handle, swparams, - frames); + status = ALSA_snd_pcm_sw_params_set_start_threshold(pcm_handle,swparams,0); + if (status < 0) { + ALSA_CloseDevice(this); + SDL_SetError("ALSA: Couldn't set start threshold: %s", + ALSA_snd_strerror(status)); + return 0; + } + status = ALSA_snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, frames); if (status < 0) { - SDL_SetError("Couldn't set avail min: %s", - SDL_NAME(snd_strerror) (status)); - ALSA_CloseAudio(this); - return (-1); + ALSA_CloseDevice(this); + SDL_SetError("Couldn't set avail min: %s", ALSA_snd_strerror(status)); + return 0; } - status = SDL_NAME(snd_pcm_sw_params) (pcm_handle, swparams); + status = ALSA_snd_pcm_sw_params(pcm_handle, swparams); if (status < 0) { + ALSA_CloseDevice(this); SDL_SetError("Couldn't set software audio parameters: %s", - SDL_NAME(snd_strerror) (status)); - ALSA_CloseAudio(this); - return (-1); + ALSA_snd_strerror(status)); + return 0; } /* Calculate the final parameters for this audio specification */ - SDL_CalculateAudioSpec(spec); + SDL_CalculateAudioSpec(&this->spec); /* Allocate mixing buffer */ - mixlen = spec->size; - mixbuf = (Uint8 *) SDL_AllocAudioMem(mixlen); - if (mixbuf == NULL) { - ALSA_CloseAudio(this); - return (-1); + this->hidden->mixlen = this->spec.size; + this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen); + if (this->hidden->mixbuf == NULL) { + ALSA_CloseDevice(this); + SDL_OutOfMemory(); + return 0; } - SDL_memset(mixbuf, spec->silence, spec->size); + SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size); /* Get the parent process id (we're the parent of the audio thread) */ - parent = getpid(); + this->hidden->parent = getpid(); /* Switch to blocking mode for playback */ - SDL_NAME(snd_pcm_nonblock) (pcm_handle, 0); + ALSA_snd_pcm_nonblock(pcm_handle, 0); /* We're ready to rock and roll. :-) */ - return (0); + return 1; +} + +static void +ALSA_Deinitialize(void) +{ + UnloadALSALibrary(); } +static int +ALSA_Init(SDL_AudioDriverImpl *impl) +{ + if (LoadALSALibrary() < 0) { + return 0; + } + + /* Set the function pointers */ + impl->OpenDevice = ALSA_OpenDevice; + impl->WaitDevice = ALSA_WaitDevice; + impl->GetDeviceBuf = ALSA_GetDeviceBuf; + impl->PlayDevice = ALSA_PlayDevice; + impl->CloseDevice = ALSA_CloseDevice; + impl->Deinitialize = ALSA_Deinitialize; + impl->OnlyHasDefaultOutputDevice = 1; /* !!! FIXME: Add device enum! */ + + return 1; +} + + +AudioBootStrap ALSA_bootstrap = { + DRIVER_NAME, "ALSA 0.9 PCM audio", ALSA_Init, 0 +}; + /* vi: set ts=4 sw=4 expandtab: */