oscillation 0.1.1

Oscillators and a collection of waveforms for real-time usage.
Documentation
use num_traits::{Euclid, Float, FloatConst};

use crate::Wavetable;

use super::Waveform;

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
pub struct Square;

impl<F> Waveform<F> for Square
where
    F: Float + FloatConst + Euclid
{
    fn waveform(&self, mut theta: F) -> F
    {
        let one = F::one();
        let pi = F::PI();
        let tau = F::TAU();

        theta = theta.rem_euclid(&tau);
        if theta < pi {-one} else {one}
    }
    fn waveform_with_dtc(&self, mut theta: F, mut duty_cycle: F) -> F
    {
        let zero = F::zero();
        let one = F::one();
        let tau = F::TAU();

        duty_cycle = duty_cycle.clamp(zero, one);
        let d = tau*duty_cycle;
        theta = theta.rem_euclid(&tau);
        if theta < d {-one} else {one}
    }

    fn wavetable<const N: usize>(&self) -> Option<Wavetable<F, N>>
    {
        let zero = F::zero();
        let pi = F::PI();

        let frac_two_pi = F::FRAC_2_PI();

        Some(Wavetable::from_fn(zero, |m| {
            let n = F::from(m + 1).unwrap();
            let g = frac_two_pi/n;

            let dn = pi*n;
            let (s, c) = dn.sin_cos();
            (
                -g*s,
                g*c - g
            )
        }))
    }

    fn wavetable_with_dtc<const N: usize>(&self, duty_cycle: F) -> Option<Wavetable<F, N>>
    {
        let zero = F::zero();
        let one = F::one();
        let pi = F::PI();
        let tau = F::TAU();

        let frac_two_pi = F::FRAC_2_PI();

        let d = tau*duty_cycle.clamp(zero, one);

        Some(Wavetable::from_fn(-(d - pi)/pi, |m| {
            let n = F::from(m + 1).unwrap();
            let g = frac_two_pi/n;

            let dn = d*n;
            let (s, c) = dn.sin_cos();
            (
                -g*s,
                g*c - g
            )
        }))
    }
}

#[cfg(test)]
mod test
{
    use core::error::Error;

    use super::Square;

    #[test]
    fn it_works() -> Result<(), Box<dyn Error>>
    {
        crate::tests::print_waveform(Square)
    }
}