diff --git a/changelog/next.md b/changelog/next.md index 583a2f4..66f87c1 100644 --- a/changelog/next.md +++ b/changelog/next.md @@ -36,6 +36,7 @@ set shell id. - Fixed volume control breaking with pipewire pro audio mode. - Fixed volume control breaking with bluez streams and potentially others. +- Fixed volume control breaking for devices without route definitions. - Fixed escape sequence handling in desktop entries. - Fixed volumes not initializing if a pipewire device was already loaded before its node. - Fixed hyprland active toplevel not resetting after window closes. diff --git a/src/services/pipewire/device.cpp b/src/services/pipewire/device.cpp index e3bc967..61079a1 100644 --- a/src/services/pipewire/device.cpp +++ b/src/services/pipewire/device.cpp @@ -141,6 +141,10 @@ bool PwDevice::tryLoadVolumeProps(qint32 routeDevice, PwVolumeProps& volumeProps return true; } +bool PwDevice::hasRouteDevice(qint32 routeDevice) const { + return this->routeDeviceIndexes.contains(routeDevice); +} + void PwDevice::polled() { // It is far more likely that the list content has not come in yet than it having no entries, // and there isn't a way to check in the case that there *aren't* actually any entries. diff --git a/src/services/pipewire/device.hpp b/src/services/pipewire/device.hpp index 22af699..cd61709 100644 --- a/src/services/pipewire/device.hpp +++ b/src/services/pipewire/device.hpp @@ -12,13 +12,15 @@ #include #include "core.hpp" -#include "node.hpp" #include "registry.hpp" namespace qs::service::pipewire { class PwDevice; +// Forward declare to avoid circular dependency with node.hpp +struct PwVolumeProps; + class PwDevice: public PwBindable { Q_OBJECT; @@ -33,6 +35,7 @@ public: [[nodiscard]] bool waitingForDevice() const; [[nodiscard]] bool tryLoadVolumeProps(qint32 routeDevice, PwVolumeProps& volumeProps); + [[nodiscard]] bool hasRouteDevice(qint32 routeDevice) const; signals: void deviceReady(); diff --git a/src/services/pipewire/node.cpp b/src/services/pipewire/node.cpp index b6f0529..075a7ec 100644 --- a/src/services/pipewire/node.cpp +++ b/src/services/pipewire/node.cpp @@ -429,6 +429,10 @@ void PwNodeBoundAudio::setMuted(bool muted) { } float PwNodeBoundAudio::averageVolume() const { + if (this->mVolumes.isEmpty()) { + return 0.0f; + } + float total = 0; for (auto volume: this->mVolumes) { @@ -572,22 +576,28 @@ PwVolumeProps PwVolumeProps::parseSpaPod(const spa_pod* param) { const auto* muteProp = spa_pod_find_prop(param, nullptr, SPA_PROP_mute); const auto* volumeStepProp = spa_pod_find_prop(param, nullptr, SPA_PROP_volumeStep); - const auto* volumes = reinterpret_cast(&volumesProp->value); - const auto* channels = reinterpret_cast(&channelsProp->value); - - spa_pod* iter = nullptr; - SPA_POD_ARRAY_FOREACH(volumes, iter) { - // Cubing behavior found in MPD source, and appears to corrospond to everyone else's measurements correctly. - auto linear = *reinterpret_cast(iter); - auto visual = std::cbrt(linear); - props.volumes.push_back(visual); + if (volumesProp) { + const auto* volumes = reinterpret_cast(&volumesProp->value); + spa_pod* iter = nullptr; + SPA_POD_ARRAY_FOREACH(volumes, iter) { + // Cubing behavior found in MPD source, and appears to corrospond to everyone else's measurements correctly. + auto linear = *reinterpret_cast(iter); + auto visual = std::cbrt(linear); + props.volumes.push_back(visual); + } } - SPA_POD_ARRAY_FOREACH(channels, iter) { - props.channels.push_back(*reinterpret_cast(iter)); + if (channelsProp) { + const auto* channels = reinterpret_cast(&channelsProp->value); + spa_pod* iter = nullptr; + SPA_POD_ARRAY_FOREACH(channels, iter) { + props.channels.push_back(*reinterpret_cast(iter)); + } } - spa_pod_get_bool(&muteProp->value, &props.mute); + if (muteProp) { + spa_pod_get_bool(&muteProp->value, &props.mute); + } if (volumeStepProp) { spa_pod_get_float(&volumeStepProp->value, &props.volumeStep); diff --git a/src/services/pipewire/node.hpp b/src/services/pipewire/node.hpp index fdec72d..efc819c 100644 --- a/src/services/pipewire/node.hpp +++ b/src/services/pipewire/node.hpp @@ -15,6 +15,7 @@ #include #include "core.hpp" +#include "device.hpp" #include "registry.hpp" namespace qs::service::pipewire { @@ -249,7 +250,9 @@ public: bool proAudio = false; [[nodiscard]] bool shouldUseDevice() const { - return this->device && !this->proAudio && this->routeDevice != -1; + if (!this->device || this->proAudio || this->routeDevice == -1) return false; + // Only use device control if the device actually has route indexes for this routeDevice + return this->device->hasRouteDevice(this->routeDevice); } signals: