Remove path and duration from the decoded song

This commit is contained in:
Anton Liaposhchenko 2024-02-16 17:09:20 +02:00
parent 719f6baaa4
commit f446e4ade7

View File

@ -33,11 +33,9 @@ use ndarray::{arr1, Array1};
use std::convert::TryInto; use std::convert::TryInto;
use std::fmt; use std::fmt;
use std::path::Path; use std::path::Path;
use std::path::PathBuf;
use std::sync::mpsc; use std::sync::mpsc;
use std::sync::mpsc::Receiver; use std::sync::mpsc::Receiver;
use std::thread as std_thread; use std::thread as std_thread;
use std::time::Duration;
use strum::{EnumCount, IntoEnumIterator}; use strum::{EnumCount, IntoEnumIterator};
use strum_macros::{EnumCount, EnumIter}; use strum_macros::{EnumCount, EnumIter};
@ -46,12 +44,8 @@ use strum_macros::{EnumCount, EnumIter};
/// Simple object used to represent a Song, with its path, analysis, and /// Simple object used to represent a Song, with its path, analysis, and
/// other metadata (artist, genre...) /// other metadata (artist, genre...)
pub struct Song { pub struct Song {
/// Song's provided file path
pub path: PathBuf,
/// bliss analysis results /// bliss analysis results
pub analysis: Analysis, pub analysis: Analysis,
/// The song's duration
pub duration: Duration,
/// Version of the features the song was analyzed with. /// Version of the features the song was analyzed with.
/// A simple integer that is bumped every time a breaking change /// A simple integer that is bumped every time a breaking change
/// is introduced in the features. /// is introduced in the features.
@ -215,12 +209,10 @@ impl Song {
/// decoding ([DecodingError](BlissError::DecodingError)) or an analysis /// decoding ([DecodingError](BlissError::DecodingError)) or an analysis
/// ([AnalysisError](BlissError::AnalysisError)) error. /// ([AnalysisError](BlissError::AnalysisError)) error.
pub fn from_path<P: AsRef<Path>>(path: P) -> BlissResult<Self> { pub fn from_path<P: AsRef<Path>>(path: P) -> BlissResult<Self> {
let raw_song = Song::decode(path.as_ref())?; let samples = Song::decode(path.as_ref())?;
Ok(Song { Ok(Song {
path: raw_song.path, analysis: Song::analyze(&samples)?,
duration: raw_song.duration,
analysis: Song::analyze(&raw_song.sample_array)?,
features_version: FEATURES_VERSION, features_version: FEATURES_VERSION,
}) })
} }
@ -348,7 +340,7 @@ impl Song {
.unwrap() .unwrap()
} }
pub(crate) fn decode(path: &Path) -> BlissResult<InternalSong> { pub(crate) fn decode(path: &Path) -> BlissResult<Vec<f32>> {
ffmpeg::init().map_err(|e| { ffmpeg::init().map_err(|e| {
BlissError::DecodingError(format!( BlissError::DecodingError(format!(
"ffmpeg init error while decoding file '{}': {:?}.", "ffmpeg init error while decoding file '{}': {:?}.",
@ -357,10 +349,7 @@ impl Song {
)) ))
})?; })?;
log::set_level(Level::Quiet); log::set_level(Level::Quiet);
let mut song = InternalSong {
path: path.into(),
..Default::default()
};
let mut ictx = ffmpeg::format::input(&path).map_err(|e| { let mut ictx = ffmpeg::format::input(&path).map_err(|e| {
BlissError::DecodingError(format!( BlissError::DecodingError(format!(
"while opening format for file '{}': {:?}.", "while opening format for file '{}': {:?}.",
@ -451,8 +440,7 @@ impl Song {
path.display() path.display()
); );
drop(tx); drop(tx);
song.sample_array = child.join().unwrap()?; return Ok(child.join().unwrap()?);
return Ok(song);
} }
Err(e) => warn!("error while decoding file '{}': {}", path.display(), e), Err(e) => warn!("error while decoding file '{}': {}", path.display(), e),
}; };
@ -490,8 +478,7 @@ impl Song {
path.display() path.display()
); );
drop(tx); drop(tx);
song.sample_array = child.join().unwrap()?; return Ok(child.join().unwrap()?);
return Ok(song);
} }
Err(e) => warn!("error while decoding {}: {}", path.display(), e), Err(e) => warn!("error while decoding {}: {}", path.display(), e),
}; };
@ -513,20 +500,10 @@ impl Song {
} }
drop(tx); drop(tx);
song.sample_array = child.join().unwrap()?; Ok(child.join().unwrap()?)
let duration_seconds = song.sample_array.len() as f32 / SAMPLE_RATE as f32;
song.duration = Duration::from_nanos((duration_seconds * 1e9_f32).round() as u64);
Ok(song)
} }
} }
#[derive(Default, Debug)]
pub(crate) struct InternalSong {
pub path: PathBuf,
pub duration: Duration,
pub sample_array: Vec<f32>,
}
fn resample_frame( fn resample_frame(
rx: Receiver<Audio>, rx: Receiver<Audio>,
in_codec_format: Sample, in_codec_format: Sample,
@ -672,9 +649,9 @@ mod tests {
} }
fn _test_decode(path: &Path, expected_hash: &[u8]) { fn _test_decode(path: &Path, expected_hash: &[u8]) {
let song = Song::decode(path).unwrap(); let samples = Song::decode(path).unwrap();
let mut hasher = Ripemd160::new(); let mut hasher = Ripemd160::new();
for sample in song.sample_array.iter() { for sample in samples.iter() {
hasher.update(sample.to_le_bytes().to_vec()); hasher.update(sample.to_le_bytes().to_vec());
} }
@ -736,26 +713,23 @@ mod tests {
#[test] #[test]
fn test_decode_right_capacity_vec() { fn test_decode_right_capacity_vec() {
let path = Path::new("data/s16_mono_22_5kHz.flac"); let path = Path::new("data/s16_mono_22_5kHz.flac");
let song = Song::decode(&path).unwrap(); let samples = Song::decode(&path).unwrap();
let sample_array = song.sample_array;
assert_eq!( assert_eq!(
sample_array.len() + SAMPLE_RATE as usize, samples.len() + SAMPLE_RATE as usize,
sample_array.capacity() samples.capacity()
); );
let path = Path::new("data/s32_stereo_44_1_kHz.flac"); let path = Path::new("data/s32_stereo_44_1_kHz.flac");
let song = Song::decode(&path).unwrap(); let samples = Song::decode(&path).unwrap();
let sample_array = song.sample_array;
assert_eq!( assert_eq!(
sample_array.len() + SAMPLE_RATE as usize, samples.len() + SAMPLE_RATE as usize,
sample_array.capacity() samples.capacity()
); );
let path = Path::new("data/capacity_fix.ogg"); let path = Path::new("data/capacity_fix.ogg");
let song = Song::decode(&path).unwrap(); let samples = Song::decode(&path).unwrap();
let sample_array = song.sample_array; assert!(samples.len() as f32 / samples.capacity() as f32 > 0.90);
assert!(sample_array.len() as f32 / sample_array.capacity() as f32 > 0.90); assert!(samples.len() as f32 / (samples.capacity() as f32) < 1.);
assert!(sample_array.len() as f32 / (sample_array.capacity() as f32) < 1.);
} }
#[test] #[test]