Imported Upstream version 2.0.4
[platform/upstream/SDL.git] / src / audio / alsa / SDL_alsa_audio.c
1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
4
5   This software is provided 'as-is', without any express or implied
6   warranty.  In no event will the authors be held liable for any damages
7   arising from the use of this software.
8
9   Permission is granted to anyone to use this software for any purpose,
10   including commercial applications, and to alter it and redistribute it
11   freely, subject to the following restrictions:
12
13   1. The origin of this software must not be misrepresented; you must not
14      claim that you wrote the original software. If you use this software
15      in a product, an acknowledgment in the product documentation would be
16      appreciated but is not required.
17   2. Altered source versions must be plainly marked as such, and must not be
18      misrepresented as being the original software.
19   3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22
23 #if SDL_AUDIO_DRIVER_ALSA
24
25 /* Allow access to a raw mixing buffer */
26
27 #include <sys/types.h>
28 #include <signal.h>             /* For kill() */
29 #include <errno.h>
30 #include <string.h>
31
32 #include "SDL_timer.h"
33 #include "SDL_audio.h"
34 #include "../SDL_audiomem.h"
35 #include "../SDL_audio_c.h"
36 #include "SDL_alsa_audio.h"
37
38 #ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
39 #include "SDL_loadso.h"
40 #endif
41
42 static int (*ALSA_snd_pcm_open)
43   (snd_pcm_t **, const char *, snd_pcm_stream_t, int);
44 static int (*ALSA_snd_pcm_close) (snd_pcm_t * pcm);
45 static snd_pcm_sframes_t(*ALSA_snd_pcm_writei)
46   (snd_pcm_t *, const void *, snd_pcm_uframes_t);
47 static int (*ALSA_snd_pcm_recover) (snd_pcm_t *, int, int);
48 static int (*ALSA_snd_pcm_prepare) (snd_pcm_t *);
49 static int (*ALSA_snd_pcm_drain) (snd_pcm_t *);
50 static const char *(*ALSA_snd_strerror) (int);
51 static size_t(*ALSA_snd_pcm_hw_params_sizeof) (void);
52 static size_t(*ALSA_snd_pcm_sw_params_sizeof) (void);
53 static void (*ALSA_snd_pcm_hw_params_copy)
54   (snd_pcm_hw_params_t *, const snd_pcm_hw_params_t *);
55 static int (*ALSA_snd_pcm_hw_params_any) (snd_pcm_t *, snd_pcm_hw_params_t *);
56 static int (*ALSA_snd_pcm_hw_params_set_access)
57   (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_access_t);
58 static int (*ALSA_snd_pcm_hw_params_set_format)
59   (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_format_t);
60 static int (*ALSA_snd_pcm_hw_params_set_channels)
61   (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int);
62 static int (*ALSA_snd_pcm_hw_params_get_channels)
63   (const snd_pcm_hw_params_t *, unsigned int *);
64 static int (*ALSA_snd_pcm_hw_params_set_rate_near)
65   (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
66 static int (*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 int (*ALSA_snd_pcm_hw_params_get_period_size)
69   (const snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *);
70 static 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)
73   (const snd_pcm_hw_params_t *, unsigned int *, int *);
74 static int (*ALSA_snd_pcm_hw_params_set_buffer_size_near)
75   (snd_pcm_t *pcm, snd_pcm_hw_params_t *, snd_pcm_uframes_t *);
76 static int (*ALSA_snd_pcm_hw_params_get_buffer_size)
77   (const snd_pcm_hw_params_t *, snd_pcm_uframes_t *);
78 static int (*ALSA_snd_pcm_hw_params) (snd_pcm_t *, snd_pcm_hw_params_t *);
79 static int (*ALSA_snd_pcm_sw_params_current) (snd_pcm_t *,
80                                               snd_pcm_sw_params_t *);
81 static int (*ALSA_snd_pcm_sw_params_set_start_threshold)
82   (snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t);
83 static int (*ALSA_snd_pcm_sw_params) (snd_pcm_t *, snd_pcm_sw_params_t *);
84 static int (*ALSA_snd_pcm_nonblock) (snd_pcm_t *, int);
85 static int (*ALSA_snd_pcm_wait)(snd_pcm_t *, int);
86 static int (*ALSA_snd_pcm_sw_params_set_avail_min)
87   (snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t);
88
89 #ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
90 #define snd_pcm_hw_params_sizeof ALSA_snd_pcm_hw_params_sizeof
91 #define snd_pcm_sw_params_sizeof ALSA_snd_pcm_sw_params_sizeof
92
93 static const char *alsa_library = SDL_AUDIO_DRIVER_ALSA_DYNAMIC;
94 static void *alsa_handle = NULL;
95
96 static int
97 load_alsa_sym(const char *fn, void **addr)
98 {
99     *addr = SDL_LoadFunction(alsa_handle, fn);
100     if (*addr == NULL) {
101         /* Don't call SDL_SetError(): SDL_LoadFunction already did. */
102         return 0;
103     }
104
105     return 1;
106 }
107
108 /* cast funcs to char* first, to please GCC's strict aliasing rules. */
109 #define SDL_ALSA_SYM(x) \
110     if (!load_alsa_sym(#x, (void **) (char *) &ALSA_##x)) return -1
111 #else
112 #define SDL_ALSA_SYM(x) ALSA_##x = x
113 #endif
114
115 static int
116 load_alsa_syms(void)
117 {
118     SDL_ALSA_SYM(snd_pcm_open);
119     SDL_ALSA_SYM(snd_pcm_close);
120     SDL_ALSA_SYM(snd_pcm_writei);
121     SDL_ALSA_SYM(snd_pcm_recover);
122     SDL_ALSA_SYM(snd_pcm_prepare);
123     SDL_ALSA_SYM(snd_pcm_drain);
124     SDL_ALSA_SYM(snd_strerror);
125     SDL_ALSA_SYM(snd_pcm_hw_params_sizeof);
126     SDL_ALSA_SYM(snd_pcm_sw_params_sizeof);
127     SDL_ALSA_SYM(snd_pcm_hw_params_copy);
128     SDL_ALSA_SYM(snd_pcm_hw_params_any);
129     SDL_ALSA_SYM(snd_pcm_hw_params_set_access);
130     SDL_ALSA_SYM(snd_pcm_hw_params_set_format);
131     SDL_ALSA_SYM(snd_pcm_hw_params_set_channels);
132     SDL_ALSA_SYM(snd_pcm_hw_params_get_channels);
133     SDL_ALSA_SYM(snd_pcm_hw_params_set_rate_near);
134     SDL_ALSA_SYM(snd_pcm_hw_params_set_period_size_near);
135     SDL_ALSA_SYM(snd_pcm_hw_params_get_period_size);
136     SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_near);
137     SDL_ALSA_SYM(snd_pcm_hw_params_get_periods);
138     SDL_ALSA_SYM(snd_pcm_hw_params_set_buffer_size_near);
139     SDL_ALSA_SYM(snd_pcm_hw_params_get_buffer_size);
140     SDL_ALSA_SYM(snd_pcm_hw_params);
141     SDL_ALSA_SYM(snd_pcm_sw_params_current);
142     SDL_ALSA_SYM(snd_pcm_sw_params_set_start_threshold);
143     SDL_ALSA_SYM(snd_pcm_sw_params);
144     SDL_ALSA_SYM(snd_pcm_nonblock);
145     SDL_ALSA_SYM(snd_pcm_wait);
146     SDL_ALSA_SYM(snd_pcm_sw_params_set_avail_min);
147     return 0;
148 }
149
150 #undef SDL_ALSA_SYM
151
152 #ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
153
154 static void
155 UnloadALSALibrary(void)
156 {
157     if (alsa_handle != NULL) {
158         SDL_UnloadObject(alsa_handle);
159         alsa_handle = NULL;
160     }
161 }
162
163 static int
164 LoadALSALibrary(void)
165 {
166     int retval = 0;
167     if (alsa_handle == NULL) {
168         alsa_handle = SDL_LoadObject(alsa_library);
169         if (alsa_handle == NULL) {
170             retval = -1;
171             /* Don't call SDL_SetError(): SDL_LoadObject already did. */
172         } else {
173             retval = load_alsa_syms();
174             if (retval < 0) {
175                 UnloadALSALibrary();
176             }
177         }
178     }
179     return retval;
180 }
181
182 #else
183
184 static void
185 UnloadALSALibrary(void)
186 {
187 }
188
189 static int
190 LoadALSALibrary(void)
191 {
192     load_alsa_syms();
193     return 0;
194 }
195
196 #endif /* SDL_AUDIO_DRIVER_ALSA_DYNAMIC */
197
198 static const char *
199 get_audio_device(int channels)
200 {
201     const char *device;
202
203     device = SDL_getenv("AUDIODEV");    /* Is there a standard variable name? */
204     if (device == NULL) {
205         switch (channels) {
206         case 6:
207             device = "plug:surround51";
208             break;
209         case 4:
210             device = "plug:surround40";
211             break;
212         default:
213             device = "default";
214             break;
215         }
216     }
217     return device;
218 }
219
220
221 /* This function waits until it is possible to write a full sound buffer */
222 static void
223 ALSA_WaitDevice(_THIS)
224 {
225     /* We're in blocking mode, so there's nothing to do here */
226 }
227
228
229 /* !!! FIXME: is there a channel swizzler in alsalib instead? */
230 /*
231  * http://bugzilla.libsdl.org/show_bug.cgi?id=110
232  * "For Linux ALSA, this is FL-FR-RL-RR-C-LFE
233  *  and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-RL-RR"
234  */
235 #define SWIZ6(T) \
236     T *ptr = (T *) this->hidden->mixbuf; \
237     Uint32 i; \
238     for (i = 0; i < this->spec.samples; i++, ptr += 6) { \
239         T tmp; \
240         tmp = ptr[2]; ptr[2] = ptr[4]; ptr[4] = tmp; \
241         tmp = ptr[3]; ptr[3] = ptr[5]; ptr[5] = tmp; \
242     }
243
244 static SDL_INLINE void
245 swizzle_alsa_channels_6_64bit(_THIS)
246 {
247     SWIZ6(Uint64);
248 }
249
250 static SDL_INLINE void
251 swizzle_alsa_channels_6_32bit(_THIS)
252 {
253     SWIZ6(Uint32);
254 }
255
256 static SDL_INLINE void
257 swizzle_alsa_channels_6_16bit(_THIS)
258 {
259     SWIZ6(Uint16);
260 }
261
262 static SDL_INLINE void
263 swizzle_alsa_channels_6_8bit(_THIS)
264 {
265     SWIZ6(Uint8);
266 }
267
268 #undef SWIZ6
269
270
271 /*
272  * Called right before feeding this->hidden->mixbuf to the hardware. Swizzle
273  *  channels from Windows/Mac order to the format alsalib will want.
274  */
275 static SDL_INLINE void
276 swizzle_alsa_channels(_THIS)
277 {
278     if (this->spec.channels == 6) {
279         const Uint16 fmtsize = (this->spec.format & 0xFF);      /* bits/channel. */
280         if (fmtsize == 16)
281             swizzle_alsa_channels_6_16bit(this);
282         else if (fmtsize == 8)
283             swizzle_alsa_channels_6_8bit(this);
284         else if (fmtsize == 32)
285             swizzle_alsa_channels_6_32bit(this);
286         else if (fmtsize == 64)
287             swizzle_alsa_channels_6_64bit(this);
288     }
289
290     /* !!! FIXME: update this for 7.1 if needed, later. */
291 }
292
293
294 static void
295 ALSA_PlayDevice(_THIS)
296 {
297     int status;
298     const Uint8 *sample_buf = (const Uint8 *) this->hidden->mixbuf;
299     const int frame_size = (((int) (this->spec.format & 0xFF)) / 8) *
300                                 this->spec.channels;
301     snd_pcm_uframes_t frames_left = ((snd_pcm_uframes_t) this->spec.samples);
302
303     swizzle_alsa_channels(this);
304
305     while ( frames_left > 0 && this->enabled ) {
306         /* !!! FIXME: This works, but needs more testing before going live */
307         /* ALSA_snd_pcm_wait(this->hidden->pcm_handle, -1); */
308         status = ALSA_snd_pcm_writei(this->hidden->pcm_handle,
309                                      sample_buf, frames_left);
310
311         if (status < 0) {
312             if (status == -EAGAIN) {
313                 /* Apparently snd_pcm_recover() doesn't handle this case -
314                    does it assume snd_pcm_wait() above? */
315                 SDL_Delay(1);
316                 continue;
317             }
318             status = ALSA_snd_pcm_recover(this->hidden->pcm_handle, status, 0);
319             if (status < 0) {
320                 /* Hmm, not much we can do - abort */
321                 fprintf(stderr, "ALSA write failed (unrecoverable): %s\n",
322                         ALSA_snd_strerror(status));
323                 SDL_OpenedAudioDeviceDisconnected(this);
324                 return;
325             }
326             continue;
327         }
328         sample_buf += status * frame_size;
329         frames_left -= status;
330     }
331 }
332
333 static Uint8 *
334 ALSA_GetDeviceBuf(_THIS)
335 {
336     return (this->hidden->mixbuf);
337 }
338
339 static void
340 ALSA_CloseDevice(_THIS)
341 {
342     if (this->hidden != NULL) {
343         SDL_FreeAudioMem(this->hidden->mixbuf);
344         this->hidden->mixbuf = NULL;
345         if (this->hidden->pcm_handle) {
346             ALSA_snd_pcm_drain(this->hidden->pcm_handle);
347             ALSA_snd_pcm_close(this->hidden->pcm_handle);
348             this->hidden->pcm_handle = NULL;
349         }
350         SDL_free(this->hidden);
351         this->hidden = NULL;
352     }
353 }
354
355 static int
356 ALSA_finalize_hardware(_THIS, snd_pcm_hw_params_t *hwparams, int override)
357 {
358     int status;
359     snd_pcm_uframes_t bufsize;
360
361     /* "set" the hardware with the desired parameters */
362     status = ALSA_snd_pcm_hw_params(this->hidden->pcm_handle, hwparams);
363     if ( status < 0 ) {
364         return(-1);
365     }
366
367     /* Get samples for the actual buffer size */
368     status = ALSA_snd_pcm_hw_params_get_buffer_size(hwparams, &bufsize);
369     if ( status < 0 ) {
370         return(-1);
371     }
372     if ( !override && bufsize != this->spec.samples * 2 ) {
373         return(-1);
374     }
375
376     /* !!! FIXME: Is this safe to do? */
377     this->spec.samples = bufsize / 2;
378
379     /* This is useful for debugging */
380     if ( SDL_getenv("SDL_AUDIO_ALSA_DEBUG") ) {
381         snd_pcm_uframes_t persize = 0;
382         unsigned int periods = 0;
383
384         ALSA_snd_pcm_hw_params_get_period_size(hwparams, &persize, NULL);
385         ALSA_snd_pcm_hw_params_get_periods(hwparams, &periods, NULL);
386
387         fprintf(stderr,
388             "ALSA: period size = %ld, periods = %u, buffer size = %lu\n",
389             persize, periods, bufsize);
390     }
391
392     return(0);
393 }
394
395 static int
396 ALSA_set_period_size(_THIS, snd_pcm_hw_params_t *params, int override)
397 {
398     const char *env;
399     int status;
400     snd_pcm_hw_params_t *hwparams;
401     snd_pcm_uframes_t frames;
402     unsigned int periods;
403
404     /* Copy the hardware parameters for this setup */
405     snd_pcm_hw_params_alloca(&hwparams);
406     ALSA_snd_pcm_hw_params_copy(hwparams, params);
407
408     if ( !override ) {
409         env = SDL_getenv("SDL_AUDIO_ALSA_SET_PERIOD_SIZE");
410         if ( env ) {
411             override = SDL_atoi(env);
412             if ( override == 0 ) {
413                 return(-1);
414             }
415         }
416     }
417
418     frames = this->spec.samples;
419     status = ALSA_snd_pcm_hw_params_set_period_size_near(
420                 this->hidden->pcm_handle, hwparams, &frames, NULL);
421     if ( status < 0 ) {
422         return(-1);
423     }
424
425     periods = 2;
426     status = ALSA_snd_pcm_hw_params_set_periods_near(
427                 this->hidden->pcm_handle, hwparams, &periods, NULL);
428     if ( status < 0 ) {
429         return(-1);
430     }
431
432     return ALSA_finalize_hardware(this, hwparams, override);
433 }
434
435 static int
436 ALSA_set_buffer_size(_THIS, snd_pcm_hw_params_t *params, int override)
437 {
438     const char *env;
439     int status;
440     snd_pcm_hw_params_t *hwparams;
441     snd_pcm_uframes_t frames;
442
443     /* Copy the hardware parameters for this setup */
444     snd_pcm_hw_params_alloca(&hwparams);
445     ALSA_snd_pcm_hw_params_copy(hwparams, params);
446
447     if ( !override ) {
448         env = SDL_getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE");
449         if ( env ) {
450             override = SDL_atoi(env);
451             if ( override == 0 ) {
452                 return(-1);
453             }
454         }
455     }
456
457     frames = this->spec.samples * 2;
458     status = ALSA_snd_pcm_hw_params_set_buffer_size_near(
459                     this->hidden->pcm_handle, hwparams, &frames);
460     if ( status < 0 ) {
461         return(-1);
462     }
463
464     return ALSA_finalize_hardware(this, hwparams, override);
465 }
466
467 static int
468 ALSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
469 {
470     int status = 0;
471     snd_pcm_t *pcm_handle = NULL;
472     snd_pcm_hw_params_t *hwparams = NULL;
473     snd_pcm_sw_params_t *swparams = NULL;
474     snd_pcm_format_t format = 0;
475     SDL_AudioFormat test_format = 0;
476     unsigned int rate = 0;
477     unsigned int channels = 0;
478
479     /* Initialize all variables that we clean on shutdown */
480     this->hidden = (struct SDL_PrivateAudioData *)
481         SDL_malloc((sizeof *this->hidden));
482     if (this->hidden == NULL) {
483         return SDL_OutOfMemory();
484     }
485     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
486
487     /* Open the audio device */
488     /* Name of device should depend on # channels in spec */
489     status = ALSA_snd_pcm_open(&pcm_handle,
490                                get_audio_device(this->spec.channels),
491                                SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
492
493     if (status < 0) {
494         ALSA_CloseDevice(this);
495         return SDL_SetError("ALSA: Couldn't open audio device: %s",
496                             ALSA_snd_strerror(status));
497     }
498
499     this->hidden->pcm_handle = pcm_handle;
500
501     /* Figure out what the hardware is capable of */
502     snd_pcm_hw_params_alloca(&hwparams);
503     status = ALSA_snd_pcm_hw_params_any(pcm_handle, hwparams);
504     if (status < 0) {
505         ALSA_CloseDevice(this);
506         return SDL_SetError("ALSA: Couldn't get hardware config: %s",
507                             ALSA_snd_strerror(status));
508     }
509
510     /* SDL only uses interleaved sample output */
511     status = ALSA_snd_pcm_hw_params_set_access(pcm_handle, hwparams,
512                                                SND_PCM_ACCESS_RW_INTERLEAVED);
513     if (status < 0) {
514         ALSA_CloseDevice(this);
515         return SDL_SetError("ALSA: Couldn't set interleaved access: %s",
516                      ALSA_snd_strerror(status));
517     }
518
519     /* Try for a closest match on audio format */
520     status = -1;
521     for (test_format = SDL_FirstAudioFormat(this->spec.format);
522          test_format && (status < 0);) {
523         status = 0;             /* if we can't support a format, it'll become -1. */
524         switch (test_format) {
525         case AUDIO_U8:
526             format = SND_PCM_FORMAT_U8;
527             break;
528         case AUDIO_S8:
529             format = SND_PCM_FORMAT_S8;
530             break;
531         case AUDIO_S16LSB:
532             format = SND_PCM_FORMAT_S16_LE;
533             break;
534         case AUDIO_S16MSB:
535             format = SND_PCM_FORMAT_S16_BE;
536             break;
537         case AUDIO_U16LSB:
538             format = SND_PCM_FORMAT_U16_LE;
539             break;
540         case AUDIO_U16MSB:
541             format = SND_PCM_FORMAT_U16_BE;
542             break;
543         case AUDIO_S32LSB:
544             format = SND_PCM_FORMAT_S32_LE;
545             break;
546         case AUDIO_S32MSB:
547             format = SND_PCM_FORMAT_S32_BE;
548             break;
549         case AUDIO_F32LSB:
550             format = SND_PCM_FORMAT_FLOAT_LE;
551             break;
552         case AUDIO_F32MSB:
553             format = SND_PCM_FORMAT_FLOAT_BE;
554             break;
555         default:
556             status = -1;
557             break;
558         }
559         if (status >= 0) {
560             status = ALSA_snd_pcm_hw_params_set_format(pcm_handle,
561                                                        hwparams, format);
562         }
563         if (status < 0) {
564             test_format = SDL_NextAudioFormat();
565         }
566     }
567     if (status < 0) {
568         ALSA_CloseDevice(this);
569         return SDL_SetError("ALSA: Couldn't find any hardware audio formats");
570     }
571     this->spec.format = test_format;
572
573     /* Set the number of channels */
574     status = ALSA_snd_pcm_hw_params_set_channels(pcm_handle, hwparams,
575                                                  this->spec.channels);
576     channels = this->spec.channels;
577     if (status < 0) {
578         status = ALSA_snd_pcm_hw_params_get_channels(hwparams, &channels);
579         if (status < 0) {
580             ALSA_CloseDevice(this);
581             return SDL_SetError("ALSA: Couldn't set audio channels");
582         }
583         this->spec.channels = channels;
584     }
585
586     /* Set the audio rate */
587     rate = this->spec.freq;
588     status = ALSA_snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams,
589                                                   &rate, NULL);
590     if (status < 0) {
591         ALSA_CloseDevice(this);
592         return SDL_SetError("ALSA: Couldn't set audio frequency: %s",
593                             ALSA_snd_strerror(status));
594     }
595     this->spec.freq = rate;
596
597     /* Set the buffer size, in samples */
598     if ( ALSA_set_period_size(this, hwparams, 0) < 0 &&
599          ALSA_set_buffer_size(this, hwparams, 0) < 0 ) {
600         /* Failed to set desired buffer size, do the best you can... */
601         if ( ALSA_set_period_size(this, hwparams, 1) < 0 ) {
602             ALSA_CloseDevice(this);
603             return SDL_SetError("Couldn't set hardware audio parameters: %s", ALSA_snd_strerror(status));
604         }
605     }
606     /* Set the software parameters */
607     snd_pcm_sw_params_alloca(&swparams);
608     status = ALSA_snd_pcm_sw_params_current(pcm_handle, swparams);
609     if (status < 0) {
610         ALSA_CloseDevice(this);
611         return SDL_SetError("ALSA: Couldn't get software config: %s",
612                             ALSA_snd_strerror(status));
613     }
614     status = ALSA_snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, this->spec.samples);
615     if (status < 0) {
616         ALSA_CloseDevice(this);
617         return SDL_SetError("Couldn't set minimum available samples: %s",
618                             ALSA_snd_strerror(status));
619     }
620     status =
621         ALSA_snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, 1);
622     if (status < 0) {
623         ALSA_CloseDevice(this);
624         return SDL_SetError("ALSA: Couldn't set start threshold: %s",
625                             ALSA_snd_strerror(status));
626     }
627     status = ALSA_snd_pcm_sw_params(pcm_handle, swparams);
628     if (status < 0) {
629         ALSA_CloseDevice(this);
630         return SDL_SetError("Couldn't set software audio parameters: %s",
631                             ALSA_snd_strerror(status));
632     }
633
634     /* Calculate the final parameters for this audio specification */
635     SDL_CalculateAudioSpec(&this->spec);
636
637     /* Allocate mixing buffer */
638     this->hidden->mixlen = this->spec.size;
639     this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
640     if (this->hidden->mixbuf == NULL) {
641         ALSA_CloseDevice(this);
642         return SDL_OutOfMemory();
643     }
644     SDL_memset(this->hidden->mixbuf, this->spec.silence, this->hidden->mixlen);
645
646     /* Switch to blocking mode for playback */
647     ALSA_snd_pcm_nonblock(pcm_handle, 0);
648
649     /* We're ready to rock and roll. :-) */
650     return 0;
651 }
652
653 static void
654 ALSA_Deinitialize(void)
655 {
656     UnloadALSALibrary();
657 }
658
659 static int
660 ALSA_Init(SDL_AudioDriverImpl * impl)
661 {
662     if (LoadALSALibrary() < 0) {
663         return 0;
664     }
665
666     /* Set the function pointers */
667     impl->OpenDevice = ALSA_OpenDevice;
668     impl->WaitDevice = ALSA_WaitDevice;
669     impl->GetDeviceBuf = ALSA_GetDeviceBuf;
670     impl->PlayDevice = ALSA_PlayDevice;
671     impl->CloseDevice = ALSA_CloseDevice;
672     impl->Deinitialize = ALSA_Deinitialize;
673     impl->OnlyHasDefaultOutputDevice = 1;       /* !!! FIXME: Add device enum! */
674
675     return 1;   /* this audio target is available. */
676 }
677
678
679 AudioBootStrap ALSA_bootstrap = {
680     "alsa", "ALSA PCM audio", ALSA_Init, 0
681 };
682
683 #endif /* SDL_AUDIO_DRIVER_ALSA */
684
685 /* vi: set ts=4 sw=4 expandtab: */