1mod audio;
2mod inference;
3mod mel;
4mod output;
5mod postprocessing;
6mod runtime;
7
8use std::path::Path;
9use std::time::Duration;
10
11use anyhow::Result;
12
13pub use audio::{load_audio, AudioData};
14pub use output::{beat_counts, calculate_bpm};
15pub use runtime::{ort::OrtRuntime, rten::RtenRuntime, Model, Runtime, Tensor};
16
17use inference::BeatPredictor;
18use mel::MelExtractor;
19use postprocessing::PeakPicker;
20
21const TARGET_SAMPLE_RATE: u32 = 22050;
23
24#[derive(Debug, Clone)]
26pub struct BeatAnalysis {
27 pub beats: Vec<f32>,
29 pub downbeats: Vec<f32>,
31 pub mel: Tensor,
33 pub beat_logits: Vec<f32>,
35 pub downbeat_logits: Vec<f32>,
37}
38
39pub struct BeatThis<M: Model> {
45 mel: MelExtractor<M>,
46 predictor: BeatPredictor<M>,
47 peak_picker: PeakPicker,
48}
49
50#[derive(Debug, Clone)]
52pub struct AnalysisTiming {
53 pub mel: Duration,
54 pub predict: Duration,
55 pub decode: Duration,
56}
57
58#[derive(Debug, Clone)]
60pub struct TimedAnalysis {
61 pub analysis: BeatAnalysis,
62 pub timing: AnalysisTiming,
63}
64
65impl<M: Model> BeatThis<M> {
66 pub fn new<R: Runtime<Model = M>>(
72 runtime: &R,
73 mel_model_path: &Path,
74 beat_model_path: &Path,
75 ) -> Result<Self> {
76 let mel_model = runtime.load_model(mel_model_path)?;
77 let beat_model = runtime.load_model(beat_model_path)?;
78
79 Ok(Self {
80 mel: MelExtractor::new(mel_model),
81 predictor: BeatPredictor::new(beat_model),
82 peak_picker: PeakPicker::default(),
83 })
84 }
85
86 pub fn from_models(mel_model: M, beat_model: M) -> Self {
91 Self {
92 mel: MelExtractor::new(mel_model),
93 predictor: BeatPredictor::new(beat_model),
94 peak_picker: PeakPicker::default(),
95 }
96 }
97
98 pub fn beat_model_mut(&mut self) -> &mut M {
102 self.predictor.model_mut()
103 }
104
105 pub fn analyze_audio(&mut self, samples: &[f32], sample_rate: u32) -> Result<BeatAnalysis> {
110 Ok(self.analyze_audio_timed(samples, sample_rate)?.analysis)
111 }
112
113 pub fn analyze_audio_timed(
115 &mut self,
116 samples: &[f32],
117 sample_rate: u32,
118 ) -> Result<TimedAnalysis> {
119 let samples = if sample_rate != TARGET_SAMPLE_RATE {
120 audio::resample(samples.to_vec(), sample_rate, TARGET_SAMPLE_RATE)?
121 } else {
122 samples.to_vec()
123 };
124
125 let t = std::time::Instant::now();
126 let mel = self.mel.extract(&samples)?;
127 let mel_time = t.elapsed();
128
129 let t = std::time::Instant::now();
130 let (beat_logits, downbeat_logits) = self.predictor.predict(&mel)?;
131 let predict_time = t.elapsed();
132
133 let t = std::time::Instant::now();
134 let (beats, downbeats) = self.peak_picker.decode(&beat_logits, &downbeat_logits)?;
135 let decode_time = t.elapsed();
136
137 Ok(TimedAnalysis {
138 analysis: BeatAnalysis {
139 beats,
140 downbeats,
141 mel,
142 beat_logits,
143 downbeat_logits,
144 },
145 timing: AnalysisTiming {
146 mel: mel_time,
147 predict: predict_time,
148 decode: decode_time,
149 },
150 })
151 }
152
153 pub fn analyze_file(&mut self, path: &Path) -> Result<BeatAnalysis> {
158 let audio = load_audio(path, TARGET_SAMPLE_RATE)?;
159 self.analyze_audio(&audio.samples, audio.sample_rate)
160 }
161}