comparison src/audio/alsa/SDL_alsa_audio.c @ 865:92615154bb68

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.
author Sam Lantinga <slouken@libsdl.org>
date Tue, 02 Mar 2004 12:49:16 +0000
parents b8d311d90021
children c7c04f811994
comparison
equal deleted inserted replaced
864:0c892e99b65b 865:92615154bb68
39 #include "SDL_audiomem.h" 39 #include "SDL_audiomem.h"
40 #include "SDL_audio_c.h" 40 #include "SDL_audio_c.h"
41 #include "SDL_timer.h" 41 #include "SDL_timer.h"
42 #include "SDL_alsa_audio.h" 42 #include "SDL_alsa_audio.h"
43 43
44 #ifdef ALSA_DYNAMIC
45 #define __USE_GNU
46 #include <dlfcn.h>
47 #include "SDL_name.h"
48 #include "SDL_loadso.h"
49 #else
50 #define SDL_NAME(X) X
51 #endif
52
53
44 /* The tag name used by ALSA audio */ 54 /* The tag name used by ALSA audio */
45 #define DRIVER_NAME "alsa" 55 #define DRIVER_NAME "alsa"
46 56
47 /* The default ALSA audio driver */ 57 /* The default ALSA audio driver */
48 #define DEFAULT_DEVICE "default" 58 #define DEFAULT_DEVICE "default"
52 static void ALSA_WaitAudio(_THIS); 62 static void ALSA_WaitAudio(_THIS);
53 static void ALSA_PlayAudio(_THIS); 63 static void ALSA_PlayAudio(_THIS);
54 static Uint8 *ALSA_GetAudioBuf(_THIS); 64 static Uint8 *ALSA_GetAudioBuf(_THIS);
55 static void ALSA_CloseAudio(_THIS); 65 static void ALSA_CloseAudio(_THIS);
56 66
67 #ifdef ALSA_DYNAMIC
68
69 static const char *alsa_library = ALSA_DYNAMIC;
70 static void *alsa_handle = NULL;
71 static int alsa_loaded = 0;
72
73 static int (*SDL_snd_pcm_open)(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode);
74 static int (*SDL_NAME(snd_pcm_open))(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode);
75 static int (*SDL_NAME(snd_pcm_close))(snd_pcm_t *pcm);
76 static snd_pcm_sframes_t (*SDL_NAME(snd_pcm_writei))(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size);
77 static int (*SDL_NAME(snd_pcm_resume))(snd_pcm_t *pcm);
78 static int (*SDL_NAME(snd_pcm_prepare))(snd_pcm_t *pcm);
79 static int (*SDL_NAME(snd_pcm_drain))(snd_pcm_t *pcm);
80 static const char *(*SDL_NAME(snd_strerror))(int errnum);
81 static size_t (*SDL_NAME(snd_pcm_hw_params_sizeof))(void);
82 static int (*SDL_NAME(snd_pcm_hw_params_any))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
83 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);
84 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);
85 static int (*SDL_NAME(snd_pcm_hw_params_set_channels))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val);
86 static int (*SDL_NAME(snd_pcm_hw_params_get_channels))(const snd_pcm_hw_params_t *params);
87 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);
88 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);
89 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);
90 static int (*SDL_NAME(snd_pcm_hw_params))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
91 static int (*SDL_NAME(snd_pcm_nonblock))(snd_pcm_t *pcm, int nonblock);
92 #define snd_pcm_hw_params_sizeof SDL_NAME(snd_pcm_hw_params_sizeof)
93
94 static struct {
95 const char *name;
96 void **func;
97 } alsa_functions[] = {
98 { "snd_pcm_open", (void**)&SDL_NAME(snd_pcm_open) },
99 { "snd_pcm_close", (void**)&SDL_NAME(snd_pcm_close) },
100 { "snd_pcm_writei", (void**)&SDL_NAME(snd_pcm_writei) },
101 { "snd_pcm_resume", (void**)&SDL_NAME(snd_pcm_resume) },
102 { "snd_pcm_prepare", (void**)&SDL_NAME(snd_pcm_prepare) },
103 { "snd_pcm_drain", (void**)&SDL_NAME(snd_pcm_drain) },
104 { "snd_strerror", (void**)&SDL_NAME(snd_strerror) },
105 { "snd_pcm_hw_params_sizeof", (void**)&SDL_NAME(snd_pcm_hw_params_sizeof) },
106 { "snd_pcm_hw_params_any", (void**)&SDL_NAME(snd_pcm_hw_params_any) },
107 { "snd_pcm_hw_params_set_access", (void**)&SDL_NAME(snd_pcm_hw_params_set_access) },
108 { "snd_pcm_hw_params_set_format", (void**)&SDL_NAME(snd_pcm_hw_params_set_format) },
109 { "snd_pcm_hw_params_set_channels", (void**)&SDL_NAME(snd_pcm_hw_params_set_channels) },
110 { "snd_pcm_hw_params_get_channels", (void**)&SDL_NAME(snd_pcm_hw_params_get_channels) },
111 { "snd_pcm_hw_params_set_rate_near", (void**)&SDL_NAME(snd_pcm_hw_params_set_rate_near) },
112 { "snd_pcm_hw_params_set_period_size_near", (void**)&SDL_NAME(snd_pcm_hw_params_set_period_size_near) },
113 { "snd_pcm_hw_params_set_periods_near", (void**)&SDL_NAME(snd_pcm_hw_params_set_periods_near) },
114 { "snd_pcm_hw_params", (void**)&SDL_NAME(snd_pcm_hw_params) },
115 { "snd_pcm_nonblock", (void**)&SDL_NAME(snd_pcm_nonblock) },
116 };
117
118 static void UnloadALSALibrary(void) {
119 if (alsa_loaded) {
120 /* SDL_UnloadObject(alsa_handle);*/
121 dlclose(alsa_handle);
122 alsa_handle = NULL;
123 alsa_loaded = 0;
124 }
125 }
126
127 static int LoadALSALibrary(void) {
128 int i, retval = -1;
129
130 /* alsa_handle = SDL_LoadObject(alsa_library);*/
131 alsa_handle = dlopen(alsa_library,RTLD_NOW);
132 if (alsa_handle) {
133 alsa_loaded = 1;
134 retval = 0;
135 for (i = 0; i < SDL_TABLESIZE(alsa_functions); i++) {
136 /* *alsa_functions[i].func = SDL_LoadFunction(alsa_handle,alsa_functions[i].name);*/
137 *alsa_functions[i].func = dlvsym(alsa_handle,alsa_functions[i].name,"ALSA_0.9");
138 if (!*alsa_functions[i].func) {
139 retval = -1;
140 UnloadALSALibrary();
141 break;
142 }
143 }
144 }
145 return retval;
146 }
147
148 #else
149
150 static void UnloadALSALibrary(void) {
151 return;
152 }
153
154 static int LoadALSALibrary(void) {
155 return 0;
156 }
157
158 #endif /* ALSA_DYNAMIC */
159
57 static const char *get_audio_device() 160 static const char *get_audio_device()
58 { 161 {
59 const char *device; 162 const char *device;
60 163
61 device = getenv("AUDIODEV"); /* Is there a standard variable name? */ 164 device = getenv("AUDIODEV"); /* Is there a standard variable name? */
72 int available; 175 int available;
73 int status; 176 int status;
74 snd_pcm_t *handle; 177 snd_pcm_t *handle;
75 178
76 available = 0; 179 available = 0;
77 status = snd_pcm_open(&handle, get_audio_device(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); 180 if (LoadALSALibrary() < 0) {
181 return available;
182 }
183 status = SDL_NAME(snd_pcm_open)(&handle, get_audio_device(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
78 if ( status >= 0 ) { 184 if ( status >= 0 ) {
79 available = 1; 185 available = 1;
80 snd_pcm_close(handle); 186 SDL_NAME(snd_pcm_close)(handle);
81 } 187 }
188 UnloadALSALibrary();
82 return(available); 189 return(available);
83 } 190 }
84 191
85 static void Audio_DeleteDevice(SDL_AudioDevice *device) 192 static void Audio_DeleteDevice(SDL_AudioDevice *device)
86 { 193 {
87 free(device->hidden); 194 free(device->hidden);
88 free(device); 195 free(device);
196 UnloadALSALibrary();
89 } 197 }
90 198
91 static SDL_AudioDevice *Audio_CreateDevice(int devindex) 199 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
92 { 200 {
93 SDL_AudioDevice *this; 201 SDL_AudioDevice *this;
94 202
95 /* Initialize all variables that we clean on shutdown */ 203 /* Initialize all variables that we clean on shutdown */
204 LoadALSALibrary();
96 this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice)); 205 this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
97 if ( this ) { 206 if ( this ) {
98 memset(this, 0, (sizeof *this)); 207 memset(this, 0, (sizeof *this));
99 this->hidden = (struct SDL_PrivateAudioData *) 208 this->hidden = (struct SDL_PrivateAudioData *)
100 malloc((sizeof *this->hidden)); 209 malloc((sizeof *this->hidden));
148 signed short *sample_buf; 257 signed short *sample_buf;
149 258
150 sample_len = this->spec.samples; 259 sample_len = this->spec.samples;
151 sample_buf = (signed short *)mixbuf; 260 sample_buf = (signed short *)mixbuf;
152 while ( sample_len > 0 ) { 261 while ( sample_len > 0 ) {
153 status = snd_pcm_writei(pcm_handle, sample_buf, sample_len); 262 status = SDL_NAME(snd_pcm_writei)(pcm_handle, sample_buf, sample_len);
154 if ( status < 0 ) { 263 if ( status < 0 ) {
155 if ( status == -EAGAIN ) { 264 if ( status == -EAGAIN ) {
156 SDL_Delay(1); 265 SDL_Delay(1);
157 continue; 266 continue;
158 } 267 }
159 if ( status == -ESTRPIPE ) { 268 if ( status == -ESTRPIPE ) {
160 do { 269 do {
161 SDL_Delay(1); 270 SDL_Delay(1);
162 status = snd_pcm_resume(pcm_handle); 271 status = SDL_NAME(snd_pcm_resume)(pcm_handle);
163 } while ( status == -EAGAIN ); 272 } while ( status == -EAGAIN );
164 } 273 }
165 if ( status < 0 ) { 274 if ( status < 0 ) {
166 status = snd_pcm_prepare(pcm_handle); 275 status = SDL_NAME(snd_pcm_prepare)(pcm_handle);
167 } 276 }
168 if ( status < 0 ) { 277 if ( status < 0 ) {
169 /* Hmm, not much we can do - abort */ 278 /* Hmm, not much we can do - abort */
170 this->enabled = 0; 279 this->enabled = 0;
171 return; 280 return;
187 if ( mixbuf != NULL ) { 296 if ( mixbuf != NULL ) {
188 SDL_FreeAudioMem(mixbuf); 297 SDL_FreeAudioMem(mixbuf);
189 mixbuf = NULL; 298 mixbuf = NULL;
190 } 299 }
191 if ( pcm_handle ) { 300 if ( pcm_handle ) {
192 snd_pcm_drain(pcm_handle); 301 SDL_NAME(snd_pcm_drain)(pcm_handle);
193 snd_pcm_close(pcm_handle); 302 SDL_NAME(snd_pcm_close)(pcm_handle);
194 pcm_handle = NULL; 303 pcm_handle = NULL;
195 } 304 }
196 } 305 }
197 306
198 static int ALSA_OpenAudio(_THIS, SDL_AudioSpec *spec) 307 static int ALSA_OpenAudio(_THIS, SDL_AudioSpec *spec)
202 snd_pcm_format_t format; 311 snd_pcm_format_t format;
203 snd_pcm_uframes_t frames; 312 snd_pcm_uframes_t frames;
204 Uint16 test_format; 313 Uint16 test_format;
205 314
206 /* Open the audio device */ 315 /* Open the audio device */
207 status = snd_pcm_open(&pcm_handle, get_audio_device(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); 316 status = SDL_NAME(snd_pcm_open)(&pcm_handle, get_audio_device(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
208 if ( status < 0 ) { 317 if ( status < 0 ) {
209 SDL_SetError("Couldn't open audio device: %s", snd_strerror(status)); 318 SDL_SetError("Couldn't open audio device: %s", SDL_NAME(snd_strerror)(status));
210 return(-1); 319 return(-1);
211 } 320 }
212 321
213 /* Figure out what the hardware is capable of */ 322 /* Figure out what the hardware is capable of */
214 snd_pcm_hw_params_alloca(&params); 323 snd_pcm_hw_params_alloca(&params);
215 status = snd_pcm_hw_params_any(pcm_handle, params); 324 status = SDL_NAME(snd_pcm_hw_params_any)(pcm_handle, params);
216 if ( status < 0 ) { 325 if ( status < 0 ) {
217 SDL_SetError("Couldn't get hardware config: %s", snd_strerror(status)); 326 SDL_SetError("Couldn't get hardware config: %s", SDL_NAME(snd_strerror)(status));
218 ALSA_CloseAudio(this); 327 ALSA_CloseAudio(this);
219 return(-1); 328 return(-1);
220 } 329 }
221 330
222 /* SDL only uses interleaved sample output */ 331 /* SDL only uses interleaved sample output */
223 status = snd_pcm_hw_params_set_access(pcm_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); 332 status = SDL_NAME(snd_pcm_hw_params_set_access)(pcm_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
224 if ( status < 0 ) { 333 if ( status < 0 ) {
225 SDL_SetError("Couldn't set interleaved access: %s", snd_strerror(status)); 334 SDL_SetError("Couldn't set interleaved access: %s", SDL_NAME(snd_strerror)(status));
226 ALSA_CloseAudio(this); 335 ALSA_CloseAudio(this);
227 return(-1); 336 return(-1);
228 } 337 }
229 338
230 /* Try for a closest match on audio format */ 339 /* Try for a closest match on audio format */
253 default: 362 default:
254 format = 0; 363 format = 0;
255 break; 364 break;
256 } 365 }
257 if ( format != 0 ) { 366 if ( format != 0 ) {
258 status = snd_pcm_hw_params_set_format(pcm_handle, params, format); 367 status = SDL_NAME(snd_pcm_hw_params_set_format)(pcm_handle, params, format);
259 } 368 }
260 if ( status < 0 ) { 369 if ( status < 0 ) {
261 test_format = SDL_NextAudioFormat(); 370 test_format = SDL_NextAudioFormat();
262 } 371 }
263 } 372 }
267 return(-1); 376 return(-1);
268 } 377 }
269 spec->format = test_format; 378 spec->format = test_format;
270 379
271 /* Set the number of channels */ 380 /* Set the number of channels */
272 status = snd_pcm_hw_params_set_channels(pcm_handle, params, spec->channels); 381 status = SDL_NAME(snd_pcm_hw_params_set_channels)(pcm_handle, params, spec->channels);
273 if ( status < 0 ) { 382 if ( status < 0 ) {
274 status = snd_pcm_hw_params_get_channels(params); 383 status = SDL_NAME(snd_pcm_hw_params_get_channels)(params);
275 if ( (status <= 0) || (status > 2) ) { 384 if ( (status <= 0) || (status > 2) ) {
276 SDL_SetError("Couldn't set audio channels"); 385 SDL_SetError("Couldn't set audio channels");
277 ALSA_CloseAudio(this); 386 ALSA_CloseAudio(this);
278 return(-1); 387 return(-1);
279 } 388 }
280 spec->channels = status; 389 spec->channels = status;
281 } 390 }
282 391
283 /* Set the audio rate */ 392 /* Set the audio rate */
284 status = snd_pcm_hw_params_set_rate_near(pcm_handle, params, spec->freq, NULL); 393 status = SDL_NAME(snd_pcm_hw_params_set_rate_near)(pcm_handle, params, spec->freq, NULL);
285 if ( status < 0 ) { 394 if ( status < 0 ) {
286 SDL_SetError("Couldn't set audio frequency: %s", snd_strerror(status)); 395 SDL_SetError("Couldn't set audio frequency: %s", SDL_NAME(snd_strerror)(status));
287 ALSA_CloseAudio(this); 396 ALSA_CloseAudio(this);
288 return(-1); 397 return(-1);
289 } 398 }
290 spec->freq = status; 399 spec->freq = status;
291 400
292 /* Set the buffer size, in samples */ 401 /* Set the buffer size, in samples */
293 frames = spec->samples; 402 frames = spec->samples;
294 frames = snd_pcm_hw_params_set_period_size_near(pcm_handle, params, frames, NULL); 403 frames = SDL_NAME(snd_pcm_hw_params_set_period_size_near)(pcm_handle, params, frames, NULL);
295 spec->samples = frames; 404 spec->samples = frames;
296 snd_pcm_hw_params_set_periods_near(pcm_handle, params, 2, NULL); 405 SDL_NAME(snd_pcm_hw_params_set_periods_near)(pcm_handle, params, 2, NULL);
297 406
298 /* "set" the hardware with the desired parameters */ 407 /* "set" the hardware with the desired parameters */
299 status = snd_pcm_hw_params(pcm_handle, params); 408 status = SDL_NAME(snd_pcm_hw_params)(pcm_handle, params);
300 if ( status < 0 ) { 409 if ( status < 0 ) {
301 SDL_SetError("Couldn't set audio parameters: %s", snd_strerror(status)); 410 SDL_SetError("Couldn't set audio parameters: %s", SDL_NAME(snd_strerror)(status));
302 ALSA_CloseAudio(this); 411 ALSA_CloseAudio(this);
303 return(-1); 412 return(-1);
304 } 413 }
305 414
306 /* Calculate the final parameters for this audio specification */ 415 /* Calculate the final parameters for this audio specification */
317 426
318 /* Get the parent process id (we're the parent of the audio thread) */ 427 /* Get the parent process id (we're the parent of the audio thread) */
319 parent = getpid(); 428 parent = getpid();
320 429
321 /* Switch to blocking mode for playback */ 430 /* Switch to blocking mode for playback */
322 snd_pcm_nonblock(pcm_handle, 0); 431 SDL_NAME(snd_pcm_nonblock)(pcm_handle, 0);
323 432
324 /* We're ready to rock and roll. :-) */ 433 /* We're ready to rock and roll. :-) */
325 return(0); 434 return(0);
326 } 435 }