Compare commits
15 Commits
20983a8c08
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66c431f20c | ||
|
|
b332d75c7f | ||
|
|
6deb31836f | ||
|
|
13326a49d9 | ||
|
|
20b8f6d4db | ||
|
|
4dd5587b49 | ||
|
|
ae215ce252 | ||
|
|
a9abc2e05b | ||
|
|
ee133379f2 | ||
|
|
b718334392 | ||
|
|
c2b2517661 | ||
|
|
4451917154 | ||
|
|
086e194a1d | ||
|
|
4c788f7313 | ||
|
|
3d8adc0d52 |
787
Cargo.lock
generated
787
Cargo.lock
generated
@@ -2,12 +2,64 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "alsa"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5915f52fe2cf65e83924d037b6c5290b7cee097c6b5c8700746e6168a343fd6b"
|
||||
dependencies = [
|
||||
"alsa-sys",
|
||||
"bitflags",
|
||||
"libc",
|
||||
"nix",
|
||||
]
|
||||
|
||||
[[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 = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
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 = "bitflags"
|
||||
version = "1.3.2"
|
||||
@@ -42,6 +94,18 @@ dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.1.0"
|
||||
@@ -53,6 +117,24 @@ name = "cc"
|
||||
version = "1.0.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
|
||||
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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@@ -60,6 +142,113 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cc00842eed744b858222c4c9faf7243aafc6d33f92f96935263ef4d8a41ce21"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"libc",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8c93436c21e4698bacadf42917db28b23017027a4deccb35dbe47a7e7840123"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"os_str_bytes",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"textwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "3.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da95d038ede1a964ce99f49cbe27a7fb538d1da595e4b4f70b8c8f338d17bf16"
|
||||
dependencies = [
|
||||
"heck 0.4.0",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "claxon"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bfbf56724aa9eca8afa4fcfadeb479e722935bb2a0900c2d37e0cc477af0688"
|
||||
|
||||
[[package]]
|
||||
name = "combine"
|
||||
version = "4.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50b727aacc797f9fc28e355d21f34709ac4fc9adecfe470ad07b8f4464f53062"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
|
||||
|
||||
[[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.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca4679a59dbd8c15f064c012dfe8c1163b9453224238b59bb9328c142b8b248b"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpal"
|
||||
version = "0.13.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74117836a5124f3629e4b474eed03e479abaf98988b4bb317e29f08cfe0e4116"
|
||||
dependencies = [
|
||||
"alsa",
|
||||
"core-foundation-sys",
|
||||
"coreaudio-rs",
|
||||
"jni",
|
||||
"js-sys",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"mach",
|
||||
"ndk",
|
||||
"ndk-glue",
|
||||
"nix",
|
||||
"oboe",
|
||||
"parking_lot",
|
||||
"stdweb",
|
||||
"thiserror",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "custom_debug"
|
||||
version = "0.5.0"
|
||||
@@ -80,6 +269,41 @@ dependencies = [
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0d720b8683f8dd83c65155f0530560cba68cd2bf395f6513a483caee57ff7f4"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a340f241d2ceed1deb47ae36c4144b2707ec7dd0b649f894cb39bb595986324"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72c41b3b7352feb3211a0d743dc5700a4e3b60f51bd2b368892d1e0f9a95f44b"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dbus"
|
||||
version = "0.9.5"
|
||||
@@ -124,6 +348,12 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.19"
|
||||
@@ -224,6 +454,12 @@ dependencies = [
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.11.2"
|
||||
@@ -239,6 +475,12 @@ dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
@@ -254,6 +496,18 @@ version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hound"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549"
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.8.0"
|
||||
@@ -279,12 +533,67 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
|
||||
|
||||
[[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"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "lewton"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "777b48df9aaab155475a83a7df3070395ea1ac6902f5cd062b8f2b028075c030"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"ogg",
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.113"
|
||||
@@ -300,6 +609,16 @@ dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.4"
|
||||
@@ -324,6 +643,15 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mach"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.1"
|
||||
@@ -339,6 +667,26 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimp3"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "985438f75febf74c392071a975a29641b420dd84431135a6e6db721de4b74372"
|
||||
dependencies = [
|
||||
"minimp3-sys",
|
||||
"slice-deque",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimp3-sys"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e21c73734c69dc95696c9ed8926a2b393171d98b3f5f5935686a26a487ab9b90"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.7.14"
|
||||
@@ -361,6 +709,62 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndk"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"jni-sys",
|
||||
"ndk-sys",
|
||||
"num_enum",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndk-context"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e3c5cc68637e21fe8f077f6a1c9e0b9ca495bb74895226b476310f613325884"
|
||||
|
||||
[[package]]
|
||||
name = "ndk-glue"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9ffb7443daba48349d545028777ca98853b018b4c16624aa01223bc29e078da"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"ndk",
|
||||
"ndk-context",
|
||||
"ndk-macro",
|
||||
"ndk-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndk-macro"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndk-sys"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97"
|
||||
dependencies = [
|
||||
"jni-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.23.1"
|
||||
@@ -374,6 +778,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 = "ntapi"
|
||||
version = "0.3.6"
|
||||
@@ -413,12 +827,74 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9"
|
||||
dependencies = [
|
||||
"num_enum_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum_derive"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oboe"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2463c8f2e19b4e0d0710a21f8e4011501ff28db1c95d7a5482a553b2100502d2"
|
||||
dependencies = [
|
||||
"jni",
|
||||
"ndk",
|
||||
"ndk-glue",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"oboe-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oboe-sys"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3370abb7372ed744232c12954d920d1a40f1c4686de9e79e800021ef492294bd"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ogg"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6951b4e8bf21c8193da321bcce9c9dd2e13c858fe078bf9054a288b419ae5d6e"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.2"
|
||||
@@ -444,6 +920,20 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pattern_derive"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "peeking_take_while"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.0.10"
|
||||
@@ -482,6 +972,46 @@ version = "0.3.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.36"
|
||||
@@ -500,6 +1030,36 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.10"
|
||||
@@ -509,24 +1069,71 @@ dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
|
||||
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 = "rodio"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0939e9f626e6c6f1989adb6226a039c855ca483053f0ee7c98b90e41cf731e"
|
||||
dependencies = [
|
||||
"claxon",
|
||||
"cpal",
|
||||
"hound",
|
||||
"lewton",
|
||||
"minimp3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust_lum"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bluer",
|
||||
"clap",
|
||||
"futures",
|
||||
"pattern_derive",
|
||||
"rand",
|
||||
"rodio",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_yaml",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
@@ -576,6 +1183,12 @@ dependencies = [
|
||||
"yaml-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.0"
|
||||
@@ -591,12 +1204,35 @@ version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5"
|
||||
|
||||
[[package]]
|
||||
name = "slice-deque"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31ef6ee280cdefba6d2d0b4b78a84a1c1a3f3a4cec98c2d4231c8bc225de0f25"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mach",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
|
||||
|
||||
[[package]]
|
||||
name = "stdweb"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef5430c8e36b713e13b48a9f709cc21e046723fe44ce34587b73a830203b533e"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.22.0"
|
||||
@@ -612,7 +1248,7 @@ version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "339f799d8b549e3744c7ac7feb216383e4005d94bdb22561b3ab8f3b808ae9fb"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"heck 0.3.3",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
@@ -641,6 +1277,56 @@ dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.15.0"
|
||||
@@ -682,6 +1368,15 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.8.0"
|
||||
@@ -703,12 +1398,93 @@ dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.2+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@@ -725,6 +1501,15 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
|
||||
@@ -9,7 +9,11 @@ edition = "2018"
|
||||
bluer = "0.13.1"
|
||||
tokio = {version = "1.15.0", features = ["full"]}
|
||||
futures = "0.3.19"
|
||||
rand = "0.8.5"
|
||||
rodio = "0.15.0"
|
||||
clap = { version = "3.1.6", features = ["derive"] }
|
||||
pattern_derive = { path = "pattern_derive" }
|
||||
|
||||
serde_derive = "1.0.136"
|
||||
serde = "1.0.136"
|
||||
serde_yaml = "0.8"
|
||||
serde_yaml = "0.8"
|
||||
|
||||
117
spectacle.yml
117
spectacle.yml
@@ -1,36 +1,81 @@
|
||||
- pattern:
|
||||
type: Fade
|
||||
current_iteration: 0
|
||||
nbr_iterations: 20
|
||||
begin_color:
|
||||
red: 255
|
||||
green: 0
|
||||
blue: 0
|
||||
end_color:
|
||||
red: 0
|
||||
green: 0
|
||||
blue: 255
|
||||
period:
|
||||
secs: 0
|
||||
nanos: 200000000
|
||||
- pattern:
|
||||
type: ColorWipe
|
||||
current_iteration: 0
|
||||
color:
|
||||
red: 0
|
||||
green: 255
|
||||
blue: 0
|
||||
infinite: false
|
||||
period:
|
||||
secs: 0
|
||||
nanos: 100000000
|
||||
- pattern:
|
||||
type: Scanner
|
||||
current_iteration: 0
|
||||
color:
|
||||
red: 0
|
||||
green: 0
|
||||
blue: 255
|
||||
period:
|
||||
secs: 0
|
||||
nanos: 100000000
|
||||
colors:
|
||||
- &red
|
||||
red: 255
|
||||
green: 0
|
||||
blue: 0
|
||||
|
||||
devices:
|
||||
- name : toto
|
||||
mac_addresses :
|
||||
- C9:81:9C:BA:53:BC
|
||||
sequence:
|
||||
- pattern:
|
||||
type: Rain
|
||||
color: &blue
|
||||
red: 0
|
||||
green: 0
|
||||
blue: 255
|
||||
background_color: &yellow
|
||||
red: 0
|
||||
green: 255
|
||||
blue: 255
|
||||
stability: 10
|
||||
lines: 5
|
||||
period: 200
|
||||
- pattern:
|
||||
type: Blink
|
||||
fade:
|
||||
current_iteration: 10
|
||||
steps: 20
|
||||
begin_color: &black
|
||||
red: 0
|
||||
green: 0
|
||||
blue: 0
|
||||
end_color: &purple
|
||||
red: 255
|
||||
green: 0
|
||||
blue: 255
|
||||
max_iteration: 10
|
||||
period: 50
|
||||
- pattern:
|
||||
type: Fade
|
||||
steps: 20
|
||||
begin_color:
|
||||
red: 255
|
||||
green: 0
|
||||
blue: 0
|
||||
end_color: *blue
|
||||
period: 200
|
||||
- pattern:
|
||||
type: ColorWipe
|
||||
max_iteration: 25
|
||||
color: &green
|
||||
red: 0
|
||||
green: 255
|
||||
blue: 0
|
||||
period: 100
|
||||
- pattern:
|
||||
type: FillRandom
|
||||
color: *blue
|
||||
stability: 40
|
||||
period: 80
|
||||
- name: titi
|
||||
sequence:
|
||||
- pattern:
|
||||
type: StarsRandom
|
||||
color: *green
|
||||
ratio: 8
|
||||
steps: 20
|
||||
background_color:
|
||||
red: 0
|
||||
green: 255
|
||||
blue: 100
|
||||
period: 50
|
||||
- pattern:
|
||||
type: Rainbow
|
||||
max_iteration: 200
|
||||
period: 10
|
||||
- pattern:
|
||||
type: Scanner
|
||||
color: *blue
|
||||
period: 100
|
||||
13
src/args.rs
Normal file
13
src/args.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
use clap::Parser;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
pub struct Cli {
|
||||
/// file for light sequences
|
||||
#[clap(short, long)]
|
||||
pub spectacle_file: String,
|
||||
|
||||
/// musique file that will be played
|
||||
#[clap(short, long)]
|
||||
pub musique_file: String,
|
||||
}
|
||||
18
src/audio.rs
Normal file
18
src/audio.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use rodio::{Decoder, OutputStream, Sink};
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::path::Path;
|
||||
|
||||
pub fn play_sound(path: &Path) {
|
||||
println!("starting musique");
|
||||
let (_stream, stream_handle) = OutputStream::try_default().unwrap();
|
||||
let sink = Sink::try_new(&stream_handle).unwrap();
|
||||
|
||||
let file = BufReader::new(File::open(path).unwrap());
|
||||
let source = Some(Decoder::new(file).unwrap()).unwrap();
|
||||
sink.append(source);
|
||||
|
||||
// The sound plays in a separate thread. This call will block the current thread until the sink
|
||||
// has finished playing all its queued sounds.
|
||||
sink.sleep_until_end();
|
||||
}
|
||||
@@ -39,7 +39,6 @@ pub(crate) async fn bluetooth_scan(
|
||||
while let Some(evt) = discover.next().await {
|
||||
match evt {
|
||||
AdapterEvent::DeviceAdded(addr) => {
|
||||
println!("new device {}", addr);
|
||||
if already_scanned.contains(&addr) {
|
||||
continue;
|
||||
}
|
||||
@@ -49,7 +48,6 @@ pub(crate) async fn bluetooth_scan(
|
||||
Ok(service_found) => {
|
||||
if service_found {
|
||||
tx.send(device).await.unwrap();
|
||||
println!("found service in device {}", addr);
|
||||
}
|
||||
}
|
||||
Err(_) => continue,
|
||||
|
||||
24
src/config.rs
Normal file
24
src/config.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
use crate::pixel_color::PixelColor;
|
||||
use crate::runner::DeviceSequence;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
use std::fs;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Config<const N: usize> {
|
||||
devices: Vec<Device<N>>,
|
||||
colors: Vec<PixelColor>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct Device<const N: usize> {
|
||||
pub mac_addresses: Option<HashSet<String>>,
|
||||
name: Option<String>,
|
||||
pub sequence: DeviceSequence<N>,
|
||||
}
|
||||
|
||||
pub fn load_from_file<const N: usize>(file: &std::path::Path) -> Vec<Device<N>> {
|
||||
let file = fs::read_to_string(file).unwrap();
|
||||
let config: Config<N> = serde_yaml::from_str(&*file).unwrap();
|
||||
config.devices
|
||||
}
|
||||
115
src/main.rs
115
src/main.rs
@@ -1,14 +1,22 @@
|
||||
mod args;
|
||||
mod audio;
|
||||
mod bluetooth;
|
||||
mod config;
|
||||
mod patterns;
|
||||
mod pixel_color;
|
||||
mod runner;
|
||||
mod spectacle;
|
||||
|
||||
use crate::patterns::{ColorWipe, Fade, Patterns, PixelColor};
|
||||
use crate::runner::Runner;
|
||||
use crate::args::Cli;
|
||||
use crate::audio::play_sound;
|
||||
use crate::config::load_from_file;
|
||||
use crate::runner::Spectacle;
|
||||
use bluer::gatt::remote::Characteristic;
|
||||
use bluer::{Device, Error};
|
||||
use clap::Parser;
|
||||
use patterns::Strip;
|
||||
use std::path::Path;
|
||||
use std::time::Duration;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::sync::watch;
|
||||
|
||||
@@ -19,48 +27,15 @@ const BASE_STRIP_DATA: [u8; 3] = [0x00, 0x00, 0x01];
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> bluer::Result<()> {
|
||||
let pixel = PixelColor {
|
||||
red: 231,
|
||||
green: 124,
|
||||
blue: 0,
|
||||
};
|
||||
let command_line = Cli::parse();
|
||||
|
||||
let fade = Fade::<25> {
|
||||
current_iteration: 0,
|
||||
nbr_iterations: 0,
|
||||
begin_color: pixel,
|
||||
end_color: Default::default(),
|
||||
};
|
||||
|
||||
let context = runner::Context {
|
||||
pattern: Patterns::Fade(fade),
|
||||
period: Duration::from_millis(300),
|
||||
};
|
||||
|
||||
let mut runner = runner::Runner::<25>::new();
|
||||
runner.push(context);
|
||||
|
||||
let context = runner::Context {
|
||||
pattern: Patterns::ColorWipe(ColorWipe {
|
||||
current_iteration: 0,
|
||||
color: Default::default(),
|
||||
infinite: false,
|
||||
}),
|
||||
period: Default::default(),
|
||||
};
|
||||
|
||||
runner.push(context);
|
||||
|
||||
let yaml = serde_yaml::to_string(&runner).unwrap();
|
||||
println!("{}", yaml);
|
||||
|
||||
let file = Path::new("spectacle.yml");
|
||||
let config: Runner<25> = Runner::load_from_file(file);
|
||||
println!("{:?}", config);
|
||||
let file = Path::new(&command_line.spectacle_file);
|
||||
let config = load_from_file(file);
|
||||
let spectacle = Spectacle::from(config);
|
||||
let spectacle = Arc::new(Mutex::new(spectacle));
|
||||
|
||||
let adapter = bluetooth::create_session().await?;
|
||||
|
||||
let (tx_scan, mut rx_scan) = mpsc::channel(3);
|
||||
let (tx_scan, rx_scan) = mpsc::channel(3);
|
||||
|
||||
tokio::spawn(bluetooth::bluetooth_scan(
|
||||
tx_scan,
|
||||
@@ -68,35 +43,55 @@ async fn main() -> bluer::Result<()> {
|
||||
SERVICE_UUID,
|
||||
));
|
||||
|
||||
let (tx, rx) = watch::channel(Strip::<STRIP_SIZE>::default());
|
||||
tokio::spawn(connect_device(spectacle.clone(), rx_scan));
|
||||
|
||||
tokio::spawn(async move {
|
||||
while let Some(device) = rx_scan.recv().await {
|
||||
bluetooth::connect(&device, 3).await?;
|
||||
let char = bluetooth::get_char(&device, SERVICE_UUID, PIXEL_DATA_UUID).await?;
|
||||
if let Some(char) = char {
|
||||
let mut rx_device = rx.clone();
|
||||
println!("starting");
|
||||
let musique_file = command_line.musique_file;
|
||||
let musique = tokio::task::spawn_blocking(move || play_sound(Path::new(&musique_file)));
|
||||
let futures;
|
||||
{
|
||||
let mut spectacle = spectacle.lock().unwrap();
|
||||
futures = spectacle.run();
|
||||
}
|
||||
let mut joinhandles = vec![];
|
||||
for future in futures {
|
||||
joinhandles.push(tokio::spawn(future));
|
||||
}
|
||||
for joinhandle in joinhandles {
|
||||
joinhandle.await.unwrap();
|
||||
}
|
||||
musique.await.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn connect_device(
|
||||
spectacle: Arc<Mutex<Spectacle<STRIP_SIZE>>>,
|
||||
mut rx_scan: mpsc::Receiver<Device>,
|
||||
) -> Result<(), Error> {
|
||||
while let Some(device) = rx_scan.recv().await {
|
||||
bluetooth::connect(&device, 3).await?;
|
||||
let char = bluetooth::get_char(&device, SERVICE_UUID, PIXEL_DATA_UUID).await?;
|
||||
if let Some(char) = char {
|
||||
let rx: Option<watch::Receiver<Strip<STRIP_SIZE>>>;
|
||||
{
|
||||
let spectacle = spectacle.lock().unwrap();
|
||||
rx = spectacle.get_channel(&device.address());
|
||||
}
|
||||
if let Some(mut rx) = rx {
|
||||
tokio::spawn(async move {
|
||||
println!("device connected : {}", &device.address());
|
||||
while rx_device.changed().await.is_ok() {
|
||||
let strip = *rx_device.borrow();
|
||||
while rx.changed().await.is_ok() {
|
||||
let strip = *rx.borrow();
|
||||
if write_strip(&strip, &char).await.is_err() {
|
||||
break;
|
||||
};
|
||||
}
|
||||
println!("device {} disconnected", &device.address());
|
||||
// drop(rx_device);
|
||||
});
|
||||
}
|
||||
}
|
||||
bluer::Result::Ok(())
|
||||
});
|
||||
|
||||
println!("starting");
|
||||
config.runner_loop(tx).await;
|
||||
tokio::time::sleep(Duration::from_secs(5)).await;
|
||||
println!("error sending value");
|
||||
Ok(())
|
||||
}
|
||||
bluer::Result::Ok(())
|
||||
}
|
||||
|
||||
pub async fn write_strip<const N: usize>(
|
||||
|
||||
381
src/patterns.rs
381
src/patterns.rs
@@ -1,16 +1,95 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::cmp::{max, min, Ordering};
|
||||
use std::convert::Infallible;
|
||||
use std::fmt;
|
||||
use std::ops::Div;
|
||||
mod blink;
|
||||
mod color_wipe;
|
||||
mod fade;
|
||||
mod fade_random;
|
||||
mod fade_unstable;
|
||||
mod fill_random;
|
||||
mod fill_unstable;
|
||||
mod rain;
|
||||
mod rainbow;
|
||||
mod ring;
|
||||
mod ring_scanner;
|
||||
mod scanner;
|
||||
mod stars_random;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
use crate::patterns::blink::Blink;
|
||||
use crate::patterns::color_wipe::ColorWipe;
|
||||
use crate::patterns::fade::Fade;
|
||||
use crate::patterns::fade_random::FadeRandom;
|
||||
use crate::patterns::fade_unstable::FadeUnstable;
|
||||
use crate::patterns::fill_random::FillRandom;
|
||||
use crate::patterns::fill_unstable::FillUnstable;
|
||||
use crate::patterns::rain::Rain;
|
||||
use crate::patterns::rainbow::Rainbow;
|
||||
use crate::patterns::ring::Ring;
|
||||
use crate::patterns::ring_scanner::RingScanner;
|
||||
use crate::patterns::scanner::Scanner;
|
||||
use crate::patterns::stars_random::StarsRandom;
|
||||
use crate::pixel_color::PixelColor;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::ops::{Index, IndexMut};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
#[serde(tag = "type")]
|
||||
pub(crate) enum Patterns<const N: usize> {
|
||||
Rainbow(Rainbow<N>),
|
||||
Fade(Fade<N>),
|
||||
ColorWipe(ColorWipe<N>),
|
||||
Scanner(Scanner<N>),
|
||||
FillRandom(FillRandom<N>),
|
||||
FillUnstable(FillUnstable<N>),
|
||||
Blink(Blink<N>),
|
||||
Rain(Rain<N>),
|
||||
FadeRandom(FadeRandom<N>),
|
||||
FadeUnstable(FadeUnstable<N>),
|
||||
Ring(Ring<N>),
|
||||
RingScanner(RingScanner<N>),
|
||||
StarsRandom(StarsRandom<N>),
|
||||
}
|
||||
|
||||
/// Pattern that have to be implemented
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
pub trait Pattern: Iterator {
|
||||
type Strip;
|
||||
|
||||
fn init(&mut self) -> Option<Self::Strip> {
|
||||
None
|
||||
}
|
||||
fn is_last_iteration(&self) -> bool;
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_pattern_init_background {
|
||||
($generic: tt) => {
|
||||
fn init(&mut self) -> Option<Self::Strip> {
|
||||
if let Some(color) = self.background_color {
|
||||
Some(Strip::<N>::new(color))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_pattern_last_iteration {
|
||||
($name:ty) => {
|
||||
fn is_last_iteration(&self) -> bool {
|
||||
match self.max_iteration {
|
||||
None => false,
|
||||
Some(max_iter) => {
|
||||
if self.current_iteration >= max_iter {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for Patterns<N> {
|
||||
@@ -22,44 +101,83 @@ impl<const N: usize> Iterator for Patterns<N> {
|
||||
Patterns::Fade(p) => p.next(),
|
||||
Patterns::ColorWipe(p) => p.next(),
|
||||
Patterns::Scanner(p) => p.next(),
|
||||
Patterns::FillRandom(p) => p.next(),
|
||||
Patterns::FillUnstable(p) => p.next(),
|
||||
Patterns::Blink(p) => p.next(),
|
||||
Patterns::Rain(p) => p.next(),
|
||||
Patterns::FadeRandom(p) => p.next(),
|
||||
Patterns::FadeUnstable(p) => p.next(),
|
||||
Patterns::Ring(p) => p.next(),
|
||||
Patterns::RingScanner(p) => p.next(),
|
||||
Patterns::StarsRandom(p) => p.next(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct PixelColor {
|
||||
pub(crate) red: u8,
|
||||
pub(crate) green: u8,
|
||||
pub(crate) blue: u8,
|
||||
}
|
||||
impl<const N: usize> Pattern for Patterns<N> {
|
||||
type Strip = Strip<N>;
|
||||
|
||||
impl PixelColor {
|
||||
fn new() -> Self {
|
||||
Default::default()
|
||||
fn init(&mut self) -> Option<Self::Strip> {
|
||||
match self {
|
||||
Patterns::Rainbow(p) => p.init(),
|
||||
Patterns::Fade(p) => p.init(),
|
||||
Patterns::ColorWipe(p) => p.init(),
|
||||
Patterns::Scanner(p) => p.init(),
|
||||
Patterns::FillRandom(p) => p.init(),
|
||||
Patterns::FillUnstable(p) => p.init(),
|
||||
Patterns::Blink(p) => p.init(),
|
||||
Patterns::Rain(p) => p.init(),
|
||||
Patterns::FadeRandom(p) => p.init(),
|
||||
Patterns::FadeUnstable(p) => p.init(),
|
||||
Patterns::Ring(p) => p.init(),
|
||||
Patterns::RingScanner(p) => p.init(),
|
||||
Patterns::StarsRandom(p) => p.init(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PixelColor {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "[{},{},{}]", self.red, self.green, self.blue)
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<u8> for PixelColor {
|
||||
type Output = PixelColor;
|
||||
|
||||
fn div(self, rhs: u8) -> Self::Output {
|
||||
PixelColor {
|
||||
red: self.red / rhs,
|
||||
green: self.green / rhs,
|
||||
blue: self.blue / rhs,
|
||||
fn is_last_iteration(&self) -> bool {
|
||||
match self {
|
||||
Patterns::Rainbow(p) => p.is_last_iteration(),
|
||||
Patterns::Fade(p) => p.is_last_iteration(),
|
||||
Patterns::ColorWipe(p) => p.is_last_iteration(),
|
||||
Patterns::Scanner(p) => p.is_last_iteration(),
|
||||
Patterns::FillRandom(p) => p.is_last_iteration(),
|
||||
Patterns::FillUnstable(p) => p.is_last_iteration(),
|
||||
Patterns::Blink(p) => p.is_last_iteration(),
|
||||
Patterns::Rain(p) => p.is_last_iteration(),
|
||||
Patterns::FadeRandom(p) => p.is_last_iteration(),
|
||||
Patterns::FadeUnstable(p) => p.is_last_iteration(),
|
||||
Patterns::Ring(p) => p.is_last_iteration(),
|
||||
Patterns::RingScanner(p) => p.is_last_iteration(),
|
||||
Patterns::StarsRandom(p) => p.is_last_iteration(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Strip<const N: usize> {
|
||||
pub strip: [PixelColor; N],
|
||||
///
|
||||
/// a basic newtype based on a fix array size.
|
||||
///
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct Strip<const N: usize>([Option<PixelColor>; N]);
|
||||
|
||||
impl<const N: usize> Default for Strip<N> {
|
||||
fn default() -> Self {
|
||||
Strip([None; N])
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Index<usize> for Strip<N> {
|
||||
type Output = Option<PixelColor>;
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
&self.0[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> IndexMut<usize> for Strip<N> {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Option<PixelColor> {
|
||||
&mut self.0[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Strip<N> {
|
||||
@@ -67,204 +185,19 @@ impl<const N: usize> Strip<N> {
|
||||
let mut data: Vec<u8> = vec![];
|
||||
|
||||
for i in 0..N {
|
||||
data.append(&mut vec![
|
||||
self.strip[i].green,
|
||||
self.strip[i].red,
|
||||
self.strip[i].blue,
|
||||
]);
|
||||
let color = self[i].unwrap_or(PixelColor::default());
|
||||
data.append(&mut vec![color.green, color.red, color.blue]);
|
||||
}
|
||||
data
|
||||
}
|
||||
|
||||
pub fn fill(&mut self, color: PixelColor) {
|
||||
self.strip = [color; N];
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Default for Strip<N> {
|
||||
fn default() -> Self {
|
||||
pub fn new(color: PixelColor) -> Strip<N> {
|
||||
Strip {
|
||||
strip: [PixelColor::new(); N],
|
||||
0: [Some(color); N],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Rainbow pattern /////////////////////////////////////////////////
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Rainbow<const N: usize> {
|
||||
pub(crate) current_iteration: usize,
|
||||
pub(crate) max_iteration: Option<usize>,
|
||||
pub(crate) step: Option<usize>,
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for Rainbow<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(nbr_iteration) = self.max_iteration {
|
||||
if nbr_iteration == self.current_iteration {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
let mut strip = Strip::default();
|
||||
let step = self.step.unwrap_or(255 / N);
|
||||
for i in 0..N {
|
||||
let pos = (i * step + self.current_iteration) as u8;
|
||||
strip.strip[i] = wheel(pos)
|
||||
}
|
||||
self.current_iteration += 1;
|
||||
Some(strip)
|
||||
}
|
||||
}
|
||||
|
||||
fn wheel(index: u8) -> PixelColor {
|
||||
let pos = 255 - index;
|
||||
match pos {
|
||||
0..=85 => PixelColor {
|
||||
red: 255 - (pos * 3),
|
||||
green: 0,
|
||||
blue: pos * 3,
|
||||
},
|
||||
86..=170 => {
|
||||
let pos = pos - 85;
|
||||
PixelColor {
|
||||
red: 0,
|
||||
green: pos * 3,
|
||||
blue: 255 - (pos * 3),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let pos = pos - 170;
|
||||
PixelColor {
|
||||
red: pos * 3,
|
||||
green: 255 - (pos * 3),
|
||||
blue: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// Colorwipe pattern ////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct ColorWipe<const N: usize> {
|
||||
pub(crate) current_iteration: usize,
|
||||
pub(crate) color: PixelColor,
|
||||
pub(crate) infinite: bool,
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for ColorWipe<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut strip = Strip::default();
|
||||
if self.infinite {
|
||||
self.current_iteration %= N;
|
||||
} else if self.current_iteration >= N {
|
||||
return None;
|
||||
}
|
||||
|
||||
for i in 0..self.current_iteration {
|
||||
strip.strip[i] = self.color;
|
||||
}
|
||||
self.current_iteration += 1;
|
||||
Some(strip)
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// fade pattern ////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Fade<const N: usize> {
|
||||
pub(crate) current_iteration: usize,
|
||||
pub(crate) nbr_iterations: usize,
|
||||
pub(crate) begin_color: PixelColor,
|
||||
pub(crate) end_color: PixelColor,
|
||||
}
|
||||
|
||||
fn fade_value(value_start: u8, value_end: u8, index: usize, nbr_iter: usize) -> u8 {
|
||||
((value_start as usize * (nbr_iter - index) + value_end as usize * index) / nbr_iter) as u8
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for Fade<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut strip = Strip::default();
|
||||
|
||||
if self.current_iteration >= self.nbr_iterations {
|
||||
return None;
|
||||
}
|
||||
|
||||
let red = fade_value(
|
||||
self.begin_color.red,
|
||||
self.end_color.red,
|
||||
self.current_iteration,
|
||||
self.nbr_iterations,
|
||||
);
|
||||
let green = fade_value(
|
||||
self.begin_color.green,
|
||||
self.end_color.green,
|
||||
self.current_iteration,
|
||||
self.nbr_iterations,
|
||||
);
|
||||
let blue = fade_value(
|
||||
self.begin_color.blue,
|
||||
self.end_color.blue,
|
||||
self.current_iteration,
|
||||
self.nbr_iterations,
|
||||
);
|
||||
|
||||
let current_color = PixelColor { red, green, blue };
|
||||
|
||||
strip.fill(current_color);
|
||||
|
||||
self.current_iteration += 1;
|
||||
Some(strip)
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// scanner pattern ////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Scanner<const N: usize> {
|
||||
pub(crate) current_iteration: usize,
|
||||
pub(crate) color: PixelColor,
|
||||
pub(crate) background_color: Option<PixelColor>,
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for Scanner<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut strip = Strip::<N>::default();
|
||||
if let Some(c) = self.background_color {
|
||||
strip.fill(c);
|
||||
} else {
|
||||
strip.fill(PixelColor::default());
|
||||
}
|
||||
|
||||
let current_led = self.current_iteration % N;
|
||||
let min_led;
|
||||
if current_led < 8 {
|
||||
min_led = 0;
|
||||
} else {
|
||||
min_led = current_led - 8;
|
||||
}
|
||||
let mut c = self.color;
|
||||
for i in min_led..current_led {
|
||||
strip.strip[i] = c;
|
||||
c = c / 2;
|
||||
println!("{}", c);
|
||||
}
|
||||
self.current_iteration += 1;
|
||||
Some(strip)
|
||||
pub fn fill(&mut self, color: PixelColor) {
|
||||
self.0.fill(Some(color));
|
||||
}
|
||||
}
|
||||
|
||||
40
src/patterns/blink.rs
Normal file
40
src/patterns/blink.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
use crate::patterns::fade::Fade;
|
||||
use crate::patterns::Pattern;
|
||||
use crate::{impl_pattern_last_iteration, Strip};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// # Blink
|
||||
///
|
||||
/// fade from one color to an other then go back several times
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct Blink<const N: usize> {
|
||||
#[serde(default)]
|
||||
pub(crate) current_iteration: usize,
|
||||
pub(crate) max_iteration: Option<usize>,
|
||||
fade: Fade<N>,
|
||||
}
|
||||
|
||||
impl<const N: usize> Pattern for Blink<N> {
|
||||
type Strip = Strip<N>;
|
||||
impl_pattern_last_iteration!(ColorWipe);
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for Blink<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let value = self.fade.next();
|
||||
match value {
|
||||
None => {
|
||||
if self.is_last_iteration() {
|
||||
return None;
|
||||
}
|
||||
self.current_iteration += 1;
|
||||
self.fade.current_iteration = 0;
|
||||
std::mem::swap(&mut self.fade.begin_color, &mut self.fade.end_color);
|
||||
self.fade.next()
|
||||
}
|
||||
Some(value) => Some(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
50
src/patterns/color_wipe.rs
Normal file
50
src/patterns/color_wipe.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
use crate::patterns::Pattern;
|
||||
use crate::pixel_color::PixelColor;
|
||||
use crate::{impl_pattern_init_background, impl_pattern_last_iteration, Strip};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// # Colorwipe pattern
|
||||
/// every pattern implement the iterator trait
|
||||
///
|
||||
/// This pattern fill the strip with a specific color, one by one.
|
||||
///
|
||||
/// ### Note
|
||||
///
|
||||
/// setting max_iteration to None lead the pattern to loop infinitely
|
||||
///
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct ColorWipe<const N: usize> {
|
||||
#[serde(default)]
|
||||
pub(crate) current_iteration: usize,
|
||||
pub(crate) max_iteration: Option<usize>,
|
||||
pub(crate) color: PixelColor,
|
||||
pub(crate) background_color: Option<PixelColor>,
|
||||
}
|
||||
|
||||
impl<const N: usize> Pattern for ColorWipe<N> {
|
||||
type Strip = Strip<N>;
|
||||
impl_pattern_init_background!(N);
|
||||
impl_pattern_last_iteration!(ColorWipe);
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for ColorWipe<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.is_last_iteration() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut strip = Strip::default();
|
||||
if let Some(c) = self.background_color {
|
||||
strip.fill(c);
|
||||
}
|
||||
let iteration = self.current_iteration % N;
|
||||
|
||||
for i in 0..iteration {
|
||||
strip[i] = Some(self.color);
|
||||
}
|
||||
self.current_iteration += 1;
|
||||
Some(strip)
|
||||
}
|
||||
}
|
||||
65
src/patterns/fade.rs
Normal file
65
src/patterns/fade.rs
Normal file
@@ -0,0 +1,65 @@
|
||||
use crate::patterns::Pattern;
|
||||
use crate::pixel_color::PixelColor;
|
||||
use crate::Strip;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// # fade pattern
|
||||
/// fade from one color to an other
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct Fade<const N: usize> {
|
||||
#[serde(default)]
|
||||
pub(crate) current_iteration: usize,
|
||||
pub(crate) steps: usize,
|
||||
pub(crate) begin_color: PixelColor,
|
||||
pub(crate) end_color: PixelColor,
|
||||
}
|
||||
|
||||
fn fade_value(value_start: u8, value_end: u8, index: usize, nbr_iter: usize) -> u8 {
|
||||
((value_start as usize * (nbr_iter - index) + value_end as usize * index) / nbr_iter) as u8
|
||||
}
|
||||
|
||||
impl<const N: usize> Pattern for Fade<N> {
|
||||
type Strip = Strip<N>;
|
||||
|
||||
fn is_last_iteration(&self) -> bool {
|
||||
self.current_iteration >= self.steps
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for Fade<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut strip = Strip::default();
|
||||
|
||||
if self.is_last_iteration() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let red = fade_value(
|
||||
self.begin_color.red,
|
||||
self.end_color.red,
|
||||
self.current_iteration,
|
||||
self.steps,
|
||||
);
|
||||
let green = fade_value(
|
||||
self.begin_color.green,
|
||||
self.end_color.green,
|
||||
self.current_iteration,
|
||||
self.steps,
|
||||
);
|
||||
let blue = fade_value(
|
||||
self.begin_color.blue,
|
||||
self.end_color.blue,
|
||||
self.current_iteration,
|
||||
self.steps,
|
||||
);
|
||||
|
||||
let current_color = PixelColor { red, green, blue };
|
||||
|
||||
strip.fill(current_color);
|
||||
|
||||
self.current_iteration += 1;
|
||||
Some(strip)
|
||||
}
|
||||
}
|
||||
44
src/patterns/fade_random.rs
Normal file
44
src/patterns/fade_random.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use crate::patterns::fade::Fade;
|
||||
use crate::patterns::Pattern;
|
||||
use crate::{impl_pattern_last_iteration, Strip};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// # FadeRandom
|
||||
///
|
||||
/// fade from one color to an other with random variations applied to each pixel
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct FadeRandom<const N: usize> {
|
||||
#[serde(default)]
|
||||
pub(crate) current_iteration: usize,
|
||||
pub(crate) max_iteration: Option<usize>,
|
||||
pub(crate) stability: usize,
|
||||
fade: Fade<N>,
|
||||
}
|
||||
|
||||
impl<const N: usize> Pattern for FadeRandom<N> {
|
||||
type Strip = Strip<N>;
|
||||
impl_pattern_last_iteration!(ColorWipe);
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for FadeRandom<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let strip = self.fade.next();
|
||||
match strip {
|
||||
None => None,
|
||||
Some(mut fade) => {
|
||||
let rng = rand::thread_rng();
|
||||
for i in 0..N {
|
||||
match fade[i] {
|
||||
None => {}
|
||||
Some(pixel) => {
|
||||
fade[i] = Some(pixel.random_delta(self.stability, &mut rng.clone()))
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(fade)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
38
src/patterns/fade_unstable.rs
Normal file
38
src/patterns/fade_unstable.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
use crate::patterns::fade::Fade;
|
||||
use crate::patterns::Pattern;
|
||||
use crate::{impl_pattern_last_iteration, Strip};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// # FadeUnstable
|
||||
///
|
||||
/// fade from one color to an other with random variations applied to the entire strip
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct FadeUnstable<const N: usize> {
|
||||
#[serde(default)]
|
||||
pub(crate) current_iteration: usize,
|
||||
pub(crate) max_iteration: Option<usize>,
|
||||
pub(crate) stability: usize,
|
||||
fade: Fade<N>,
|
||||
}
|
||||
|
||||
impl<const N: usize> Pattern for FadeUnstable<N> {
|
||||
type Strip = Strip<N>;
|
||||
impl_pattern_last_iteration!(ColorWipe);
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for FadeUnstable<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let strip = self.fade.next();
|
||||
match strip {
|
||||
None => None,
|
||||
Some(fade) => {
|
||||
let mut rng = rand::thread_rng();
|
||||
let pixel = fade[0].unwrap().random_delta(self.stability, &mut rng);
|
||||
|
||||
Some(Strip::new(pixel))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
src/patterns/fill_random.rs
Normal file
40
src/patterns/fill_random.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
use crate::patterns::Pattern;
|
||||
use crate::pixel_color::PixelColor;
|
||||
use crate::{impl_pattern_last_iteration, Strip};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// # FillRandom
|
||||
///
|
||||
/// fill strip with color then apply random variation to each pixel
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct FillRandom<const N: usize> {
|
||||
pub(crate) color: PixelColor,
|
||||
pub(crate) stability: usize,
|
||||
pub(crate) max_iteration: Option<usize>,
|
||||
#[serde(default)]
|
||||
pub(crate) current_iteration: usize,
|
||||
}
|
||||
|
||||
impl<const N: usize> Pattern for FillRandom<N> {
|
||||
type Strip = Strip<N>;
|
||||
impl_pattern_last_iteration!(ColorWipe);
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for FillRandom<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.is_last_iteration() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut strip = Strip::<N>::default();
|
||||
let rng = rand::thread_rng();
|
||||
for i in 0..N {
|
||||
let c = self.color.random_delta(self.stability, &mut rng.clone());
|
||||
strip[i] = Some(c);
|
||||
}
|
||||
self.current_iteration += 1;
|
||||
Some(strip)
|
||||
}
|
||||
}
|
||||
36
src/patterns/fill_unstable.rs
Normal file
36
src/patterns/fill_unstable.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
use crate::patterns::Pattern;
|
||||
use crate::pixel_color::PixelColor;
|
||||
use crate::{impl_pattern_last_iteration, Strip};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// # FillUnstable
|
||||
///
|
||||
/// fill strip with color then apply same random variation to all pixel
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct FillUnstable<const N: usize> {
|
||||
pub(crate) color: PixelColor,
|
||||
pub(crate) stability: usize,
|
||||
pub(crate) max_iteration: Option<usize>,
|
||||
#[serde(default)]
|
||||
pub(crate) current_iteration: usize,
|
||||
}
|
||||
|
||||
impl<const N: usize> Pattern for FillUnstable<N> {
|
||||
type Strip = Strip<N>;
|
||||
impl_pattern_last_iteration!(FillUnstable);
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for FillUnstable<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.is_last_iteration() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let unstable_color = self.color.random_delta(self.stability, &mut rng);
|
||||
self.current_iteration += 1;
|
||||
Some(Strip::<N>::new(unstable_color))
|
||||
}
|
||||
}
|
||||
70
src/patterns/rain.rs
Normal file
70
src/patterns/rain.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
use crate::patterns::Pattern;
|
||||
use crate::pixel_color::PixelColor;
|
||||
use crate::{impl_pattern_last_iteration, Strip};
|
||||
use rand::Rng;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// # rain pattern
|
||||
/// randomly fall colors from top or bottom.
|
||||
///
|
||||
///
|
||||
/// # note
|
||||
///
|
||||
/// `stability` is the probability (between 0 an 1) that any led will light on.
|
||||
/// The probability of at least one led light on is `stability*number of lines`
|
||||
///
|
||||
/// this pattern works only for 5x5 square
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct Rain<const N: usize> {
|
||||
pub(crate) color: PixelColor,
|
||||
pub(crate) background_color: Option<PixelColor>,
|
||||
pub(crate) max_iteration: Option<usize>,
|
||||
#[serde(default)]
|
||||
pub(crate) current_iteration: usize,
|
||||
pub(crate) stability: usize,
|
||||
pub(crate) lines: usize,
|
||||
#[serde(skip)]
|
||||
drops: Strip<N>,
|
||||
}
|
||||
|
||||
impl<const N: usize> Pattern for Rain<N> {
|
||||
type Strip = Strip<N>;
|
||||
fn init(&mut self) -> Option<Self::Strip> {
|
||||
if let Some(color) = self.background_color {
|
||||
self.drops = Strip::<N>::new(color);
|
||||
Some(self.drops)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
impl_pattern_last_iteration!(ColorRain);
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for Rain<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut strip = Strip::<N>::default();
|
||||
|
||||
for i in 0..(N - self.lines) {
|
||||
strip[i + self.lines] = self.drops[i];
|
||||
}
|
||||
let mut rng = rand::thread_rng();
|
||||
for i in 0..self.lines {
|
||||
if rng.gen_bool(self.stability as f64 / 100 as f64) {
|
||||
strip[i] = Some(self.color);
|
||||
} else {
|
||||
let c = strip[i + self.lines];
|
||||
if self.background_color != c && c != Some(PixelColor::default()) {
|
||||
strip[i] = Some(c.unwrap() / 2);
|
||||
} else {
|
||||
strip[i] = self.background_color;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.drops = strip.clone();
|
||||
|
||||
self.current_iteration += 1;
|
||||
Some(strip)
|
||||
}
|
||||
}
|
||||
80
src/patterns/rainbow.rs
Normal file
80
src/patterns/rainbow.rs
Normal file
@@ -0,0 +1,80 @@
|
||||
use crate::patterns::Pattern;
|
||||
use crate::pixel_color::PixelColor;
|
||||
use crate::{impl_pattern_last_iteration, Strip};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// # Rainbow pattern
|
||||
///
|
||||
/// every pattern implement the iterator trait
|
||||
///
|
||||
/// This pattern display a moving rainbow over the strip
|
||||
///
|
||||
/// ### Note
|
||||
///
|
||||
/// If max iteration is let to None, it's an infinite pattern.
|
||||
///
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct Rainbow<const N: usize> {
|
||||
#[serde(default)]
|
||||
pub(crate) current_iteration: usize,
|
||||
pub(crate) max_iteration: Option<usize>,
|
||||
pub(crate) step: Option<usize>,
|
||||
}
|
||||
|
||||
impl<const N: usize> Pattern for Rainbow<N> {
|
||||
type Strip = Strip<N>;
|
||||
impl_pattern_last_iteration!(Rainbow);
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for Rainbow<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.is_last_iteration() {
|
||||
return None;
|
||||
}
|
||||
let mut strip = Strip::default();
|
||||
let step = self.step.unwrap_or(255 / N);
|
||||
for i in 0..N {
|
||||
let pos = (i * step + self.current_iteration) as u8;
|
||||
strip[i] = Some(wheel(pos))
|
||||
}
|
||||
self.current_iteration += 1;
|
||||
Some(strip)
|
||||
}
|
||||
}
|
||||
|
||||
/// compute **rgb** pixel color according to the **hsv** wheel
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `index`: position in the hsv wheel
|
||||
///
|
||||
/// returns: PixelColor
|
||||
///
|
||||
fn wheel(index: u8) -> PixelColor {
|
||||
let pos = 255 - index;
|
||||
match pos {
|
||||
0..=85 => PixelColor {
|
||||
red: 255 - (pos * 3),
|
||||
green: 0,
|
||||
blue: pos * 3,
|
||||
},
|
||||
86..=170 => {
|
||||
let pos = pos - 85;
|
||||
PixelColor {
|
||||
red: 0,
|
||||
green: pos * 3,
|
||||
blue: 255 - (pos * 3),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let pos = pos - 170;
|
||||
PixelColor {
|
||||
red: pos * 3,
|
||||
green: 255 - (pos * 3),
|
||||
blue: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
52
src/patterns/ring.rs
Normal file
52
src/patterns/ring.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
use crate::patterns::Pattern;
|
||||
use crate::pixel_color::PixelColor;
|
||||
use crate::{impl_pattern_init_background, impl_pattern_last_iteration, Strip};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// # Ring
|
||||
///
|
||||
/// display a ring on the strip
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct Ring<const N: usize> {
|
||||
#[serde(default)]
|
||||
pub(crate) current_iteration: usize,
|
||||
pub(crate) max_iteration: Option<usize>,
|
||||
pub(crate) color: PixelColor,
|
||||
pub(crate) background_color: Option<PixelColor>,
|
||||
pub(crate) ring_size: usize,
|
||||
}
|
||||
|
||||
impl<const N: usize> Pattern for Ring<N> {
|
||||
type Strip = Strip<N>;
|
||||
impl_pattern_init_background!(N);
|
||||
impl_pattern_last_iteration!(Ring);
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for Ring<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.is_last_iteration() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut strip: Strip<N>;
|
||||
|
||||
if let Some(color) = self.background_color {
|
||||
strip = Strip::new(color);
|
||||
} else {
|
||||
strip = Strip::default();
|
||||
}
|
||||
|
||||
let nbr_iter = N / self.ring_size;
|
||||
let iter = self.current_iteration % nbr_iter;
|
||||
let first_index = iter * self.ring_size;
|
||||
let last_index = first_index + self.ring_size;
|
||||
|
||||
for i in first_index..last_index {
|
||||
strip[i] = Some(self.color);
|
||||
}
|
||||
self.current_iteration += 1;
|
||||
Some(strip)
|
||||
}
|
||||
}
|
||||
56
src/patterns/ring_scanner.rs
Normal file
56
src/patterns/ring_scanner.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
use crate::patterns::Pattern;
|
||||
use crate::pixel_color::PixelColor;
|
||||
use crate::{impl_pattern_init_background, impl_pattern_last_iteration, Strip};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// # RingScanner
|
||||
///
|
||||
/// display a ring on the strip fading for the previous ones
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct RingScanner<const N: usize> {
|
||||
#[serde(default)]
|
||||
pub(crate) current_iteration: usize,
|
||||
pub(crate) max_iteration: Option<usize>,
|
||||
pub(crate) color: PixelColor,
|
||||
pub(crate) background_color: Option<PixelColor>,
|
||||
pub(crate) ring_size: usize,
|
||||
}
|
||||
|
||||
impl<const N: usize> Pattern for RingScanner<N> {
|
||||
type Strip = Strip<N>;
|
||||
impl_pattern_init_background!(N);
|
||||
impl_pattern_last_iteration!(ColorWipe);
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for RingScanner<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.is_last_iteration() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut strip: Strip<N>;
|
||||
|
||||
if let Some(color) = self.background_color {
|
||||
strip = Strip::new(color);
|
||||
} else {
|
||||
strip = Strip::default();
|
||||
}
|
||||
|
||||
let nbr_iter = N / self.ring_size;
|
||||
let iter = self.current_iteration % nbr_iter;
|
||||
|
||||
for j in 0..iter {
|
||||
let first_index = j * self.ring_size;
|
||||
let last_index = first_index + self.ring_size;
|
||||
let fade_gain = 2 * (iter - j);
|
||||
for i in first_index..last_index {
|
||||
strip[i] = Some(self.color / fade_gain as u8);
|
||||
}
|
||||
}
|
||||
|
||||
self.current_iteration += 1;
|
||||
Some(strip)
|
||||
}
|
||||
}
|
||||
59
src/patterns/scanner.rs
Normal file
59
src/patterns/scanner.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
use crate::patterns::Pattern;
|
||||
use crate::pixel_color::PixelColor;
|
||||
use crate::{impl_pattern_init_background, impl_pattern_last_iteration, Strip};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// # scanner pattern
|
||||
/// color one pixel with a color and leave a train of this color fading to black
|
||||
///
|
||||
/// # note
|
||||
///
|
||||
/// background_color work only for color not set by scanner pattern
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct Scanner<const N: usize> {
|
||||
#[serde(default)]
|
||||
pub(crate) current_iteration: usize,
|
||||
pub(crate) color: PixelColor,
|
||||
pub(crate) background_color: Option<PixelColor>,
|
||||
#[serde(default)]
|
||||
pub(crate) max_iteration: Option<usize>,
|
||||
}
|
||||
|
||||
impl<const N: usize> Pattern for Scanner<N> {
|
||||
type Strip = Strip<N>;
|
||||
impl_pattern_init_background!(N);
|
||||
impl_pattern_last_iteration!(ColorWipe);
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for Scanner<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.is_last_iteration() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut strip: Strip<N>;
|
||||
|
||||
if let Some(color) = self.background_color {
|
||||
strip = Strip::new(color);
|
||||
} else {
|
||||
strip = Strip::default();
|
||||
}
|
||||
|
||||
let current_led = self.current_iteration % N;
|
||||
let min_led;
|
||||
if current_led < 8 {
|
||||
min_led = 0;
|
||||
} else {
|
||||
min_led = current_led - 8;
|
||||
}
|
||||
let mut c = self.color;
|
||||
for i in min_led..current_led {
|
||||
strip[i] = Some(c);
|
||||
c = c / 2;
|
||||
}
|
||||
self.current_iteration += 1;
|
||||
Some(strip)
|
||||
}
|
||||
}
|
||||
83
src/patterns/stars_random.rs
Normal file
83
src/patterns/stars_random.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
use crate::patterns::Pattern;
|
||||
use crate::pixel_color::PixelColor;
|
||||
use crate::{impl_pattern_last_iteration, Strip};
|
||||
use rand::Rng;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// # StarsRandom
|
||||
///
|
||||
/// fill strip with background color then color random pixel with other color fading to background color
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct StarsRandom<const N: usize> {
|
||||
pub(crate) color: PixelColor,
|
||||
pub(crate) ratio: usize,
|
||||
pub(crate) max_iteration: Option<usize>,
|
||||
pub(crate) background_color: Option<PixelColor>,
|
||||
pub(crate) steps: usize,
|
||||
#[serde(default)]
|
||||
pub(crate) current_iteration: usize,
|
||||
#[serde(skip)]
|
||||
strip: Strip<N>,
|
||||
#[serde(skip)]
|
||||
step: (i8, i8, i8),
|
||||
}
|
||||
|
||||
impl<const N: usize> Pattern for StarsRandom<N> {
|
||||
type Strip = Strip<N>;
|
||||
fn init(&mut self) -> Option<Self::Strip> {
|
||||
if let Some(color) = self.background_color {
|
||||
self.strip = Strip::<N>::new(color);
|
||||
let red = self.color.red as i16 - color.red as i16;
|
||||
let green = color.green as i16 - self.color.green as i16;
|
||||
let blue = self.color.blue as i16 - color.blue as i16;
|
||||
self.step = (
|
||||
-(red / self.steps as i16) as i8,
|
||||
-(green / self.steps as i16) as i8,
|
||||
-(blue / self.steps as i16) as i8,
|
||||
);
|
||||
Some(self.strip)
|
||||
} else {
|
||||
self.step = (
|
||||
-((self.color.red / self.steps as u8) as i8),
|
||||
-((self.color.green / self.steps as u8) as i8),
|
||||
-((self.color.blue / self.steps as u8) as i8),
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
impl_pattern_last_iteration!(StarsRandom);
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for StarsRandom<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.is_last_iteration() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
for i in 0..N {
|
||||
let current_color = self.strip[i];
|
||||
if rng.gen_bool(1.0 / self.ratio as f64) {
|
||||
self.strip[i] = Some(self.color);
|
||||
} else {
|
||||
let color = current_color.unwrap();
|
||||
let background = self.background_color.unwrap_or(PixelColor::default());
|
||||
let distance = PixelColor {
|
||||
red: self.step.0.abs() as u8,
|
||||
green: self.step.1.abs() as u8,
|
||||
blue: self.step.2.abs() as u8,
|
||||
};
|
||||
if color.is_closer(background, distance) {
|
||||
self.strip[i] = self.background_color;
|
||||
} else {
|
||||
self.strip[i] = Some(color.delta(self.step));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.current_iteration += 1;
|
||||
Some(self.strip)
|
||||
}
|
||||
}
|
||||
122
src/pixel_color.rs
Normal file
122
src/pixel_color.rs
Normal file
@@ -0,0 +1,122 @@
|
||||
use rand::prelude::ThreadRng;
|
||||
use rand::Rng;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::ops::{Add, AddAssign, Div, Sub, SubAssign};
|
||||
|
||||
///
|
||||
/// a struct for an RGB color
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Default, Eq, Hash, PartialEq)]
|
||||
pub struct PixelColor {
|
||||
pub(crate) red: u8,
|
||||
pub(crate) green: u8,
|
||||
pub(crate) blue: u8,
|
||||
}
|
||||
|
||||
impl fmt::Display for PixelColor {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "[{},{},{}]", self.red, self.green, self.blue)
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<u8> for PixelColor {
|
||||
type Output = PixelColor;
|
||||
|
||||
fn div(self, rhs: u8) -> Self::Output {
|
||||
PixelColor {
|
||||
red: self.red / rhs,
|
||||
green: self.green / rhs,
|
||||
blue: self.blue / rhs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for PixelColor {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
PixelColor {
|
||||
red: self.red.saturating_add(rhs.red),
|
||||
green: self.green.saturating_add(rhs.green),
|
||||
blue: self.blue.saturating_add(rhs.blue),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<u8> for PixelColor {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, rhs: u8) -> Self::Output {
|
||||
PixelColor {
|
||||
red: self.red.saturating_sub(rhs),
|
||||
green: self.green.saturating_sub(rhs),
|
||||
blue: self.blue.saturating_sub(rhs),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign for PixelColor {
|
||||
fn sub_assign(&mut self, rhs: Self) {
|
||||
self.red = self.red.saturating_sub(rhs.red);
|
||||
self.green = self.red.saturating_sub(rhs.green);
|
||||
self.blue = self.red.saturating_sub(rhs.blue);
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for PixelColor {
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
self.red = self.red.saturating_add(rhs.red);
|
||||
self.green = self.red.saturating_add(rhs.green);
|
||||
self.blue = self.red.saturating_add(rhs.blue);
|
||||
}
|
||||
}
|
||||
|
||||
impl PixelColor {
|
||||
/// apply random delta to pixel and return computed value
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `pixel`: pixel to apply delta
|
||||
/// * `stability`: amplitude of delta between 0 and `stability`
|
||||
///
|
||||
/// returns: PixelColor
|
||||
///
|
||||
pub fn random_delta(&self, stability: usize, rng: &mut ThreadRng) -> PixelColor {
|
||||
let minus_interval = -(stability as i8);
|
||||
let max_interval = stability as i8;
|
||||
let red_delta = rng.gen_range(minus_interval..max_interval) as i8;
|
||||
let green_delta = rng.gen_range(minus_interval..max_interval) as i8;
|
||||
let blue_delta = rng.gen_range(minus_interval..max_interval) as i8;
|
||||
self.delta((red_delta, green_delta, blue_delta))
|
||||
}
|
||||
|
||||
pub fn delta(&self, delta: (i8, i8, i8)) -> PixelColor {
|
||||
let (red, green, blue) = delta;
|
||||
|
||||
let red = self.red as i16 + red as i16;
|
||||
let green = self.green as i16 + green as i16;
|
||||
let blue = self.blue as i16 + blue as i16;
|
||||
|
||||
let bound = |color: i16| {
|
||||
if color < 0 {
|
||||
0
|
||||
} else if color > 255 {
|
||||
255
|
||||
} else {
|
||||
color as u8
|
||||
}
|
||||
};
|
||||
|
||||
PixelColor {
|
||||
red: bound(red),
|
||||
green: bound(green),
|
||||
blue: bound(blue),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_closer(&self, pixel: PixelColor, distance: PixelColor) -> bool {
|
||||
(self.red as i16 - pixel.red as i16).abs() <= distance.red as i16
|
||||
&& (self.green as i16 - pixel.green as i16).abs() <= distance.green as i16
|
||||
&& (self.blue as i16 - pixel.blue as i16).abs() <= distance.blue as i16
|
||||
}
|
||||
}
|
||||
241
src/runner.rs
241
src/runner.rs
@@ -1,57 +1,234 @@
|
||||
use crate::patterns::Patterns;
|
||||
use crate::config::Device;
|
||||
use crate::patterns::{Pattern, Patterns};
|
||||
use crate::pixel_color::PixelColor;
|
||||
use crate::Strip;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::fs;
|
||||
use bluer::Address;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::future::Future;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::watch::Sender;
|
||||
use tokio::sync::watch::{Receiver, Sender};
|
||||
use tokio::time::Instant;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub(crate) mod period {
|
||||
use serde::{de::Error as _, Deserialize, Deserializer};
|
||||
use std::time::Duration;
|
||||
|
||||
/// Custom deserializer for period from millisecond value
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `deserializer`:
|
||||
///
|
||||
/// returns: Result<Duration, <D as Deserializer>::Error>
|
||||
///
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
String::deserialize(deserializer)?
|
||||
.parse::<u64>()
|
||||
.and_then(|d| Ok(Duration::from_millis(d)))
|
||||
.map_err(|e| D::Error::custom(format!("{}", e)))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct Context<const N: usize> {
|
||||
pub(crate) pattern: Patterns<N>,
|
||||
#[serde(deserialize_with = "period::deserialize")]
|
||||
pub period: Duration,
|
||||
}
|
||||
|
||||
impl<const N: usize> IntoIterator for Runner<N> {
|
||||
type Item = Context<N>;
|
||||
type IntoIter = <Vec<Context<N>> as IntoIterator>::IntoIter; // so that you don't have to write std::vec::IntoIter, which nobody remembers anyway
|
||||
pub(crate) type DeviceSequence<const N: usize> = Vec<Context<N>>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
/// iterate over `runner` and stream strip data to `tx` channel
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `runner`: Sequence to stream
|
||||
/// * `tx`: channel to stream data to
|
||||
///
|
||||
/// returns: ()
|
||||
///
|
||||
async fn runner_loop<const N: usize>(runner: DeviceSequence<N>, tx: Sender<Strip<N>>) {
|
||||
let mut background = Strip::<N>::default();
|
||||
for mut context in runner {
|
||||
let mut strip = context.pattern.init().unwrap_or(background);
|
||||
let delay = context.period;
|
||||
let mut next_instant = Instant::now() + delay;
|
||||
while tx.send(strip).is_ok() {
|
||||
if let Some(value) = context.pattern.next() {
|
||||
strip = apply_mask(background, value);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
tokio::time::sleep_until(next_instant).await;
|
||||
next_instant += delay;
|
||||
}
|
||||
background = strip;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub(crate) struct Runner<const N: usize>(Vec<Context<N>>);
|
||||
fn apply_mask<const N: usize>(previous_strip: Strip<N>, strip: Strip<{ N }>) -> Strip<N> {
|
||||
let mut new_strip = Strip::default();
|
||||
for i in 0..N {
|
||||
if let Some(color) = strip[i] {
|
||||
new_strip[i] = Some(color);
|
||||
} else {
|
||||
new_strip[i] = previous_strip[i];
|
||||
}
|
||||
}
|
||||
new_strip
|
||||
}
|
||||
|
||||
impl<const N: usize> Runner<N> {
|
||||
pub fn new() -> Runner<N> {
|
||||
let runner = Vec::<Context<N>>::new();
|
||||
Runner(runner)
|
||||
#[derive(Default)]
|
||||
pub struct Spectacle<const N: usize> {
|
||||
runners: HashMap<DeviceSequence<N>, (Sender<Strip<N>>, Receiver<Strip<N>>)>,
|
||||
channels: HashMap<Address, Receiver<Strip<N>>>,
|
||||
default_runner: Option<(DeviceSequence<N>, Sender<Strip<N>>)>,
|
||||
default_receiver: Option<Receiver<Strip<N>>>,
|
||||
}
|
||||
|
||||
impl<const N: usize> Spectacle<N> {
|
||||
pub fn add(&mut self, mac_address: Address, runner: &DeviceSequence<N>) {
|
||||
if let Some((_, rx)) = self.runners.get(runner as &DeviceSequence<N>) {
|
||||
self.channels.insert(mac_address, rx.clone());
|
||||
} else {
|
||||
let (tx, rx) = tokio::sync::watch::channel(Strip::<N>::new(PixelColor {
|
||||
red: 0,
|
||||
green: 255,
|
||||
blue: 0,
|
||||
}));
|
||||
self.runners.insert(runner.clone(), (tx, rx.clone()));
|
||||
self.channels.insert(mac_address, rx.clone());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, value: Context<N>) {
|
||||
self.0.push(value)
|
||||
/// add default sequence for any device implementing **adafruit neopixel BLE**
|
||||
/// not specified in an other sequence.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `runner`: sequence to stream
|
||||
///
|
||||
/// returns: ()
|
||||
///
|
||||
pub fn add_default(&mut self, runner: DeviceSequence<N>) {
|
||||
let (tx, rx) = tokio::sync::watch::channel(Strip::<N>::new(PixelColor {
|
||||
red: 0,
|
||||
green: 0,
|
||||
blue: 255,
|
||||
}));
|
||||
self.default_runner = Some((runner, tx));
|
||||
self.default_receiver = Some(rx);
|
||||
}
|
||||
|
||||
pub async fn runner_loop(self, tx: Sender<Strip<N>>) {
|
||||
for mut context in self {
|
||||
let mut strip = context.pattern.next().unwrap();
|
||||
let delay = context.period;
|
||||
println!("{:?}", delay);
|
||||
while tx.send(strip).is_ok() {
|
||||
if let Some(value) = context.pattern.next() {
|
||||
strip = value;
|
||||
} else {
|
||||
break;
|
||||
/// Get channel associated with the mac address passed in parameters. If no channel found,
|
||||
/// give back the default channel if existing.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `mac_address`: mac address to look for
|
||||
///
|
||||
/// returns: Option<Receiver<Strip<{ N }>>>
|
||||
///
|
||||
pub fn get_channel(&self, mac_address: &Address) -> Option<Receiver<Strip<N>>> {
|
||||
let channel = self.channels.get(mac_address);
|
||||
match channel {
|
||||
None => {
|
||||
let d = &self.default_receiver;
|
||||
match d {
|
||||
None => None,
|
||||
Some(runner) => {
|
||||
println!("default rx");
|
||||
Some(runner.clone())
|
||||
}
|
||||
}
|
||||
tokio::time::sleep(delay).await;
|
||||
}
|
||||
Some(channel) => {
|
||||
println!("rx found ");
|
||||
Some(channel.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_from_file(file: &std::path::Path) -> Runner<N> {
|
||||
let file = fs::read_to_string(file).unwrap();
|
||||
let spectacle: Runner<N> = serde_yaml::from_str(&*file).unwrap();
|
||||
/// start to stream configured sequences. Returns the joinhandle of task to wait for
|
||||
///
|
||||
///
|
||||
///
|
||||
/// returns: Vec<impl Future<Output = ()>>
|
||||
///
|
||||
pub fn run(&mut self) -> Vec<impl Future<Output = ()>> {
|
||||
let mut joinhandles = vec![];
|
||||
|
||||
if self.default_runner.is_some() {
|
||||
let d = std::mem::take(&mut self.default_runner).unwrap();
|
||||
joinhandles.push(runner_loop(d.0.clone(), d.1));
|
||||
println!("add default runner loop");
|
||||
}
|
||||
|
||||
for (runner, (tx, _)) in self.runners.drain().into_iter() {
|
||||
joinhandles.push(runner_loop(runner.clone(), tx));
|
||||
println!("add custom runner loop");
|
||||
}
|
||||
joinhandles
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> From<Vec<Device<N>>> for Spectacle<N> {
|
||||
fn from(config: Vec<Device<N>>) -> Self {
|
||||
let mut spectacle: Spectacle<N> = Spectacle::default();
|
||||
for device in config {
|
||||
if let Some(mac_addresses) = device.mac_addresses {
|
||||
for mac_address in mac_addresses {
|
||||
let mac_address = Address::from_str(&*mac_address);
|
||||
match mac_address {
|
||||
Ok(address) => {
|
||||
spectacle.add(address, &device.sequence);
|
||||
}
|
||||
Err(_) => {
|
||||
println!("error reading mac address");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
spectacle.add_default(device.sequence);
|
||||
}
|
||||
}
|
||||
spectacle
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn apply_mask() {
|
||||
let pixel_entry = PixelColor {
|
||||
red: 10,
|
||||
green: 10,
|
||||
blue: 10,
|
||||
};
|
||||
|
||||
let pixel_mask = PixelColor {
|
||||
red: 20,
|
||||
green: 20,
|
||||
blue: 20,
|
||||
};
|
||||
|
||||
let mut entry = Strip::<5>::new(pixel_entry);
|
||||
entry[0] = None;
|
||||
entry[1] = None;
|
||||
|
||||
let mut mask = Strip::<5>::new(pixel_mask);
|
||||
mask[0] = None;
|
||||
|
||||
let mut expected = entry.clone();
|
||||
expected[1] = Some(pixel_mask);
|
||||
assert_eq!(expected, super::apply_mask(mask, entry));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
|
||||
Reference in New Issue
Block a user