diff --git a/src/outputs/alsa.c b/src/outputs/alsa.c index 84db1182..8964eb16 100644 --- a/src/outputs/alsa.c +++ b/src/outputs/alsa.c @@ -1185,6 +1185,120 @@ alsa_write(struct output_buffer *obuf) } } +static void +alsa_device_log(int card, snd_ctl_card_info_t *info) +{ + snd_ctl_t *hdl; + snd_mixer_t *mixer; + snd_mixer_elem_t *elem; + char hwdev[14]; // 'hw:' (3) + max_uint (10) + + char *buf = NULL; + unsigned buf_size = 128; + unsigned buf_len = 0; + unsigned len; + + int err; + + snprintf(hwdev, sizeof(hwdev), "hw:%d", card); + + err = snd_ctl_open(&hdl, hwdev, 0); + if (err < 0) + { + DPRINTF(E_WARN, L_LAUDIO, "Failed to probe ALSA card=%d - %s\n", card, snd_strerror(err)); + return; + } + + err = snd_ctl_card_info(hdl, info); + if (err < 0) + { + DPRINTF(E_WARN, L_LAUDIO, "Failed to probe ALSA (info) card=%d - %s\n", card, snd_strerror(err)); + goto error; + } + + err = snd_mixer_open(&mixer, 0); + if (err < 0) + { + DPRINTF(E_WARN, L_LAUDIO, "Failed to probe ALSA (mixer open) card=%d - %s\n", card, snd_strerror(err)); + goto error; + } + + err = snd_mixer_attach(mixer, hwdev); + if (err < 0) + { + DPRINTF(E_WARN, L_LAUDIO, "Failed to probe ALSA (mixer attach) card=%d - %s\n", card, snd_strerror(err)); + goto errormixer; + } + + err = snd_mixer_selem_register(mixer, NULL, NULL); + if (err < 0) + { + DPRINTF(E_WARN, L_LAUDIO, "Failed to probe ALSA (mixer setup) card=%d - %s\n", card, snd_strerror(err)); + goto errormixer; + } + + err = snd_mixer_load(mixer); + if (err < 0) + { + DPRINTF(E_WARN, L_LAUDIO, "Failed to probe ALSA (mixer setup) card=%d - %s\n", card, snd_strerror(err)); + goto errormixer; + } + + for (elem = snd_mixer_first_elem(mixer); elem; elem = snd_mixer_elem_next(elem)) + { + if (snd_mixer_selem_has_common_volume(elem) || !snd_mixer_selem_has_playback_volume(elem)) + continue; + + len = strlen(snd_mixer_selem_get_name(elem)) + 3; + if (!buf) + { + if (buf_size < len*2) + buf_size = len*2; + buf = malloc(buf_size+1); + buf[0] = '\0'; + buf_len = 0; + } + else + { + if (buf_len + len > buf_size) + { + buf_size += len + 128; + buf = realloc(buf, buf_size+1); + } + } + strcat(buf, "'"); + strcat(buf, snd_mixer_selem_get_name(elem)); + strcat(buf, "'"); + strcat(buf, " "); + buf_len += len; + } + DPRINTF(E_INFO, L_LAUDIO, "Available ALSA playback/mixer(s) on '%s' (%s): %s\n", hwdev, snd_ctl_card_info_get_name(info), buf ? buf : "n/a"); + +errormixer: + snd_mixer_close(mixer); +error: + snd_ctl_close(hdl); + free(buf); +} + +// walk all the alsa devices here and report valid playback mixers +static void +alsa_device_list() +{ + snd_ctl_card_info_t *info = NULL; + int card = 0; + + snd_ctl_card_info_alloca(&info); + + while (card >= 0) + { + alsa_device_log(card, info); + + if (snd_card_next(&card) < 0) + break; + } +} + static void alsa_device_add(cfg_t* cfg_audio, int id) { @@ -1244,6 +1358,8 @@ alsa_init(void) if (type && (strcasecmp(type, "alsa") != 0)) return -1; + alsa_device_list(); + alsa_sync_disable = cfg_getbool(cfg_audio, "sync_disable"); alsa_latency_history_size = cfg_getint(cfg_audio, "adjust_period_seconds");