//! # `chanora_audio`
//!
//! Audio subsystem promoted from `poc/audio-capture-playback-spike`
//! and wired against `chanora_protocol`'s voice channels.
//!
//! ## What's wired in this Beta
//!
//! * Default-input capture via `cpal` (DEC-011.1)
//! * Frame-aligned 20 ms / 48 kHz mono Opus encoding via `audiopus`
//! * Forward encoded frames to the protocol crate as `OutPacket`s
//! * Inbound voice packets fed to `tsclientlib::audio::AudioHandler`
//!   which owns Opus decode + per-client jitter buffer + mix
//! * Mixed f32 PCM pulled by the cpal output callback at 48 kHz stereo
//! * Push-to-talk: capture stream is permanently open; encoding is
//!   gated by an atomic `ptt_active` flag
//!
//! ## What's NOT wired in this Beta
//!
//! * AEC / AGC / NS / HPF DSP chain (DEC-007/008/009/010 — Beta+
//!   work; the toggles in `AudioEffects` are honoured by *naming*
//!   but the filters are no-ops)
//! * Mobile audio paths (DEC-011.1 desktop + Android proven; this
//!   integration is desktop-only for v0.2.0-beta.1)
//! * Hot-plug device-change handling
//! * Sample-rate adaptation if the device cannot do 48 kHz / mono in
//!   the format we request (returns `AudioError::StreamConfig`)
//! * Multi-channel speaker layouts beyond stereo

#![warn(missing_docs)]

mod engine;
pub mod ptt;
pub mod ptt_backends;

pub use engine::{AudioEngine, AudioEngineConfig};
pub use ptt::{
    AudioTransmitGate, MissedKeyUpWatchdog, PttBackendDescriptor, PttCapabilityLevel,
};
pub use ptt_backends::{
    select as select_ptt_backend, DesktopPttBackend, FocusedPttBackend, PttBackendError,
    PttBinding, PttInputClass,
};

use thiserror::Error;

/// Errors raised by the audio subsystem.
#[derive(Debug, Error)]
pub enum AudioError {
    /// Platform did not expose a usable default input device.
    #[error("no default input device")]
    NoInputDevice,
    /// Platform did not expose a usable default output device.
    #[error("no default output device")]
    NoOutputDevice,
    /// The audio backend rejected a stream configuration.
    #[error("stream config rejected: {0}")]
    StreamConfig(String),
    /// Opus codec init/encode/decode failure.
    #[error("opus: {0}")]
    Opus(String),
    /// A backend-specific failure surfaced without a typed mapping.
    #[error("audio backend: {0}")]
    Backend(String),
}

/// Audio-effect toggles. Defaults match DEC-007 (AEC),
/// DEC-008 (AGC), DEC-009 (NS), DEC-010 (HPF) — all enabled.
///
/// Note: in Beta v0.2.0-beta.1 the actual DSP filters are not yet
/// implemented; the struct is kept here as the public API surface so
/// later work can flip an internal flag without breaking callers.
#[derive(Debug, Clone, Copy)]
pub struct AudioEffects {
    /// Acoustic echo cancellation (DEC-007).
    pub aec: bool,
    /// Automatic gain control (DEC-008).
    pub agc: bool,
    /// Noise suppression (DEC-009).
    pub noise_suppression: bool,
    /// High-pass filter (DEC-010).
    pub high_pass: bool,
}

impl Default for AudioEffects {
    fn default() -> Self {
        Self {
            aec: true,
            agc: true,
            noise_suppression: true,
            high_pass: true,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn defaults_match_decisions() {
        let e = AudioEffects::default();
        assert!(e.aec && e.agc && e.noise_suppression && e.high_pass);
    }
}
