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
348 ALSA_PlayDevice(
_THIS)
350 const Uint8 *sample_buf = (
const Uint8 *) this->hidden->mixbuf;
353 snd_pcm_uframes_t frames_left = ((snd_pcm_uframes_t) this->
spec.
samples);
355 this->hidden->swizzle_func(
this, this->hidden->mixbuf, frames_left);
358 int status = ALSA_snd_pcm_writei(this->hidden->pcm_handle,
359 sample_buf, frames_left);
362 if (status == -EAGAIN) {
368 status = ALSA_snd_pcm_recover(this->hidden->pcm_handle, status, 0);
371 fprintf(stderr,
"ALSA write failed (unrecoverable): %s\n",
372 ALSA_snd_strerror(status));
378 else if (status == 0) {
385 sample_buf += status * frame_size;
386 frames_left -= status;
391 ALSA_GetDeviceBuf(
_THIS)
393 return (this->hidden->mixbuf);
397 ALSA_CaptureFromDevice(
_THIS,
void *
buffer,
int buflen)
402 const int total_frames = buflen / frame_size;
403 snd_pcm_uframes_t frames_left = total_frames;
404 snd_pcm_uframes_t wait_time = frame_size / 2;
411 status = ALSA_snd_pcm_readi(this->hidden->pcm_handle,
412 sample_buf, frames_left);
414 if (status == -EAGAIN) {
415 ALSA_snd_pcm_wait(this->hidden->pcm_handle, wait_time);
418 else if (status < 0) {
420 status = ALSA_snd_pcm_recover(this->hidden->pcm_handle, status, 0);
423 fprintf(stderr,
"ALSA read failed (unrecoverable): %s\n",
424 ALSA_snd_strerror(status));
431 sample_buf += status * frame_size;
432 frames_left -= status;
435 this->hidden->swizzle_func(
this,
buffer, total_frames - frames_left);
437 return (total_frames - frames_left) * frame_size;
441 ALSA_FlushCapture(
_THIS)
443 ALSA_snd_pcm_reset(this->hidden->pcm_handle);
447 ALSA_CloseDevice(
_THIS)
449 if (this->hidden->pcm_handle) {
456 ALSA_snd_pcm_close(this->hidden->pcm_handle);
463 ALSA_set_buffer_size(
_THIS, snd_pcm_hw_params_t *
params)
466 snd_pcm_hw_params_t *hwparams;
467 snd_pcm_uframes_t persize;
468 unsigned int periods;
471 snd_pcm_hw_params_alloca(&hwparams);
472 ALSA_snd_pcm_hw_params_copy(hwparams,
params);
476 status = ALSA_snd_pcm_hw_params_set_period_size_near(
477 this->hidden->pcm_handle, hwparams, &persize,
NULL);
484 status = ALSA_snd_pcm_hw_params_set_periods_min(
485 this->hidden->pcm_handle, hwparams, &periods,
NULL);
490 status = ALSA_snd_pcm_hw_params_set_periods_first(
491 this->hidden->pcm_handle, hwparams, &periods,
NULL);
497 status = ALSA_snd_pcm_hw_params(this->hidden->pcm_handle, hwparams);
508 ALSA_snd_pcm_hw_params_get_buffer_size(hwparams, &
bufsize);
511 "ALSA: period size = %ld, periods = %u, buffer size = %lu\n",
519 ALSA_OpenDevice(
_THIS,
void *
handle,
const char *devname,
int iscapture)
522 snd_pcm_t *pcm_handle =
NULL;
523 snd_pcm_hw_params_t *hwparams =
NULL;
524 snd_pcm_sw_params_t *swparams =
NULL;
525 snd_pcm_format_t
format = 0;
527 unsigned int rate = 0;
528 unsigned int channels = 0;
529 #ifdef SND_CHMAP_API_VERSION
530 snd_pcm_chmap_t *chmap;
537 if (this->hidden ==
NULL) {
546 iscapture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
550 return SDL_SetError(
"ALSA: Couldn't open audio device: %s",
551 ALSA_snd_strerror(status));
557 snd_pcm_hw_params_alloca(&hwparams);
558 status = ALSA_snd_pcm_hw_params_any(
pcm_handle, hwparams);
560 return SDL_SetError(
"ALSA: Couldn't get hardware config: %s",
561 ALSA_snd_strerror(status));
565 status = ALSA_snd_pcm_hw_params_set_access(
pcm_handle, hwparams,
566 SND_PCM_ACCESS_RW_INTERLEAVED);
568 return SDL_SetError(
"ALSA: Couldn't set interleaved access: %s",
569 ALSA_snd_strerror(status));
575 test_format && (status < 0);) {
577 switch (test_format) {
579 format = SND_PCM_FORMAT_U8;
582 format = SND_PCM_FORMAT_S8;
585 format = SND_PCM_FORMAT_S16_LE;
588 format = SND_PCM_FORMAT_S16_BE;
591 format = SND_PCM_FORMAT_U16_LE;
594 format = SND_PCM_FORMAT_U16_BE;
597 format = SND_PCM_FORMAT_S32_LE;
600 format = SND_PCM_FORMAT_S32_BE;
603 format = SND_PCM_FORMAT_FLOAT_LE;
606 format = SND_PCM_FORMAT_FLOAT_BE;
613 status = ALSA_snd_pcm_hw_params_set_format(
pcm_handle,
621 return SDL_SetError(
"ALSA: Couldn't find any hardware audio formats");
628 this->hidden->swizzle_func = swizzle_alsa_channels;
629 #ifdef SND_CHMAP_API_VERSION
632 ALSA_snd_pcm_chmap_print(chmap,
sizeof(chmap_str), chmap_str);
633 if (
SDL_strcmp(
"FL FR FC LFE RL RR", chmap_str) == 0 ||
634 SDL_strcmp(
"FL FR FC LFE SL SR", chmap_str) == 0) {
635 this->hidden->swizzle_func = no_swizzle;
642 status = ALSA_snd_pcm_hw_params_set_channels(
pcm_handle, hwparams,
646 status = ALSA_snd_pcm_hw_params_get_channels(hwparams, &channels);
648 return SDL_SetError(
"ALSA: Couldn't set audio channels");
655 status = ALSA_snd_pcm_hw_params_set_rate_near(
pcm_handle, hwparams,
658 return SDL_SetError(
"ALSA: Couldn't set audio frequency: %s",
659 ALSA_snd_strerror(status));
664 status = ALSA_set_buffer_size(
this, hwparams);
666 return SDL_SetError(
"Couldn't set hardware audio parameters: %s", ALSA_snd_strerror(status));
670 snd_pcm_sw_params_alloca(&swparams);
671 status = ALSA_snd_pcm_sw_params_current(
pcm_handle, swparams);
673 return SDL_SetError(
"ALSA: Couldn't get software config: %s",
674 ALSA_snd_strerror(status));
678 return SDL_SetError(
"Couldn't set minimum available samples: %s",
679 ALSA_snd_strerror(status));
682 ALSA_snd_pcm_sw_params_set_start_threshold(
pcm_handle, swparams, 1);
684 return SDL_SetError(
"ALSA: Couldn't set start threshold: %s",
685 ALSA_snd_strerror(status));
687 status = ALSA_snd_pcm_sw_params(
pcm_handle, swparams);
689 return SDL_SetError(
"Couldn't set software audio parameters: %s",
690 ALSA_snd_strerror(status));
698 this->hidden->mixlen = this->
spec.
size;
700 if (this->hidden->mixbuf ==
NULL) {
703 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->hidden->mixlen);
706 #if !SDL_ALSA_NON_BLOCKING
716 typedef struct ALSA_Device
720 struct ALSA_Device *next;
724 add_device(
const int iscapture,
const char *
name,
void *hint, ALSA_Device **pSeen)
726 ALSA_Device *dev =
SDL_malloc(
sizeof (ALSA_Device));
740 desc = ALSA_snd_device_name_get_hint(hint,
"DESC");
746 desc = (
char *)
name;
754 if ((
ptr = strchr(desc,
'\n')) !=
NULL) {
773 dev->iscapture = iscapture;
783 ALSA_HotplugThread(
void *arg)
785 SDL_sem *first_run_semaphore = (SDL_sem *) arg;
799 if (ALSA_snd_device_name_hint(-1,
"pcm", &hints) == 0) {
801 const char *match =
NULL;
802 int bestmatch = 0xFFFF;
803 size_t match_len = 0;
805 static const char *
const prefixes[] = {
806 "hw:",
"sysdefault:",
"default:",
NULL
815 for (
i = 0; hints[
i];
i++) {
816 char *
name = ALSA_snd_device_name_get_hint(hints[
i],
"NAME");
826 for (
j = 0; prefixes[
j];
j++) {
827 const char *prefix = prefixes[
j];
833 match_len = prefixlen;
842 for (
i = 0; hints[
i];
i++) {
846 if ((!match) && (defaultdev !=
i)) {
850 name = ALSA_snd_device_name_get_hint(hints[
i],
"NAME");
857 char *ioid = ALSA_snd_device_name_get_hint(hints[
i],
"IOID");
865 if (!isoutput && !isinput) {
871 for (dev = unseen; dev; dev = next) {
873 if ( (
SDL_strcmp(dev->name,
name) == 0) && (((isinput) && dev->iscapture) || ((isoutput) && !dev->iscapture)) ) {
882 if (isoutput) have_output =
SDL_TRUE;
888 if (isinput && !have_input) {
891 if (isoutput && !have_output) {
899 ALSA_snd_device_name_free_hint(hints);
904 for (dev = unseen; dev; dev = next) {
914 if (first_run_semaphore) {
916 first_run_semaphore =
NULL;
927 for (dev =
devices; dev; dev = next) {
938 ALSA_DetectDevices(
void)
948 ALSA_hotplug_thread =
SDL_CreateThread(ALSA_HotplugThread,
"SDLHotplugALSA", semaphore);
949 if (ALSA_hotplug_thread) {
957 ALSA_Deinitialize(
void)
959 if (ALSA_hotplug_thread !=
NULL) {
962 ALSA_hotplug_thread =
NULL;
971 if (LoadALSALibrary() < 0) {
993 "alsa",
"ALSA PCM audio", ALSA_Init, 0