pulseaudio: add target option to control source (microphone) volume
The pulseaudio/slider module had partial support for a "target" config
option but changeVolume() in the audio backend was hardcoded to only
modify sink volume. This adds full source volume control:
- Add AudioTarget enum (Sink/Source) to audio_backend
- Extend changeVolume() overloads with AudioTarget parameter
- Store and use pa_cvolume for source (with mono channel default)
- Add sourceVolumeModifyCb for source volume change confirmation
- Add "target" config option to the pulseaudio module for scroll control
- Document "target" in both pulseaudio and pulseaudio-slider man pages
Usage:
"pulseaudio/slider#in": { "target": "source" }
"pulseaudio#in": { "target": "source" }
This commit is contained in:
@@ -22,6 +22,7 @@ class Pulseaudio : public ALabel {
|
|||||||
const std::vector<std::string> getPulseIcon() const;
|
const std::vector<std::string> getPulseIcon() const;
|
||||||
|
|
||||||
std::shared_ptr<util::AudioBackend> backend = nullptr;
|
std::shared_ptr<util::AudioBackend> backend = nullptr;
|
||||||
|
util::AudioTarget target = util::AudioTarget::Sink;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules
|
} // namespace waybar::modules
|
||||||
|
|||||||
@@ -6,11 +6,6 @@
|
|||||||
#include "util/audio_backend.hpp"
|
#include "util/audio_backend.hpp"
|
||||||
namespace waybar::modules {
|
namespace waybar::modules {
|
||||||
|
|
||||||
enum class PulseaudioSliderTarget {
|
|
||||||
Sink,
|
|
||||||
Source,
|
|
||||||
};
|
|
||||||
|
|
||||||
class PulseaudioSlider : public ASlider {
|
class PulseaudioSlider : public ASlider {
|
||||||
public:
|
public:
|
||||||
PulseaudioSlider(const std::string&, const Json::Value&);
|
PulseaudioSlider(const std::string&, const Json::Value&);
|
||||||
@@ -21,7 +16,7 @@ class PulseaudioSlider : public ASlider {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<util::AudioBackend> backend = nullptr;
|
std::shared_ptr<util::AudioBackend> backend = nullptr;
|
||||||
PulseaudioSliderTarget target = PulseaudioSliderTarget::Sink;
|
util::AudioTarget target = util::AudioTarget::Sink;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules
|
} // namespace waybar::modules
|
||||||
@@ -14,6 +14,11 @@
|
|||||||
|
|
||||||
namespace waybar::util {
|
namespace waybar::util {
|
||||||
|
|
||||||
|
enum class AudioTarget {
|
||||||
|
Sink,
|
||||||
|
Source,
|
||||||
|
};
|
||||||
|
|
||||||
class AudioBackend {
|
class AudioBackend {
|
||||||
private:
|
private:
|
||||||
static void subscribeCb(pa_context*, pa_subscription_event_type_t, uint32_t, void*);
|
static void subscribeCb(pa_context*, pa_subscription_event_type_t, uint32_t, void*);
|
||||||
@@ -22,12 +27,14 @@ class AudioBackend {
|
|||||||
static void sourceInfoCb(pa_context*, const pa_source_info* i, int, void* data);
|
static void sourceInfoCb(pa_context*, const pa_source_info* i, int, void* data);
|
||||||
static void serverInfoCb(pa_context*, const pa_server_info*, void*);
|
static void serverInfoCb(pa_context*, const pa_server_info*, void*);
|
||||||
static void volumeModifyCb(pa_context*, int, void*);
|
static void volumeModifyCb(pa_context*, int, void*);
|
||||||
|
static void sourceVolumeModifyCb(pa_context*, int, void*);
|
||||||
void connectContext();
|
void connectContext();
|
||||||
|
|
||||||
pa_threaded_mainloop* mainloop_;
|
pa_threaded_mainloop* mainloop_;
|
||||||
pa_mainloop_api* mainloop_api_;
|
pa_mainloop_api* mainloop_api_;
|
||||||
pa_context* context_;
|
pa_context* context_;
|
||||||
pa_cvolume pa_volume_;
|
pa_cvolume pa_volume_;
|
||||||
|
pa_cvolume pa_source_volume_;
|
||||||
|
|
||||||
// SINK
|
// SINK
|
||||||
uint32_t sink_idx_{0};
|
uint32_t sink_idx_{0};
|
||||||
@@ -67,8 +74,10 @@ class AudioBackend {
|
|||||||
AudioBackend(std::function<void()> on_updated_cb, private_constructor_tag tag);
|
AudioBackend(std::function<void()> on_updated_cb, private_constructor_tag tag);
|
||||||
~AudioBackend();
|
~AudioBackend();
|
||||||
|
|
||||||
void changeVolume(uint16_t volume, uint16_t min_volume = 0, uint16_t max_volume = 100);
|
void changeVolume(uint16_t volume, uint16_t min_volume = 0, uint16_t max_volume = 100,
|
||||||
void changeVolume(ChangeType change_type, double step = 1, uint16_t max_volume = 100);
|
AudioTarget target = AudioTarget::Sink);
|
||||||
|
void changeVolume(ChangeType change_type, double step = 1, uint16_t max_volume = 100,
|
||||||
|
AudioTarget target = AudioTarget::Sink);
|
||||||
|
|
||||||
void setIgnoredSinks(const Json::Value& config);
|
void setIgnoredSinks(const Json::Value& config);
|
||||||
|
|
||||||
|
|||||||
@@ -32,16 +32,28 @@ The volume can be controlled by dragging the slider across the bar or clicking o
|
|||||||
default: false ++
|
default: false ++
|
||||||
Enables this module to consume all left over space dynamically.
|
Enables this module to consume all left over space dynamically.
|
||||||
|
|
||||||
|
*target*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: sink ++
|
||||||
|
The audio target to control. Can be either `sink` (output/speakers) or `source` (input/microphone).
|
||||||
|
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
|
||||||
```
|
```
|
||||||
"modules-right": [
|
"modules-right": [
|
||||||
"pulseaudio/slider",
|
"pulseaudio/slider#out",
|
||||||
|
"pulseaudio/slider#in",
|
||||||
],
|
],
|
||||||
"pulseaudio/slider": {
|
"pulseaudio/slider#out": {
|
||||||
"min": 0,
|
"min": 0,
|
||||||
"max": 100,
|
"max": 100,
|
||||||
"orientation": "horizontal"
|
"orientation": "horizontal"
|
||||||
|
},
|
||||||
|
"pulseaudio/slider#in": {
|
||||||
|
"min": 0,
|
||||||
|
"max": 100,
|
||||||
|
"orientation": "horizontal",
|
||||||
|
"target": "source"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -135,6 +135,11 @@ Additionally, you can control the volume by scrolling *up* or *down* while the c
|
|||||||
default: false ++
|
default: false ++
|
||||||
Enables this module to consume all left over space dynamically.
|
Enables this module to consume all left over space dynamically.
|
||||||
|
|
||||||
|
*target*: ++
|
||||||
|
typeof: string ++
|
||||||
|
default: sink ++
|
||||||
|
The audio target to control when scrolling. Can be either `sink` (output/speakers) or `source` (input/microphone).
|
||||||
|
|
||||||
# FORMAT REPLACEMENTS
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
*{desc}*: Pulseaudio port's description, for bluetooth it'll be the device name.
|
*{desc}*: Pulseaudio port's description, for bluetooth it'll be the device name.
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ waybar::modules::Pulseaudio::Pulseaudio(const std::string& id, const Json::Value
|
|||||||
|
|
||||||
backend = util::AudioBackend::getInstance([this] { this->dp.emit(); });
|
backend = util::AudioBackend::getInstance([this] { this->dp.emit(); });
|
||||||
backend->setIgnoredSinks(config_["ignored-sinks"]);
|
backend->setIgnoredSinks(config_["ignored-sinks"]);
|
||||||
|
|
||||||
|
if (config_["target"].isString() && config_["target"].asString() == "source") {
|
||||||
|
target = util::AudioTarget::Source;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll* e) {
|
bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll* e) {
|
||||||
@@ -33,7 +37,7 @@ bool waybar::modules::Pulseaudio::handleScroll(GdkEventScroll* e) {
|
|||||||
? util::ChangeType::Increase
|
? util::ChangeType::Increase
|
||||||
: util::ChangeType::Decrease;
|
: util::ChangeType::Decrease;
|
||||||
|
|
||||||
backend->changeVolume(change_type, step, max_volume);
|
backend->changeVolume(change_type, step, max_volume, target);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,16 +10,16 @@ PulseaudioSlider::PulseaudioSlider(const std::string& id, const Json::Value& con
|
|||||||
if (config_["target"].isString()) {
|
if (config_["target"].isString()) {
|
||||||
std::string target = config_["target"].asString();
|
std::string target = config_["target"].asString();
|
||||||
if (target == "sink") {
|
if (target == "sink") {
|
||||||
this->target = PulseaudioSliderTarget::Sink;
|
this->target = util::AudioTarget::Sink;
|
||||||
} else if (target == "source") {
|
} else if (target == "source") {
|
||||||
this->target = PulseaudioSliderTarget::Source;
|
this->target = util::AudioTarget::Source;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PulseaudioSlider::update() {
|
void PulseaudioSlider::update() {
|
||||||
switch (target) {
|
switch (target) {
|
||||||
case PulseaudioSliderTarget::Sink:
|
case util::AudioTarget::Sink:
|
||||||
if (backend->getSinkMuted()) {
|
if (backend->getSinkMuted()) {
|
||||||
scale_.set_value(min_);
|
scale_.set_value(min_);
|
||||||
} else {
|
} else {
|
||||||
@@ -27,7 +27,7 @@ void PulseaudioSlider::update() {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PulseaudioSliderTarget::Source:
|
case util::AudioTarget::Source:
|
||||||
if (backend->getSourceMuted()) {
|
if (backend->getSourceMuted()) {
|
||||||
scale_.set_value(min_);
|
scale_.set_value(min_);
|
||||||
} else {
|
} else {
|
||||||
@@ -41,13 +41,13 @@ void PulseaudioSlider::onValueChanged() {
|
|||||||
bool is_mute = false;
|
bool is_mute = false;
|
||||||
|
|
||||||
switch (target) {
|
switch (target) {
|
||||||
case PulseaudioSliderTarget::Sink:
|
case util::AudioTarget::Sink:
|
||||||
if (backend->getSinkMuted()) {
|
if (backend->getSinkMuted()) {
|
||||||
is_mute = true;
|
is_mute = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PulseaudioSliderTarget::Source:
|
case util::AudioTarget::Source:
|
||||||
if (backend->getSourceMuted()) {
|
if (backend->getSourceMuted()) {
|
||||||
is_mute = true;
|
is_mute = true;
|
||||||
}
|
}
|
||||||
@@ -65,18 +65,18 @@ void PulseaudioSlider::onValueChanged() {
|
|||||||
// If the sink/source is mute, but the user clicked the slider, unmute it!
|
// If the sink/source is mute, but the user clicked the slider, unmute it!
|
||||||
else {
|
else {
|
||||||
switch (target) {
|
switch (target) {
|
||||||
case PulseaudioSliderTarget::Sink:
|
case util::AudioTarget::Sink:
|
||||||
backend->toggleSinkMute(false);
|
backend->toggleSinkMute(false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PulseaudioSliderTarget::Source:
|
case util::AudioTarget::Source:
|
||||||
backend->toggleSourceMute(false);
|
backend->toggleSourceMute(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
backend->changeVolume(volume, min_, max_);
|
backend->changeVolume(volume, min_, max_, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace waybar::modules
|
} // namespace waybar::modules
|
||||||
+70
-23
@@ -24,8 +24,9 @@ AudioBackend::AudioBackend(std::function<void()> on_updated_cb, private_construc
|
|||||||
source_volume_(0),
|
source_volume_(0),
|
||||||
source_muted_(false),
|
source_muted_(false),
|
||||||
on_updated_cb_(std::move(on_updated_cb)) {
|
on_updated_cb_(std::move(on_updated_cb)) {
|
||||||
// Initialize pa_volume_ with safe defaults
|
// Initialize pa_volume_ and pa_source_volume_ with safe defaults
|
||||||
pa_cvolume_init(&pa_volume_);
|
pa_cvolume_init(&pa_volume_);
|
||||||
|
pa_cvolume_init(&pa_source_volume_);
|
||||||
mainloop_ = pa_threaded_mainloop_new();
|
mainloop_ = pa_threaded_mainloop_new();
|
||||||
if (mainloop_ == nullptr) {
|
if (mainloop_ == nullptr) {
|
||||||
throw std::runtime_error("pa_mainloop_new() failed.");
|
throw std::runtime_error("pa_mainloop_new() failed.");
|
||||||
@@ -155,6 +156,19 @@ void AudioBackend::volumeModifyCb(pa_context* c, int success, void* data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AudioBackend::sourceVolumeModifyCb(pa_context* c, int success, void* data) {
|
||||||
|
auto* backend = static_cast<AudioBackend*>(data);
|
||||||
|
if (success != 0) {
|
||||||
|
if ((backend->context_ != nullptr) &&
|
||||||
|
pa_context_get_state(backend->context_) == PA_CONTEXT_READY) {
|
||||||
|
pa_context_get_source_info_by_index(backend->context_, backend->source_idx_, sourceInfoCb,
|
||||||
|
data);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
spdlog::debug("Source volume modification failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Called when the requested sink information is ready.
|
* Called when the requested sink information is ready.
|
||||||
*/
|
*/
|
||||||
@@ -234,6 +248,11 @@ void AudioBackend::sourceInfoCb(pa_context* /*context*/, const pa_source_info* i
|
|||||||
void* data) {
|
void* data) {
|
||||||
auto* backend = static_cast<AudioBackend*>(data);
|
auto* backend = static_cast<AudioBackend*>(data);
|
||||||
if (i != nullptr && backend->default_source_name_ == i->name) {
|
if (i != nullptr && backend->default_source_name_ == i->name) {
|
||||||
|
if (pa_cvolume_valid(&i->volume) != 0) {
|
||||||
|
backend->pa_source_volume_ = i->volume;
|
||||||
|
} else {
|
||||||
|
pa_cvolume_init(&backend->pa_source_volume_);
|
||||||
|
}
|
||||||
auto source_volume = static_cast<float>(pa_cvolume_avg(&(i->volume))) / float{PA_VOLUME_NORM};
|
auto source_volume = static_cast<float>(pa_cvolume_avg(&(i->volume))) / float{PA_VOLUME_NORM};
|
||||||
backend->source_volume_ = std::round(source_volume * 100.0F);
|
backend->source_volume_ = std::round(source_volume * 100.0F);
|
||||||
backend->source_idx_ = i->index;
|
backend->source_idx_ = i->index;
|
||||||
@@ -259,25 +278,31 @@ void AudioBackend::serverInfoCb(pa_context* context, const pa_server_info* i, vo
|
|||||||
pa_context_get_source_info_list(context, sourceInfoCb, data);
|
pa_context_get_source_info_list(context, sourceInfoCb, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioBackend::changeVolume(uint16_t volume, uint16_t min_volume, uint16_t max_volume) {
|
void AudioBackend::changeVolume(uint16_t volume, uint16_t min_volume, uint16_t max_volume,
|
||||||
|
AudioTarget target) {
|
||||||
// Early return if context is not ready
|
// Early return if context is not ready
|
||||||
if ((context_ == nullptr) || pa_context_get_state(context_) != PA_CONTEXT_READY) {
|
if ((context_ == nullptr) || pa_context_get_state(context_) != PA_CONTEXT_READY) {
|
||||||
spdlog::error("PulseAudio context not ready");
|
spdlog::error("PulseAudio context not ready");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_source = target == AudioTarget::Source;
|
||||||
|
|
||||||
|
// Select the appropriate stored volume structure
|
||||||
|
auto& ref_volume = is_source ? pa_source_volume_ : pa_volume_;
|
||||||
|
|
||||||
// Prepare volume structure
|
// Prepare volume structure
|
||||||
pa_cvolume pa_volume;
|
pa_cvolume pa_volume;
|
||||||
|
|
||||||
pa_cvolume_init(&pa_volume);
|
pa_cvolume_init(&pa_volume);
|
||||||
|
|
||||||
// Use existing volume structure if valid, otherwise create a safe default
|
// Use existing volume structure if valid, otherwise create a safe default
|
||||||
if ((pa_cvolume_valid(&pa_volume_) != 0) && (pa_channels_valid(pa_volume_.channels) != 0)) {
|
if ((pa_cvolume_valid(&ref_volume) != 0) && (pa_channels_valid(ref_volume.channels) != 0)) {
|
||||||
pa_volume = pa_volume_;
|
pa_volume = ref_volume;
|
||||||
} else {
|
} else {
|
||||||
// Set stereo as a safe default
|
// Mono for sources (microphones), stereo for sinks
|
||||||
pa_volume.channels = 2;
|
pa_volume.channels = is_source ? 1 : 2;
|
||||||
spdlog::debug("Using default stereo volume structure");
|
spdlog::debug("Using default volume structure ({} channels)", pa_volume.channels);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the volume safely
|
// Set the volume safely
|
||||||
@@ -291,39 +316,56 @@ void AudioBackend::changeVolume(uint16_t volume, uint16_t min_volume, uint16_t m
|
|||||||
|
|
||||||
// Apply the volume change
|
// Apply the volume change
|
||||||
pa_threaded_mainloop_lock(mainloop_);
|
pa_threaded_mainloop_lock(mainloop_);
|
||||||
pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this);
|
if (is_source) {
|
||||||
|
pa_context_set_source_volume_by_index(context_, source_idx_, &pa_volume, sourceVolumeModifyCb,
|
||||||
|
this);
|
||||||
|
} else {
|
||||||
|
pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this);
|
||||||
|
}
|
||||||
pa_threaded_mainloop_unlock(mainloop_);
|
pa_threaded_mainloop_unlock(mainloop_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioBackend::changeVolume(ChangeType change_type, double step, uint16_t max_volume) {
|
void AudioBackend::changeVolume(ChangeType change_type, double step, uint16_t max_volume,
|
||||||
|
AudioTarget target) {
|
||||||
// Early return if context is not ready
|
// Early return if context is not ready
|
||||||
if ((context_ == nullptr) || pa_context_get_state(context_) != PA_CONTEXT_READY) {
|
if ((context_ == nullptr) || pa_context_get_state(context_) != PA_CONTEXT_READY) {
|
||||||
spdlog::error("PulseAudio context not ready");
|
spdlog::error("PulseAudio context not ready");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_source = target == AudioTarget::Source;
|
||||||
|
|
||||||
|
// Select the appropriate stored volume structure and current volume
|
||||||
|
auto& ref_volume = is_source ? pa_source_volume_ : pa_volume_;
|
||||||
|
auto current_volume = is_source ? source_volume_ : volume_;
|
||||||
|
|
||||||
// Prepare volume structure
|
// Prepare volume structure
|
||||||
pa_cvolume pa_volume;
|
pa_cvolume pa_volume;
|
||||||
pa_cvolume_init(&pa_volume);
|
pa_cvolume_init(&pa_volume);
|
||||||
|
|
||||||
// Use existing volume structure if valid, otherwise create a safe default
|
// Use existing volume structure if valid, otherwise create a safe default
|
||||||
if ((pa_cvolume_valid(&pa_volume_) != 0) && (pa_channels_valid(pa_volume_.channels) != 0)) {
|
if ((pa_cvolume_valid(&ref_volume) != 0) && (pa_channels_valid(ref_volume.channels) != 0)) {
|
||||||
pa_volume = pa_volume_;
|
pa_volume = ref_volume;
|
||||||
} else {
|
} else {
|
||||||
// Set stereo as a safe default
|
// Mono for sources (microphones), stereo for sinks
|
||||||
pa_volume.channels = 2;
|
pa_volume.channels = is_source ? 1 : 2;
|
||||||
spdlog::debug("Using default stereo volume structure");
|
spdlog::debug("Using default volume structure ({} channels)", pa_volume.channels);
|
||||||
|
|
||||||
// Initialize all channels to current volume level
|
// Initialize all channels to current volume level
|
||||||
double volume_tick = static_cast<double>(PA_VOLUME_NORM) / 100;
|
double volume_tick = static_cast<double>(PA_VOLUME_NORM) / 100;
|
||||||
pa_volume_t vol = volume_ * volume_tick;
|
pa_volume_t vol = current_volume * volume_tick;
|
||||||
for (uint8_t i = 0; i < pa_volume.channels; i++) {
|
for (uint8_t i = 0; i < pa_volume.channels; i++) {
|
||||||
pa_volume.values[i] = vol;
|
pa_volume.values[i] = vol;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No need to continue with volume change if we had to create a new structure
|
// No need to continue with volume change if we had to create a new structure
|
||||||
pa_threaded_mainloop_lock(mainloop_);
|
pa_threaded_mainloop_lock(mainloop_);
|
||||||
pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this);
|
if (is_source) {
|
||||||
|
pa_context_set_source_volume_by_index(context_, source_idx_, &pa_volume,
|
||||||
|
sourceVolumeModifyCb, this);
|
||||||
|
} else {
|
||||||
|
pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this);
|
||||||
|
}
|
||||||
pa_threaded_mainloop_unlock(mainloop_);
|
pa_threaded_mainloop_unlock(mainloop_);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -333,10 +375,10 @@ void AudioBackend::changeVolume(ChangeType change_type, double step, uint16_t ma
|
|||||||
pa_volume_t change;
|
pa_volume_t change;
|
||||||
max_volume = std::min(max_volume, static_cast<uint16_t>(PA_VOLUME_UI_MAX));
|
max_volume = std::min(max_volume, static_cast<uint16_t>(PA_VOLUME_UI_MAX));
|
||||||
|
|
||||||
if (change_type == ChangeType::Increase && volume_ < max_volume) {
|
if (change_type == ChangeType::Increase && current_volume < max_volume) {
|
||||||
// Calculate how much to increase
|
// Calculate how much to increase
|
||||||
if (volume_ + step > max_volume) {
|
if (current_volume + step > max_volume) {
|
||||||
change = round((max_volume - volume_) * volume_tick);
|
change = round((max_volume - current_volume) * volume_tick);
|
||||||
} else {
|
} else {
|
||||||
change = round(step * volume_tick);
|
change = round(step * volume_tick);
|
||||||
}
|
}
|
||||||
@@ -345,10 +387,10 @@ void AudioBackend::changeVolume(ChangeType change_type, double step, uint16_t ma
|
|||||||
for (uint8_t i = 0; i < pa_volume.channels; i++) {
|
for (uint8_t i = 0; i < pa_volume.channels; i++) {
|
||||||
pa_volume.values[i] = std::min(pa_volume.values[i] + change, PA_VOLUME_MAX);
|
pa_volume.values[i] = std::min(pa_volume.values[i] + change, PA_VOLUME_MAX);
|
||||||
}
|
}
|
||||||
} else if (change_type == ChangeType::Decrease && volume_ > 0) {
|
} else if (change_type == ChangeType::Decrease && current_volume > 0) {
|
||||||
// Calculate how much to decrease
|
// Calculate how much to decrease
|
||||||
if (volume_ - step < 0) {
|
if (current_volume - step < 0) {
|
||||||
change = round(volume_ * volume_tick);
|
change = round(current_volume * volume_tick);
|
||||||
} else {
|
} else {
|
||||||
change = round(step * volume_tick);
|
change = round(step * volume_tick);
|
||||||
}
|
}
|
||||||
@@ -364,7 +406,12 @@ void AudioBackend::changeVolume(ChangeType change_type, double step, uint16_t ma
|
|||||||
|
|
||||||
// Apply the volume change
|
// Apply the volume change
|
||||||
pa_threaded_mainloop_lock(mainloop_);
|
pa_threaded_mainloop_lock(mainloop_);
|
||||||
pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this);
|
if (is_source) {
|
||||||
|
pa_context_set_source_volume_by_index(context_, source_idx_, &pa_volume, sourceVolumeModifyCb,
|
||||||
|
this);
|
||||||
|
} else {
|
||||||
|
pa_context_set_sink_volume_by_index(context_, sink_idx_, &pa_volume, volumeModifyCb, this);
|
||||||
|
}
|
||||||
pa_threaded_mainloop_unlock(mainloop_);
|
pa_threaded_mainloop_unlock(mainloop_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user