Remove path and duration from the decoded song
This commit is contained in:
parent
719f6baaa4
commit
f446e4ade7
62
src/song.rs
62
src/song.rs
|
@ -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]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user