#[derive(Debug, Clone, Copy, PartialEq)]
pub struct BeatTiming {
pub time_seconds: f64,
pub strength: f32,
}
impl BeatTiming {
pub fn new(time_seconds: f64, strength: f32) -> Self {
Self {
time_seconds,
strength,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct BpmCandidate {
pub bpm: f32,
pub confidence: f32,
}
impl BpmCandidate {
pub fn new(bpm: f32, confidence: f32) -> Self {
Self { bpm, confidence }
}
pub fn in_range(&self, min: f32, max: f32) -> bool {
self.bpm >= min && self.bpm <= max
}
}
impl From<(f32, f32)> for BpmCandidate {
fn from((bpm, confidence): (f32, f32)) -> Self {
Self::new(bpm, confidence)
}
}
#[derive(Debug, Clone)]
pub struct BpmDetection {
candidates: Vec<BpmCandidate>,
beat_timings: Vec<BeatTiming>,
}
impl BpmDetection {
pub fn from_array(arr: [(f32, f32); 5]) -> Self {
let candidates = arr
.into_iter()
.filter(|(bpm, _)| *bpm > 0.0) .map(BpmCandidate::from)
.collect();
Self {
candidates,
beat_timings: Vec::new(),
}
}
pub fn with_beats(arr: [(f32, f32); 5], beat_timings: Vec<BeatTiming>) -> Self {
let candidates = arr
.into_iter()
.filter(|(bpm, _)| *bpm > 0.0)
.map(BpmCandidate::from)
.collect();
Self {
candidates,
beat_timings,
}
}
pub fn top_candidate(&self) -> Option<&BpmCandidate> {
self.candidates.first()
}
pub fn candidates(&self) -> &[BpmCandidate] {
&self.candidates
}
pub fn bpm(&self) -> Option<f32> {
self.top_candidate().map(|c| c.bpm)
}
pub fn in_range(&self, min: f32, max: f32) -> Vec<BpmCandidate> {
self.candidates
.iter()
.filter(|c| c.in_range(min, max))
.copied()
.collect()
}
pub fn beat_timings(&self) -> &[BeatTiming] {
&self.beat_timings
}
pub fn last_beat(&self) -> Option<&BeatTiming> {
self.beat_timings.last()
}
pub fn last_beat_interval(&self) -> Option<f64> {
if self.beat_timings.len() >= 2 {
let len = self.beat_timings.len();
Some(self.beat_timings[len - 1].time_seconds - self.beat_timings[len - 2].time_seconds)
} else {
None
}
}
}