21 #include "../../SDL_internal.h"
23 #if SDL_AUDIO_DRIVER_ALSA
25 #ifndef SDL_ALSA_NON_BLOCKING
26 #define SDL_ALSA_NON_BLOCKING 0
31 #include <sys/types.h>
38 #include "../SDL_audio_c.h"
41 #ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
45 static int (*ALSA_snd_pcm_open)
46 (snd_pcm_t **,
const char *, snd_pcm_stream_t, int);
47 static int (*ALSA_snd_pcm_close) (snd_pcm_t * pcm);
48 static snd_pcm_sframes_t (*ALSA_snd_pcm_writei)
49 (snd_pcm_t *,
const void *, snd_pcm_uframes_t);
50 static snd_pcm_sframes_t (*ALSA_snd_pcm_readi)
51 (snd_pcm_t *,
void *, snd_pcm_uframes_t);
52 static int (*ALSA_snd_pcm_recover) (snd_pcm_t *, int, int);
53 static int (*ALSA_snd_pcm_prepare) (snd_pcm_t *);
54 static int (*ALSA_snd_pcm_drain) (snd_pcm_t *);
55 static const char *(*ALSA_snd_strerror) (int);
56 static size_t(*ALSA_snd_pcm_hw_params_sizeof) (
void);
57 static size_t(*ALSA_snd_pcm_sw_params_sizeof) (
void);
58 static void (*ALSA_snd_pcm_hw_params_copy)
59 (snd_pcm_hw_params_t *,
const snd_pcm_hw_params_t *);
60 static int (*ALSA_snd_pcm_hw_params_any) (snd_pcm_t *, snd_pcm_hw_params_t *);
61 static int (*ALSA_snd_pcm_hw_params_set_access)
62 (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_access_t);
63 static int (*ALSA_snd_pcm_hw_params_set_format)
64 (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_format_t);
65 static int (*ALSA_snd_pcm_hw_params_set_channels)
66 (snd_pcm_t *, snd_pcm_hw_params_t *,
unsigned int);
67 static int (*ALSA_snd_pcm_hw_params_get_channels)
68 (
const snd_pcm_hw_params_t *,
unsigned int *);
69 static int (*ALSA_snd_pcm_hw_params_set_rate_near)
70 (snd_pcm_t *, snd_pcm_hw_params_t *,
unsigned int *,
int *);
71 static int (*ALSA_snd_pcm_hw_params_set_period_size_near)
72 (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_uframes_t *,
int *);
73 static int (*ALSA_snd_pcm_hw_params_get_period_size)
74 (
const snd_pcm_hw_params_t *, snd_pcm_uframes_t *,
int *);
75 static int (*ALSA_snd_pcm_hw_params_set_periods_near)
76 (snd_pcm_t *, snd_pcm_hw_params_t *,
unsigned int *,
int *);
77 static int (*ALSA_snd_pcm_hw_params_get_periods)
78 (
const snd_pcm_hw_params_t *,
unsigned int *,
int *);
79 static int (*ALSA_snd_pcm_hw_params_set_buffer_size_near)
80 (snd_pcm_t *pcm, snd_pcm_hw_params_t *, snd_pcm_uframes_t *);
81 static int (*ALSA_snd_pcm_hw_params_get_buffer_size)
82 (
const snd_pcm_hw_params_t *, snd_pcm_uframes_t *);
83 static int (*ALSA_snd_pcm_hw_params) (snd_pcm_t *, snd_pcm_hw_params_t *);
84 static int (*ALSA_snd_pcm_sw_params_current) (snd_pcm_t *,
85 snd_pcm_sw_params_t *);
86 static int (*ALSA_snd_pcm_sw_params_set_start_threshold)
87 (snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t);
88 static int (*ALSA_snd_pcm_sw_params) (snd_pcm_t *, snd_pcm_sw_params_t *);
89 static int (*ALSA_snd_pcm_nonblock) (snd_pcm_t *, int);
90 static int (*ALSA_snd_pcm_wait)(snd_pcm_t *, int);
91 static int (*ALSA_snd_pcm_sw_params_set_avail_min)
92 (snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t);
93 static int (*ALSA_snd_pcm_reset)(snd_pcm_t *);
94 static int (*ALSA_snd_device_name_hint) (int,
const char *,
void ***);
95 static char* (*ALSA_snd_device_name_get_hint) (
const void *,
const char *);
96 static int (*ALSA_snd_device_name_free_hint) (
void **);
97 static snd_pcm_sframes_t (*ALSA_snd_pcm_avail)(snd_pcm_t *);
98 #ifdef SND_CHMAP_API_VERSION
99 static snd_pcm_chmap_t* (*ALSA_snd_pcm_get_chmap) (snd_pcm_t *);
100 static int (*ALSA_snd_pcm_chmap_print) (
const snd_pcm_chmap_t *
map,
size_t maxlen,
char *
buf);
103 #ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
104 #define snd_pcm_hw_params_sizeof ALSA_snd_pcm_hw_params_sizeof
105 #define snd_pcm_sw_params_sizeof ALSA_snd_pcm_sw_params_sizeof
107 static const char *alsa_library = SDL_AUDIO_DRIVER_ALSA_DYNAMIC;
108 static void *alsa_handle =
NULL;
111 load_alsa_sym(
const char *fn,
void **
addr)
123 #define SDL_ALSA_SYM(x) \
124 if (!load_alsa_sym(#x, (void **) (char *) &ALSA_##x)) return -1
126 #define SDL_ALSA_SYM(x) ALSA_##x = x
132 SDL_ALSA_SYM(snd_pcm_open);
133 SDL_ALSA_SYM(snd_pcm_close);
134 SDL_ALSA_SYM(snd_pcm_writei);
135 SDL_ALSA_SYM(snd_pcm_readi);
136 SDL_ALSA_SYM(snd_pcm_recover);
137 SDL_ALSA_SYM(snd_pcm_prepare);
138 SDL_ALSA_SYM(snd_pcm_drain);
139 SDL_ALSA_SYM(snd_strerror);
140 SDL_ALSA_SYM(snd_pcm_hw_params_sizeof);
141 SDL_ALSA_SYM(snd_pcm_sw_params_sizeof);
142 SDL_ALSA_SYM(snd_pcm_hw_params_copy);
143 SDL_ALSA_SYM(snd_pcm_hw_params_any);
144 SDL_ALSA_SYM(snd_pcm_hw_params_set_access);
145 SDL_ALSA_SYM(snd_pcm_hw_params_set_format);
146 SDL_ALSA_SYM(snd_pcm_hw_params_set_channels);
147 SDL_ALSA_SYM(snd_pcm_hw_params_get_channels);
148 SDL_ALSA_SYM(snd_pcm_hw_params_set_rate_near);
149 SDL_ALSA_SYM(snd_pcm_hw_params_set_period_size_near);
150 SDL_ALSA_SYM(snd_pcm_hw_params_get_period_size);
151 SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_near);
152 SDL_ALSA_SYM(snd_pcm_hw_params_get_periods);
153 SDL_ALSA_SYM(snd_pcm_hw_params_set_buffer_size_near);
154 SDL_ALSA_SYM(snd_pcm_hw_params_get_buffer_size);
155 SDL_ALSA_SYM(snd_pcm_hw_params);
156 SDL_ALSA_SYM(snd_pcm_sw_params_current);
157 SDL_ALSA_SYM(snd_pcm_sw_params_set_start_threshold);
158 SDL_ALSA_SYM(snd_pcm_sw_params);
159 SDL_ALSA_SYM(snd_pcm_nonblock);
160 SDL_ALSA_SYM(snd_pcm_wait);
161 SDL_ALSA_SYM(snd_pcm_sw_params_set_avail_min);
162 SDL_ALSA_SYM(snd_pcm_reset);
163 SDL_ALSA_SYM(snd_device_name_hint);
164 SDL_ALSA_SYM(snd_device_name_get_hint);
165 SDL_ALSA_SYM(snd_device_name_free_hint);
166 SDL_ALSA_SYM(snd_pcm_avail);
167 #ifdef SND_CHMAP_API_VERSION
168 SDL_ALSA_SYM(snd_pcm_get_chmap);
169 SDL_ALSA_SYM(snd_pcm_chmap_print);
177 #ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
180 UnloadALSALibrary(
void)
182 if (alsa_handle !=
NULL) {
189 LoadALSALibrary(
void)
192 if (alsa_handle ==
NULL) {
194 if (alsa_handle ==
NULL) {
198 retval = load_alsa_syms();
210 UnloadALSALibrary(
void)
215 LoadALSALibrary(
void)
229 return (
const char *)
handle;
239 return "plug:surround51";
240 }
else if (channels == 4) {
241 return "plug:surround40";
250 ALSA_WaitDevice(
_THIS)
252 #if SDL_ALSA_NON_BLOCKING
253 const snd_pcm_sframes_t needed = (snd_pcm_sframes_t) this->
spec.
samples;
255 const snd_pcm_sframes_t rc = ALSA_snd_pcm_avail(this->hidden->pcm_handle);
256 if ((rc < 0) && (rc != -EAGAIN)) {
258 fprintf(stderr,
"ALSA snd_pcm_avail failed (unrecoverable): %s\n",
259 ALSA_snd_strerror(rc));
262 }
else if (rc < needed) {
279 #define SWIZ6(T, buf, numframes) \
280 T *ptr = (T *) buf; \
282 for (i = 0; i < numframes; i++, ptr += 6) { \
284 tmp = ptr[2]; ptr[2] = ptr[4]; ptr[4] = tmp; \
285 tmp = ptr[3]; ptr[3] = ptr[5]; ptr[5] = tmp; \
289 swizzle_alsa_channels_6_64bit(
void *
buffer,
Uint32 bufferlen)
295 swizzle_alsa_channels_6_32bit(
void *
buffer,
Uint32 bufferlen)
301 swizzle_alsa_channels_6_16bit(
void *
buffer,
Uint32 bufferlen)
307 swizzle_alsa_channels_6_8bit(
void *
buffer,
Uint32 bufferlen)
324 case 8: swizzle_alsa_channels_6_8bit(
buffer, bufferlen);
break;
325 case 16: swizzle_alsa_channels_6_16bit(
buffer, bufferlen);
break;
326 case 32: swizzle_alsa_channels_6_32bit(
buffer, bufferlen);
break;
327 case 64: swizzle_alsa_channels_6_64bit(
buffer, bufferlen);
break;
328 default:
SDL_assert(!
"unhandled bitsize");
break;
335 #ifdef SND_CHMAP_API_VERSION
346 ALSA_PlayDevice(
_THIS)
348 const Uint8 *sample_buf = (
const Uint8 *) this->hidden->mixbuf;
351 snd_pcm_uframes_t frames_left = ((snd_pcm_uframes_t) this->
spec.
samples);
353 this->hidden->swizzle_func(
this, this->hidden->mixbuf, frames_left);
356 int status = ALSA_snd_pcm_writei(this->hidden->pcm_handle,
357 sample_buf, frames_left);
360 if (status == -EAGAIN) {
366 status = ALSA_snd_pcm_recover(this->hidden->pcm_handle, status, 0);
369 fprintf(stderr,
"ALSA write failed (unrecoverable): %s\n",
370 ALSA_snd_strerror(status));
376 else if (status == 0) {
383 sample_buf += status * frame_size;
384 frames_left -= status;
389 ALSA_GetDeviceBuf(
_THIS)
391 return (this->hidden->mixbuf);
395 ALSA_CaptureFromDevice(
_THIS,
void *
buffer,
int buflen)
400 const int total_frames = buflen / frame_size;
401 snd_pcm_uframes_t frames_left = total_frames;
402 snd_pcm_uframes_t wait_time = frame_size / 2;
409 status = ALSA_snd_pcm_readi(this->hidden->pcm_handle,
410 sample_buf, frames_left);
412 if (status == -EAGAIN) {
413 ALSA_snd_pcm_wait(this->hidden->pcm_handle, wait_time);
416 else if (status < 0) {
418 status = ALSA_snd_pcm_recover(this->hidden->pcm_handle, status, 0);
421 fprintf(stderr,
"ALSA read failed (unrecoverable): %s\n",
422 ALSA_snd_strerror(status));
429 sample_buf += status * frame_size;
430 frames_left -= status;
433 this->hidden->swizzle_func(
this,
buffer, total_frames - frames_left);
435 return (total_frames - frames_left) * frame_size;
439 ALSA_FlushCapture(
_THIS)
441 ALSA_snd_pcm_reset(this->hidden->pcm_handle);
445 ALSA_CloseDevice(
_THIS)
447 if (this->hidden->pcm_handle) {
454 ALSA_snd_pcm_close(this->hidden->pcm_handle);
461 ALSA_set_buffer_size(
_THIS, snd_pcm_hw_params_t *
params)
464 snd_pcm_hw_params_t *hwparams;
466 snd_pcm_uframes_t persize;
469 snd_pcm_hw_params_alloca(&hwparams);
470 ALSA_snd_pcm_hw_params_copy(hwparams,
params);
474 status = ALSA_snd_pcm_hw_params_set_period_size_near(
475 this->hidden->pcm_handle, hwparams, &persize,
NULL);
482 status = ALSA_snd_pcm_hw_params_set_buffer_size_near(
483 this->hidden->pcm_handle, hwparams, &
bufsize);
489 status = ALSA_snd_pcm_hw_params(this->hidden->pcm_handle, hwparams);
498 unsigned int periods = 0;
500 ALSA_snd_pcm_hw_params_get_periods(hwparams, &periods,
NULL);
503 "ALSA: period size = %ld, periods = %u, buffer size = %lu\n",
511 ALSA_OpenDevice(
_THIS,
void *
handle,
const char *devname,
int iscapture)
514 snd_pcm_t *pcm_handle =
NULL;
515 snd_pcm_hw_params_t *hwparams =
NULL;
516 snd_pcm_sw_params_t *swparams =
NULL;
517 snd_pcm_format_t
format = 0;
519 unsigned int rate = 0;
520 unsigned int channels = 0;
521 #ifdef SND_CHMAP_API_VERSION
522 snd_pcm_chmap_t *chmap;
529 if (this->hidden ==
NULL) {
538 iscapture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
542 return SDL_SetError(
"ALSA: Couldn't open audio device: %s",
543 ALSA_snd_strerror(status));
549 snd_pcm_hw_params_alloca(&hwparams);
550 status = ALSA_snd_pcm_hw_params_any(
pcm_handle, hwparams);
552 return SDL_SetError(
"ALSA: Couldn't get hardware config: %s",
553 ALSA_snd_strerror(status));
557 status = ALSA_snd_pcm_hw_params_set_access(
pcm_handle, hwparams,
558 SND_PCM_ACCESS_RW_INTERLEAVED);
560 return SDL_SetError(
"ALSA: Couldn't set interleaved access: %s",
561 ALSA_snd_strerror(status));
567 test_format && (status < 0);) {
569 switch (test_format) {
571 format = SND_PCM_FORMAT_U8;
574 format = SND_PCM_FORMAT_S8;
577 format = SND_PCM_FORMAT_S16_LE;
580 format = SND_PCM_FORMAT_S16_BE;
583 format = SND_PCM_FORMAT_U16_LE;
586 format = SND_PCM_FORMAT_U16_BE;
589 format = SND_PCM_FORMAT_S32_LE;
592 format = SND_PCM_FORMAT_S32_BE;
595 format = SND_PCM_FORMAT_FLOAT_LE;
598 format = SND_PCM_FORMAT_FLOAT_BE;
605 status = ALSA_snd_pcm_hw_params_set_format(
pcm_handle,
613 return SDL_SetError(
"ALSA: Couldn't find any hardware audio formats");
620 this->hidden->swizzle_func = swizzle_alsa_channels;
621 #ifdef SND_CHMAP_API_VERSION
624 ALSA_snd_pcm_chmap_print(chmap,
sizeof(chmap_str), chmap_str);
625 if (
SDL_strcmp(
"FL FR FC LFE RL RR", chmap_str) == 0 ||
626 SDL_strcmp(
"FL FR FC LFE SL SR", chmap_str) == 0) {
627 this->hidden->swizzle_func = no_swizzle;
634 status = ALSA_snd_pcm_hw_params_set_channels(
pcm_handle, hwparams,
638 status = ALSA_snd_pcm_hw_params_get_channels(hwparams, &channels);
640 return SDL_SetError(
"ALSA: Couldn't set audio channels");
647 status = ALSA_snd_pcm_hw_params_set_rate_near(
pcm_handle, hwparams,
650 return SDL_SetError(
"ALSA: Couldn't set audio frequency: %s",
651 ALSA_snd_strerror(status));
656 status = ALSA_set_buffer_size(
this, hwparams);
658 return SDL_SetError(
"Couldn't set hardware audio parameters: %s", ALSA_snd_strerror(status));
662 snd_pcm_sw_params_alloca(&swparams);
663 status = ALSA_snd_pcm_sw_params_current(
pcm_handle, swparams);
665 return SDL_SetError(
"ALSA: Couldn't get software config: %s",
666 ALSA_snd_strerror(status));
670 return SDL_SetError(
"Couldn't set minimum available samples: %s",
671 ALSA_snd_strerror(status));
674 ALSA_snd_pcm_sw_params_set_start_threshold(
pcm_handle, swparams, 1);
676 return SDL_SetError(
"ALSA: Couldn't set start threshold: %s",
677 ALSA_snd_strerror(status));
679 status = ALSA_snd_pcm_sw_params(
pcm_handle, swparams);
681 return SDL_SetError(
"Couldn't set software audio parameters: %s",
682 ALSA_snd_strerror(status));
690 this->hidden->mixlen = this->
spec.
size;
692 if (this->hidden->mixbuf ==
NULL) {
695 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->hidden->mixlen);
698 #if !SDL_ALSA_NON_BLOCKING
708 typedef struct ALSA_Device
712 struct ALSA_Device *next;
716 add_device(
const int iscapture,
const char *
name,
void *hint, ALSA_Device **pSeen)
718 ALSA_Device *dev =
SDL_malloc(
sizeof (ALSA_Device));
732 desc = ALSA_snd_device_name_get_hint(hint,
"DESC");
738 desc = (
char *)
name;
746 if ((ptr = strchr(desc,
'\n')) !=
NULL) {
765 dev->iscapture = iscapture;
775 ALSA_HotplugThread(
void *arg)
777 SDL_sem *first_run_semaphore = (SDL_sem *) arg;
791 if (ALSA_snd_device_name_hint(-1,
"pcm", &hints) != -1) {
793 const char *match =
NULL;
794 int bestmatch = 0xFFFF;
795 size_t match_len = 0;
797 static const char *
const prefixes[] = {
798 "hw:",
"sysdefault:",
"default:",
NULL
807 for (
i = 0; hints[
i];
i++) {
808 char *
name = ALSA_snd_device_name_get_hint(hints[
i],
"NAME");
818 for (
j = 0; prefixes[
j];
j++) {
819 const char *prefix = prefixes[
j];
825 match_len = prefixlen;
834 for (
i = 0; hints[
i];
i++) {
838 if ((!match) && (defaultdev !=
i)) {
842 name = ALSA_snd_device_name_get_hint(hints[
i],
"NAME");
849 char *ioid = ALSA_snd_device_name_get_hint(hints[
i],
"IOID");
857 if (!isoutput && !isinput) {
863 for (dev = unseen; dev; dev = next) {
865 if ( (
SDL_strcmp(dev->name,
name) == 0) && (((isinput) && dev->iscapture) || ((isoutput) && !dev->iscapture)) ) {
874 if (isoutput) have_output =
SDL_TRUE;
880 if (isinput && !have_input) {
883 if (isoutput && !have_output) {
891 ALSA_snd_device_name_free_hint(hints);
896 for (dev = unseen; dev; dev = next) {
906 if (first_run_semaphore) {
908 first_run_semaphore =
NULL;
919 for (dev =
devices; dev; dev = next) {
930 ALSA_DetectDevices(
void)
940 ALSA_hotplug_thread =
SDL_CreateThread(ALSA_HotplugThread,
"SDLHotplugALSA", semaphore);
941 if (ALSA_hotplug_thread) {
949 ALSA_Deinitialize(
void)
951 if (ALSA_hotplug_thread !=
NULL) {
954 ALSA_hotplug_thread =
NULL;
963 if (LoadALSALibrary() < 0) {
985 "alsa",
"ALSA PCM audio", ALSA_Init, 0