respeaker 0.1.0

USB control library for Seeed ReSpeaker Mic Array v2.0 (LEDs + XVF-3000 DSP tuning)
Documentation
//! XVF-3000 DSP parameters from Seeed's `tuning.py`.

/// Value type stored on the device for a parameter.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ParamKind {
    Int,
    Float,
}

/// Access mode for a DSP parameter.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Access {
    ReadWrite,
    ReadOnly,
}

/// Metadata for one XVF-3000 tuning parameter.
#[derive(Clone, Copy, Debug)]
pub struct Parameter {
    pub name: &'static str,
    /// USB wIndex / parameter group id.
    pub id: u16,
    /// Field offset within the group.
    pub offset: u8,
    pub kind: ParamKind,
    pub max: f64,
    pub min: f64,
    pub access: Access,
    pub info: &'static str,
}

/// Runtime value returned from / written to the device.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ParamValue {
    Int(i32),
    Float(f64),
}

impl ParamValue {
    pub fn as_i32(self) -> Option<i32> {
        match self {
            Self::Int(v) => Some(v),
            Self::Float(v) => Some(v as i32),
        }
    }

    pub fn as_f64(self) -> f64 {
        match self {
            Self::Int(v) => v as f64,
            Self::Float(v) => v,
        }
    }
}

impl std::fmt::Display for ParamValue {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Int(v) => write!(f, "{v}"),
            Self::Float(v) => write!(f, "{v}"),
        }
    }
}

macro_rules! params {
    ($(($name:literal, $id:expr, $off:expr, $kind:ident, $max:expr, $min:expr, $acc:ident, $info:literal)),* $(,)?) => {
        pub static PARAMETERS: &[Parameter] = &[
            $(
                Parameter {
                    name: $name,
                    id: $id,
                    offset: $off,
                    kind: ParamKind::$kind,
                    max: $max,
                    min: $min,
                    access: Access::$acc,
                    info: $info,
                },
            )*
        ];
    };
}

