-
Notifications
You must be signed in to change notification settings - Fork 53
Description
My audio sink is a SteelSeries Arctis Pro Wireless. This sink appear to be special. Only "very" significant volume changes are applied and result in an update from pipewire.
As a result, small volume changes make the volume stuck as quickshell waits for an update:
// Successful volume change (but the resulting volume is quite different from what we've asked for)
INFO quickshell.service.pipewire.node: Changing volumes of PwNode(0x7f2641b01280, id=64/bound) to QList(0.34, 0.34) via device
INFO quickshell.service.pipewire.device: Changing volumes of PwDevice(0x7f2641b185c0, id=62/bound) on route device 1 to QList(0.34, 0.34)
INFO quickshell.service.pipewire.node: Got updated volumes of PwNode(0x7f2641b01280, id=64/bound) - QList(0.3125, 0.3125)
// Another successful one: We ask for 0.35 and get 0.375
INFO quickshell.service.pipewire.node: Changing volumes of PwNode(0x7f2641b01280, id=64/bound) to QList(0.35, 0.35) via device
INFO quickshell.service.pipewire.device: Changing volumes of PwDevice(0x7f2641b185c0, id=62/bound) on route device 1 to QList(0.35, 0.35)
INFO quickshell.service.pipewire.node: Got updated volumes of PwNode(0x7f2641b01280, id=64/bound) - QList(0.375, 0.375)
// Now we ask for 0.36, which most likely also translates to 0.375. So we see no actual change and no update.
INFO quickshell.service.pipewire.node: Changing volumes of PwNode(0x7f2641b01280, id=64/bound) to QList(0.36, 0.36) via device
INFO quickshell.service.pipewire.device: Changing volumes of PwDevice(0x7f2641b185c0, id=62/bound) on route device 1 to QList(0.36, 0.36)
// We'll wait forever before issuing another change.
INFO quickshell.service.pipewire.node: Waiting to change volumes of PwNode(0x7f2641b01280, id=64/bound) to QList(0.77, 0.77) via device
I've seen this logic here:
quickshell/src/services/pipewire/node.cpp
Lines 438 to 463 in 482744c
auto significantChange = this->mServerVolumes.isEmpty(); | |
for (auto i = 0; i < this->mServerVolumes.length(); i++) { | |
auto serverVolume = this->mServerVolumes.value(i); | |
auto targetVolume = realVolumes.value(i); | |
if (targetVolume == 0 || abs(targetVolume - serverVolume) >= 0.0001) { | |
significantChange = true; | |
break; | |
} | |
} | |
if (significantChange) { | |
qCInfo(logNode) << "Changing volumes of" << this->node << "to" << realVolumes | |
<< "via device"; | |
if (!this->node->device->setVolumes(this->node->routeDevice, realVolumes)) { | |
return; | |
} | |
this->mDeviceVolumes = realVolumes; | |
this->node->device->waitForDevice(); | |
} else { | |
// Insignificant changes won't cause an info event on the device, leaving qs hung in the | |
// "waiting for acknowledgement" state forever. | |
qCInfo(logNode) << "Ignoring volume change for" << this->node << "to" << realVolumes | |
<< "from" << this->mServerVolumes | |
<< "as it is a device node and the change is too small."; | |
} |
I believe it's the wrong approach for my device. The threshold would have to be set to something like 0.02
or 0.03
, which feels too large (?).
Perhaps the right thing to do, which hopefully works for all devices, is to use time-based debouncing? That could be easily implemented by changing the implementation of waitForDevice()
and waitingForDevice()
.
Or, simpler, is it really necessary to have some kind of rate limiting? I've skimmed the source code of pavucontrol, and it doesn't seem to do anything like this.
Further information
quickshell works for other sinks on my system. I don't think there's any special property that could tell quickshell that the threshold should be high here. pw-cli
tells me this:
id: 64
permissions: rwxm-
type: PipeWire:Interface:Node/3
* input ports: 2/65
* output ports: 2/0
* state: "suspended"
* properties:
* alsa.card = "1"
* alsa.card_name = "Arctis Pro Wireless"
* alsa.class = "generic"
* alsa.components = "USB1038:1294"
* alsa.device = "1"
* alsa.driver_name = "snd_usb_audio"
* alsa.id = "USB Audio"
* alsa.long_card_name = "SteelSeries Arctis Pro Wireless at usb-0000:00:14.0-5.1.1.2.3, full speed"
* alsa.mixer_name = "USB Mixer"
* alsa.name = "USB Audio #1"
* alsa.resolution_bits = "16"
* alsa.subclass = "generic-mix"
* alsa.subdevice = "0"
* alsa.subdevice_name = "subdevice #0"
* alsa.sync.id = "00000000:00000000:00000000:00000000"
* api.alsa.card.longname = "SteelSeries Arctis Pro Wireless at usb-0000:00:14.0-5.1.1.2.3, full speed"
* api.alsa.card.name = "Arctis Pro Wireless"
* api.alsa.path = "hw:1,1,0"
* api.alsa.pcm.card = "1"
* api.alsa.pcm.stream = "playback"
* audio.channels = "2"
* audio.position = "FL,FR"
* card.profile.device = "1"
* device.api = "alsa"
* device.bus = "usb"
* device.class = "sound"
* device.icon-name = "audio-card-analog"
* device.id = "62"
* device.profile.description = "Game"
* device.profile.name = "stereo-game"
* device.routes = "1"
* factory.name = "api.alsa.pcm.sink"
* media.class = "Audio/Sink"
* node.description = "Arctis Pro Wireless Game"
* node.name = "alsa_output.usb-SteelSeries_Arctis_Pro_Wireless-00.stereo-game"
* node.nick = "USB Audio #1"
* node.pause-on-idle = "false"
* object.path = "alsa:acp:Wireless:1:playback"
* port.group = "playback"
* priority.driver = "828"
* priority.session = "828"
* factory.id = "19"
* clock.quantum-limit = "8192"
* client.id = "41"
* node.driver = "true"
* node.loop.name = "data-loop.0"
* library.name = "audioconvert/libspa-audioconvert"
* object.id = "64"
* object.serial = "64"
* params: (9)
* 3 (Spa:Enum:ParamId:EnumFormat) r-
* 1 (Spa:Enum:ParamId:PropInfo) r-
* 2 (Spa:Enum:ParamId:Props) rw
* 4 (Spa:Enum:ParamId:Format) -w
* 10 (Spa:Enum:ParamId:EnumPortConfig) r-
* 11 (Spa:Enum:ParamId:PortConfig) rw
* 15 (Spa:Enum:ParamId:Latency) rw
* 16 (Spa:Enum:ParamId:ProcessLatency) rw
* 17 (Spa:Enum:ParamId:Tag) rw
I use Dank Material Shell.