29 #include <alsa/asoundlib.h> 34 #define ALSA_DEFAULT_CARD "(default)" 35 #define ALSA_DEFAULT_HCTL "default" 43 const char *strerr,
const char *format, ...)
49 snprintf(buf,
sizeof buf,
"%s", format);
50 else if (card && !strerr)
51 snprintf(buf,
sizeof buf,
"'%s': %s", card, format);
52 else if (!card && strerr)
53 snprintf(buf,
sizeof buf,
"%s: %s", format, strerr);
55 snprintf(buf,
sizeof buf,
"'%s': %s: %s", card, format, strerr);
57 va_start(args, format);
62 #define ALSA_ERR(err, ...) \ 63 alsa_log_msg(LOG_ERROR, NULL, snd_strerror(err), __VA_ARGS__) 65 #define ALSA_CARD_DEBUG(card, ...) \ 66 alsa_log_msg(LOG_DEBUG, card, NULL, __VA_ARGS__) 68 #define ALSA_CARD_WARN(card, ...) \ 69 alsa_log_msg(LOG_WARN, card, NULL, __VA_ARGS__) 71 #define ALSA_CARD_ERR(card, err, ...) \ 72 alsa_log_msg(LOG_ERROR, card, snd_strerror(err), __VA_ARGS__) 86 #define MAX_LINEAR_DB_SCALE 24 88 static inline gboolean
98 return lrint(ceil(x));
100 return lrint(floor(x));
109 return snd_mixer_selem_get_name(elem);
116 snd_mixer_selem_channel_id_t channel = SND_MIXER_SCHN_FRONT_RIGHT;
118 long min, max, value;
122 err = snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
129 ALSA_CARD_WARN(hctl,
"Invalid playback volume range [%ld - %ld]", min, max);
133 err = snd_mixer_selem_get_playback_volume(elem, channel, &value);
139 *volume = (value - min) / (
double) (max - min);
149 long min, max, value;
151 err = snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
158 ALSA_CARD_WARN(hctl,
"Invalid playback volume range [%ld - %ld]", min, max);
162 value =
lrint_dir(volume * (max - min), dir) + min;
164 err = snd_mixer_selem_set_playback_volume_all(elem, value);
166 ALSA_CARD_ERR(hctl, err,
"Can't set playback volume to %ld", value);
177 snd_mixer_selem_channel_id_t channel = SND_MIXER_SCHN_FRONT_RIGHT;
179 long min, max, value;
180 double normalized, min_norm;
184 err = snd_mixer_selem_get_playback_dB_range(elem, &min, &max);
191 ALSA_CARD_WARN(hctl,
"Invalid playback dB range [%ld - %ld]", min, max);
195 err = snd_mixer_selem_get_playback_dB(elem, channel, &value);
202 normalized = (value - min) / (
double) (max - min);
204 normalized = exp10((value - max) / 6000.0);
205 if (min != SND_CTL_TLV_DB_GAIN_MUTE) {
206 min_norm = exp10((min - max) / 6000.0);
207 normalized = (normalized - min_norm) / (1 - min_norm);
211 *volume = normalized;
223 long min, max, value;
225 err = snd_mixer_selem_get_playback_dB_range(elem, &min, &max);
232 ALSA_CARD_WARN(hctl,
"Invalid playback dB range [%ld - %ld]", min, max);
237 value =
lrint_dir(volume * (max - min), dir) + min;
239 if (min != SND_CTL_TLV_DB_GAIN_MUTE) {
240 double min_norm = exp10((min - max) / 6000.0);
241 volume = volume * (1 - min_norm) + min_norm;
243 value =
lrint_dir(6000.0 * log10(volume), dir) + max;
246 err = snd_mixer_selem_set_playback_dB_all(elem, value, dir);
248 ALSA_CARD_ERR(hctl, err,
"Can't set playback dB to %ld", value);
261 return snd_mixer_selem_has_playback_switch(elem) ? TRUE : FALSE;
268 snd_mixer_selem_channel_id_t channel = SND_MIXER_SCHN_FRONT_RIGHT;
274 if (snd_mixer_selem_has_playback_switch(elem)) {
275 err = snd_mixer_selem_get_playback_switch(elem, channel, &value);
286 *muted = value == 0 ? TRUE : FALSE;
301 value = mute ? 0 : 1;
303 if (snd_mixer_selem_has_playback_switch(elem)) {
304 err = snd_mixer_selem_set_playback_switch_all(elem, value);
325 static struct pollfd *
332 count = snd_mixer_poll_descriptors_count(mixer);
335 fds = g_new0(
struct pollfd, count + 1);
336 err = snd_mixer_poll_descriptors(mixer, fds, count);
353 snd_mixer_elem_t *elem = NULL;
355 elem = snd_mixer_first_elem(mixer);
357 if (snd_mixer_selem_has_playback_volume(elem)) {
358 const char *chan_name = snd_mixer_selem_get_name(elem);
359 list = g_slist_append(list, g_strdup(chan_name));
361 elem = snd_mixer_elem_next(elem);
371 GSList *playable_chans;
374 if (playable_chans) {
375 g_slist_free_full(playable_chans, g_free);
383 static snd_mixer_elem_t *
386 snd_mixer_elem_t *elem;
387 snd_mixer_selem_id_t *sid;
392 ALSA_CARD_DEBUG(hctl,
"Looking for playable mixer element '%s'", channel);
395 snd_mixer_selem_id_alloca(&sid);
396 snd_mixer_selem_id_set_name(sid, channel);
397 elem = snd_mixer_find_selem(mixer, sid);
404 if (!snd_mixer_selem_has_playback_volume(elem)) {
405 ALSA_CARD_WARN(hctl,
"Mixer element '%s' is not playable", channel);
413 static snd_mixer_elem_t *
416 snd_mixer_elem_t *elem;
418 ALSA_CARD_DEBUG(hctl,
"Looking for the first playable mixer element...");
421 elem = snd_mixer_first_elem(mixer);
423 if (snd_mixer_selem_has_playback_volume(elem))
425 elem = snd_mixer_elem_next(elem);
444 snd_mixer_free(mixer);
446 err = snd_mixer_detach(mixer, hctl);
450 err = snd_mixer_close(mixer);
460 snd_mixer_t *mixer = NULL;
464 err = snd_mixer_open(&mixer, 0);
470 err = snd_mixer_attach(mixer, hctl);
476 err = snd_mixer_selem_register(mixer, NULL, NULL);
478 ALSA_CARD_ERR(hctl, err,
"Can't register mixer simple element");
482 err = snd_mixer_load(mixer);
491 snd_mixer_close(mixer);
562 err = snd_card_next(&iter->
number);
564 ALSA_ERR(err,
"Can't enumerate sound cards");
573 err = snd_card_get_name(iter->
number, &(iter->
name));
575 ALSA_ERR(err,
"Can't get card name");
580 iter->
hctl = g_strdup_printf(
"hw:%d", iter->
number);
592 GIOFunc func, gpointer data)
599 while (pollfds[i++].fd != -1)
603 watch_ids = g_new0(guint, nfds + 1);
606 for (i = 0; i < nfds; i++) {
609 gioc = g_io_channel_unix_new(pollfds[i].fd);
610 watch_ids[i] = g_io_add_watch(gioc, G_IO_IN | G_IO_ERR, func, data);
611 g_io_channel_unref(gioc);
625 for (i = 0; watch_ids[i] != 0; i++) {
626 g_source_remove(watch_ids[i]);
672 snd_mixer_handle_events(card->
mixer);
677 if (condition == G_IO_ERR) {
692 stat = g_io_channel_read_chars(source, sbuf, 256, &sread, NULL);
695 case G_IO_STATUS_AGAIN:
699 case G_IO_STATUS_NORMAL:
701 ERROR(
"Alsa failed to clear the channel");
706 case G_IO_STATUS_ERROR:
707 case G_IO_STATUS_EOF:
708 ERROR(
"GIO error has occurred");
714 WARN(
"Unknown status from g_io_channel_read_chars()");
807 gboolean gotten = FALSE;
830 gboolean
set = FALSE;
832 volume = value / 100.0;
891 alsa_card_new(
const char *card_name,
const char *channel, gboolean normalize)
904 card->
name = g_strdup(card_name);
909 if (!g_strcmp0(iter->
name, card_name)) {
916 if (card->
hctl == NULL)
921 if (card->
mixer == NULL)
935 struct pollfd *pollfds;
945 DEBUG(
"'%s': Card '%s' with channel '%s' initialized !",
983 list = g_slist_append(list, g_strdup(iter->
name));
1006 snd_mixer_t *mixer = NULL;
1007 GSList *list = NULL;
1012 if (!g_strcmp0(iter->
name, card_name)) {
1013 hctl = g_strdup(iter->
hctl);
static GSList * mixer_list_playable(G_GNUC_UNUSED const char *hctl, snd_mixer_t *mixer)
#define ALSA_ERR(err,...)
#define ALSA_CARD_WARN(card,...)
static guint * watch_poll_descriptors(const char *hctl, struct pollfd *pollfds, GIOFunc func, gpointer data)
static struct pollfd * mixer_get_poll_descriptors(const char *hctl, snd_mixer_t *mixer)
static const char * elem_get_name(snd_mixer_elem_t *elem)
static void alsa_card_iter_free(AlsaCardIter *iter)
static snd_mixer_elem_t * mixer_get_first_playable_elem(const char *hctl, snd_mixer_t *mixer)
gboolean alsa_card_has_mute(AlsaCard *card)
static gboolean elem_get_volume_normalized(const char *hctl, snd_mixer_elem_t *elem, double *volume)
snd_mixer_elem_t * mixer_elem
static snd_mixer_t * mixer_open(const char *hctl)
const char * alsa_card_get_channel(AlsaCard *card)
static gboolean elem_get_mute(const char *hctl, snd_mixer_elem_t *elem, gboolean *muted)
static gboolean alsa_card_iter_loop(AlsaCardIter *iter)
static void alsa_log_msg(enum log_level level, const char *card, const char *strerr, const char *format,...)
gdouble alsa_card_get_volume(AlsaCard *card)
GSList * alsa_list_cards(void)
static gboolean elem_set_mute(const char *hctl, snd_mixer_elem_t *elem, gboolean mute)
#define MAX_LINEAR_DB_SCALE
void alsa_card_toggle_mute(AlsaCard *card)
#define ALSA_DEFAULT_HCTL
static void unwatch_poll_descriptors(guint *watch_ids)
#define ALSA_DEFAULT_CARD
void alsa_card_free(AlsaCard *card)
gboolean alsa_card_is_muted(AlsaCard *card)
static gboolean elem_set_volume(const char *hctl, snd_mixer_elem_t *elem, double volume, int dir)
#define ALSA_CARD_DEBUG(card,...)
static gboolean use_linear_dB_scale(long db_min, long db_max)
GSList * alsa_list_channels(const char *card_name)
#define ALSA_CARD_ERR(card, err,...)
static gboolean elem_set_volume_normalized(const char *hctl, snd_mixer_elem_t *elem, double volume, int dir)
static AlsaCardIter * alsa_card_iter_new(void)
AlsaCard * alsa_card_new(const char *card_name, const char *channel, gboolean normalize)
void alsa_card_set_volume(AlsaCard *card, gdouble value, int dir)
void log_msg_v(enum log_level level, const char *file, const char *format, va_list args)
void alsa_card_install_callback(AlsaCard *card, AlsaCb callback, gpointer user_data)
static gboolean elem_has_mute(G_GNUC_UNUSED const char *hctl, snd_mixer_elem_t *elem)
static double elem_get_volume(const char *hctl, snd_mixer_elem_t *elem, double *volume)
void(* AlsaCb)(enum alsa_event event, gpointer data)
static gboolean poll_watch_cb(GIOChannel *source, GIOCondition condition, AlsaCard *card)
static long lrint_dir(double x, int dir)
static void mixer_close(const char *hctl, snd_mixer_t *mixer)
static gboolean mixer_is_playable(const char *hctl, snd_mixer_t *mixer)
const char * alsa_card_get_name(AlsaCard *card)
static snd_mixer_elem_t * mixer_get_playable_elem(const char *hctl, snd_mixer_t *mixer, const char *channel)