feat(audio): native PipeWire capture backend on Linux - #1774
Open
olosegres wants to merge 1 commit into
Open
Conversation
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).
Owner
|
Thank you I will take a look at this! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On Linux, microphone capture is pinned to cpal's ALSA host (
get_cpal_host()→HostId::Alsainaudio_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:"default"client. Handy therefore captures the raw, un-boosted signal and ends up dramatically quieter than every other app on the same mic (near-silent recordings, poor/empty transcriptions), with no in-app way to fix it: raising the source volume withwpctl/pavucontrol has no effect on Handy.wpctl/ pavucontrol / WirePlumber — Handy is invisible to all of that."default"on many PipeWire systems — the ALSA cpal host doesn't surface the graph's friendly per-source nodes — so the mic selector can't offer the real inputs.Change
Add a native PipeWire capture backend (
pipewire-rs0.10) that registers Handy's microphone as a real PipeWire node, so it shows up inwpctl statuswith its own settable volume and is routable per-app.pipewire_recorder.rs: runs the PipeWire main loop on a dedicated thread (all non-Sendpw objects stay on it), negotiates F32, downmixes to mono in the RTprocesscallback, and feeds the existing resampler → VAD → consumer pipeline unchanged via the sharedCmd/AudioChunkprotocol. No DSP is reimplemented.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.pipewireis added only under[target.'cfg(target_os = "linux")'.dependencies](featurev0_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 inwpctl statuswith 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 aTODOinRecorder::open, where a user-selected mic would map to anode.namepassed throughTARGET_OBJECT(already threaded throughPipeWireRecorder::open). Happy to iterate on device selection and feature-gating in review.