PipeWire EQ with Smart Filters on Fedora/KDE (XPS 15 or any laptop)
Not sure if this has been documented elsewhere, but leaving it here in case it helps someone in the future.
The problem
I wanted a working EQ for my internal speakers on Fedora 43 / KDE Plasma, with PipeWire. The catch: I needed the hardware sink to stay as the system default, so the volume OSD and keyboard shortcuts stay in sync. But I also needed all audio to pass through the EQ automatically, without having to manually route apps.
Most guides I found (including the ones floating around GitHub Gists) use node.target in playback.props to route the EQ output to the hardware. This worked at some point, but it no longer works on PipeWire 1.x / WirePlumber 0.5+. The default node policy overrides it silently and you end up with audio bypassing the EQ, or the OSD getting out of sync.
The solution: Smart Filters
WirePlumber 0.5 introduced a feature called Smart Filters. When you mark a filter-chain node with filter.smart = true, WirePlumber automatically intercepts any stream going to the default sink and routes it through the EQ transparently. The hardware stays as the default for the system, but all audio passes through the filter first. No manual stream routing needed.
The fix is just two lines added to capture.props, and removing the old node.target from playback.props.
Setup
Create ~/.config/pipewire/pipewire.conf.d/sink-eq6.conf with this content (tune the EQ bands to your liking):
context.modules = [
{ name = libpipewire-module-filter-chain
args = {
node.description = "Internal Speakers Equalizer Sink"
media.name = "Internal Speakers Equalizer Sink"
filter.graph = {
nodes = [
{
type = builtin
name = eq_band_1
label = bq_peaking
control = { "Freq" = 119.0 "Q" = 1.5 "Gain" = 11.0 }
}
{
type = builtin
name = eq_band_2
label = bq_peaking
control = { "Freq" = 238.0 "Q" = 1.5 "Gain" = 2.0 }
}
{
type = builtin
name = eq_band_3
label = bq_peaking
control = { "Freq" = 475.0 "Q" = 1.5 "Gain" = -11.0 }
}
{
type = builtin
name = eq_band_4
label = bq_peaking
control = { "Freq" = 947.0 "Q" = 1.5 "Gain" = -11.0 }
}
{
type = builtin
name = eq_band_5
label = bq_peaking
control = { "Freq" = 1890.0 "Q" = 1.5 "Gain" = -2.0 }
}
{
type = builtin
name = eq_band_6
label = bq_peaking
control = { "Freq" = 3771.0 "Q" = 1.5 "Gain" = 2.0 }
}
{
type = builtin
name = eq_band_7
label = bq_peaking
control = { "Freq" = 7524.0 "Q" = 1.5 "Gain" = 9.0 }
}
{
type = builtin
name = eq_band_8
label = bq_peaking
control = { "Freq" = 15012.0 "Q" = 1.5 "Gain" = 10.0 }
}
]
links = [
{ output = "eq_band_1:Out" input = "eq_band_2:In" }
{ output = "eq_band_2:Out" input = "eq_band_3:In" }
{ output = "eq_band_3:Out" input = "eq_band_4:In" }
{ output = "eq_band_4:Out" input = "eq_band_5:In" }
{ output = "eq_band_5:Out" input = "eq_band_6:In" }
{ output = "eq_band_6:Out" input = "eq_band_7:In" }
{ output = "eq_band_7:Out" input = "eq_band_8:In" }
]
}
audio.channels = 2
audio.position = [ FL FR ]
capture.props = {
node.name = "internal_speaker"
media.class = Audio/Sink
filter.smart = true
filter.smart.target = { node.name = "alsa_output.pci-0000_00_1f.3-platform-skl_hda_dsp_generic.HiFi__Speaker__sink" }
}
playback.props = {
node.name = "internal_speaker_equalizer_output"
node.passive = true
}
}
}
]
No WirePlumber config files needed. Then restart the audio stack:
systemctl --user restart pipewire pipewire-pulse wireplumber
Verify it worked:
wpctl status
You should see your apps listed under the EQ filter, and the hardware speaker still marked as * (default).
Finding your hardware sink name
If your sink name is different, run wpctl status and look for your speaker under Sinks. Then run:
wpctl inspect
The node.name property is what you need for filter.smart.target.
Why the old guides don’t work
Older configs use node.target in playback.props. This property is deprecated in PipeWire 1.x and WirePlumber silently ignores it in most cases when the hardware is set as the default sink. filter.smart is the correct approach as of WirePlumber 0.5, and it’s documented in the official WirePlumber docs under [Smart Filters](Smart Filters — WirePlumber 0.5.13 documentation).
Tested on
- Dell XPS 15 9530
- Fedora 43
- KDE Plasma 6.6.2
- PipeWire 1.4.10
- WirePlumber 0.5.x