aede_core/
audio_features.rs1use crate::playback::stream::PcmTrack;
3use aede_moods::{Extractor, Features, RATE};
4use std::path::Path;
5use std::time::{Duration, Instant};
6mod cache;
7pub use cache::{InputIdentity, Record, load, save};
8pub fn extract(
12 path: &Path,
13 mut checkpoint: impl FnMut() -> Result<(), String>,
14) -> Result<Features, String> {
15 checkpoint()?;
16 let mut track = PcmTrack::open_native(path).map_err(|e| e.to_string())?;
17 let format = track.source_format();
18 if !(1..=2).contains(&format.channels()) || !(8000..=192000).contains(&format.sample_rate()) {
19 return Err("source outside native mono/stereo acoustic profile".into());
20 }
21 track.set_output_rate(RATE).map_err(|e| e.to_string())?;
22 let mut extractor = Extractor::new(usize::from(format.channels()))?;
23 let mut active = Duration::ZERO;
24 loop {
25 checkpoint()?;
26 let step = Instant::now();
27 let block = track
28 .read_block::<std::convert::Infallible>(|_| Ok(()))
29 .map_err(|e| e.to_string())?;
30 active += step.elapsed();
31 let Some(block) = block else {
32 break;
33 };
34 let mut parked = Duration::ZERO;
35 let step = Instant::now();
36 extractor.push(block.samples, || {
37 let before = Instant::now();
38 let result = checkpoint();
39 parked += before.elapsed();
40 result
41 })?;
42 active += step.elapsed().saturating_sub(parked);
43 if active > Duration::from_secs(180) {
44 return Err("acoustic active computation budget exceeded".into());
45 }
46 }
47 checkpoint()?;
48 extractor.finish()
49}
50#[cfg(test)]
51#[path = "audio_features_tests.rs"]
52mod tests;