use std::sync::Arc; use std::sync::atomic::AtomicUsize; use std::sync::mpsc::{Receiver, Sender}; 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 clip_player; pub mod eq; pub mod gate; pub mod limiter; pub mod multitrack; pub mod pan; // Linear resamplers used by the Windows/cpal backend (W4). Platform-neutral and // pure, so it builds (and its tests run) everywhere even though only the cpal // backend wires it in. #[cfg(windows)] pub mod cpal_impl; #[cfg(target_os = "linux")] pub mod echo_cancel; #[cfg(target_os = "linux")] pub mod pipewire_impl; #[cfg(target_os = "linux")] pub mod pw_cli; pub mod recorder; pub mod resample; /// A selectable audio device for the input/output pickers. `name` is the stable /// identifier the backend uses to request the device (`target_node`); /// `description` is the human-facing label shown in the UI. The two may be equal /// (cpal/WASAPI) or differ (PipeWire node name vs. description). #[derive(Debug, Clone, PartialEq, Eq)] pub struct AudioDevice { pub name: String, pub description: String, pub is_input: bool, } impl std::fmt::Display for AudioDevice { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.description) } } // Enumerate audio input/output devices for the pickers (sorted by description), // returning the same `AudioDevice` shape regardless of platform: PipeWire // (`pw-cli`) on Linux, cpal/WASAPI on Windows. #[cfg(windows)] pub use cpal_impl::enumerate_audio_devices; #[cfg(target_os = "linux")] pub use pw_cli::enumerate_audio_devices; /// The audio backend implementation for the current platform. /// /// The whole app constructs and threads this alias (via /// `PlatformAudioBackend::new()`) rather than any concrete backend type, so /// platform selection lives entirely here. Both implementations satisfy the /// [`AudioBackend`] trait, which is the only interface the core talks to. /// /// - Linux → PipeWire ([`pipewire_impl::PipeWireBackend`]). /// - Windows → cpal/WASAPI ([`cpal_impl::CpalBackend`]). #[cfg(target_os = "linux")] pub type PlatformAudioBackend = pipewire_impl::PipeWireBackend; #[cfg(windows)] pub type PlatformAudioBackend = cpal_impl::CpalBackend;