lasprs 0.14.2

Library for Acoustic Signal Processing (Rust edition, with optional Python bindings via pyo3)
use std::ops::Deref;

use super::*;
use crate::*;
use serde::{Deserialize, Serialize};
use snafu::{ResultExt, ensure};

type Result<T> = std::result::Result<T, SiggenError>;

/// High level descriptor of a signal source. This provides all information to
/// configure a signal source.
#[cfg_attr(
    feature = "python-bindings",
    gen_stub_pyclass_complex_enum,
    pyclass(from_py_object)
)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum SourceDescriptor {
    /// No signal / silence
    Silence {},
    /// White noise source.
    WhiteNoise {
        /// Amount of seconds silence, after amount of seconds with noise
        interrupt_time: Option<InterruptTime>,
    },
    /// Pink noise source.
    PinkNoise {
        /// Amount of seconds silence, after amount of seconds with noise
        interrupt_time: Option<InterruptTime>,

        /// Frequency point after which the roll-off starts with 3dB/octave
        rolloffPoint: StrictlyPositive,
    },
    /// Sine wave source.
    Sine {
        /// Frequency in Hz
        frequency: Positive,
    },
    /// Sine frequency sweep
    Sweep {
        /// Settings for the sweep
        settings: SweepSettings,
    },
}

impl Default for SourceDescriptor {
    fn default() -> Self {
        Self::Silence {}
    }
}

impl SourceDescriptor {
    /// Create sine sweep signal generator source
    ///
    /// # Args
    ///
    /// - `fl` - Lower frequency \[Hz\]
    /// - `fu` - Upper frequency \[Hz\]
    /// - `sweep_time` - The duration of a single sweep \[s\]
    /// - `sweep_type` - The type of the sweep, see [SweepType].
    /// - `quiet_time` - Optional: time of silence after one sweep and start of the next \[s\]
    pub fn newSweep(
        fl: Flt,
        fu: Flt,
        sweep_time: Flt,
        sweep_type: SweepType,
        quiet_time: Option<Flt>,
        amplitude_modulation: Option<Vec<(Positive, Positive)>>,
    ) -> Result<Self> {
        let params = SweepSettings::new(
            fl,
            fu,
            sweep_time,
            sweep_type,
            quiet_time,
            amplitude_modulation,
        )?;
        Ok(SourceDescriptor::Sweep { settings: params })
    }

    /// Create a white noise signal source
    ///
    /// # Args
    ///
    /// - `interrupt_period` - when given AND > 0, this turns on and off the
    ///   noise source with periods given by the value, in \[s\].
    pub fn newWhiteNoise(interrupt_period: Option<Flt>) -> Result<Self> {
        let interrupt_time = if let Some(interrupt_period) = interrupt_period {
            let t = interrupt_period
                .try_into()
                .context(ParameterOutOfRangeSnafu {
                    parameter: "interrupt_period",
                })?;
            Some(InterruptTime { interrupt_time: t })
        } else {
            None
        };

        Ok(SourceDescriptor::WhiteNoise { interrupt_time })
    }
}

#[cfg_attr(feature = "python-bindings", gen_stub_pyclass, pyclass(from_py_object))]
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
/// Specification for the interrupt time for a noise source
pub struct InterruptTime {
    interrupt_time: Bounded<1, 100>,
}

#[cfg(feature = "python-bindings")]
#[cfg_attr(feature = "python-bindings", gen_stub_pymethods, pymethods)]
impl InterruptTime {
    #[new]
    fn new_py(interrupt_time: Flt) -> PyResult<Self> {
        let interrupt_time = interrupt_time
            .try_into()
            .context(ParameterOutOfRangeSnafu {
                parameter: "interrupt_time",
            })?;
        Ok(Self { interrupt_time })
    }
}

impl Deref for InterruptTime {
    type Target = Flt;

    fn deref(&self) -> &Self::Target {
        &self.interrupt_time
    }
}
impl From<InterruptTime> for Flt {
    fn from(val: InterruptTime) -> Self {
        *val.interrupt_time
    }
}

// #[cfg(feature = "python-bindings")]
// #[cfg_attr(feature = "python-bindings", pymethods)]
// impl SourceDescriptor {
//     #[staticmethod]
//     #[pyo3(name = "newSine")]
//     fn newSine_py(fs: Flt, freq: Flt) -> PyResult<Source> {
//         Ok(Self::newSine(fs, freq)?)
//     }
//     #[pyo3(name = "newSilence")]
//     #[staticmethod]
//     fn newSilence_py() -> Source {
//         Self::newSilence()
//     }
//     #[staticmethod]
//     #[pyo3(name = "newWhiteNoise", signature=(interrupt_period=None))]
//     fn newWhiteNoise_py(interrupt_period: Option<Flt>) -> Source {
//         Self::newWhiteNoise(DUMMY_SAMPLING_FREQ, interrupt_period)
//     }
//     #[staticmethod]
//     #[pyo3(name = "newPinkNoise", signature=(interrupt_period=None))]
//     fn newPinkNoise_py(interrupt_period: Option<Flt>) -> Source {
//         Self::newPinkNoise(DUMMY_SAMPLING_FREQ, interrupt_period)
//     }
//     #[staticmethod]
//     #[pyo3(name = "newSweep")]
//     fn newSweep_py(
//         fs: Flt,
//         fl: Flt,
//         fu: Flt,
//         sweep_time: Flt,
//         quiet_time: Flt,
//         sweep_type: SweepType,
//     ) -> PyResult<Source> {
//         Ok(Self::newSweep(
//             fs, fl, fu, sweep_time, quiet_time, sweep_type,
//         )?)
//     }
// }