pacman -S pipewire wireplumber pipewire-audio pipewire-alsa pipewire-pulse
pacman -S lib32-pipewire lib32-libpulse
pacman -S alsa-firmware
mkdir -p ~/.config/wireplumber/wireplumber.conf.d
By default, PipeWire on Arch Linux may resample all audio to 48kHz, preventing proper hi-res audio playback (96kHz, 192kHz, 384kHz, DSD, etc.). This guide shows how to configure PipeWire and WirePlumber to enable bit-perfect, sample-rate-switching audio output.
cat /proc/asound/cardX/pcm0p/sub0/hw_params shows
rate: 48000 even when playing 96kHz contentpw-top shows correct sample rate, but hardware receives
48kHzFirst, check if you have an .asoundrc file that’s
forcing resampling:
cat ~/.asoundrcIf it exists and contains type plug or similar
resampling configurations, back it up and remove it:
mv ~/.asoundrc ~/.asoundrc.backupCreate or edit the PipeWire configuration file:
mkdir -p ~/.config/pipewire/pipewire.conf.d
vim ~/.config/pipewire/pipewire.conf.d/99-hires.confAdd the following content:
context.properties = {
default.clock.allowed-rates = [ 44100 48000 88200 96000 176400 192000 352800 384000 ]
}Note: Do NOT set default.clock.rate -
this allows PipeWire to switch rates dynamically.
ACP can interfere with sample rate switching. Create a WirePlumber config:
mkdir -p ~/.config/wireplumber/wireplumber.conf.d
vim ~/.config/wireplumber/wireplumber.conf.d/50-alsa-config.confAdd:
monitor.alsa.rules = [
{
matches = [
{ device.name = "~alsa_card.*" }
]
actions = {
update-props = {
api.alsa.use-acp = false
}
}
}
]Create a configuration for your specific audio device. Replace
QX13 with your device name (check with
aplay -l):
vim ~/.config/wireplumber/wireplumber.conf.d/51-device-format.confAdd (adjust device name pattern as needed):
monitor.alsa.rules = [
{
matches = [
{ node.name = "~alsa_output.*YOUR_DEVICE_NAME.*" }
]
actions = {
update-props = {
audio.format = "S32LE"
audio.rate = 0
audio.allowed-rates = "44100,48000,88200,96000,176400,192000,352800,384000"
node.max-latency = 0
api.alsa.period-size = 128
api.alsa.headroom = 128
}
}
}
]Key settings explained: -
audio.format = "S32LE": 32-bit signed little-endian
(highest quality) - audio.rate = 0: Passthrough mode (no
resampling) - node.max-latency = 0: Disables latency
constraints that can lock sample rates -
api.alsa.period-size = 128: Buffer size for low latency
systemctl --user restart wireplumber pipewire pipewire-pulseWhile playing a 96kHz FLAC file:
cat /proc/asound/card4/pcm0p/sub0/hw_paramsYou should see:
rate: 96000 (96000/1)
format: S32_LE
pw-topLook at the “RATE” column - it should show the actual source file’s sample rate.
Find your device:
wpctl status | grep -i "your_device_name"Inspect the node (replace ID with yours):
wpctl inspect <ID>Verify: - audio.rate = "0" (passthrough enabled) -
audio.allowed-rates includes your target rates -
api.alsa.use-acp = false (on the device card)
Test files of different sample rates to verify switching:
# Check file sample rate
ffprobe your_audio_file.flac 2>&1 | grep "Stream.*Audio"
# Play and verify hardware rate
cat /proc/asound/card4/pcm0p/sub0/hw_paramsYour DAC should display: - 44.1kHz for CD rips - 48kHz for some digital sources - 96kHz, 192kHz, etc. for hi-res files
Add to ~/.config/mpd/mpd.conf:
audio_output {
type "pipewire"
name "PipeWire Output"
}Check: 1. Device is not busy:
fuser -v /dev/snd/* 2. Configs are loaded:
pw-dump | grep "use-acp" 3. Node properties:
wpctl inspect <device_id> | grep rate
Solution:
# Force reload
systemctl --user restart wireplumber pipewire pipewire-pulse
# Clear metadata
pw-metadata -n settings 0 clock.force-rate 0Increase buffer sizes in
51-device-format.conf:
api.alsa.period-size = 256
api.alsa.headroom = 256Then restart services.
Verify file is actually hi-res:
ffprobe file.flac 2>&1 | grep AudioMany “hi-res” downloads are actually 44.1kHz upsampled.
If you see warnings about old Lua configs:
rm -rf ~/.config/wireplumber/main.lua.d/WirePlumber 0.5+ uses .conf files, not Lua.
If getting “Device busy” errors with direct ALSA:
# Stop PipeWire temporarily
systemctl --user stop wireplumber pipewire pipewire-pulse
# Test direct ALSA
aplay -D hw:CARD=YourCard,DEV=0 file.wav
# Restart
systemctl --user start wireplumber pipewire pipewire-pulseYou can configure different settings per application:
monitor.alsa.rules = [
{
matches = [
{ application.name = "strawberry" }
]
actions = {
update-props = {
node.latency = "256/96000"
resample.quality = 15
}
}
}
]aplay -lcat /proc/asound/card*/stream0Look for supported rates and formats.
lsusb -v | grep -A 20 "Your Device"Verify bcdUSB 2.00 or higher for hi-res support.
After configuration, you should have:
~/.config/pipewire/pipewire.conf.d/99-hires.conf
~/.config/wireplumber/wireplumber.conf.d/50-alsa-config.conf
~/.config/wireplumber/wireplumber.conf.d/51-device-format.conf
And removed or backed up:
~/.asoundrc (if it existed)
ixaxaar Configuration developed through troubleshooting a Fiio QX13 USB DAC on Arch Linux.
Using JACK system for recording in something like Audacity, will alway be at what the pipewire default sample rate is (most cases 48000hz). To work around this problem create a shell script and force samplerate to the dired value
#!/bin/bash
pw-metadata -n settings 0 clock.force-rate 96000
audacity
pw-metadata -n settings 0 clock.force-rate 0pw-cli info 0
monitor.alsa.rules = [
{
matches = [
{
device.name = "~alsa_card.*"
}
]
actions = {
update-props = {
api.alsa.use-acp = true,
api.acp.auto-profile = false
api.acp.auto-port = false
}
}
}
]monitor.alsa.rules = [
{
matches = [
{
# Matches all sources
node.name = "~alsa_input.*"
},
{
# Matches all sinks
node.name = "~alsa_output.*"
}
]
actions = {
update-props = {
session.suspend-timeout-seconds = 0
}
}
}
]
# bluetooth devices
monitor.bluez.rules = [
{
matches = [
{
# Matches all sources
node.name = "~bluez_input.*"
},
{
# Matches all sinks
node.name = "~bluez_output.*"
}
]
actions = {
update-props = {
session.suspend-timeout-seconds = 0
}
}
}
]setting device.profile-set to
analog-only.conf to disable S/PDIFF
monitor.alsa.rules = [
{
matches = [
{
device.name = "alsa_card.pci-0000_06_00.0"
}
]
actions = {
update-props = {
device.description = "SB-Z"
device.product.name = "SB-Z"
device.profile-set = "analog-only.conf"
}
}
}
]monitor.alsa.rules = [
{
matches = [
{
device.name = "alsa_card.usb-SteelSeries_Arctis_Nova_Pro_Wireless-00"
}
]
actions = {
update-props = {
device.description = "Arctis"
device.product.name = "Arctis"
}
}
}
]monitor.alsa.rules = [
{
matches = [
{
device.name = "alsa_card.pci-0000_0a_00.1"
}
]
actions = {
update-props = {
device.disabled = true,
}
}
}
]This seems like a fucking broken mess at the moment, could not find a proper solution to set this in wireplumber. For now just using a bash script to run at startup of DE.
#!/bin/bash
for i in $(pactl list cards short|cut -d$'\t' -f2); do
if [[ $i =~ alsa_card.pci ]]; then
pactl set-card-profile alsa_card.pci-0000_06_00.0 output:analog-surround-21
fi
if [[ $i =~ SteelSeries_Arctis_Nova_Pro_Wireless ]]; then
pactl set-card-profile alsa_card.usb-SteelSeries_Arctis_Nova_Pro_Wireless-00 output:iec958-stereo+input:mono-fallback
fi
doneLast modified: Tue Oct 6 10:13:47 2026