Compare commits
2 Commits
master
...
pulseaudio
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3db6277512 | ||
|
|
135147af9c |
@@ -26,9 +26,9 @@ class pulseaudio {
|
|||||||
enum class evtype { NEW = 0, CHANGE, REMOVE, SERVER };
|
enum class evtype { NEW = 0, CHANGE, REMOVE, SERVER };
|
||||||
using queue = std::queue<evtype>;
|
using queue = std::queue<evtype>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit pulseaudio(const logger& logger, string&& sink_name, bool m_max_volume);
|
explicit pulseaudio(const logger& logger, string&& name, bool is_sink_or_source, bool m_max_volume);
|
||||||
~pulseaudio();
|
~pulseaudio();
|
||||||
|
|
||||||
pulseaudio(const pulseaudio& o) = delete;
|
pulseaudio(const pulseaudio& o) = delete;
|
||||||
pulseaudio& operator=(const pulseaudio& o) = delete;
|
pulseaudio& operator=(const pulseaudio& o) = delete;
|
||||||
@@ -46,14 +46,16 @@ class pulseaudio {
|
|||||||
void toggle_mute();
|
void toggle_mute();
|
||||||
bool is_muted();
|
bool is_muted();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void update_volume(pa_operation* o);
|
void update_volume(pa_operation *o);
|
||||||
static void check_mute_callback(pa_context* context, const pa_sink_info* info, int eol, void* userdata);
|
static void check_mute_callback(pa_context *context, const pa_sink_info *info, int eol, void *userdata);
|
||||||
static void get_sink_volume_callback(pa_context* context, const pa_sink_info* info, int is_last, void* userdata);
|
static void get_sink_volume_callback(pa_context *context, const pa_sink_info *info, int is_last, void *userdata);
|
||||||
static void subscribe_callback(pa_context* context, pa_subscription_event_type_t t, uint32_t idx, void* userdata);
|
static void get_source_volume_callback(pa_context *context, const pa_source_info *info, int is_last, void *userdata);
|
||||||
static void simple_callback(pa_context* context, int success, void* userdata);
|
static void subscribe_callback(pa_context* context, pa_subscription_event_type_t t, uint32_t idx, void* userdata);
|
||||||
static void sink_info_callback(pa_context* context, const pa_sink_info* info, int eol, void* userdata);
|
static void simple_callback(pa_context *context, int success, void *userdata);
|
||||||
static void context_state_callback(pa_context* context, void* userdata);
|
static void sink_info_callback(pa_context *context, const pa_sink_info *info, int eol, void *userdata);
|
||||||
|
static void source_info_callback(pa_context *context, const pa_source_info *info, int eol, void *userdata);
|
||||||
|
static void context_state_callback(pa_context *context, void *userdata);
|
||||||
|
|
||||||
inline void wait_loop(pa_operation* op, pa_threaded_mainloop* loop);
|
inline void wait_loop(pa_operation* op, pa_threaded_mainloop* loop);
|
||||||
|
|
||||||
@@ -64,22 +66,24 @@ class pulseaudio {
|
|||||||
*/
|
*/
|
||||||
std::atomic_bool m_state_callback_signal{false};
|
std::atomic_bool m_state_callback_signal{false};
|
||||||
|
|
||||||
// used for temporary callback results
|
// used for temporary callback results
|
||||||
int success{0};
|
int success{0};
|
||||||
pa_cvolume cv{};
|
pa_cvolume cv{};
|
||||||
bool muted{false};
|
bool muted{false};
|
||||||
// default sink name
|
// default sink name
|
||||||
static constexpr auto DEFAULT_SINK = "@DEFAULT_SINK@";
|
static constexpr auto DEFAULT_SINK = "@DEFAULT_SINK@";
|
||||||
|
static constexpr auto DEFAULT_SOURCE = "@DEFAULT_SOURCE@";
|
||||||
|
|
||||||
pa_context* m_context{nullptr};
|
pa_context* m_context{nullptr};
|
||||||
pa_threaded_mainloop* m_mainloop{nullptr};
|
pa_threaded_mainloop* m_mainloop{nullptr};
|
||||||
|
|
||||||
queue m_events;
|
queue m_events;
|
||||||
|
|
||||||
// specified sink name
|
// specified sink name
|
||||||
string spec_s_name;
|
string spec_s_name;
|
||||||
string s_name;
|
string s_name;
|
||||||
uint32_t m_index{0};
|
bool is_sink_or_source; // true => sink / false => source
|
||||||
|
uint32_t m_index{0};
|
||||||
|
|
||||||
pa_volume_t m_max_volume{PA_VOLUME_UI_MAX};
|
pa_volume_t m_max_volume{PA_VOLUME_UI_MAX};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -45,8 +45,17 @@ class config {
|
|||||||
|
|
||||||
void warn_deprecated(const string& section, const string& key, string replacement = "") const;
|
void warn_deprecated(const string& section, const string& key, string replacement = "") const;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if a given parameter exists
|
* Returns true if a given parameter exists by name
|
||||||
|
*/
|
||||||
|
bool has(const string& key) const {
|
||||||
|
auto it = m_sections.find(section());
|
||||||
|
return it != m_sections.end() && it->second.find(key) != it->second.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if a given parameter exists by section and name
|
||||||
*/
|
*/
|
||||||
bool has(const string& section, const string& key) const;
|
bool has(const string& section, const string& key) const;
|
||||||
|
|
||||||
|
|||||||
Submodule lib/i3ipcpp updated: 0daa58349a...c982bc069c
2
lib/xpp
2
lib/xpp
Submodule lib/xpp updated: a8b9e682ba...2f9ff5afc2
@@ -7,8 +7,7 @@ POLYBAR_NS
|
|||||||
/**
|
/**
|
||||||
* Construct pulseaudio object
|
* Construct pulseaudio object
|
||||||
*/
|
*/
|
||||||
pulseaudio::pulseaudio(const logger& logger, string&& sink_name, bool max_volume)
|
pulseaudio::pulseaudio(const logger& logger, string&& name, bool is_sink_or_source, bool max_volume) : m_log(logger), spec_s_name(name), is_sink_or_source(is_sink_or_source) {
|
||||||
: m_log(logger), spec_s_name(sink_name) {
|
|
||||||
m_mainloop = pa_threaded_mainloop_new();
|
m_mainloop = pa_threaded_mainloop_new();
|
||||||
if (!m_mainloop) {
|
if (!m_mainloop) {
|
||||||
throw pulseaudio_error("Could not create pulseaudio threaded mainloop.");
|
throw pulseaudio_error("Could not create pulseaudio threaded mainloop.");
|
||||||
@@ -65,27 +64,39 @@ pulseaudio::pulseaudio(const logger& logger, string&& sink_name, bool max_volume
|
|||||||
}
|
}
|
||||||
|
|
||||||
pa_operation* op{nullptr};
|
pa_operation* op{nullptr};
|
||||||
if (!sink_name.empty()) {
|
if (!name.empty()) {
|
||||||
op = pa_context_get_sink_info_by_name(m_context, sink_name.c_str(), sink_info_callback, this);
|
if (is_sink_or_source) {
|
||||||
|
op = pa_context_get_sink_info_by_name(m_context, name.c_str(), sink_info_callback, this);
|
||||||
|
} else {
|
||||||
|
op = pa_context_get_source_info_by_name(m_context, name.c_str(), source_info_callback, this);
|
||||||
|
}
|
||||||
wait_loop(op, m_mainloop);
|
wait_loop(op, m_mainloop);
|
||||||
}
|
}
|
||||||
if (s_name.empty()) {
|
if (s_name.empty()) {
|
||||||
// get the sink index
|
// get the sink/source index
|
||||||
op = pa_context_get_sink_info_by_name(m_context, DEFAULT_SINK, sink_info_callback, this);
|
if (is_sink_or_source) {
|
||||||
|
op = pa_context_get_sink_info_by_name(m_context, DEFAULT_SINK, sink_info_callback, this);
|
||||||
|
} else {
|
||||||
|
op = pa_context_get_source_info_by_name(m_context, DEFAULT_SOURCE, source_info_callback, this);
|
||||||
|
}
|
||||||
wait_loop(op, m_mainloop);
|
wait_loop(op, m_mainloop);
|
||||||
m_log.notice("pulseaudio: using default sink %s", s_name);
|
m_log.notice("pulseaudio: using default sink/source %s", s_name);
|
||||||
} else {
|
} else {
|
||||||
m_log.trace("pulseaudio: using sink %s", s_name);
|
m_log.trace("pulseaudio: using sink/source %s", s_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_max_volume = max_volume ? PA_VOLUME_UI_MAX : PA_VOLUME_NORM;
|
m_max_volume = max_volume ? PA_VOLUME_UI_MAX : PA_VOLUME_NORM;
|
||||||
|
|
||||||
auto event_types = static_cast<pa_subscription_mask_t>(PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SERVER);
|
pa_subscription_mask_t event_types;
|
||||||
|
if (is_sink_or_source) {
|
||||||
|
event_types = static_cast<pa_subscription_mask_t>(PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SERVER);
|
||||||
|
} else {
|
||||||
|
event_types = static_cast<pa_subscription_mask_t>(PA_SUBSCRIPTION_MASK_SOURCE | PA_SUBSCRIPTION_MASK_SERVER);
|
||||||
|
}
|
||||||
op = pa_context_subscribe(m_context, event_types, simple_callback, this);
|
op = pa_context_subscribe(m_context, event_types, simple_callback, this);
|
||||||
wait_loop(op, m_mainloop);
|
wait_loop(op, m_mainloop);
|
||||||
if (!success) {
|
if (!success)
|
||||||
throw pulseaudio_error("Failed to subscribe to sink.");
|
throw pulseaudio_error("Failed to subscribe to sink/source.");
|
||||||
}
|
|
||||||
pa_context_set_subscribe_callback(m_context, subscribe_callback, this);
|
pa_context_set_subscribe_callback(m_context, subscribe_callback, this);
|
||||||
|
|
||||||
update_volume(op);
|
update_volume(op);
|
||||||
@@ -104,7 +115,7 @@ pulseaudio::~pulseaudio() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get sink name
|
* Get sink/source name
|
||||||
*/
|
*/
|
||||||
const string& pulseaudio::get_name() {
|
const string& pulseaudio::get_name() {
|
||||||
return s_name;
|
return s_name;
|
||||||
@@ -129,26 +140,33 @@ int pulseaudio::process_events() {
|
|||||||
switch (m_events.front()) {
|
switch (m_events.front()) {
|
||||||
// try to get specified sink
|
// try to get specified sink
|
||||||
case evtype::NEW:
|
case evtype::NEW:
|
||||||
// redundant if already using specified sink
|
// redundant if already using specified sink/source
|
||||||
if (!spec_s_name.empty()) {
|
if (!spec_s_name.empty()) {
|
||||||
o = pa_context_get_sink_info_by_name(m_context, spec_s_name.c_str(), sink_info_callback, this);
|
if (is_sink_or_source) {
|
||||||
|
o = pa_context_get_sink_info_by_name(m_context, spec_s_name.c_str(), sink_info_callback, this);
|
||||||
|
} else {
|
||||||
|
o = pa_context_get_source_info_by_name(m_context, spec_s_name.c_str(), source_info_callback, this);
|
||||||
|
}
|
||||||
wait_loop(o, m_mainloop);
|
wait_loop(o, m_mainloop);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// FALLTHRU
|
// FALLTHRU
|
||||||
case evtype::SERVER:
|
case evtype::SERVER:
|
||||||
// don't fallthrough only if always using default sink
|
// don't fallthrough only if always using default sink/source
|
||||||
if (!spec_s_name.empty()) {
|
if (!spec_s_name.empty()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// FALLTHRU
|
// FALLTHRU
|
||||||
// get default sink
|
// get default sink
|
||||||
case evtype::REMOVE:
|
case evtype::REMOVE:
|
||||||
o = pa_context_get_sink_info_by_name(m_context, DEFAULT_SINK, sink_info_callback, this);
|
if (is_sink_or_source) {
|
||||||
wait_loop(o, m_mainloop);
|
o = pa_context_get_sink_info_by_name(m_context, DEFAULT_SINK, sink_info_callback, this);
|
||||||
if (spec_s_name != s_name) {
|
} else {
|
||||||
m_log.notice("pulseaudio: using default sink %s", s_name);
|
o = pa_context_get_source_info_by_name(m_context, DEFAULT_SOURCE, source_info_callback, this);
|
||||||
}
|
}
|
||||||
|
wait_loop(o, m_mainloop);
|
||||||
|
if (spec_s_name != s_name)
|
||||||
|
m_log.notice("pulseaudio: using default sink/source %s", s_name);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -182,10 +200,15 @@ void pulseaudio::set_volume(float percentage) {
|
|||||||
pa_threaded_mainloop_lock(m_mainloop);
|
pa_threaded_mainloop_lock(m_mainloop);
|
||||||
pa_volume_t vol = math_util::percentage_to_value<pa_volume_t>(percentage, PA_VOLUME_MUTED, PA_VOLUME_NORM);
|
pa_volume_t vol = math_util::percentage_to_value<pa_volume_t>(percentage, PA_VOLUME_MUTED, PA_VOLUME_NORM);
|
||||||
pa_cvolume_scale(&cv, vol);
|
pa_cvolume_scale(&cv, vol);
|
||||||
pa_operation* op = pa_context_set_sink_volume_by_index(m_context, m_index, &cv, simple_callback, this);
|
pa_operation* op;
|
||||||
|
if (is_sink_or_source) {
|
||||||
|
op = pa_context_set_sink_volume_by_index(m_context, m_index, &cv, simple_callback, this);
|
||||||
|
} else {
|
||||||
|
op = pa_context_set_source_volume_by_index(m_context, m_index, &cv, simple_callback, this);
|
||||||
|
}
|
||||||
wait_loop(op, m_mainloop);
|
wait_loop(op, m_mainloop);
|
||||||
if (!success)
|
if (!success)
|
||||||
throw pulseaudio_error("Failed to set sink volume.");
|
throw pulseaudio_error("Failed to set sink/source volume.");
|
||||||
pa_threaded_mainloop_unlock(m_mainloop);
|
pa_threaded_mainloop_unlock(m_mainloop);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,10 +230,15 @@ void pulseaudio::inc_volume(int delta_perc) {
|
|||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
pa_cvolume_dec(&cv, vol);
|
pa_cvolume_dec(&cv, vol);
|
||||||
pa_operation* op = pa_context_set_sink_volume_by_index(m_context, m_index, &cv, simple_callback, this);
|
pa_operation* op;
|
||||||
|
if (is_sink_or_source) {
|
||||||
|
op = pa_context_set_sink_volume_by_index(m_context, m_index, &cv, simple_callback, this);
|
||||||
|
} else {
|
||||||
|
op = pa_context_set_source_volume_by_index(m_context, m_index, &cv, simple_callback, this);
|
||||||
|
}
|
||||||
wait_loop(op, m_mainloop);
|
wait_loop(op, m_mainloop);
|
||||||
if (!success)
|
if (!success)
|
||||||
throw pulseaudio_error("Failed to set sink volume.");
|
throw pulseaudio_error("Failed to set sink/source volume.");
|
||||||
pa_threaded_mainloop_unlock(m_mainloop);
|
pa_threaded_mainloop_unlock(m_mainloop);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,10 +247,15 @@ void pulseaudio::inc_volume(int delta_perc) {
|
|||||||
*/
|
*/
|
||||||
void pulseaudio::set_mute(bool mode) {
|
void pulseaudio::set_mute(bool mode) {
|
||||||
pa_threaded_mainloop_lock(m_mainloop);
|
pa_threaded_mainloop_lock(m_mainloop);
|
||||||
pa_operation* op = pa_context_set_sink_mute_by_index(m_context, m_index, mode, simple_callback, this);
|
pa_operation* op;
|
||||||
|
if (is_sink_or_source) {
|
||||||
|
op = pa_context_set_sink_mute_by_index(m_context, m_index, mode, simple_callback, this);
|
||||||
|
} else {
|
||||||
|
op = pa_context_set_source_mute_by_index(m_context, m_index, mode, simple_callback, this);
|
||||||
|
}
|
||||||
wait_loop(op, m_mainloop);
|
wait_loop(op, m_mainloop);
|
||||||
if (!success)
|
if (!success)
|
||||||
throw pulseaudio_error("Failed to mute sink.");
|
throw pulseaudio_error("Failed to mute sink/source.");
|
||||||
pa_threaded_mainloop_unlock(m_mainloop);
|
pa_threaded_mainloop_unlock(m_mainloop);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,13 +276,17 @@ bool pulseaudio::is_muted() {
|
|||||||
/**
|
/**
|
||||||
* Update local volume cache
|
* Update local volume cache
|
||||||
*/
|
*/
|
||||||
void pulseaudio::update_volume(pa_operation* o) {
|
void pulseaudio::update_volume(pa_operation *o) {
|
||||||
o = pa_context_get_sink_info_by_index(m_context, m_index, get_sink_volume_callback, this);
|
if (is_sink_or_source) {
|
||||||
|
o = pa_context_get_sink_info_by_index(m_context, m_index, get_sink_volume_callback, this);
|
||||||
|
} else {
|
||||||
|
o = pa_context_get_source_info_by_index(m_context, m_index, get_source_volume_callback, this);
|
||||||
|
}
|
||||||
wait_loop(o, m_mainloop);
|
wait_loop(o, m_mainloop);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback when getting volume
|
* Callback when getting sink volume
|
||||||
*/
|
*/
|
||||||
void pulseaudio::get_sink_volume_callback(pa_context*, const pa_sink_info* info, int, void* userdata) {
|
void pulseaudio::get_sink_volume_callback(pa_context*, const pa_sink_info* info, int, void* userdata) {
|
||||||
pulseaudio* This = static_cast<pulseaudio*>(userdata);
|
pulseaudio* This = static_cast<pulseaudio*>(userdata);
|
||||||
@@ -260,6 +297,18 @@ void pulseaudio::get_sink_volume_callback(pa_context*, const pa_sink_info* info,
|
|||||||
pa_threaded_mainloop_signal(This->m_mainloop, 0);
|
pa_threaded_mainloop_signal(This->m_mainloop, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback when getting source volume
|
||||||
|
*/
|
||||||
|
void pulseaudio::get_source_volume_callback(pa_context *, const pa_source_info *info, int, void *userdata) {
|
||||||
|
pulseaudio* This = static_cast<pulseaudio *>(userdata);
|
||||||
|
if (info) {
|
||||||
|
This->cv = info->volume;
|
||||||
|
This->muted = info->mute;
|
||||||
|
}
|
||||||
|
pa_threaded_mainloop_signal(This->m_mainloop, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback when subscribing to changes
|
* Callback when subscribing to changes
|
||||||
*/
|
*/
|
||||||
@@ -274,7 +323,8 @@ void pulseaudio::subscribe_callback(pa_context*, pa_subscription_event_type_t t,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PA_SUBSCRIPTION_EVENT_SINK:
|
case PA_SUBSCRIPTION_EVENT_SINK:
|
||||||
switch (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) {
|
case PA_SUBSCRIPTION_EVENT_SOURCE:
|
||||||
|
switch(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) {
|
||||||
case PA_SUBSCRIPTION_EVENT_NEW:
|
case PA_SUBSCRIPTION_EVENT_NEW:
|
||||||
This->m_events.emplace(evtype::NEW);
|
This->m_events.emplace(evtype::NEW);
|
||||||
break;
|
break;
|
||||||
@@ -313,6 +363,18 @@ void pulseaudio::sink_info_callback(pa_context*, const pa_sink_info* info, int e
|
|||||||
pa_threaded_mainloop_signal(This->m_mainloop, 0);
|
pa_threaded_mainloop_signal(This->m_mainloop, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback when getting source info & existence
|
||||||
|
*/
|
||||||
|
void pulseaudio::source_info_callback(pa_context *, const pa_source_info *info, int eol, void *userdata) {
|
||||||
|
pulseaudio *This = static_cast<pulseaudio *>(userdata);
|
||||||
|
if (!eol && info) {
|
||||||
|
This->m_index = info->index;
|
||||||
|
This->s_name = info->name;
|
||||||
|
}
|
||||||
|
pa_threaded_mainloop_signal(This->m_mainloop, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback when context state changes
|
* Callback when context state changes
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -25,12 +25,24 @@ namespace modules {
|
|||||||
m_interval = m_conf.get(name(), "interval", m_interval);
|
m_interval = m_conf.get(name(), "interval", m_interval);
|
||||||
m_unmute_on_scroll = m_conf.get(name(), "unmute-on-scroll", m_unmute_on_scroll);
|
m_unmute_on_scroll = m_conf.get(name(), "unmute-on-scroll", m_unmute_on_scroll);
|
||||||
|
|
||||||
auto sink_name = m_conf.get(name(), "sink", ""s);
|
// true => sink | false => source
|
||||||
|
// no entry in the config is the default sink
|
||||||
|
bool is_sink_or_source = true;
|
||||||
|
string pa_name = "";
|
||||||
|
|
||||||
|
if (m_conf.has(name(), "sink")) {
|
||||||
|
pa_name = m_conf.get(name(), "sink", ""s);
|
||||||
|
is_sink_or_source = true;
|
||||||
|
} else if (m_conf.has(name(), "source")) {
|
||||||
|
pa_name = m_conf.get(name(), "source", ""s);
|
||||||
|
is_sink_or_source = false;
|
||||||
|
}
|
||||||
|
|
||||||
bool m_max_volume = m_conf.get(name(), "use-ui-max", true);
|
bool m_max_volume = m_conf.get(name(), "use-ui-max", true);
|
||||||
m_reverse_scroll = m_conf.get(name(), "reverse-scroll", false);
|
m_reverse_scroll = m_conf.get(name(), "reverse-scroll", false);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
m_pulseaudio = std::make_unique<pulseaudio>(m_log, move(sink_name), m_max_volume);
|
m_pulseaudio = std::make_unique<pulseaudio>(m_log, move(pa_name), is_sink_or_source, m_max_volume);
|
||||||
} catch (const pulseaudio_error& err) {
|
} catch (const pulseaudio_error& err) {
|
||||||
throw module_error(err.what());
|
throw module_error(err.what());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user