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 <functional>
#include <string>
#include <cmath>
#include <alsa/asoundlib.h>
@ -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();

View File

@ -57,6 +57,8 @@ namespace modules {
int m_headphoneid = 0;
bool m_mapped;
stateflag m_muted{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;
}
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) {
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<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) {
std::lock_guard<threading_util::spin_lock> guard(m_lock);
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(), speaker_mixer_name, "speaker-mixer");
GET_CONFIG_VALUE(name(), headphone_mixer_name, "headphone-mixer");
m_mapped = m_conf.get<bool>(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<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) {
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 {
return false;