# Omarchy Audio Pipeline: Output Switching and Volume Control Architecture

> Discover how the Omarchy audio pipeline manages output switching and volume control using PipeWire and WirePlumber. Learn to switch audio outputs seamlessly.

- Repository: [Omacom/omarchy](https://github.com/omacom/omarchy)
- Tags: architecture
- Published: 2026-09-10

---

**Omarchy’s audio pipeline uses PipeWire and WirePlumber to maintain a single source of truth for the current output sink, resolving physical audio devices through the `omarchy-audio-output-sink` helper while using `omarchy-audio-output-set-default` to switch outputs and move only application streams, ensuring speaker tunings remain intact.**

Omarchy represents every audio entity—sources, sinks, and streams—as nodes in a unified graph and centralizes control through a JavaScript model and specialized shell scripts. This architecture ensures volume changes always target the physical sink even when DSP front-ends like EasyEffects or speaker tunings are active. The implementation distinguishes between raw PipeWire sinks, virtual tuning front-ends, and playback streams to prevent audio loops and double-volume issues.

## The Node Model Architecture

The pipeline’s state management lives in [`shell/plugins/panels/audio/Model.js`](https://github.com/omacom/omarchy/blob/main/shell/plugins/panels/audio/Model.js), which enumerates three live lists that represent the entire audio graph:

- **`audioSinks`** – Physical and virtual output devices (speakers, headphones, HDMI, and tuning sinks).
- **`audioSources`** – Capture devices including microphones and line inputs.
- **`audioSources`** – Playback streams that carry metadata such as `application.name`.

The model maintains these lists through a `listSnapshot` utility and exposes derived properties at **lines 146‑149** including `outputVolume`, `outputMuted`, `hasOutput`, and `hasInput`. These properties provide the single source of truth that the OSD, the QML panel, and volume commands all consume. The model also implements `outputVolumeName(volume, muted)` at **lines 25‑35** to generate human-readable volume labels for the UI.

## Resolving the Physical Output Sink

Volume-related UI elements do not interact with raw sink objects directly. Instead, they invoke the **`omarchy-audio-output-sink`** helper script (referenced from `shell/plugins/panels/audio/Panel.qml`), which traverses the PipeWire graph to resolve any DSP front-ends—such as tuning sinks or EasyEffects—and returns the physical sink that actually drives the hardware.

This resolution step is critical because it allows the UI to remain consistent regardless of whether a tuning sink is active or headphones have been selected. By always targeting the resolved physical sink, the system avoids the "double-volume" problem where users might accidentally adjust both a virtual DSP sink and the underlying hardware sink simultaneously.

## Switching Default Audio Outputs

User-facing output switching flows through two specialized commands that handle the complexity of moving streams without breaking audio tunings.

**`omarchy-audio-output-switch`** cycles through the list of usable sinks (excluding hidden tuning sinks) and delegates to **`omarchy-audio-output-set-default`**, located at `bin/omarchy-audio-output-set-default`. This script performs three distinct operations:

1. **Receives a PipeWire node ID and a friendly sink name** as parameters.
2. **Executes `pw-cli set-default <node-id>`** to update PipeWire’s default target.
3. **Moves only streams that carry an `application.name` property** onto the new default sink, leaving system-owned streams—such as the tuning sink itself—untouched.

This selective stream migration prevents the tuning sink from being inadvertently moved to headphones or another output, which would create an audio routing cycle or break the tuning chain.

## Volume Control and UI Binding

The volume control system binds directly to the `outputVolume` and `outputMuted` properties exposed by the model. When users interact with the OSD, panel sliders, or hotkeys, they trigger commands such as **`omarchy-audio-volume-up`**, **`omarchy-audio-volume-down`**, or **`omarchy-audio-volume-toggle`**.

Because these commands utilize the same `omarchy-audio-output-sink` resolution logic, every volume adjustment targets the physical hardware sink rather than any intermediate DSP node. The model’s computed properties ensure that all UI components reflect identical state, whether the change originates from a keyboard shortcut or the graphical panel.

## Audio Tuning Integration

When a speaker-tuning sink is present, `omarchy-audio-output-sink` continues to resolve and expose the physical hardware sink while keeping the tuning sink "fronted" (hidden from `omarchy-audio-output-switch`). The tuning sink’s volume remains fixed, and all default-output volume commands affect the physical sink exclusively.

This design pattern, documented in [`docs/audio-tuning.md`](https://github.com/omacom/omarchy/blob/main/docs/audio-tuning.md), ensures that users never encounter conflicting volume controls when tunings are active, and it prevents the tuning pipeline from being disrupted by routine output switches.

## Summary

- **Node-based architecture**: The pipeline models audio devices as nodes in [`shell/plugins/panels/audio/Model.js`](https://github.com/omacom/omarchy/blob/main/shell/plugins/panels/audio/Model.js), exposing unified properties like `outputVolume` and `hasOutput`.
- **Physical sink resolution**: The `omarchy-audio-output-sink` script resolves DSP front-ends to ensure volume commands target actual hardware.
- **Safe output switching**: `omarchy-audio-output-set-default` moves only streams with `application.name` properties, preventing system loops.
- **Tuning compatibility**: Virtual tuning sinks remain invisible to the switcher while volume controls manipulate the underlying physical device.

## Frequently Asked Questions

### How does Omarchy handle volume control when a speaker tuning is active?

Omarchy resolves the physical hardware sink through `omarchy-audio-output-sink` and directs all volume commands to that device while keeping the tuning sink’s volume fixed. This prevents the "double-volume" problem where users might adjust both the tuning virtual sink and the physical speakers separately.

### What is the difference between `omarchy-audio-output-switch` and `omarchy-audio-output-set-default`?

**`omarchy-audio-output-switch`** is a high-level command that cycles through available sinks and selects the next logical output, while **`omarchy-audio-output-set-default`** is the low-level implementation that receives a specific PipeWire node ID, executes `pw-cli set-default`, and migrates eligible application streams to the new sink.

### Why does the pipeline move only streams with an `application.name` property when switching outputs?

The system filters for `application.name` to distinguish user application audio (like music players or browsers) from system-owned streams (such as the tuning sink itself). Moving system streams would break the audio routing graph by detaching the tuning sink from its intended output, potentially creating feedback loops or silent audio chains.

### Which source files should developers examine to understand the Omarchy audio pipeline?

Developers should review [`shell/plugins/panels/audio/Model.js`](https://github.com/omacom/omarchy/blob/main/shell/plugins/panels/audio/Model.js) for the JavaScript model and state management, `shell/plugins/panels/audio/Panel.qml` for the UI bindings, `bin/omarchy-audio-output-set-default` for the default-sink logic, and [`docs/audio-tuning.md`](https://github.com/omacom/omarchy/blob/main/docs/audio-tuning.md) for the interaction between tunings and volume control.