A Linux application for creating and managing persistent PipeWire audio routing rules for applications, inputs, outputs, and output groups.
Sound Rules aims to provide a simple graphical interface for controlling where application audio is routed without requiring users to understand PipeWire nodes, WirePlumber configuration, virtual sinks, or manual command-line routing.
Note
Sound Rules is currently in early development.
Planned functionality includes:
- Detect running applications and their audio streams
- Route application audio to specific output devices
- Route application input to specific microphones or input devices
- Remember per-application routing choices
- Automatically restore routing when applications start
- Automatically handle devices connecting and disconnecting
- Create persistent routing rules
- Create output groups for routing audio to multiple devices simultaneously
- Manage saved rules and output groups
- Configure fallback devices when a preferred device is unavailable
- Start and stop individual routing rules
- Persistent configuration across sessions
- PipeWire and WirePlumber integration
- Background routing independent of the graphical interface
- Command-line management
- Multiple frontend support
Sound Rules is designed around a frontend-independent core so that multiple graphical interfaces can share the same audio routing and rule implementation.
Planned frontends include:
A GTK 4 and Libadwaita frontend written in Vala, designed to integrate naturally with GNOME and follow the GNOME Human Interface Guidelines.
A Qt 6 frontend written in C++, intended to provide a native experience on KDE Plasma and other Qt-based desktop environments.
Both frontends communicate with the same Sound Rules service and use the same underlying device detection, application tracking, routing, rules, and configuration implementation.
Sound Rules separates audio routing and application logic from the graphical interfaces.
Sound Rules
├── Core — Vala
│ ├── Application tracking
│ ├── Audio device management
│ ├── PipeWire integration
│ ├── Routing
│ ├── Rule engine
│ ├── Output groups
│ ├── Configuration
│ └── Events
│
├── Service — Vala
│ └── D-Bus API
│
├── Frontends
│ ├── GTK 4 / Libadwaita — Vala
│ └── Qt 6 — C++
│
└── CLI — Vala
The core must remain independent from GTK, Qt, and other user-interface frameworks.
Frontend-specific concepts such as widgets, dialogs, windows, view models, and toolkit signals should remain entirely inside their respective frontend implementations.
The graphical frontends communicate with the Sound Rules service through D-Bus rather than implementing audio functionality themselves.
GTK / Vala ─┐
│
Qt / C++ ───┼── D-Bus ── soundrulesd ── Sound Rules Core ── PipeWire
│
CLI / Vala ─┘
This allows routing rules to continue working even when no graphical frontend is running.
The core is responsible for concepts such as:
Application
AudioStream
AudioDevice
Route
RouteRule
OutputGroup
RuleEngine
AudioBackend
Typical operations include:
list_applications()
list_outputs()
list_inputs()
list_rules()
set_application_output()
set_application_input()
create_rule()
update_rule()
remove_rule()
create_output_group()
update_output_group()
remove_output_group()
State changes are exposed through toolkit-independent events and the D-Bus service.
Examples include:
application_added
application_removed
device_added
device_removed
device_changed
rule_added
rule_removed
rule_changed
route_changed
output_group_changed
error
The GTK and Qt frontends react to these events using their own native toolkit mechanisms.
Sound Rules is built around the modern Linux audio stack:
- PipeWire — audio streams, devices, and routing
- WirePlumber — PipeWire session and policy management
- D-Bus — communication between the service, frontends, and CLI
- Vala — core, service, CLI, and GTK frontend
- GTK 4 / Libadwaita — GNOME frontend
- C++ / Qt 6 — KDE and Qt frontend
When an application creates an audio stream, Sound Rules identifies it and checks for a matching rule.
Application starts
↓
Audio stream appears
↓
Sound Rules detects it
↓
Rule engine finds a match
↓
Preferred output/input selected
↓
PipeWire routing applied
For example:
Firefox
└── Built-in Speakers
Discord
├── Output → USB Headset
└── Input → USB Microphone
Steam
└── USB Headset
The user does not need to manually reconnect streams or understand the underlying PipeWire graph.
Routing rules describe where an application's audio should go.
For example:
Firefox → Speakers
Discord → Headset
Spotify → HDMI Receiver
Input rules can also define which recording device an application should use:
Discord → USB Microphone
OBS → Audio Interface
Rules can be applied automatically whenever a matching application or audio stream appears.
A rule may eventually contain information such as:
Application
Preferred output
Preferred input
Fallback device
Output group
Priority
Enabled state
The exact matching and rule format may evolve as the project develops.
Sound Rules can support routing one application to multiple audio outputs simultaneously.
For example:
Desk
├── Desktop Speakers
└── USB Headset
An application can then be routed to the group:
Spotify → Desk
Internally, Sound Rules handles the required PipeWire routing or virtual audio devices.
The graphical interfaces expose this as a simple group of outputs rather than requiring users to manually create and connect PipeWire nodes.
Output groups can be persistent and automatically restored when their devices become available.
Audio hardware on desktop systems is not always permanently available.
Devices may appear or disappear when:
- USB headsets are connected
- Bluetooth devices connect
- HDMI displays are enabled
- Docking stations are attached
- Audio interfaces are powered on
Sound Rules should handle these changes without requiring users to recreate their configuration.
For example:
Discord → USB Headset
If the headset disappears:
USB Headset unavailable
↓
Fallback → Built-in Speakers
When the headset returns:
USB Headset connected
↓
Discord → USB Headset
The goal is for routing configuration to describe user intent rather than depend on the exact current state of the PipeWire graph.
Sound Rules uses a background user service so routing rules remain active independently of the graphical frontend.
GTK
│
├── D-Bus ──┐
│ │
Qt ▼
├────── soundrulesd
│ │
CLI ▼
Sound Rules Core
│
▼
PipeWire
Closing the GTK or Qt application should not disable existing audio rules.
For example:
Sound Rules GUI closed
Discord starts
↓
soundrulesd detects Discord
↓
Saved rule matches
↓
Discord → USB Headset
The service may use systemd user services and D-Bus activation where appropriate.
Sound Rules will also provide a small command-line interface for testing, scripting, and automation.
Example operations may include:
soundrulesctl applications
soundrulesctl outputs
soundrulesctl inputs
soundrulesctl rules
soundrulesctl groups
Routing commands may eventually resemble:
soundrulesctl route firefox speakers
soundrulesctl route discord headset
The CLI communicates with the same Sound Rules service as the graphical frontends.
It does not contain a separate routing implementation.
Sound Rules should remain:
- Simple
- Desktop-native
- PipeWire-native
- Toolkit-independent at its core
- Persistent
- Predictable
- Easy to understand
- Useful without requiring knowledge of PipeWire internals
- Focused specifically on application audio routing
The GTK frontend should follow GNOME conventions and Human Interface Guidelines.
The Qt frontend should integrate naturally with KDE Plasma and Qt-based environments.
Both frontends should expose the same core functionality while remaining free to use interaction patterns appropriate for their respective desktop environments.
Sound Rules is not intended to replace a complete audio mixer, patchbay, or professional audio workstation.
Complex functionality such as:
- Audio effects
- Equalizers
- DSP processing
- Recording
- Audio editing
- Professional studio routing
- Large visual PipeWire graphs
- Manual node-by-node patchbay management
is outside the primary scope of the project.
Sound Rules instead focuses on the common use case:
“I want this application's audio to always go here.”
And, when needed:
“I want this application's audio to go to these devices.”
The underlying PipeWire implementation may be complex, but the user-facing abstraction should remain simple.
The project is currently under development.
APIs, architecture, behaviour, UI design, rule formats, and implementation details may change significantly before the first stable release.
Contributions, bug reports, design feedback, and feature suggestions are welcome.
Before implementing major functionality, please open an issue so the proposed approach can be discussed first.
When contributing:
- Keep core logic independent from frontend frameworks
- Do not introduce GTK or Qt dependencies into the core
- Keep PipeWire and routing logic out of the graphical frontends
- Keep the GTK frontend written in Vala
- Keep the Qt frontend written in C++
- Communicate with the background service through the shared D-Bus API
- Keep routing behaviour consistent between all frontends
- Follow the conventions of the frontend being modified
- Prefer simple user-facing abstractions over exposing PipeWire internals
- Avoid expanding the application into a general-purpose audio workstation