Skip to content

Commit a00ff03

Browse files
committed
services/pipewire: cache route device volumes to initialize nodes
Nodes referencing a device can be bound later than the device is bound. If this happens, the node will not receive an initial route device volume change event. This change caches the last known route device volume and initializes the device with it if present.
1 parent fc704e6 commit a00ff03

File tree

5 files changed

+28
-1
lines changed

5 files changed

+28
-1
lines changed

changelog/next.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ set shell id.
2424

2525
- Fixed volume control breaking with pipewire pro audio mode.
2626
- Fixed escape sequence handling in desktop entries.
27+
- Fixed volumes not initializing if a pipewire device was already loaded before its node.
2728

2829
## Packaging Changes
2930

src/services/pipewire/device.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,12 +125,22 @@ void PwDevice::addDeviceIndexPairs(const spa_pod* param) {
125125
// Insert into the main map as well, staging's purpose is to remove old entries.
126126
this->routeDeviceIndexes.insert(device, index);
127127

128+
// Used for initial node volume if the device is bound before the node
129+
// (e.g. multiple nodes pointing to the same device)
130+
this->routeDeviceVolumes.insert(device, volumeProps);
131+
128132
qCDebug(logDevice).nospace() << "Registered device/index pair for " << this
129133
<< ": [device: " << device << ", index: " << index << ']';
130134

131135
emit this->routeVolumesChanged(device, volumeProps);
132136
}
133137

138+
bool PwDevice::tryLoadVolumeProps(qint32 routeDevice, PwVolumeProps& volumeProps) {
139+
if (!this->routeDeviceVolumes.contains(routeDevice)) return false;
140+
volumeProps = this->routeDeviceVolumes.value(routeDevice);
141+
return true;
142+
}
143+
134144
void PwDevice::polled() {
135145
// It is far more likely that the list content has not come in yet than it having no entries,
136146
// and there isn't a way to check in the case that there *aren't* actually any entries.

src/services/pipewire/device.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ class PwDevice: public PwBindable<pw_device, PW_TYPE_INTERFACE_Device, PW_VERSIO
3232
void waitForDevice();
3333
[[nodiscard]] bool waitingForDevice() const;
3434

35+
[[nodiscard]] bool tryLoadVolumeProps(qint32 routeDevice, PwVolumeProps& volumeProps);
36+
3537
signals:
3638
void deviceReady();
3739
void routeVolumesChanged(qint32 routeDevice, const PwVolumeProps& volumeProps);
@@ -46,6 +48,7 @@ private slots:
4648
onParam(void* data, qint32 seq, quint32 id, quint32 index, quint32 next, const spa_pod* param);
4749

4850
QHash<qint32, qint32> routeDeviceIndexes;
51+
QHash<qint32, PwVolumeProps> routeDeviceVolumes;
4952
QList<qint32> stagingIndexes;
5053
void addDeviceIndexPairs(const spa_pod* param);
5154

src/services/pipewire/node.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ void PwNode::onInfo(void* data, const pw_node_info* info) {
218218
}
219219

220220
self->routeDevice = id;
221+
if (self->boundData) self->boundData->onDeviceChanged();
221222
} else {
222223
qCCritical(logNode) << self << "has attached device" << self->device
223224
<< "but no card.profile.device property.";
@@ -277,6 +278,15 @@ PwNodeBoundAudio::PwNodeBoundAudio(PwNode* node): QObject(node), node(node) {
277278
}
278279
}
279280

281+
void PwNodeBoundAudio::onDeviceChanged() {
282+
PwVolumeProps volumeProps;
283+
if (this->node->device->tryLoadVolumeProps(this->node->routeDevice, volumeProps)) {
284+
qCDebug(logNode) << "Initializing volume props for" << this->node
285+
<< "with known values from backing device.";
286+
this->updateVolumeProps(volumeProps);
287+
}
288+
}
289+
280290
void PwNodeBoundAudio::onInfo(const pw_node_info* info) {
281291
if ((info->change_mask & PW_NODE_CHANGE_MASK_PARAMS) != 0) {
282292
for (quint32 i = 0; i < info->n_params; i++) {
@@ -299,7 +309,8 @@ void PwNodeBoundAudio::onSpaParam(quint32 id, quint32 index, const spa_pod* para
299309
if (id == SPA_PARAM_Props && index == 0) {
300310
if (this->node->shouldUseDevice()) {
301311
qCDebug(logNode) << "Skipping node volume props update for" << this->node
302-
<< "in favor of device updates.";
312+
<< "in favor of device updates from routeDevice" << this->node->routeDevice
313+
<< "of" << this->node->device;
303314
return;
304315
}
305316

src/services/pipewire/node.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ class PwNodeBoundData {
169169
virtual ~PwNodeBoundData() = default;
170170
Q_DISABLE_COPY_MOVE(PwNodeBoundData);
171171

172+
virtual void onDeviceChanged() {};
172173
virtual void onInfo(const pw_node_info* /*info*/) {}
173174
virtual void onSpaParam(quint32 /*id*/, quint32 /*index*/, const spa_pod* /*param*/) {}
174175
virtual void onUnbind() {}
@@ -182,6 +183,7 @@ class PwNodeBoundAudio
182183
public:
183184
explicit PwNodeBoundAudio(PwNode* node);
184185

186+
void onDeviceChanged() override;
185187
void onInfo(const pw_node_info* info) override;
186188
void onSpaParam(quint32 id, quint32 index, const spa_pod* param) override;
187189
void onUnbind() override;

0 commit comments

Comments
 (0)