Merge pull request #5 from Polochon-street/final-touches
Final touches to adhere to the Rust API Guidelines
This commit is contained in:
commit
6a070a6d13
11 changed files with 312 additions and 412 deletions
3
CHANGELOG.md
Normal file
3
CHANGELOG.md
Normal file
|
@ -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
|
348
Cargo.lock
generated
348
Cargo.lock
generated
|
@ -6,17 +6,6 @@ version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
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]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "0.7.18"
|
version = "0.7.18"
|
||||||
|
@ -26,21 +15,6 @@ dependencies = [
|
||||||
"memchr",
|
"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]]
|
[[package]]
|
||||||
name = "atty"
|
name = "atty"
|
||||||
version = "0.2.14"
|
version = "0.2.14"
|
||||||
|
@ -84,6 +58,28 @@ version = "1.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bliss-audio"
|
||||||
|
version = "0.1.3"
|
||||||
|
dependencies = [
|
||||||
|
"bliss-audio-aubio-rs",
|
||||||
|
"crossbeam",
|
||||||
|
"env_logger",
|
||||||
|
"ffmpeg-next",
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"ndarray",
|
||||||
|
"ndarray-npy",
|
||||||
|
"ndarray-stats",
|
||||||
|
"noisy_float",
|
||||||
|
"num_cpus",
|
||||||
|
"rayon",
|
||||||
|
"ripemd160",
|
||||||
|
"rustfft",
|
||||||
|
"serde",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bliss-audio-aubio-rs"
|
name = "bliss-audio-aubio-rs"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
@ -102,33 +98,6 @@ dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
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",
|
|
||||||
"noisy_float",
|
|
||||||
"num_cpus",
|
|
||||||
"rayon",
|
|
||||||
"ripemd160",
|
|
||||||
"rusqlite",
|
|
||||||
"rustfft",
|
|
||||||
"tempdir",
|
|
||||||
"thiserror",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
version = "0.7.3"
|
version = "0.7.3"
|
||||||
|
@ -159,12 +128,6 @@ dependencies = [
|
||||||
"byte-tools",
|
"byte-tools",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bufstream"
|
|
||||||
version = "0.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byte-tools"
|
name = "byte-tools"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
@ -218,21 +181,6 @@ dependencies = [
|
||||||
"libloading",
|
"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]]
|
[[package]]
|
||||||
name = "crc32fast"
|
name = "crc32fast"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
|
@ -329,26 +277,6 @@ dependencies = [
|
||||||
"generic-array 0.14.4",
|
"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]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.6.1"
|
version = "1.6.1"
|
||||||
|
@ -374,18 +302,6 @@ version = "0.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
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]]
|
[[package]]
|
||||||
name = "ffmpeg-next"
|
name = "ffmpeg-next"
|
||||||
version = "4.3.8"
|
version = "4.3.8"
|
||||||
|
@ -423,12 +339,6 @@ dependencies = [
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fuchsia-cprng"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.12.4"
|
version = "0.12.4"
|
||||||
|
@ -471,24 +381,6 @@ version = "0.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
|
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]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.1.18"
|
version = "0.1.18"
|
||||||
|
@ -511,7 +403,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3"
|
checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"hashbrown 0.9.1",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -560,16 +452,6 @@ dependencies = [
|
||||||
"winapi",
|
"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]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.14"
|
version = "0.4.14"
|
||||||
|
@ -619,17 +501,6 @@ dependencies = [
|
||||||
"autocfg",
|
"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]]
|
[[package]]
|
||||||
name = "ndarray"
|
name = "ndarray"
|
||||||
version = "0.15.1"
|
version = "0.15.1"
|
||||||
|
@ -669,7 +540,7 @@ dependencies = [
|
||||||
"noisy_float",
|
"noisy_float",
|
||||||
"num-integer",
|
"num-integer",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"rand 0.8.3",
|
"rand",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -749,12 +620,6 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "once_cell"
|
|
||||||
version = "1.7.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opaque-debug"
|
name = "opaque-debug"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
|
@ -868,19 +733,6 @@ dependencies = [
|
||||||
"proc-macro2",
|
"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]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.3"
|
version = "0.8.3"
|
||||||
|
@ -889,7 +741,7 @@ checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"rand_chacha",
|
"rand_chacha",
|
||||||
"rand_core 0.6.2",
|
"rand_core",
|
||||||
"rand_hc",
|
"rand_hc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -900,24 +752,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
|
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ppv-lite86",
|
"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]]
|
[[package]]
|
||||||
name = "rand_core"
|
name = "rand_core"
|
||||||
version = "0.6.2"
|
version = "0.6.2"
|
||||||
|
@ -933,7 +770,7 @@ version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
|
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rand_core 0.6.2",
|
"rand_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -967,34 +804,6 @@ dependencies = [
|
||||||
"num_cpus",
|
"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]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.5.4"
|
version = "1.5.4"
|
||||||
|
@ -1012,15 +821,6 @@ version = "0.6.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
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]]
|
[[package]]
|
||||||
name = "ripemd160"
|
name = "ripemd160"
|
||||||
version = "0.9.1"
|
version = "0.9.1"
|
||||||
|
@ -1032,33 +832,12 @@ dependencies = [
|
||||||
"opaque-debug 0.3.0",
|
"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]]
|
[[package]]
|
||||||
name = "rustc-hash"
|
name = "rustc-hash"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustc-serialize"
|
|
||||||
version = "0.3.24"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustfft"
|
name = "rustfft"
|
||||||
version = "5.1.1"
|
version = "5.1.1"
|
||||||
|
@ -1079,6 +858,26 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
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]]
|
[[package]]
|
||||||
name = "sha-1"
|
name = "sha-1"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
|
@ -1097,24 +896,12 @@ version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
|
checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "smallvec"
|
|
||||||
version = "1.6.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strength_reduce"
|
name = "strength_reduce"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a3ff2f71c82567c565ba4b3009a9350a96a7269eaa4001ebedae926230bc2254"
|
checksum = "a3ff2f71c82567c565ba4b3009a9350a96a7269eaa4001ebedae926230bc2254"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "strsim"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.72"
|
version = "1.0.72"
|
||||||
|
@ -1126,16 +913,6 @@ dependencies = [
|
||||||
"unicode-xid",
|
"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]]
|
[[package]]
|
||||||
name = "termcolor"
|
name = "termcolor"
|
||||||
version = "1.1.2"
|
version = "1.1.2"
|
||||||
|
@ -1145,15 +922,6 @@ dependencies = [
|
||||||
"winapi-util",
|
"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]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.24"
|
version = "1.0.24"
|
||||||
|
@ -1174,16 +942,6 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "time"
|
|
||||||
version = "0.1.43"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "transpose"
|
name = "transpose"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
@ -1206,12 +964,6 @@ version = "0.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-width"
|
|
||||||
version = "0.1.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
@ -1224,12 +976,6 @@ version = "0.2.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cbdbff6266a24120518560b5dc983096efb98462e51d0d68169895b237be3e5d"
|
checksum = "cbdbff6266a24120518560b5dc983096efb98462e51d0d68169895b237be3e5d"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "vec_map"
|
|
||||||
version = "0.8.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.3"
|
version = "0.9.3"
|
||||||
|
|
14
Cargo.toml
14
Cargo.toml
|
@ -1,12 +1,13 @@
|
||||||
[package]
|
[package]
|
||||||
name = "bliss-rs"
|
name = "bliss-audio"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
authors = ["Polochon-street <polochonstreet@gmx.fr>"]
|
authors = ["Polochon-street <polochonstreet@gmx.fr>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "GPL-3.0-only"
|
license = "GPL-3.0-only"
|
||||||
description = "A song analysis library for making playlists"
|
description = "A song analysis library for making playlists"
|
||||||
homepage = "https://lelele.io/bliss.html"
|
homepage = "https://lelele.io/bliss.html"
|
||||||
repository = "https://github.com/Polochon-street/bliss-rs"
|
repository = "https://github.com/Polochon-street/bliss-rs"
|
||||||
|
keywords = ["audio", "analysis", "MIR", "playlist"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
|
@ -39,11 +40,4 @@ thiserror = "1.0.24"
|
||||||
# Until https://github.com/aubio/aubio/issues/336 is somehow solved
|
# 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.
|
# Hopefully we'll be able to use the official aubio-rs at some point.
|
||||||
bliss-audio-aubio-rs = "0.2.0"
|
bliss-audio-aubio-rs = "0.2.0"
|
||||||
|
serde = { version = "1.0", optional = true, features = ["derive"] }
|
||||||
[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"
|
|
||||||
|
|
96
README.md
96
README.md
|
@ -1,18 +1,96 @@
|
||||||
![build](https://github.com/Polochon-street/bliss-rs/workflows/Rust/badge.svg)
|
[![crate](https://img.shields.io/crates/v/bliss-audio.svg)](https://crates.io/crates/bliss-audio)
|
||||||
![doc](https://docs.rs/bliss-rs/badge.svg)
|
[![build](https://github.com/Polochon-street/bliss-rs/workflows/Rust/badge.svg)](https://github.com/Polochon-street/bliss-rs/actions)
|
||||||
|
[![doc](https://docs.rs/bliss-rs/badge.svg)](https://docs.rs/bliss-audio/)
|
||||||
|
|
||||||
# Bliss music analyser - Rust version
|
# bliss music analyser - Rust version
|
||||||
Bliss-rs is the Rust improvement of [Bliss](https://github.com/Polochon-street/bliss). The data it
|
bliss-rs is the Rust improvement of [bliss](https://github.com/Polochon-street/bliss), a
|
||||||
outputs is not compatible with the ones used by Bliss, since it uses
|
library used to make playlists by analyzing songs, and computing distance between them.
|
||||||
different, more accurate features, based on actual literature this time.
|
|
||||||
|
|
||||||
Like Bliss, it ease the creation of « intelligent » playlists and/or continuous
|
Like bliss, it eases the creation of « intelligent » playlists and/or continuous
|
||||||
play, à la Spotify/Grooveshark Radio.
|
play, à la Spotify/Grooveshark Radio, as well as easing creating plug-ins for
|
||||||
|
existing audio players.
|
||||||
|
|
||||||
## Usage
|
For now (and if you're looking for an easy-to use smooth play experience),
|
||||||
|
[blissify](https://crates.io/crates/blissify) implements bliss for
|
||||||
|
[MPD](https://www.musicpd.org/).
|
||||||
|
|
||||||
|
Note 1: the features bliss-rs outputs is not compatible with the ones
|
||||||
|
used by C-bliss, since it uses
|
||||||
|
different, more accurate values, based on
|
||||||
|
[actual literature](https://lelele.io/thesis.pdf). It is also faster.
|
||||||
|
|
||||||
|
Note 2: The `bliss-rs` crate is outdated. You should use `bliss-audio`
|
||||||
|
(this crate) instead.
|
||||||
|
|
||||||
|
## Examples
|
||||||
For simple analysis / distance computing, a look at `examples/distance.rs` and
|
For simple analysis / distance computing, a look at `examples/distance.rs` and
|
||||||
`examples/analyse.rs`.
|
`examples/analyse.rs`.
|
||||||
|
|
||||||
|
Ready to use examples:
|
||||||
|
|
||||||
|
### Compute the distance between two songs
|
||||||
|
```
|
||||||
|
use bliss_audio::Song;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let song1 = Song::new("/path/to/song1");
|
||||||
|
let song2 = Song::new("/path/to/song2");
|
||||||
|
|
||||||
|
println!("Distance between song1 and song2 is {}", song1.distance(song2));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Make a playlist from a song
|
||||||
|
```
|
||||||
|
use bliss_rs::{BlissError, Song};
|
||||||
|
use ndarray::{arr1, Array};
|
||||||
|
use noisy_float::prelude::n32;
|
||||||
|
|
||||||
|
fn main() -> Result<(), BlissError> {
|
||||||
|
let paths = vec!["/path/to/song1", "/path/to/song2", "/path/to/song3"];
|
||||||
|
let mut songs: Vec<Song> = paths
|
||||||
|
.iter()
|
||||||
|
.map(|path| Song::new(path))
|
||||||
|
.collect::<Result<Vec<Song>, BlissError>>()?;
|
||||||
|
|
||||||
|
// Assuming there is a first song
|
||||||
|
let analysis_first_song = arr1(&songs[0].analysis);
|
||||||
|
|
||||||
|
// Identity matrix used to compute the distance.
|
||||||
|
// Note that it can be changed to alter feature ponderation, which
|
||||||
|
// may yield to better playlists (subjectively).
|
||||||
|
let m = Array::eye(analysis_first_song.len());
|
||||||
|
|
||||||
|
songs.sort_by_cached_key(|song| {
|
||||||
|
n32((arr1(&song.analysis) - &analysis_first_song)
|
||||||
|
.dot(&m)
|
||||||
|
.dot(&(arr1(&song.analysis) - &analysis_first_song)))
|
||||||
|
});
|
||||||
|
println!(
|
||||||
|
"Playlist is: {:?}",
|
||||||
|
songs
|
||||||
|
.iter()
|
||||||
|
.map(|song| &song.path)
|
||||||
|
.collect::<Vec<&String>>()
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Further use
|
||||||
|
|
||||||
|
Instead of reinventing ways to fetch a user library, play songs, etc,
|
||||||
|
and embed that into bliss, it is easier to look at the
|
||||||
|
[Library](https://github.com/Polochon-street/bliss-rs/blob/master/src/library.rs#L12)
|
||||||
|
trait.
|
||||||
|
|
||||||
|
By implementing a few functions to get songs from a media library, and store
|
||||||
|
the resulting analysis, you get access to functions to analyze an entire
|
||||||
|
library (with multithreading), and to make playlists easily.
|
||||||
|
|
||||||
|
See [blissify](https://crates.io/crates/blissify) for a reference
|
||||||
|
implementation.
|
||||||
|
|
||||||
## Acknowledgements
|
## Acknowledgements
|
||||||
|
|
||||||
* This library relies heavily on [aubio](https://aubio.org/)'s
|
* This library relies heavily on [aubio](https://aubio.org/)'s
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use bliss_rs::Song;
|
use bliss_audio::Song;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use bliss_rs::Song;
|
use bliss_audio::Song;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -258,11 +258,11 @@ fn pip_track(
|
||||||
let beginning = freq_mask
|
let beginning = freq_mask
|
||||||
.iter()
|
.iter()
|
||||||
.position(|&b| b)
|
.position(|&b| b)
|
||||||
.ok_or(BlissError::AnalysisError("in chroma".to_string()))?;
|
.ok_or_else(|| BlissError::AnalysisError("in chroma".to_string()))?;
|
||||||
let end = freq_mask
|
let end = freq_mask
|
||||||
.iter()
|
.iter()
|
||||||
.rposition(|&b| b)
|
.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, ..]))
|
let zipped = Zip::indexed(spectrum.slice(s![beginning..end - 3, ..]))
|
||||||
.and(spectrum.slice(s![beginning + 1..end - 2, ..]))
|
.and(spectrum.slice(s![beginning + 1..end - 2, ..]))
|
||||||
|
|
73
src/lib.rs
73
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))]
|
#![cfg_attr(feature = "bench", feature(test))]
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
#![warn(missing_doc_code_examples)]
|
||||||
mod chroma;
|
mod chroma;
|
||||||
pub mod library;
|
mod library;
|
||||||
mod misc;
|
mod misc;
|
||||||
mod song;
|
mod song;
|
||||||
mod temporal;
|
mod temporal;
|
||||||
|
@ -9,37 +30,29 @@ mod utils;
|
||||||
|
|
||||||
extern crate crossbeam;
|
extern crate crossbeam;
|
||||||
extern crate num_cpus;
|
extern crate num_cpus;
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
pub use song::Song;
|
||||||
|
pub use library::Library;
|
||||||
|
|
||||||
const CHANNELS: u16 = 1;
|
const CHANNELS: u16 = 1;
|
||||||
const SAMPLE_RATE: u32 = 22050;
|
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<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq)]
|
#[derive(Error, Debug, PartialEq)]
|
||||||
|
/// Umbrella type for bliss error types
|
||||||
pub enum BlissError {
|
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),
|
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),
|
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),
|
ProviderError(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,6 +96,18 @@ pub fn bulk_analyse(paths: Vec<String>) -> Vec<Result<Song, BlissError>> {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
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]
|
#[test]
|
||||||
fn test_bulk_analyse() {
|
fn test_bulk_analyse() {
|
||||||
let results = bulk_analyse(vec![
|
let results = bulk_analyse(vec![
|
||||||
|
@ -113,7 +138,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vec![
|
vec![
|
||||||
String::from(
|
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
|
8
|
||||||
],
|
],
|
||||||
|
|
|
@ -68,6 +68,7 @@ pub trait Library {
|
||||||
}
|
}
|
||||||
let num_cpus = num_cpus::get();
|
let num_cpus = num_cpus::get();
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
let (tx, rx): (
|
let (tx, rx): (
|
||||||
Sender<(String, Result<Song, BlissError>)>,
|
Sender<(String, Result<Song, BlissError>)>,
|
||||||
Receiver<(String, Result<Song, BlissError>)>,
|
Receiver<(String, Result<Song, BlissError>)>,
|
||||||
|
@ -98,8 +99,8 @@ pub trait Library {
|
||||||
match song {
|
match song {
|
||||||
Ok(song) => {
|
Ok(song) => {
|
||||||
self.store_song(&song)
|
self.store_song(&song)
|
||||||
.unwrap_or_else(|_| error!("Error while storing song '{}'", (&song).path));
|
.unwrap_or_else(|_| error!("Error while storing song '{}'", song.path));
|
||||||
info!("Analyzed and stored song '{}' successfully.", (&song).path)
|
info!("Analyzed and stored song '{}' successfully.", song.path)
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
self.store_error_song(path.to_string(), e)
|
self.store_error_song(path.to_string(), e)
|
||||||
|
@ -114,7 +115,7 @@ pub trait Library {
|
||||||
for child in handles {
|
for child in handles {
|
||||||
child
|
child
|
||||||
.join()
|
.join()
|
||||||
.map_err(|_| BlissError::AnalysisError(format!("in analysis")))?;
|
.map_err(|_| BlissError::AnalysisError("in analysis".to_string()))?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -236,7 +237,7 @@ mod test {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
test_library.analyze_library(),
|
test_library.analyze_library(),
|
||||||
Err(BlissError::ProviderError(String::from(
|
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"
|
||||||
))),
|
))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
171
src/song.rs
171
src/song.rs
|
@ -3,6 +3,9 @@
|
||||||
//! Use decoding, and features-extraction functions from other modules
|
//! Use decoding, and features-extraction functions from other modules
|
||||||
//! e.g. tempo features, spectral features, etc to build a Song and its
|
//! e.g. tempo features, spectral features, etc to build a Song and its
|
||||||
//! corresponding Analysis.
|
//! corresponding Analysis.
|
||||||
|
//!
|
||||||
|
//! For implementation of plug-ins for already existing audio players,
|
||||||
|
//! a look at Library is instead recommended.
|
||||||
|
|
||||||
extern crate crossbeam;
|
extern crate crossbeam;
|
||||||
extern crate ffmpeg_next as ffmpeg;
|
extern crate ffmpeg_next as ffmpeg;
|
||||||
|
@ -14,7 +17,7 @@ use crate::chroma::ChromaDesc;
|
||||||
use crate::misc::LoudnessDesc;
|
use crate::misc::LoudnessDesc;
|
||||||
use crate::temporal::BPMDesc;
|
use crate::temporal::BPMDesc;
|
||||||
use crate::timbral::{SpectralDesc, ZeroCrossingRateDesc};
|
use crate::timbral::{SpectralDesc, ZeroCrossingRateDesc};
|
||||||
use crate::{BlissError, Song, SAMPLE_RATE};
|
use crate::{BlissError, SAMPLE_RATE};
|
||||||
use ::log::warn;
|
use ::log::warn;
|
||||||
use crossbeam::thread;
|
use crossbeam::thread;
|
||||||
use ffmpeg_next::codec::threading::{Config, Type as ThreadingType};
|
use ffmpeg_next::codec::threading::{Config, Type as ThreadingType};
|
||||||
|
@ -32,73 +35,41 @@ 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;
|
||||||
|
|
||||||
fn push_to_sample_array(frame: &ffmpeg::frame::Audio, sample_array: &mut Vec<f32>) {
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
if frame.samples() == 0 {
|
#[derive(Default, Debug, PartialEq, Clone)]
|
||||||
return;
|
/// Simple object used to represent a Song, with its path, analysis, and
|
||||||
}
|
/// other metadata (artist, genre...)
|
||||||
// Account for the padding
|
pub struct Song {
|
||||||
let actual_size = util::format::sample::Buffer::size(
|
/// Song's provided file path
|
||||||
Sample::F32(Type::Packed),
|
|
||||||
CHANNELS,
|
|
||||||
frame.samples(),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
let f32_frame: Vec<f32> = 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 {
|
|
||||||
pub path: String,
|
pub path: String,
|
||||||
|
/// Song's artist, read from the metadata (`""` if empty)
|
||||||
pub artist: String,
|
pub artist: String,
|
||||||
|
/// Song's title, read from the metadata (`""` if empty)
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
/// Song's album name, read from the metadata (`""` if empty)
|
||||||
pub album: String,
|
pub album: String,
|
||||||
|
/// Song's tracked number, read from the metadata (`""` if empty)
|
||||||
pub track_number: String,
|
pub track_number: String,
|
||||||
|
/// Song's genre, read from the metadata (`""` if empty)
|
||||||
pub genre: String,
|
pub genre: String,
|
||||||
pub sample_array: Vec<f32>,
|
/// Vec containing analysis, in order: tempo, zero-crossing rate,
|
||||||
}
|
/// mean spectral centroid, std deviation spectral centroid,
|
||||||
|
/// mean spectral rolloff, std deviation spectral rolloff
|
||||||
fn resample_frame(
|
/// mean spectral_flatness, std deviation spectral flatness,
|
||||||
rx: Receiver<Audio>,
|
/// mean loudness, std deviation loudness, chroma interval feature 1 to 10.
|
||||||
mut resample_context: Context,
|
///
|
||||||
mut sample_array: Vec<f32>,
|
/// All the numbers are between -1 and 1.
|
||||||
) -> Result<Vec<f32>, BlissError> {
|
pub analysis: Vec<f32>,
|
||||||
let mut resampled = ffmpeg::frame::Audio::empty();
|
|
||||||
for decoded in rx.iter() {
|
|
||||||
resample_context
|
|
||||||
.run(&decoded, &mut resampled)
|
|
||||||
.map_err(|e| {
|
|
||||||
BlissError::DecodingError(format!("while trying to resample song: {:?}", e))
|
|
||||||
})?;
|
|
||||||
push_to_sample_array(&resampled, &mut sample_array);
|
|
||||||
}
|
|
||||||
loop {
|
|
||||||
match resample_context.flush(&mut resampled).map_err(|e| {
|
|
||||||
BlissError::DecodingError(format!("while trying to resample song: {:?}", e))
|
|
||||||
})? {
|
|
||||||
Some(_) => {
|
|
||||||
push_to_sample_array(&resampled, &mut sample_array);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
if resampled.samples() == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
push_to_sample_array(&resampled, &mut sample_array);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Ok(sample_array)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Song {
|
impl Song {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
/// Compute the distance between the current song and any given Song.
|
||||||
|
///
|
||||||
|
/// The smaller the number, the closer the songs; usually more useful
|
||||||
|
/// if compared between several songs
|
||||||
|
/// (e.g. if song1.distance(song2) < song1.distance(song3), then song1 is
|
||||||
|
/// closer to song2 than it is to song3.
|
||||||
pub fn distance(&self, other: &Self) -> f32 {
|
pub fn distance(&self, other: &Self) -> f32 {
|
||||||
let a1 = arr1(&self.analysis.to_vec());
|
let a1 = arr1(&self.analysis.to_vec());
|
||||||
let a2 = arr1(&other.analysis.to_vec());
|
let a2 = arr1(&other.analysis.to_vec());
|
||||||
|
@ -110,6 +81,22 @@ impl Song {
|
||||||
(arr1(&self.analysis) - &a2).dot(&m).dot(&(&a1 - &a2))
|
(arr1(&self.analysis) - &a2).dot(&m).dot(&(&a1 - &a2))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a decoded Song given a file path, or an error if the song
|
||||||
|
/// could not be analyzed for some reason.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `path` - A string holding a valid file path to a valid audio file.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This function will return an error if the file path is invalid, if
|
||||||
|
/// the file path points to a file containing no or corrupted audio stream,
|
||||||
|
/// or if the analysis could not be conducted to the end for some reason.
|
||||||
|
///
|
||||||
|
/// The error type returned should give a hint as to whether it was a
|
||||||
|
/// decoding ([DecodingError](BlissError::DecodingError)) or an analysis
|
||||||
|
/// ([AnalysisError](BlissError::AnalysisError)) error.
|
||||||
pub fn new(path: &str) -> Result<Self, BlissError> {
|
pub fn new(path: &str) -> Result<Self, BlissError> {
|
||||||
let raw_song = Song::decode(&path)?;
|
let raw_song = Song::decode(&path)?;
|
||||||
|
|
||||||
|
@ -172,6 +159,7 @@ impl Song {
|
||||||
Ok(chroma_desc.get_values())
|
Ok(chroma_desc.get_values())
|
||||||
});
|
});
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
let child_timbral: thread::ScopedJoinHandle<
|
let child_timbral: thread::ScopedJoinHandle<
|
||||||
'_,
|
'_,
|
||||||
Result<(Vec<f32>, Vec<f32>, Vec<f32>), BlissError>,
|
Result<(Vec<f32>, Vec<f32>, Vec<f32>), BlissError>,
|
||||||
|
@ -238,7 +226,7 @@ impl Song {
|
||||||
let stream = format
|
let stream = format
|
||||||
.streams()
|
.streams()
|
||||||
.find(|s| s.codec().medium() == ffmpeg::media::Type::Audio)
|
.find(|s| s.codec().medium() == ffmpeg::media::Type::Audio)
|
||||||
.ok_or(BlissError::DecodingError(String::from(
|
.ok_or_else(|| BlissError::DecodingError(String::from(
|
||||||
"No audio stream found.",
|
"No audio stream found.",
|
||||||
)))?;
|
)))?;
|
||||||
stream.codec().set_threading(Config {
|
stream.codec().set_threading(Config {
|
||||||
|
@ -379,6 +367,71 @@ impl Song {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub(crate) struct InternalSong {
|
||||||
|
pub path: String,
|
||||||
|
pub artist: String,
|
||||||
|
pub title: String,
|
||||||
|
pub album: String,
|
||||||
|
pub track_number: String,
|
||||||
|
pub genre: String,
|
||||||
|
pub sample_array: Vec<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resample_frame(
|
||||||
|
rx: Receiver<Audio>,
|
||||||
|
mut resample_context: Context,
|
||||||
|
mut sample_array: Vec<f32>,
|
||||||
|
) -> Result<Vec<f32>, BlissError> {
|
||||||
|
let mut resampled = ffmpeg::frame::Audio::empty();
|
||||||
|
for decoded in rx.iter() {
|
||||||
|
resample_context
|
||||||
|
.run(&decoded, &mut resampled)
|
||||||
|
.map_err(|e| {
|
||||||
|
BlissError::DecodingError(format!("while trying to resample song: {:?}", e))
|
||||||
|
})?;
|
||||||
|
push_to_sample_array(&resampled, &mut sample_array);
|
||||||
|
}
|
||||||
|
loop {
|
||||||
|
match resample_context.flush(&mut resampled).map_err(|e| {
|
||||||
|
BlissError::DecodingError(format!("while trying to resample song: {:?}", e))
|
||||||
|
})? {
|
||||||
|
Some(_) => {
|
||||||
|
push_to_sample_array(&resampled, &mut sample_array);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
if resampled.samples() == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
push_to_sample_array(&resampled, &mut sample_array);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(sample_array)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_to_sample_array(frame: &ffmpeg::frame::Audio, sample_array: &mut Vec<f32>) {
|
||||||
|
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<f32> = 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);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -103,7 +103,7 @@ pub(crate) fn geometric_mean(input: &[f32]) -> f32 {
|
||||||
for ch in input.chunks_exact(8) {
|
for ch in input.chunks_exact(8) {
|
||||||
let mut m;
|
let mut m;
|
||||||
m = (ch[0] as f64 * ch[1] as f64) * (ch[2] as f64 * ch[3] as f64);
|
m = (ch[0] as f64 * ch[1] as f64) * (ch[2] as f64 * ch[3] as f64);
|
||||||
m *= 3.27339060789614187e150; // 2^500 : avoid underflows and denormals
|
m *= 3.273390607896142e150; // 2^500 : avoid underflows and denormals
|
||||||
m *= (ch[4] as f64 * ch[5] as f64) * (ch[6] as f64 * ch[7] as f64);
|
m *= (ch[4] as f64 * ch[5] as f64) * (ch[6] as f64 * ch[7] as f64);
|
||||||
if m == 0. {
|
if m == 0. {
|
||||||
return 0.;
|
return 0.;
|
||||||
|
|
Loading…
Reference in a new issue