fix(wireplumber): show correct volume on Bluetooth sinks

The mixer-api is configured with the linear scale (0), so volume_ holds
the raw linear gain. The perceptual "cubic" value shown by wpctl and
exposed as {volume} is cbrt(linear), but update() computed pow(volume_, 3)
instead. Cubing under-reads every volume below max and collapses small
linear gains to 0% -- which is why the default Bluetooth sink (whose
normal levels map to low linear gains, e.g. wpctl 0.55 -> linear 0.166)
displayed 0% while wpctl reported a normal, unmuted volume.

Replace the inverted conversions with the correct cube-root/cube pair in
the display path, the scroll-scale conversions (cubic / cubic_percent),
and the max-volume ceiling mapping so scrolling and the cap stay
consistent with the corrected {volume}.

Fixes #5159.
This commit is contained in:
Alex
2026-07-04 08:57:15 +02:00
parent ea33ceb055
commit 69b9a14b96
+12 -9
View File
@@ -547,8 +547,11 @@ auto waybar::modules::Wireplumber::update() -> void {
label_.get_style_context()->remove_class("source-muted");
}
double vol_cube = pow(volume_, 3);
double source_vol_cube = pow(source_volume_, 3);
// mixer-api is configured with the linear scale, so volume_ holds the raw linear gain. The
// perceptual "cubic" value shown by wpctl and used for {volume} is cbrt(linear), not linear^3.
// Cubing here drives small linear gains (typical for Bluetooth sinks) to 0%.
double vol_cube = cbrt(volume_);
double source_vol_cube = cbrt(source_volume_);
int vol = round(vol_cube * 100.0);
int source_vol = round(source_vol_cube * 100.0);
@@ -622,10 +625,10 @@ bool waybar::modules::Wireplumber::handleScroll(GdkEventScroll* e) {
step = config_["scroll-step"].asDouble();
}
if (config_["max-volume"].isDouble()) {
// {volume} is displayed as cubic-percent (pow(volume_, 3) * 100), while volume_/newVol are
// {volume} is displayed as cubic-percent (cbrt(volume_) * 100), while volume_/newVol are
// linear gains. Map the documented cubic-percent ceiling into the linear domain the clamp
// operates in, restoring the 0.15.0 cap semantics (e.g. 130 -> cbrt(1.3) linear -> 130%).
maxVolume = cbrt(config_["max-volume"].asDouble() / 100.0);
// operates in, restoring the 0.15.0 cap semantics (e.g. 130 -> pow(1.3, 3) linear -> 130%).
maxVolume = pow(config_["max-volume"].asDouble() / 100.0, 3);
}
double vol = volume_;
@@ -635,11 +638,11 @@ bool waybar::modules::Wireplumber::handleScroll(GdkEventScroll* e) {
}
if (scale == "cubic") {
vol = pow(vol, 3);
vol = cbrt(vol);
} else if (scale == "db") {
vol = log10(vol) * 20.0;
} else if (scale == "cubic_percent") {
vol = pow(vol, 3) * 100.0;
vol = cbrt(vol) * 100.0;
}
double newVol = vol;
@@ -650,11 +653,11 @@ bool waybar::modules::Wireplumber::handleScroll(GdkEventScroll* e) {
}
if (scale == "cubic") {
newVol = cbrt(newVol);
newVol = pow(newVol, 3);
} else if (scale == "db") {
newVol = exp10(newVol / 20.0);
} else if (scale == "cubic_percent") {
newVol = cbrt(newVol / 100.0);
newVol = pow(newVol / 100.0, 3);
}
if (dir == SCROLL_DIR::UP) {