Mercurial > sdl-ios-xcode
comparison 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 |
comparison
equal
deleted
inserted
replaced
2048:6067c7f9a672 | 2049:5f6550e5184f |
---|---|
23 | 23 |
24 /* Allow access to a raw mixing buffer */ | 24 /* Allow access to a raw mixing buffer */ |
25 | 25 |
26 #include <sys/types.h> | 26 #include <sys/types.h> |
27 #include <signal.h> /* For kill() */ | 27 #include <signal.h> /* For kill() */ |
28 #include <dlfcn.h> | |
29 #include <errno.h> | |
30 #include <string.h> | |
28 | 31 |
29 #include "SDL_timer.h" | 32 #include "SDL_timer.h" |
30 #include "SDL_audio.h" | 33 #include "SDL_audio.h" |
31 #include "../SDL_audiomem.h" | 34 #include "../SDL_audiomem.h" |
32 #include "../SDL_audio_c.h" | 35 #include "../SDL_audio_c.h" |
33 #include "SDL_alsa_audio.h" | 36 #include "SDL_alsa_audio.h" |
34 | 37 |
35 #ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC | |
36 #include <dlfcn.h> | |
37 #include "SDL_name.h" | |
38 #include "SDL_loadso.h" | |
39 #else | |
40 #define SDL_NAME(X) X | |
41 #endif | |
42 | |
43 | 38 |
44 /* The tag name used by ALSA audio */ | 39 /* The tag name used by ALSA audio */ |
45 #define DRIVER_NAME "alsa" | 40 #define DRIVER_NAME "alsa" |
46 | 41 |
47 /* The default ALSA audio driver */ | 42 /* The default ALSA audio driver */ |
48 #define DEFAULT_DEVICE "default" | 43 #define DEFAULT_DEVICE "default" |
49 | 44 |
50 /* Audio driver functions */ | 45 static int (*ALSA_snd_pcm_open) |
51 static int ALSA_OpenAudio(_THIS, SDL_AudioSpec * spec); | 46 (snd_pcm_t **, const char *, snd_pcm_stream_t, int); |
52 static void ALSA_WaitAudio(_THIS); | 47 static int (*ALSA_snd_pcm_close)(snd_pcm_t * pcm); |
53 static void ALSA_PlayAudio(_THIS); | 48 static snd_pcm_sframes_t(*ALSA_snd_pcm_writei) |
54 static Uint8 *ALSA_GetAudioBuf(_THIS); | 49 (snd_pcm_t *,const void *, snd_pcm_uframes_t); |
55 static void ALSA_CloseAudio(_THIS); | 50 static int (*ALSA_snd_pcm_resume)(snd_pcm_t *); |
51 static int (*ALSA_snd_pcm_prepare)(snd_pcm_t *); | |
52 static int (*ALSA_snd_pcm_drain)(snd_pcm_t *); | |
53 static const char *(*ALSA_snd_strerror)(int); | |
54 static size_t(*ALSA_snd_pcm_hw_params_sizeof)(void); | |
55 static size_t(*ALSA_snd_pcm_sw_params_sizeof)(void); | |
56 static int (*ALSA_snd_pcm_hw_params_any)(snd_pcm_t *, snd_pcm_hw_params_t *); | |
57 static int (*ALSA_snd_pcm_hw_params_set_access) | |
58 (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_access_t); | |
59 static int (*ALSA_snd_pcm_hw_params_set_format) | |
60 (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_format_t); | |
61 static int (*ALSA_snd_pcm_hw_params_set_channels) | |
62 (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int); | |
63 static int (*ALSA_snd_pcm_hw_params_get_channels)(const snd_pcm_hw_params_t *); | |
64 static unsigned int (*ALSA_snd_pcm_hw_params_set_rate_near) | |
65 (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int, int *); | |
66 static snd_pcm_uframes_t (*ALSA_snd_pcm_hw_params_set_period_size_near) | |
67 (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_uframes_t, int *); | |
68 static snd_pcm_sframes_t (*ALSA_snd_pcm_hw_params_get_period_size) | |
69 (const snd_pcm_hw_params_t *); | |
70 static unsigned int (*ALSA_snd_pcm_hw_params_set_periods_near) | |
71 (snd_pcm_t *,snd_pcm_hw_params_t *, unsigned int, int *); | |
72 static int (*ALSA_snd_pcm_hw_params_get_periods)(snd_pcm_hw_params_t *); | |
73 static int (*ALSA_snd_pcm_hw_params)(snd_pcm_t *, snd_pcm_hw_params_t *); | |
74 static int (*ALSA_snd_pcm_sw_params_current)(snd_pcm_t*, snd_pcm_sw_params_t*); | |
75 static int (*ALSA_snd_pcm_sw_params_set_start_threshold) | |
76 (snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t); | |
77 static int (*ALSA_snd_pcm_sw_params_set_avail_min) | |
78 (snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t); | |
79 static int (*ALSA_snd_pcm_sw_params)(snd_pcm_t *, snd_pcm_sw_params_t *); | |
80 static int (*ALSA_snd_pcm_nonblock)(snd_pcm_t *, int); | |
81 #define snd_pcm_hw_params_sizeof ALSA_snd_pcm_hw_params_sizeof | |
82 #define snd_pcm_sw_params_sizeof ALSA_snd_pcm_sw_params_sizeof | |
83 | |
56 | 84 |
57 #ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC | 85 #ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC |
58 | 86 |
59 static const char *alsa_library = SDL_AUDIO_DRIVER_ALSA_DYNAMIC; | 87 static const char *alsa_library = SDL_AUDIO_DRIVER_ALSA_DYNAMIC; |
60 static void *alsa_handle = NULL; | 88 static void *alsa_handle = NULL; |
61 static int alsa_loaded = 0; | 89 |
62 | 90 static int |
63 static int (*SDL_snd_pcm_open) (snd_pcm_t ** pcm, const char *name, | 91 load_alsa_sym(const char *fn, void **addr) |
64 snd_pcm_stream_t stream, int mode); | 92 { |
65 static int (*SDL_NAME(snd_pcm_open)) (snd_pcm_t ** pcm, const char *name, | 93 /* |
66 snd_pcm_stream_t stream, int mode); | 94 * !!! FIXME: |
67 static int (*SDL_NAME(snd_pcm_close)) (snd_pcm_t * pcm); | 95 * Eventually, this will deal with fallbacks, version changes, and |
68 static snd_pcm_sframes_t(*SDL_NAME(snd_pcm_writei)) (snd_pcm_t * pcm, | 96 * missing symbols we can workaround. But for now, it doesn't. |
69 const void *buffer, | 97 */ |
70 snd_pcm_uframes_t size); | 98 |
71 static int (*SDL_NAME(snd_pcm_resume)) (snd_pcm_t * pcm); | 99 #if HAVE_DLVSYM |
72 static int (*SDL_NAME(snd_pcm_prepare)) (snd_pcm_t * pcm); | 100 *addr = dlvsym(alsa_handle, fn, "ALSA_0.9"); |
73 static int (*SDL_NAME(snd_pcm_drain)) (snd_pcm_t * pcm); | 101 if (*addr == NULL) |
74 static const char *(*SDL_NAME(snd_strerror)) (int errnum); | 102 #endif |
75 static size_t(*SDL_NAME(snd_pcm_hw_params_sizeof)) (void); | 103 { |
76 static size_t(*SDL_NAME(snd_pcm_sw_params_sizeof)) (void); | 104 *addr = dlsym(alsa_handle, fn); |
77 static int (*SDL_NAME(snd_pcm_hw_params_any)) (snd_pcm_t * pcm, | 105 if (*addr == NULL) { |
78 snd_pcm_hw_params_t * params); | 106 SDL_SetError("dlsym('%s') failed: %s", fn, strerror(errno)); |
79 static int (*SDL_NAME(snd_pcm_hw_params_set_access)) (snd_pcm_t * pcm, | 107 return 0; |
80 snd_pcm_hw_params_t * | 108 } |
81 params, | 109 } |
82 snd_pcm_access_t | 110 |
83 access); | 111 return 1; |
84 static int (*SDL_NAME(snd_pcm_hw_params_set_format)) (snd_pcm_t * pcm, | 112 } |
85 snd_pcm_hw_params_t * | |
86 params, | |
87 snd_pcm_format_t val); | |
88 static int (*SDL_NAME(snd_pcm_hw_params_set_channels)) (snd_pcm_t * pcm, | |
89 snd_pcm_hw_params_t * | |
90 params, | |
91 unsigned int val); | |
92 static int (*SDL_NAME(snd_pcm_hw_params_get_channels)) (const | |
93 snd_pcm_hw_params_t * | |
94 params); | |
95 static unsigned int | |
96 (*SDL_NAME(snd_pcm_hw_params_set_rate_near)) (snd_pcm_t * | |
97 pcm, | |
98 snd_pcm_hw_params_t | |
99 * params, | |
100 unsigned int val, int *dir); | |
101 static snd_pcm_uframes_t | |
102 (*SDL_NAME(snd_pcm_hw_params_set_period_size_near)) (snd_pcm_t * pcm, | |
103 snd_pcm_hw_params_t | |
104 * params, | |
105 snd_pcm_uframes_t | |
106 val, int *dir); | |
107 static snd_pcm_sframes_t | |
108 (*SDL_NAME(snd_pcm_hw_params_get_period_size)) (const | |
109 snd_pcm_hw_params_t | |
110 * params); | |
111 static unsigned int | |
112 (*SDL_NAME(snd_pcm_hw_params_set_periods_near)) (snd_pcm_t * pcm, | |
113 snd_pcm_hw_params_t | |
114 * params, | |
115 unsigned int val, | |
116 int *dir); | |
117 static int (*SDL_NAME(snd_pcm_hw_params_get_periods)) (snd_pcm_hw_params_t * | |
118 params); | |
119 static int (*SDL_NAME(snd_pcm_hw_params)) (snd_pcm_t * pcm, | |
120 snd_pcm_hw_params_t * params); | |
121 /* | |
122 */ | |
123 static int (*SDL_NAME(snd_pcm_sw_params_current)) (snd_pcm_t * pcm, | |
124 snd_pcm_sw_params_t * | |
125 swparams); | |
126 static int (*SDL_NAME(snd_pcm_sw_params_set_start_threshold)) (snd_pcm_t * | |
127 pcm, | |
128 snd_pcm_sw_params_t | |
129 * params, | |
130 snd_pcm_uframes_t | |
131 val); | |
132 static int (*SDL_NAME(snd_pcm_sw_params_set_avail_min)) (snd_pcm_t * pcm, | |
133 snd_pcm_sw_params_t | |
134 * params, | |
135 snd_pcm_uframes_t | |
136 val); | |
137 static int (*SDL_NAME(snd_pcm_sw_params)) (snd_pcm_t * pcm, | |
138 snd_pcm_sw_params_t * params); | |
139 static int (*SDL_NAME(snd_pcm_nonblock)) (snd_pcm_t * pcm, int nonblock); | |
140 #define snd_pcm_hw_params_sizeof SDL_NAME(snd_pcm_hw_params_sizeof) | |
141 #define snd_pcm_sw_params_sizeof SDL_NAME(snd_pcm_sw_params_sizeof) | |
142 | 113 |
143 /* cast funcs to char* first, to please GCC's strict aliasing rules. */ | 114 /* cast funcs to char* first, to please GCC's strict aliasing rules. */ |
144 static struct | 115 #define SDL_ALSA_SYM(x) \ |
145 { | 116 if (!load_alsa_sym(#x, (void **) (char *) &ALSA_##x)) return -1 |
146 const char *name; | 117 #else |
147 void **func; | 118 #define SDL_ALSA_SYM(x) ALSA_##x = x |
148 } alsa_functions[] = { | 119 #endif |
149 { | 120 |
150 "snd_pcm_open", (void **) (char *) &SDL_NAME(snd_pcm_open)}, { | 121 static int load_alsa_syms(void) |
151 "snd_pcm_close", (void **) (char *) &SDL_NAME(snd_pcm_close)}, { | 122 { |
152 "snd_pcm_writei", (void **) (char *) &SDL_NAME(snd_pcm_writei)}, { | 123 SDL_ALSA_SYM(snd_pcm_open); |
153 "snd_pcm_resume", (void **) (char *) &SDL_NAME(snd_pcm_resume)}, { | 124 SDL_ALSA_SYM(snd_pcm_close); |
154 "snd_pcm_prepare", (void **) (char *) &SDL_NAME(snd_pcm_prepare)}, { | 125 SDL_ALSA_SYM(snd_pcm_writei); |
155 "snd_pcm_drain", (void **) (char *) &SDL_NAME(snd_pcm_drain)}, { | 126 SDL_ALSA_SYM(snd_pcm_resume); |
156 "snd_strerror", (void **) (char *) &SDL_NAME(snd_strerror)}, { | 127 SDL_ALSA_SYM(snd_pcm_prepare); |
157 "snd_pcm_hw_params_sizeof", | 128 SDL_ALSA_SYM(snd_pcm_drain); |
158 (void **) (char *) &SDL_NAME(snd_pcm_hw_params_sizeof)}, { | 129 SDL_ALSA_SYM(snd_strerror); |
159 "snd_pcm_sw_params_sizeof", | 130 SDL_ALSA_SYM(snd_pcm_hw_params_sizeof); |
160 (void **) (char *) &SDL_NAME(snd_pcm_sw_params_sizeof)}, { | 131 SDL_ALSA_SYM(snd_pcm_sw_params_sizeof); |
161 "snd_pcm_hw_params_any", | 132 SDL_ALSA_SYM(snd_pcm_hw_params_any); |
162 (void **) (char *) &SDL_NAME(snd_pcm_hw_params_any)}, { | 133 SDL_ALSA_SYM(snd_pcm_hw_params_set_access); |
163 "snd_pcm_hw_params_set_access", | 134 SDL_ALSA_SYM(snd_pcm_hw_params_set_format); |
164 (void **) (char *) &SDL_NAME(snd_pcm_hw_params_set_access)}, { | 135 SDL_ALSA_SYM(snd_pcm_hw_params_set_channels); |
165 "snd_pcm_hw_params_set_format", | 136 SDL_ALSA_SYM(snd_pcm_hw_params_get_channels); |
166 (void **) (char *) &SDL_NAME(snd_pcm_hw_params_set_format)}, { | 137 SDL_ALSA_SYM(snd_pcm_hw_params_set_rate_near); |
167 "snd_pcm_hw_params_set_channels", | 138 SDL_ALSA_SYM(snd_pcm_hw_params_set_period_size_near); |
168 (void **) (char *) &SDL_NAME(snd_pcm_hw_params_set_channels)}, { | 139 SDL_ALSA_SYM(snd_pcm_hw_params_get_period_size); |
169 "snd_pcm_hw_params_get_channels", | 140 SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_near); |
170 (void **) (char *) &SDL_NAME(snd_pcm_hw_params_get_channels)}, { | 141 SDL_ALSA_SYM(snd_pcm_hw_params_get_periods); |
171 "snd_pcm_hw_params_set_rate_near", | 142 SDL_ALSA_SYM(snd_pcm_hw_params); |
172 (void **) (char *) &SDL_NAME(snd_pcm_hw_params_set_rate_near)}, { | 143 SDL_ALSA_SYM(snd_pcm_sw_params_current); |
173 "snd_pcm_hw_params_set_period_size_near", (void **) (char *) | 144 SDL_ALSA_SYM(snd_pcm_sw_params_set_start_threshold); |
174 &SDL_NAME(snd_pcm_hw_params_set_period_size_near)}, { | 145 SDL_ALSA_SYM(snd_pcm_sw_params_set_avail_min); |
175 "snd_pcm_hw_params_get_period_size", | 146 SDL_ALSA_SYM(snd_pcm_sw_params); |
176 (void **) (char *) &SDL_NAME(snd_pcm_hw_params_get_period_size)}, | 147 SDL_ALSA_SYM(snd_pcm_nonblock); |
177 { | 148 return 0; |
178 "snd_pcm_hw_params_set_periods_near", (void **) (char *) | 149 } |
179 &SDL_NAME(snd_pcm_hw_params_set_periods_near)}, { | 150 #undef SDL_ALSA_SYM |
180 "snd_pcm_hw_params_get_periods", | 151 |
181 (void **) (char *) &SDL_NAME(snd_pcm_hw_params_get_periods)}, { | 152 #ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC |
182 "snd_pcm_hw_params", (void **) (char *) &SDL_NAME(snd_pcm_hw_params)}, { | |
183 "snd_pcm_sw_params_current", | |
184 (void **) (char *) &SDL_NAME(snd_pcm_sw_params_current)}, { | |
185 "snd_pcm_sw_params_set_start_threshold", (void **) (char *) | |
186 &SDL_NAME(snd_pcm_sw_params_set_start_threshold)}, { | |
187 "snd_pcm_sw_params_set_avail_min", | |
188 (void **) (char *) &SDL_NAME(snd_pcm_sw_params_set_avail_min)}, { | |
189 "snd_pcm_sw_params", (void **) (char *) &SDL_NAME(snd_pcm_sw_params)}, { | |
190 "snd_pcm_nonblock", (void **) (char *) &SDL_NAME(snd_pcm_nonblock)},}; | |
191 | 153 |
192 static void | 154 static void |
193 UnloadALSALibrary(void) | 155 UnloadALSALibrary(void) |
194 { | 156 { |
195 if (alsa_loaded) { | 157 if (alsa_handle != NULL) { |
196 /* SDL_UnloadObject(alsa_handle);*/ | |
197 dlclose(alsa_handle); | 158 dlclose(alsa_handle); |
198 alsa_handle = NULL; | 159 alsa_handle = NULL; |
199 alsa_loaded = 0; | |
200 } | 160 } |
201 } | 161 } |
202 | 162 |
203 static int | 163 static int |
204 LoadALSALibrary(void) | 164 LoadALSALibrary(void) |
205 { | 165 { |
206 int i, retval = -1; | 166 int retval = 0; |
207 | 167 if (alsa_handle == NULL) { |
208 /* alsa_handle = SDL_LoadObject(alsa_library);*/ | 168 alsa_handle = dlopen(alsa_library, RTLD_NOW); |
209 alsa_handle = dlopen(alsa_library, RTLD_NOW); | 169 if (alsa_handle == NULL) { |
210 if (alsa_handle) { | 170 retval = -1; |
211 alsa_loaded = 1; | 171 SDL_SetError("ALSA: dlopen('%s') failed: %s\n", |
212 retval = 0; | 172 alsa_library, strerror(errno)); |
213 for (i = 0; i < SDL_arraysize(alsa_functions); i++) { | 173 } else { |
214 /* *alsa_functions[i].func = SDL_LoadFunction(alsa_handle,alsa_functions[i].name);*/ | 174 retval = load_alsa_syms(); |
215 #if HAVE_DLVSYM | 175 if (retval < 0) { |
216 *alsa_functions[i].func = | |
217 dlvsym(alsa_handle, alsa_functions[i].name, "ALSA_0.9"); | |
218 if (!*alsa_functions[i].func) | |
219 #endif | |
220 *alsa_functions[i].func = | |
221 dlsym(alsa_handle, alsa_functions[i].name); | |
222 if (!*alsa_functions[i].func) { | |
223 retval = -1; | |
224 UnloadALSALibrary(); | 176 UnloadALSALibrary(); |
225 break; | |
226 } | 177 } |
227 } | 178 } |
228 } | 179 } |
229 return retval; | 180 return retval; |
230 } | 181 } |
232 #else | 183 #else |
233 | 184 |
234 static void | 185 static void |
235 UnloadALSALibrary(void) | 186 UnloadALSALibrary(void) |
236 { | 187 { |
237 return; | |
238 } | 188 } |
239 | 189 |
240 static int | 190 static int |
241 LoadALSALibrary(void) | 191 LoadALSALibrary(void) |
242 { | 192 { |
193 load_alsa_syms(); | |
243 return 0; | 194 return 0; |
244 } | 195 } |
245 | 196 |
246 #endif /* SDL_AUDIO_DRIVER_ALSA_DYNAMIC */ | 197 #endif /* SDL_AUDIO_DRIVER_ALSA_DYNAMIC */ |
247 | 198 |
260 device = DEFAULT_DEVICE; | 211 device = DEFAULT_DEVICE; |
261 } | 212 } |
262 return device; | 213 return device; |
263 } | 214 } |
264 | 215 |
265 /* Audio driver bootstrap functions */ | |
266 | |
267 static int | |
268 Audio_Available(void) | |
269 { | |
270 int available; | |
271 int status; | |
272 snd_pcm_t *handle; | |
273 | |
274 available = 0; | |
275 if (LoadALSALibrary() < 0) { | |
276 return available; | |
277 } | |
278 status = | |
279 SDL_NAME(snd_pcm_open) (&handle, get_audio_device(2), | |
280 SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); | |
281 if (status >= 0) { | |
282 available = 1; | |
283 SDL_NAME(snd_pcm_close) (handle); | |
284 } | |
285 UnloadALSALibrary(); | |
286 return (available); | |
287 } | |
288 | |
289 static void | |
290 Audio_DeleteDevice(SDL_AudioDevice * device) | |
291 { | |
292 SDL_free(device->hidden); | |
293 SDL_free(device); | |
294 UnloadALSALibrary(); | |
295 } | |
296 | |
297 static SDL_AudioDevice * | |
298 Audio_CreateDevice(int devindex) | |
299 { | |
300 SDL_AudioDevice *this; | |
301 | |
302 /* Initialize all variables that we clean on shutdown */ | |
303 LoadALSALibrary(); | |
304 this = (SDL_AudioDevice *) SDL_malloc(sizeof(SDL_AudioDevice)); | |
305 if (this) { | |
306 SDL_memset(this, 0, (sizeof *this)); | |
307 this->hidden = (struct SDL_PrivateAudioData *) | |
308 SDL_malloc((sizeof *this->hidden)); | |
309 } | |
310 if ((this == NULL) || (this->hidden == NULL)) { | |
311 SDL_OutOfMemory(); | |
312 if (this) { | |
313 SDL_free(this); | |
314 } | |
315 return (0); | |
316 } | |
317 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); | |
318 | |
319 /* Set the function pointers */ | |
320 this->OpenAudio = ALSA_OpenAudio; | |
321 this->WaitAudio = ALSA_WaitAudio; | |
322 this->PlayAudio = ALSA_PlayAudio; | |
323 this->GetAudioBuf = ALSA_GetAudioBuf; | |
324 this->CloseAudio = ALSA_CloseAudio; | |
325 | |
326 this->free = Audio_DeleteDevice; | |
327 | |
328 return this; | |
329 } | |
330 | |
331 AudioBootStrap ALSA_bootstrap = { | |
332 DRIVER_NAME, "ALSA 0.9 PCM audio", | |
333 Audio_Available, Audio_CreateDevice | |
334 }; | |
335 | 216 |
336 /* This function waits until it is possible to write a full sound buffer */ | 217 /* This function waits until it is possible to write a full sound buffer */ |
337 static void | 218 static void |
338 ALSA_WaitAudio(_THIS) | 219 ALSA_WaitDevice(_THIS) |
339 { | 220 { |
340 /* Check to see if the thread-parent process is still alive */ | 221 /* Check to see if the thread-parent process is still alive */ |
341 { | 222 { |
342 static int cnt = 0; | 223 static int cnt = 0; |
343 /* Note that this only works with thread implementations | 224 /* Note that this only works with thread implementations |
344 that use a different process id for each thread. | 225 that use a different process id for each thread. |
345 */ | 226 */ |
346 if (parent && (((++cnt) % 10) == 0)) { /* Check every 10 loops */ | 227 /* Check every 10 loops */ |
347 if (kill(parent, 0) < 0) { | 228 if (this->hidden->parent && (((++cnt) % 10) == 0)) { |
229 if (kill(this->hidden->parent, 0) < 0) { | |
348 this->enabled = 0; | 230 this->enabled = 0; |
349 } | 231 } |
350 } | 232 } |
351 } | 233 } |
352 } | 234 } |
353 | 235 |
354 | 236 |
237 /* !!! FIXME: is there a channel swizzler in alsalib instead? */ | |
355 /* | 238 /* |
356 * http://bugzilla.libsdl.org/show_bug.cgi?id=110 | 239 * http://bugzilla.libsdl.org/show_bug.cgi?id=110 |
357 * "For Linux ALSA, this is FL-FR-RL-RR-C-LFE | 240 * "For Linux ALSA, this is FL-FR-RL-RR-C-LFE |
358 * and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-RL-RR" | 241 * and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-RL-RR" |
359 */ | 242 */ |
360 #define SWIZ6(T) \ | 243 #define SWIZ6(T) \ |
361 T *ptr = (T *) mixbuf; \ | 244 T *ptr = (T *) this->hidden->mixbuf; \ |
362 const Uint32 count = (this->spec.samples / 6); \ | 245 const Uint32 count = (this->spec.samples / 6); \ |
363 Uint32 i; \ | 246 Uint32 i; \ |
364 for (i = 0; i < count; i++, ptr += 6) { \ | 247 for (i = 0; i < count; i++, ptr += 6) { \ |
365 T tmp; \ | 248 T tmp; \ |
366 tmp = ptr[2]; ptr[2] = ptr[4]; ptr[4] = tmp; \ | 249 tmp = ptr[2]; ptr[2] = ptr[4]; ptr[4] = tmp; \ |
390 | 273 |
391 #undef SWIZ6 | 274 #undef SWIZ6 |
392 | 275 |
393 | 276 |
394 /* | 277 /* |
395 * Called right before feeding this->mixbuf to the hardware. Swizzle channels | 278 * Called right before feeding this->hidden->mixbuf to the hardware. Swizzle |
396 * from Windows/Mac order to the format alsalib will want. | 279 * channels from Windows/Mac order to the format alsalib will want. |
397 */ | 280 */ |
398 static __inline__ void | 281 static __inline__ void |
399 swizzle_alsa_channels(_THIS) | 282 swizzle_alsa_channels(_THIS) |
400 { | 283 { |
401 if (this->spec.channels == 6) { | 284 if (this->spec.channels == 6) { |
413 /* !!! FIXME: update this for 7.1 if needed, later. */ | 296 /* !!! FIXME: update this for 7.1 if needed, later. */ |
414 } | 297 } |
415 | 298 |
416 | 299 |
417 static void | 300 static void |
418 ALSA_PlayAudio(_THIS) | 301 ALSA_PlayDevice(_THIS) |
419 { | 302 { |
420 int status; | 303 int status; |
421 int sample_len; | 304 int sample_len; |
422 signed short *sample_buf; | 305 signed short *sample_buf; |
423 | 306 |
424 swizzle_alsa_channels(this); | 307 swizzle_alsa_channels(this); |
425 | 308 |
426 sample_len = this->spec.samples; | 309 sample_len = this->spec.samples; |
427 sample_buf = (signed short *) mixbuf; | 310 sample_buf = (signed short *) this->hidden->mixbuf; |
428 | 311 |
429 while (sample_len > 0) { | 312 while (sample_len > 0) { |
430 status = | 313 status = ALSA_snd_pcm_writei(this->hidden->pcm_handle, |
431 SDL_NAME(snd_pcm_writei) (pcm_handle, sample_buf, sample_len); | 314 sample_buf, sample_len); |
315 | |
432 if (status < 0) { | 316 if (status < 0) { |
433 if (status == -EAGAIN) { | 317 if (status == -EAGAIN) { |
434 SDL_Delay(1); | 318 SDL_Delay(1); |
435 continue; | 319 continue; |
436 } | 320 } |
437 if (status == -ESTRPIPE) { | 321 if (status == -ESTRPIPE) { |
438 do { | 322 do { |
439 SDL_Delay(1); | 323 SDL_Delay(1); |
440 status = SDL_NAME(snd_pcm_resume) (pcm_handle); | 324 status = ALSA_snd_pcm_resume(this->hidden->pcm_handle); |
441 } while (status == -EAGAIN); | 325 } while (status == -EAGAIN); |
442 } | 326 } |
443 if (status < 0) { | 327 if (status < 0) { |
444 status = SDL_NAME(snd_pcm_prepare) (pcm_handle); | 328 status = ALSA_snd_pcm_prepare(this->hidden->pcm_handle); |
445 } | 329 } |
446 if (status < 0) { | 330 if (status < 0) { |
447 /* Hmm, not much we can do - abort */ | 331 /* Hmm, not much we can do - abort */ |
448 this->enabled = 0; | 332 this->enabled = 0; |
449 return; | 333 return; |
454 sample_len -= status; | 338 sample_len -= status; |
455 } | 339 } |
456 } | 340 } |
457 | 341 |
458 static Uint8 * | 342 static Uint8 * |
459 ALSA_GetAudioBuf(_THIS) | 343 ALSA_GetDeviceBuf(_THIS) |
460 { | 344 { |
461 return (mixbuf); | 345 return (this->hidden->mixbuf); |
462 } | 346 } |
463 | 347 |
464 static void | 348 static void |
465 ALSA_CloseAudio(_THIS) | 349 ALSA_CloseDevice(_THIS) |
466 { | 350 { |
467 if (mixbuf != NULL) { | 351 if (this->hidden != NULL) { |
468 SDL_FreeAudioMem(mixbuf); | 352 if (this->hidden->mixbuf != NULL) { |
469 mixbuf = NULL; | 353 SDL_FreeAudioMem(this->hidden->mixbuf); |
470 } | 354 this->hidden->mixbuf = NULL; |
471 if (pcm_handle) { | 355 } |
472 SDL_NAME(snd_pcm_drain) (pcm_handle); | 356 if (this->hidden->pcm_handle) { |
473 SDL_NAME(snd_pcm_close) (pcm_handle); | 357 ALSA_snd_pcm_drain(this->hidden->pcm_handle); |
474 pcm_handle = NULL; | 358 ALSA_snd_pcm_close(this->hidden->pcm_handle); |
359 this->hidden->pcm_handle = NULL; | |
360 } | |
361 SDL_free(this->hidden); | |
362 this->hidden = NULL; | |
475 } | 363 } |
476 } | 364 } |
477 | 365 |
478 static int | 366 static int |
479 ALSA_OpenAudio(_THIS, SDL_AudioSpec * spec) | 367 ALSA_OpenDevice(_THIS, const char *devname, int iscapture) |
480 { | 368 { |
481 int status; | 369 int status = 0; |
482 snd_pcm_hw_params_t *hwparams; | 370 snd_pcm_t *pcm_handle = NULL; |
483 snd_pcm_sw_params_t *swparams; | 371 snd_pcm_hw_params_t *hwparams = NULL; |
484 snd_pcm_format_t format; | 372 snd_pcm_sw_params_t *swparams = NULL; |
485 snd_pcm_uframes_t frames; | 373 snd_pcm_format_t format = 0; |
486 SDL_AudioFormat test_format; | 374 snd_pcm_uframes_t frames = 0; |
375 SDL_AudioFormat test_format = 0; | |
376 | |
377 /* Initialize all variables that we clean on shutdown */ | |
378 this->hidden = (struct SDL_PrivateAudioData *) | |
379 SDL_malloc((sizeof *this->hidden)); | |
380 if (this->hidden == NULL) { | |
381 SDL_OutOfMemory(); | |
382 return 0; | |
383 } | |
384 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); | |
487 | 385 |
488 /* Open the audio device */ | 386 /* Open the audio device */ |
489 /* Name of device should depend on # channels in spec */ | 387 /* Name of device should depend on # channels in spec */ |
490 status = | 388 status = ALSA_snd_pcm_open(&pcm_handle, |
491 SDL_NAME(snd_pcm_open) (&pcm_handle, | 389 get_audio_device(this->spec.channels), |
492 get_audio_device(spec->channels), | 390 SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); |
493 SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); | 391 |
494 | 392 if (status < 0) { |
495 if (status < 0) { | 393 ALSA_CloseDevice(this); |
496 SDL_SetError("Couldn't open audio device: %s", | 394 SDL_SetError("ALSA: Couldn't open audio device: %s", |
497 SDL_NAME(snd_strerror) (status)); | 395 ALSA_snd_strerror(status)); |
498 return (-1); | 396 return 0; |
499 } | 397 } |
398 | |
399 this->hidden->pcm_handle = pcm_handle; | |
500 | 400 |
501 /* Figure out what the hardware is capable of */ | 401 /* Figure out what the hardware is capable of */ |
502 snd_pcm_hw_params_alloca(&hwparams); | 402 snd_pcm_hw_params_alloca(&hwparams); |
503 status = SDL_NAME(snd_pcm_hw_params_any) (pcm_handle, hwparams); | 403 status = ALSA_snd_pcm_hw_params_any(pcm_handle, hwparams); |
504 if (status < 0) { | 404 if (status < 0) { |
505 SDL_SetError("Couldn't get hardware config: %s", | 405 ALSA_CloseDevice(this); |
506 SDL_NAME(snd_strerror) (status)); | 406 SDL_SetError("ALSA: Couldn't get hardware config: %s", |
507 ALSA_CloseAudio(this); | 407 ALSA_snd_strerror(status)); |
508 return (-1); | 408 return 0; |
509 } | 409 } |
510 | 410 |
511 /* SDL only uses interleaved sample output */ | 411 /* SDL only uses interleaved sample output */ |
512 status = | 412 status = ALSA_snd_pcm_hw_params_set_access(pcm_handle, hwparams, |
513 SDL_NAME(snd_pcm_hw_params_set_access) (pcm_handle, hwparams, | 413 SND_PCM_ACCESS_RW_INTERLEAVED); |
514 SND_PCM_ACCESS_RW_INTERLEAVED); | 414 if (status < 0) { |
515 if (status < 0) { | 415 ALSA_CloseDevice(this); |
516 SDL_SetError("Couldn't set interleaved access: %s", | 416 SDL_SetError("ALSA: Couldn't set interleaved access: %s", |
517 SDL_NAME(snd_strerror) (status)); | 417 ALSA_snd_strerror(status)); |
518 ALSA_CloseAudio(this); | 418 return 0; |
519 return (-1); | |
520 } | 419 } |
521 | 420 |
522 /* Try for a closest match on audio format */ | 421 /* Try for a closest match on audio format */ |
523 status = -1; | 422 status = -1; |
524 for (test_format = SDL_FirstAudioFormat(spec->format); | 423 for (test_format = SDL_FirstAudioFormat(this->spec.format); |
525 test_format && (status < 0);) { | 424 test_format && (status < 0);) { |
526 status = 0; /* if we can't support a format, it'll become -1. */ | 425 status = 0; /* if we can't support a format, it'll become -1. */ |
527 switch (test_format) { | 426 switch (test_format) { |
528 case AUDIO_U8: | 427 case AUDIO_U8: |
529 format = SND_PCM_FORMAT_U8; | 428 format = SND_PCM_FORMAT_U8; |
530 break; | 429 break; |
531 case AUDIO_S8: | 430 case AUDIO_S8: |
558 default: | 457 default: |
559 status = -1; | 458 status = -1; |
560 break; | 459 break; |
561 } | 460 } |
562 if (status >= 0) { | 461 if (status >= 0) { |
563 status = | 462 status = ALSA_snd_pcm_hw_params_set_format(pcm_handle, |
564 SDL_NAME(snd_pcm_hw_params_set_format) (pcm_handle, | 463 hwparams, format); |
565 hwparams, format); | |
566 } | 464 } |
567 if (status < 0) { | 465 if (status < 0) { |
568 test_format = SDL_NextAudioFormat(); | 466 test_format = SDL_NextAudioFormat(); |
569 } | 467 } |
570 } | 468 } |
571 if (status < 0) { | 469 if (status < 0) { |
572 SDL_SetError("Couldn't find any hardware audio formats"); | 470 ALSA_CloseDevice(this); |
573 ALSA_CloseAudio(this); | 471 SDL_SetError("ALSA: Couldn't find any hardware audio formats"); |
574 return (-1); | 472 return 0; |
575 } | 473 } |
576 spec->format = test_format; | 474 this->spec.format = test_format; |
577 | 475 |
578 /* Set the number of channels */ | 476 /* Set the number of channels */ |
579 status = | 477 status = ALSA_snd_pcm_hw_params_set_channels(pcm_handle, hwparams, |
580 SDL_NAME(snd_pcm_hw_params_set_channels) (pcm_handle, hwparams, | 478 this->spec.channels); |
581 spec->channels); | 479 if (status < 0) { |
582 if (status < 0) { | 480 status = ALSA_snd_pcm_hw_params_get_channels(hwparams); |
583 status = SDL_NAME(snd_pcm_hw_params_get_channels) (hwparams); | |
584 if ((status <= 0) || (status > 2)) { | 481 if ((status <= 0) || (status > 2)) { |
585 SDL_SetError("Couldn't set audio channels"); | 482 ALSA_CloseDevice(this); |
586 ALSA_CloseAudio(this); | 483 SDL_SetError("ALSA: Couldn't set audio channels"); |
587 return (-1); | 484 return 0; |
588 } | 485 } |
589 spec->channels = status; | 486 this->spec.channels = status; |
590 } | 487 } |
591 | 488 |
592 /* Set the audio rate */ | 489 /* Set the audio rate */ |
593 status = | 490 status = ALSA_snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, |
594 SDL_NAME(snd_pcm_hw_params_set_rate_near) (pcm_handle, hwparams, | 491 this->spec.freq, NULL); |
595 spec->freq, NULL); | 492 if (status < 0) { |
596 if (status < 0) { | 493 ALSA_CloseDevice(this); |
597 SDL_SetError("Couldn't set audio frequency: %s", | 494 SDL_SetError("ALSA: Couldn't set audio frequency: %s", |
598 SDL_NAME(snd_strerror) (status)); | 495 ALSA_snd_strerror(status)); |
599 ALSA_CloseAudio(this); | 496 return 0; |
600 return (-1); | 497 } |
601 } | 498 this->spec.freq = status; |
602 spec->freq = status; | |
603 | 499 |
604 /* Set the buffer size, in samples */ | 500 /* Set the buffer size, in samples */ |
605 frames = spec->samples; | 501 frames = this->spec.samples; |
606 frames = | 502 frames = ALSA_snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, |
607 SDL_NAME(snd_pcm_hw_params_set_period_size_near) (pcm_handle, | 503 frames, NULL); |
608 hwparams, frames, | 504 this->spec.samples = frames; |
609 NULL); | 505 ALSA_snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, 2, NULL); |
610 spec->samples = frames; | |
611 SDL_NAME(snd_pcm_hw_params_set_periods_near) (pcm_handle, hwparams, 2, | |
612 NULL); | |
613 | 506 |
614 /* "set" the hardware with the desired parameters */ | 507 /* "set" the hardware with the desired parameters */ |
615 status = SDL_NAME(snd_pcm_hw_params) (pcm_handle, hwparams); | 508 status = ALSA_snd_pcm_hw_params(pcm_handle, hwparams); |
616 if (status < 0) { | 509 if (status < 0) { |
617 SDL_SetError("Couldn't set hardware audio parameters: %s", | 510 ALSA_CloseDevice(this); |
618 SDL_NAME(snd_strerror) (status)); | 511 SDL_SetError("ALSA: Couldn't set hardware audio parameters: %s", |
619 ALSA_CloseAudio(this); | 512 ALSA_snd_strerror(status)); |
620 return (-1); | 513 return 0; |
621 } | 514 } |
622 | 515 |
623 /* This is useful for debugging... */ | 516 #if AUDIO_DEBUG |
624 /* | 517 { |
625 { snd_pcm_sframes_t bufsize; int fragments; | 518 snd_pcm_sframes_t bufsize; |
626 bufsize = SDL_NAME(snd_pcm_hw_params_get_period_size)(hwparams); | 519 int fragments; |
627 fragments = SDL_NAME(snd_pcm_hw_params_get_periods)(hwparams); | 520 bufsize = ALSA_snd_pcm_hw_params_get_period_size(hwparams); |
628 | 521 fragments = ALSA_snd_pcm_hw_params_get_periods(hwparams); |
629 fprintf(stderr, "ALSA: bufsize = %ld, fragments = %d\n", bufsize, fragments); | 522 fprintf(stderr,"ALSA: bufsize = %ld, fragments = %d\n",bufsize,fragments); |
630 } | 523 } |
631 */ | 524 #endif |
632 | 525 |
633 /* Set the software parameters */ | 526 /* Set the software parameters */ |
634 snd_pcm_sw_params_alloca(&swparams); | 527 snd_pcm_sw_params_alloca(&swparams); |
635 status = SDL_NAME(snd_pcm_sw_params_current) (pcm_handle, swparams); | 528 status = ALSA_snd_pcm_sw_params_current(pcm_handle, swparams); |
636 if (status < 0) { | 529 if (status < 0) { |
637 SDL_SetError("Couldn't get software config: %s", | 530 ALSA_CloseDevice(this); |
638 SDL_NAME(snd_strerror) (status)); | 531 SDL_SetError("ALSA: Couldn't get software config: %s", |
639 ALSA_CloseAudio(this); | 532 ALSA_snd_strerror(status)); |
640 return (-1); | 533 return 0; |
641 } | 534 } |
642 status = | 535 status = ALSA_snd_pcm_sw_params_set_start_threshold(pcm_handle,swparams,0); |
643 SDL_NAME(snd_pcm_sw_params_set_start_threshold) (pcm_handle, | 536 if (status < 0) { |
644 swparams, 0); | 537 ALSA_CloseDevice(this); |
645 if (status < 0) { | 538 SDL_SetError("ALSA: Couldn't set start threshold: %s", |
646 SDL_SetError("Couldn't set start threshold: %s", | 539 ALSA_snd_strerror(status)); |
647 SDL_NAME(snd_strerror) (status)); | 540 return 0; |
648 ALSA_CloseAudio(this); | 541 } |
649 return (-1); | 542 status = ALSA_snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, frames); |
650 } | 543 if (status < 0) { |
651 status = | 544 ALSA_CloseDevice(this); |
652 SDL_NAME(snd_pcm_sw_params_set_avail_min) (pcm_handle, swparams, | 545 SDL_SetError("Couldn't set avail min: %s", ALSA_snd_strerror(status)); |
653 frames); | 546 return 0; |
654 if (status < 0) { | 547 } |
655 SDL_SetError("Couldn't set avail min: %s", | 548 status = ALSA_snd_pcm_sw_params(pcm_handle, swparams); |
656 SDL_NAME(snd_strerror) (status)); | 549 if (status < 0) { |
657 ALSA_CloseAudio(this); | 550 ALSA_CloseDevice(this); |
658 return (-1); | |
659 } | |
660 status = SDL_NAME(snd_pcm_sw_params) (pcm_handle, swparams); | |
661 if (status < 0) { | |
662 SDL_SetError("Couldn't set software audio parameters: %s", | 551 SDL_SetError("Couldn't set software audio parameters: %s", |
663 SDL_NAME(snd_strerror) (status)); | 552 ALSA_snd_strerror(status)); |
664 ALSA_CloseAudio(this); | 553 return 0; |
665 return (-1); | |
666 } | 554 } |
667 | 555 |
668 /* Calculate the final parameters for this audio specification */ | 556 /* Calculate the final parameters for this audio specification */ |
669 SDL_CalculateAudioSpec(spec); | 557 SDL_CalculateAudioSpec(&this->spec); |
670 | 558 |
671 /* Allocate mixing buffer */ | 559 /* Allocate mixing buffer */ |
672 mixlen = spec->size; | 560 this->hidden->mixlen = this->spec.size; |
673 mixbuf = (Uint8 *) SDL_AllocAudioMem(mixlen); | 561 this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen); |
674 if (mixbuf == NULL) { | 562 if (this->hidden->mixbuf == NULL) { |
675 ALSA_CloseAudio(this); | 563 ALSA_CloseDevice(this); |
676 return (-1); | 564 SDL_OutOfMemory(); |
677 } | 565 return 0; |
678 SDL_memset(mixbuf, spec->silence, spec->size); | 566 } |
567 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size); | |
679 | 568 |
680 /* Get the parent process id (we're the parent of the audio thread) */ | 569 /* Get the parent process id (we're the parent of the audio thread) */ |
681 parent = getpid(); | 570 this->hidden->parent = getpid(); |
682 | 571 |
683 /* Switch to blocking mode for playback */ | 572 /* Switch to blocking mode for playback */ |
684 SDL_NAME(snd_pcm_nonblock) (pcm_handle, 0); | 573 ALSA_snd_pcm_nonblock(pcm_handle, 0); |
685 | 574 |
686 /* We're ready to rock and roll. :-) */ | 575 /* We're ready to rock and roll. :-) */ |
687 return (0); | 576 return 1; |
688 } | 577 } |
578 | |
579 static void | |
580 ALSA_Deinitialize(void) | |
581 { | |
582 UnloadALSALibrary(); | |
583 } | |
584 | |
585 static int | |
586 ALSA_Init(SDL_AudioDriverImpl *impl) | |
587 { | |
588 if (LoadALSALibrary() < 0) { | |
589 return 0; | |
590 } | |
591 | |
592 /* Set the function pointers */ | |
593 impl->OpenDevice = ALSA_OpenDevice; | |
594 impl->WaitDevice = ALSA_WaitDevice; | |
595 impl->GetDeviceBuf = ALSA_GetDeviceBuf; | |
596 impl->PlayDevice = ALSA_PlayDevice; | |
597 impl->CloseDevice = ALSA_CloseDevice; | |
598 impl->Deinitialize = ALSA_Deinitialize; | |
599 impl->OnlyHasDefaultOutputDevice = 1; /* !!! FIXME: Add device enum! */ | |
600 | |
601 return 1; | |
602 } | |
603 | |
604 | |
605 AudioBootStrap ALSA_bootstrap = { | |
606 DRIVER_NAME, "ALSA 0.9 PCM audio", ALSA_Init, 0 | |
607 }; | |
689 | 608 |
690 /* vi: set ts=4 sw=4 expandtab: */ | 609 /* vi: set ts=4 sw=4 expandtab: */ |