Elgato Wave controls, native to Linux.
Built with Python, GTK4, and libadwaita.
Supported devices · Install · Usage · How it works
OpenWave is an open-source, reverse-engineered alternative to Elgato Wave Link for controlling Wave XLR, XLR Dock, and Wave:3 hardware on Linux. Adjust your microphone and headphones from a native desktop app, with changes kept in sync with your device and audio system.
- Microphone controls — Adjust gain and mute, with 48 V phantom power on supported XLR devices.
- Headphone controls — Set volume, enable low impedance mode on supported devices, or adjust the Wave:3 monitor mix.
- Hardware and system sync — Polling at 10 Hz tracks physical buttons and knobs. Mute and headphone volume sync bidirectionally with PipeWire/ALSA.
- Audio capture fix — Starts capture before playback, keeps it pinned while running, and releases it after the audio graph shuts down to address the firmware race during warm reboots.
- System tray — Keep OpenWave running in the background and mute directly from the tray menu.
- Guided setup — Configure USB permissions and the audio service from the app, with support for systemd and runit.
| Device | USB ID | Available controls |
|---|---|---|
| Wave XLR | 0fd9:007d |
Gain, mute, 48 V phantom power, headphone volume, low impedance mode |
XLR Dock (00a6 variant) |
0fd9:00a6 |
Gain, mute, 48 V phantom power, headphone volume, low impedance mode |
| Wave:3 | 0fd9:0070 |
Gain, mute, headphone volume, monitor mix |
Controls are enabled by the device profile. Phantom power and low impedance mode are available on the supported XLR models; monitor mix is available on Wave:3.
With curl, git, and make available, run:
curl -fsSL https://raw.githubusercontent.com/rikkichy/openwave/main/install.sh | shThe installer detects Arch, Debian/Ubuntu, Fedora, openSUSE, or Void and installs the dependencies and application. The default installation prefix is /usr/local.
git clone https://github.com/rikkichy/openwave.git
cd openwave
./install.shFor a packaging-style layout under /usr, use this instead of the last command:
PREFIX=/usr ./install.sh- Python 3.10+
- GTK4 and libadwaita
- PipeWire and PulseAudio client tools (
pw-*,pactl) - ALSA utilities (
aplay,amixer) - libusb 1.0
Launch an installed copy:
openwaveOr run from a checkout with the dependencies installed:
python3 -m wavexlrOn first launch, OpenWave prompts you to set up USB permissions and the audio service. USB permission changes use polkit. Reconnect the device when prompted after setup.
Start hidden in the system tray:
openwave --hideFrom a checkout, use python3 -m wavexlr --hide.
For the default /usr/local installation:
mkdir -p ~/.config/autostart
cp /usr/local/share/openwave/openwave-autostart.desktop ~/.config/autostart/For a PREFIX=/usr or PKGBUILD installation, use /usr/share/openwave/openwave-autostart.desktop as the source instead.
The installer also adds an OpenWave app launcher entry under $PREFIX/share/applications.
OpenWave detects the service backend at runtime:
| Init system | Setup and behavior |
|---|---|
| systemd | Installs and enables the user unit at ~/.config/systemd/user/openwave.service. Service installation and status checks do not require root. |
| runit | Uses polkit to install /etc/sv/wavexlr-audio and enable it in /var/service. The daemon runs as your user through chpst. |
| Other / not detected | Automatic audio-service management is unsupported. |
On runit, the GUI checks service status with sv check. If the supervise FIFO is inaccessible to the current user, as on stock Void, it falls back to scanning /proc for the daemon.
For a full cleanup, first use Uninstall capture fix in the app to remove the audio service, audio configuration, and USB permissions. Then remove the application files from a checkout:
sudo make -C /path/to/openwave uninstall PREFIX=/usr/localUse the same PREFIX you installed with. If you enabled startup at login, also remove the autostart entry:
rm -f ~/.config/autostart/openwave-autostart.desktopWave devices use USB Class control transfers on endpoint 0 for configuration. On Linux, snd-usb-audio normally blocks these transfers because wIndex=0x3300 routes through interface 0, which belongs to the audio driver.
OpenWave uses wIndex=0x3303. The firmware checks only the 0x33 prefix, while the kernel sees the unclaimed interface 3 and allows the transfer. This lets OpenWave control the device without detaching its audio driver.
USB protocol and configuration layout
All supported models use bRequest=0x85 to read and bRequest=0x05 to write configuration. The Wave XLR and 0fd9:00a6 XLR Dock share a 34-byte configuration block; Wave:3 uses a 16-byte block.
Offsets below are in bytes:
| Field | Wave XLR / XLR Dock | Wave:3 |
|---|---|---|
Gain (uint16, Q8.8 dB) |
0 |
0 |
| Mute | 4 |
4 |
| 48 V phantom power | 6 |
— |
Headphone volume (int16, Q8.8) |
9 |
7 |
Monitor mix (uint16, Q8.8 percent) |
— | 10 |
| Knob / dial mode | 14 |
12 |
| Low impedance mode | 33 |
— |
For Wave:3, dial mode values are 1 = gain, 2 = headphones, and 3 = mix. Per-model constants and capabilities live in wavexlr/profiles.py.
The wavexlr.probe CLI provides dump, watch, and poke commands to verify a device against its profile and help map new fields.
Quit OpenWave, including its tray process, before probing: the device services vendor transfers from only one process at a time.
python3 -m wavexlr.probe --help| Module | Responsibility |
|---|---|
device.py |
USB backend using raw libusb through ctypes and wIndex=0x3303 |
profiles.py |
Per-model protocol constants and capabilities |
probe.py |
Protocol verification CLI: dump, watch, poke |
app.py |
GTK4/libadwaita interface with 10 Hz polling |
tray.py |
StatusNotifierItem tray icon over D-Bus |
audio.py |
PipeWire capture keepalive for the firmware race condition |
daemon.py |
Audio-service entry point |
service.py |
systemd and runit service management |
setup.py |
First-run USB permissions and audio setup |
The USB protocol was reverse-engineered from the macOS Wave Link application using Frida. Inspired by GoXLR-on-Linux/goxlr-utility.
OpenWave is licensed under the MIT License.