car-voice 0.13.0

Voice I/O capability for CAR — mic capture, VAD, listener/speaker traits
Documentation
//! Channel-neutral microphone listener.

use crate::{Result, VoiceConfig, VoiceEvent};
use async_trait::async_trait;
use tokio::sync::mpsc;

/// A `Listener` owns the microphone, runs VAD, drives STT, and emits a stream
/// of [`VoiceEvent`]s.
///
/// Channels (CLI, Bevy GUI) consume the trait, not concrete impls — that
/// keeps the audio capture / VAD / STT pipeline pluggable and lets us swap
/// providers without touching the channels.
///
/// # Lifecycle
///
/// 1. Construct with a [`VoiceConfig`].
/// 2. Call [`Listener::start`] to acquire the device and begin emitting.
/// 3. Drain the returned receiver until you want to stop.
/// 4. Call [`Listener::stop`] to release the device.
///
/// # Cancellation and backpressure
///
/// The returned [`mpsc::Receiver`] is bounded, which gives natural
/// backpressure: if the consumer falls behind, the producer side either
/// blocks (preferred) or drops the oldest event (configurable per impl).
/// Implementations should document their drop policy.
///
/// End-of-stream is signalled by the sender being dropped (the receiver's
/// `recv()` will return `None`). This happens when:
///   - the caller drops the receiver (consumer-driven cancellation),
///   - the caller invokes [`Listener::stop`] (graceful shutdown),
///   - the underlying audio device disappears (error path — the impl should
///     log and emit a final event, then drop).
#[async_trait]
pub trait Listener: Send {
    /// Begin capturing audio from the configured input device. Returns a
    /// bounded receiver that the caller drains for [`VoiceEvent`]s.
    ///
    /// Implementations must be safe to call exactly once between `start` /
    /// [`stop`] cycles. Calling `start` while already running must return
    /// [`crate::VoiceError::AlreadyRunning`].
    async fn start(&mut self, config: VoiceConfig) -> Result<mpsc::Receiver<VoiceEvent>>;

    /// Release the audio device and stop emitting events. Calling `stop` when
    /// not running must return [`crate::VoiceError::NotRunning`]. Idempotent
    /// from the caller's perspective except for the error return.
    async fn stop(&mut self) -> Result<()>;

    /// True if the listener is currently capturing.
    fn is_running(&self) -> bool;
}