mirror of
https://github.com/polybar/polybar.git
synced 2024-11-18 13:55:11 -05:00
refactor(alsa): Forward alsa structs and split classes
This commit is contained in:
parent
a33c15b3ad
commit
5e2a0bd298
10 changed files with 608 additions and 505 deletions
|
@ -1,121 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <endian.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <poll.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#ifndef __GNUC__
|
|
||||||
#define __inline__ inline
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <alsa/asoundef.h>
|
|
||||||
#include <alsa/version.h>
|
|
||||||
#include <alsa/global.h>
|
|
||||||
#include <alsa/input.h>
|
|
||||||
#include <alsa/output.h>
|
|
||||||
#include <alsa/error.h>
|
|
||||||
#include <alsa/conf.h>
|
|
||||||
#include <alsa/pcm.h>
|
|
||||||
#include <alsa/rawmidi.h>
|
|
||||||
#include <alsa/timer.h>
|
|
||||||
#include <alsa/hwdep.h>
|
|
||||||
#include <alsa/control.h>
|
|
||||||
#include <alsa/mixer.h>
|
|
||||||
#include <alsa/seq_event.h>
|
|
||||||
#include <alsa/seq.h>
|
|
||||||
#include <alsa/seqmid.h>
|
|
||||||
#include <alsa/seq_midi_event.h>
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <functional>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "common.hpp"
|
|
||||||
#include "config.hpp"
|
|
||||||
#include "errors.hpp"
|
|
||||||
#include "utils/concurrency.hpp"
|
|
||||||
|
|
||||||
#define MAX_LINEAR_DB_SCALE 24
|
|
||||||
|
|
||||||
POLYBAR_NS
|
|
||||||
|
|
||||||
DEFINE_ERROR(alsa_exception);
|
|
||||||
DEFINE_CHILD_ERROR(alsa_ctl_interface_error, alsa_exception);
|
|
||||||
DEFINE_CHILD_ERROR(alsa_mixer_error, alsa_exception);
|
|
||||||
|
|
||||||
// class definition : alsa_ctl_interface {{{
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void throw_exception(string&& message, int error_code) {
|
|
||||||
const char* snd_error = snd_strerror(error_code);
|
|
||||||
if (snd_error != nullptr)
|
|
||||||
message += ": " + string{snd_error};
|
|
||||||
throw T(message.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
class alsa_ctl_interface {
|
|
||||||
public:
|
|
||||||
explicit alsa_ctl_interface(int numid);
|
|
||||||
~alsa_ctl_interface();
|
|
||||||
|
|
||||||
int get_numid();
|
|
||||||
bool wait(int timeout = -1);
|
|
||||||
bool test_device_plugged();
|
|
||||||
void process_events();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::mutex m_lock;
|
|
||||||
|
|
||||||
int m_numid{0};
|
|
||||||
|
|
||||||
snd_hctl_t* m_hctl{nullptr};
|
|
||||||
snd_hctl_elem_t* m_elem{nullptr};
|
|
||||||
|
|
||||||
snd_ctl_t* m_ctl{nullptr};
|
|
||||||
snd_ctl_elem_info_t* m_info{nullptr};
|
|
||||||
snd_ctl_elem_value_t* m_value{nullptr};
|
|
||||||
snd_ctl_elem_id_t* m_id{nullptr};
|
|
||||||
};
|
|
||||||
|
|
||||||
// }}}
|
|
||||||
// class definition : alsa_mixer {{{
|
|
||||||
|
|
||||||
class alsa_mixer {
|
|
||||||
public:
|
|
||||||
explicit alsa_mixer(string mixer_control_name);
|
|
||||||
~alsa_mixer();
|
|
||||||
|
|
||||||
string get_name();
|
|
||||||
|
|
||||||
bool wait(int timeout = -1);
|
|
||||||
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();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::mutex m_lock;
|
|
||||||
|
|
||||||
string m_name;
|
|
||||||
|
|
||||||
snd_mixer_selem_id_t* m_mixerid{nullptr};
|
|
||||||
snd_mixer_t* m_hardwaremixer{nullptr};
|
|
||||||
snd_mixer_elem_t* m_mixerelement{nullptr};
|
|
||||||
};
|
|
||||||
|
|
||||||
// }}}
|
|
||||||
|
|
||||||
POLYBAR_NS_END
|
|
49
include/adapters/alsa/control.hpp
Normal file
49
include/adapters/alsa/control.hpp
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
|
||||||
|
// fwd
|
||||||
|
struct _snd_ctl_elem_id;
|
||||||
|
struct _snd_ctl_elem_info;
|
||||||
|
struct _snd_ctl_elem_value;
|
||||||
|
struct _snd_ctl;
|
||||||
|
struct _snd_hctl_elem;
|
||||||
|
struct _snd_hctl;
|
||||||
|
typedef struct _snd_ctl_elem_id snd_ctl_elem_id_t;
|
||||||
|
typedef struct _snd_ctl_elem_info snd_ctl_elem_info_t;
|
||||||
|
typedef struct _snd_ctl_elem_value snd_ctl_elem_value_t;
|
||||||
|
typedef struct _snd_ctl snd_ctl_t;
|
||||||
|
typedef struct _snd_hctl_elem snd_hctl_elem_t;
|
||||||
|
typedef struct _snd_hctl snd_hctl_t;
|
||||||
|
|
||||||
|
POLYBAR_NS
|
||||||
|
|
||||||
|
namespace alsa {
|
||||||
|
class control {
|
||||||
|
public:
|
||||||
|
explicit control(int numid);
|
||||||
|
~control();
|
||||||
|
|
||||||
|
int get_numid();
|
||||||
|
bool wait(int timeout = -1);
|
||||||
|
bool test_device_plugged();
|
||||||
|
void process_events();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex m_lock;
|
||||||
|
|
||||||
|
int m_numid{0};
|
||||||
|
|
||||||
|
snd_hctl_t* m_hctl{nullptr};
|
||||||
|
snd_hctl_elem_t* m_elem{nullptr};
|
||||||
|
|
||||||
|
snd_ctl_t* m_ctl{nullptr};
|
||||||
|
snd_ctl_elem_info_t* m_info{nullptr};
|
||||||
|
snd_ctl_elem_value_t* m_value{nullptr};
|
||||||
|
snd_ctl_elem_id_t* m_id{nullptr};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
POLYBAR_NS_END
|
60
include/adapters/alsa/generic.hpp
Normal file
60
include/adapters/alsa/generic.hpp
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef USE_ALSALIB_H
|
||||||
|
#include <alsa/asoundlib.h>
|
||||||
|
#else
|
||||||
|
#include <assert.h>
|
||||||
|
#include <endian.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifndef __GNUC__
|
||||||
|
#define __inline__ inline
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <alsa/asoundef.h>
|
||||||
|
#include <alsa/version.h>
|
||||||
|
#include <alsa/global.h>
|
||||||
|
#include <alsa/input.h>
|
||||||
|
#include <alsa/output.h>
|
||||||
|
#include <alsa/error.h>
|
||||||
|
#include <alsa/conf.h>
|
||||||
|
#include <alsa/pcm.h>
|
||||||
|
#include <alsa/rawmidi.h>
|
||||||
|
#include <alsa/timer.h>
|
||||||
|
#include <alsa/hwdep.h>
|
||||||
|
#include <alsa/control.h>
|
||||||
|
#include <alsa/mixer.h>
|
||||||
|
#include <alsa/seq_event.h>
|
||||||
|
#include <alsa/seq.h>
|
||||||
|
#include <alsa/seqmid.h>
|
||||||
|
#include <alsa/seq_midi_event.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "errors.hpp"
|
||||||
|
|
||||||
|
POLYBAR_NS
|
||||||
|
|
||||||
|
namespace alsa {
|
||||||
|
DEFINE_ERROR(alsa_exception);
|
||||||
|
DEFINE_CHILD_ERROR(mixer_error, alsa_exception);
|
||||||
|
DEFINE_CHILD_ERROR(control_error, alsa_exception);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void throw_exception(string&& message, int error_code) {
|
||||||
|
const char* snd_error = snd_strerror(error_code);
|
||||||
|
if (snd_error != nullptr)
|
||||||
|
message += ": " + string{snd_error};
|
||||||
|
throw T(message.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
POLYBAR_NS_END
|
47
include/adapters/alsa/mixer.hpp
Normal file
47
include/adapters/alsa/mixer.hpp
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
|
||||||
|
// fwd
|
||||||
|
struct _snd_mixer;
|
||||||
|
struct _snd_mixer_elem;
|
||||||
|
struct _snd_mixer_selem_id;
|
||||||
|
typedef struct _snd_mixer snd_mixer_t;
|
||||||
|
typedef struct _snd_mixer_elem snd_mixer_elem_t;
|
||||||
|
typedef struct _snd_mixer_selem_id snd_mixer_selem_id_t;
|
||||||
|
|
||||||
|
POLYBAR_NS
|
||||||
|
|
||||||
|
namespace alsa {
|
||||||
|
class mixer {
|
||||||
|
public:
|
||||||
|
explicit mixer(string mixer_control_name);
|
||||||
|
~mixer();
|
||||||
|
|
||||||
|
string get_name();
|
||||||
|
|
||||||
|
bool wait(int timeout = -1);
|
||||||
|
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();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex m_lock;
|
||||||
|
|
||||||
|
string m_name;
|
||||||
|
|
||||||
|
snd_mixer_selem_id_t* m_mixerid{nullptr};
|
||||||
|
snd_mixer_t* m_hardwaremixer{nullptr};
|
||||||
|
snd_mixer_elem_t* m_mixerelement{nullptr};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
POLYBAR_NS_END
|
|
@ -1,18 +1,22 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "adapters/alsa.hpp"
|
|
||||||
#include "components/config.hpp"
|
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
#include "modules/meta/event_module.hpp"
|
#include "modules/meta/event_module.hpp"
|
||||||
|
|
||||||
POLYBAR_NS
|
POLYBAR_NS
|
||||||
|
|
||||||
|
// fwd
|
||||||
|
namespace alsa {
|
||||||
|
class mixer;
|
||||||
|
class control;
|
||||||
|
}
|
||||||
|
|
||||||
namespace modules {
|
namespace modules {
|
||||||
enum class mixer { NONE = 0, MASTER, SPEAKER, HEADPHONE };
|
enum class mixer { NONE = 0, MASTER, SPEAKER, HEADPHONE };
|
||||||
enum class control { NONE = 0, HEADPHONE };
|
enum class control { NONE = 0, HEADPHONE };
|
||||||
|
|
||||||
using mixer_t = shared_ptr<alsa_mixer>;
|
using mixer_t = shared_ptr<alsa::mixer>;
|
||||||
using control_t = shared_ptr<alsa_ctl_interface>;
|
using control_t = shared_ptr<alsa::control>;
|
||||||
|
|
||||||
class volume_module : public event_module<volume_module> {
|
class volume_module : public event_module<volume_module> {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -32,7 +32,7 @@ namespace factory_util {
|
||||||
|
|
||||||
template <class T, class... Deps>
|
template <class T, class... Deps>
|
||||||
shared_ptr<T> singleton(Deps&&... deps) {
|
shared_ptr<T> singleton(Deps&&... deps) {
|
||||||
static auto instance = make_shared<T>(forward<Deps>(deps)...);
|
static shared_ptr<T> instance{make_shared<T>(forward<Deps>(deps)...)};
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,376 +0,0 @@
|
||||||
#include "adapters/alsa.hpp"
|
|
||||||
#include "utils/math.hpp"
|
|
||||||
|
|
||||||
POLYBAR_NS
|
|
||||||
|
|
||||||
// class : alsa_ctl_interface {{{
|
|
||||||
|
|
||||||
alsa_ctl_interface::alsa_ctl_interface(int numid) : m_numid(numid) {
|
|
||||||
snd_ctl_elem_info_malloc(&m_info);
|
|
||||||
|
|
||||||
if (m_info == nullptr) {
|
|
||||||
throw alsa_ctl_interface_error("Failed to allocate alsa_ctl info");
|
|
||||||
}
|
|
||||||
|
|
||||||
snd_ctl_elem_value_malloc(&m_value);
|
|
||||||
|
|
||||||
if (m_value == nullptr) {
|
|
||||||
throw alsa_ctl_interface_error("Failed to allocate alsa_ctl value");
|
|
||||||
}
|
|
||||||
|
|
||||||
snd_ctl_elem_id_malloc(&m_id);
|
|
||||||
|
|
||||||
if (m_id == nullptr) {
|
|
||||||
throw alsa_ctl_interface_error("Failed to allocate alsa_ctl id");
|
|
||||||
}
|
|
||||||
|
|
||||||
snd_ctl_elem_id_set_numid(m_id, m_numid);
|
|
||||||
snd_ctl_elem_info_set_id(m_info, m_id);
|
|
||||||
|
|
||||||
int err = 0;
|
|
||||||
|
|
||||||
if ((err = snd_ctl_open(&m_ctl, ALSA_SOUNDCARD, SND_CTL_NONBLOCK | SND_CTL_READONLY)) == -1) {
|
|
||||||
throw_exception<alsa_ctl_interface_error>("Could not open control '" + string{ALSA_SOUNDCARD} + "'", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
snd_config_update_free_global();
|
|
||||||
|
|
||||||
if ((err = snd_ctl_elem_info(m_ctl, m_info)) < 0) {
|
|
||||||
throw_exception<alsa_ctl_interface_error>("Could not get control datal", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
snd_ctl_elem_info_get_id(m_info, m_id);
|
|
||||||
|
|
||||||
if ((err = snd_hctl_open(&m_hctl, ALSA_SOUNDCARD, 0)) == -1) {
|
|
||||||
throw_exception<alsa_ctl_interface_error>("Failed to open hctl", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
snd_config_update_free_global();
|
|
||||||
|
|
||||||
if (m_hctl == nullptr || (err = snd_hctl_load(m_hctl)) < 0) {
|
|
||||||
throw_exception<alsa_ctl_interface_error>("Failed to load hctl", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((m_elem = snd_hctl_find_elem(m_hctl, m_id)) == nullptr) {
|
|
||||||
throw alsa_ctl_interface_error("Could not find control with id " + to_string(snd_ctl_elem_id_get_numid(m_id)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((err = snd_ctl_subscribe_events(m_ctl, 1)) < 0) {
|
|
||||||
throw alsa_ctl_interface_error("Could not subscribe to events: " + to_string(snd_ctl_elem_id_get_numid(m_id)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
alsa_ctl_interface::~alsa_ctl_interface() {
|
|
||||||
std::lock_guard<std::mutex> guard(m_lock);
|
|
||||||
if (m_info != nullptr) {
|
|
||||||
snd_ctl_elem_info_free(m_info);
|
|
||||||
}
|
|
||||||
if (m_value != nullptr) {
|
|
||||||
snd_ctl_elem_value_free(m_value);
|
|
||||||
}
|
|
||||||
if (m_id != nullptr) {
|
|
||||||
snd_ctl_elem_id_free(m_id);
|
|
||||||
}
|
|
||||||
if (m_ctl != nullptr) {
|
|
||||||
snd_ctl_close(m_ctl);
|
|
||||||
}
|
|
||||||
if (m_hctl != nullptr) {
|
|
||||||
snd_hctl_close(m_hctl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int alsa_ctl_interface::get_numid() {
|
|
||||||
return m_numid;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool alsa_ctl_interface::wait(int timeout) {
|
|
||||||
assert(m_ctl);
|
|
||||||
|
|
||||||
if (!m_lock.try_lock()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> guard(m_lock, std::adopt_lock);
|
|
||||||
|
|
||||||
int err = 0;
|
|
||||||
|
|
||||||
if ((err = snd_ctl_wait(m_ctl, timeout)) == -1) {
|
|
||||||
throw_exception<alsa_ctl_interface_error>("Failed to wait for events", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
snd_ctl_event_t* event;
|
|
||||||
snd_ctl_event_alloca(&event);
|
|
||||||
|
|
||||||
if ((err = snd_ctl_read(m_ctl, event)) == -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (snd_ctl_event_get_type(event) == SND_CTL_EVENT_ELEM) {
|
|
||||||
return snd_ctl_event_elem_get_mask(event) & SND_CTL_EVENT_MASK_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool alsa_ctl_interface::test_device_plugged() {
|
|
||||||
assert(m_elem);
|
|
||||||
assert(m_value);
|
|
||||||
|
|
||||||
if (!m_lock.try_lock()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> guard(m_lock, std::adopt_lock);
|
|
||||||
|
|
||||||
int err = 0;
|
|
||||||
if ((err = snd_hctl_elem_read(m_elem, m_value)) < 0) {
|
|
||||||
throw_exception<alsa_ctl_interface_error>("Could not read control value", err);
|
|
||||||
}
|
|
||||||
return snd_ctl_elem_value_get_boolean(m_value, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void alsa_ctl_interface::process_events() {
|
|
||||||
wait(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// }}}
|
|
||||||
// class : alsa_mixer {{{
|
|
||||||
|
|
||||||
alsa_mixer::alsa_mixer(string mixer_control_name) : m_name(move(mixer_control_name)) {
|
|
||||||
if (m_name.empty()) {
|
|
||||||
throw alsa_mixer_error("Invalid control name");
|
|
||||||
}
|
|
||||||
|
|
||||||
snd_mixer_selem_id_malloc(&m_mixerid);
|
|
||||||
|
|
||||||
if (m_mixerid == nullptr) {
|
|
||||||
throw alsa_mixer_error("Failed to allocate mixer id");
|
|
||||||
}
|
|
||||||
|
|
||||||
int err = 0;
|
|
||||||
if ((err = snd_mixer_open(&m_hardwaremixer, 1)) == -1) {
|
|
||||||
throw_exception<alsa_mixer_error>("Failed to open hardware mixer", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
snd_config_update_free_global();
|
|
||||||
|
|
||||||
if ((err = snd_mixer_attach(m_hardwaremixer, ALSA_SOUNDCARD)) == -1) {
|
|
||||||
throw_exception<alsa_mixer_error>("Failed to attach hardware mixer control", err);
|
|
||||||
}
|
|
||||||
if ((err = snd_mixer_selem_register(m_hardwaremixer, nullptr, nullptr)) == -1) {
|
|
||||||
throw_exception<alsa_mixer_error>("Failed to register simple mixer element", err);
|
|
||||||
}
|
|
||||||
if ((err = snd_mixer_load(m_hardwaremixer)) == -1) {
|
|
||||||
throw_exception<alsa_mixer_error>("Failed to load mixer", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
snd_mixer_selem_id_set_index(m_mixerid, 0);
|
|
||||||
snd_mixer_selem_id_set_name(m_mixerid, m_name.c_str());
|
|
||||||
|
|
||||||
if ((m_mixerelement = snd_mixer_find_selem(m_hardwaremixer, m_mixerid)) == nullptr) {
|
|
||||||
throw alsa_mixer_error("Cannot find simple element");
|
|
||||||
}
|
|
||||||
|
|
||||||
// log_trace("Successfully initialized mixer: "+ string{m_name});
|
|
||||||
}
|
|
||||||
|
|
||||||
alsa_mixer::~alsa_mixer() {
|
|
||||||
std::lock_guard<std::mutex> guard(m_lock);
|
|
||||||
if (m_mixerid != nullptr) {
|
|
||||||
snd_mixer_selem_id_free(m_mixerid);
|
|
||||||
}
|
|
||||||
if (m_mixerelement != nullptr) {
|
|
||||||
snd_mixer_elem_remove(m_mixerelement);
|
|
||||||
}
|
|
||||||
if (m_hardwaremixer != nullptr) {
|
|
||||||
snd_mixer_detach(m_hardwaremixer, ALSA_SOUNDCARD);
|
|
||||||
snd_mixer_close(m_hardwaremixer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
string alsa_mixer::get_name() {
|
|
||||||
return m_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool alsa_mixer::wait(int timeout) {
|
|
||||||
assert(m_hardwaremixer);
|
|
||||||
|
|
||||||
if (!m_lock.try_lock()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_lock<std::mutex> guard(m_lock, std::adopt_lock);
|
|
||||||
|
|
||||||
int err = 0;
|
|
||||||
|
|
||||||
if ((err = snd_mixer_wait(m_hardwaremixer, timeout)) == -1) {
|
|
||||||
throw_exception<alsa_mixer_error>("Failed to wait for events", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
guard.unlock();
|
|
||||||
|
|
||||||
return process_events() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int alsa_mixer::process_events() {
|
|
||||||
if (!m_lock.try_lock()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> guard(m_lock, std::adopt_lock);
|
|
||||||
|
|
||||||
int num_events = snd_mixer_handle_events(m_hardwaremixer);
|
|
||||||
if (num_events < 0) {
|
|
||||||
throw_exception<alsa_mixer_error>("Failed to process pending events", num_events);
|
|
||||||
}
|
|
||||||
|
|
||||||
return num_events;
|
|
||||||
}
|
|
||||||
|
|
||||||
int alsa_mixer::get_volume() {
|
|
||||||
if (!m_lock.try_lock()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> guard(m_lock, std::adopt_lock);
|
|
||||||
|
|
||||||
long chan_n = 0, vol_total = 0, vol, vol_min, vol_max;
|
|
||||||
|
|
||||||
snd_mixer_selem_get_playback_volume_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_volume(m_mixerelement, static_cast<snd_mixer_selem_channel_id_t>(i), &vol);
|
|
||||||
vol_total += vol;
|
|
||||||
chan_n++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return math_util::percentage(vol_total / chan_n, vol_min, vol_max);
|
|
||||||
}
|
|
||||||
|
|
||||||
int alsa_mixer::get_normalized_volume() {
|
|
||||||
if (!m_lock.try_lock()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> guard(m_lock, std::adopt_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 math_util::percentage(vol_total / chan_n, vol_min, vol_max);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_lock.try_lock()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> guard(m_lock, std::adopt_lock);
|
|
||||||
|
|
||||||
long vol_min, vol_max;
|
|
||||||
snd_mixer_selem_get_playback_volume_range(m_mixerelement, &vol_min, &vol_max);
|
|
||||||
snd_mixer_selem_set_playback_volume_all(
|
|
||||||
m_mixerelement, math_util::percentage_to_value<int>(percentage, vol_min, vol_max));
|
|
||||||
}
|
|
||||||
|
|
||||||
void alsa_mixer::set_normalized_volume(float percentage) {
|
|
||||||
if (is_muted()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_lock.try_lock()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> guard(m_lock, std::adopt_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) {
|
|
||||||
if (!m_lock.try_lock()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> guard(m_lock, std::adopt_lock);
|
|
||||||
|
|
||||||
snd_mixer_selem_set_playback_switch_all(m_mixerelement, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
void alsa_mixer::toggle_mute() {
|
|
||||||
if (!m_lock.try_lock()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> guard(m_lock, std::adopt_lock);
|
|
||||||
|
|
||||||
int state;
|
|
||||||
|
|
||||||
snd_mixer_selem_get_playback_switch(m_mixerelement, SND_MIXER_SCHN_MONO, &state);
|
|
||||||
snd_mixer_selem_set_playback_switch_all(m_mixerelement, !state);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool alsa_mixer::is_muted() {
|
|
||||||
if (!m_lock.try_lock()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> guard(m_lock, std::adopt_lock);
|
|
||||||
|
|
||||||
int state = 0;
|
|
||||||
|
|
||||||
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))) {
|
|
||||||
int state_ = 0;
|
|
||||||
snd_mixer_selem_get_playback_switch(m_mixerelement, static_cast<snd_mixer_selem_channel_id_t>(i), &state_);
|
|
||||||
state = state || state_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return !state;
|
|
||||||
}
|
|
||||||
|
|
||||||
// }}}
|
|
||||||
|
|
||||||
POLYBAR_NS_END
|
|
154
src/adapters/alsa/control.cpp
Normal file
154
src/adapters/alsa/control.cpp
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
#include "adapters/alsa/control.hpp"
|
||||||
|
#include "adapters/alsa/generic.hpp"
|
||||||
|
|
||||||
|
POLYBAR_NS
|
||||||
|
|
||||||
|
namespace alsa {
|
||||||
|
/**
|
||||||
|
* Construct control object
|
||||||
|
*/
|
||||||
|
control::control(int numid) : m_numid(numid) {
|
||||||
|
snd_ctl_elem_info_malloc(&m_info);
|
||||||
|
|
||||||
|
if (m_info == nullptr) {
|
||||||
|
throw control_error("Failed to allocate alsa_ctl info");
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_ctl_elem_value_malloc(&m_value);
|
||||||
|
|
||||||
|
if (m_value == nullptr) {
|
||||||
|
throw control_error("Failed to allocate alsa_ctl value");
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_ctl_elem_id_malloc(&m_id);
|
||||||
|
|
||||||
|
if (m_id == nullptr) {
|
||||||
|
throw control_error("Failed to allocate alsa_ctl id");
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_ctl_elem_id_set_numid(m_id, m_numid);
|
||||||
|
snd_ctl_elem_info_set_id(m_info, m_id);
|
||||||
|
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
if ((err = snd_ctl_open(&m_ctl, ALSA_SOUNDCARD, SND_CTL_NONBLOCK | SND_CTL_READONLY)) == -1) {
|
||||||
|
throw_exception<control_error>("Could not open control '" + string{ALSA_SOUNDCARD} + "'", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_config_update_free_global();
|
||||||
|
|
||||||
|
if ((err = snd_ctl_elem_info(m_ctl, m_info)) == -1) {
|
||||||
|
throw_exception<control_error>("Could not get control datal", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_ctl_elem_info_get_id(m_info, m_id);
|
||||||
|
|
||||||
|
if ((err = snd_hctl_open(&m_hctl, ALSA_SOUNDCARD, 0)) == -1) {
|
||||||
|
throw_exception<control_error>("Failed to open hctl", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_config_update_free_global();
|
||||||
|
|
||||||
|
if (m_hctl == nullptr || (err = snd_hctl_load(m_hctl)) == -1) {
|
||||||
|
throw_exception<control_error>("Failed to load hctl", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((m_elem = snd_hctl_find_elem(m_hctl, m_id)) == nullptr) {
|
||||||
|
throw control_error("Could not find control with id " + to_string(snd_ctl_elem_id_get_numid(m_id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_ctl_subscribe_events(m_ctl, 1)) == -1) {
|
||||||
|
throw control_error("Could not subscribe to events: " + to_string(snd_ctl_elem_id_get_numid(m_id)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deconstruct control object
|
||||||
|
*/
|
||||||
|
control::~control() {
|
||||||
|
std::lock_guard<std::mutex> guard(m_lock);
|
||||||
|
if (m_info != nullptr) {
|
||||||
|
snd_ctl_elem_info_free(m_info);
|
||||||
|
}
|
||||||
|
if (m_value != nullptr) {
|
||||||
|
snd_ctl_elem_value_free(m_value);
|
||||||
|
}
|
||||||
|
if (m_id != nullptr) {
|
||||||
|
snd_ctl_elem_id_free(m_id);
|
||||||
|
}
|
||||||
|
if (m_ctl != nullptr) {
|
||||||
|
snd_ctl_close(m_ctl);
|
||||||
|
}
|
||||||
|
if (m_hctl != nullptr) {
|
||||||
|
snd_hctl_close(m_hctl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the id number
|
||||||
|
*/
|
||||||
|
int control::get_numid() {
|
||||||
|
return m_numid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for events
|
||||||
|
*/
|
||||||
|
bool control::wait(int timeout) {
|
||||||
|
assert(m_ctl);
|
||||||
|
|
||||||
|
if (!m_lock.try_lock()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> guard(m_lock, std::adopt_lock);
|
||||||
|
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
if ((err = snd_ctl_wait(m_ctl, timeout)) == -1) {
|
||||||
|
throw_exception<control_error>("Failed to wait for events", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_ctl_event_t* event;
|
||||||
|
snd_ctl_event_alloca(&event);
|
||||||
|
|
||||||
|
if ((err = snd_ctl_read(m_ctl, event)) == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snd_ctl_event_get_type(event) == SND_CTL_EVENT_ELEM) {
|
||||||
|
return snd_ctl_event_elem_get_mask(event) & SND_CTL_EVENT_MASK_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the interface is in use
|
||||||
|
*/
|
||||||
|
bool control::test_device_plugged() {
|
||||||
|
assert(m_elem);
|
||||||
|
assert(m_value);
|
||||||
|
|
||||||
|
if (!m_lock.try_lock()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> guard(m_lock, std::adopt_lock);
|
||||||
|
|
||||||
|
int err = 0;
|
||||||
|
if ((err = snd_hctl_elem_read(m_elem, m_value)) == -1) {
|
||||||
|
throw_exception<control_error>("Could not read control value", err);
|
||||||
|
}
|
||||||
|
return snd_ctl_elem_value_get_boolean(m_value, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process queued events
|
||||||
|
*/
|
||||||
|
void control::process_events() {
|
||||||
|
wait(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
POLYBAR_NS_END
|
282
src/adapters/alsa/mixer.cpp
Normal file
282
src/adapters/alsa/mixer.cpp
Normal file
|
@ -0,0 +1,282 @@
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "adapters/alsa/generic.hpp"
|
||||||
|
#include "adapters/alsa/mixer.hpp"
|
||||||
|
#include "utils/math.hpp"
|
||||||
|
|
||||||
|
#define MAX_LINEAR_DB_SCALE 24
|
||||||
|
|
||||||
|
POLYBAR_NS
|
||||||
|
|
||||||
|
namespace alsa {
|
||||||
|
/**
|
||||||
|
* Construct mixer object
|
||||||
|
*/
|
||||||
|
mixer::mixer(string mixer_control_name) : m_name(move(mixer_control_name)) {
|
||||||
|
if (m_name.empty()) {
|
||||||
|
throw mixer_error("Invalid control name");
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_mixer_selem_id_malloc(&m_mixerid);
|
||||||
|
|
||||||
|
if (m_mixerid == nullptr) {
|
||||||
|
throw mixer_error("Failed to allocate mixer id");
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = 0;
|
||||||
|
if ((err = snd_mixer_open(&m_hardwaremixer, 1)) == -1) {
|
||||||
|
throw_exception<mixer_error>("Failed to open hardware mixer", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_config_update_free_global();
|
||||||
|
|
||||||
|
if ((err = snd_mixer_attach(m_hardwaremixer, ALSA_SOUNDCARD)) == -1) {
|
||||||
|
throw_exception<mixer_error>("Failed to attach hardware mixer control", err);
|
||||||
|
}
|
||||||
|
if ((err = snd_mixer_selem_register(m_hardwaremixer, nullptr, nullptr)) == -1) {
|
||||||
|
throw_exception<mixer_error>("Failed to register simple mixer element", err);
|
||||||
|
}
|
||||||
|
if ((err = snd_mixer_load(m_hardwaremixer)) == -1) {
|
||||||
|
throw_exception<mixer_error>("Failed to load mixer", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_mixer_selem_id_set_index(m_mixerid, 0);
|
||||||
|
snd_mixer_selem_id_set_name(m_mixerid, m_name.c_str());
|
||||||
|
|
||||||
|
if ((m_mixerelement = snd_mixer_find_selem(m_hardwaremixer, m_mixerid)) == nullptr) {
|
||||||
|
throw mixer_error("Cannot find simple element");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deconstruct mixer
|
||||||
|
*/
|
||||||
|
mixer::~mixer() {
|
||||||
|
std::lock_guard<std::mutex> guard(m_lock);
|
||||||
|
if (m_mixerid != nullptr) {
|
||||||
|
snd_mixer_selem_id_free(m_mixerid);
|
||||||
|
}
|
||||||
|
if (m_mixerelement != nullptr) {
|
||||||
|
snd_mixer_elem_remove(m_mixerelement);
|
||||||
|
}
|
||||||
|
if (m_hardwaremixer != nullptr) {
|
||||||
|
snd_mixer_detach(m_hardwaremixer, ALSA_SOUNDCARD);
|
||||||
|
snd_mixer_close(m_hardwaremixer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get mixer name
|
||||||
|
*/
|
||||||
|
string mixer::get_name() {
|
||||||
|
return m_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for events
|
||||||
|
*/
|
||||||
|
bool mixer::wait(int timeout) {
|
||||||
|
assert(m_hardwaremixer);
|
||||||
|
|
||||||
|
if (!m_lock.try_lock()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> guard(m_lock, std::adopt_lock);
|
||||||
|
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
if ((err = snd_mixer_wait(m_hardwaremixer, timeout)) == -1) {
|
||||||
|
throw_exception<mixer_error>("Failed to wait for events", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
guard.unlock();
|
||||||
|
|
||||||
|
return process_events() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process queued mixer events
|
||||||
|
*/
|
||||||
|
int mixer::process_events() {
|
||||||
|
if (!m_lock.try_lock()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> guard(m_lock, std::adopt_lock);
|
||||||
|
|
||||||
|
int num_events = snd_mixer_handle_events(m_hardwaremixer);
|
||||||
|
if (num_events == -1) {
|
||||||
|
throw_exception<mixer_error>("Failed to process pending events", num_events);
|
||||||
|
}
|
||||||
|
|
||||||
|
return num_events;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get volume in percentage
|
||||||
|
*/
|
||||||
|
int mixer::get_volume() {
|
||||||
|
if (!m_lock.try_lock()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> guard(m_lock, std::adopt_lock);
|
||||||
|
|
||||||
|
long chan_n = 0, vol_total = 0, vol, vol_min, vol_max;
|
||||||
|
|
||||||
|
snd_mixer_selem_get_playback_volume_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_volume(m_mixerelement, static_cast<snd_mixer_selem_channel_id_t>(i), &vol);
|
||||||
|
vol_total += vol;
|
||||||
|
chan_n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return math_util::percentage(vol_total / chan_n, vol_min, vol_max);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get normalized volume in percentage
|
||||||
|
*/
|
||||||
|
int mixer::get_normalized_volume() {
|
||||||
|
if (!m_lock.try_lock()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> guard(m_lock, std::adopt_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 math_util::percentage(vol_total / chan_n, vol_min, vol_max);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set volume to given percentage
|
||||||
|
*/
|
||||||
|
void mixer::set_volume(float percentage) {
|
||||||
|
if (is_muted()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_lock.try_lock()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> guard(m_lock, std::adopt_lock);
|
||||||
|
|
||||||
|
long vol_min, vol_max;
|
||||||
|
snd_mixer_selem_get_playback_volume_range(m_mixerelement, &vol_min, &vol_max);
|
||||||
|
snd_mixer_selem_set_playback_volume_all(
|
||||||
|
m_mixerelement, math_util::percentage_to_value<int>(percentage, vol_min, vol_max));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set normalized volume to given percentage
|
||||||
|
*/
|
||||||
|
void mixer::set_normalized_volume(float percentage) {
|
||||||
|
if (is_muted()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_lock.try_lock()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> guard(m_lock, std::adopt_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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set mute state
|
||||||
|
*/
|
||||||
|
void mixer::set_mute(bool mode) {
|
||||||
|
if (!m_lock.try_lock()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> guard(m_lock, std::adopt_lock);
|
||||||
|
|
||||||
|
snd_mixer_selem_set_playback_switch_all(m_mixerelement, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle mute state
|
||||||
|
*/
|
||||||
|
void mixer::toggle_mute() {
|
||||||
|
if (!m_lock.try_lock()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> guard(m_lock, std::adopt_lock);
|
||||||
|
|
||||||
|
int state;
|
||||||
|
|
||||||
|
snd_mixer_selem_get_playback_switch(m_mixerelement, SND_MIXER_SCHN_MONO, &state);
|
||||||
|
snd_mixer_selem_set_playback_switch_all(m_mixerelement, !state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current mute state
|
||||||
|
*/
|
||||||
|
bool mixer::is_muted() {
|
||||||
|
if (!m_lock.try_lock()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> guard(m_lock, std::adopt_lock);
|
||||||
|
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
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))) {
|
||||||
|
int state_ = 0;
|
||||||
|
snd_mixer_selem_get_playback_switch(m_mixerelement, static_cast<snd_mixer_selem_channel_id_t>(i), &state_);
|
||||||
|
state = state || state_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
POLYBAR_NS_END
|
|
@ -1,5 +1,7 @@
|
||||||
#include "modules/volume.hpp"
|
#include "modules/volume.hpp"
|
||||||
|
#include "adapters/alsa/control.hpp"
|
||||||
|
#include "adapters/alsa/generic.hpp"
|
||||||
|
#include "adapters/alsa/mixer.hpp"
|
||||||
#include "drawtypes/label.hpp"
|
#include "drawtypes/label.hpp"
|
||||||
#include "drawtypes/progressbar.hpp"
|
#include "drawtypes/progressbar.hpp"
|
||||||
#include "drawtypes/ramp.hpp"
|
#include "drawtypes/ramp.hpp"
|
||||||
|
@ -10,6 +12,8 @@
|
||||||
|
|
||||||
POLYBAR_NS
|
POLYBAR_NS
|
||||||
|
|
||||||
|
using namespace alsa;
|
||||||
|
|
||||||
namespace modules {
|
namespace modules {
|
||||||
template class module<volume_module>;
|
template class module<volume_module>;
|
||||||
template class event_module<volume_module>;
|
template class event_module<volume_module>;
|
||||||
|
@ -53,9 +57,9 @@ namespace modules {
|
||||||
if (m_mixer.empty()) {
|
if (m_mixer.empty()) {
|
||||||
throw module_error("No configured mixers");
|
throw module_error("No configured mixers");
|
||||||
}
|
}
|
||||||
} catch (const alsa_mixer_error& err) {
|
} catch (const mixer_error& err) {
|
||||||
throw module_error(err.what());
|
throw module_error(err.what());
|
||||||
} catch (const alsa_ctl_interface_error& err) {
|
} catch (const control_error& err) {
|
||||||
throw module_error(err.what());
|
throw module_error(err.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue