#![cfg_attr(feature = "bench", feature(test))]
#![warn(missing_docs)]
#![warn(missing_doc_code_examples)]
mod chroma;
mod library;
mod misc;
mod song;
mod temporal;
mod timbral;
mod utils;
extern crate crossbeam;
extern crate num_cpus;
#[cfg(feature = "serde")]
#[macro_use]
extern crate serde;
use thiserror::Error;
pub use song::Song;
pub use library::Library;
const CHANNELS: u16 = 1;
const SAMPLE_RATE: u32 = 22050;
#[derive(Error, Debug, PartialEq)]
pub enum BlissError {
#[error("error happened while decoding file – {0}")]
DecodingError(String),
#[error("error happened while analyzing file – {0}")]
AnalysisError(String),
#[error("error happened with the music library provider - {0}")]
ProviderError(String),
}
#[doc(hidden)]
pub fn bulk_analyse(paths: Vec<String>) -> Vec<Result<Song, BlissError>> {
let mut songs = Vec::with_capacity(paths.len());
let num_cpus = num_cpus::get();
crossbeam::scope(|s| {
let mut handles = Vec::with_capacity(paths.len() / num_cpus);
let mut chunk_number = paths.len() / num_cpus;
if chunk_number == 0 {
chunk_number = paths.len();
}
for chunk in paths.chunks(chunk_number) {
handles.push(s.spawn(move |_| {
let mut result = Vec::with_capacity(chunk.len());
for path in chunk {
let song = Song::new(&path);
result.push(song);
}
result
}));
}
for handle in handles {
songs.extend(handle.join().unwrap());
}
})
.unwrap();
songs
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_send_song() {
fn assert_send<T: Send>() {}
assert_send::<Song>();
}
#[test]
fn test_sync_song() {
fn assert_sync<T: Send>() {}
assert_sync::<Song>();
}
#[test]
fn test_bulk_analyse() {
let results = bulk_analyse(vec![
String::from("data/s16_mono_22_5kHz.flac"),
String::from("data/s16_mono_22_5kHz.flac"),
String::from("nonexistent"),
String::from("data/s16_stereo_22_5kHz.flac"),
String::from("nonexistent"),
String::from("nonexistent"),
String::from("nonexistent"),
String::from("nonexistent"),
String::from("nonexistent"),
String::from("nonexistent"),
String::from("nonexistent"),
]);
let mut errored_songs: Vec<String> = results
.iter()
.filter_map(|x| x.as_ref().err().map(|x| x.to_string()))
.collect();
errored_songs.sort_by(|a, b| a.cmp(b));
let mut analysed_songs: Vec<String> = results
.iter()
.filter_map(|x| x.as_ref().ok().map(|x| x.path.to_string()))
.collect();
analysed_songs.sort_by(|a, b| a.cmp(b));
assert_eq!(
vec![
String::from(
"error happened while decoding file – while opening format: ffmpeg::Error(2: No such file or directory)."
);
8
],
errored_songs
);
assert_eq!(
vec![
String::from("data/s16_mono_22_5kHz.flac"),
String::from("data/s16_mono_22_5kHz.flac"),
String::from("data/s16_stereo_22_5kHz.flac"),
],
analysed_songs,
);
}
}