From 4eb513cbf45b46c83f4b3456d66277183615da8e Mon Sep 17 00:00:00 2001 From: Juho Hämäläinen Date: Mon, 20 Dec 2010 18:25:59 +0200 Subject: alsa-mixer: select nearest alsa volume step in sync-volume mode --- src/modules/alsa/alsa-mixer.c | 76 ++++++++++++++++++++++++++++++++++++++---- src/modules/alsa/alsa-mixer.h | 2 +- src/modules/alsa/alsa-sink.c | 6 ++-- src/modules/alsa/alsa-source.c | 6 ++-- 4 files changed, 76 insertions(+), 14 deletions(-) diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c index e3673f8c..dada1223 100644 --- a/src/modules/alsa/alsa-mixer.c +++ b/src/modules/alsa/alsa-mixer.c @@ -849,7 +849,59 @@ static long decibel_fix_get_step(pa_alsa_decibel_fix *db_fix, long *db_value, in return i + db_fix->min_step; } -static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, pa_bool_t write_to_hw) { +/* Alsa lib documentation says for snd_mixer_selem_set_playback_dB() direction argument, + * that "-1 = accurate or first below, 0 = accurate, 1 = accurate or first above". + * But even with accurate nearest dB volume step is not selected, so that is why we need + * this function. Returns 0 and nearest selectable volume in *value_dB on success or + * negative error code if fails. */ +static int element_get_nearest_alsa_dB(snd_mixer_elem_t *me, snd_mixer_selem_channel_id_t c, pa_alsa_direction_t d, long *value_dB) { + + long alsa_val; + long value_high; + long value_low; + int r = -1; + + pa_assert(me); + pa_assert(value_dB); + + if (d == PA_ALSA_DIRECTION_OUTPUT) { + if ((r = snd_mixer_selem_ask_playback_dB_vol(me, *value_dB, +1, &alsa_val)) >= 0) + r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value_high); + + if (r < 0) + return r; + + if (value_high == *value_dB) + return r; + + if ((r = snd_mixer_selem_ask_playback_dB_vol(me, *value_dB, -1, &alsa_val)) >= 0) + r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value_low); + } else { + if ((r = snd_mixer_selem_ask_capture_dB_vol(me, *value_dB, +1, &alsa_val)) >= 0) + r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value_high); + + if (r < 0) + return r; + + if (value_high == *value_dB) + return r; + + if ((r = snd_mixer_selem_ask_capture_dB_vol(me, *value_dB, -1, &alsa_val)) >= 0) + r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value_low); + } + + if (r < 0) + return r; + + if (labs(value_high - *value_dB) < labs(value_low - *value_dB)) + *value_dB = value_high; + else + *value_dB = value_low; + + return r; +} + +static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, pa_bool_t sync_volume, pa_bool_t write_to_hw) { snd_mixer_selem_id_t *sid; pa_cvolume rv; @@ -914,8 +966,13 @@ static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann } else { if (write_to_hw) { - if ((r = snd_mixer_selem_set_playback_dB(me, c, value, rounding)) >= 0) - r = snd_mixer_selem_get_playback_dB(me, c, &value); + if (sync_volume) { + if ((r = element_get_nearest_alsa_dB(me, c, PA_ALSA_DIRECTION_OUTPUT, &value)) >= 0) + r = snd_mixer_selem_set_playback_dB(me, c, value, 0); + } else { + if ((r = snd_mixer_selem_set_playback_dB(me, c, value, rounding)) >= 0) + r = snd_mixer_selem_get_playback_dB(me, c, &value); + } } else { long alsa_val; if ((r = snd_mixer_selem_ask_playback_dB_vol(me, value, rounding, &alsa_val)) >= 0) @@ -937,8 +994,13 @@ static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann } else { if (write_to_hw) { - if ((r = snd_mixer_selem_set_capture_dB(me, c, value, rounding)) >= 0) - r = snd_mixer_selem_get_capture_dB(me, c, &value); + if (sync_volume) { + if ((r = element_get_nearest_alsa_dB(me, c, PA_ALSA_DIRECTION_INPUT, &value)) >= 0) + r = snd_mixer_selem_set_capture_dB(me, c, value, 0); + } else { + if ((r = snd_mixer_selem_set_capture_dB(me, c, value, rounding)) >= 0) + r = snd_mixer_selem_get_capture_dB(me, c, &value); + } } else { long alsa_val; if ((r = snd_mixer_selem_ask_capture_dB_vol(me, value, rounding, &alsa_val)) >= 0) @@ -999,7 +1061,7 @@ static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann return 0; } -int pa_alsa_path_set_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, pa_bool_t write_to_hw) { +int pa_alsa_path_set_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, pa_bool_t sync_volume, pa_bool_t write_to_hw) { pa_alsa_element *e; pa_cvolume rv; @@ -1025,7 +1087,7 @@ int pa_alsa_path_set_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_ma pa_assert(!p->has_dB || e->has_dB); ev = rv; - if (element_set_volume(e, m, cm, &ev, write_to_hw) < 0) + if (element_set_volume(e, m, cm, &ev, sync_volume, write_to_hw) < 0) return -1; if (!p->has_dB) { diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h index a29aed1c..e1cf1f81 100644 --- a/src/modules/alsa/alsa-mixer.h +++ b/src/modules/alsa/alsa-mixer.h @@ -218,7 +218,7 @@ int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB); void pa_alsa_path_dump(pa_alsa_path *p); int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v); int pa_alsa_path_get_mute(pa_alsa_path *path, snd_mixer_t *m, pa_bool_t *muted); -int pa_alsa_path_set_volume(pa_alsa_path *path, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, pa_bool_t write_to_hw); +int pa_alsa_path_set_volume(pa_alsa_path *path, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, pa_bool_t sync_volume, pa_bool_t write_to_hw); int pa_alsa_path_set_mute(pa_alsa_path *path, snd_mixer_t *m, pa_bool_t muted); int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m); void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata); diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 96812c7f..a042c2d1 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -1253,7 +1253,7 @@ static void sink_set_volume_cb(pa_sink *s) { struct userdata *u = s->userdata; pa_cvolume r; char vol_str_pcnt[PA_CVOLUME_SNPRINT_MAX]; - pa_bool_t write_to_hw = (s->flags & PA_SINK_SYNC_VOLUME) ? FALSE : TRUE; + pa_bool_t sync_volume = !!(s->flags & PA_SINK_SYNC_VOLUME); pa_assert(u); pa_assert(u->mixer_path); @@ -1262,7 +1262,7 @@ static void sink_set_volume_cb(pa_sink *s) { /* Shift up by the base volume */ pa_sw_cvolume_divide_scalar(&r, &s->real_volume, s->base_volume); - if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r, write_to_hw) < 0) + if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r, sync_volume, !sync_volume) < 0) return; /* Shift down by the base volume, so that 0dB becomes maximum volume */ @@ -1319,7 +1319,7 @@ static void sink_write_volume_cb(pa_sink *s) { /* Shift up by the base volume */ pa_sw_cvolume_divide_scalar(&hw_vol, &hw_vol, s->base_volume); - if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &hw_vol, TRUE) < 0) + if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &hw_vol, TRUE, TRUE) < 0) pa_log_error("Writing HW volume failed"); else { pa_cvolume tmp_vol; diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index adec8f64..fb96ed01 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -1129,7 +1129,7 @@ static void source_set_volume_cb(pa_source *s) { struct userdata *u = s->userdata; pa_cvolume r; char vol_str_pcnt[PA_CVOLUME_SNPRINT_MAX]; - pa_bool_t write_to_hw = (s->flags & PA_SOURCE_SYNC_VOLUME) ? FALSE : TRUE; + pa_bool_t sync_volume = !!(s->flags & PA_SOURCE_SYNC_VOLUME); pa_assert(u); pa_assert(u->mixer_path); @@ -1138,7 +1138,7 @@ static void source_set_volume_cb(pa_source *s) { /* Shift up by the base volume */ pa_sw_cvolume_divide_scalar(&r, &s->real_volume, s->base_volume); - if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r, write_to_hw) < 0) + if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r, sync_volume, !sync_volume) < 0) return; /* Shift down by the base volume, so that 0dB becomes maximum volume */ @@ -1195,7 +1195,7 @@ static void source_write_volume_cb(pa_source *s) { /* Shift up by the base volume */ pa_sw_cvolume_divide_scalar(&hw_vol, &hw_vol, s->base_volume); - if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &hw_vol, TRUE) < 0) + if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &hw_vol, TRUE, TRUE) < 0) pa_log_error("Writing HW volume failed"); else { pa_cvolume tmp_vol; -- cgit