use std::sync::mpsc::{Receiver, Sender}; use std::sync::Arc; use std::sync::atomic::AtomicUsize; use thiserror::Error; /// Playback output channel count. Capture/encode/network remain mono; only the /// listener-side playout bus is stereo. pub const PLAYBACK_CHANNELS: usize = 2; /// Target depth of the playback ring buffer, in interleaved samples (48kHz /// stereo). /// /// The playout chain is paced to keep the ring near this level: production is /// driven by how fast PipeWire actually drains the ring (the hardware clock), /// not by a fixed software timer — which is what eliminates the producer/ /// consumer beat that otherwise churns ~20% of audio into drops + silence. /// 5760 = 60ms = 3×20ms stereo frames, comfortably above the 2048-frame max quantum /// so a single hardware pull can never empty the ring before the mixer refills. pub const PLAYBACK_TARGET_SAMPLES: usize = 2880 * PLAYBACK_CHANNELS; #[derive(Error, Debug)] pub enum AudioError { #[error("Failed to initialize audio backend: {0}")] Init(String), #[error("Audio device error: {0}")] Device(String), #[error("Stream error: {0}")] Stream(String), #[error("Audio buffer overflow/underflow")] BufferError, #[error("Other audio error: {0}")] Other(String), } pub trait AudioBackend: Send + Sync { /// Starts capturing raw PCM audio from the input device (microphone), /// sending chunks of samples (e.g. `Vec`) to the provided Sender. fn start_capture(&self, tx: Sender>, target_node: Option) -> Result<(), AudioError>; /// Starts playing back raw PCM audio to the output device (speaker), /// reading mixed/incoming chunks of samples from the provided Receiver. /// /// `ring_fill` is updated with the playback ring's current occupancy (in /// samples) as the device drains and the worker fills it. The caller (the /// mixer) reads it to pace production to the hardware clock — produce only /// while the ring is below [`PLAYBACK_TARGET_SAMPLES`] — instead of on a /// fixed timer that beats against the device quantum. fn start_playback( &self, rx: Receiver>, target_node: Option, ring_fill: Arc, ) -> Result<(), AudioError>; /// Stops both capture and playback streams. fn stop(&self) -> Result<(), AudioError>; } pub mod echo_cancel; pub mod eq; pub mod gate; pub mod limiter; pub mod multitrack; pub mod pan; pub mod pipewire_impl; pub mod pw_cli; pub mod recorder;