params! {
    ("AECFREEZEONOFF", 18, 7, Int, 1.0, 0.0, ReadWrite, "Adaptive Echo Canceler updates inhibit. 0=enabled 1=freeze"),
    ("AECNORM", 18, 19, Float, 16.0, 0.25, ReadWrite, "Limit on norm of AEC filter coefficients"),
    ("AECPATHCHANGE", 18, 25, Int, 1.0, 0.0, ReadOnly, "AEC Path Change Detection"),
    ("RT60", 18, 26, Float, 0.9, 0.25, ReadOnly, "Current RT60 estimate in seconds"),
    ("HPFONOFF", 18, 27, Int, 3.0, 0.0, ReadWrite, "High-pass Filter: 0=OFF 1=70Hz 2=125Hz 3=180Hz"),
    ("RT60ONOFF", 18, 28, Int, 1.0, 0.0, ReadWrite, "RT60 Estimation for AES"),
    ("AECSILENCELEVEL", 18, 30, Float, 1.0, 1e-9, ReadWrite, "Threshold for signal detection in AEC"),
    ("AECSILENCEMODE", 18, 31, Int, 1.0, 0.0, ReadOnly, "AEC far-end silence detection status"),
    ("AGCONOFF", 19, 0, Int, 1.0, 0.0, ReadWrite, "Automatic Gain Control. 0=OFF 1=ON"),
    ("AGCMAXGAIN", 19, 1, Float, 1000.0, 1.0, ReadWrite, "Maximum AGC gain factor"),
    ("AGCDESIREDLEVEL", 19, 2, Float, 0.99, 1e-8, ReadWrite, "Target power level of the output signal"),
    ("AGCGAIN", 19, 3, Float, 1000.0, 1.0, ReadWrite, "Current AGC gain factor"),
    ("AGCTIME", 19, 4, Float, 1.0, 0.1, ReadWrite, "Ramps-up / down time-constant in seconds"),
    ("CNIONOFF", 19, 5, Int, 1.0, 0.0, ReadWrite, "Comfort Noise Insertion"),
    ("FREEZEONOFF", 19, 6, Int, 1.0, 0.0, ReadWrite, "Adaptive beamformer updates"),
    ("STATNOISEONOFF", 19, 8, Int, 1.0, 0.0, ReadWrite, "Stationary noise suppression"),
    ("GAMMA_NS", 19, 9, Float, 3.0, 0.0, ReadWrite, "Over-subtraction factor of stationary noise"),
    ("MIN_NS", 19, 10, Float, 1.0, 0.0, ReadWrite, "Gain-floor for stationary noise suppression"),
    ("NONSTATNOISEONOFF", 19, 11, Int, 1.0, 0.0, ReadWrite, "Non-stationary noise suppression"),
    ("GAMMA_NN", 19, 12, Float, 3.0, 0.0, ReadWrite, "Over-subtraction factor of non-stationary noise"),
    ("MIN_NN", 19, 13, Float, 1.0, 0.0, ReadWrite, "Gain-floor for non-stationary noise suppression"),
    ("ECHOONOFF", 19, 14, Int, 1.0, 0.0, ReadWrite, "Echo suppression"),
    ("GAMMA_E", 19, 15, Float, 3.0, 0.0, ReadWrite, "Over-subtraction factor of echo (direct/early)"),
    ("GAMMA_ETAIL", 19, 16, Float, 3.0, 0.0, ReadWrite, "Over-subtraction factor of echo (tail)"),
    ("GAMMA_ENL", 19, 17, Float, 5.0, 0.0, ReadWrite, "Over-subtraction factor of non-linear echo"),
    ("NLATTENONOFF", 19, 18, Int, 1.0, 0.0, ReadWrite, "Non-Linear echo attenuation"),
    ("NLAEC_MODE", 19, 20, Int, 2.0, 0.0, ReadWrite, "Non-Linear AEC training mode"),
    ("SPEECHDETECTED", 19, 22, Int, 1.0, 0.0, ReadOnly, "Speech detection status"),
    ("FSBUPDATED", 19, 23, Int, 1.0, 0.0, ReadOnly, "FSB Update Decision"),
    ("FSBPATHCHANGE", 19, 24, Int, 1.0, 0.0, ReadOnly, "FSB Path Change Detection"),
    ("TRANSIENTONOFF", 19, 29, Int, 1.0, 0.0, ReadWrite, "Transient echo suppression"),
    ("VOICEACTIVITY", 19, 32, Int, 1.0, 0.0, ReadOnly, "VAD voice activity status"),
    ("STATNOISEONOFF_SR", 19, 33, Int, 1.0, 0.0, ReadWrite, "Stationary noise suppression for ASR"),
    ("NONSTATNOISEONOFF_SR", 19, 34, Int, 1.0, 0.0, ReadWrite, "Non-stationary noise suppression for ASR"),
    ("GAMMA_NS_SR", 19, 35, Float, 3.0, 0.0, ReadWrite, "Over-subtraction factor of stationary noise for ASR"),
    ("GAMMA_NN_SR", 19, 36, Float, 3.0, 0.0, ReadWrite, "Over-subtraction factor of non-stationary noise for ASR"),
    ("MIN_NS_SR", 19, 37, Float, 1.0, 0.0, ReadWrite, "Gain-floor for stationary noise suppression for ASR"),
    ("MIN_NN_SR", 19, 38, Float, 1.0, 0.0, ReadWrite, "Gain-floor for non-stationary noise suppression for ASR"),
    ("GAMMAVAD_SR", 19, 39, Float, 1000.0, 0.0, ReadWrite, "Threshold for voice activity detection"),
    ("DOAANGLE", 21, 0, Int, 359.0, 0.0, ReadOnly, "DOA angle in degrees"),
}

/// Look up a parameter by name (case-insensitive).
pub fn find_parameter(name: &str) -> Option<&'static Parameter> {
    PARAMETERS
        .iter()
        .find(|p| p.name.eq_ignore_ascii_case(name))
}