forked from external/yambar
module/alsa: track all channels
Warn if volume and/or muted state is inconsistent (as we only expose a single volume/muted state). Also, don't query for current volume if max == 0 (i.e. typically a digital output). Finally, make absolutely sure that volume min is really less (or equal) to volume max, and that the current volume level is between min and max.
This commit is contained in:
parent
558f75a54b
commit
54798567c0
1 changed files with 122 additions and 14 deletions
136
modules/alsa.c
136
modules/alsa.c
|
@ -9,12 +9,15 @@
|
||||||
#define LOG_ENABLE_DBG 0
|
#define LOG_ENABLE_DBG 0
|
||||||
#include "../log.h"
|
#include "../log.h"
|
||||||
#include "../bar.h"
|
#include "../bar.h"
|
||||||
|
#include "../tllist.h"
|
||||||
|
|
||||||
struct private {
|
struct private {
|
||||||
char *card;
|
char *card;
|
||||||
char *mixer;
|
char *mixer;
|
||||||
struct particle *label;
|
struct particle *label;
|
||||||
|
|
||||||
|
tll(snd_mixer_selem_channel_id_t) channels;
|
||||||
|
|
||||||
long vol_min;
|
long vol_min;
|
||||||
long vol_max;
|
long vol_max;
|
||||||
long vol_cur;
|
long vol_cur;
|
||||||
|
@ -25,6 +28,7 @@ static void
|
||||||
destroy(struct module *mod)
|
destroy(struct module *mod)
|
||||||
{
|
{
|
||||||
struct private *m = mod->private;
|
struct private *m = mod->private;
|
||||||
|
tll_free(m->channels);
|
||||||
m->label->destroy(m->label);
|
m->label->destroy(m->label);
|
||||||
free(m->card);
|
free(m->card);
|
||||||
free(m->mixer);
|
free(m->mixer);
|
||||||
|
@ -58,29 +62,113 @@ update_state(struct module *mod, snd_mixer_elem_t *elem)
|
||||||
{
|
{
|
||||||
struct private *m = mod->private;
|
struct private *m = mod->private;
|
||||||
|
|
||||||
long cur = 0;
|
int idx = 0;
|
||||||
int r = snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_MONO, &cur);
|
|
||||||
if (r < 0)
|
|
||||||
LOG_WARN("%s,%s: failed to get current volume", m->card, m->mixer);
|
|
||||||
|
|
||||||
|
/* Get min/max volume levels */
|
||||||
long min = 0, max = 0;
|
long min = 0, max = 0;
|
||||||
r = snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
|
int r = snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
|
||||||
if (r < 0)
|
|
||||||
LOG_WARN("%s,%s: failed to get volume min/max", m->card, m->mixer);
|
|
||||||
|
|
||||||
|
if (r < 0) {
|
||||||
|
LOG_DBG("%s,%s: failed to get volume min/max (mixer is digital?)",
|
||||||
|
m->card, m->mixer);
|
||||||
|
}
|
||||||
|
|
||||||
int unmuted = false;
|
/* Make sure min <= max */
|
||||||
r = snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_MONO, &unmuted);
|
if (min > max) {
|
||||||
if (r < 0)
|
LOG_WARN(
|
||||||
LOG_WARN("%s,%s: failed to get muted state", m->card, m->mixer);
|
"%s,%s: indicated minimum volume is greater than the maximum: "
|
||||||
|
"%ld > %ld", m->card, m->mixer, min, max);
|
||||||
|
min = max;
|
||||||
|
}
|
||||||
|
|
||||||
LOG_DBG("muted=%d, cur=%ld, min=%ld, max=%ld", !unmuted, cur, min, max);
|
long cur[tll_length(m->channels)];
|
||||||
|
memset(cur, 0, sizeof(cur));
|
||||||
|
|
||||||
|
/* If volume level can be changed (i.e. this isn't just a switch;
|
||||||
|
* e.g. a digital channel), get current level */
|
||||||
|
if (max > 0) {
|
||||||
|
tll_foreach(m->channels, it) {
|
||||||
|
int r = snd_mixer_selem_get_playback_volume(
|
||||||
|
elem, it->item, &cur[idx]);
|
||||||
|
|
||||||
|
if (r < 0) {
|
||||||
|
LOG_WARN("%s,%s: %s: failed to get current volume",
|
||||||
|
m->card, m->mixer,
|
||||||
|
snd_mixer_selem_channel_name(it->item));
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("%s,%s: %s: volume: %ld", m->card, m->mixer,
|
||||||
|
snd_mixer_selem_channel_name(it->item), cur[idx]);
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int unmuted[tll_length(m->channels)];
|
||||||
|
memset(unmuted, 0, sizeof(unmuted));
|
||||||
|
|
||||||
|
/* Get muted state */
|
||||||
|
idx = 0;
|
||||||
|
tll_foreach(m->channels, it) {
|
||||||
|
int r = snd_mixer_selem_get_playback_switch(
|
||||||
|
elem, it->item, &unmuted[idx]);
|
||||||
|
|
||||||
|
if (r < 0) {
|
||||||
|
LOG_WARN("%s,%s: %s: failed to get muted state",
|
||||||
|
m->card, m->mixer, snd_mixer_selem_channel_name(it->item));
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("%s,%s: %s: muted: %d", m->card, m->mixer,
|
||||||
|
snd_mixer_selem_channel_name(it->item), !unmuted[idx]);
|
||||||
|
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Warn if volume level is inconsistent across the channels */
|
||||||
|
for (size_t i = 1; i < tll_length(m->channels); i++) {
|
||||||
|
if (cur[i] != cur[i - 1]) {
|
||||||
|
LOG_WARN("%s,%s: channel volume mismatch, using value from %s",
|
||||||
|
m->card, m->mixer,
|
||||||
|
snd_mixer_selem_channel_name(tll_front(m->channels)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Warn if muted state is inconsistent across the channels */
|
||||||
|
for (size_t i = 1; i < tll_length(m->channels); i++) {
|
||||||
|
if (unmuted[i] != unmuted[i - 1]) {
|
||||||
|
LOG_WARN("%s,%s: channel muted mismatch, using value from %s",
|
||||||
|
m->card, m->mixer,
|
||||||
|
snd_mixer_selem_channel_name(tll_front(m->channels)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure min <= cur <= max */
|
||||||
|
if (cur[0] < min) {
|
||||||
|
LOG_WARN(
|
||||||
|
"%s,%s: current volume is less than the indicated minimum: "
|
||||||
|
"%ld < %ld", m->card, m->mixer, cur[0], min);
|
||||||
|
cur[0] = min;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cur[0] > max) {
|
||||||
|
LOG_WARN(
|
||||||
|
"%s,%s: current volume is greater than the indicated maximum: "
|
||||||
|
"%ld > %ld", m->card, m->mixer, cur[0], max);
|
||||||
|
cur[0] = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(cur[0] >= min);
|
||||||
|
assert(cur[0] <= max);
|
||||||
|
|
||||||
|
LOG_DBG(
|
||||||
|
"muted=%d, cur=%ld, min=%ld, max=%ld", !unmuted[0], cur[0], min, max);
|
||||||
|
|
||||||
mtx_lock(&mod->lock);
|
mtx_lock(&mod->lock);
|
||||||
m->vol_min = min;
|
m->vol_min = min;
|
||||||
m->vol_max = max;
|
m->vol_max = max;
|
||||||
m->vol_cur = cur;
|
m->vol_cur = cur[0];
|
||||||
m->muted = !unmuted;
|
m->muted = !unmuted[0];
|
||||||
mtx_unlock(&mod->lock);
|
mtx_unlock(&mod->lock);
|
||||||
|
|
||||||
mod->bar->refresh(mod->bar);
|
mod->bar->refresh(mod->bar);
|
||||||
|
@ -106,6 +194,25 @@ run(struct module_run_context *ctx)
|
||||||
snd_mixer_selem_id_set_name(sid, m->mixer);
|
snd_mixer_selem_id_set_name(sid, m->mixer);
|
||||||
snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);
|
snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);
|
||||||
|
|
||||||
|
/* Get available channels */
|
||||||
|
for (size_t i = 0; i < SND_MIXER_SCHN_LAST; i++) {
|
||||||
|
if (snd_mixer_selem_has_playback_channel(elem, i)) {
|
||||||
|
tll_push_back(m->channels, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char channels_str[1024];
|
||||||
|
int channels_idx = 0;
|
||||||
|
tll_foreach(m->channels, it) {
|
||||||
|
channels_idx += snprintf(
|
||||||
|
&channels_str[channels_idx], sizeof(channels_str) - channels_idx,
|
||||||
|
channels_idx == 0 ? "%s" : ", %s",
|
||||||
|
snd_mixer_selem_channel_name(it->item));
|
||||||
|
assert(channels_idx <= sizeof(channels_str));
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO("%s,%s: channels: %s", m->card, m->mixer, channels_str);
|
||||||
|
|
||||||
/* Initial state */
|
/* Initial state */
|
||||||
update_state(mod, elem);
|
update_state(mod, elem);
|
||||||
|
|
||||||
|
@ -149,6 +256,7 @@ module_alsa(const char *card, const char *mixer, struct particle *label)
|
||||||
priv->label = label;
|
priv->label = label;
|
||||||
priv->card = strdup(card);
|
priv->card = strdup(card);
|
||||||
priv->mixer = strdup(mixer);
|
priv->mixer = strdup(mixer);
|
||||||
|
memset(&priv->channels, 0, sizeof(priv->channels));
|
||||||
priv->vol_cur = priv->vol_min = priv->vol_max = 0;
|
priv->vol_cur = priv->vol_min = priv->vol_max = 0;
|
||||||
priv->muted = true;
|
priv->muted = true;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue