Final touches to adhere to the Rust API Guidelines
This commit is contained in:
parent
e29056aaa8
commit
e2e2f2cad9
8 changed files with 200 additions and 378 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
|
306
Cargo.lock
generated
306
Cargo.lock
generated
|
@ -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"
|
||||
|
|
10
Cargo.toml
10
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"] }
|
||||
|
|
|
@ -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, ..]))
|
||||
|
|
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))]
|
||||
#![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<f32>,
|
||||
}
|
||||
|
||||
#[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<String>) -> Vec<Result<Song, BlissError>> {
|
|||
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![
|
||||
|
@ -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
|
||||
],
|
||||
|
|
|
@ -68,6 +68,7 @@ pub trait Library {
|
|||
}
|
||||
let num_cpus = num_cpus::get();
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
let (tx, rx): (
|
||||
Sender<(String, Result<Song, BlissError>)>,
|
||||
Receiver<(String, Result<Song, BlissError>)>,
|
||||
|
@ -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"
|
||||
))),
|
||||
);
|
||||
}
|
||||
|
|
171
src/song.rs
171
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<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);
|
||||
}
|
||||
|
||||
#[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<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)
|
||||
/// 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.
|
||||
///
|
||||
/// All the numbers are between -1 and 1.
|
||||
pub analysis: Vec<f32>,
|
||||
}
|
||||
|
||||
impl Song {
|
||||
#[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 {
|
||||
let a1 = arr1(&self.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))
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
let raw_song = Song::decode(&path)?;
|
||||
|
||||
|
@ -172,6 +159,7 @@ impl Song {
|
|||
Ok(chroma_desc.get_values())
|
||||
});
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
let child_timbral: thread::ScopedJoinHandle<
|
||||
'_,
|
||||
Result<(Vec<f32>, Vec<f32>, Vec<f32>), BlissError>,
|
||||
|
@ -238,7 +226,7 @@ impl Song {
|
|||
let stream = format
|
||||
.streams()
|
||||
.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.",
|
||||
)))?;
|
||||
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)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -103,7 +103,7 @@ pub(crate) fn geometric_mean(input: &[f32]) -> f32 {
|
|||
for ch in input.chunks_exact(8) {
|
||||
let mut m;
|
||||
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);
|
||||
if m == 0. {
|
||||
return 0.;
|
||||
|
|
Loading…
Reference in a new issue