Omarchy Audio Pipeline: Output Switching and Volume Control Architecture
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, 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 asapplication.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:
- Receives a PipeWire node ID and a friendly sink name as parameters.
- Executes
pw-cli set-default <node-id>to update PipeWire’s default target. - Moves only streams that carry an
application.nameproperty 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, 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, exposing unified properties likeoutputVolumeandhasOutput. - Physical sink resolution: The
omarchy-audio-output-sinkscript resolves DSP front-ends to ensure volume commands target actual hardware. - Safe output switching:
omarchy-audio-output-set-defaultmoves only streams withapplication.nameproperties, 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 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 for the interaction between tunings and volume control.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →