Skip to content

feat(audio): native PipeWire capture backend on Linux - #1774

Open
olosegres wants to merge 1 commit into
cjpais:mainfrom
olosegres:pipewire-capture-backend
Open

feat(audio): native PipeWire capture backend on Linux#1774
olosegres wants to merge 1 commit into
cjpais:mainfrom
olosegres:pipewire-capture-backend

Conversation

@olosegres

@olosegres olosegres commented Jul 23, 2026

Copy link
Copy Markdown

Problem

On Linux, microphone capture is pinned to cpal's ALSA host (get_cpal_host()HostId::Alsa in audio_toolkit/audio/utils.rs). Handy's capture stream is therefore an ALSA "default" client, and PipeWire never sees it as a first-class node. On the (now near-universal) PipeWire desktop this means:

Change

Add a native PipeWire capture backend (pipewire-rs 0.10) that registers Handy's microphone as a real PipeWire node, so it shows up in wpctl status with its own settable volume and is routable per-app.

  • New pipewire_recorder.rs: runs the PipeWire main loop on a dedicated thread (all non-Send pw objects stay on it), negotiates F32, downmixes to mono in the RT process callback, and feeds the existing resampler → VAD → consumer pipeline unchanged via the shared Cmd/AudioChunk protocol. No DSP is reimplemented.
  • New recorder_backend.rs (Recorder): a thin seam that selects the backend. On Linux it prefers PipeWire and falls back to the cpal/ALSA path if PipeWire setup fails (e.g. no running session). Non-Linux targets compile cpal-only and are behaviourally unchanged; cpal stays fully compiled as the fallback everywhere.
  • pipewire is added only under [target.'cfg(target_os = "linux")'.dependencies] (feature v0_3_44), so Windows/macOS builds are untouched.

Testing

Built and run on Fedora (Asahi, aarch64) under GNOME/Wayland with PipeWire 1.6.8. Hotkey → record → transcribe works end-to-end through the native backend; the log shows Microphone capture using native PipeWire backend, and the capture node appears in wpctl status with a settable volume (wpctl set-volume <id> …) — which the old ALSA client did not support (Node … does not support volume).

Scope / follow-ups

This is an MVP that captures the default source (reproducing today's "default" behaviour). PipeWire-native device enumeration/selection is intentionally deferred — there's a TODO in Recorder::open, where a user-selected mic would map to a node.name passed through TARGET_OBJECT (already threaded through PipeWireRecorder::open). Happy to iterate on device selection and feature-gating in review.

On Linux, capture is pinned to cpal's ALSA host (`get_cpal_host` ->
`HostId::Alsa`), so Handy's microphone stream is an ALSA "default" client.
PipeWire cannot see it as a first-class node: there is no per-application
capture volume, no routing, and device enumeration only ever exposes
"default" instead of the graph's real sources.

Add a native PipeWire capture backend (pipewire-rs 0.10) that registers the
microphone stream as a real PipeWire node, so it appears in `wpctl status`
with its own settable volume and is routable per-app. It runs the PipeWire
main loop on a dedicated thread (all non-Send pw objects stay on it),
downmixes interleaved F32 to mono, and feeds the existing resampler -> VAD
-> consumer pipeline unchanged via the shared Cmd/AudioChunk protocol.

A small `Recorder` seam selects the backend: on Linux it prefers PipeWire
and falls back to the cpal/ALSA path if PipeWire setup fails; non-Linux
targets compile cpal-only and are unaffected. cpal stays fully compiled and
available as the fallback.

The MVP captures the default source (reproducing today's "default"
behaviour). PipeWire-native device enumeration/selection is left as a
follow-up (TODO in recorder_backend.rs, where a selected mic would map to a
node.name passed through TARGET_OBJECT).
@cjpais

cjpais commented Jul 23, 2026

Copy link
Copy Markdown
Owner

Thank you I will take a look at this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants