summaryrefslogtreecommitdiffstats
path: root/src/modules/alsa
diff options
context:
space:
mode:
authorTanu Kaskinen <tanu.kaskinen@digia.com>2011-03-11 13:40:51 +0200
committerTanu Kaskinen <tanu.kaskinen@digia.com>2011-03-11 13:40:51 +0200
commit064780d9dea6a79c4c3adb07f530e2e6d5c68c0f (patch)
tree02ef3202831e50c1222627dbb7a20155109d21a0 /src/modules/alsa
parent68e6340394d66d9d41bf094440d950dd62adba62 (diff)
alsa-mixer: Implement support for setting element specific upper limits for volume.
This feature is mainly useful in embedded systems that have built-in speakers. In such situations the full audio path is known beforehand, so it's possible to know what is the maximum sensible volume, and any higher volume can be disabled. The volume limit is set in path configuration files in the [Element] section, using option "volume-limit". The value is the desired maximum volume step of the volume element.
Diffstat (limited to 'src/modules/alsa')
-rw-r--r--src/modules/alsa/alsa-mixer.c68
-rw-r--r--src/modules/alsa/alsa-mixer.h1
-rw-r--r--src/modules/alsa/mixer/paths/analog-output.conf.common1
3 files changed, 69 insertions, 1 deletions
diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index 9a7e045a..41997a79 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -895,6 +895,9 @@ static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
long value = to_alsa_dB(f);
int rounding = value > 0 ? -1 : +1;
+ if (e->volume_limit >= 0 && value > (e->max_dB * 100))
+ value = e->max_dB * 100;
+
if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
/* If we call set_playback_volume() without checking first
* if the channel is available, ALSA behaves very
@@ -1284,6 +1287,7 @@ static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
pa_assert(m);
pa_assert(e);
+ pa_assert(e->path);
SELEM_INIT(sid, e->alsa_name);
@@ -1407,6 +1411,36 @@ static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
}
}
+ if (e->volume_limit >= 0) {
+ if (e->volume_limit <= e->min_volume || e->volume_limit > e->max_volume)
+ pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
+ "%li-%li. The volume limit is ignored.",
+ e->alsa_name, e->path->name, e->volume_limit, e->min_volume + 1, e->max_volume);
+
+ else {
+ e->max_volume = e->volume_limit;
+
+ if (e->has_dB) {
+ if (e->db_fix) {
+ e->db_fix->max_step = e->max_volume;
+ e->max_dB = ((double) e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step]) / 100.0;
+
+ } else {
+ if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
+ r = snd_mixer_selem_ask_playback_vol_dB(me, e->max_volume, &max_dB);
+ else
+ r = snd_mixer_selem_ask_capture_vol_dB(me, e->max_volume, &max_dB);
+
+ if (r < 0) {
+ pa_log_warn("Failed to get dB value of %s: %s", e->alsa_name, pa_alsa_strerror(r));
+ e->has_dB = FALSE;
+ } else
+ e->max_dB = ((double) max_dB) / 100.0;
+ }
+ }
+ }
+ }
+
if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
is_mono = snd_mixer_selem_is_playback_mono(me) > 0;
else
@@ -1530,6 +1564,7 @@ static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, pa_boo
e->path = p;
e->alsa_name = pa_xstrdup(section);
e->direction = p->direction;
+ e->volume_limit = -1;
PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
@@ -1864,6 +1899,33 @@ static int element_parse_direction_try_other(
return 0;
}
+static int element_parse_volume_limit(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ pa_alsa_path *p = userdata;
+ pa_alsa_element *e;
+ uint32_t volume_limit;
+
+ if (!(e = element_get(p, section, TRUE))) {
+ pa_log("[%s:%u] volume-limit makes no sense in '%s'", filename, line, section);
+ return -1;
+ }
+
+ if (pa_atou(rvalue, &volume_limit) < 0 || volume_limit > LONG_MAX) {
+ pa_log("[%s:%u] Invalid value for volume-limit", filename, line);
+ return -1;
+ }
+
+ e->volume_limit = volume_limit;
+ return 0;
+}
+
static pa_channel_position_mask_t parse_mask(const char *m) {
pa_channel_position_mask_t v;
@@ -2141,6 +2203,7 @@ pa_alsa_path* pa_alsa_path_new(const char *fname, pa_alsa_direction_t direction)
{ "required-absent", element_parse_required, NULL, NULL },
{ "direction", element_parse_direction, NULL, NULL },
{ "direction-try-other", element_parse_direction_try_other, NULL, NULL },
+ { "volume-limit", element_parse_volume_limit, NULL, NULL },
{ NULL, NULL, NULL, NULL }
};
@@ -2191,6 +2254,7 @@ pa_alsa_path* pa_alsa_path_synthesize(const char*element, pa_alsa_direction_t di
e->path = p;
e->alsa_name = pa_xstrdup(element);
e->direction = direction;
+ e->volume_limit = -1;
e->switch_use = PA_ALSA_SWITCH_MUTE;
e->volume_use = PA_ALSA_VOLUME_MERGE;
@@ -2454,11 +2518,12 @@ void pa_alsa_element_dump(pa_alsa_element *e) {
pa_alsa_option *o;
pa_assert(e);
- pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, enumeration=%i, required=%i, required_any=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s",
+ pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, volume_limit=%li, enumeration=%i, required=%i, required_any=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s",
e->alsa_name,
e->direction,
e->switch_use,
e->volume_use,
+ e->volume_limit,
e->enumeration_use,
e->required,
e->required_any,
@@ -2619,6 +2684,7 @@ pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t d
e->alsa_name = pa_xstrdup(*je);
e->direction = direction;
e->required_absent = PA_ALSA_REQUIRED_ANY;
+ e->volume_limit = -1;
PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e);
p->last_element = e;
diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h
index 8ec5dca1..c24a8965 100644
--- a/src/modules/alsa/alsa-mixer.h
+++ b/src/modules/alsa/alsa-mixer.h
@@ -142,6 +142,7 @@ struct pa_alsa_element {
pa_bool_t has_dB:1;
long min_volume, max_volume;
+ long volume_limit; /* -1 for no configured limit */
double min_dB, max_dB;
pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST][2];
diff --git a/src/modules/alsa/mixer/paths/analog-output.conf.common b/src/modules/alsa/mixer/paths/analog-output.conf.common
index ffd1b413..c7c44350 100644
--- a/src/modules/alsa/mixer/paths/analog-output.conf.common
+++ b/src/modules/alsa/mixer/paths/analog-output.conf.common
@@ -82,6 +82,7 @@
; volume = ignore | merge | off | zero # What to do with this volume: ignore it, merge it into the device
; # volume slider, always set it to the lowest value possible, or always
; # set it to 0 dB (for whatever that means)
+; volume-limit = <volume step> # Limit the maximum volume by disabling the volume steps above <volume step>.
; enumeration = ignore | select # What to do with this enumeration, ignore it or make it selectable
; # via device ports. If set to 'select' you need to define an Option section
; # for each of the items you want to expose