Point it at your microphone and it publishes a second, processed one that every app can record from — gated, compressed, de‑essed, EQ'd and limited. Hear yourself while you turn the knobs, then close the window: it keeps working from the tray.
- 🎚️ A second microphone, not a setting. Vocalis publishes a real
Audio/Sourcenamed Vocalis (processed mic). Discord, OBS, Zoom, your browser — anything that lists microphones lists this one, and picks up the processing with no per‑app configuration. - 🎧 Hear yourself while you tune. The monitor tap sits after the limiter and the output gain, so what lands in your headphones is byte‑for‑byte what the other side receives. Monitor level changes your playback only; it never touches the signal apps record.
- 🚪 Noise gate. Threshold, range, attack, hold and release, with hysteresis and a hold timer so the tail of a word never gets chopped.
- 🎛️ Compressor. Soft knee, ratio, attack, release and make‑up gain — the part that makes a voice sound close instead of merely loud.
- 🐍 De‑esser that doesn't dull you. It splits the band, ducks only the sibilance
and adds the rest back untouched:
low + gain × high, which reconstructs the input exactly at 0 dB of reduction, so at rest it is audibly transparent. - 🎼 Three‑band EQ. Low shelf, parametric mid with adjustable Q, high shelf.
- 🧱 Peak limiter. Adjustable ceiling, so a laugh or a slammed desk never clips the stream.
- 🧩 No plug‑in packs. Every filter is implemented inside Vocalis. It does not load LADSPA, LV2 or Calf/LSP bundles, and there is no second audio daemon: the only thing underneath it is the PipeWire you already run.
- 🎨 Presets that tell you where you are. The header button always names the
preset in effect — or
Customthe moment you move a knob. Open it and the active one is ticked,Broadcastis labelled Default. Monitoring, the master switch and the input/output gains are your rig, not the preset, so loading one never silences your headphones or undoes your mic calibration. - 📊 Live meters. Input and output levels plus per‑stage gain reduction for the gate, the compressor and the de‑esser, so you can see which stage is working.
- 🖥️ Monochrome tray icon. Closing the window hides it — the processed mic keeps running. The tray toggles processing and monitoring, reopens the window, and quits.
- 🚀 Start with the system. One switch and Vocalis launches
--hiddenat login: it is in the tray with the mic already processed, no window in your face. Under Flatpak this goes through the desktop's background portal, so your desktop asks you once and stays in charge of the permission. - 🔒 Exactly one instance. Launching it again re‑presents the existing window instead of publishing a second virtual microphone.
- 📦 Flatpak‑first. Sandboxed, no host packages, no
sudo.
Works great with: Discord, OBS, Zoom/Meet/Teams, browser calls, recording into Audacity or Reaper — anything that lets you choose an input device.
flowchart LR
MIC["🎤 Your mic<br/>(any PipeWire source)"] --> HPF["High-pass"]
HPF --> GATE["Noise gate"]
GATE --> COMP["Compressor"]
COMP --> DEESS["De-esser"]
DEESS --> EQ["3-band EQ"]
EQ --> LIM["Limiter"]
LIM --> SRC["🎚️ Vocalis<br/>(processed mic)"]
LIM --> MON["🎧 Monitor<br/>your headphones"]
SRC --> APPS["💬 Discord · OBS · Zoom · browser"]
style SRC fill:#1d99f3,stroke:#0b76c4,color:#fff
style MON fill:#2ea043,stroke:#1a7f37,color:#fff
style MIC fill:#2a2e32,stroke:#444,color:#eee
style APPS fill:#2a2e32,stroke:#444,color:#eee
Logitech's Blue VO!CE does this on Windows and has no Linux counterpart. The usual answer here is EasyEffects plus a pile of LSP/Calf plug‑ins — a second application to keep running, a plug‑in ecosystem to install, and a graph to understand before you can make your voice sound better.
The reason people reach for those plug‑ins is that PipeWire's own filter‑chain
cannot do this alone: as of 1.6 its built‑ins cover biquads, a parametric EQ, a
convolver, a delay and a noisegate — but no compressor, no de‑esser and no
limiter, which are exactly the stages that make a voice sound broadcast‑ready.
So Vocalis brings its own. The DSP is a few hundred lines of Rust inside the app, which is what lets the whole thing be one sandboxed program on top of stock PipeWire, with a slider for every parameter and no graph to wire up.
Every push builds an installable .flatpak bundle in CI. Grab it from the
latest release, or straight from
a workflow run with the GitHub CLI:
gh run download --repo geodro/vocalis --name vocalis-flatpak
flatpak install --user --bundle vocalis.flatpak
flatpak run co.dumitres.VocalisNo sudo, and it brings its own runtime.
git clone https://github.com/geodro/vocalis.git
cd vocalis
flatpak install --user -y flathub org.gnome.Sdk//49 \
org.freedesktop.Sdk.Extension.rust-stable//25.08 \
org.freedesktop.Sdk.Extension.llvm20//25.08
flatpak run org.flatpak.Builder --force-clean --user --install build-dir co.dumitres.Vocalis.json
flatpak run co.dumitres.Vocaliscargo build --release
./target/release/vocalisNeeds the libpipewire-0.3, gtk4 and libadwaita development packages, plus
libclang for the PipeWire bindings.
flatpak uninstall --user co.dumitres.Vocalis
rm -f ~/.config/autostart/co.dumitres.Vocalis.desktop
rm -rf ~/.var/app/co.dumitres.Vocalis # or ~/.config/vocalis when run from sourcePick your real microphone at the top, switch Hear myself on, and turn knobs until it sounds right. Then select Vocalis (processed mic) as the input in whatever app you are talking through.
| Section | What to reach for |
|---|---|
| Input | The raw device to listen to, input gain, and the autostart switch. System default follows whatever the desktop calls the default microphone. |
| Monitor | Hear myself and its level. Playback only — it never changes what other apps receive. |
| High‑pass filter | Rumble, desk thumps, air conditioning. 80–120 Hz suits most voices. |
| Noise gate | Threshold just above your room noise; raise Hold if word endings get clipped; Range is how far a closed gate ducks, not a hard mute. |
| Compressor | Lower Threshold / raise Ratio for a denser voice; Knee softens the onset; Make‑up brings the level back. Watch its gain‑reduction readout — 4–8 dB on peaks is a good place to live. |
| De‑esser | Frequency around 5–8 kHz, then lower Threshold until the readout only moves on "s" sounds. |
| Equaliser | Low shelf for weight, mid for presence or to notch boxiness, high shelf for air. |
| Limiter | Leave it on. -1 dB ceiling is a safe default. |
| Output | Final gain, and the output meter that tells you what apps actually get. |
Presets live behind the header button; Broadcast is the default. The switch at the
top‑right bypasses the whole chain, so you can A/B against your raw mic instantly.
Settings and the chosen microphone are saved on close and restored next launch.
Vocalis is a PipeWire client, not a server module — which is what lets it live in
a sandbox. It runs its own main loop on a dedicated thread and owns three streams: a
capture stream on your real microphone, an Audio/Source node other apps record
from, and a playback stream for monitoring.
The capture callback does the DSP in place and pushes the result into two lock‑free
ring buffers, which the source and monitor callbacks drain. Each drain throws away a
backlog before reading, so a stretch where nobody was recording cannot turn into
permanent delay. Capture and source share a node.link-group: that is what stops the
session manager from ever linking Vocalis's own output back into its input when the
processed mic becomes your default one.
Parameters cross from the GUI to the audio thread as plain atomics — no locks, no allocation, nothing that can block a realtime callback. The chain snapshots them once per block and only recomputes filter coefficients when something actually changed.
The chain itself, in signal order: high‑pass biquad → gate (peak follower with
hysteresis and a hold counter) → feed‑forward compressor with a soft‑knee curve in
the log domain → de‑esser splitting at an adjustable frequency and recombining as
low + gain × high → low shelf, peaking mid and high shelf → output gain → peak
limiter with a fast attack and a hard ceiling.
The tray icon is a StatusNotifierItem. Inside Flatpak it deliberately runs without owning a well‑known D‑Bus name, which is the documented workaround for sandboxed SNI clients.
- Vocalis isn't in my app's microphone list. It only exists while Vocalis is
running. Confirm with
pactl list sources short | grep vocalis. Some apps cache their device list — reopen their audio settings, or restart them once. - I can't hear myself. Hear myself has to be on, and the monitor follows your
default output — if your headphones aren't the default sink, the sound is going
somewhere else. Check with
wpctl status. - It sounds gated off / too quiet. The gate threshold is above your voice. Lower
it until the gate's gain‑reduction readout sits at
0.0 dBwhile you talk. - The tray icon is missing. Your desktop needs a StatusNotifier host. KDE Plasma has one built in; on GNOME install the AppIndicator extension. Vocalis still runs fine without one — it just cannot be reopened from the tray.
- Autostart didn't stick. Under Flatpak the switch asks the background portal,
which is allowed to say no — if it flips itself back off, the desktop refused it
(GNOME lists it under Settings → Apps → Vocalis → Background). Run from source it
writes
~/.config/autostart/co.dumitres.Vocalis.desktop, and any error is on stderr. - Nothing at all is running.
pw-cli info 0has to work first — everything here assumes a live PipeWire session.
cargo test # DSP and preset behaviour
python3 build-aux/gen-cargo-sources.py # after any dependency changebuild-aux/gen-cargo-sources.py regenerates the vendored crate list for Flatpak
straight from Cargo.lock; it needs no network, because every checksum is already
in the lock file. CI fails if the generated file is stale.
Before submitting to Flathub, check it the way Flathub does:
flatpak run --command=flatpak-builder-lint org.flatpak.Builder manifest co.dumitres.Vocalis.json
flatpak run --command=flatpak-builder-lint org.flatpak.Builder appstream data/co.dumitres.Vocalis.metainfo.xml
flatpak run --command=flatpak-builder-lint org.flatpak.Builder builddir build-dirInspired by Logitech's Blue VO!CE, and built on the work of the PipeWire project and the pipewire‑rs / gtk‑rs bindings.
GPL‑3.0‑or‑later © George Dumitrescu
