microdsp 0.1.3

DSP algorithms and utilities. Performant, embedded friendly and no_std compatible.
Documentation
use dev_helpers::rtrb;
use dev_helpers::{AudioHost, AudioProcessor};
use microdsp::sfnov::{HardKneeCompression, SpectralFluxNoveltyDetector};
use std::thread;
use std::time::Duration;

enum DetectorMessage {
    NoveltyValue(f32),
}

const WINDOW_SIZE: usize = 1024;

type DetectorType = SpectralFluxNoveltyDetector<HardKneeCompression>;
struct NoveltyDetectorProcessor {
    detector: DetectorType,
}

impl NoveltyDetectorProcessor {
    fn new() -> Self {
        NoveltyDetectorProcessor {
            detector: DetectorType::new(WINDOW_SIZE),
        }
    }
}

impl AudioProcessor<DetectorMessage> for NoveltyDetectorProcessor {
    fn process(
        &mut self,
        in_buffer: &[f32],
        _: &mut [f32],
        _: usize,
        to_main_thread: &mut rtrb::Producer<DetectorMessage>,
        _: &mut rtrb::Consumer<DetectorMessage>,
    ) -> bool {
        self.detector.process(in_buffer, |novelty| {
            let _ = to_main_thread.push(DetectorMessage::NoveltyValue(novelty.novelty()));
        });

        true
    }
}

struct PeakDetector {
    threshold: f32,
    is_armed: bool,
}

impl PeakDetector {
    fn new(threshold: f32) -> Self {
        PeakDetector {
            threshold,
            is_armed: true,
        }
    }

    fn process(&mut self, input: f32) -> bool {
        if input > self.threshold && self.is_armed {
            self.is_armed = false;
            return true;
        } else if input < self.threshold {
            self.is_armed = true;
        };
        false
    }
}

fn main() {
    let sample_rate = 44100.0;
    let mut audio_host = AudioHost::new(sample_rate, NoveltyDetectorProcessor::new());
    println!("Listening for sounds...");

    let poll_interval_ms = 30;
    let mut novelty_peak_detector = PeakDetector::new(0.4);

    loop {
        thread::sleep(Duration::from_millis(poll_interval_ms));

        loop {
            match audio_host.from_audio_thread.pop() {
                Ok(message) => match message {
                    DetectorMessage::NoveltyValue(value) => {
                        if novelty_peak_detector.process(value) {
                            println!("Onset detected (novelty {value})")
                        }
                    }
                },
                _ => {}
            }
        }
    }
}