Native Linux control suite for Elgato XLR interfaces: full hardware control over reverse-engineered USB protocols, a Wave Link style PipeWire submixer with per-application channels, virtual microphones, LV2, CLAP and VST3 plugin inserts, multi-output monitoring, a dedicated mix for a second computer on the USB Aux port, and an OpenDeck plugin for Stream Deck control.
Elgato ships no Linux software. These devices enumerate as class-compliant USB audio interfaces, so audio flows out of the box. Gain, DSP, phantom power, output routing and the hardware mixer only answer to vendor protocols, which this project reverse engineered from USB captures of Wave Link and reimplemented from scratch.
Not affiliated with or endorsed by Elgato. Built by protocol analysis on the author's own hardware.
| Device | USB id | Status |
|---|---|---|
| Wave XLR Pro | 0fd9:00b4 | full support, verified on hardware |
| XLR Dock (Stream Deck+ module) | 0fd9:00a6 | gain, mute, headphone volume, 48V phantom power, low impedance; verified on hardware |
| Wave XLR | 0fd9:007d | gain, mute, headphone volume, low impedance, 48V phantom power; verified on hardware by community testers |
| Wave XLR MK.2 | 0fd9:00b6 | gain, mute, phantom power, DSP, ClipGuard, compressor, headphone volume, crossfade; verified on hardware by a community tester |
| XLR Dock MK.2 (Stream Deck+ module) | 0fd9:00c7 | same controls as the Wave XLR MK.2; every control verified on hardware |
The UI shows only the controls the connected device has, and a picker in the header switches between several attached interfaces. The per-control state of every device is in docs/hardware-support.md. Own an untested device? Open an issue with a diagnostics archive (Options, SUPPORT, Collect diagnostics).
- Hardware control over the vendor USB protocol. On the Pro: gain, mute, low cut, expander, voice tune and phantom power per input, ClipGuard, compressor, aux input level and lock, two headphone volumes with low-impedance mode, the mic/PC crossfade, and the physical output routing (HP1, HP2, Line Out, USB Aux). The other devices expose the subset their protocol has; see the table above. Devices without onboard DSP get a software low cut, ClipGuard and gain lock in the PipeWire layer instead.
- Submixer built from PipeWire nodes (null sinks, remap sources, filter chains), no kernel modules. Channels for the hardware inputs and for application groups; mixes for what you hear (Monitor A and Monitor B, each output choosing one of the two or both summed), for virtual microphones other apps record from, and for the USB Aux port. The default layout is Game, Music, Browser, System, Voice Chat and SFX with Stream and Chat microphones; channels and microphones can be added, renamed, reordered and removed while audio plays, from the window or the API. Per-send levels and mutes, level meters, the monitor mixes on several outputs at once.
- Inserts: LV2, CLAP and VST3 plugin chains on each XLR input and each mix, with a plugin picker, generated control windows and bypass LEDs.
- Application routing: audio clients are detected from their PipeWire registration and routed to a channel by name rules, with the assignment remembered per app; an app can also be left to the desktop's own routing. Electron apps are identified by their process binary rather than the "Chromium" name they report.
- Profiles: named scenes holding the hardware settings and the whole submix (levels, mutes, outputs, insert chains), saved per device and recalled from the UI, the API or a Stream Deck key. One profile per device can be recalled on connect, so an interface comes up in a known scene at login or after a power cycle. Interfaces without settings memory (Wave XLR, the first XLR Dock) come back as they were left even without a profile, with a reset to firmware defaults.
- OpenDeck plugin: key and dial actions for every switch, mute, level and insert, rendered with level meters and status LEDs. It is a client of the daemon's API, so it reflects changes made in the UI or on the hardware.
- Daemon and UI: the daemon owns the device and the graph, keeps
running with the window closed, re-asserts the chosen default sink
and source once a second, and serves a WebSocket API and a versioned
HTTP API (
/api/v1) on 127.0.0.1:37890. The UI has a routing graph view, a tray icon and a diagnostics archive exporter. - Optional update notice: the UI can check the upstream GitHub release feed for a newer stable release. Startup checks are off by default and, when enabled, run at most once per day. Nothing is installed automatically.
The full feature list, area by area: docs/features.md.
Dials get a touch panel with a knob, a level meter, the value and a mute overlay; one dial can hold several targets, cycled by tap or press.
Keys show an icon and a status LED (red for a mute, green for an engaged feature or the active monitor output). Every hardware switch, mute, level and insert is a target.
Packages exist for Arch (AUR), Ubuntu (PPA), Fedora (COPR) and NixOS
(flake); every release also carries a .deb and an .rpm for a manual
install, with SHA-256 checksums and a GitHub build provenance
attestation (gh attestation verify <file> --owner emaspa). Pick your
distribution:
Arch Linux, from the AUR:
yay -S openxlr # or: paru -S openxlr
systemctl --user enable --now openxlr-daemon
openxlr # the mixer UI, also in your application menuUbuntu 24.04 and 26.04, from the PPA:
sudo add-apt-repository ppa:sparvoli/openxlr
sudo apt install openxlr
systemctl --user enable --now openxlr-daemon
openxlrWithout the PPA, download the .deb from the
latest release and
run sudo apt install ./openxlr_*_amd64.deb.
Fedora 44 or newer, from the COPR repository:
sudo dnf copr enable emaspa/openxlr
sudo dnf install openxlr
systemctl --user enable --now openxlr-daemon
openxlrWithout COPR, download the .rpm from the
latest release and
run sudo dnf install ./openxlr-*.x86_64.rpm.
NixOS: the repo is a flake with a package and a module. The module
enables the daemon itself; after a rebuild, openxlr is in the
application menu.
{
inputs.openxlr.url = "github:emaspa/openxlr";
# in your NixOS configuration:
imports = [ openxlr.nixosModules.default ];
services.openxlr.enable = true;
}On every distribution, replug the interface once after installing so
the udev rule applies. For the Stream Deck, install
com.emaspa.openxlr.sdPlugin.zip from the release with OpenDeck's
install-from-file, or copy the folder the package puts in
/usr/share/openxlr/ into ~/.config/opendeck/plugins/. Inserts show
whatever LV2 plugins are installed (lsp-plugins-lv2 is the set used
during development); the software ClipGuard for the XLR Dock needs
swh-plugins. The NixOS module wires both up itself. The packages also
raise pipewire-pulse's open-file limit with a systemd drop-in, which
applies at the next login or after systemctl --user restart pipewire-pulse; a source install needs the same file before growing the
layout (manual, section 5.8).
Needs the .NET 10 SDK, PipeWire with its CLI tools, libusb, and lilv (package names per distribution in docs/install-from-source.md).
git clone https://github.com/emaspa/openxlr.git
cd openxlr/src
dotnet build -c Release
OPENXLR_BUILD_MIXER=1 ./OpenXLR.Daemon/bin/Release/net10.0/OpenXLR.Daemon # terminal 1
./OpenXLR.UI/bin/Release/net10.0/OpenXLR.UI # terminal 2Device access needs the udev rule from packaging/70-openxlr.rules
installed under /etc/udev/rules.d/ and a replug. The XLR Dock also
needs the WirePlumber rule from packaging/. Running the daemon as a
user service, updating and uninstalling:
docs/install-from-source.md.
- Manual: first run, the concepts behind the mixer, step-by-step tasks, the Stream Deck plugin, troubleshooting
- Features: every control, the submixer, inserts, routing, profiles and the OpenDeck plugin in detail
- Roadmap: what comes next, in order, and the rules a change has to meet to land
- Installing from source: prerequisites by distribution, device access, the user service, updating, uninstall, environment variables
- WebSocket API: the daemon's command set and the files
under
~/.config/openxlr; HTTP API for the same commands over plain HTTP - Saved mixer layout: the layout file and the live layout commands
- Architecture: daemon, UI and plugin, the PipeWire graph, the device protocols, repository layout
- Hardware support: per-control status of every device
- Wave XLR Pro protocol: the vendor protocol as reverse engineered, with offsets
- USB capture guide: how to capture Wave Link traffic for an untested device
- CONTRIBUTING.md: how to build, test and send a change; AGENTS.md for AI-assisted contributions, which are allowed as long as the author owns and can explain the code
Support, hardware reports, feature requests and release news also live on the OpenXLR Discord server, discord.gg/4bswtnGPW4, and on Reddit at r/OpenXLR. The window links to both from Options, About. Confirmed bugs still end up as GitHub issues, so any of the three works to start. Want to help with code or hardware reports? Read CONTRIBUTING.md first.
Open Options, then SUPPORT, then Collect diagnostics. It writes
~/openxlr-diagnostics-<timestamp>.tar.gz with the app and device
state, a raw vendor-block dump, the PipeWire graph, daemon logs and
configs. Nothing gets uploaded; attach the archive to an issue, or to a
post in the Discord support forum, yourself.
OpenXLR is written and maintained by Emanuele Sparvoli. It exists in its current form because other people gave it code, hardware time and prior work.
Code:
- Carina Schoppe: much of the daemon's hardening (safe routing and device control, transactional graph changes, the bounded send queue, diagnostics redaction, systemd sandboxing, the test project and CI), the progress-gated watchdog, the update notice, the versioned HTTP API, the OpenDeck choices generated from daemon state, the first pieces of the editable layout (its saved format, live channel creation and the saved order), and the optional native LV2 host that opens a plugin's own editor on the instance processing your audio.
- Michael Brooks: the stream-sweep starvation fix (#7) and the diagnosis that led to it.
Hardware testing, on devices the maintainer does not own:
- BenjyEX3: Wave XLR MK.2, every control verified, including the block dump that placed phantom power, ClipGuard and the compressor (#2).
- Michael Brooks and a second owner: the original Wave XLR on two units (#6).
- chromacurse: the Wave XLR Pro headphone-mix report and the two diagnostics archives that let the hardware mix membership be decoded (#8).
- Astros52: the XLR Dock MK.2 descriptor dump that got the device registered before one was on hand (#1).
- The CachyOS tester whose first-run failure found the missing ASP.NET runtime dependency in the AUR package.
Prior work OpenXLR builds on:
- openwave by rikkichy: the original Wave XLR's class protocol, and the phantom-power byte found in openwave PR #8, which the XLR Dock turned out to share.
- OpenDeck by nekename: the Stream Deck host the plugin runs in, including the touch-tap support merged upstream for the Stream Deck + XL.
- FrostyCoolSlug, author of goxlr-utility and PipeWeaver, for suggesting ALSA UCM for the Pro's channel split.
Developed and used daily by the author with a Wave XLR Pro, an XLR Dock, an XLR Dock MK.2 and a Stream Deck + XL. The Wave XLR and Wave XLR MK.2 backends were verified on hardware by community testers. See the device table and docs/hardware-support.md for what is still open.
The majority of the code was produced by the author, with AI tooling (Anthropic's Claude) assisting with protocol capture analysis, UI design and parts of the coding. Every hardware finding was verified live on a real device.
GPL-3.0. If you find OpenXLR useful, consider buying me a coffee.



