From 5d87fd457e66df28af31a0019310604f6372bd23 Mon Sep 17 00:00:00 2001 From: LiteHell Date: Mon, 13 May 2024 17:16:51 +0900 Subject: [PATCH 1/9] chore: init bidrum-hat project --- Cargo.lock | 544 +++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 3 +- bidrum-hat/Cargo.toml | 9 + bidrum-hat/src/lib.rs | 14 ++ 4 files changed, 565 insertions(+), 5 deletions(-) create mode 100644 bidrum-hat/Cargo.toml create mode 100644 bidrum-hat/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 402e04a..4e78a09 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" version = "1.1.2" @@ -87,6 +102,17 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +[[package]] +name = "async-trait" +version = "0.1.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "atomic-arena" version = "0.1.1" @@ -99,6 +125,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "bidrum" version = "0.1.0" @@ -127,6 +168,13 @@ dependencies = [ "serde_json", ] +[[package]] +name = "bidrum-hat" +version = "0.1.0" +dependencies = [ + "btleplug", +] + [[package]] name = "bindgen" version = "0.69.2" @@ -159,6 +207,69 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "bluez-async" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce7d4413c940e8e3cb6afc122d3f4a07096aca259d286781128683fc9f39d9b" +dependencies = [ + "async-trait", + "bitflags 2.5.0", + "bluez-generated", + "dbus", + "dbus-tokio", + "futures", + "itertools", + "log", + "serde", + "serde-xml-rs", + "thiserror", + "tokio", + "uuid", +] + +[[package]] +name = "bluez-generated" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d1c659dbc82f0b8ca75606c91a371e763589b7f6acf36858eeed0c705afe367" +dependencies = [ + "dbus", +] + +[[package]] +name = "btleplug" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba345f9db94939c72959b2008abe1ffcdbcaa235243fd92ad12436532ff199cf" +dependencies = [ + "async-trait", + "bitflags 2.5.0", + "bluez-async", + "cocoa", + "dashmap", + "dbus", + "futures", + "jni 0.19.0", + "jni-utils", + "libc", + "log", + "objc", + "once_cell", + "static_assertions", + "thiserror", + "tokio", + "tokio-stream", + "uuid", + "windows 0.52.0", +] + [[package]] name = "bumpalo" version = "3.14.0" @@ -179,12 +290,13 @@ checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cc" -version = "1.0.83" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" dependencies = [ "jobserver", "libc", + "once_cell", ] [[package]] @@ -270,6 +382,36 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation", + "core-graphics-types", + "libc", + "objc", +] + [[package]] name = "colorchoice" version = "1.0.0" @@ -322,6 +464,30 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + [[package]] name = "coreaudio-rs" version = "0.11.3" @@ -373,12 +539,49 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "dasp_sample" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" +[[package]] +name = "dbus" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" +dependencies = [ + "futures-channel", + "futures-util", + "libc", + "libdbus-sys", + "winapi", +] + +[[package]] +name = "dbus-tokio" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "007688d459bc677131c063a3a77fb899526e17b7980f390b69644bdbc41fad13" +dependencies = [ + "dbus", + "libc", + "tokio", +] + [[package]] name = "device_query" version = "2.0.0" @@ -394,6 +597,12 @@ dependencies = [ "x11", ] +[[package]] +name = "either" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" + [[package]] name = "encoding_rs" version = "0.8.33" @@ -443,6 +652,122 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -454,6 +779,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + [[package]] name = "glam" version = "0.25.0" @@ -507,6 +838,15 @@ dependencies = [ "mach2", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.10" @@ -547,11 +887,26 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jni-utils" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "259e9f2c3ead61de911f147000660511f07ab00adeed1d84f5ac4d0386e7a6c4" +dependencies = [ + "dashmap", + "futures", + "jni 0.19.0", + "log", + "once_cell", + "static_assertions", + "uuid", +] + [[package]] name = "jobserver" -version = "0.1.27" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" dependencies = [ "libc", ] @@ -598,6 +953,15 @@ version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +[[package]] +name = "libdbus-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +dependencies = [ + "pkg-config", +] + [[package]] name = "libloading" version = "0.8.1" @@ -663,6 +1027,15 @@ dependencies = [ "core-foundation-sys", ] +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + [[package]] name = "memchr" version = "2.7.1" @@ -675,12 +1048,32 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + [[package]] name = "mint" version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff" +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + [[package]] name = "ndk" version = "0.7.0" @@ -826,6 +1219,24 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + [[package]] name = "oboe" version = "0.5.0" @@ -884,6 +1295,18 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "pkg-config" version = "0.3.29" @@ -1019,6 +1442,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + [[package]] name = "rustc-hash" version = "1.1.0" @@ -1084,6 +1513,18 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-xml-rs" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb3aa78ecda1ebc9ec9847d5d3aba7d618823446a049ba2491940506da6e2782" +dependencies = [ + "log", + "serde", + "thiserror", + "xml-rs", +] + [[package]] name = "serde_derive" version = "1.0.196" @@ -1131,12 +1572,37 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "smallvec" version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.10.0" @@ -1306,6 +1772,45 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "tokio" +version = "1.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +dependencies = [ + "backtrace", + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-stream" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + [[package]] name = "toml_datetime" version = "0.6.5" @@ -1350,6 +1855,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "uuid" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" + [[package]] name = "vcpkg" version = "0.2.15" @@ -1503,6 +2014,25 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -1710,3 +2240,9 @@ dependencies = [ "libc", "pkg-config", ] + +[[package]] +name = "xml-rs" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193" diff --git a/Cargo.toml b/Cargo.toml index 32ca1c9..8092833 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,8 @@ members = [ "game", "data-struct-lib", - "chart-recorder" + "chart-recorder", + "bidrum-hat" ] [profile.dev.package.kira] diff --git a/bidrum-hat/Cargo.toml b/bidrum-hat/Cargo.toml new file mode 100644 index 0000000..e0a88e0 --- /dev/null +++ b/bidrum-hat/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "bidrum-hat" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +btleplug = "0.11.5" diff --git a/bidrum-hat/src/lib.rs b/bidrum-hat/src/lib.rs new file mode 100644 index 0000000..7d12d9a --- /dev/null +++ b/bidrum-hat/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} From deb166a8246601f194ea29195c8c3c2842dd42c5 Mon Sep 17 00:00:00 2001 From: LiteHell Date: Mon, 13 May 2024 22:30:39 +0900 Subject: [PATCH 2/9] chore: move janggu controller arduino files --- {controller => controllers/janggu}/circuit.png | Bin {controller => controllers/janggu}/controller.ino | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {controller => controllers/janggu}/circuit.png (100%) rename {controller => controllers/janggu}/controller.ino (100%) diff --git a/controller/circuit.png b/controllers/janggu/circuit.png similarity index 100% rename from controller/circuit.png rename to controllers/janggu/circuit.png diff --git a/controller/controller.ino b/controllers/janggu/controller.ino similarity index 100% rename from controller/controller.ino rename to controllers/janggu/controller.ino From e552b741321c15eddcb6cd6bda1d01484f5f819c Mon Sep 17 00:00:00 2001 From: LiteHell Date: Mon, 13 May 2024 22:34:18 +0900 Subject: [PATCH 3/9] feat(hat): impl bidrum-hat library roughly --- Cargo.lock | 4 ++ bidrum-hat/Cargo.toml | 3 + bidrum-hat/src/lib.rs | 126 ++++++++++++++++++++++++++++++--- controllers/hat/circuit.md | 13 ++++ controllers/hat/controller.ino | 126 +++++++++++++++++++++++++++++++++ 5 files changed, 263 insertions(+), 9 deletions(-) create mode 100644 controllers/hat/circuit.md create mode 100644 controllers/hat/controller.ino diff --git a/Cargo.lock b/Cargo.lock index 4e78a09..8db878c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -173,6 +173,9 @@ name = "bidrum-hat" version = "0.1.0" dependencies = [ "btleplug", + "futures", + "tokio", + "uuid", ] [[package]] @@ -1781,6 +1784,7 @@ dependencies = [ "backtrace", "libc", "mio", + "num_cpus", "pin-project-lite", "socket2", "windows-sys 0.48.0", diff --git a/bidrum-hat/Cargo.toml b/bidrum-hat/Cargo.toml index e0a88e0..09463ba 100644 --- a/bidrum-hat/Cargo.toml +++ b/bidrum-hat/Cargo.toml @@ -7,3 +7,6 @@ edition = "2021" [dependencies] btleplug = "0.11.5" +futures = "0.3.30" +tokio = { version = "1.37.0", features = ["rt", "rt-multi-thread"] } +uuid = "1.8.0" diff --git a/bidrum-hat/src/lib.rs b/bidrum-hat/src/lib.rs index 7d12d9a..d0344e3 100644 --- a/bidrum-hat/src/lib.rs +++ b/bidrum-hat/src/lib.rs @@ -1,14 +1,122 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use std::{f32, thread}; + +use btleplug::api::{Central, CentralEvent, Manager as _, Peripheral, ScanFilter}; +use btleplug::platform::Manager; +use futures::executor; +use futures::stream::StreamExt; +use uuid::Uuid; + +pub struct BidrumHat { + spinning: Arc, +} + +async fn get_data(spinning: Arc) { + let service_uuid = Uuid::parse_str("8e191920-dda8-4f40-b2bc-f8f99f680c94").unwrap(); + let characteristic_uuid = Uuid::parse_str("2a89fe67-88ff-4bac-8e42-d122d6995ad1").unwrap(); + + let manager = Manager::new().await.expect("Failed to get manager"); + let adapter_list = manager + .adapters() + .await + .expect("Failed to get adapter list"); + if adapter_list.is_empty() { + eprintln!("No Bluetooth adapters found"); + } + + for adapter in adapter_list.iter() { + // println!("Starting scan..."); + let mut events = adapter + .events() + .await + .expect("Failed to get adapter event stream"); + + adapter + .start_scan(ScanFilter::default()) + .await + .expect("Can't scan BLE adapter for connected devices..."); + + while let Some(event) = events.next().await { + match event { + CentralEvent::DeviceDiscovered(id) => { + let peripheral = adapter.peripheral(&id).await.unwrap(); + let local_name = peripheral.properties().await.unwrap().unwrap().local_name; + + if local_name.is_some_and(|x| x.contains("bidrum-hat")) { + if !peripheral.is_connected().await.unwrap_or(false) { + peripheral.connect().await.expect("Failed to connect"); + } + + peripheral + .discover_services() + .await + .expect("Failed to discover services"); + + if let Some(characteristic) = + peripheral.characteristics().iter().find(|x| { + x.uuid == characteristic_uuid && x.service_uuid == service_uuid + }) + { + peripheral + .subscribe(characteristic) + .await + .expect("Failed to subscribe characteristic"); + + let mut notification_stream = peripheral + .notifications() + .await + .expect("Failed to make notification stream"); + + // Process while the BLE connection is not broken or stopped. + while let Some(data) = notification_stream.next().await { + let norm = std::str::from_utf8(data.value.as_slice()) + .unwrap_or("0.0") + .parse::() + .unwrap(); + + spinning.store(norm > 1.0, Ordering::Relaxed); + } + } + } + } + _ => {} + } + } + } +} + +impl BidrumHat { + pub fn new() -> BidrumHat { + let spinning = Arc::new(AtomicBool::new(false)); + let spinning_for_thread = spinning.clone(); + + thread::spawn(move || { + let handle = tokio::runtime::Runtime::new().unwrap(); + let _guard = handle.enter(); + executor::block_on(get_data(spinning_for_thread)); + }); + + let result = BidrumHat { spinning: spinning }; + + return result; + } + pub fn spinning(&self) -> bool { + self.spinning.load(Ordering::Relaxed) + } } -#[cfg(test)] -mod tests { - use super::*; +// "-- --nocapture" is needed when running test +#[test] +fn test() { + let hat = BidrumHat::new(); - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); + println!("Running test"); + loop { + if hat.spinning() { + println!("spinning"); + } else { + println!("not spinning"); + } } } diff --git a/controllers/hat/circuit.md b/controllers/hat/circuit.md new file mode 100644 index 0000000..de8740f --- /dev/null +++ b/controllers/hat/circuit.md @@ -0,0 +1,13 @@ +# Required things +- ESP32(or ESP32-*) dev board with BLE Support +- MPU6050 GY-521 module + +# Circuit +| GY-521 | ESP32/ESP32-* | +| ------ | ------------- | +| VCC | 3V3 | +| GND | GND | +| SCL | Pin 22 | +| SDA | Pin 21 | + +Done! \ No newline at end of file diff --git a/controllers/hat/controller.ino b/controllers/hat/controller.ino new file mode 100644 index 0000000..9742433 --- /dev/null +++ b/controllers/hat/controller.ino @@ -0,0 +1,126 @@ +// Basic demo for accelerometer readings from Adafruit MPU6050 + +// ESP32 Guide: https://RandomNerdTutorials.com/esp32-mpu-6050-accelerometer-gyroscope-arduino/ +// ESP8266 Guide: https://RandomNerdTutorials.com/esp8266-nodemcu-mpu-6050-accelerometer-gyroscope-arduino/ +// Arduino Guide: https://RandomNerdTutorials.com/arduino-mpu-6050-accelerometer-gyroscope/ + +#include +#include +#include +#include +#include +#include +#include + +#define SERVICE_UUID "8e191920-dda8-4f40-b2bc-f8f99f680c94" +#define CHARACTERISTIC_UUID "2a89fe67-88ff-4bac-8e42-d122d6995ad1" +Adafruit_MPU6050 mpu; + +bool deviceConnected = false; +bool oldDeviceConnected = false; + +class MyServerCallbacks : public BLEServerCallbacks { + void onConnect(BLEServer *pServer) { + deviceConnected = true; + }; + + void onDisconnect(BLEServer *pServer) { + deviceConnected = false; + } +}; + +void initMPU() { + if (!mpu.begin()) { + while (1) { + delay(10); + } + } + + mpu.setAccelerometerRange(MPU6050_RANGE_8_G); + mpu.setGyroRange(MPU6050_RANGE_500_DEG); + mpu.setFilterBandwidth(MPU6050_BAND_5_HZ); +} + +BLECharacteristic *pCharacteristic; +BLEServer *pServer; +void initBLE() { + // init BLE + BLEDevice::init("bidrum-hat-1234"); + pServer = BLEDevice::createServer(); + pServer->setCallbacks(new MyServerCallbacks()); + + BLEService *pService = pServer->createService(SERVICE_UUID); + pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE| + BLECharacteristic::PROPERTY_NOTIFY | + BLECharacteristic::PROPERTY_INDICATE + ); + + pCharacteristic->addDescriptor(new BLE2902()); + pService->start(); + + BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); + pAdvertising->addServiceUUID(SERVICE_UUID); + pAdvertising->setScanResponse(false); + BLEDevice::startAdvertising(); +} + +void setup(void) { + initMPU(); + initBLE(); + + delay(100); +} + +void loop() { + /* Get new sensor events with the readings */ + sensors_event_t a, g, temp; + mpu.getEvent(&a, &g, &temp); + + + if (deviceConnected) { + float norm = g.gyro.x * g.gyro.x + g.gyro.y * g.gyro.y + g.gyro.z * g.gyro.z; + char normStrBuf[90]; + sprintf(normStrBuf, "%f", norm); + char *normStr = (char*)malloc(sizeof(char) * (strlen(normStrBuf) + 1)); + strcpy(normStr, normStrBuf); + normStr[strlen(normStrBuf)] = 0; + + pCharacteristic->setValue(normStr); + free(normStr); + pCharacteristic->notify(); + } + + if (!deviceConnected && oldDeviceConnected) { + delay(500); // give the bluetooth stack the chance to get things ready + pServer->startAdvertising(); // restart advertising + oldDeviceConnected = deviceConnected; + } + // connecting + if (deviceConnected && !oldDeviceConnected) { + // do stuff here on connecting + oldDeviceConnected = deviceConnected; + } + /* Print out the values + Serial.print("Acceleration X: "); + Serial.print(a.acceleration.x); + Serial.print(", Y: "); + Serial.print(a.acceleration.y); + Serial.print(", Z: "); + Serial.print(a.acceleration.z); + Serial.println(" m/s^2"); + + Serial.print("Rotation X: "); + Serial.print(g.gyro.x); + Serial.print(", Y: "); + Serial.print(g.gyro.y); + Serial.print(", Z: "); + Serial.print(g.gyro.z); + Serial.println(" rad/s"); + + Serial.print("Temperature: "); + Serial.print(temp.temperature); + Serial.println(" degC");*/ +} From 5f2622ada0a3414ffd77ee74c70f4d501eec6717 Mon Sep 17 00:00:00 2001 From: LiteHell Date: Sat, 18 May 2024 16:24:59 +0900 Subject: [PATCH 4/9] feat(hat): impl Drop trait for hat library --- bidrum-hat/src/lib.rs | 104 ++++++++++++++++++++++++++---------------- 1 file changed, 64 insertions(+), 40 deletions(-) diff --git a/bidrum-hat/src/lib.rs b/bidrum-hat/src/lib.rs index d0344e3..d9f2277 100644 --- a/bidrum-hat/src/lib.rs +++ b/bidrum-hat/src/lib.rs @@ -4,15 +4,16 @@ use std::{f32, thread}; use btleplug::api::{Central, CentralEvent, Manager as _, Peripheral, ScanFilter}; use btleplug::platform::Manager; -use futures::executor; use futures::stream::StreamExt; +use futures::{executor, FutureExt}; use uuid::Uuid; pub struct BidrumHat { spinning: Arc, + dropping: Arc, } -async fn get_data(spinning: Arc) { +async fn get_data(spinning: Arc, dropping: Arc) { let service_uuid = Uuid::parse_str("8e191920-dda8-4f40-b2bc-f8f99f680c94").unwrap(); let characteristic_uuid = Uuid::parse_str("2a89fe67-88ff-4bac-8e42-d122d6995ad1").unwrap(); @@ -25,7 +26,7 @@ async fn get_data(spinning: Arc) { eprintln!("No Bluetooth adapters found"); } - for adapter in adapter_list.iter() { + 'adapter_loop: for adapter in adapter_list.iter() { // println!("Starting scan..."); let mut events = adapter .events() @@ -37,50 +38,62 @@ async fn get_data(spinning: Arc) { .await .expect("Can't scan BLE adapter for connected devices..."); - while let Some(event) = events.next().await { - match event { - CentralEvent::DeviceDiscovered(id) => { - let peripheral = adapter.peripheral(&id).await.unwrap(); - let local_name = peripheral.properties().await.unwrap().unwrap().local_name; + let mut event_join_handle = events.next(); + loop { + let event_option = loop { + if dropping.load(Ordering::Relaxed) { + break 'adapter_loop; + } + if let Some(v) = (&mut event_join_handle).now_or_never() { + break v; + } + }; - if local_name.is_some_and(|x| x.contains("bidrum-hat")) { - if !peripheral.is_connected().await.unwrap_or(false) { - peripheral.connect().await.expect("Failed to connect"); - } + if let Some(event) = event_option { + match event { + CentralEvent::DeviceDiscovered(id) => { + let peripheral = adapter.peripheral(&id).await.unwrap(); + let local_name = peripheral.properties().await.unwrap().unwrap().local_name; - peripheral - .discover_services() - .await - .expect("Failed to discover services"); + if local_name.is_some_and(|x| x.contains("bidrum-hat")) { + if !peripheral.is_connected().await.unwrap_or(false) { + peripheral.connect().await.expect("Failed to connect"); + } - if let Some(characteristic) = - peripheral.characteristics().iter().find(|x| { - x.uuid == characteristic_uuid && x.service_uuid == service_uuid - }) - { peripheral - .subscribe(characteristic) - .await - .expect("Failed to subscribe characteristic"); - - let mut notification_stream = peripheral - .notifications() + .discover_services() .await - .expect("Failed to make notification stream"); - - // Process while the BLE connection is not broken or stopped. - while let Some(data) = notification_stream.next().await { - let norm = std::str::from_utf8(data.value.as_slice()) - .unwrap_or("0.0") - .parse::() - .unwrap(); - - spinning.store(norm > 1.0, Ordering::Relaxed); + .expect("Failed to discover services"); + + if let Some(characteristic) = + peripheral.characteristics().iter().find(|x| { + x.uuid == characteristic_uuid && x.service_uuid == service_uuid + }) + { + peripheral + .subscribe(characteristic) + .await + .expect("Failed to subscribe characteristic"); + + let mut notification_stream = peripheral + .notifications() + .await + .expect("Failed to make notification stream"); + + // Process while the BLE connection is not broken or stopped. + while let Some(data) = notification_stream.next().await { + let norm = std::str::from_utf8(data.value.as_slice()) + .unwrap_or("0.0") + .parse::() + .unwrap(); + + spinning.store(norm > 1.0, Ordering::Relaxed); + } } } } + _ => {} } - _ => {} } } } @@ -90,14 +103,19 @@ impl BidrumHat { pub fn new() -> BidrumHat { let spinning = Arc::new(AtomicBool::new(false)); let spinning_for_thread = spinning.clone(); + let dropping = Arc::new(AtomicBool::new(false)); + let dropping_for_thread = dropping.clone(); thread::spawn(move || { let handle = tokio::runtime::Runtime::new().unwrap(); let _guard = handle.enter(); - executor::block_on(get_data(spinning_for_thread)); + executor::block_on(get_data(spinning_for_thread, dropping_for_thread)); }); - let result = BidrumHat { spinning: spinning }; + let result = BidrumHat { + spinning: spinning, + dropping: dropping, + }; return result; } @@ -106,6 +124,12 @@ impl BidrumHat { } } +impl Drop for BidrumHat { + fn drop(&mut self) { + self.dropping.store(true, Ordering::Relaxed); + } +} + // "-- --nocapture" is needed when running test #[test] fn test() { From 1ffe4fcc94d98260db872d06ec9916e357f85db9 Mon Sep 17 00:00:00 2001 From: LiteHell Date: Sat, 18 May 2024 17:06:01 +0900 Subject: [PATCH 5/9] feat(hat): add hat note to chart data structure --- chart-recorder/src/main.rs | 1 + data-struct-lib/src/song.rs | 91 +++++++++++++++------ data-struct-lib/src/song/beat_and_timing.rs | 46 +++++++++++ 3 files changed, 113 insertions(+), 25 deletions(-) create mode 100644 data-struct-lib/src/song/beat_and_timing.rs diff --git a/chart-recorder/src/main.rs b/chart-recorder/src/main.rs index 2361308..72f13e4 100644 --- a/chart-recorder/src/main.rs +++ b/chart-recorder/src/main.rs @@ -225,6 +225,7 @@ fn main() { args.bpm.into(), left_face, right_face, + vec![], ) .unwrap(); diff --git a/data-struct-lib/src/song.rs b/data-struct-lib/src/song.rs index c74be95..2e389e6 100644 --- a/data-struct-lib/src/song.rs +++ b/data-struct-lib/src/song.rs @@ -1,3 +1,5 @@ +mod beat_and_timing; + use std::{ fs::{self, File}, path::Path, @@ -9,6 +11,8 @@ use serde::{Deserialize, Serialize}; use crate::janggu::{JangguFace, JangguStick}; +use self::beat_and_timing::{beat, get_position, timing_in_ms}; + #[derive(Serialize, Deserialize, Debug, Clone, Copy)] pub enum GameSongCategory { Kpop, @@ -41,6 +45,16 @@ pub struct GameNote { #[serde(skip, default = "JangguFace::default")] pub face: JangguFace, } + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct GameHatNote { + beat_index: u64, + tick_nomiator: i64, + tick_denomiator: i64, + #[serde(skip)] + pub id: u64, +} + #[derive(Serialize, Deserialize, Debug, Clone)] pub struct GameChart { pub artist: String, @@ -48,6 +62,8 @@ pub struct GameChart { pub bpm: u32, pub left_face: Vec, pub right_face: Vec, + #[serde(default)] + pub hats: Vec, } impl GameChart { @@ -58,6 +74,7 @@ impl GameChart { bpm: u32, left_face: Vec, right_face: Vec, + hats: Vec, ) -> Result { let chart = GameChart { artist: artist, @@ -65,6 +82,7 @@ impl GameChart { bpm: bpm, left_face: left_face, right_face: right_face, + hats: hats, }; serde_json::to_string(&chart) @@ -101,12 +119,43 @@ impl GameChart { } else { vec![] }, + hats: vec![], }; return chart; } } +impl GameHatNote { + pub fn create_raw_note( + beat_index: u64, + tick_nomiator: i64, + tick_denomiator: i64, + ) -> GameHatNote { + return GameHatNote { + beat_index: beat_index, + tick_nomiator: tick_nomiator, + tick_denomiator: tick_denomiator, + // id is useless + id: 0, + }; + } + + /// get the position of the note in unit of beat + pub fn beat(&self) -> Rational64 { + beat( + self.beat_index as i64, + self.tick_nomiator, + self.tick_denomiator, + ) + } + + /// calculate the timing of the note + pub fn timing_in_ms(&self, track_bpm: u32, track_delay: u64) -> u64 { + timing_in_ms(self.beat(), track_bpm, track_delay) + } +} + impl GameNote { pub fn create_raw_note( stick: JangguStick, @@ -127,24 +176,18 @@ impl GameNote { /// get the position of the note in unit of beat pub fn beat(&self) -> Rational64 { - return Rational64::new(self.beat_index as i64, 1) - + if self.tick_denomiator == 0 { - Rational64::new(0, 1) - } else { - Rational64::new(self.tick_nomiator, self.tick_denomiator) - }; + beat( + self.beat_index as i64, + self.tick_nomiator, + self.tick_denomiator, + ) } + /// calculate the timing of the note pub fn timing_in_ms(&self, track_bpm: u32, track_delay: u64) -> u64 { - // bpm = beat / minute - // minute-per-beat = 1 / bpm - // timing-in-minute = beat * minute-per-beat - // timing-in-millisecond = timing-in-minute (minute) * ( 60000(millisecond) / 1(minute) ) - // timing = timing-in-millisecond - let timing = self.beat() * Rational64::new(60000, track_bpm as i64); - - (timing.numer() / timing.denom()) as u64 + track_delay + timing_in_ms(self.beat(), track_bpm, track_delay) } + /// Get the position of the note in the display. /// In other words, get the note should be how far from the judgement line /// in unit of the note width. @@ -161,17 +204,11 @@ impl GameNote { display_bpm: u32, current_time_in_ms: u64, ) -> f64 { - let end_time = self.timing_in_ms(track_bpm, track_delay); - // beat_per_millisecond = (display_bpm / 60000) - // millisecond_per_beat = 1/ beat_per_millisecond - // speed = 1 / millisecond_per_beat - let speed_ratio = Rational64::new(display_bpm as i64, 60000); - - // convert the ratio into floating value - let speed = *speed_ratio.numer() as f64 / *speed_ratio.denom() as f64; - - // return the note should be how far from the judgement line - (end_time as f64 - current_time_in_ms as f64) * speed + get_position( + self.timing_in_ms(track_bpm, track_delay), + display_bpm, + current_time_in_ms, + ) } } @@ -196,6 +233,10 @@ impl GameSong { note.id = note_index; note_index += 1; } + for note in &mut result_unwrapped.hats { + note.id = note_index; + note_index += 1; + } } return result; diff --git a/data-struct-lib/src/song/beat_and_timing.rs b/data-struct-lib/src/song/beat_and_timing.rs new file mode 100644 index 0000000..53b3563 --- /dev/null +++ b/data-struct-lib/src/song/beat_and_timing.rs @@ -0,0 +1,46 @@ +use num_rational::Rational64; + +/// get the position of the note in unit of beat +pub(super) fn beat(beat_index: i64, tick_nomiator: i64, tick_denomiator: i64) -> Rational64 { + return Rational64::new(beat_index as i64, 1) + + if tick_denomiator == 0 { + Rational64::new(0, 1) + } else { + Rational64::new(tick_nomiator, tick_denomiator) + }; +} + +/// calculate the timing of the note +pub(super) fn timing_in_ms(beat: Rational64, track_bpm: u32, track_delay: u64) -> u64 { + // bpm = beat / minute + // minute-per-beat = 1 / bpm + // timing-in-minute = beat * minute-per-beat + // timing-in-millisecond = timing-in-minute (minute) * ( 60000(millisecond) / 1(minute) ) + // timing = timing-in-millisecond + let timing = beat * Rational64::new(60000, track_bpm as i64); + + (timing.numer() / timing.denom()) as u64 + track_delay +} + +/// Get the position of the note in the display. +/// In other words, get the note should be how far from the judgement line +/// in unit of the note width. +/// +/// # Return value example +/// - `-1.0` : after the judgement line the width of the note +/// - `0.0` : at the judgement line +/// - `1.0` : before the judgement line the width of the note +/// - `2.0` : before the judgement line the width of the two notes +pub(super) fn get_position(timinig_in_ms: u64, display_bpm: u32, current_time_in_ms: u64) -> f64 { + let end_time = timinig_in_ms; + // beat_per_millisecond = (display_bpm / 60000) + // millisecond_per_beat = 1/ beat_per_millisecond + // speed = 1 / millisecond_per_beat + let speed_ratio = Rational64::new(display_bpm as i64, 60000); + + // convert the ratio into floating value + let speed = *speed_ratio.numer() as f64 / *speed_ratio.denom() as f64; + + // return the note should be how far from the judgement line + (end_time as f64 - current_time_in_ms as f64) * speed +} From 00075b61d6b9239cd7958f740c11c29c92ce4c93 Mon Sep 17 00:00:00 2001 From: LiteHell Date: Tue, 21 May 2024 21:19:19 +0900 Subject: [PATCH 6/9] feat(hat): add hat_timing_judge spinning is temporarily false in game_player.rs --- game/src/constants.rs | 4 + game/src/game/game_player/chart_player.rs | 2 +- game/src/game/game_player/timing_judge.rs | 20 +++- .../timing_judge/hat_timing_judge.rs | 100 ++++++++++++++++++ 4 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 game/src/game/game_player/timing_judge/hat_timing_judge.rs diff --git a/game/src/constants.rs b/game/src/constants.rs index a5ac112..b3b07c3 100644 --- a/game/src/constants.rs +++ b/game/src/constants.rs @@ -16,6 +16,10 @@ pub const PERFECT_TIMING: i64 = 44; pub const GREAT_TIMING: i64 = 70; pub const GOOD_TIMING: i64 = 95; pub const BAD_TIMING: i64 = 160; +pub const HAT_TIMING: i64 = 1500; + +// hat score value +pub const HAT_SCORE: u64 = 1500; // add combo value pub const OVERCHAOS_COMBO: u64 = 1; diff --git a/game/src/game/game_player/chart_player.rs b/game/src/game/game_player/chart_player.rs index a2f4e23..9504eb6 100644 --- a/game/src/game/game_player/chart_player.rs +++ b/game/src/game/game_player/chart_player.rs @@ -66,7 +66,7 @@ impl ChartPlayer<'_> { } pub fn judge(&mut self, janggu: &JangguStateWithTick, tick: i128) { - let new_accuracies = self.timing_judge.judge(janggu, tick as u64); + let new_accuracies = self.timing_judge.judge(janggu, false, tick as u64); if !new_accuracies.is_empty() { self.accuracy = Some(( diff --git a/game/src/game/game_player/timing_judge.rs b/game/src/game/game_player/timing_judge.rs index fd35725..88a1e92 100644 --- a/game/src/game/game_player/timing_judge.rs +++ b/game/src/game/game_player/timing_judge.rs @@ -1,5 +1,9 @@ +mod hat_timing_judge; + use bidrum_data_struct_lib::{janggu::JangguStick, song::GameChart}; +use self::hat_timing_judge::HatTimingJudge; + use super::{game_result::GameResult, janggu_state_with_tick::JangguStateWithTick}; use bidrum_data_struct_lib::song::GameNote; @@ -22,7 +26,7 @@ pub(crate) enum NoteAccuracy { // timings for accuracy judgement // e.g. 10 means -10ms ~ +10ms use crate::constants::{ - BAD_COMBO, GOOD_COMBO, GREAT_COMBO, MISS_COMBO, OVERCHAOS_COMBO, PERFECT_COMBO, + BAD_COMBO, GOOD_COMBO, GREAT_COMBO, HAT_SCORE, MISS_COMBO, OVERCHAOS_COMBO, PERFECT_COMBO, }; use crate::constants::{ BAD_HEALTH, DEFAULT_HEALTH, GOOD_HEALTH, GREAT_HEALTH, MISS_HEALTH, OVERCHAOS_HEALTH, @@ -42,6 +46,7 @@ struct NoteForProcessing { /// Judges timing accuracy pub(crate) struct TimingJudge { notes: Vec, + hat_judge: HatTimingJudge, overchaos_count: u64, perfect_count: u64, great_count: u64, @@ -108,6 +113,8 @@ impl TimingJudge { .cmp(&b.note.timing_in_ms(b.bpm, b.delay)) }); + let hat_judge = HatTimingJudge::new(chart); + return TimingJudge { notes: notes, overchaos_count: 0, @@ -121,6 +128,7 @@ impl TimingJudge { score: 0, health: DEFAULT_HEALTH as i64, max_health: DEFAULT_HEALTH, + hat_judge: hat_judge, }; } @@ -134,10 +142,20 @@ impl TimingJudge { pub fn judge( &mut self, keydown: &JangguStateWithTick, + spinning: bool, tick_in_milliseconds: u64, ) -> Vec { let mut judged_notes = vec![]; + // process hat notes first + let hat_judge_result = self.hat_judge.judge(spinning, tick_in_milliseconds); + for i in hat_judge_result + .iter() + .filter(|x| !matches!(x.accuracy, NoteAccuracy::Miss)) + { + self.score += HAT_SCORE; + } + // if sticks are not keydown, there's no need to process the stick let mut proceseed_left_stick = false; let mut proceseed_right_stick = false; diff --git a/game/src/game/game_player/timing_judge/hat_timing_judge.rs b/game/src/game/game_player/timing_judge/hat_timing_judge.rs new file mode 100644 index 0000000..082420e --- /dev/null +++ b/game/src/game/game_player/timing_judge/hat_timing_judge.rs @@ -0,0 +1,100 @@ +use bidrum_data_struct_lib::song::{GameChart, GameHatNote}; + +use crate::constants::HAT_TIMING; + +use super::NoteAccuracy; + +// Combination of GameNoteTrack and GameNote +struct HatNoteForProcessing { + note: GameHatNote, + bpm: u32, + delay: u64, + id: u64, +} + +/// Judges timing accuracy +pub(super) struct HatTimingJudge { + notes: Vec, +} + +#[derive(Clone)] +pub(super) struct HatJudgeResult { + pub accuracy: NoteAccuracy, + pub note_id: u64, +} + +impl HatTimingJudge { + /// Creates new TimingJudge with collection of notes + pub fn new(chart: &GameChart) -> HatTimingJudge { + // flattens GameNote and GameNoteTrack into NoteForProcessing + let mut notes = Vec::::new(); + for j in &chart.hats { + notes.push(HatNoteForProcessing { + note: j.clone(), + bpm: chart.bpm, + delay: chart.delay, + id: j.id, + }); + } + + // sort the notes by their precise timings + notes.sort_by(|a, b| { + a.note + .timing_in_ms(a.bpm, a.delay) + .cmp(&b.note.timing_in_ms(b.bpm, b.delay)) + }); + + return HatTimingJudge { notes: notes }; + } + + /// Checks the notes for judgement + /// If there's judged note by the given janggu state and timing, return the judged elements + /// If there's no judged note, return empty vector + /// + /// # Arguments + /// * `spinning`: the current hat sate + /// * `tick_in_milliseconds` : the current time position of the song + pub fn judge(&mut self, spinning: bool, tick_in_milliseconds: u64) -> Vec { + let mut judged_notes = vec![]; + + // if sticks are not keydown, there's no need to process the stick + for i in &mut self.notes { + let precise_timing = i.note.timing_in_ms(i.bpm.into(), i.delay); + let difference = tick_in_milliseconds as i64 - precise_timing as i64; + + // judge the miss + if difference > (HAT_TIMING) { + judged_notes.push(HatJudgeResult { + note_id: i.id, + accuracy: NoteAccuracy::Miss, + }); + continue; + } + + // skip not-yet notes + if difference < -HAT_TIMING { + continue; + } + + // process the timings + if spinning { + judged_notes.push(HatJudgeResult { + note_id: i.id, + accuracy: NoteAccuracy::Perfect, + }); + + break; + } + } + + // process combo and delete judged notes + for i in &judged_notes { + // delete judged note + self.notes + .remove(self.notes.iter().position(|x| x.id == i.note_id).unwrap()); + } + + // return judgement result of the judged note + judged_notes + } +} From 2a9950b1d005cbaf5bf163dd3f2df88909a51603 Mon Sep 17 00:00:00 2001 From: LiteHell Date: Tue, 21 May 2024 21:23:12 +0900 Subject: [PATCH 7/9] feat(hat): process hat input when playing game --- Cargo.lock | 1 + game/Cargo.toml | 1 + game/src/game/game_common_context.rs | 2 ++ game/src/game/game_player.rs | 6 +++++- game/src/game/game_player/chart_player.rs | 4 ++-- game/src/game/init.rs | 5 +++++ game/src/game/tutorial/learn_stick_note.rs | 2 +- 7 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8db878c..bd5dc1f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -145,6 +145,7 @@ name = "bidrum" version = "0.1.0" dependencies = [ "bidrum-data-struct-lib", + "bidrum-hat", "clap", "const_format", "device_query", diff --git a/game/Cargo.toml b/game/Cargo.toml index 7488bb5..2331092 100644 --- a/game/Cargo.toml +++ b/game/Cargo.toml @@ -17,6 +17,7 @@ serde = { version = "1.0.195", features = ["derive"] } serde_json = "1.0.111" serialport = "4.3.0" bidrum-data-struct-lib = { path = "../data-struct-lib" } +bidrum-hat = { path = "../bidrum-hat" } device_query = "2.0.0" ezing = "0.2.1" const_format = "0.2.32" diff --git a/game/src/game/game_common_context.rs b/game/src/game/game_common_context.rs index b8e97e1..79bdcd8 100644 --- a/game/src/game/game_common_context.rs +++ b/game/src/game/game_common_context.rs @@ -3,6 +3,7 @@ use std::{ time::Instant, }; +use bidrum_hat::BidrumHat; use kira::manager::AudioManager; use sdl2::{render::Canvas, video::Window, EventPump}; @@ -21,6 +22,7 @@ pub(crate) struct GameCommonContext { pub(crate) dpi: (f32, f32, f32), pub(crate) ttf_context: sdl2::ttf::Sdl2TtfContext, pub(crate) game_initialized_at: Instant, + pub(crate) hat: BidrumHat, } impl GameCommonContext { diff --git a/game/src/game/game_player.rs b/game/src/game/game_player.rs index 598452a..5fb7291 100644 --- a/game/src/game/game_player.rs +++ b/game/src/game/game_player.rs @@ -197,7 +197,11 @@ pub(crate) fn play_song( // display notes and accuracy if tick_now >= 0 { - chart_player.judge(&janggu_state_with_tick, tick_now); + chart_player.judge( + &janggu_state_with_tick, + common_context.hat.spinning(), + tick_now, + ); chart_player.draw( tick_now, &mut common_context.canvas, diff --git a/game/src/game/game_player/chart_player.rs b/game/src/game/game_player/chart_player.rs index 9504eb6..c8c1f97 100644 --- a/game/src/game/game_player/chart_player.rs +++ b/game/src/game/game_player/chart_player.rs @@ -65,8 +65,8 @@ impl ChartPlayer<'_> { } } - pub fn judge(&mut self, janggu: &JangguStateWithTick, tick: i128) { - let new_accuracies = self.timing_judge.judge(janggu, false, tick as u64); + pub fn judge(&mut self, janggu: &JangguStateWithTick, spinning: bool, tick: i128) { + let new_accuracies = self.timing_judge.judge(janggu, spinning, tick as u64); if !new_accuracies.is_empty() { self.accuracy = Some(( diff --git a/game/src/game/init.rs b/game/src/game/init.rs index 6c838c0..ebd0fc7 100644 --- a/game/src/game/init.rs +++ b/game/src/game/init.rs @@ -3,6 +3,7 @@ use std::{ time::Instant, }; +use bidrum_hat::BidrumHat; use kira::manager::{backend::DefaultBackend, AudioManager, AudioManagerSettings}; use super::{game_common_context::GameCommonContext, start::start_game, title::render_title}; @@ -93,6 +94,9 @@ pub(crate) fn init_game(janggu_bits: Arc, options: InitGameOptions) { // create ttf context let ttf_context = sdl2::ttf::init().expect("Failed to init ttf context"); + // create hat + let hat = BidrumHat::new(); + // create GameCommonContext object let mut context = GameCommonContext { coins: 0, @@ -106,6 +110,7 @@ pub(crate) fn init_game(janggu_bits: Arc, options: InitGameOptions) { janggu_bits_ptr: janggu_bits, ttf_context: ttf_context, game_initialized_at: Instant::now(), + hat: hat, }; // enter game loop diff --git a/game/src/game/tutorial/learn_stick_note.rs b/game/src/game/tutorial/learn_stick_note.rs index f2c3707..b5e5862 100644 --- a/game/src/game/tutorial/learn_stick_note.rs +++ b/game/src/game/tutorial/learn_stick_note.rs @@ -246,7 +246,7 @@ fn display_tryitout_notes( effect_sound_player.play_janggu_sound(&janggu_state, &mut common_context.audio_manager); // Judge and display UI - chart_player.judge(&janggu_state, tick.into()); + chart_player.judge(&janggu_state, common_context.hat.spinning(), tick.into()); chart_player.draw( tick.into(), &mut common_context.canvas, From 914d991e5eeb60d85923bc1a1619a19a97fa94cd Mon Sep 17 00:00:00 2001 From: LiteHell Date: Tue, 11 Jun 2024 22:06:47 +0900 Subject: [PATCH 8/9] feat: blink janggu icon when there's hat notes --- game/src/game/game_player/chart_player.rs | 9 ++++ game/src/game/game_player/chart_player_ui.rs | 48 +++++++++++++++----- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/game/src/game/game_player/chart_player.rs b/game/src/game/game_player/chart_player.rs index 8006a60..5823f5f 100644 --- a/game/src/game/game_player/chart_player.rs +++ b/game/src/game/game_player/chart_player.rs @@ -182,6 +182,15 @@ impl ChartPlayer<'_> { self.ui.notes = self.get_display_notes(tick as u64); } self.ui.overall_effect_tick = overall_tick; + self.ui.remaining_hat_ticks = self + .chart + .hats + .iter() + .map(|x| x.timing_in_ms(self.chart.bpm, self.chart.delay)) + .map(|x| x as i128 - tick) + .filter(|x| *x >= -3000) + .map(|x| x as i64) + .collect(); self.ui.draw(canvas); } diff --git a/game/src/game/game_player/chart_player_ui.rs b/game/src/game/game_player/chart_player_ui.rs index 33417bc..df05052 100644 --- a/game/src/game/game_player/chart_player_ui.rs +++ b/game/src/game/game_player/chart_player_ui.rs @@ -31,6 +31,7 @@ use super::timing_judge::NoteAccuracy; pub struct ChartPlayerUI<'a> { pub notes: Vec, + pub remaining_hat_ticks: Vec, pub accuracy: Option, pub combo: Option, pub accuracy_and_combo_time_progress: Option, @@ -50,6 +51,7 @@ impl ChartPlayerUI<'_> { pub fn with_resources(resources: ChartPlayerUIResources) -> ChartPlayerUI { return ChartPlayerUI { notes: vec![], + remaining_hat_ticks: vec![], accuracy: None, combo: None, accuracy_and_combo_time_progress: None, @@ -345,18 +347,40 @@ impl ChartPlayerUI<'_> { } // draw janggu icon on center - canvas - .copy( - &janggu_texture, - None, - Rect::new( - (viewport.width() - janggu_width) as i32 / 2, - (viewport.height() - janggu_height) as i32 / 2, - janggu_width, - janggu_height, - ), - ) - .expect("Failed to draw janggu icon"); + let janggu_hidden = if self.remaining_hat_ticks.is_empty() + || self + .remaining_hat_ticks + .iter() + .map(|i| i.abs()) + .all(|i| i > 3000) + { + false + } else { + (self + .remaining_hat_ticks + .iter() + .map(|i| i.abs()) + .min() + .unwrap() + / 100) + % 2 + == 0 + }; + + if !janggu_hidden { + canvas + .copy( + &janggu_texture, + None, + Rect::new( + (viewport.width() - janggu_width) as i32 / 2, + (viewport.height() - janggu_height) as i32 / 2, + janggu_width, + janggu_height, + ), + ) + .expect("Failed to draw janggu icon"); + } // draw note accuracy if let Some(accuracy) = self.accuracy { From 8dbede3dc7b8a70f7a8a746de5172a7a1ba9cb5c Mon Sep 17 00:00:00 2001 From: LiteHell Date: Wed, 12 Jun 2024 17:04:45 +0900 Subject: [PATCH 9/9] feat: reconnects hat automatically --- bidrum-hat/src/lib.rs | 48 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/bidrum-hat/src/lib.rs b/bidrum-hat/src/lib.rs index d9f2277..4fe0930 100644 --- a/bidrum-hat/src/lib.rs +++ b/bidrum-hat/src/lib.rs @@ -1,5 +1,6 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; +use std::time::Duration; use std::{f32, thread}; use btleplug::api::{Central, CentralEvent, Manager as _, Peripheral, ScanFilter}; @@ -51,13 +52,24 @@ async fn get_data(spinning: Arc, dropping: Arc) { if let Some(event) = event_option { match event { - CentralEvent::DeviceDiscovered(id) => { - let peripheral = adapter.peripheral(&id).await.unwrap(); + CentralEvent::DeviceDiscovered(id) | CentralEvent::DeviceUpdated(id) => { + let peripheral = { + let peripheral = adapter.peripheral(&id).await; + if let Ok(peripheral) = peripheral { + peripheral + } else { + break; + } + }; + let local_name = peripheral.properties().await.unwrap().unwrap().local_name; if local_name.is_some_and(|x| x.contains("bidrum-hat")) { if !peripheral.is_connected().await.unwrap_or(false) { - peripheral.connect().await.expect("Failed to connect"); + let connect_result = peripheral.connect().await; + if connect_result.is_err() { + break; + } } peripheral @@ -81,13 +93,29 @@ async fn get_data(spinning: Arc, dropping: Arc) { .expect("Failed to make notification stream"); // Process while the BLE connection is not broken or stopped. - while let Some(data) = notification_stream.next().await { - let norm = std::str::from_utf8(data.value.as_slice()) - .unwrap_or("0.0") - .parse::() - .unwrap(); - - spinning.store(norm > 1.0, Ordering::Relaxed); + loop { + let notification = notification_stream.next(); + let timeouted_notification = tokio::time::timeout( + Duration::from_millis(100), + notification, + ) + .await; + + if !peripheral.is_connected().await.unwrap_or(false) { + break; + } + + if let Ok(unwrapped) = timeouted_notification { + if let Some(data) = &unwrapped { + let norm = std::str::from_utf8(data.value.as_slice()) + .unwrap_or("0.0") + .parse::() + .unwrap(); + + spinning.store(norm > 1.0, Ordering::Relaxed); + println!("spin norm: {:#}", norm); + } + } } } }