Music and controls!!!!! 🎉
This commit is contained in:
parent
02fbd15f44
commit
c211e79658
4
.gitmodules
vendored
Normal file
4
.gitmodules
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
[submodule "rodio"]
|
||||
path = rodio
|
||||
url = https://github.com/Alch-Emi/rodio.git
|
||||
branch = new-from-format-reader
|
406
Cargo.lock
generated
406
Cargo.lock
generated
|
@ -36,6 +36,28 @@ version = "0.4.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e"
|
||||
|
||||
[[package]]
|
||||
name = "alsa"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75c4da790adcb2ce5e758c064b4f3ec17a30349f9961d3e5e6c9688b052a9e18"
|
||||
dependencies = [
|
||||
"alsa-sys",
|
||||
"bitflags",
|
||||
"libc",
|
||||
"nix 0.20.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alsa-sys"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "andrew"
|
||||
version = "0.3.1"
|
||||
|
@ -72,9 +94,9 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
|||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.2"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
|
||||
checksum = "269d0f5e68353a7cab87f81e7c736adc008d279a36ebc6a05dfe01193a89f0c9"
|
||||
|
||||
[[package]]
|
||||
name = "ash"
|
||||
|
@ -207,6 +229,25 @@ version = "1.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.56.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2da379dbebc0b76ef63ca68d8fc6e71c0f13e59432e0987e508c1820e6ab5239"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"lazy_static",
|
||||
"lazycell",
|
||||
"peeking_take_while",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.5.2"
|
||||
|
@ -286,6 +327,12 @@ version = "1.4.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
|
||||
|
||||
[[package]]
|
||||
name = "cache-padded"
|
||||
version = "1.2.0"
|
||||
|
@ -321,6 +368,21 @@ dependencies = [
|
|||
"jobserver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cesu8"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
|
||||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27"
|
||||
dependencies = [
|
||||
"nom 5.1.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-expr"
|
||||
version = "0.8.1"
|
||||
|
@ -348,6 +410,17 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa785e9017cb8e8c8045e3f096b7d1ebc4d7337cceccdca8d678a27f788ac133"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"libc",
|
||||
"libloading 0.6.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
version = "4.2.2"
|
||||
|
@ -435,6 +508,16 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "combine"
|
||||
version = "4.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2b2f5d0ee456f3928812dfc8c6d9a1d592b98678f6d56db9b0cd2b7bc6c8db5"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "1.2.2"
|
||||
|
@ -544,6 +627,50 @@ dependencies = [
|
|||
"objc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "coreaudio-rs"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11894b20ebfe1ff903cbdc52259693389eea03b94918a2def2c30c3bf227ad88"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"coreaudio-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "coreaudio-sys"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b7e3347be6a09b46aba228d6608386739fb70beff4f61e07422da87b0bb31fa"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpal"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98f45f0a21f617cd2c788889ef710b63f075c949259593ea09c826f1e47a2418"
|
||||
dependencies = [
|
||||
"alsa",
|
||||
"core-foundation-sys 0.8.3",
|
||||
"coreaudio-rs",
|
||||
"jni",
|
||||
"js-sys",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"mach",
|
||||
"ndk 0.3.0",
|
||||
"ndk-glue 0.3.0",
|
||||
"nix 0.20.2",
|
||||
"oboe",
|
||||
"parking_lot",
|
||||
"stdweb",
|
||||
"thiserror",
|
||||
"web-sys",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.3.0"
|
||||
|
@ -669,6 +796,7 @@ dependencies = [
|
|||
"iced_native",
|
||||
"image",
|
||||
"rfd",
|
||||
"rodio",
|
||||
"symphonia",
|
||||
]
|
||||
|
||||
|
@ -1306,6 +1434,12 @@ dependencies = [
|
|||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "glow"
|
||||
version = "0.7.2"
|
||||
|
@ -1671,6 +1805,20 @@ dependencies = [
|
|||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jni"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec"
|
||||
dependencies = [
|
||||
"cesu8",
|
||||
"combine",
|
||||
"jni-sys",
|
||||
"log",
|
||||
"thiserror",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jni-sys"
|
||||
version = "0.3.0"
|
||||
|
@ -1843,6 +1991,15 @@ dependencies = [
|
|||
"sid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mach"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "malloc_buf"
|
||||
version = "0.0.6"
|
||||
|
@ -1997,7 +2154,32 @@ checksum = "5eb167c1febed0a496639034d0c76b3b74263636045db5489eee52143c246e73"
|
|||
dependencies = [
|
||||
"jni-sys",
|
||||
"ndk-sys",
|
||||
"num_enum",
|
||||
"num_enum 0.4.3",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndk"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8794322172319b972f528bf90c6b467be0079f1fa82780ffb431088e741a73ab"
|
||||
dependencies = [
|
||||
"jni-sys",
|
||||
"ndk-sys",
|
||||
"num_enum 0.5.6",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndk"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d64d6af06fde0e527b1ba5c7b79a6cc89cfc46325b0b2887dffe8f70197e0c3c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"jni-sys",
|
||||
"ndk-sys",
|
||||
"num_enum 0.5.6",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
|
@ -2010,7 +2192,35 @@ dependencies = [
|
|||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"ndk",
|
||||
"ndk 0.2.1",
|
||||
"ndk-macro",
|
||||
"ndk-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndk-glue"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5caf0c24d51ac1c905c27d4eda4fa0635bbe0de596b8f79235e0b17a4d29385"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"ndk 0.3.0",
|
||||
"ndk-macro",
|
||||
"ndk-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndk-glue"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3e9e94628f24e7a3cb5b96a2dc5683acd9230bf11991c2a1677b87695138420"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"ndk 0.4.0",
|
||||
"ndk-macro",
|
||||
"ndk-sys",
|
||||
]
|
||||
|
@ -2022,7 +2232,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "05d1c6307dc424d0f65b9b06e94f88248e6305726b14729fd67a5e47b2dc481d"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro-crate",
|
||||
"proc-macro-crate 0.1.5",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
|
@ -2083,6 +2293,16 @@ dependencies = [
|
|||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "5.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.0"
|
||||
|
@ -2094,6 +2314,17 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-derive"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.44"
|
||||
|
@ -2152,7 +2383,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "ca565a7df06f3d4b485494f25ba05da1435950f4dc263440eda7a6fa9b8e36e4"
|
||||
dependencies = [
|
||||
"derivative",
|
||||
"num_enum_derive",
|
||||
"num_enum_derive 0.4.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "720d3ea1055e4e4574c0c0b0f8c3fd4f24c4cdaf465948206dea090b57b526ad"
|
||||
dependencies = [
|
||||
"num_enum_derive 0.5.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2161,7 +2401,19 @@ version = "0.4.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffa5a33ddddfee04c0283a7653987d634e880347e96b5b2ed64de07efb59db9d"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro-crate 0.1.5",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum_derive"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d992b768490d7fe0d8586d9b5745f6c49f557da6d81dc982b1d167ad4edbb21"
|
||||
dependencies = [
|
||||
"proc-macro-crate 1.1.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
|
@ -2206,6 +2458,29 @@ dependencies = [
|
|||
"objc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oboe"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e15e22bc67e047fe342a32ecba55f555e3be6166b04dd157cd0f803dfa9f48e1"
|
||||
dependencies = [
|
||||
"jni",
|
||||
"ndk 0.4.0",
|
||||
"ndk-glue 0.4.0",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"oboe-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oboe-sys"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "338142ae5ab0aaedc8275aa8f67f460e43ae0fca76a695a742d56da0a269eadc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.9.0"
|
||||
|
@ -2301,6 +2576,12 @@ dependencies = [
|
|||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "peeking_take_while"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.1.0"
|
||||
|
@ -2384,6 +2665,16 @@ dependencies = [
|
|||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.36"
|
||||
|
@ -2511,6 +2802,21 @@ dependencies = [
|
|||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
|
||||
dependencies = [
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "rfd"
|
||||
version = "0.6.3"
|
||||
|
@ -2534,6 +2840,14 @@ dependencies = [
|
|||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rodio"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"cpal",
|
||||
"symphonia",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
|
@ -2631,6 +2945,12 @@ dependencies = [
|
|||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
|
||||
|
||||
[[package]]
|
||||
name = "sid"
|
||||
version = "0.6.1"
|
||||
|
@ -2779,6 +3099,12 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "stdweb"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef5430c8e36b713e13b48a9f709cc21e046723fe44ce34587b73a830203b533e"
|
||||
|
||||
[[package]]
|
||||
name = "storage-map"
|
||||
version = "0.3.0"
|
||||
|
@ -2826,16 +3152,15 @@ checksum = "8fb1df15f412ee2e9dfc1c504260fa695c1c3f10fe9f4a6ee2d2184d7d6450e2"
|
|||
|
||||
[[package]]
|
||||
name = "symphonia"
|
||||
version = "0.4.0"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7e5f38aa07e792f4eebb0faa93cee088ec82c48222dd332897aae1569d9a4b7"
|
||||
checksum = "9fae959d5ea7b4cd0cd8db3b899ec4f549b0d8a298694826a36ae7e5f19d4aa6"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"symphonia-bundle-flac",
|
||||
"symphonia-bundle-mp3",
|
||||
"symphonia-codec-aac",
|
||||
"symphonia-codec-pcm",
|
||||
"symphonia-codec-vorbis",
|
||||
"symphonia-core",
|
||||
"symphonia-format-isomp4",
|
||||
"symphonia-format-ogg",
|
||||
|
@ -2845,9 +3170,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "symphonia-bundle-flac"
|
||||
version = "0.4.0"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "116e5412f5fb4e5d07efd6628d50d6fcd7a61ebef43d98f5012f3cf763b25d02"
|
||||
checksum = "b237be42d0ff1ff64c6e073aea4f93985ca51de93b8279f16a4b006e3e5997af"
|
||||
dependencies = [
|
||||
"log",
|
||||
"symphonia-core",
|
||||
|
@ -2857,9 +3182,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "symphonia-bundle-mp3"
|
||||
version = "0.4.0"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec4d97c4a61ece4651751dddb393ebecb7579169d9e758ae808fe507a5250790"
|
||||
checksum = "b9f596fe16d2ae06e9404558644b61e27e2dcfee6c511f559be4699c403283fa"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"lazy_static",
|
||||
|
@ -2870,9 +3195,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "symphonia-codec-aac"
|
||||
version = "0.4.0"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd3d7ab37eb9b7df16ddedd7adb7cc382afe708ff078e525a14dc9b05e57558f"
|
||||
checksum = "9e636422dccfb202b24f8066b8d3ebfa914bbc690dae78db52b54691ba916c3f"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
|
@ -2881,43 +3206,32 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "symphonia-codec-pcm"
|
||||
version = "0.4.0"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba1d54738758993546107e3a4be2c1da827f2d4489fcffee0fa47867254e44c7"
|
||||
checksum = "021d8161b186bea81c7cf4a80c67c71fb53862cd9f426cd3e032ae09bdd42dec"
|
||||
dependencies = [
|
||||
"log",
|
||||
"symphonia-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-codec-vorbis"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a29ed6748078effb35a05064a451493a78038918981dc1a76bdf5a2752d441fa"
|
||||
dependencies = [
|
||||
"log",
|
||||
"symphonia-core",
|
||||
"symphonia-utils-xiph",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-core"
|
||||
version = "0.4.0"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa135e97be0f4a666c31dfe5ef4c75435ba3d355fd6a73d2100aa79b14c104c9"
|
||||
checksum = "c1742e06f50b4a7ed7abee53433231e050a248b498cd0ae2c639c8a70b115001"
|
||||
dependencies = [
|
||||
"arrayvec 0.7.2",
|
||||
"arrayvec 0.6.1",
|
||||
"bitflags",
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
"lazy_static",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-format-isomp4"
|
||||
version = "0.4.0"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "feee3a7711e7ec1b7540756f3868bbb3cbb0d1195569b9bc26471a24a02f57b5"
|
||||
checksum = "bf3c6b6ca3347caa22d72f04cfd509f0c32683b0dbe01d0cfb63fd2726ac6da5"
|
||||
dependencies = [
|
||||
"encoding_rs",
|
||||
"log",
|
||||
|
@ -2927,9 +3241,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "symphonia-format-ogg"
|
||||
version = "0.4.0"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7b2357288a79adfec532cfd86049696cfa5c58efeff83bd51687a528f18a519"
|
||||
checksum = "4898eef85c5a05136e1f3b5ff1afe4423cd7642c8979ed007cc8924b9a4c18c1"
|
||||
dependencies = [
|
||||
"log",
|
||||
"symphonia-core",
|
||||
|
@ -2939,9 +3253,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "symphonia-format-wav"
|
||||
version = "0.4.0"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da3586e944a951f3ff19ae14d3f46643c063784f119bffb091fc536102909575"
|
||||
checksum = "97e863d9a912ea518dfae664292e38b2b961a1eb780251eba89b60e3905fef37"
|
||||
dependencies = [
|
||||
"log",
|
||||
"symphonia-core",
|
||||
|
@ -2950,9 +3264,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "symphonia-metadata"
|
||||
version = "0.4.0"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5260599daba18d8fe905ca3eb3b42ba210529a6276886632412cc74984e79b1a"
|
||||
checksum = "db5e36e38a7400f968569135e7ac0f8647de42e93905ad41c79d583aaeae565c"
|
||||
dependencies = [
|
||||
"encoding_rs",
|
||||
"lazy_static",
|
||||
|
@ -2962,9 +3276,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "symphonia-utils-xiph"
|
||||
version = "0.4.0"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a37026c6948ff842e0bf94b4008579cc71ab16ed0ff9ca70a331f60f4f1e1e9"
|
||||
checksum = "47377d86d61acf4d5b1a054b8e7a7cac8266155577a5410e4d746aec6394c42a"
|
||||
dependencies = [
|
||||
"symphonia-core",
|
||||
"symphonia-metadata",
|
||||
|
@ -3629,8 +3943,8 @@ dependencies = [
|
|||
"log",
|
||||
"mio",
|
||||
"mio-extras",
|
||||
"ndk",
|
||||
"ndk-glue",
|
||||
"ndk 0.2.1",
|
||||
"ndk-glue 0.2.1",
|
||||
"ndk-sys",
|
||||
"objc",
|
||||
"parking_lot",
|
||||
|
@ -3690,7 +4004,7 @@ version = "0.3.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7"
|
||||
dependencies = [
|
||||
"nom",
|
||||
"nom 7.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
10
Cargo.toml
10
Cargo.toml
|
@ -20,9 +20,9 @@ iced_native = "0.4.0"
|
|||
rfd = "0.6.3"
|
||||
|
||||
[dependencies.symphonia]
|
||||
# Music decoding, playing, and metadata parsing
|
||||
# Music decoding and metadata parsing
|
||||
features = ["isomp4", "aac", "mp3"]
|
||||
version = "0.4.0"
|
||||
version = "0.3.0"
|
||||
|
||||
[dependencies.iced]
|
||||
# Display windows & graphics
|
||||
|
@ -33,3 +33,9 @@ version = "0.3.0"
|
|||
# Display windows & graphics
|
||||
features = ["smol"]
|
||||
version = "0.3.0"
|
||||
|
||||
[dependencies.rodio]
|
||||
# Playing audio
|
||||
default-features = false
|
||||
features = ["symphonia"]
|
||||
path = "./rodio/"
|
||||
|
|
1
rodio
Submodule
1
rodio
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 706abafcadab2c0049d7a7724cae43f203ba0f67
|
33
src/app.rs
33
src/app.rs
|
@ -1,3 +1,4 @@
|
|||
use crate::controls::ControlsEvent;
|
||||
use crate::load_song::load_song;
|
||||
use crate::editor::Editor;
|
||||
use crate::file_select::FileSelector;
|
||||
|
@ -34,6 +35,7 @@ pub enum Message {
|
|||
PromptForFile,
|
||||
FileOpened(PathBuf),
|
||||
Resized(u32, u32),
|
||||
ControlsEvent(ControlsEvent),
|
||||
Null,
|
||||
}
|
||||
|
||||
|
@ -111,7 +113,12 @@ impl Application for DelyriumApp {
|
|||
if let Some(lyrics) = self.lyrics_component.as_mut() {
|
||||
lyrics.notify_resized(w, h);
|
||||
}
|
||||
}
|
||||
},
|
||||
Message::ControlsEvent(e) => {
|
||||
if let Some(lyrics) = self.lyrics_component.as_mut() {
|
||||
lyrics.handle_controls_event(e);
|
||||
}
|
||||
},
|
||||
Message::Null => { },
|
||||
}
|
||||
|
||||
|
@ -150,15 +157,21 @@ impl Application for DelyriumApp {
|
|||
}
|
||||
});
|
||||
|
||||
let fps30 = time::every(Duration::from_millis(1000 / 30)).map(|_| Message::Tick);
|
||||
|
||||
if self.lyrics_component.is_none() {
|
||||
Subscription::batch([
|
||||
runtime_events,
|
||||
fps30
|
||||
])
|
||||
let is_animating = if let Some(editor) = &self.lyrics_component {
|
||||
editor.is_animating()
|
||||
} else {
|
||||
runtime_events
|
||||
}
|
||||
true
|
||||
};
|
||||
|
||||
let fps30 = if is_animating {
|
||||
time::every(Duration::from_millis(1000 / 30)).map(|_| Message::Tick)
|
||||
} else {
|
||||
Subscription::none()
|
||||
};
|
||||
|
||||
Subscription::batch([
|
||||
runtime_events,
|
||||
fps30
|
||||
])
|
||||
}
|
||||
}
|
||||
|
|
139
src/controls.rs
Normal file
139
src/controls.rs
Normal file
|
@ -0,0 +1,139 @@
|
|||
use iced::Length;
|
||||
use iced::Color;
|
||||
use symphonia::core::formats::FormatReader;
|
||||
use crate::player::PlayerError;
|
||||
use iced::canvas::event::Status;
|
||||
use iced::canvas::Event;
|
||||
use crate::styles::Theme;
|
||||
use iced::canvas::Frame;
|
||||
use iced::canvas::Geometry;
|
||||
use iced::canvas::Cursor;
|
||||
use iced::Rectangle;
|
||||
use crate::app::Message;
|
||||
use iced::canvas::Program;
|
||||
use iced::Canvas;
|
||||
use iced::mouse::{self, Button};
|
||||
use crate::player::Player;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum ControlsEvent {
|
||||
SeekPosition(f32),
|
||||
TogglePlay,
|
||||
}
|
||||
|
||||
enum ErrorState {
|
||||
Error(PlayerError),
|
||||
NoError {
|
||||
player: Player,
|
||||
has_device: bool
|
||||
}
|
||||
}
|
||||
|
||||
use ErrorState::*;
|
||||
|
||||
pub struct Controls {
|
||||
error_state: ErrorState,
|
||||
}
|
||||
|
||||
impl Controls {
|
||||
pub fn new(song: Box<dyn FormatReader>) -> Self {
|
||||
match Player::new(song) {
|
||||
Ok(player) => {
|
||||
Controls {
|
||||
error_state: NoError {
|
||||
has_device: player.has_output_device(),
|
||||
player,
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
Controls {
|
||||
error_state: Error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn view_progress(&mut self, theme: Theme) -> Canvas<Message, (&Controls, Theme)> {
|
||||
Canvas::new((&*self, theme))
|
||||
.width(Length::Units(50))
|
||||
.height(Length::Fill)
|
||||
}
|
||||
|
||||
pub fn handle_event(&mut self, event: ControlsEvent) {
|
||||
if let NoError { player, has_device } = &mut self.error_state {
|
||||
let result = match event {
|
||||
ControlsEvent::SeekPosition(pos) => player.seek_percentage(pos),
|
||||
ControlsEvent::TogglePlay => player.toggle_play(),
|
||||
};
|
||||
|
||||
match result {
|
||||
Ok(now_has_device) => {
|
||||
*has_device = now_has_device;
|
||||
},
|
||||
Err(e) => {
|
||||
self.error_state = Error(e);
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_playing(&self) -> bool {
|
||||
if let NoError { player, has_device: true } = &self.error_state {
|
||||
player.is_playing()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Program<Message> for (&Controls, Theme) {
|
||||
fn draw(&self, bounds: Rectangle<f32>, _cursor: Cursor) -> Vec<Geometry> {
|
||||
let mut frame = Frame::new(bounds.size());
|
||||
|
||||
match &self.0.error_state {
|
||||
NoError { player, has_device: true } => {
|
||||
let mut background = self.1.text_color;
|
||||
background.a = 0.2;
|
||||
|
||||
frame.fill_rectangle(bounds.position(), bounds.size(), background);
|
||||
|
||||
let mut played_size = bounds.size();
|
||||
played_size.height *= player.position_percent();
|
||||
|
||||
frame.fill_rectangle(bounds.position(), played_size, self.1.text_color);
|
||||
},
|
||||
NoError { player: _, has_device: false } => {
|
||||
let mut background = self.1.text_color;
|
||||
background.a = 0.1;
|
||||
|
||||
frame.fill_rectangle(bounds.position(), bounds.size(), background);
|
||||
},
|
||||
Error(e) => {
|
||||
let background = Color {r: 1., g: 0.1, b: 0.1, a: 1.};
|
||||
|
||||
frame.fill_rectangle(bounds.position(), bounds.size(), background);
|
||||
eprintln!("Error!!! {}", e.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
vec![frame.into_geometry()]
|
||||
}
|
||||
|
||||
fn update(&mut self, event: Event, bounds: Rectangle<f32>, cursor: Cursor) -> (Status, Option<Message>) {
|
||||
match (event, cursor) {
|
||||
(Event::Mouse(mouse::Event::ButtonReleased(Button::Left)), Cursor::Available(pos))
|
||||
if bounds.contains(pos) => {
|
||||
let sought = (pos.y - bounds.position().y) / bounds.size().height;
|
||||
|
||||
(Status::Captured, Some(Message::ControlsEvent(ControlsEvent::SeekPosition(sought))))
|
||||
},
|
||||
(Event::Mouse(mouse::Event::ButtonReleased(Button::Right)), Cursor::Available(pos))
|
||||
if bounds.contains(pos) => {
|
||||
// TODO! This should be somewhere intuitive
|
||||
(Status::Captured, Some(Message::ControlsEvent(ControlsEvent::TogglePlay)))
|
||||
},
|
||||
_ => (Status::Ignored, None),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
use crate::controls::ControlsEvent;
|
||||
use crate::controls::Controls;
|
||||
use crate::lyrics::Lyric;
|
||||
use crate::lyrics::Lyrics;
|
||||
use crate::app::Message;
|
||||
|
@ -18,7 +20,7 @@ use image::GenericImageView;
|
|||
pub struct Editor {
|
||||
lyrics: Lyrics,
|
||||
theme: Theme,
|
||||
song: Box<dyn FormatReader>,
|
||||
controls: Controls,
|
||||
left_peri: Periphery,
|
||||
rite_peri: Periphery,
|
||||
}
|
||||
|
@ -40,13 +42,18 @@ impl Editor {
|
|||
let left_peri = Periphery::new(cover.clone(), true, size);
|
||||
let rite_peri = Periphery::new(cover, false, size);
|
||||
|
||||
let controls = Controls::new(song);
|
||||
|
||||
Self {
|
||||
lyrics: Lyrics::new(),
|
||||
song, theme, left_peri, rite_peri,
|
||||
controls, theme, left_peri, rite_peri,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: work on untangling this mess
|
||||
pub fn handle_controls_event(&mut self, event: ControlsEvent) {
|
||||
self.controls.handle_event(event)
|
||||
}
|
||||
pub fn insert_text(&mut self, text: String) {
|
||||
self.lyrics.insert_text(text);
|
||||
}
|
||||
|
@ -60,6 +67,10 @@ impl Editor {
|
|||
self.lyrics.current_line_mut()
|
||||
}
|
||||
|
||||
pub fn is_animating(&self) -> bool {
|
||||
self.controls.is_playing()
|
||||
}
|
||||
|
||||
pub fn notify_resized(&mut self, w: u32, h: u32) {
|
||||
self.left_peri.notify_resized(w, h);
|
||||
self.rite_peri.notify_resized(w, h);
|
||||
|
@ -70,6 +81,7 @@ impl Editor {
|
|||
Container::new(
|
||||
Row::new()
|
||||
.push(self.left_peri.view())
|
||||
.push(self.controls.view_progress(self.theme))
|
||||
.push(self.lyrics.view(self.theme))
|
||||
.push(self.rite_peri.view())
|
||||
)
|
||||
|
|
|
@ -8,12 +8,10 @@ use std::ffi::OsStr;
|
|||
use symphonia::default;
|
||||
use symphonia::core::io::MediaSourceStream;
|
||||
use symphonia::core::probe::{Hint, ProbeResult};
|
||||
use symphonia::core::meta::MetadataRevision;
|
||||
use symphonia::core::meta::StandardVisualKey;
|
||||
use symphonia::core::formats::FormatReader;
|
||||
|
||||
pub fn load_song(path: &Path) -> Result<ProbeResult, LoadError> {
|
||||
let codecs = default::get_codecs();
|
||||
let probe = default::get_probe();
|
||||
|
||||
let file = OpenOptions::new()
|
||||
|
@ -44,8 +42,9 @@ pub fn extract_cover(format: &mut dyn FormatReader) -> Option<DynamicImage>{
|
|||
format.metadata()
|
||||
.current()
|
||||
.into_iter()
|
||||
.flat_map(MetadataRevision::visuals)
|
||||
.filter_map(|vis| image::load_from_memory(&vis.data).ok().map(|img| (vis.usage, img)))
|
||||
// Replace this whole closure with MetadataRef::usage once we update
|
||||
.flat_map(|meta| meta.visuals().iter().map(|v|(v.data.clone(), v.usage.clone())).collect::<Vec<_>>())
|
||||
.filter_map(|(data, usage)| image::load_from_memory(&data).ok().map(|img| (usage, img)))
|
||||
.max_by_key(|(usage, _)|
|
||||
usage.map(|usage| match usage {
|
||||
StandardVisualKey::FrontCover => 00,
|
||||
|
|
|
@ -9,6 +9,8 @@ mod file_select;
|
|||
mod load_song;
|
||||
mod peripheries;
|
||||
mod editor;
|
||||
mod player;
|
||||
mod controls;
|
||||
|
||||
fn main() {
|
||||
app::DelyriumApp::run(Settings::default()).unwrap();
|
||||
|
|
257
src/player.rs
Normal file
257
src/player.rs
Normal file
|
@ -0,0 +1,257 @@
|
|||
use std::time::Instant;
|
||||
use core::time::Duration;
|
||||
use rodio::Decoder;
|
||||
use symphonia::core::formats::FormatReader;
|
||||
use rodio::source::Buffered;
|
||||
use rodio::OutputStream;
|
||||
use rodio::Sink;
|
||||
use rodio::Source;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
pub type Song = Buffered<Decoder<std::io::Empty>>;
|
||||
|
||||
pub struct Player {
|
||||
// [Buffered] is a pointer to a linked-list representation of the song, and so cloning
|
||||
// it is as cheap as cloning an [Arc]. This should always point to the start of the
|
||||
// song though, and so should not be changed after being initialized.
|
||||
song: Song,
|
||||
sink: Option<(Sink, OutputStream)>,
|
||||
duration: Duration,
|
||||
|
||||
/// The position of the playhead when playback started or was last stopped
|
||||
start_position: Duration,
|
||||
|
||||
/// The [`Instant`] playback started
|
||||
playback_started: Option<Instant>,
|
||||
}
|
||||
|
||||
impl Player {
|
||||
|
||||
/// Create a new player from a song
|
||||
pub fn new(song: Box<dyn FormatReader>) -> Result<Self, PlayerError> {
|
||||
|
||||
let song = Decoder::new_from_format_reader(song)
|
||||
.map_err(PlayerError::DecoderError)?
|
||||
.buffered();
|
||||
|
||||
let duration = get_duration_of_song(song.clone());
|
||||
|
||||
let mut player = Player {
|
||||
sink: None,
|
||||
start_position: Duration::ZERO,
|
||||
playback_started: None,
|
||||
song, duration, };
|
||||
player.get_or_set_sink()?;
|
||||
|
||||
Ok(player)
|
||||
}
|
||||
|
||||
/// Check if an audio sink is available
|
||||
///
|
||||
/// Returns `true` if an output device is currently loaded, or `false` if a device was
|
||||
/// not availble last time we tried to access one.
|
||||
pub fn has_output_device(&self) -> bool {
|
||||
self.sink.is_some()
|
||||
}
|
||||
|
||||
/// Attempt to re-request access to the output sink
|
||||
///
|
||||
/// Returns `&self.sink`
|
||||
///
|
||||
/// Can produce a [PlayerError::PlayError]
|
||||
fn try_set_sink(&mut self) -> Result<Option<&Sink>, PlayerError> {
|
||||
self.sink = Some(OutputStream::try_default())
|
||||
.and_then(|result|
|
||||
if let Err(&rodio::StreamError::NoDevice) = result.as_ref() {
|
||||
None // This is okay and doesn't need to raise an error
|
||||
} else {
|
||||
Some(result) // We'll report this error
|
||||
})
|
||||
.transpose()
|
||||
.map_err(PlayerError::StreamError)?
|
||||
.map(|(stream, handle)| Sink::try_new(&handle).map(|sink| (sink, stream)))
|
||||
.transpose()
|
||||
.map_err(PlayerError::PlayError)?;
|
||||
|
||||
Ok(self.sink.as_ref().map(|(s, _)| s))
|
||||
}
|
||||
|
||||
/// Return the current active sink, or attempt to request a new one
|
||||
///
|
||||
/// Equivilent to calling [`try_set_sink()`] if the sink is missing
|
||||
fn get_or_set_sink(&mut self) -> Result<Option<&Sink>, PlayerError> {
|
||||
if self.sink.is_some() {
|
||||
Ok(self.sink.as_ref().map(|(s, _)| s))
|
||||
} else {
|
||||
let song = self.song.clone();
|
||||
let out = self.try_set_sink();
|
||||
|
||||
if let Ok(Some(sink)) = out.as_ref() {
|
||||
sink.pause();
|
||||
sink.append(song);
|
||||
}
|
||||
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to resume playback, or start fresh
|
||||
///
|
||||
/// Returns `true` if playback was resumed, `false` if there is no audio sink
|
||||
/// available to start play, and [PlayerError] if there was a problem requesting
|
||||
/// access to the audio sink.
|
||||
pub fn play(&mut self) -> Result<bool, PlayerError> {
|
||||
if let Some(sink) = self.get_or_set_sink()? {
|
||||
sink.play();
|
||||
self.playback_started = Some(Instant::now());
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
/// Pause playback if playing, do nothing otherwise
|
||||
///
|
||||
/// May be resumed later with [`play()`]
|
||||
pub fn pause(&mut self) {
|
||||
if let Some((sink, _)) = &self.sink {
|
||||
sink.pause();
|
||||
self.start_position = self.position();
|
||||
self.playback_started = None;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the song is currently playing
|
||||
///
|
||||
/// That is, if playback has started, has not been paused, and has not ended naturally
|
||||
/// yet.
|
||||
pub fn is_playing(&self) -> bool {
|
||||
self.playback_started.is_some() && self.position() < self.duration
|
||||
}
|
||||
|
||||
/// Toggle playing
|
||||
///
|
||||
/// This calls [`play()`] is the track is currently paused or stopped, and calls
|
||||
/// [`pause()`] is the track is currently playing. If the track was already playing,
|
||||
/// this will always return `Ok(true)`.
|
||||
pub fn toggle_play(&mut self) -> Result<bool, PlayerError> {
|
||||
if self.position() == self.duration {
|
||||
self.seek(Duration::ZERO)
|
||||
.and_then(|device| if device { self.play() } else { Ok(false) } )
|
||||
} else if self.playback_started.is_none() {
|
||||
self.play()
|
||||
} else {
|
||||
self.pause();
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the duration of this track
|
||||
///
|
||||
/// This is cheap and exact. All processing is done before hand.
|
||||
pub fn get_duration(&self) -> Duration {
|
||||
self.duration
|
||||
}
|
||||
|
||||
/// Attempt to seek to a given duration
|
||||
///
|
||||
/// This is pretty expensive, since due to technical limitations, this means resetting
|
||||
/// the player to the beginning before skipping the given duration.
|
||||
///
|
||||
/// This can fail if there is no current output sink, and the attempt to access a
|
||||
/// new one fails. If this is the case, false will be returned, unless the reason the
|
||||
/// access failed was due to an error.
|
||||
pub fn seek(&mut self, duration: Duration) -> Result<bool, PlayerError> {
|
||||
|
||||
let was_stopped = self.sink
|
||||
.as_mut()
|
||||
.map(|(s, _)| s.is_paused() || s.empty())
|
||||
.unwrap_or(false);
|
||||
|
||||
let song = self.song.clone();
|
||||
if let Some(sink) = self.try_set_sink()? {
|
||||
sink.pause();
|
||||
sink.append(
|
||||
song.skip_duration(duration)
|
||||
);
|
||||
if was_stopped {
|
||||
self.playback_started = None;
|
||||
} else {
|
||||
sink.play();
|
||||
self.playback_started = Some(Instant::now());
|
||||
}
|
||||
self.start_position = duration;
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
/// Seek to a specific percentage (out of 1.0)
|
||||
///
|
||||
/// Performs a [`seek()`] operation, seeking to a given percentage of the song's full
|
||||
/// length. See [`seek()`] for more details
|
||||
pub fn seek_percentage(&mut self, percent: f32) -> Result<bool, PlayerError> {
|
||||
self.seek(
|
||||
self.duration.mul_f32(percent)
|
||||
)
|
||||
}
|
||||
|
||||
/// How far into the song the playback head currently is
|
||||
///
|
||||
/// Computes the duration of the song that is before the playback head. This is
|
||||
/// really an approximation based on how much time has elapsed since playback started,
|
||||
/// but it should be close enough for almost all purposes.
|
||||
pub fn position(&self) -> Duration {
|
||||
self.duration.min(
|
||||
self.start_position +
|
||||
self.playback_started.map_or(
|
||||
Duration::ZERO,
|
||||
|ts| Instant::now() - ts,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/// Computes the position as a fraction of the song duration
|
||||
///
|
||||
/// 0.0 represents the beginning of the song, while 1.0 represents the end. See
|
||||
/// [`position()`] for more information.
|
||||
pub fn position_percent(&self) -> f32 {
|
||||
// nightly: self.position().div_duration_f32(self.duration)
|
||||
self.position().as_secs_f32() / self.duration.as_secs_f32()
|
||||
}
|
||||
}
|
||||
|
||||
/// Manually calculate the exact length of a given song
|
||||
///
|
||||
/// This is really expensive, and involves decoding and inefficiently counting the number
|
||||
/// of samples in the song, but produces an exact output. Use sparingly.
|
||||
fn get_duration_of_song(song: Song) -> Duration {
|
||||
let sample_rate = song.sample_rate() as u64;
|
||||
let n_channels = song.channels() as u64;
|
||||
let n_samples = song.count() as u64; // expensive!
|
||||
let n_whole_seconds = n_samples / n_channels / sample_rate;
|
||||
let remaining_samples = n_samples % sample_rate;
|
||||
let n_nanos = remaining_samples * 1_000_000_000 / sample_rate;
|
||||
Duration::new(n_whole_seconds, n_nanos as u32)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PlayerError {
|
||||
DecoderError(rodio::decoder::DecoderError),
|
||||
PlayError(rodio::PlayError),
|
||||
StreamError(rodio::StreamError),
|
||||
}
|
||||
|
||||
impl fmt::Display for PlayerError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::DecoderError(e) => write!(f, "Could not decode the provided song: {}", e),
|
||||
Self::PlayError(e) => write!(f, "Problem playing to the audio output: {}", e),
|
||||
Self::StreamError(e) => write!(f, "Problem connecting to the audio output: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for PlayerError { }
|
Loading…
Reference in a new issue