Skip to content

Commit 9662234

Browse files
committed
services/pipewire: consider device volume step when sending updates
Previously a hardcoded 0.0001 offset was used to determine if a volume change was significant enough to send to a device, however some devices have a much more granular step size, which caused future volume updates to be blocked. This change replaces the hardcoded offset with the volumeStep device route property which should be large enough for the device to work with. Fixes #279
1 parent 475856b commit 9662234

File tree

3 files changed

+38
-22
lines changed

3 files changed

+38
-22
lines changed

changelog/next.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
## Bug Fixes
88

9+
- Fixed volumes getting stuck on change for pipewire devices with few volume steps.
910
- Fixed a crash when running out of disk space to write log files.
1011
- Fixed a rare crash when disconnecting a monitor.
1112
- Fixed build issues preventing cross compilation from working.

src/services/pipewire/node.cpp

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,8 @@ void PwNodeBoundAudio::updateVolumeProps(const PwVolumeProps& volumeProps) {
304304
return;
305305
}
306306

307+
this->volumeStep = volumeProps.volumeStep;
308+
307309
// It is important that the lengths of channels and volumes stay in sync whenever you read them.
308310
auto channelsChanged = false;
309311
auto volumesChanged = false;
@@ -435,31 +437,35 @@ void PwNodeBoundAudio::setVolumes(const QVector<float>& volumes) {
435437
<< "via device";
436438
this->waitingVolumes = realVolumes;
437439
} else {
438-
auto significantChange = this->mServerVolumes.isEmpty();
439-
for (auto i = 0; i < this->mServerVolumes.length(); i++) {
440-
auto serverVolume = this->mServerVolumes.value(i);
441-
auto targetVolume = realVolumes.value(i);
442-
if (targetVolume == 0 || abs(targetVolume - serverVolume) >= 0.0001) {
443-
significantChange = true;
444-
break;
440+
if (this->volumeStep != -1) {
441+
auto significantChange = this->mServerVolumes.isEmpty();
442+
for (auto i = 0; i < this->mServerVolumes.length(); i++) {
443+
auto serverVolume = this->mServerVolumes.value(i);
444+
auto targetVolume = realVolumes.value(i);
445+
if (targetVolume == 0 || abs(targetVolume - serverVolume) >= this->volumeStep) {
446+
significantChange = true;
447+
break;
448+
}
445449
}
446-
}
447450

448-
if (significantChange) {
449-
qCInfo(logNode) << "Changing volumes of" << this->node << "to" << realVolumes
450-
<< "via device";
451-
if (!this->node->device->setVolumes(this->node->routeDevice, realVolumes)) {
452-
return;
453-
}
451+
if (significantChange) {
452+
qCInfo(logNode) << "Changing volumes of" << this->node << "to" << realVolumes
453+
<< "via device";
454+
if (!this->node->device->setVolumes(this->node->routeDevice, realVolumes)) {
455+
return;
456+
}
454457

455-
this->mDeviceVolumes = realVolumes;
456-
this->node->device->waitForDevice();
457-
} else {
458-
// Insignificant changes won't cause an info event on the device, leaving qs hung in the
459-
// "waiting for acknowledgement" state forever.
460-
qCInfo(logNode) << "Ignoring volume change for" << this->node << "to" << realVolumes
461-
<< "from" << this->mServerVolumes
462-
<< "as it is a device node and the change is too small.";
458+
this->mDeviceVolumes = realVolumes;
459+
this->node->device->waitForDevice();
460+
} else {
461+
// Insignificant changes won't cause an info event on the device, leaving qs hung in the
462+
// "waiting for acknowledgement" state forever.
463+
qCInfo(logNode).nospace()
464+
<< "Ignoring volume change for " << this->node << " to " << realVolumes << " from "
465+
<< this->mServerVolumes
466+
<< " as it is a device node and the change is too small (min step: "
467+
<< this->volumeStep << ").";
468+
}
463469
}
464470
}
465471
} else {
@@ -519,6 +525,7 @@ PwVolumeProps PwVolumeProps::parseSpaPod(const spa_pod* param) {
519525
const auto* volumesProp = spa_pod_find_prop(param, nullptr, SPA_PROP_channelVolumes);
520526
const auto* channelsProp = spa_pod_find_prop(param, nullptr, SPA_PROP_channelMap);
521527
const auto* muteProp = spa_pod_find_prop(param, nullptr, SPA_PROP_mute);
528+
const auto* volumeStepProp = spa_pod_find_prop(param, nullptr, SPA_PROP_volumeStep);
522529

523530
const auto* volumes = reinterpret_cast<const spa_pod_array*>(&volumesProp->value);
524531
const auto* channels = reinterpret_cast<const spa_pod_array*>(&channelsProp->value);
@@ -537,6 +544,12 @@ PwVolumeProps PwVolumeProps::parseSpaPod(const spa_pod* param) {
537544

538545
spa_pod_get_bool(&muteProp->value, &props.mute);
539546

547+
if (volumeStepProp) {
548+
spa_pod_get_float(&volumeStepProp->value, &props.volumeStep);
549+
} else {
550+
props.volumeStep = -1;
551+
}
552+
540553
return props;
541554
}
542555

src/services/pipewire/node.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ struct PwVolumeProps {
158158
QVector<PwAudioChannel::Enum> channels;
159159
QVector<float> volumes;
160160
bool mute = false;
161+
float volumeStep = -1;
161162

162163
static PwVolumeProps parseSpaPod(const spa_pod* param);
163164
};
@@ -214,6 +215,7 @@ private slots:
214215
QVector<float> mServerVolumes;
215216
QVector<float> mDeviceVolumes;
216217
QVector<float> waitingVolumes;
218+
float volumeStep = -1;
217219
PwNode* node;
218220
};
219221

0 commit comments

Comments
 (0)