Skip to main content

pipecrab_audio_cpal/
lib.rs

1//! pipecrab-audio-cpal: the [`cpal`]-backed audio backend for pipecrab.
2//!
3//! [`CpalSource`] captures from an input device and [`CpalSink`] plays to an
4//! output device, both implementing the platform-neutral
5//! [`AudioSource`](pipecrab_audio::AudioSource) /
6//! [`AudioSink`](pipecrab_audio::AudioSink) traits — so a pipeline drives audio
7//! I/O without ever naming cpal. Both are opened from one shared [`CpalConfig`]
8//! (which device per side, plus chunk/buffer sizing); [`input_device_names`] /
9//! [`output_device_names`] enumerate the choices for
10//! [`DeviceSelection::Name`].
11//!
12//! A native backend (macOS, Windows, Linux, iOS, Android). The browser/wasm
13//! audio path is a separate future crate, not cpal: cpal's wasm backend runs on
14//! the main thread and isn't the intended web path, so this crate is not built
15//! for `wasm32`.
16//!
17//! # The real-time boundary
18//!
19//! cpal's device callbacks run on a real-time thread that must never block,
20//! allocate, or lock. Each callback only moves `f32` samples across a lock-free
21//! [`rtrb`] ring and wakes a `futures::task::AtomicWaker`; the async side
22//! (`next_chunk` / `play`) polls the ring and registers that waker. Waking from
23//! the callback is a pragmatic simplification — glitch-free at these ~20 ms
24//! buffer sizes; a strict wait-free bridge is deferred.
25//!
26//! The [`AudioSource`](pipecrab_audio::AudioSource) /
27//! [`AudioSink`](pipecrab_audio::AudioSink) trait is `Send`, but a `cpal::Stream`
28//! is `!Send`. So the stream is built and parked on a dedicated owning thread
29//! (see `stream`), and [`CpalSource`] / [`CpalSink`] hold only the `Send` ring
30//! end — a server can spawn one pump per session.
31//!
32//! # Format & timing
33//!
34//! Capture and playback run at their device's default sample rate (no
35//! resampling — a shared-clock same-device setup keeps the rates matched), mono
36//! end to end: multi-channel input is downmixed, mono output is duplicated
37//! across the device's channels. Chunk size and ring depth come from
38//! [`CpalConfig`].
39#![forbid(unsafe_code)]
40#![warn(missing_docs)]
41
42mod bridge;
43mod config;
44mod sink;
45mod source;
46mod stream;
47
48pub use config::{CpalConfig, DeviceSelection};
49pub use sink::{CpalSink, output_device_names};
50pub use source::{CpalSource, input_device_names};