1#[derive(Debug, Clone, Copy, PartialEq)]
5pub struct BeatTiming {
6 pub time_seconds: f64,
8 pub strength: f32,
10}
11
12impl BeatTiming {
13 pub fn new(time_seconds: f64, strength: f32) -> Self {
15 Self {
16 time_seconds,
17 strength,
18 }
19 }
20}
21
22#[derive(Debug, Clone, Copy, PartialEq)]
24pub struct BpmCandidate {
25 pub bpm: f32,
27 pub confidence: f32,
29}
30
31impl BpmCandidate {
32 pub fn new(bpm: f32, confidence: f32) -> Self {
34 Self { bpm, confidence }
35 }
36
37 pub fn in_range(&self, min: f32, max: f32) -> bool {
39 self.bpm >= min && self.bpm <= max
40 }
41}
42
43impl From<(f32, f32)> for BpmCandidate {
44 fn from((bpm, confidence): (f32, f32)) -> Self {
45 Self::new(bpm, confidence)
46 }
47}
48
49#[derive(Debug, Clone)]
51pub struct BpmDetection {
52 candidates: Vec<BpmCandidate>,
53 beat_timings: Vec<BeatTiming>,
55}
56
57impl BpmDetection {
58 pub fn from_array(arr: [(f32, f32); 5]) -> Self {
60 let candidates = arr
61 .into_iter()
62 .filter(|(bpm, _)| *bpm > 0.0) .map(BpmCandidate::from)
64 .collect();
65 Self {
66 candidates,
67 beat_timings: Vec::new(),
68 }
69 }
70
71 pub fn with_beats(arr: [(f32, f32); 5], beat_timings: Vec<BeatTiming>) -> Self {
73 let candidates = arr
74 .into_iter()
75 .filter(|(bpm, _)| *bpm > 0.0)
76 .map(BpmCandidate::from)
77 .collect();
78 Self {
79 candidates,
80 beat_timings,
81 }
82 }
83
84 pub fn top_candidate(&self) -> Option<&BpmCandidate> {
86 self.candidates.first()
87 }
88
89 pub fn candidates(&self) -> &[BpmCandidate] {
91 &self.candidates
92 }
93
94 pub fn bpm(&self) -> Option<f32> {
96 self.top_candidate().map(|c| c.bpm)
97 }
98
99 pub fn in_range(&self, min: f32, max: f32) -> Vec<BpmCandidate> {
101 self.candidates
102 .iter()
103 .filter(|c| c.in_range(min, max))
104 .copied()
105 .collect()
106 }
107
108 pub fn beat_timings(&self) -> &[BeatTiming] {
110 &self.beat_timings
111 }
112
113 pub fn last_beat(&self) -> Option<&BeatTiming> {
115 self.beat_timings.last()
116 }
117
118 pub fn last_beat_interval(&self) -> Option<f64> {
120 if self.beat_timings.len() >= 2 {
121 let len = self.beat_timings.len();
122 Some(self.beat_timings[len - 1].time_seconds - self.beat_timings[len - 2].time_seconds)
123 } else {
124 None
125 }
126 }
127}