Skip to content
TensorRayaPublic

About

A streamer's audio mixer for KDE Plasma on PipeWire: per-app channels, unlimited output mixes, per-mix levels, effects, Stream Deck control. Wave Link for Linux.

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

kmixdeck

A streamer's audio mixer for Linux, built on PipeWire — with a KDE window, a web UI, a CLI and a Stream Deck plugin on one service.

Route every application into its own channel, build as many output mixes as you need (what you hear vs. what the stream hears vs. …), set each channel's level independently per mix, put effects on anything, and drive it from whatever is in reach — the desktop, a phone on the LAN, a shell script, a Stream Deck. Think Elgato Wave Link, for Linux, without the hardware lock-in and without the artificial limits.

kmixdeck — six mixes at 1280 px

Qt 6 / Kirigami · PipeWire-native · GPL-3.0-or-later · v0.4.1

Why

There is no Wave Link / VoiceMeeter / Sonar equivalent on Linux. PipeWire has every primitive needed (virtual sinks, loopbacks, per-app routing), but the tooling stops at patchbays (qpwgraph, Helvum) and volume controls (pavucontrol). The one project that tried to fill the gap, Sonusmix, is unmaintained since 2025 and never solved independent per-mix levels (#72). ADR 0001 says why this is a new project rather than a fork.

What you get

  • Channels × mixes. Channels (Game, System, Voice, …) as rows, mixes (Monitor, Stream, any number) as columns; one fader per intersection, levels that never leak between mixes. Measured on the wire, not assumed (−12.0 / +6.0 / −∞ dB, ADR 0002).
  • Per-mix outputs and capture sources. Each mix plays to one or several hardware outputs and is exposed as kmixdeck.source.<mix> for OBS, Discord, anything that records. A fallback output covers the unplugged headset.
  • Ports, not just devices. A 32-in/32-out interface is 32 addressable ports: any port or pair can be a channel, a mix can go to AUX3,AUX4, a side can be bound left-only (ADR 0009).
  • Effects. Ordered insert chains on channels and mixes — builtin plus LADSPA (noise suppression, gate, compressor), presets, bypass, live controls (ADR 0008).
  • Named scenes. Store a whole mix state and recall it from any frontend — stream night, just recording, talkback on. Stored per name, survives a restart, and recall is one undoable graph pass. Deliberately independent of the channel set and the wiring, so a scene still fits after a rename or a repatch (CLI).
  • Soundboard. Samples on pads, played by the service into the graph — so a jingle lands in the stream and in your headphones like any other source, not in a player only you can hear. Per-sample gain, one board per channel (CLI).
  • Loudness per mix. EBU R128 measurement with a target line, plus a broadcast limiter that works on the summed mix instead of the individual channels — so the stream lands where it should without riding the faders.
  • Devices that survive real life. Identified by stable node.name; unplug, replug, sleep, wake — routing and levels come back without a restart. Tested with every device vanishing at once.
  • Live meters everywhere. Cells, apps, inputs, outputs — 25 Hz over one D-Bus signal that only runs while somebody looks (ADR 0006).
  • Kill every kmixdeck process, audio keeps flowing. The graph is plain PipeWire objects from a config the service renders; PipeWire builds it at login on its own. The service is only the control plane.
  • Edit the config while it runs. layout.json is watched, not just read at startup: an edit by hand, by config management or by a synced copy from another machine is applied in about half a second — the graph reconciles and every frontend redraws. A half-written or corrupt file is refused and the running layout stays up, and an edit is never overwritten by a save the daemon happens to make at the same moment.
  • One service, N frontends. Window, tray, web UI, CLI and Stream Deck speak the same D-Bus contract; a change in one is in all the others within a frame. A feature counts as done only when every frontend has it (ADR 0010) — and a machine-checked requirements table enforces that.

Install

Build dependencies: CMake ≥ 3.20, a C++20 compiler, Qt ≥ 6.6 (Core, Gui, Widgets, Qml, Quick, QuickControls2, Svg, DBus), KDE Frameworks ≥ 6.8 (CoreAddons, Config, I18n, Kirigami, KirigamiAddons, QQC2DesktopStyle, IconThemes, GlobalAccel, StatusNotifierItem, Notifications, DBusAddons, Crash), libpipewire-0.3 ≥ 1.0, libebur128 ≥ 1.2 (EBU R128 loudness metering, UX-18 — libebur128-dev on Debian/Ubuntu, libebur128 on Arch), the LADSPA header (ladspa-sdk on Debian/Ubuntu, ladspa on Arch, ladspa-devel on Fedora). Recommended: LayerShellQt (liblayershellqtinterface-dev on Debian/Ubuntu, layer-shell-qt on Arch) — without it the tray popover exists on X11 only, and a click on the tray under Wayland opens the window instead. Runtime: PipeWire + WirePlumber (any distro of 2024 or later). Optional: swh-plugins/rnnoise for LADSPA effects; python3-gi + python3-websockets for the web UI.

PipeWire 1.6.0–1.6.2 (Ubuntu 26.04 ships 1.6.2) have a filter-chain bug that crashes kmixdeckd and the PipeWire server (#5202). On exactly those versions the build also compiles upstream's fixed 1.6.3 module as libpipewire-module-kmixdeck-filter-chain and installs it into PipeWire's module directory; kmixdeck uses it until your PipeWire is 1.6.3 or newer, then PipeWire's own again (ADR 0015). The configure summary says whether it is built. With a prefix inside $HOME, install it separately: sudo cmake --install build --component pipewire-module.

cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
ninja -C build
sudo ninja -C build install
systemctl --user daemon-reload && systemctl --user enable --now kmixdeckd   # or let D-Bus start it on first use
kmixdeck-kde

Headless host? -DBUILD_KDE_FRONTEND=OFF builds the service, the CLI and the web bridge without the desktop stack — no Kirigami, no KDE Frameworks beyond the basics. That is the build for a streaming box with the interface on USB and no monitor attached; drive it from the web UI on your phone or over the CLI via SSH.

The first start offers to set up defaults (Monitor → your speakers, Voice ← your mic, running apps by role). From then on PipeWire builds the graph at every login from ~/.config/pipewire/pipewire.conf.d/kmixdeck.conf, which the service keeps in sync with your layout.

Use it

Frontend Start Docs
Window + tray (Plasma) kmixdeck-kde docs/window.md
Web UI (phone, tablet, headless box) kmixdeck-web / kmixdeck-web --lan / systemctl --user enable --now kmixdeck-web docs/web.md
CLI (scripts, tests) kmixdeck status, kmixdeck --json … docs/cli.md
Stream Deck (OpenDeck) kmixdeck streamdeck install streamdeck/README.md
Your own D-Bus org.kmixdeck1 docs/frontend-guide.md, contract in interfaces/

Thirty seconds of CLI:

kmixdeck cell set game stream -12dB          # game quieter in the stream only
kmixdeck mix output stream alsa_output.usb-Focusrite_Scarlett-00.analog-stereo
kmixdeck app move firefox browser            # remembered for next time firefox starts
kmixdeck export > backup.json                # layout + every level

How it fits together

applications ──▶ channel sinks ──(loopback = one fader per cell)──▶ mix sinks ──▶ headphones / USB / …
hardware inputs ─┘                                                            └▶ kmixdeck.source.<mix> → OBS
kmixdeckd ── owns the graph, renders it to PipeWire config, exports org.kmixdeck1 on the session bus
     ▲            ▲             ▲            ▲              ▲
  kmixdeck    kmixdeck-kde   kmixdeck-web   Stream Deck   your frontend

The long version with diagrams: docs/architecture.md. What it must do and how we know it does: docs/spec/requirements.md — 102 numbered requirements, 101 proven by a named test, the count written by the audit that fails the build otherwise. Why it is built this way: docs/adr/.

Developing

cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_COMPILE_WARNING_AS_ERROR=ON && ninja -C build
pip install pytest pulsectl                     # integration tests
sudo apt install pipewire wireplumber pipewire-pulse ffmpeg gettext swh-plugins libebur128-dev ladspa-sdk   # what the suites need
# plus Google Chrome or Chromium for the browser tests — Ubuntu 26.04 has no chromium .deb (see .github/workflows/ci.yml)
ctest --test-dir build --output-on-failure      # ~32 min serial: 199 integration tests in 14 suites against a
                                                # private PipeWire per suite, real audio measured, four frontends driven
ruff check .                                    # Python; the C++ build is -Werror

Every requirement row names its test; ctest -R sot-audit checks that and fails on a ✅ without proof. Read CONTRIBUTING.md for the two rules (backend/API first; a feature is done when every frontend has it) and the review that shaped v0.1: docs/review/2026-09-18-v0.1-review.md.

Repository layout

src/                 kmixdeckd (service) · kmixdeck (CLI) · kmixdeck-kde (window, tray) · frontend/ (MixerClient lib) · qml/
web/                 kmixdeck-web (bridge) · static/ (the browser UI, plain ES modules)
streamdeck/          OpenAction plugin for OpenDeck
interfaces/          the D-Bus introspection XML — the API contract
docs/                window.md · web.md · cli.md · architecture.md · frontend-guide.md · spec/ · adr/ · review/
tests/               QTest units · pytest integration (private PipeWire sandbox per suite) · tools/
po/                  translations (English source, German)
data/                systemd user units, D-Bus service, desktop/metainfo/notifyrc

Translations

English is the source language; German ships in po/de/. sh tools/extract-messages.sh refreshes the catalogs; ctest fails on a stale .pot or an incomplete German one.

License

GPL-3.0-or-later. See LICENSE.

About

A streamer's audio mixer for KDE Plasma on PipeWire: per-app channels, unlimited output mixes, per-mix levels, effects, Stream Deck control. Wave Link for Linux.

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages