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: */