Linux control application for the Elgato Wave XLR microphone interface. A reverse-engineered replacement for Elgato Wave Link, built with GTK4 + Adwaita.
- Microphone controls — Gain, mute (syncs with hardware button)
- Headphone controls — Volume (syncs with hardware knob), low impedance mode
- Hardware sync — 10 Hz polling keeps the app in sync with physical controls
- System integration — Mute and HP volume sync bidirectionally with PipeWire/ALSA
- Audio capture fix — Background daemon (systemd or runit) prevents the firmware race condition where mic goes silent
- System tray — Runs in background with tray icon, mute from tray menu
- First-run setup — Configures udev permissions and audio service automatically
The Wave XLR uses USB Class control transfers on endpoint 0 for device configuration. On Linux, snd-usb-audio normally blocks these transfers because wIndex=0x3300 routes through interface 0 (owned by the audio driver). OpenWave uses wIndex=0x3303 instead — the firmware only checks the 0x33 prefix, while the kernel sees interface 3 (unclaimed) and lets the transfer through. No driver detach needed, audio is never interrupted.
One-liner — detects Arch, Debian/Ubuntu, Fedora, openSUSE, or Void; installs deps and OpenWave:
curl -fsSL https://raw.githubusercontent.com/rikkichy/openwave/main/install.sh | shOr from a checkout:
git clone https://github.com/rikkichy/openwave.git
cd openwave
./install.sh # default PREFIX=/usr/local
PREFIX=/usr ./install.sh # for packaging-style layoutUninstall:
sudo make -C /path/to/openwave uninstall PREFIX=/usr/local- Python 3.10+
- GTK4, libadwaita
- PipeWire (for audio capture fix)
- libusb 1.0
openwave # if installed via install.sh / PKGBUILD
python3 -m wavexlr # from a checkout, no install neededOn first launch, OpenWave will prompt to set up USB permissions (via polkit) and install the audio service.
OpenWave detects your init system at runtime:
-
systemd — the GUI installs a user unit at
~/.config/systemd/user/openwave.serviceand enables it. No root needed for install or status checks. -
runit (Artix, Void, Devuan-runit) — the GUI cannot install the system service itself (writing to
/etc/svrequires root). Create awavexlr-audioservice directory at/etc/sv/wavexlr-audio/whoserunscript execspython3 -m wavexlr.daemonas your user (typically viachpst -u), then enable it withln -s /etc/sv/wavexlr-audio /var/service/.Status detection from the non-root GUI uses
sv check; on stock Void the supervise FIFO is mode 0700, so OpenWave falls back to scanning/procfor the daemon process. -
other (macOS, Windows, no init detected) — the capture-fix section is disabled.
Start hidden in tray
python3 -m wavexlr -- --hideCopy wavexlr.desktop to ~/.local/share/applications/ for app launcher integration.
wavexlr/
device.py — USB backend (raw libusb via ctypes, wIndex=0x3303 trick)
app.py — GTK4/Adwaita UI with 10Hz polling
tray.py — StatusNotifierItem tray icon via D-Bus
audio.py — PipeWire capture keepalive (fixes firmware race condition)
daemon.py — Systemd service entry point
setup.py — First-run udev + systemd setup
USB protocol reverse-engineered from the macOS Wave Link application using Frida. Inspired by GoXLR-on-Linux/goxlr-utility.
MIT