feat(volume): Added volume mapping

This commit is contained in:
NBonaparte 2016-11-05 14:27:07 -07:00
parent b470337e0a
commit dce81d4266
4 changed files with 74 additions and 5 deletions

View File

@ -3,6 +3,7 @@
#include <stdio.h> #include <stdio.h>
#include <functional> #include <functional>
#include <string> #include <string>
#include <cmath>
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
@ -10,6 +11,8 @@
#include "config.hpp" #include "config.hpp"
#include "utils/threading.hpp" #include "utils/threading.hpp"
#define MAX_LINEAR_DB_SCALE 24
LEMONBUDDY_NS LEMONBUDDY_NS
DEFINE_ERROR(alsa_exception); DEFINE_ERROR(alsa_exception);
@ -64,7 +67,9 @@ class alsa_mixer {
int process_events(); int process_events();
int get_volume(); int get_volume();
int get_normalized_volume();
void set_volume(float percentage); void set_volume(float percentage);
void set_normalized_volume(float percentage);
void set_mute(bool mode); void set_mute(bool mode);
void toggle_mute(); void toggle_mute();
bool is_muted(); bool is_muted();

View File

@ -57,6 +57,8 @@ namespace modules {
int m_headphoneid = 0; int m_headphoneid = 0;
bool m_mapped;
stateflag m_muted{false}; stateflag m_muted{false};
stateflag m_headphones{false}; stateflag m_headphones{false};

View File

@ -169,6 +169,35 @@ int alsa_mixer::get_volume() {
return 100.0f * (vol_total / chan_n) / vol_max + 0.5f; return 100.0f * (vol_total / chan_n) / vol_max + 0.5f;
} }
int alsa_mixer::get_normalized_volume() {
std::lock_guard<threading_util::spin_lock> 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<snd_mixer_selem_channel_id_t>(i))) {
snd_mixer_selem_get_playback_dB(
m_mixerelement, static_cast<snd_mixer_selem_channel_id_t>(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) { void alsa_mixer::set_volume(float percentage) {
if (is_muted()) if (is_muted())
return; 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); 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<threading_util::spin_lock> 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) { void alsa_mixer::set_mute(bool mode) {
std::lock_guard<threading_util::spin_lock> guard(m_lock); std::lock_guard<threading_util::spin_lock> guard(m_lock);
snd_mixer_selem_set_playback_switch_all(m_mixerelement, mode); snd_mixer_selem_set_playback_switch_all(m_mixerelement, mode);

View File

@ -14,6 +14,7 @@ namespace modules {
GET_CONFIG_VALUE(name(), master_mixer_name, "master-mixer"); GET_CONFIG_VALUE(name(), master_mixer_name, "master-mixer");
GET_CONFIG_VALUE(name(), speaker_mixer_name, "speaker-mixer"); GET_CONFIG_VALUE(name(), speaker_mixer_name, "speaker-mixer");
GET_CONFIG_VALUE(name(), headphone_mixer_name, "headphone-mixer"); GET_CONFIG_VALUE(name(), headphone_mixer_name, "headphone-mixer");
m_mapped = m_conf.get<bool>(name(), "mapped", false);
if (!headphone_mixer_name.empty()) if (!headphone_mixer_name.empty())
REQ_CONFIG_VALUE(name(), m_headphoneid, "headphone-id"); REQ_CONFIG_VALUE(name(), m_headphoneid, "headphone-id");
@ -110,16 +111,19 @@ namespace modules {
m_headphones = false; m_headphones = false;
if (m_mixers[mixer::MASTER]) { 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(); m_muted = m_muted || m_mixers[mixer::MASTER]->is_muted();
} }
if (m_controls[control::HEADPHONE] && m_controls[control::HEADPHONE]->test_device_plugged()) { if (m_controls[control::HEADPHONE] && m_controls[control::HEADPHONE]->test_device_plugged()) {
m_headphones = true; 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(); m_muted = m_muted || m_mixers[mixer::HEADPHONE]->is_muted();
} else if (m_mixers[mixer::SPEAKER]) { } 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(); 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) { } else if (cmd.compare(0, strlen(EVENT_VOLUME_UP), EVENT_VOLUME_UP) == 0) {
for (auto&& mixer : mixers) { for (auto&& mixer : mixers) {
mixer->set_volume(math_util::cap<float>(mixer->get_volume() + 5, 0, 100)); m_mapped ?
mixer->set_normalized_volume(math_util::cap<float>(mixer->get_normalized_volume() + 5, 0, 100)) :
mixer->set_volume(math_util::cap<float>(mixer->get_volume() + 5, 0, 100));
} }
} else if (cmd.compare(0, strlen(EVENT_VOLUME_DOWN), EVENT_VOLUME_DOWN) == 0) { } else if (cmd.compare(0, strlen(EVENT_VOLUME_DOWN), EVENT_VOLUME_DOWN) == 0) {
for (auto&& mixer : mixers) { for (auto&& mixer : mixers) {
mixer->set_volume(math_util::cap<float>(mixer->get_volume() - 5, 0, 100)); m_mapped ?
mixer->set_normalized_volume(math_util::cap<float>(mixer->get_normalized_volume() - 5, 0, 100)) :
mixer->set_volume(math_util::cap<float>(mixer->get_volume() - 5, 0, 100));
} }
} else { } else {
return false; return false;