No description
Find a file
atagen 9220143db7 8a: assert_no_alloc on audio-thread callbacks
Wraps the three audio-thread `process` callbacks
(`capture_process`, `playback_process`, `tap_process`) with
`assert_no_alloc::assert_no_alloc(|| inner(...))`. The
`headroom-cli` binary installs `AllocDisabler` as `#[global_allocator]`
so any allocation inside one of those blocks during debug builds
aborts the process with "memory allocation of N bytes failed".

Each callback was renamed to `*_inner` to keep the thin wrapper
function pointer stable for pipewire-rs's `process(fn_ptr)`.

`assert_no_alloc`'s `disable_release` is on by default — release
builds get the system allocator unwrapped and the macros become
no-ops, so the audio thread pays zero runtime cost in production.

Verified

  Positive smoke (5 s of 1 kHz sine through processed): daemon
  stays up across thousands of capture/playback/tap callbacks. No
  abort. Audio threads are alloc-free as designed.

  Negative smoke (temporarily inserted `Vec::with_capacity(1024)`
  inside `capture_process_inner`): daemon aborts (SIGABRT, exit
  134) on the first audio callback with the expected
  "memory allocation of 1024 bytes failed" stderr message —
  confirming the harness is wired correctly and not silently a
  no-op. Sanity-check alloc reverted before commit.

  185 tests pass; clippy clean at -D warnings --all-targets.
2026-05-21 16:21:53 +10:00
crates 8a: assert_no_alloc on audio-thread callbacks 2026-05-21 16:21:53 +10:00
docs stage 2 2026-05-19 16:33:09 +10:00
profiles stage 2 2026-05-19 16:33:09 +10:00
.gitignore stage 2 2026-05-19 16:33:09 +10:00
Cargo.lock 8a: assert_no_alloc on audio-thread callbacks 2026-05-21 16:21:53 +10:00
Cargo.toml 5: monitor TUI + wire fill-ins 2026-05-21 13:35:27 +10:00
flake.lock stage 2 2026-05-19 16:33:09 +10:00
flake.nix stage 2 2026-05-19 16:33:09 +10:00
IPC.md stage 2 2026-05-19 16:33:09 +10:00
PLAN.md stage 6: per-app 2026-05-20 23:49:58 +10:00
README.md stage 6: per-app 2026-05-20 23:49:58 +10:00
rust-toolchain.toml stage 2 2026-05-19 16:33:09 +10:00

headroom

AGC + compressor + true-peak limiter daemon for PipeWire, in Rust.

Headroom puts a per-application audio safety net between noisy sources (browsers, voice chat, random video) and your speakers, while leaving the things you don't want compressed (music players, games, DAWs) untouched.

  • Hard 0.1 dBTP ceiling on the processed route, with proper inter-sample-peak handling, enforced inline so the contract holds regardless of control-plane state. Streams routed bypass ride the real sink directly and are not in scope of the contract — that's the trade-off that makes the per-app exclusion useful.
  • Per-app exclusion with profile-driven rules.
  • Single binary daemon + CLI, controlled over a Unix-domain socket with a documented JSON wire protocol (see IPC.md).
  • First-party Rust crate (headroom-client) for programmatic use; third-party clients (Qt panels, status bars, …) target the wire protocol directly.

See PLAN.md for the full design and roadmap.

Status

Pre-alpha. Wire protocol and crate scaffolding are in; daemon and filter are under construction.

Building

nix develop          # toolchain + pipewire dev libs + helpers
cargo build          # iterate
nix build            # final packaged headroom binary

License

GPL-3.0-or-later for the daemon and CLI. headroom-dsp and headroom-ipc are MPL-2.0 so they can be reused by non-GPL plugin hosts and clients.