Mercurial > sdl-ios-xcode
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(¶ms); | 323 snd_pcm_hw_params_alloca(¶ms); |
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 } |