flexaudio 0.7.0

Flexible cross-platform audio capture (microphone, system loopback, per-process) for Linux, Windows, and macOS with a unified API.
docs.rs failed to build flexaudio-0.7.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

flexaudio

Flexible, cross-platform audio capture for Rust. Capture from microphones, system output (loopback), individual processes, and microphone + system mix on Linux, Windows, and macOS through one unified API.

use flexaudio::{open, StreamConfig, SourceKind};

let mut stream = open(StreamConfig {
    kind: SourceKind::Mic,
    ..Default::default()
})?;
stream.start()?;
while let Some(chunk) = stream.poll_chunk() {
    let _ = chunk; // interleaved f32, plus frames / peak / rms / seq
}
stream.stop();
# Ok::<(), flexaudio::Error>(())

Capability matrix

Source Linux Windows macOS
Microphone ✅ cpal/ALSA ✅ cpal/WASAPI ✅ cpal/CoreAudio
System output ✅ PipeWire ✅ WASAPI loopback ✅ CoreAudio taps
Per-process ✅ PipeWire ✅ WASAPI process ✅ CoreAudio taps

Highlights

  • open(StreamConfig) selects the right backend by source + OS.
  • Stream::poll_chunk / poll_event — simple pull loop; no callbacks required.
  • Stream::switch_source — hot-swap source without stopping the stream.
  • devices() enumerates devices; watch_devices() reports Linux PipeWire system output additions, removals and default changes using the same output IDs as devices(). Microphone and default-microphone changes are not reported: CPAL microphone IDs cannot be resolved from PipeWire nodes. Windows/macOS return a no-op watcher. Discovery returns a complete inventory or an error, including when PipeWire is unavailable. Linux microphone entries use only CPAL device-name IDs; PipeWire source-node IDs are omitted because the microphone backend cannot open them.
  • processes() lists audio output session/stream owners on Linux/Windows and Core Audio processes on macOS, including input-only processes. Idle/stopped processes are included and the caller is excluded; use pid as target_pid for capture and is_output_active to check current playback.
  • SourceKind::Mix combines mic + system audio. Select each side with mix_mic_device_id / mix_system_device_id (None uses defaults), and set mix_mic_gain / mix_system_gain before mixing; gain applies afterward. device_id is ignored for Mix.
  • exclude_pids combines with exclude_self for system capture and the system side of Mix. Windows excludes one process-tree root (self when exclude_self is true, otherwise the first PID); every entry must equal that root, and distinct PIDs are rejected. macOS resolves audio objects once at start and honors device selection. Linux matches exact PIDs (application.process.id for pulse-proxied streams, pipewire.sec.pid for native clients), without descendants. Linux and Windows ignore system-device selection during exclusion.
  • Output is normalized interleaved f32 at a sample rate / channel count you choose (two-stage resampling internally).

Capability discovery

capabilities() returns a read-only snapshot of source backends and availability, OS version when known, PID/exclusion scopes and limits, output-device selection semantics, watcher support, and optional rendering side effects. It opens no capture or render streams, enumerates no microphones, requests no recording permission, and plays no audio.

let support = flexaudio::capabilities();
println!("{:?}", support.sources.system.availability);
println!("{:?}", support.system_exclusions);

Availability::Supported describes backend prerequisites, not permission or a usable device. Availability::Unknown { error } retains a typed prerequisite query failure; do not assume support or confirmed unavailability. On Linux, missing PipeWire sockets are unavailable; failed socket inspection, connection or server sync is unknown. The bounded query can wait for the audio service; run it on a worker thread in GUI applications.

Capture open options

Use open_with_options to select optional backend behavior without changing StreamConfig. Existing open calls keep the default behavior.

use flexaudio::{open_with_options, OpenOptions, SourceKind, StreamConfig};

let config = StreamConfig {
    kind: SourceKind::SystemLoopback,
    exclude_self: false,
    ..Default::default()
};
let mut stream = open_with_options(
    config.clone(),
    OpenOptions::new().windows_classic_loopback_keepalive(false),
)?;
stream.start()?;
stream.switch_source_with_options(config, OpenOptions::new())?;
stream.stop();
# Ok::<(), flexaudio::Error>(())

Windows classic loopback keepalive is enabled by default and renders silence to keep idle system capture delivering packets. It also applies to the system lane of Mix without PID exclusions. Other platforms and process/exclusion paths ignore it. Disabling it means idle classic loopback delivers no packets: the 2 s watchdog reports Event::StreamStalled and reopens repeatedly until audio plays.

Stream::open_options() reads the current settings. switch_source_with_options replaces them together with the source; ordinary switch_source retains them. Construction/startup failures retain the previous settings.

Install

cargo add flexaudio@0.4

Permissions

Audio capture requires user consent: macOS TCC (kTCCServiceAudioCapture, add NSAudioCaptureUsageDescription to Info.plist), the Windows Microphone privacy setting, and a running PipeWire session on Linux for system/process capture. See the workspace README.

On macOS, system/process loopback (Core Audio process taps) requires macOS 14.4 or later.

MSRV

Rust 1.85.

License

MIT © 2026 tubome / Studio Sadola. Third-party notices: THIRD_PARTY_NOTICES.md.