Hmm yes that looks promising. Having some trouble getting it to work at the moment though. I followed the example, finding the node.name for my headphones sink by using "wpctl status", which returned this:
PipeWire 'pipewire-0' [1.0.4, user@B650-Plus, cookie:3368478153]
└─ Clients:
28. FireDragon [1.0.4, user@B650-Plus, pid:2650]
32. xdg-desktop-portal-hyprland [1.0.4, user@B650-Plus, pid:2040]
33. WirePlumber [1.0.4, user@B650-Plus, pid:2047]
34. pipewire [1.0.4, user@B650-Plus, pid:2083]
35. waybar [1.0.4, user@B650-Plus, pid:1901]
36. Blueman [1.0.4, user@B650-Plus, pid:1856]
49. WirePlumber [export] [1.0.4, user@B650-Plus, pid:2047]
61. Blueman [1.0.4, user@B650-Plus, pid:2268]
63. WirePlumber [1.0.4, user@B650-Plus, pid:2047]
74. FireDragon [1.0.4, user@B650-Plus, pid:2698]
75. FireDragon [1.0.4, user@B650-Plus, pid:2482]
76. FireDragon [1.0.4, user@B650-Plus, pid:2776]
77. FireDragon [1.0.4, user@B650-Plus, pid:2779]
78. FireDragon [1.0.4, user@B650-Plus, pid:2891]
79. FireDragon [1.0.4, user@B650-Plus, pid:2894]
80. FireDragon [1.0.4, user@B650-Plus, pid:2897]
86. FireDragon [1.0.4, user@B650-Plus, pid:5415]
87. wpctl [1.0.4, user@B650-Plus, pid:6375]
88. FireDragon [1.0.4, user@B650-Plus, pid:3820]
89. FireDragon [1.0.4, user@B650-Plus, pid:5379]
90. FireDragon [1.0.4, user@B650-Plus, pid:5726]
91. FireDragon [1.0.4, user@B650-Plus, pid:5456]
Audio
├─ Devices:
│ 50. Navi 21/23 HDMI/DP Audio Controller [alsa]
│ 51. Family 17h/19h HD Audio Controller [alsa]
│ 62. HD 350BT [bluez5]
│
├─ Sinks:
│ 56. Family 17h/19h HD Audio Controller Analog Stereo [vol: 1.00]
│ * 66. HD 350BT [vol: 0.53]
│
├─ Sources:
│ 57. Family 17h/19h HD Audio Controller Analog Stereo [vol: 1.00]
│
├─ Filters:
│ - loopback-2047-17
│ * 64. bluez_input.80:C3:BA:39:3D:FF [Audio/Source]
│ 65. bluez_capture_internal.80:C3:BA:39:3D:FF [Stream/Input/Audio/Internal]
│
└─ Streams:
81. FireDragon
82. output_FL > HD 350BT:playback_FL [active]
83. output_FR > HD 350BT:playback_FR [active]
Video
├─ Devices:
│
├─ Sinks:
│
├─ Sources:
│
├─ Filters:
│
└─ Streams:
Settings
└─ Default Configured Devices:
0. Audio/Sink bluez_output.80_C3_BA_39_3D_FF.1
1. Audio/Source bluez_input.80:C3:BA:39:3D:FF
and then "wpctl inspect 66", which returned this:
id 66, type PipeWire:Interface:Node
api.bluez5.address = "80:C3:BA:39:3D:FF"
api.bluez5.codec = "aptx"
api.bluez5.profile = "a2dp-sink"
api.bluez5.transport = ""
audio.adapt.follower = ""
bluez5.loopback = "false"
card.profile.device = "1"
* client.id = "49"
clock.quantum-limit = "8192"
device.api = "bluez5"
* device.id = "62"
device.routes = "1"
* factory.id = "11"
factory.mode = "merge"
factory.name = "api.bluez5.a2dp.sink"
library.name = "audioconvert/libspa-audioconvert"
* media.class = "Audio/Sink"
media.name = "HD 350BT"
* node.description = "HD 350BT"
node.driver = "true"
* node.name = "bluez_output.80_C3_BA_39_3D_FF.1"
node.pause-on-idle = "false"
* object.serial = "72"
* priority.driver = "1010"
* priority.session = "1010"
So then I created ~/.config/wireplumber/wireplumber.conf.d/99-my-dsp.conf like this:
node.software-dsp.rules = [
{
matches = [
{ "node.name" = "bluez_output.80_C3_BA_39_3D_FF.1" }
## { "alsa.id" = "~WeirdHardware*" } # Wildcard match
]
actions = {
create-filter = {
filter-graph = {
"nodes": [
{
"type": "lv2",
"plugin": "http://lsp-plug.in/plugins/lv2/slap_delay_stereo",
"name": "slap_delay_stereo",
"control": {
"pred": "200",
"wet": "10",
"dm0": "1",
"dt0": "200,"
"dd0": "200",
"df0": "1",
},
"target.object": "bluez_output.80_C3_BA_39_3D_FF.1"
}.
{
"type": "lv2",
"plugin": "http://lsp-plug.in/plugins/lv2/noise_generator_x4",
"name": "noise_generator_x4",
"control": {
"na_1": "50",
},
"target.object": "bluez_output.80_C3_BA_39_3D_FF.1"
}
]
} # Virtual node goes here
hide-parent = true
}
}
}
]
And I put the
wireplumber.profiles = [
main = {
node.software-dsp = required
}
]
part into the relevant part of /usr/share/wireplumber/wireplumber.conf because for some reason with it in ~/.config/wireplumber/wireplumber.conf.d/99-my-dsp.conf wireplumber failed to start and gave a whole whack of errors in the logs. Kinda confusing, but I guess maybe the .conf.d entry of "main = " overwrote the whole "main = " from wireplumber.conf rather than just adding the additional node.software-dsp = required line, which makes sense I suppose. Could have copied the default settings from wireplumber.conf into the .conf.d file rather than the other way around. Probably will try that in a moment.
But anyways, no filters/effects are present in my audio as of now. I tried to activate the most obvious "yeah it's working" effects so it was easy to tell (hence the slap delay and noise generator plugins being used). At first I tried just the slap delay and that wasn't working on it's own so it's not a multiples issue.
One thing the example/tutorial wasn't clear on was this "Note" section:
Note
The target.object property of the virtual node should be configured statically to point to the node matched by the rule.
Because nowhere in the example is there anything like target.object, so I tried just adding that in myself but I'm not sure where it is supposed to go or what the syntax should be looking like. I've since tried putting it within different sections like this:
} # Virtual node goes here
hide-parent = true
"target.object" = "bluez_output.80_C3_BA_39_3D_FF.1"
}
"target.object" = "bluez_output.80_C3_BA_39_3D_FF.1"
}
"target.object" = "bluez_output.80_C3_BA_39_3D_FF.1"
}
"target.object" = "bluez_output.80_C3_BA_39_3D_FF.1"
]
And tried with "target.object": rather than "target.object" = , but not getting any effects on the audio.
Also, I'm a little confused by the final statement after the example "This will match any sinks with the UCM HiFi Speaker profile set that are associated with cards containing the string “WeirdHardware” at the start of their name." since this seems to put a lot of importance on the "alsa.id" value, but not the "node.name".
This would be an amazing feature to have a gui for (similar to EasyEffects, but just for setting .conf.d file contents).