From dce81d4266d970e36d0b1c382bcbf28b3e1c865a Mon Sep 17 00:00:00 2001 From: NBonaparte <98007b33@opayq.com> Date: Sat, 5 Nov 2016 14:27:07 -0700 Subject: [PATCH] feat(volume): Added volume mapping --- include/adapters/alsa.hpp | 5 ++++ include/modules/volume.hpp | 2 ++ src/adapters/alsa.cpp | 54 ++++++++++++++++++++++++++++++++++++++ src/modules/volume.cpp | 18 +++++++++---- 4 files changed, 74 insertions(+), 5 deletions(-) diff --git a/include/adapters/alsa.hpp b/include/adapters/alsa.hpp index 7feb2709..be4ad8ce 100644 --- a/include/adapters/alsa.hpp +++ b/include/adapters/alsa.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -10,6 +11,8 @@ #include "config.hpp" #include "utils/threading.hpp" +#define MAX_LINEAR_DB_SCALE 24 + LEMONBUDDY_NS DEFINE_ERROR(alsa_exception); @@ -64,7 +67,9 @@ class alsa_mixer { int process_events(); int get_volume(); + int get_normalized_volume(); void set_volume(float percentage); + void set_normalized_volume(float percentage); void set_mute(bool mode); void toggle_mute(); bool is_muted(); diff --git a/include/modules/volume.hpp b/include/modules/volume.hpp index 66077e7c..311003ad 100644 --- a/include/modules/volume.hpp +++ b/include/modules/volume.hpp @@ -57,6 +57,8 @@ namespace modules { int m_headphoneid = 0; + bool m_mapped; + stateflag m_muted{false}; stateflag m_headphones{false}; diff --git a/src/adapters/alsa.cpp b/src/adapters/alsa.cpp index 0231d830..396010fb 100644 --- a/src/adapters/alsa.cpp +++ b/src/adapters/alsa.cpp @@ -169,6 +169,35 @@ int alsa_mixer::get_volume() { return 100.0f * (vol_total / chan_n) / vol_max + 0.5f; } +int alsa_mixer::get_normalized_volume() { + std::lock_guard guard(m_lock); + long chan_n = 0, vol_total = 0, vol, vol_min, vol_max; + double normalized, min_norm; + + snd_mixer_selem_get_playback_dB_range(m_mixerelement, &vol_min, &vol_max); + + for (int i = 0; i <= SND_MIXER_SCHN_LAST; i++) { + if (snd_mixer_selem_has_playback_channel( + m_mixerelement, static_cast(i))) { + snd_mixer_selem_get_playback_dB( + m_mixerelement, static_cast(i), &vol); + vol_total += vol; + chan_n++; + } + } + + if (vol_max - vol_min <= MAX_LINEAR_DB_SCALE * 100) + return 100.0f * (vol_total / chan_n - vol_min) / (vol_max - vol_min) + 0.5f; + + normalized = pow10((vol_total / chan_n - vol_max) / 6000.0); + if (vol_min != SND_CTL_TLV_DB_GAIN_MUTE) { + min_norm = pow10((vol_min - vol_max) / 6000.0); + normalized = (normalized - min_norm) / (1 - min_norm); + } + + return 100.0f * normalized + 0.5f; +} + void alsa_mixer::set_volume(float percentage) { if (is_muted()) return; @@ -181,6 +210,31 @@ void alsa_mixer::set_volume(float percentage) { snd_mixer_selem_set_playback_volume_all(m_mixerelement, vol_max * percentage / 100); } +void alsa_mixer::set_normalized_volume(float percentage) { + if (is_muted()) + return; + + std::lock_guard guard(m_lock); + + long vol_min, vol_max; + double min_norm; + percentage = percentage / 100.0f; + + snd_mixer_selem_get_playback_dB_range(m_mixerelement, &vol_min, &vol_max); + + if (vol_max - vol_min <= MAX_LINEAR_DB_SCALE * 100) { + snd_mixer_selem_set_playback_dB_all(m_mixerelement, lrint(percentage * (vol_max - vol_min)) + vol_min, 0); + return; + } + + if (vol_min != SND_CTL_TLV_DB_GAIN_MUTE) { + min_norm = pow10((vol_min - vol_max) / 6000.0); + percentage = percentage * (1 - min_norm) + min_norm; + } + + snd_mixer_selem_set_playback_dB_all(m_mixerelement, lrint(6000.0 * log10(percentage)) + vol_max, 0); +} + void alsa_mixer::set_mute(bool mode) { std::lock_guard guard(m_lock); snd_mixer_selem_set_playback_switch_all(m_mixerelement, mode); diff --git a/src/modules/volume.cpp b/src/modules/volume.cpp index 75978a0d..598f59ae 100644 --- a/src/modules/volume.cpp +++ b/src/modules/volume.cpp @@ -14,6 +14,7 @@ namespace modules { GET_CONFIG_VALUE(name(), master_mixer_name, "master-mixer"); GET_CONFIG_VALUE(name(), speaker_mixer_name, "speaker-mixer"); GET_CONFIG_VALUE(name(), headphone_mixer_name, "headphone-mixer"); + m_mapped = m_conf.get(name(), "mapped", false); if (!headphone_mixer_name.empty()) REQ_CONFIG_VALUE(name(), m_headphoneid, "headphone-id"); @@ -110,16 +111,19 @@ namespace modules { m_headphones = false; if (m_mixers[mixer::MASTER]) { - m_volume = m_volume * m_mixers[mixer::MASTER]->get_volume() / 100.0f; + m_volume = m_volume * (m_mapped ? m_mixers[mixer::MASTER]->get_normalized_volume() / 100.0f : + m_mixers[mixer::MASTER]->get_volume() / 100.0f); m_muted = m_muted || m_mixers[mixer::MASTER]->is_muted(); } if (m_controls[control::HEADPHONE] && m_controls[control::HEADPHONE]->test_device_plugged()) { m_headphones = true; - m_volume = m_volume * m_mixers[mixer::HEADPHONE]->get_volume() / 100.0f; + m_volume = m_volume * (m_mapped ? m_mixers[mixer::HEADPHONE]->get_normalized_volume() / 100.0f : + m_mixers[mixer::HEADPHONE]->get_volume() / 100.0f); m_muted = m_muted || m_mixers[mixer::HEADPHONE]->is_muted(); } else if (m_mixers[mixer::SPEAKER]) { - m_volume = m_volume * m_mixers[mixer::SPEAKER]->get_volume() / 100.0f; + m_volume = m_volume * (m_mapped ? m_mixers[mixer::SPEAKER]->get_normalized_volume() / 100.0f : + m_mixers[mixer::SPEAKER]->get_volume() / 100.0f); m_muted = m_muted || m_mixers[mixer::SPEAKER]->is_muted(); } @@ -196,11 +200,15 @@ namespace modules { } } else if (cmd.compare(0, strlen(EVENT_VOLUME_UP), EVENT_VOLUME_UP) == 0) { for (auto&& mixer : mixers) { - mixer->set_volume(math_util::cap(mixer->get_volume() + 5, 0, 100)); + m_mapped ? + mixer->set_normalized_volume(math_util::cap(mixer->get_normalized_volume() + 5, 0, 100)) : + mixer->set_volume(math_util::cap(mixer->get_volume() + 5, 0, 100)); } } else if (cmd.compare(0, strlen(EVENT_VOLUME_DOWN), EVENT_VOLUME_DOWN) == 0) { for (auto&& mixer : mixers) { - mixer->set_volume(math_util::cap(mixer->get_volume() - 5, 0, 100)); + m_mapped ? + mixer->set_normalized_volume(math_util::cap(mixer->get_normalized_volume() - 5, 0, 100)) : + mixer->set_volume(math_util::cap(mixer->get_volume() - 5, 0, 100)); } } else { return false;