diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f8a2131 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +Change Log + +All user visible changes to this project will be documented in this file. This project adheres to Semantic Versioning, as described for Rust libraries in RFC #1105 diff --git a/Cargo.lock b/Cargo.lock index 5f1d14f..239a7c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,17 +6,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "ahash" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f200cbb1e856866d9eade941cf3aa0c5d7dd36f74311c4273b494f4ef036957" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - [[package]] name = "aho-corasick" version = "0.7.18" @@ -26,21 +15,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "ansi_term" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -dependencies = [ - "winapi", -] - -[[package]] -name = "anyhow" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" - [[package]] name = "atty" version = "0.2.14" @@ -106,16 +80,12 @@ dependencies = [ name = "bliss-rs" version = "0.1.2" dependencies = [ - "anyhow", "bliss-audio-aubio-rs", - "clap", "crossbeam", - "dirs", "env_logger", "ffmpeg-next", "lazy_static", "log", - "mpd", "ndarray", "ndarray-npy", "ndarray-stats", @@ -123,9 +93,8 @@ dependencies = [ "num_cpus", "rayon", "ripemd160", - "rusqlite", "rustfft", - "tempdir", + "serde", "thiserror", ] @@ -159,12 +128,6 @@ dependencies = [ "byte-tools", ] -[[package]] -name = "bufstream" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8" - [[package]] name = "byte-tools" version = "0.3.1" @@ -218,21 +181,6 @@ dependencies = [ "libloading", ] -[[package]] -name = "clap" -version = "2.33.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" -dependencies = [ - "ansi_term", - "atty", - "bitflags", - "strsim", - "textwrap", - "unicode-width", - "vec_map", -] - [[package]] name = "crc32fast" version = "1.2.1" @@ -329,26 +277,6 @@ dependencies = [ "generic-array 0.14.4", ] -[[package]] -name = "dirs" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - [[package]] name = "either" version = "1.6.1" @@ -374,18 +302,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" - -[[package]] -name = "fallible-streaming-iterator" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" - [[package]] name = "ffmpeg-next" version = "4.3.8" @@ -423,12 +339,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - [[package]] name = "generic-array" version = "0.12.4" @@ -471,24 +381,6 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashlink" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf" -dependencies = [ - "hashbrown 0.11.2", -] - [[package]] name = "hermit-abi" version = "0.1.18" @@ -511,7 +403,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" dependencies = [ "autocfg", - "hashbrown 0.9.1", + "hashbrown", ] [[package]] @@ -560,16 +452,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "libsqlite3-sys" -version = "0.22.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290b64917f8b0cb885d9de0f9959fe1f775d7fa12f1da2db9001c1c8ab60f89d" -dependencies = [ - "pkg-config", - "vcpkg", -] - [[package]] name = "log" version = "0.4.14" @@ -619,17 +501,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "mpd" -version = "0.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a20784da57fa01bf7910a5da686d9f39ff37feaa774856b71f050e4331bf82" -dependencies = [ - "bufstream", - "rustc-serialize", - "time", -] - [[package]] name = "ndarray" version = "0.15.1" @@ -669,7 +540,7 @@ dependencies = [ "noisy_float", "num-integer", "num-traits", - "rand 0.8.3", + "rand", ] [[package]] @@ -749,12 +620,6 @@ dependencies = [ "libc", ] -[[package]] -name = "once_cell" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" - [[package]] name = "opaque-debug" version = "0.2.3" @@ -868,19 +733,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" -dependencies = [ - "fuchsia-cprng", - "libc", - "rand_core 0.3.1", - "rdrand", - "winapi", -] - [[package]] name = "rand" version = "0.8.3" @@ -889,7 +741,7 @@ checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" dependencies = [ "libc", "rand_chacha", - "rand_core 0.6.2", + "rand_core", "rand_hc", ] @@ -900,24 +752,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" dependencies = [ "ppv-lite86", - "rand_core 0.6.2", + "rand_core", ] -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", -] - -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - [[package]] name = "rand_core" version = "0.6.2" @@ -933,7 +770,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" dependencies = [ - "rand_core 0.6.2", + "rand_core", ] [[package]] @@ -967,34 +804,6 @@ dependencies = [ "num_cpus", ] -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "redox_syscall" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_users" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" -dependencies = [ - "getrandom", - "redox_syscall", -] - [[package]] name = "regex" version = "1.5.4" @@ -1012,15 +821,6 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - [[package]] name = "ripemd160" version = "0.9.1" @@ -1032,33 +832,12 @@ dependencies = [ "opaque-debug 0.3.0", ] -[[package]] -name = "rusqlite" -version = "0.25.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57adcf67c8faaf96f3248c2a7b419a0dbc52ebe36ba83dd57fe83827c1ea4eb3" -dependencies = [ - "bitflags", - "fallible-iterator", - "fallible-streaming-iterator", - "hashlink", - "libsqlite3-sys", - "memchr", - "smallvec", -] - [[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -[[package]] -name = "rustc-serialize" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" - [[package]] name = "rustfft" version = "5.1.1" @@ -1079,6 +858,26 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "serde" +version = "1.0.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "sha-1" version = "0.8.2" @@ -1097,24 +896,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" -[[package]] -name = "smallvec" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" - [[package]] name = "strength_reduce" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3ff2f71c82567c565ba4b3009a9350a96a7269eaa4001ebedae926230bc2254" -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - [[package]] name = "syn" version = "1.0.72" @@ -1126,16 +913,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "tempdir" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" -dependencies = [ - "rand 0.4.6", - "remove_dir_all", -] - [[package]] name = "termcolor" version = "1.1.2" @@ -1145,15 +922,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - [[package]] name = "thiserror" version = "1.0.24" @@ -1174,16 +942,6 @@ dependencies = [ "syn", ] -[[package]] -name = "time" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "transpose" version = "0.2.1" @@ -1206,12 +964,6 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" -[[package]] -name = "unicode-width" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" - [[package]] name = "unicode-xid" version = "0.2.2" @@ -1224,12 +976,6 @@ version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbdbff6266a24120518560b5dc983096efb98462e51d0d68169895b237be3e5d" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version_check" version = "0.9.3" diff --git a/Cargo.toml b/Cargo.toml index 2247668..9024e79 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ license = "GPL-3.0-only" description = "A song analysis library for making playlists" homepage = "https://lelele.io/bliss.html" repository = "https://github.com/Polochon-street/bliss-rs" +keywords = ["audio", "analysis", "MIR", "playlist"] readme = "README.md" [package.metadata.docs.rs] @@ -39,11 +40,4 @@ thiserror = "1.0.24" # Until https://github.com/aubio/aubio/issues/336 is somehow solved # Hopefully we'll be able to use the official aubio-rs at some point. bliss-audio-aubio-rs = "0.2.0" - -[dev-dependencies] -mpd = "0.0.12" -rusqlite = "0.25.0" -dirs = "3.0.1" -tempdir = "0.3.7" -clap = "2.33.3" -anyhow = "1.0.40" +serde = { version = "1.0", optional = true, features = ["derive"] } diff --git a/src/chroma.rs b/src/chroma.rs index 9ae7205..5c7bfb8 100644 --- a/src/chroma.rs +++ b/src/chroma.rs @@ -258,11 +258,11 @@ fn pip_track( let beginning = freq_mask .iter() .position(|&b| b) - .ok_or(BlissError::AnalysisError("in chroma".to_string()))?; + .ok_or_else(|| BlissError::AnalysisError("in chroma".to_string()))?; let end = freq_mask .iter() .rposition(|&b| b) - .ok_or(BlissError::AnalysisError("in chroma".to_string()))?; + .ok_or_else(|| BlissError::AnalysisError("in chroma".to_string()))?; let zipped = Zip::indexed(spectrum.slice(s![beginning..end - 3, ..])) .and(spectrum.slice(s![beginning + 1..end - 2, ..])) diff --git a/src/lib.rs b/src/lib.rs index 7375ea6..b8d0246 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,27 @@ +//! bliss is a library for making "smart" audio playlists. +//! +//! The core of the library is the `Song` object, which relates to a +//! specific analyzed song and contains its path, title, analysis, and +//! other metadata fields (album, genre...). +//! Analyzing a song is as simple as running `Song::new("/path/to/song")`. +//! +//! The [analysis](Song::analysis) field of each song is an array of f32, which makes the +//! comparison between songs easy, by just using euclidean distance (see +//! [distance](Song::distance) for instance). +//! +//! Once several songs have been analyzed, making a playlist from one Song +//! is as easy as computing distances between that song and the rest, and ordering +//! the songs by distance, ascending. +//! +//! It is also convenient to make plug-ins for existing audio players. +//! It should be as easy as implementing the necessary traits for [Library]. +//! A reference implementation for the MPD player is available +//! [here](https://github.com/Polochon-street/blissify-rs) #![cfg_attr(feature = "bench", feature(test))] +#![warn(missing_docs)] +#![warn(missing_doc_code_examples)] mod chroma; -pub mod library; +mod library; mod misc; mod song; mod temporal; @@ -9,37 +30,29 @@ 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(Default, Debug, PartialEq, Clone)] -/// Simple object used to represent a Song, with its path, analysis, and -/// other metadata (artist, genre...) -pub struct Song { - pub path: String, - pub artist: String, - pub title: String, - pub album: String, - pub track_number: String, - pub genre: String, - /// Vec containing analysis, in order: tempo, zero-crossing rate, - /// mean spectral centroid, std deviation spectral centroid, - /// mean spectral rolloff, std deviation spectral rolloff - /// mean spectral_flatness, std deviation spectral flatness, - /// mean loudness, std deviation loudness - /// chroma interval feature 1 to 10 - pub analysis: Vec, -} - #[derive(Error, Debug, PartialEq)] +/// Umbrella type for bliss error types pub enum BlissError { - #[error("Error happened while decoding file – {0}")] + #[error("error happened while decoding file – {0}")] + /// An error happened while decoding an (audio) file DecodingError(String), - #[error("Error happened while analyzing file – {0}")] + #[error("error happened while analyzing file – {0}")] + /// An error happened during the analysis of the samples by bliss AnalysisError(String), - #[error("Error happened with the music library provider - {0}")] + #[error("error happened with the music library provider - {0}")] + /// An error happened with the music library provider. + /// Useful to report errors when you implement the [Library] trait. ProviderError(String), } @@ -83,6 +96,18 @@ pub fn bulk_analyse(paths: Vec) -> Vec> { mod tests { use super::*; + #[test] + fn test_send_song() { + fn assert_send() {} + assert_send::(); + } + + #[test] + fn test_sync_song() { + fn assert_sync() {} + assert_sync::(); + } + #[test] fn test_bulk_analyse() { let results = bulk_analyse(vec![ @@ -113,7 +138,7 @@ mod tests { assert_eq!( vec![ String::from( - "Error happened while decoding file – while opening format: ffmpeg::Error(2: No such file or directory)." + "error happened while decoding file – while opening format: ffmpeg::Error(2: No such file or directory)." ); 8 ], diff --git a/src/library.rs b/src/library.rs index 0b6f9ad..118d4ec 100644 --- a/src/library.rs +++ b/src/library.rs @@ -68,6 +68,7 @@ pub trait Library { } let num_cpus = num_cpus::get(); + #[allow(clippy::type_complexity)] let (tx, rx): ( Sender<(String, Result)>, Receiver<(String, Result)>, @@ -98,8 +99,8 @@ pub trait Library { match song { Ok(song) => { self.store_song(&song) - .unwrap_or_else(|_| error!("Error while storing song '{}'", (&song).path)); - info!("Analyzed and stored song '{}' successfully.", (&song).path) + .unwrap_or_else(|_| error!("Error while storing song '{}'", song.path)); + info!("Analyzed and stored song '{}' successfully.", song.path) } Err(e) => { self.store_error_song(path.to_string(), e) @@ -114,7 +115,7 @@ pub trait Library { for child in handles { child .join() - .map_err(|_| BlissError::AnalysisError(format!("in analysis")))?; + .map_err(|_| BlissError::AnalysisError("in analysis".to_string()))?; } Ok(()) } @@ -236,7 +237,7 @@ mod test { assert_eq!( test_library.analyze_library(), Err(BlissError::ProviderError(String::from( - "Error happened with the music library provider - Could not get songs path" + "error happened with the music library provider - Could not get songs path" ))), ); } diff --git a/src/song.rs b/src/song.rs index 79705e6..b9e086b 100644 --- a/src/song.rs +++ b/src/song.rs @@ -3,6 +3,9 @@ //! Use decoding, and features-extraction functions from other modules //! e.g. tempo features, spectral features, etc to build a Song and its //! corresponding Analysis. +//! +//! For implementation of plug-ins for already existing audio players, +//! a look at Library is instead recommended. extern crate crossbeam; extern crate ffmpeg_next as ffmpeg; @@ -14,7 +17,7 @@ use crate::chroma::ChromaDesc; use crate::misc::LoudnessDesc; use crate::temporal::BPMDesc; use crate::timbral::{SpectralDesc, ZeroCrossingRateDesc}; -use crate::{BlissError, Song, SAMPLE_RATE}; +use crate::{BlissError, SAMPLE_RATE}; use ::log::warn; use crossbeam::thread; use ffmpeg_next::codec::threading::{Config, Type as ThreadingType}; @@ -32,73 +35,41 @@ use std::sync::mpsc; use std::sync::mpsc::Receiver; use std::thread as std_thread; -fn push_to_sample_array(frame: &ffmpeg::frame::Audio, sample_array: &mut Vec) { - if frame.samples() == 0 { - return; - } - // Account for the padding - let actual_size = util::format::sample::Buffer::size( - Sample::F32(Type::Packed), - CHANNELS, - frame.samples(), - false, - ); - let f32_frame: Vec = frame.data(0)[..actual_size] - .chunks_exact(4) - .map(|x| { - let mut a: [u8; 4] = [0; 4]; - a.copy_from_slice(x); - f32::from_le_bytes(a) - }) - .collect(); - sample_array.extend_from_slice(&f32_frame); -} - -#[derive(Default, Debug)] -pub(crate) struct InternalSong { +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Default, Debug, PartialEq, Clone)] +/// Simple object used to represent a Song, with its path, analysis, and +/// other metadata (artist, genre...) +pub struct Song { + /// Song's provided file path pub path: String, + /// Song's artist, read from the metadata ("" if empty) pub artist: String, + /// Song's title, read from the metadata ("" if empty) pub title: String, + /// Song's album name, read from the metadata ("" if empty) pub album: String, + /// Song's tracked number, read from the metadata ("" if empty) pub track_number: String, + /// Song's genre, read from the metadata ("" if empty) pub genre: String, - pub sample_array: Vec, -} - -fn resample_frame( - rx: Receiver