use std::{
fs::File,
io::{Read, Seek},
path::Path,
thread::sleep,
time::Duration,
};
use rodio::{buffer::SamplesBuffer, Decoder, OutputStream, Source};
use crate::core::{base::Res, note::Note};
use super::base::get_notes_from_audio_data;
pub fn get_notes_from_audio_file(file: impl AsRef<Path>, start: Option<Duration>, end: Option<Duration>) -> Res<Vec<Note>> {
let (data, length_in_seconds) = get_audio_data_from_file(file, start, end)?;
get_notes_from_audio_data(&data, length_in_seconds)
}
pub fn get_audio_data_from_file(file: impl AsRef<Path>, start: Option<Duration>, end: Option<Duration>) -> Res<(Vec<f32>, u8)> {
let path = file.as_ref();
let start = start.unwrap_or_default();
let decoder = Decoder::new(File::open(path)?)?.skip_duration(start).convert_samples();
let num_channels = decoder.channels();
let sample_rate = decoder.sample_rate();
let samples: Vec<_> = if let Some(end) = end { decoder.take_duration(end - start).collect() } else { decoder.collect() };
let num_samples = samples.len();
let length_in_seconds = dbg!(num_samples as f32 / (sample_rate as f32 * num_channels as f32)) as u8;
let data = samples[..(length_in_seconds as f32 * sample_rate as f32 * num_channels as f32) as usize].to_vec();
Ok((data, length_in_seconds))
}
#[coverage(off)]
pub fn preview_audio_file_clip(file: impl AsRef<Path>, start: Option<Duration>, end: Option<Duration>) -> Res<()> {
let file = File::open(file)?;
preview_audio_clip(file, start, end)
}
#[coverage(off)]
pub fn preview_audio_clip(stream: impl Read + Seek + Send + Sync + 'static, start: Option<Duration>, end: Option<Duration>) -> Res<()> {
let start = start.unwrap_or_default();
let decoder = Decoder::new(stream)?.skip_duration(start).convert_samples();
let (_stream, stream_handle) = OutputStream::try_default()?;
if let Some(end) = end {
stream_handle.play_raw(decoder.take_duration(end - start))?;
sleep(end - start);
} else if let Some(duration) = decoder.total_duration() {
stream_handle.play_raw(decoder)?;
sleep(duration);
} else {
let channels = decoder.channels();
let sample_rate = decoder.sample_rate() as f32;
let samples: Vec<_> = decoder.collect();
let time = Duration::from_secs((samples.len() as f32 / sample_rate).ceil() as u64);
stream_handle.play_raw(SamplesBuffer::new(channels, sample_rate as u32, samples))?;
sleep(time);
}
Ok(())
}
#[cfg(test)]
mod tests {
use crate::core::{base::Parsable, chord::Chord};
use super::*;
#[ignore]
#[test]
fn test_preview_audio_clip() {
preview_audio_file_clip("tests/C7b9.wav", None, None).unwrap();
}
#[cfg(feature = "analyze_file")]
#[test]
fn test_get_notes_from_audio_file() {
let notes = get_notes_from_audio_file("tests/C7b9.wav", None, None).unwrap();
assert_eq!(Chord::parse("C7b9").unwrap(), Chord::try_from_notes(¬es).unwrap()[0]);
}
#[cfg(feature = "analyze_file")]
#[cfg(feature = "analyze_file_mp3")]
#[test]
fn test_get_notes_from_mp3_file() {
let notes = get_notes_from_audio_file("tests/C7b9.mp3", None, None).unwrap();
assert_eq!(Chord::parse("C7b9").unwrap(), Chord::try_from_notes(¬es).unwrap()[0]);
}
}