use std::cell::Cell;
pub(crate) mod lpf;
pub(crate) mod spectrum;
pub mod window_stats;
#[derive(Debug)]
pub struct AnalysisState {
sampling_rate: u32,
ms_per_sample: f32,
beat_time_ms: Cell<u32>,
time_ms: Cell<u32>,
last_beat_timestamp: Cell<u32>,
}
impl AnalysisState {
pub fn new(sampling_rate: u32) -> Self {
Self {
sampling_rate,
ms_per_sample: 1.0 / sampling_rate as f32 * 1000.0,
beat_time_ms: Cell::new(0),
time_ms: Cell::new(0),
last_beat_timestamp: Cell::new(0),
}
}
#[inline(always)]
pub fn update_time(&self, frame_len: usize) {
let ms_of_frame = self.ms_per_sample * frame_len as f32;
self.beat_time_ms.set(
self.time_ms.get() + (ms_of_frame / 2.0) as u32,
);
self.time_ms.set(self.time_ms.get() + ms_of_frame as u32);
}
#[inline(always)]
pub fn update_last_discovered_beat_timestamp(&self) {
self.last_beat_timestamp.replace(self.beat_time_ms.get());
}
#[inline(always)]
pub const fn sampling_rate(&self) -> u32 {
self.sampling_rate
}
#[inline(always)]
pub fn last_beat_timestamp(&self) -> u32 {
self.last_beat_timestamp.get()
}
#[inline(always)]
pub fn beat_time_ms(&self) -> u32 {
self.beat_time_ms.get()
}
#[inline(always)]
pub fn time_ms(&self) -> u32 {
self.time_ms.get()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_analysis_state_get_relative_time_ms() {
let state = AnalysisState::new(44100);
state.update_time(1024);
assert_eq!(
23 / 2,
state.beat_time_ms(),
"Must return timestamp in middle of first window"
);
assert_eq!(
23,
state.time_ms(),
"Must return timestamp at end of first window"
);
state.update_time(1024);
assert_eq!(
(23.0 * 1.5) as u32,
state.beat_time_ms(),
"Must return timestamp in middle of second window"
);
assert_eq!(
46,
state.time_ms(),
"Must return timestamp at end of second window"
);
state.update_time(317);
assert_eq!(
(46 + (317.0 / 2.0 * 1.0 / 44100.0 * 1000.0) as u32),
state.beat_time_ms(),
"Must return timestamp in middle of third window"
);
assert_eq!(
(46 + (317.0 / 1.0 / 44100.0 * 1000.0) as u32),
state.time_ms(),
"Must return timestamp at end of third window"
);
}
}