diff options
author | arcnmx <arcnmx@users.noreply.github.com> | 2018-02-21 13:25:08 -0500 |
---|---|---|
committer | Orestis <orestisf1993@gmail.com> | 2018-10-11 00:21:03 +0300 |
commit | ddadc8e4d728923cf5bc7c8a894b103cf061c47d (patch) | |
tree | f278c0a2e647b589d2f3892881ef1785b105705f /src/print_volume.c | |
parent | 494efd49a26ed96d7b0d3d4f69099ccd83e2ccba (diff) |
Provide a more natural volume percentage with ALSA.
The rationale of the code is explained in the header:
http://git.alsa-project.org/?p=alsa-utils.git;a=blob;f=alsamixer/volume_mapping.c;h=1c0d7c45e6686239464e1b0bbc8983ea57f3914f;hb=HEAD
> The mapping is designed so that the position in the interval is
> proportional to the volume as a human ear would perceive it (i.e., the
> position is the cubic root of the linear sample multiplication
> factor).
and the commit message:
http://git.alsa-project.org/?p=alsa-utils.git;a=commit;h=34bb514b5fd1d6f91ba9a7b3a70b0ea0c6014250
> use a mapping where the bar height is proportional to the audible
> volume, i.e., where the amplitude is the cube of the bar height.
and further explanation can be found in the pull request:
https://github.com/i3/i3status/pull/268#pullrequestreview-147429763
Diffstat (limited to 'src/print_volume.c')
-rw-r--r-- | src/print_volume.c | 35 |
1 files changed, 28 insertions, 7 deletions
diff --git a/src/print_volume.c b/src/print_volume.c index 4c0fbde..c3180fe 100644 --- a/src/print_volume.c +++ b/src/print_volume.c @@ -11,6 +11,7 @@ #ifdef LINUX #include <alsa/asoundlib.h> #include <alloca.h> +#include <math.h> #endif #if defined(__FreeBSD__) || defined(__DragonFly__) @@ -111,11 +112,13 @@ void print_volume(yajl_gen json_gen, char *buffer, const char *fmt, const char * #endif #ifdef LINUX + const long MAX_LINEAR_DB_SCALE = 24; int err; snd_mixer_t *m; snd_mixer_selem_id_t *sid; snd_mixer_elem_t *elem; long min, max, val; + bool force_linear = false; int avg; if ((err = snd_mixer_open(&m, 0)) < 0) { @@ -161,16 +164,34 @@ void print_volume(yajl_gen json_gen, char *buffer, const char *fmt, const char * } /* Get the volume range to convert the volume later */ - snd_mixer_selem_get_playback_volume_range(elem, &min, &max); - snd_mixer_handle_events(m); - snd_mixer_selem_get_playback_volume(elem, 0, &val); - if (max != 100) { - float avgf = ((float)val / max) * 100; + err = snd_mixer_selem_get_playback_dB_range(elem, &min, &max) || + snd_mixer_selem_get_playback_dB(elem, 0, &val); + if (err != 0 || min >= max) { + err = snd_mixer_selem_get_playback_volume_range(elem, &min, &max) || + snd_mixer_selem_get_playback_volume(elem, 0, &val); + force_linear = true; + } + + if (err != 0) { + fprintf(stderr, "i3status: ALSA: Cannot get playback volume.\n"); + goto out; + } + + /* Use linear mapping for raw register values or small ranges of 24 dB */ + if (force_linear || max - min <= MAX_LINEAR_DB_SCALE * 100) { + float avgf = ((float)(val - min) / (max - min)) * 100; avg = (int)avgf; avg = (avgf - avg < 0.5 ? avg : (avg + 1)); - } else - avg = (int)val; + } else { + /* mapped volume to be more natural for the human ear */ + double normalized = exp10((val - max) / 6000.0); + if (min != SND_CTL_TLV_DB_GAIN_MUTE) { + double min_norm = exp10((min - max) / 6000.0); + normalized = (normalized - min_norm) / (1 - min_norm); + } + avg = lround(normalized * 100); + } /* Check for mute */ if (snd_mixer_selem_has_playback_switch(elem)) { |