Linux-native audio mixer for Elgato Wave:3 - providing Wave Link-style functionality using PipeWire.
Undertone gives you independent control over multiple audio channels with separate stream and monitor mixes, perfect for streamers and content creators on Linux.
Important
This project is primarily intended for AI experimentation and research. While functional, the codebase may emphasize exploration and iteration over refinement, and some components may be experimental or evolve rapidly. It is provided as-is and is not optimized for production use.
- 5 Audio Channels - System, Voice, Music, Browser, Game
- Dual Mix Architecture - Separate Stream and Monitor mixes with independent volume/mute per channel
- Automatic App Routing - Apps route to channels based on configurable rules (Discord → Voice, Spotify → Music, etc.)
- Master Volume Control - Per-mix master volume and mute
- Output Device Selection - Route monitor mix to any audio output (headphones, speakers, HDMI)
- Profiles - Save and load mixer configurations
- Mic Control - Gain and mute control for Wave:3 microphone
- Native UI - Qt6/QML with KDE Kirigami theming
Coming soon
- Linux with PipeWire (Fedora 43+, Ubuntu 24.04+, Arch, etc.)
- Elgato Wave:3 microphone (optional - works as general audio mixer too)
- Rust 1.85+ (Edition 2024)
- Qt6 with Kirigami
curl -sSL https://raw.githubusercontent.com/polariscli/Undertone/main/scripts/install.sh | bashThis will:
- Check and report missing dependencies
- Clone the repository
- Build from source
- Install binaries, systemd service, udev rules, and WirePlumber config
The installer will check for these automatically, but you can install them manually:
# Fedora
sudo dnf install pipewire-devel qt6-qtbase-devel qt6-qtdeclarative-devel \
clang kf6-kirigami-devel kf6-qqc2-desktop-style
# Arch Linux
sudo pacman -S pipewire qt6-base qt6-declarative clang kirigami
# Ubuntu/Debian
sudo apt install libpipewire-0.3-dev qt6-base-dev qt6-declarative-dev \
clang libkf6kirigami-devYou also need Rust 1.85+ (for Edition 2024):
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shgit clone https://github.com/polariscli/Undertone.git
cd Undertone
cargo build --release# Start the daemon (required)
cargo run -p undertone-daemon --release
# In another terminal, start the UI
cargo run -p undertone-ui --release# Full installation
./scripts/install.sh install
# Uninstall completely
./scripts/install.sh uninstall
# Update to latest version
./scripts/install.sh update
# Check dependencies only
./scripts/install.sh check
# Service management
./scripts/install.sh start|stop|enable|disable|status|logs
# Install individual components
./scripts/install.sh udev|wireplumber|service|buildUndertone creates virtual audio sinks in PipeWire that applications connect to. Each channel feeds into volume filter nodes that control the audio level independently for Stream and Monitor mixes.
App (Spotify)
-> ut-ch-music (channel sink)
-> ut-ch-music-stream-vol -> ut-stream-mix -> OBS
-> ut-ch-music-monitor-vol -> ut-monitor-mix -> Headphones
| Pattern | Channel |
|---|---|
| discord | Voice |
| zoom | Voice |
| teams | Voice |
| spotify | Music |
| rhythmbox | Music |
| firefox | Browser |
| chrome | Browser |
| steam | Game |
| default | System |
- Adjust volume sliders to control audio levels
- Click mute button to silence a channel
- Toggle between Stream and Monitor mix views
- Use master volume for overall mix control
- View currently playing audio applications
- Click channel dropdown to reassign apps
- Routes are automatically saved
- View Wave:3 connection status
- Adjust microphone gain
- Toggle mic mute
- Click profile name in header to switch profiles
- Use menu to save current settings or delete profiles
Data is stored in ~/.local/share/undertone/:
undertone.db- SQLite database with channels, routes, profiles- Logs via systemd journal when running as service
WirePlumber configuration for Wave:3 naming:
~/.config/wireplumber/wireplumber.conf.d/51-elgato.conf
# Check if daemon is running
pgrep undertone-daemon
# Verify PipeWire nodes exist
pw-cli list-objects Node | grep ut-
# Check audio links
pw-link -l | grep ut-# Check database routes
sqlite3 ~/.local/share/undertone/undertone.db "SELECT * FROM app_routes;"
# Restart daemon to re-apply routes
pkill undertone-daemon && cargo run -p undertone-daemon# Check socket exists
ls -la $XDG_RUNTIME_DIR/undertone/daemon.sock
# Test IPC
echo '{"id":1,"method":{"type":"GetState"}}' | \
socat - UNIX-CONNECT:$XDG_RUNTIME_DIR/undertone/daemon.sockundertone-daemon (background service)
- IPC Server (Unix socket) | Signal Handler | Event Loop (Tokio)
- undertone-core: Channels, Mixer, App Routing, Profiles, State
- undertone-pipewire: PipeWire graph management
- undertone-db: SQLite persistence
- undertone-hid: Wave:3 hardware (ALSA fallback)
Unix Socket IPC
undertone-ui (Qt6/QML + Kirigami + cxx-qt)
Contributions welcome! Please see PROGRESS.md for current status and planned features.
GPL-3.0 - See LICENSE for details.
- pipewire-rs - Rust bindings for PipeWire
- cxx-qt - Safe Rust/Qt interop
- KDE Kirigami - UI framework
- Elgato for the excellent Wave:3 hardware