Date: Sun, 29 Feb 2004 15:14:22 +0200
authorSam Lantinga <slouken@libsdl.org>
Tue, 02 Mar 2004 12:49:16 +0000
changeset 86592615154bb68
parent 864 0c892e99b65b
child 866 0a45995a7fc3
Date: Sun, 29 Feb 2004 15:14:22 +0200
From: Martin_Storsj
Subject: Dynamic loading of ALSA

I recently discovered that SDL can dynamically load ESD and aRts, and
made a patch which adds this same functionality to ALSA.

The update for configure.in isn't too good (it should e.g. look for
libasound.so in other directories than /usr/lib), because I'm not too
good at shellscripting and autoconf.

The reason for using dlfcn.h and dlopen instead of SDL_LoadLibrary and
SDL_LoadFunction is that libasound uses versioned symbols, and it is
necessary to load the correct version using dlvsym. This isn't probably
any real portability issue, because ALSA is linux-only.
configure.in
src/audio/alsa/Makefile.am
src/audio/alsa/SDL_alsa_audio.c
     1.1 --- a/configure.in	Tue Mar 02 12:45:22 2004 +0000
     1.2 +++ b/configure.in	Tue Mar 02 12:49:16 2004 +0000
     1.3 @@ -295,8 +295,22 @@
     1.4          AC_CHECK_LIB(asound, snd_pcm_open, have_alsa=yes)
     1.5          ])
     1.6          if test x$have_alsa = xyes; then
     1.7 -            CFLAGS="$CFLAGS -DALSA_SUPPORT"
     1.8 -            SYSTEM_LIBS="$SYSTEM_LIBS -lasound"
     1.9 +            AC_ARG_ENABLE(alsa-shared,
    1.10 +[  --enable-alsa-shared     dynamically load ALSA audio support [default=yes]],
    1.11 +                          , enable_alsa_shared=yes)
    1.12 +            alsa_lib=`ls /usr/lib/libasound.so.* | head -1 | sed 's/.*\/\(.*\)/\1/'`
    1.13 +            if test x$use_dlopen != xyes && \
    1.14 +               test x$enable_alsa_shared = xyes; then
    1.15 +                AC_MSG_ERROR([You must have dlopen() support and use the --enable-dlopen option])
    1.16 +            fi
    1.17 +            if test x$use_dlopen = xyes && \
    1.18 +               test x$enable_alsa_shared = xyes && test x$alsa_lib != x; then
    1.19 +                CFLAGS="$CFLAGS -DALSA_SUPPORT -DALSA_DYNAMIC=\$(alsa_lib)"
    1.20 +                AC_SUBST(alsa_lib)
    1.21 +            else
    1.22 +                CFLAGS="$CFLAGS -DALSA_SUPPORT"
    1.23 +                SYSTEM_LIBS="$SYSTEM_LIBS -lasound"
    1.24 +            fi
    1.25              AUDIO_SUBDIRS="$AUDIO_SUBDIRS alsa"
    1.26              AUDIO_DRIVERS="$AUDIO_DRIVERS alsa/libaudio_alsa.la"
    1.27          else
     2.1 --- a/src/audio/alsa/Makefile.am	Tue Mar 02 12:45:22 2004 +0000
     2.2 +++ b/src/audio/alsa/Makefile.am	Tue Mar 02 12:49:16 2004 +0000
     2.3 @@ -4,6 +4,8 @@
     2.4  noinst_LTLIBRARIES = libaudio_alsa.la
     2.5  libaudio_alsa_la_SOURCES = $(SRCS)
     2.6  
     2.7 +alsa_lib = \"@alsa_lib@\"
     2.8 +
     2.9  # The SDL audio driver sources
    2.10  SRCS =	SDL_alsa_audio.c	\
    2.11  	SDL_alsa_audio.h
     3.1 --- a/src/audio/alsa/SDL_alsa_audio.c	Tue Mar 02 12:45:22 2004 +0000
     3.2 +++ b/src/audio/alsa/SDL_alsa_audio.c	Tue Mar 02 12:49:16 2004 +0000
     3.3 @@ -41,6 +41,16 @@
     3.4  #include "SDL_timer.h"
     3.5  #include "SDL_alsa_audio.h"
     3.6  
     3.7 +#ifdef ALSA_DYNAMIC
     3.8 +#define __USE_GNU
     3.9 +#include <dlfcn.h>
    3.10 +#include "SDL_name.h"
    3.11 +#include "SDL_loadso.h"
    3.12 +#else
    3.13 +#define SDL_NAME(X)	X
    3.14 +#endif
    3.15 +
    3.16 +
    3.17  /* The tag name used by ALSA audio */
    3.18  #define DRIVER_NAME         "alsa"
    3.19  
    3.20 @@ -54,6 +64,99 @@
    3.21  static Uint8 *ALSA_GetAudioBuf(_THIS);
    3.22  static void ALSA_CloseAudio(_THIS);
    3.23  
    3.24 +#ifdef ALSA_DYNAMIC
    3.25 +
    3.26 +static const char *alsa_library = ALSA_DYNAMIC;
    3.27 +static void *alsa_handle = NULL;
    3.28 +static int alsa_loaded = 0;
    3.29 +
    3.30 +static int (*SDL_snd_pcm_open)(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode);
    3.31 +static int (*SDL_NAME(snd_pcm_open))(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode);
    3.32 +static int (*SDL_NAME(snd_pcm_close))(snd_pcm_t *pcm);
    3.33 +static snd_pcm_sframes_t (*SDL_NAME(snd_pcm_writei))(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size);
    3.34 +static int (*SDL_NAME(snd_pcm_resume))(snd_pcm_t *pcm);
    3.35 +static int (*SDL_NAME(snd_pcm_prepare))(snd_pcm_t *pcm);
    3.36 +static int (*SDL_NAME(snd_pcm_drain))(snd_pcm_t *pcm);
    3.37 +static const char *(*SDL_NAME(snd_strerror))(int errnum);
    3.38 +static size_t (*SDL_NAME(snd_pcm_hw_params_sizeof))(void);
    3.39 +static int (*SDL_NAME(snd_pcm_hw_params_any))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
    3.40 +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);
    3.41 +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);
    3.42 +static int (*SDL_NAME(snd_pcm_hw_params_set_channels))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val);
    3.43 +static int (*SDL_NAME(snd_pcm_hw_params_get_channels))(const snd_pcm_hw_params_t *params);
    3.44 +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);
    3.45 +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);
    3.46 +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);
    3.47 +static int (*SDL_NAME(snd_pcm_hw_params))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
    3.48 +static int (*SDL_NAME(snd_pcm_nonblock))(snd_pcm_t *pcm, int nonblock);
    3.49 +#define snd_pcm_hw_params_sizeof SDL_NAME(snd_pcm_hw_params_sizeof)
    3.50 +
    3.51 +static struct {
    3.52 +	const char *name;
    3.53 +	void **func;
    3.54 +} alsa_functions[] = {
    3.55 +	{ "snd_pcm_open",	(void**)&SDL_NAME(snd_pcm_open)		},
    3.56 +	{ "snd_pcm_close",	(void**)&SDL_NAME(snd_pcm_close)	},
    3.57 +	{ "snd_pcm_writei",	(void**)&SDL_NAME(snd_pcm_writei)	},
    3.58 +	{ "snd_pcm_resume",	(void**)&SDL_NAME(snd_pcm_resume)	},
    3.59 +	{ "snd_pcm_prepare",	(void**)&SDL_NAME(snd_pcm_prepare)	},
    3.60 +	{ "snd_pcm_drain",	(void**)&SDL_NAME(snd_pcm_drain)	},
    3.61 +	{ "snd_strerror",	(void**)&SDL_NAME(snd_strerror)		},
    3.62 +	{ "snd_pcm_hw_params_sizeof",		(void**)&SDL_NAME(snd_pcm_hw_params_sizeof)		},
    3.63 +	{ "snd_pcm_hw_params_any",		(void**)&SDL_NAME(snd_pcm_hw_params_any)		},
    3.64 +	{ "snd_pcm_hw_params_set_access",	(void**)&SDL_NAME(snd_pcm_hw_params_set_access)		},
    3.65 +	{ "snd_pcm_hw_params_set_format",	(void**)&SDL_NAME(snd_pcm_hw_params_set_format)		},
    3.66 +	{ "snd_pcm_hw_params_set_channels",	(void**)&SDL_NAME(snd_pcm_hw_params_set_channels)	},
    3.67 +	{ "snd_pcm_hw_params_get_channels",	(void**)&SDL_NAME(snd_pcm_hw_params_get_channels)	},
    3.68 +	{ "snd_pcm_hw_params_set_rate_near",	(void**)&SDL_NAME(snd_pcm_hw_params_set_rate_near)	},
    3.69 +	{ "snd_pcm_hw_params_set_period_size_near",	(void**)&SDL_NAME(snd_pcm_hw_params_set_period_size_near)	},
    3.70 +	{ "snd_pcm_hw_params_set_periods_near",	(void**)&SDL_NAME(snd_pcm_hw_params_set_periods_near)	},
    3.71 +	{ "snd_pcm_hw_params",	(void**)&SDL_NAME(snd_pcm_hw_params)	},
    3.72 +	{ "snd_pcm_nonblock",	(void**)&SDL_NAME(snd_pcm_nonblock)	},
    3.73 +};
    3.74 +
    3.75 +static void UnloadALSALibrary(void) {
    3.76 +	if (alsa_loaded) {
    3.77 +/*		SDL_UnloadObject(alsa_handle);*/
    3.78 +		dlclose(alsa_handle);
    3.79 +		alsa_handle = NULL;
    3.80 +		alsa_loaded = 0;
    3.81 +	}
    3.82 +}
    3.83 +
    3.84 +static int LoadALSALibrary(void) {
    3.85 +	int i, retval = -1;
    3.86 +
    3.87 +/*	alsa_handle = SDL_LoadObject(alsa_library);*/
    3.88 +	alsa_handle = dlopen(alsa_library,RTLD_NOW);
    3.89 +	if (alsa_handle) {
    3.90 +		alsa_loaded = 1;
    3.91 +		retval = 0;
    3.92 +		for (i = 0; i < SDL_TABLESIZE(alsa_functions); i++) {
    3.93 +/*			*alsa_functions[i].func = SDL_LoadFunction(alsa_handle,alsa_functions[i].name);*/
    3.94 +			*alsa_functions[i].func = dlvsym(alsa_handle,alsa_functions[i].name,"ALSA_0.9");
    3.95 +			if (!*alsa_functions[i].func) {
    3.96 +				retval = -1;
    3.97 +				UnloadALSALibrary();
    3.98 +				break;
    3.99 +			}
   3.100 +		}
   3.101 +	}
   3.102 +	return retval;
   3.103 +}
   3.104 +
   3.105 +#else
   3.106 +
   3.107 +static void UnloadALSALibrary(void) {
   3.108 +	return;
   3.109 +}
   3.110 +
   3.111 +static int LoadALSALibrary(void) {
   3.112 +	return 0;
   3.113 +}
   3.114 +
   3.115 +#endif /* ALSA_DYNAMIC */
   3.116 +
   3.117  static const char *get_audio_device()
   3.118  {
   3.119  	const char *device;
   3.120 @@ -74,11 +177,15 @@
   3.121  	snd_pcm_t *handle;
   3.122  
   3.123  	available = 0;
   3.124 -	status = snd_pcm_open(&handle, get_audio_device(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
   3.125 +	if (LoadALSALibrary() < 0) {
   3.126 +		return available;
   3.127 +	}
   3.128 +	status = SDL_NAME(snd_pcm_open)(&handle, get_audio_device(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
   3.129  	if ( status >= 0 ) {
   3.130  		available = 1;
   3.131 -        	snd_pcm_close(handle);
   3.132 +        	SDL_NAME(snd_pcm_close)(handle);
   3.133  	}
   3.134 +	UnloadALSALibrary();
   3.135  	return(available);
   3.136  }
   3.137  
   3.138 @@ -86,6 +193,7 @@
   3.139  {
   3.140  	free(device->hidden);
   3.141  	free(device);
   3.142 +	UnloadALSALibrary();
   3.143  }
   3.144  
   3.145  static SDL_AudioDevice *Audio_CreateDevice(int devindex)
   3.146 @@ -93,6 +201,7 @@
   3.147  	SDL_AudioDevice *this;
   3.148  
   3.149  	/* Initialize all variables that we clean on shutdown */
   3.150 +	LoadALSALibrary();
   3.151  	this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
   3.152  	if ( this ) {
   3.153  		memset(this, 0, (sizeof *this));
   3.154 @@ -150,7 +259,7 @@
   3.155  	sample_len = this->spec.samples;
   3.156  	sample_buf = (signed short *)mixbuf;
   3.157  	while ( sample_len > 0 ) {
   3.158 -		status = snd_pcm_writei(pcm_handle, sample_buf, sample_len);
   3.159 +		status = SDL_NAME(snd_pcm_writei)(pcm_handle, sample_buf, sample_len);
   3.160  		if ( status < 0 ) {
   3.161  			if ( status == -EAGAIN ) {
   3.162  				SDL_Delay(1);
   3.163 @@ -159,11 +268,11 @@
   3.164  			if ( status == -ESTRPIPE ) {
   3.165  				do {
   3.166  					SDL_Delay(1);
   3.167 -					status = snd_pcm_resume(pcm_handle);
   3.168 +					status = SDL_NAME(snd_pcm_resume)(pcm_handle);
   3.169  				} while ( status == -EAGAIN );
   3.170  			}
   3.171  			if ( status < 0 ) {
   3.172 -				status = snd_pcm_prepare(pcm_handle);
   3.173 +				status = SDL_NAME(snd_pcm_prepare)(pcm_handle);
   3.174  			}
   3.175  			if ( status < 0 ) {
   3.176  				/* Hmm, not much we can do - abort */
   3.177 @@ -189,8 +298,8 @@
   3.178  		mixbuf = NULL;
   3.179  	}
   3.180  	if ( pcm_handle ) {
   3.181 -		snd_pcm_drain(pcm_handle);
   3.182 -		snd_pcm_close(pcm_handle);
   3.183 +		SDL_NAME(snd_pcm_drain)(pcm_handle);
   3.184 +		SDL_NAME(snd_pcm_close)(pcm_handle);
   3.185  		pcm_handle = NULL;
   3.186  	}
   3.187  }
   3.188 @@ -204,25 +313,25 @@
   3.189  	Uint16               test_format;
   3.190  
   3.191  	/* Open the audio device */
   3.192 -	status = snd_pcm_open(&pcm_handle, get_audio_device(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
   3.193 +	status = SDL_NAME(snd_pcm_open)(&pcm_handle, get_audio_device(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
   3.194  	if ( status < 0 ) {
   3.195 -		SDL_SetError("Couldn't open audio device: %s", snd_strerror(status));
   3.196 +		SDL_SetError("Couldn't open audio device: %s", SDL_NAME(snd_strerror)(status));
   3.197  		return(-1);
   3.198  	}
   3.199  
   3.200  	/* Figure out what the hardware is capable of */
   3.201  	snd_pcm_hw_params_alloca(&params);
   3.202 -	status = snd_pcm_hw_params_any(pcm_handle, params);
   3.203 +	status = SDL_NAME(snd_pcm_hw_params_any)(pcm_handle, params);
   3.204  	if ( status < 0 ) {
   3.205 -		SDL_SetError("Couldn't get hardware config: %s", snd_strerror(status));
   3.206 +		SDL_SetError("Couldn't get hardware config: %s", SDL_NAME(snd_strerror)(status));
   3.207  		ALSA_CloseAudio(this);
   3.208  		return(-1);
   3.209  	}
   3.210  
   3.211  	/* SDL only uses interleaved sample output */
   3.212 -	status = snd_pcm_hw_params_set_access(pcm_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
   3.213 +	status = SDL_NAME(snd_pcm_hw_params_set_access)(pcm_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
   3.214  	if ( status < 0 ) {
   3.215 -		SDL_SetError("Couldn't set interleaved access: %s", snd_strerror(status));
   3.216 +		SDL_SetError("Couldn't set interleaved access: %s", SDL_NAME(snd_strerror)(status));
   3.217  		ALSA_CloseAudio(this);
   3.218  		return(-1);
   3.219  	}
   3.220 @@ -255,7 +364,7 @@
   3.221  				break;
   3.222  		}
   3.223  		if ( format != 0 ) {
   3.224 -			status = snd_pcm_hw_params_set_format(pcm_handle, params, format);
   3.225 +			status = SDL_NAME(snd_pcm_hw_params_set_format)(pcm_handle, params, format);
   3.226  		}
   3.227  		if ( status < 0 ) {
   3.228  			test_format = SDL_NextAudioFormat();
   3.229 @@ -269,9 +378,9 @@
   3.230  	spec->format = test_format;
   3.231  
   3.232  	/* Set the number of channels */
   3.233 -	status = snd_pcm_hw_params_set_channels(pcm_handle, params, spec->channels);
   3.234 +	status = SDL_NAME(snd_pcm_hw_params_set_channels)(pcm_handle, params, spec->channels);
   3.235  	if ( status < 0 ) {
   3.236 -		status = snd_pcm_hw_params_get_channels(params);
   3.237 +		status = SDL_NAME(snd_pcm_hw_params_get_channels)(params);
   3.238  		if ( (status <= 0) || (status > 2) ) {
   3.239  			SDL_SetError("Couldn't set audio channels");
   3.240  			ALSA_CloseAudio(this);
   3.241 @@ -281,9 +390,9 @@
   3.242  	}
   3.243  
   3.244  	/* Set the audio rate */
   3.245 -	status = snd_pcm_hw_params_set_rate_near(pcm_handle, params, spec->freq, NULL);
   3.246 +	status = SDL_NAME(snd_pcm_hw_params_set_rate_near)(pcm_handle, params, spec->freq, NULL);
   3.247  	if ( status < 0 ) {
   3.248 -		SDL_SetError("Couldn't set audio frequency: %s", snd_strerror(status));
   3.249 +		SDL_SetError("Couldn't set audio frequency: %s", SDL_NAME(snd_strerror)(status));
   3.250  		ALSA_CloseAudio(this);
   3.251  		return(-1);
   3.252  	}
   3.253 @@ -291,14 +400,14 @@
   3.254  
   3.255  	/* Set the buffer size, in samples */
   3.256  	frames = spec->samples;
   3.257 -	frames = snd_pcm_hw_params_set_period_size_near(pcm_handle, params, frames, NULL);
   3.258 +	frames = SDL_NAME(snd_pcm_hw_params_set_period_size_near)(pcm_handle, params, frames, NULL);
   3.259  	spec->samples = frames;
   3.260 -	snd_pcm_hw_params_set_periods_near(pcm_handle, params, 2, NULL);
   3.261 +	SDL_NAME(snd_pcm_hw_params_set_periods_near)(pcm_handle, params, 2, NULL);
   3.262  
   3.263  	/* "set" the hardware with the desired parameters */
   3.264 -	status = snd_pcm_hw_params(pcm_handle, params);
   3.265 +	status = SDL_NAME(snd_pcm_hw_params)(pcm_handle, params);
   3.266  	if ( status < 0 ) {
   3.267 -		SDL_SetError("Couldn't set audio parameters: %s", snd_strerror(status));
   3.268 +		SDL_SetError("Couldn't set audio parameters: %s", SDL_NAME(snd_strerror)(status));
   3.269  		ALSA_CloseAudio(this);
   3.270  		return(-1);
   3.271  	}
   3.272 @@ -319,7 +428,7 @@
   3.273  	parent = getpid();
   3.274  
   3.275  	/* Switch to blocking mode for playback */
   3.276 -	snd_pcm_nonblock(pcm_handle, 0);
   3.277 +	SDL_NAME(snd_pcm_nonblock)(pcm_handle, 0);
   3.278  
   3.279  	/* We're ready to rock and roll. :-) */
   3.280  	return(0);