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_min)
76 (snd_pcm_t *, snd_pcm_hw_params_t *,
unsigned int *,
int *);
77 static int (*ALSA_snd_pcm_hw_params_set_periods_first)
78 (snd_pcm_t *, snd_pcm_hw_params_t *,
unsigned int *,
int *);
79 static int (*ALSA_snd_pcm_hw_params_get_periods)
80 (
const snd_pcm_hw_params_t *,
unsigned int *,
int *);
81 static int (*ALSA_snd_pcm_hw_params_set_buffer_size_near)
82 (snd_pcm_t *pcm, snd_pcm_hw_params_t *, snd_pcm_uframes_t *);
83 static int (*ALSA_snd_pcm_hw_params_get_buffer_size)
84 (
const snd_pcm_hw_params_t *, snd_pcm_uframes_t *);
85 static int (*ALSA_snd_pcm_hw_params) (snd_pcm_t *, snd_pcm_hw_params_t *);
86 static int (*ALSA_snd_pcm_sw_params_current) (snd_pcm_t *,
87 snd_pcm_sw_params_t *);
88 static int (*ALSA_snd_pcm_sw_params_set_start_threshold)
89 (snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t);
90 static int (*ALSA_snd_pcm_sw_params) (snd_pcm_t *, snd_pcm_sw_params_t *);
91 static int (*ALSA_snd_pcm_nonblock) (snd_pcm_t *, int);
92 static int (*ALSA_snd_pcm_wait)(snd_pcm_t *, int);
93 static int (*ALSA_snd_pcm_sw_params_set_avail_min)
94 (snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t);
95 static int (*ALSA_snd_pcm_reset)(snd_pcm_t *);
96 static int (*ALSA_snd_device_name_hint) (int,
const char *,
void ***);
97 static char* (*ALSA_snd_device_name_get_hint) (
const void *,
const char *);
98 static int (*ALSA_snd_device_name_free_hint) (
void **);
99 static snd_pcm_sframes_t (*ALSA_snd_pcm_avail)(snd_pcm_t *);
100 #ifdef SND_CHMAP_API_VERSION
101 static snd_pcm_chmap_t* (*ALSA_snd_pcm_get_chmap) (snd_pcm_t *);
102 static int (*ALSA_snd_pcm_chmap_print) (
const snd_pcm_chmap_t *
map,
size_t maxlen,
char *
buf);
105 #ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
106 #define snd_pcm_hw_params_sizeof ALSA_snd_pcm_hw_params_sizeof
107 #define snd_pcm_sw_params_sizeof ALSA_snd_pcm_sw_params_sizeof
109 static const char *alsa_library = SDL_AUDIO_DRIVER_ALSA_DYNAMIC;
110 static void *alsa_handle =
NULL;
113 load_alsa_sym(
const char *fn,
void **
addr)
125 #define SDL_ALSA_SYM(x) \
126 if (!load_alsa_sym(#x, (void **) (char *) &ALSA_##x)) return -1
128 #define SDL_ALSA_SYM(x) ALSA_##x = x
134 SDL_ALSA_SYM(snd_pcm_open);
135 SDL_ALSA_SYM(snd_pcm_close);
136 SDL_ALSA_SYM(snd_pcm_writei);
137 SDL_ALSA_SYM(snd_pcm_readi);
138 SDL_ALSA_SYM(snd_pcm_recover);
139 SDL_ALSA_SYM(snd_pcm_prepare);
140 SDL_ALSA_SYM(snd_pcm_drain);
141 SDL_ALSA_SYM(snd_strerror);
142 SDL_ALSA_SYM(snd_pcm_hw_params_sizeof);
143 SDL_ALSA_SYM(snd_pcm_sw_params_sizeof);
144 SDL_ALSA_SYM(snd_pcm_hw_params_copy);
145 SDL_ALSA_SYM(snd_pcm_hw_params_any);
146 SDL_ALSA_SYM(snd_pcm_hw_params_set_access);
147 SDL_ALSA_SYM(snd_pcm_hw_params_set_format);
148 SDL_ALSA_SYM(snd_pcm_hw_params_set_channels);
149 SDL_ALSA_SYM(snd_pcm_hw_params_get_channels);
150 SDL_ALSA_SYM(snd_pcm_hw_params_set_rate_near);
151 SDL_ALSA_SYM(snd_pcm_hw_params_set_period_size_near);
152 SDL_ALSA_SYM(snd_pcm_hw_params_get_period_size);
153 SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_min);
154 SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_first);
155 SDL_ALSA_SYM(snd_pcm_hw_params_get_periods);
156 SDL_ALSA_SYM(snd_pcm_hw_params_set_buffer_size_near);
157 SDL_ALSA_SYM(snd_pcm_hw_params_get_buffer_size);
158 SDL_ALSA_SYM(snd_pcm_hw_params);
159 SDL_ALSA_SYM(snd_pcm_sw_params_current);
160 SDL_ALSA_SYM(snd_pcm_sw_params_set_start_threshold);
161 SDL_ALSA_SYM(snd_pcm_sw_params);
162 SDL_ALSA_SYM(snd_pcm_nonblock);
163 SDL_ALSA_SYM(snd_pcm_wait);
164 SDL_ALSA_SYM(snd_pcm_sw_params_set_avail_min);
165 SDL_ALSA_SYM(snd_pcm_reset);
166 SDL_ALSA_SYM(snd_device_name_hint);
167 SDL_ALSA_SYM(snd_device_name_get_hint);
168 SDL_ALSA_SYM(snd_device_name_free_hint);
169 SDL_ALSA_SYM(snd_pcm_avail);
170 #ifdef SND_CHMAP_API_VERSION
171 SDL_ALSA_SYM(snd_pcm_get_chmap);
172 SDL_ALSA_SYM(snd_pcm_chmap_print);
180 #ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
183 UnloadALSALibrary(
void)
185 if (alsa_handle !=
NULL) {
192 LoadALSALibrary(
void)
195 if (alsa_handle ==
NULL) {
197 if (alsa_handle ==
NULL) {
201 retval = load_alsa_syms();
213 UnloadALSALibrary(
void)
218 LoadALSALibrary(
void)
232 return (
const char *)
handle;
242 return "plug:surround51";
243 }
else if (channels == 4) {
244 return "plug:surround40";
253 ALSA_WaitDevice(
_THIS)
255 #if SDL_ALSA_NON_BLOCKING
256 const snd_pcm_sframes_t needed = (snd_pcm_sframes_t) this->
spec.
samples;
258 const snd_pcm_sframes_t rc = ALSA_snd_pcm_avail(this->hidden->pcm_handle);
259 if ((rc < 0) && (rc != -EAGAIN)) {
261 fprintf(stderr,
"ALSA snd_pcm_avail failed (unrecoverable): %s\n",
262 ALSA_snd_strerror(rc));
265 }
else if (rc < needed) {
282 #define SWIZ6(T, buf, numframes) \
283 T *ptr = (T *) buf; \
285 for (i = 0; i < numframes; i++, ptr += 6) { \
287 tmp = ptr[2]; ptr[2] = ptr[4]; ptr[4] = tmp; \
288 tmp = ptr[3]; ptr[3] = ptr[5]; ptr[5] = tmp; \
292 swizzle_alsa_channels_6_64bit(
void *
buffer,
Uint32 bufferlen)
298 swizzle_alsa_channels_6_32bit(
void *
buffer,
Uint32 bufferlen)
304 swizzle_alsa_channels_6_16bit(
void *
buffer,
Uint32 bufferlen)
310 swizzle_alsa_channels_6_8bit(
void *
buffer,
Uint32 bufferlen)
327 case 8: swizzle_alsa_channels_6_8bit(
buffer, bufferlen);
break;
328 case 16: swizzle_alsa_channels_6_16bit(
buffer, bufferlen);
break;
329 case 32: swizzle_alsa_channels_6_32bit(
buffer, bufferlen);
break;
330 case 64: swizzle_alsa_channels_6_64bit(
buffer, bufferlen);
break;
331 default:
SDL_assert(!
"unhandled bitsize");
break;
338 #ifdef SND_CHMAP_API_VERSION
349 ALSA_PlayDevice(
_THIS)
351 const Uint8 *sample_buf = (
const Uint8 *) this->hidden->mixbuf;
354 snd_pcm_uframes_t frames_left = ((snd_pcm_uframes_t) this->
spec.
samples);
356 this->hidden->swizzle_func(
this, this->hidden->mixbuf, frames_left);
359 int status = ALSA_snd_pcm_writei(this->hidden->pcm_handle,
360 sample_buf, frames_left);
363 if (status == -EAGAIN) {
369 status = ALSA_snd_pcm_recover(this->hidden->pcm_handle, status, 0);
372 fprintf(stderr,
"ALSA write failed (unrecoverable): %s\n",
373 ALSA_snd_strerror(status));
379 else if (status == 0) {
386 sample_buf += status * frame_size;
387 frames_left -= status;
392 ALSA_GetDeviceBuf(
_THIS)
394 return (this->hidden->mixbuf);
398 ALSA_CaptureFromDevice(
_THIS,
void *
buffer,
int buflen)
403 const int total_frames = buflen / frame_size;
404 snd_pcm_uframes_t frames_left = total_frames;
405 snd_pcm_uframes_t wait_time = frame_size / 2;
412 status = ALSA_snd_pcm_readi(this->hidden->pcm_handle,
413 sample_buf, frames_left);
415 if (status == -EAGAIN) {
416 ALSA_snd_pcm_wait(this->hidden->pcm_handle, wait_time);
419 else if (status < 0) {
421 status = ALSA_snd_pcm_recover(this->hidden->pcm_handle, status, 0);
424 fprintf(stderr,
"ALSA read failed (unrecoverable): %s\n",
425 ALSA_snd_strerror(status));
432 sample_buf += status * frame_size;
433 frames_left -= status;
436 this->hidden->swizzle_func(
this,
buffer, total_frames - frames_left);
438 return (total_frames - frames_left) * frame_size;
442 ALSA_FlushCapture(
_THIS)
444 ALSA_snd_pcm_reset(this->hidden->pcm_handle);
448 ALSA_CloseDevice(
_THIS)
450 if (this->hidden->pcm_handle) {
457 ALSA_snd_pcm_close(this->hidden->pcm_handle);
464 ALSA_set_buffer_size(
_THIS, snd_pcm_hw_params_t *
params)
467 snd_pcm_hw_params_t *hwparams;
468 snd_pcm_uframes_t persize;
469 unsigned int periods;
472 snd_pcm_hw_params_alloca(&hwparams);
473 ALSA_snd_pcm_hw_params_copy(hwparams,
params);
477 status = ALSA_snd_pcm_hw_params_set_period_size_near(
478 this->hidden->pcm_handle, hwparams, &persize,
NULL);
485 status = ALSA_snd_pcm_hw_params_set_periods_min(
486 this->hidden->pcm_handle, hwparams, &periods,
NULL);
491 status = ALSA_snd_pcm_hw_params_set_periods_first(
492 this->hidden->pcm_handle, hwparams, &periods,
NULL);
498 status = ALSA_snd_pcm_hw_params(this->hidden->pcm_handle, hwparams);
509 ALSA_snd_pcm_hw_params_get_buffer_size(hwparams, &
bufsize);
512 "ALSA: period size = %ld, periods = %u, buffer size = %lu\n",
520 ALSA_OpenDevice(
_THIS,
void *
handle,
const char *devname,
int iscapture)
523 snd_pcm_t *pcm_handle =
NULL;
524 snd_pcm_hw_params_t *hwparams =
NULL;
525 snd_pcm_sw_params_t *swparams =
NULL;
526 snd_pcm_format_t
format = 0;
528 unsigned int rate = 0;
529 unsigned int channels = 0;
530 #ifdef SND_CHMAP_API_VERSION
531 snd_pcm_chmap_t *chmap;
538 if (this->hidden ==
NULL) {
547 iscapture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
551 return SDL_SetError(
"ALSA: Couldn't open audio device: %s",
552 ALSA_snd_strerror(status));
558 snd_pcm_hw_params_alloca(&hwparams);
559 status = ALSA_snd_pcm_hw_params_any(
pcm_handle, hwparams);
561 return SDL_SetError(
"ALSA: Couldn't get hardware config: %s",
562 ALSA_snd_strerror(status));
566 status = ALSA_snd_pcm_hw_params_set_access(
pcm_handle, hwparams,
567 SND_PCM_ACCESS_RW_INTERLEAVED);
569 return SDL_SetError(
"ALSA: Couldn't set interleaved access: %s",
570 ALSA_snd_strerror(status));
576 test_format && (status < 0);) {
578 switch (test_format) {
580 format = SND_PCM_FORMAT_U8;
583 format = SND_PCM_FORMAT_S8;
586 format = SND_PCM_FORMAT_S16_LE;
589 format = SND_PCM_FORMAT_S16_BE;
592 format = SND_PCM_FORMAT_U16_LE;
595 format = SND_PCM_FORMAT_U16_BE;
598 format = SND_PCM_FORMAT_S32_LE;
601 format = SND_PCM_FORMAT_S32_BE;
604 format = SND_PCM_FORMAT_FLOAT_LE;
607 format = SND_PCM_FORMAT_FLOAT_BE;
614 status = ALSA_snd_pcm_hw_params_set_format(
pcm_handle,
622 return SDL_SetError(
"ALSA: Couldn't find any hardware audio formats");
629 this->hidden->swizzle_func = swizzle_alsa_channels;
630 #ifdef SND_CHMAP_API_VERSION
633 ALSA_snd_pcm_chmap_print(chmap,
sizeof(chmap_str), chmap_str);
634 if (
SDL_strcmp(
"FL FR FC LFE RL RR", chmap_str) == 0 ||
635 SDL_strcmp(
"FL FR FC LFE SL SR", chmap_str) == 0) {
636 this->hidden->swizzle_func = no_swizzle;
643 status = ALSA_snd_pcm_hw_params_set_channels(
pcm_handle, hwparams,
647 status = ALSA_snd_pcm_hw_params_get_channels(hwparams, &channels);
649 return SDL_SetError(
"ALSA: Couldn't set audio channels");
656 status = ALSA_snd_pcm_hw_params_set_rate_near(
pcm_handle, hwparams,
659 return SDL_SetError(
"ALSA: Couldn't set audio frequency: %s",
660 ALSA_snd_strerror(status));
665 status = ALSA_set_buffer_size(
this, hwparams);
667 return SDL_SetError(
"Couldn't set hardware audio parameters: %s", ALSA_snd_strerror(status));
671 snd_pcm_sw_params_alloca(&swparams);
672 status = ALSA_snd_pcm_sw_params_current(
pcm_handle, swparams);
674 return SDL_SetError(
"ALSA: Couldn't get software config: %s",
675 ALSA_snd_strerror(status));
679 return SDL_SetError(
"Couldn't set minimum available samples: %s",
680 ALSA_snd_strerror(status));
683 ALSA_snd_pcm_sw_params_set_start_threshold(
pcm_handle, swparams, 1);
685 return SDL_SetError(
"ALSA: Couldn't set start threshold: %s",
686 ALSA_snd_strerror(status));
688 status = ALSA_snd_pcm_sw_params(
pcm_handle, swparams);
690 return SDL_SetError(
"Couldn't set software audio parameters: %s",
691 ALSA_snd_strerror(status));
699 this->hidden->mixlen = this->
spec.
size;
701 if (this->hidden->mixbuf ==
NULL) {
704 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->hidden->mixlen);
707 #if !SDL_ALSA_NON_BLOCKING
717 typedef struct ALSA_Device
721 struct ALSA_Device *next;
725 add_device(
const int iscapture,
const char *
name,
void *hint, ALSA_Device **pSeen)
727 ALSA_Device *dev =
SDL_malloc(
sizeof (ALSA_Device));
741 desc = ALSA_snd_device_name_get_hint(hint,
"DESC");
747 desc = (
char *)
name;
755 if ((ptr = strchr(desc,
'\n')) !=
NULL) {
774 dev->iscapture = iscapture;
784 ALSA_HotplugThread(
void *arg)
786 SDL_sem *first_run_semaphore = (SDL_sem *) arg;
800 if (ALSA_snd_device_name_hint(-1,
"pcm", &hints) == 0) {
802 const char *match =
NULL;
803 int bestmatch = 0xFFFF;
804 size_t match_len = 0;
806 static const char *
const prefixes[] = {
807 "hw:",
"sysdefault:",
"default:",
NULL
816 for (
i = 0; hints[
i];
i++) {
817 char *
name = ALSA_snd_device_name_get_hint(hints[
i],
"NAME");
827 for (
j = 0; prefixes[
j];
j++) {
828 const char *prefix = prefixes[
j];
834 match_len = prefixlen;
843 for (
i = 0; hints[
i];
i++) {
847 if ((!match) && (defaultdev !=
i)) {
851 name = ALSA_snd_device_name_get_hint(hints[
i],
"NAME");
858 char *ioid = ALSA_snd_device_name_get_hint(hints[
i],
"IOID");
866 if (!isoutput && !isinput) {
872 for (dev = unseen; dev; dev = next) {
874 if ( (
SDL_strcmp(dev->name,
name) == 0) && (((isinput) && dev->iscapture) || ((isoutput) && !dev->iscapture)) ) {
883 if (isoutput) have_output =
SDL_TRUE;
889 if (isinput && !have_input) {
892 if (isoutput && !have_output) {
900 ALSA_snd_device_name_free_hint(hints);
905 for (dev = unseen; dev; dev = next) {
915 if (first_run_semaphore) {
917 first_run_semaphore =
NULL;
928 for (dev =
devices; dev; dev = next) {
939 ALSA_DetectDevices(
void)
949 ALSA_hotplug_thread =
SDL_CreateThread(ALSA_HotplugThread,
"SDLHotplugALSA", semaphore);
950 if (ALSA_hotplug_thread) {
958 ALSA_Deinitialize(
void)
960 if (ALSA_hotplug_thread !=
NULL) {
963 ALSA_hotplug_thread =
NULL;
972 if (LoadALSALibrary() < 0) {
994 "alsa",
"ALSA PCM audio", ALSA_Init, 0