diff --git a/Cargo.lock b/Cargo.lock
index 89a3eb7..ef48ccd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -103,7 +103,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43"
dependencies = [
"alsa-sys",
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"cfg-if",
"libc",
]
@@ -125,7 +125,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046"
dependencies = [
"android-properties",
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"cc",
"cesu8",
"jni",
@@ -136,7 +136,7 @@ dependencies = [
"ndk-context",
"ndk-sys 0.6.0+11769913",
"num_enum",
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
@@ -147,9 +147,9 @@ checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
[[package]]
name = "android_log-sys"
-version = "0.3.1"
+version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ecc8056bf6ab9892dcd53216c83d1597487d7dacac16c8df6b877d127df9937"
+checksum = "84521a3cf562bc62942e294181d9eef17eb38ceb8c68677bc49f144e4c3d4f8d"
[[package]]
name = "android_system_properties"
@@ -160,6 +160,12 @@ dependencies = [
"libc",
]
+[[package]]
+name = "anyhow"
+version = "1.0.97"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
+
[[package]]
name = "approx"
version = "0.5.1"
@@ -390,7 +396,7 @@ dependencies = [
"bevy_tasks",
"bevy_utils",
"bevy_window",
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"blake3",
"crossbeam-channel",
"derive_more",
@@ -437,7 +443,6 @@ dependencies = [
"bevy_reflect",
"bevy_transform",
"bevy_utils",
- "cpal",
"rodio",
]
@@ -489,7 +494,7 @@ dependencies = [
"bevy_transform",
"bevy_utils",
"bevy_window",
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"derive_more",
"nonmax",
"radsort",
@@ -536,7 +541,7 @@ dependencies = [
"bevy_reflect",
"bevy_tasks",
"bevy_utils",
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"concurrent-queue",
"derive_more",
"disqualified",
@@ -678,7 +683,7 @@ dependencies = [
"bevy_math",
"bevy_reflect",
"bevy_utils",
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"bytemuck",
"derive_more",
"futures-lite",
@@ -748,6 +753,20 @@ dependencies = [
"bevy_winit",
]
+[[package]]
+name = "bevy_kira_audio"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c929e59bffeb04011aab93880623163c0f8c31c7e99d752d74ba935a53546731"
+dependencies = [
+ "anyhow",
+ "bevy",
+ "kira",
+ "parking_lot",
+ "thiserror 2.0.12",
+ "uuid",
+]
+
[[package]]
name = "bevy_log"
version = "0.15.3"
@@ -807,7 +826,7 @@ dependencies = [
"bevy_reflect",
"bevy_transform",
"bevy_utils",
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"bytemuck",
"derive_more",
"hexasphere",
@@ -843,7 +862,7 @@ dependencies = [
"bevy_transform",
"bevy_utils",
"bevy_window",
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"bytemuck",
"derive_more",
"fixedbitset 0.5.7",
@@ -1017,7 +1036,7 @@ dependencies = [
"bevy_transform",
"bevy_utils",
"bevy_window",
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"bytemuck",
"derive_more",
"fixedbitset 0.5.7",
@@ -1239,7 +1258,7 @@ version = "0.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"cexpr",
"clang-sys",
"itertools",
@@ -1291,25 +1310,24 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
-version = "2.8.0"
+version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
+checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
dependencies = [
"serde",
]
[[package]]
name = "blake3"
-version = "1.6.0"
+version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1230237285e3e10cde447185e8975408ae24deaa67205ce684805c25bc0c7937"
+checksum = "675f87afced0413c9bb02843499dbbd3882a237645883f71a2b59644a6d2f753"
dependencies = [
"arrayref",
"arrayvec",
"cc",
"cfg-if",
"constant_time_eq",
- "memmap2",
]
[[package]]
@@ -1348,9 +1366,9 @@ checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
[[package]]
name = "bytemuck"
-version = "1.21.0"
+version = "1.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
+checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540"
dependencies = [
"bytemuck_derive",
]
@@ -1380,9 +1398,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]]
name = "bytes"
-version = "1.10.0"
+version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9"
+checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
name = "calloop"
@@ -1390,19 +1408,19 @@ version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"log",
"polling",
"rustix",
"slab",
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
name = "cc"
-version = "1.2.15"
+version = "1.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af"
+checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
dependencies = [
"jobserver",
"libc",
@@ -1621,7 +1639,7 @@ version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59fd57d82eb4bfe7ffa9b1cec0c05e2fd378155b47f255a67983cb4afe0e80c2"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"fontdb",
"log",
"rangemap",
@@ -1803,9 +1821,9 @@ checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53"
[[package]]
name = "either"
-version = "1.14.0"
+version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d"
+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "encase"
@@ -1816,7 +1834,7 @@ dependencies = [
"const_panic",
"encase_derive",
"glam",
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
@@ -1839,6 +1857,15 @@ dependencies = [
"syn",
]
+[[package]]
+name = "encoding_rs"
+version = "0.8.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
+dependencies = [
+ "cfg-if",
+]
+
[[package]]
name = "equivalent"
version = "1.0.2"
@@ -1847,9 +1874,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "erased-serde"
-version = "0.4.5"
+version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d"
+checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7"
dependencies = [
"serde",
"typeid",
@@ -2118,6 +2145,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc46dd3ec48fdd8e693a98d2b8bafae273a2d54c1de02a2a7e3d57d501f39677"
dependencies = [
"bytemuck",
+ "mint",
"rand",
"serde",
]
@@ -2191,7 +2219,7 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"gpu-alloc-types",
]
@@ -2201,7 +2229,7 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
]
[[package]]
@@ -2212,7 +2240,7 @@ checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd"
dependencies = [
"log",
"presser",
- "thiserror",
+ "thiserror 1.0.69",
"windows 0.58.0",
]
@@ -2222,7 +2250,7 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf29e94d6d243368b7a56caa16bc213e4f9f8ed38c4d9557069527b5d5281ca"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"gpu-descriptor-types",
"hashbrown 0.15.2",
]
@@ -2233,7 +2261,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
]
[[package]]
@@ -2338,7 +2366,7 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"inotify-sys",
"libc",
]
@@ -2373,9 +2401,9 @@ dependencies = [
[[package]]
name = "itoa"
-version = "1.0.14"
+version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
+checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "jni"
@@ -2388,7 +2416,7 @@ dependencies = [
"combine",
"jni-sys",
"log",
- "thiserror",
+ "thiserror 1.0.69",
"walkdir",
"windows-sys 0.45.0",
]
@@ -2435,6 +2463,22 @@ version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
+[[package]]
+name = "kira"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb5a9f9dff5e262540b628b00d5e1a772270a962d6869f8695bb24832ff3b394"
+dependencies = [
+ "cpal",
+ "glam",
+ "mint",
+ "paste",
+ "ringbuf",
+ "send_wrapper",
+ "symphonia",
+ "triple_buffer",
+]
+
[[package]]
name = "ktx2"
version = "0.3.0"
@@ -2489,9 +2533,9 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"libc",
- "redox_syscall 0.5.9",
+ "redox_syscall 0.5.10",
]
[[package]]
@@ -2580,7 +2624,7 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"block",
"core-graphics-types",
"foreign-types",
@@ -2605,6 +2649,21 @@ dependencies = [
"simd-adler32",
]
+[[package]]
+name = "mint"
+version = "0.5.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff"
+
+[[package]]
+name = "mobile"
+version = "0.1.0"
+dependencies = [
+ "bevy",
+ "bevy_kira_audio",
+ "tempo-trainer",
+]
+
[[package]]
name = "naga"
version = "23.1.0"
@@ -2613,7 +2672,7 @@ checksum = "364f94bc34f61332abebe8cad6f6cd82a5b65cff22c828d05d0968911462ca4f"
dependencies = [
"arrayvec",
"bit-set 0.8.0",
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"cfg_aliases 0.1.1",
"codespan-reporting",
"hexf-parse",
@@ -2623,7 +2682,7 @@ dependencies = [
"rustc-hash",
"spirv",
"termcolor",
- "thiserror",
+ "thiserror 1.0.69",
"unicode-xid",
]
@@ -2642,7 +2701,7 @@ dependencies = [
"regex",
"regex-syntax 0.8.5",
"rustc-hash",
- "thiserror",
+ "thiserror 1.0.69",
"tracing",
"unicode-ident",
]
@@ -2653,12 +2712,12 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"jni-sys",
"log",
"ndk-sys 0.5.0+25.2.9519653",
"num_enum",
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
@@ -2667,13 +2726,13 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"jni-sys",
"log",
"ndk-sys 0.6.0+11769913",
"num_enum",
"raw-window-handle",
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
@@ -2706,7 +2765,7 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"cfg-if",
"cfg_aliases 0.2.1",
"libc",
@@ -2820,7 +2879,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"block2",
"libc",
"objc2",
@@ -2836,7 +2895,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"block2",
"objc2",
"objc2-core-location",
@@ -2860,7 +2919,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"block2",
"objc2",
"objc2-foundation",
@@ -2902,7 +2961,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"block2",
"dispatch",
"libc",
@@ -2927,7 +2986,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"block2",
"objc2",
"objc2-foundation",
@@ -2939,7 +2998,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"block2",
"objc2",
"objc2-foundation",
@@ -2962,7 +3021,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"block2",
"objc2",
"objc2-cloud-kit",
@@ -2994,7 +3053,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"block2",
"objc2",
"objc2-core-location",
@@ -3088,7 +3147,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
"cfg-if",
"libc",
- "redox_syscall 0.5.9",
+ "redox_syscall 0.5.10",
"smallvec",
"windows-targets 0.52.6",
]
@@ -3119,18 +3178,18 @@ dependencies = [
[[package]]
name = "pin-project"
-version = "1.1.9"
+version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfe2e71e1471fe07709406bf725f710b02927c9c54b2b5b2ec0e8087d97c327d"
+checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
-version = "1.1.9"
+version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6e859e6e5bd50440ab63c47e3ebabc90f26251f7c73c3d3e837b74a1cc3fa67"
+checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
dependencies = [
"proc-macro2",
"quote",
@@ -3156,9 +3215,9 @@ dependencies = [
[[package]]
name = "pkg-config"
-version = "0.3.31"
+version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
+checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "png"
@@ -3214,9 +3273,9 @@ checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa"
[[package]]
name = "prettyplease"
-version = "0.2.29"
+version = "0.2.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac"
+checksum = "f1ccf34da56fc294e7d4ccf69a85992b7dfb826b7cf57bac6a70bba3494cc08a"
dependencies = [
"proc-macro2",
"syn",
@@ -3224,18 +3283,18 @@ dependencies = [
[[package]]
name = "proc-macro-crate"
-version = "3.2.0"
+version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b"
+checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35"
dependencies = [
"toml_edit",
]
[[package]]
name = "proc-macro2"
-version = "1.0.93"
+version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
+checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
dependencies = [
"unicode-ident",
]
@@ -3248,9 +3307,9 @@ checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d"
[[package]]
name = "quote"
-version = "1.0.38"
+version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
+checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801"
dependencies = [
"proc-macro2",
]
@@ -3366,11 +3425,11 @@ dependencies = [
[[package]]
name = "redox_syscall"
-version = "0.5.9"
+version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f"
+checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
]
[[package]]
@@ -3423,6 +3482,15 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
+[[package]]
+name = "ringbuf"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79abed428d1fd2a128201cec72c5f6938e2da607c6f3745f769fabea399d950a"
+dependencies = [
+ "crossbeam-utils",
+]
+
[[package]]
name = "rodio"
version = "0.19.0"
@@ -3431,7 +3499,7 @@ checksum = "6006a627c1a38d37f3d3a85c6575418cfe34a5392d60a686d0071e1c8d427acb"
dependencies = [
"cpal",
"lewton",
- "thiserror",
+ "thiserror 1.0.69",
]
[[package]]
@@ -3441,7 +3509,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
dependencies = [
"base64 0.21.7",
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"serde",
"serde_derive",
]
@@ -3464,7 +3532,7 @@ version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"errno",
"libc",
"linux-raw-sys",
@@ -3473,9 +3541,9 @@ dependencies = [
[[package]]
name = "rustversion"
-version = "1.0.19"
+version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
+checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
[[package]]
name = "rustybuzz"
@@ -3483,7 +3551,7 @@ version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"bytemuck",
"libm",
"smallvec",
@@ -3505,9 +3573,9 @@ dependencies = [
[[package]]
name = "ryu"
-version = "1.0.19"
+version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd"
+checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "same-file"
@@ -3558,9 +3626,9 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.139"
+version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6"
+checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
dependencies = [
"itoa",
"memchr",
@@ -3638,7 +3706,7 @@ version = "0.3.0+sdk-1.3.268.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
]
[[package]]
@@ -3670,11 +3738,82 @@ dependencies = [
"zeno",
]
+[[package]]
+name = "symphonia"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "815c942ae7ee74737bb00f965fa5b5a2ac2ce7b6c01c0cc169bbeaf7abd5f5a9"
+dependencies = [
+ "lazy_static",
+ "symphonia-codec-vorbis",
+ "symphonia-core",
+ "symphonia-format-ogg",
+ "symphonia-metadata",
+]
+
+[[package]]
+name = "symphonia-codec-vorbis"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a98765fb46a0a6732b007f7e2870c2129b6f78d87db7987e6533c8f164a9f30"
+dependencies = [
+ "log",
+ "symphonia-core",
+ "symphonia-utils-xiph",
+]
+
+[[package]]
+name = "symphonia-core"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "798306779e3dc7d5231bd5691f5a813496dc79d3f56bf82e25789f2094e022c3"
+dependencies = [
+ "arrayvec",
+ "bitflags 1.3.2",
+ "bytemuck",
+ "lazy_static",
+ "log",
+]
+
+[[package]]
+name = "symphonia-format-ogg"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ada3505789516bcf00fc1157c67729eded428b455c27ca370e41f4d785bfa931"
+dependencies = [
+ "log",
+ "symphonia-core",
+ "symphonia-metadata",
+ "symphonia-utils-xiph",
+]
+
+[[package]]
+name = "symphonia-metadata"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc622b9841a10089c5b18e99eb904f4341615d5aa55bbf4eedde1be721a4023c"
+dependencies = [
+ "encoding_rs",
+ "lazy_static",
+ "log",
+ "symphonia-core",
+]
+
+[[package]]
+name = "symphonia-utils-xiph"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "484472580fa49991afda5f6550ece662237b00c6f562c7d9638d1b086ed010fe"
+dependencies = [
+ "symphonia-core",
+ "symphonia-metadata",
+]
+
[[package]]
name = "syn"
-version = "2.0.98"
+version = "2.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
+checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2"
dependencies = [
"proc-macro2",
"quote",
@@ -3721,6 +3860,7 @@ name = "tempo-trainer"
version = "0.1.0"
dependencies = [
"bevy",
+ "log",
]
[[package]]
@@ -3738,7 +3878,16 @@ version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
- "thiserror-impl",
+ "thiserror-impl 1.0.69",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
+dependencies = [
+ "thiserror-impl 2.0.12",
]
[[package]]
@@ -3752,6 +3901,17 @@ dependencies = [
"syn",
]
+[[package]]
+name = "thiserror-impl"
+version = "2.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "thread_local"
version = "1.1.8"
@@ -3773,9 +3933,9 @@ dependencies = [
[[package]]
name = "tinyvec"
-version = "1.8.1"
+version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8"
+checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71"
dependencies = [
"tinyvec_macros",
]
@@ -3890,6 +4050,15 @@ dependencies = [
"wasm-bindgen",
]
+[[package]]
+name = "triple_buffer"
+version = "8.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de7a7d39da903eaef0d0fd14aae8c8c36cdd7dc1d5a251f88c84b676e8dc0a14"
+dependencies = [
+ "crossbeam-utils",
+]
+
[[package]]
name = "ttf-parser"
version = "0.20.0"
@@ -3914,9 +4083,9 @@ dependencies = [
[[package]]
name = "typeid"
-version = "1.0.2"
+version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e"
+checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"
[[package]]
name = "unicode-bidi"
@@ -3938,9 +4107,9 @@ checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656"
[[package]]
name = "unicode-ident"
-version = "1.0.17"
+version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe"
+checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "unicode-linebreak"
@@ -3985,6 +4154,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b"
dependencies = [
"getrandom",
+ "rand",
"serde",
]
@@ -4146,7 +4316,7 @@ checksum = "d63c3c478de8e7e01786479919c8769f62a22eec16788d8c2ac77ce2c132778a"
dependencies = [
"arrayvec",
"bit-vec 0.8.0",
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"cfg_aliases 0.1.1",
"document-features",
"indexmap",
@@ -4158,7 +4328,7 @@ dependencies = [
"raw-window-handle",
"rustc-hash",
"smallvec",
- "thiserror",
+ "thiserror 1.0.69",
"wgpu-hal",
"wgpu-types",
]
@@ -4173,7 +4343,7 @@ dependencies = [
"arrayvec",
"ash",
"bit-set 0.8.0",
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"block",
"bytemuck",
"cfg_aliases 0.1.1",
@@ -4200,7 +4370,7 @@ dependencies = [
"renderdoc-sys",
"rustc-hash",
"smallvec",
- "thiserror",
+ "thiserror 1.0.69",
"wasm-bindgen",
"web-sys",
"wgpu-types",
@@ -4214,7 +4384,7 @@ version = "23.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "610f6ff27778148c31093f3b03abc4840f9636d58d597ca2f5977433acfe0068"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"js-sys",
"web-sys",
]
@@ -4733,7 +4903,7 @@ checksum = "a809eacf18c8eca8b6635091543f02a5a06ddf3dad846398795460e6e0ae3cc0"
dependencies = [
"android-activity",
"atomic-waker",
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"block2",
"bytemuck",
"calloop",
@@ -4816,7 +4986,7 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5"
dependencies = [
- "bitflags 2.8.0",
+ "bitflags 2.9.0",
"dlib",
"log",
"once_cell",
diff --git a/Cargo.toml b/Cargo.toml
index 880263a..51a054e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,8 +3,50 @@ name = "tempo-trainer"
version = "0.1.0"
edition = "2024"
+[workspace]
+members = ["mobile"]
+
[dependencies]
-bevy = "0.15.3"
+bevy = { version = "0.15", default-features = false, features = [
+ "animation",
+ "bevy_asset",
+ "bevy_color",
+ "bevy_core_pipeline",
+ "bevy_gilrs",
+ "bevy_gizmos",
+ "bevy_gltf",
+ "bevy_mesh_picking_backend",
+ "bevy_pbr",
+ "bevy_picking",
+ "bevy_render",
+ "bevy_scene",
+ "bevy_sprite",
+ "bevy_sprite_picking_backend",
+ "bevy_state",
+ "bevy_text",
+ "bevy_ui",
+ "bevy_ui_picking_backend",
+ "bevy_window",
+ "bevy_winit",
+ "custom_cursor",
+ "default_font",
+ "hdr",
+ "multi_threaded",
+ "png",
+ "smaa_luts",
+ "sysinfo_plugin",
+ "tonemapping_luts",
+ "webgl2",
+ "x11",
+ "bevy_audio",
+ "vorbis",
+] }
+
+## This greatly improves WGPU's performance due to its heavy use of trace! calls
+log = { version = "0.4", features = [
+ "max_level_debug",
+ "release_max_level_warn",
+] }
[profile.web]
inherits = "release"
diff --git a/build/android/res/mipmap-mdpi/icon.png b/build/android/res/mipmap-mdpi/icon.png
new file mode 100644
index 0000000..63a5cd3
Binary files /dev/null and b/build/android/res/mipmap-mdpi/icon.png differ
diff --git a/build/icon_1024x1024.png b/build/icon_1024x1024.png
new file mode 100644
index 0000000..e1a7be5
Binary files /dev/null and b/build/icon_1024x1024.png differ
diff --git a/build/macos/AppIcon.iconset/icon_128x128.png b/build/macos/AppIcon.iconset/icon_128x128.png
new file mode 100644
index 0000000..a31ad87
Binary files /dev/null and b/build/macos/AppIcon.iconset/icon_128x128.png differ
diff --git a/build/macos/AppIcon.iconset/icon_128x128@2x.png b/build/macos/AppIcon.iconset/icon_128x128@2x.png
new file mode 100644
index 0000000..8c780b8
Binary files /dev/null and b/build/macos/AppIcon.iconset/icon_128x128@2x.png differ
diff --git a/build/macos/AppIcon.iconset/icon_16x16.png b/build/macos/AppIcon.iconset/icon_16x16.png
new file mode 100644
index 0000000..20e53e8
Binary files /dev/null and b/build/macos/AppIcon.iconset/icon_16x16.png differ
diff --git a/build/macos/AppIcon.iconset/icon_16x16@2x.png b/build/macos/AppIcon.iconset/icon_16x16@2x.png
new file mode 100644
index 0000000..c4f2b8f
Binary files /dev/null and b/build/macos/AppIcon.iconset/icon_16x16@2x.png differ
diff --git a/build/macos/AppIcon.iconset/icon_256x256.png b/build/macos/AppIcon.iconset/icon_256x256.png
new file mode 100644
index 0000000..8c780b8
Binary files /dev/null and b/build/macos/AppIcon.iconset/icon_256x256.png differ
diff --git a/build/macos/AppIcon.iconset/icon_256x256@2x.png b/build/macos/AppIcon.iconset/icon_256x256@2x.png
new file mode 100644
index 0000000..a16a1aa
Binary files /dev/null and b/build/macos/AppIcon.iconset/icon_256x256@2x.png differ
diff --git a/build/macos/AppIcon.iconset/icon_32x32.png b/build/macos/AppIcon.iconset/icon_32x32.png
new file mode 100644
index 0000000..c4f2b8f
Binary files /dev/null and b/build/macos/AppIcon.iconset/icon_32x32.png differ
diff --git a/build/macos/AppIcon.iconset/icon_32x32@2x.png b/build/macos/AppIcon.iconset/icon_32x32@2x.png
new file mode 100644
index 0000000..250577f
Binary files /dev/null and b/build/macos/AppIcon.iconset/icon_32x32@2x.png differ
diff --git a/build/macos/AppIcon.iconset/icon_512x512.png b/build/macos/AppIcon.iconset/icon_512x512.png
new file mode 100644
index 0000000..a16a1aa
Binary files /dev/null and b/build/macos/AppIcon.iconset/icon_512x512.png differ
diff --git a/build/macos/AppIcon.iconset/icon_512x512@2x.png b/build/macos/AppIcon.iconset/icon_512x512@2x.png
new file mode 100644
index 0000000..e1a7be5
Binary files /dev/null and b/build/macos/AppIcon.iconset/icon_512x512@2x.png differ
diff --git a/build/macos/create_icns.sh b/build/macos/create_icns.sh
new file mode 100755
index 0000000..f71ed47
--- /dev/null
+++ b/build/macos/create_icns.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env sh
+
+rm -rf AppIcon.iconset/*
+mkdir -p AppIcon.iconset
+sips -z 16 16 ../icon_1024x1024.png --out AppIcon.iconset/icon_16x16.png
+sips -z 32 32 ../icon_1024x1024.png --out AppIcon.iconset/icon_16x16@2x.png
+sips -z 32 32 ../icon_1024x1024.png --out AppIcon.iconset/icon_32x32.png
+sips -z 64 64 ../icon_1024x1024.png --out AppIcon.iconset/icon_32x32@2x.png
+sips -z 128 128 ../icon_1024x1024.png --out AppIcon.iconset/icon_128x128.png
+sips -z 256 256 ../icon_1024x1024.png --out AppIcon.iconset/icon_128x128@2x.png
+sips -z 256 256 ../icon_1024x1024.png --out AppIcon.iconset/icon_256x256.png
+sips -z 512 512 ../icon_1024x1024.png --out AppIcon.iconset/icon_256x256@2x.png
+sips -z 512 512 ../icon_1024x1024.png --out AppIcon.iconset/icon_512x512.png
+cp ../icon_1024x1024.png AppIcon.iconset/icon_512x512@2x.png
+iconutil -c icns AppIcon.iconset
+mkdir -p src/Game.app/Contents/Resources
+mv AppIcon.icns src/Game.app/Contents/Resources/
diff --git a/build/macos/create_icns_linux.sh b/build/macos/create_icns_linux.sh
new file mode 100755
index 0000000..d538ef0
--- /dev/null
+++ b/build/macos/create_icns_linux.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env sh
+
+rm -rf AppIcon.iconset/*
+mkdir -p AppIcon.iconset
+convert ../icon_1024x1024.png -resize 16x16 AppIcon.iconset/icon_16x16.png
+convert ../icon_1024x1024.png -resize 32x32 AppIcon.iconset/icon_16x16@2x.png
+convert ../icon_1024x1024.png -resize 32x32 AppIcon.iconset/icon_32x32.png
+convert ../icon_1024x1024.png -resize 64x64 AppIcon.iconset/icon_32x32@2x.png
+convert ../icon_1024x1024.png -resize 128x128 AppIcon.iconset/icon_128x128.png
+convert ../icon_1024x1024.png -resize 256x256 AppIcon.iconset/icon_128x128@2x.png
+convert ../icon_1024x1024.png -resize 256x256 AppIcon.iconset/icon_256x256.png
+convert ../icon_1024x1024.png -resize 512x512 AppIcon.iconset/icon_256x256@2x.png
+convert ../icon_1024x1024.png -resize 512x512 AppIcon.iconset/icon_512x512.png
+cp ../icon_1024x1024.png AppIcon.iconset/icon_512x512@2x.png
+png2icns ./AppIcon.icns AppIcon.iconset/icon_16x16.png AppIcon.iconset/icon_32x32.png AppIcon.iconset/icon_128x128.png AppIcon.iconset/icon_256x256.png AppIcon.iconset/icon_512x512.png
+mkdir -p src/Game.app/Contents/Resources
+mv AppIcon.icns src/Game.app/Contents/Resources/
diff --git a/build/macos/src/Game.app/Contents/Info.plist b/build/macos/src/Game.app/Contents/Info.plist
new file mode 100644
index 0000000..3609a04
--- /dev/null
+++ b/build/macos/src/Game.app/Contents/Info.plist
@@ -0,0 +1,29 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleDisplayName
+ BevyGame
+ CFBundleExecutable
+ bevy_game
+ CFBundleIconFile
+ AppIcon.icns
+ CFBundleIdentifier
+ your.domain.bevy-game
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ Bevy Game
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+
+ 0.1.0
+ CFBundleSupportedPlatforms
+
+ MacOSX
+
+
+
diff --git a/build/macos/src/Game.app/Contents/Resources/AppIcon.icns b/build/macos/src/Game.app/Contents/Resources/AppIcon.icns
new file mode 100644
index 0000000..a46cb58
Binary files /dev/null and b/build/macos/src/Game.app/Contents/Resources/AppIcon.icns differ
diff --git a/build/web/sound.js b/build/web/sound.js
new file mode 100644
index 0000000..b2ab677
--- /dev/null
+++ b/build/web/sound.js
@@ -0,0 +1,62 @@
+// Insert hack to make sound autoplay on Chrome as soon as the user interacts with the tab:
+// https://developers.google.com/web/updates/2018/11/web-audio-autoplay#moving-forward
+
+// the following function keeps track of all AudioContexts and resumes them on the first user
+// interaction with the page. If the function is called and all contexts are already running,
+// it will remove itself from all event listeners.
+(function () {
+ // An array of all contexts to resume on the page
+ const audioContextList = [];
+
+ // An array of various user interaction events we should listen for
+ const userInputEventNames = [
+ "click",
+ "contextmenu",
+ "auxclick",
+ "dblclick",
+ "mousedown",
+ "mouseup",
+ "pointerup",
+ "touchend",
+ "keydown",
+ "keyup",
+ ];
+
+ // A proxy object to intercept AudioContexts and
+ // add them to the array for tracking and resuming later
+ self.AudioContext = new Proxy(self.AudioContext, {
+ construct(target, args) {
+ const result = new target(...args);
+ audioContextList.push(result);
+ return result;
+ },
+ });
+
+ // To resume all AudioContexts being tracked
+ function resumeAllContexts(_event) {
+ let count = 0;
+
+ audioContextList.forEach((context) => {
+ if (context.state !== "running") {
+ context.resume();
+ } else {
+ count++;
+ }
+ });
+
+ // If all the AudioContexts have now resumed then we unbind all
+ // the event listeners from the page to prevent unnecessary resume attempts
+ // Checking count > 0 ensures that the user interaction happens AFTER the game started up
+ if (count > 0 && count === audioContextList.length) {
+ userInputEventNames.forEach((eventName) => {
+ document.removeEventListener(eventName, resumeAllContexts);
+ });
+ }
+ }
+
+ // We bind the resume function for each user interaction
+ // event on the page
+ userInputEventNames.forEach((eventName) => {
+ document.addEventListener(eventName, resumeAllContexts);
+ });
+})();
diff --git a/build/web/styles.css b/build/web/styles.css
new file mode 100644
index 0000000..b0a0359
--- /dev/null
+++ b/build/web/styles.css
@@ -0,0 +1,53 @@
+body, html {
+ height: 100%;
+}
+
+body {
+ background-color: lightgray;
+ margin: 0;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.game-container {
+ width: 800px;
+ height: 600px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.lds-dual-ring {
+ display: inline-block;
+ position: absolute;
+ left: 0;
+ right: 0;
+ margin: auto;
+ width: 80px;
+ height: 80px;
+}
+
+.lds-dual-ring:after {
+ content: " ";
+ display: block;
+ width: 64px;
+ height: 64px;
+ border-radius: 50%;
+ border: 6px solid #fff;
+ border-color: #fff transparent #fff transparent;
+ animation: lds-dual-ring 1.2s linear infinite;
+}
+
+@keyframes lds-dual-ring {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+
+#bevy {
+ z-index: 2;
+}
diff --git a/build/windows/icon.ico b/build/windows/icon.ico
new file mode 100644
index 0000000..8c7afbf
Binary files /dev/null and b/build/windows/icon.ico differ
diff --git a/build/windows/icon.rc b/build/windows/icon.rc
new file mode 100644
index 0000000..61c0aef
--- /dev/null
+++ b/build/windows/icon.rc
@@ -0,0 +1 @@
+app_icon ICON "icon.ico"
diff --git a/build/windows/installer/.gitignore b/build/windows/installer/.gitignore
new file mode 100644
index 0000000..4ded7c4
--- /dev/null
+++ b/build/windows/installer/.gitignore
@@ -0,0 +1,2 @@
+/bin/
+/obj/
diff --git a/build/windows/installer/InstallDirUi.wxs b/build/windows/installer/InstallDirUi.wxs
new file mode 100644
index 0000000..4815975
--- /dev/null
+++ b/build/windows/installer/InstallDirUi.wxs
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/windows/installer/Installer.sln b/build/windows/installer/Installer.sln
new file mode 100644
index 0000000..a79b034
--- /dev/null
+++ b/build/windows/installer/Installer.sln
@@ -0,0 +1,40 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{B7DD6F7E-DEF8-4E67-B5B7-07EF123DB6F0}") = "Installer", "Installer.wixproj", "{340293B0-F46C-46A0-88D8-4BB2F3465C53}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|ARM64 = Debug|ARM64
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|ARM64 = Release|ARM64
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {340293B0-F46C-46A0-88D8-4BB2F3465C53}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {340293B0-F46C-46A0-88D8-4BB2F3465C53}.Debug|Any CPU.Build.0 = Debug|x64
+ {340293B0-F46C-46A0-88D8-4BB2F3465C53}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {340293B0-F46C-46A0-88D8-4BB2F3465C53}.Debug|ARM64.Build.0 = Debug|ARM64
+ {340293B0-F46C-46A0-88D8-4BB2F3465C53}.Debug|x64.ActiveCfg = Debug|x64
+ {340293B0-F46C-46A0-88D8-4BB2F3465C53}.Debug|x64.Build.0 = Debug|x64
+ {340293B0-F46C-46A0-88D8-4BB2F3465C53}.Debug|x86.ActiveCfg = Debug|x86
+ {340293B0-F46C-46A0-88D8-4BB2F3465C53}.Debug|x86.Build.0 = Debug|x86
+ {340293B0-F46C-46A0-88D8-4BB2F3465C53}.Release|Any CPU.ActiveCfg = Release|x64
+ {340293B0-F46C-46A0-88D8-4BB2F3465C53}.Release|Any CPU.Build.0 = Release|x64
+ {340293B0-F46C-46A0-88D8-4BB2F3465C53}.Release|ARM64.ActiveCfg = Release|ARM64
+ {340293B0-F46C-46A0-88D8-4BB2F3465C53}.Release|ARM64.Build.0 = Release|ARM64
+ {340293B0-F46C-46A0-88D8-4BB2F3465C53}.Release|x64.ActiveCfg = Release|x64
+ {340293B0-F46C-46A0-88D8-4BB2F3465C53}.Release|x64.Build.0 = Release|x64
+ {340293B0-F46C-46A0-88D8-4BB2F3465C53}.Release|x86.ActiveCfg = Release|x86
+ {340293B0-F46C-46A0-88D8-4BB2F3465C53}.Release|x86.Build.0 = Release|x86
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/build/windows/installer/Installer.wixproj b/build/windows/installer/Installer.wixproj
new file mode 100644
index 0000000..4c3ca90
--- /dev/null
+++ b/build/windows/installer/Installer.wixproj
@@ -0,0 +1,29 @@
+
+
+
+ none
+ installer
+
+
+
+
+
+
+
+
+ AssetsDirectory
+ INSTALLFOLDER
+ false
+
+
+
+
+
+
+ CreditsDirectory
+ INSTALLFOLDER
+ false
+
+
+
+
diff --git a/build/windows/installer/Package.en-us.wxl b/build/windows/installer/Package.en-us.wxl
new file mode 100644
index 0000000..83e657b
--- /dev/null
+++ b/build/windows/installer/Package.en-us.wxl
@@ -0,0 +1,6 @@
+
+
+
+
diff --git a/build/windows/installer/Package.wxs b/build/windows/installer/Package.wxs
new file mode 100644
index 0000000..305bc1f
--- /dev/null
+++ b/build/windows/installer/Package.wxs
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/windows/installer/global.json b/build/windows/installer/global.json
new file mode 100644
index 0000000..9f70a2c
--- /dev/null
+++ b/build/windows/installer/global.json
@@ -0,0 +1,6 @@
+{
+ "sdk": {
+ "version": "7.0.102",
+ "rollForward": "latestFeature"
+ }
+}
diff --git a/mobile/.cargo/config.toml b/mobile/.cargo/config.toml
new file mode 100644
index 0000000..19a6340
--- /dev/null
+++ b/mobile/.cargo/config.toml
@@ -0,0 +1,5 @@
+# Flag to notify the compiler we're building for the iOS simulator from an Apple silicon mac
+# This needs some workarounds for now
+# See https://github.com/bevyengine/bevy/pull/10178 - remove if it's not needed anymore.
+[target.aarch64-apple-ios-sim]
+rustflags = ["--cfg=ios_simulator"]
diff --git a/mobile/.gitignore b/mobile/.gitignore
new file mode 100644
index 0000000..b446ed7
--- /dev/null
+++ b/mobile/.gitignore
@@ -0,0 +1,3 @@
+mobile.xcodeproj/xcuserdata/
+mobile.xcodeproj/project.xcworkspace/
+build/
diff --git a/mobile/Cargo.toml b/mobile/Cargo.toml
new file mode 100644
index 0000000..176e414
--- /dev/null
+++ b/mobile/Cargo.toml
@@ -0,0 +1,43 @@
+[package]
+name = "mobile"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[lib]
+name = "mobile"
+crate-type = ["staticlib", "cdylib"]
+
+[dependencies]
+tempo-trainer = { path = ".." }
+bevy = { version = "0.15", default-features = false, features = [
+ "android-native-activity",
+] }
+
+# Needed for Android
+bevy_kira_audio = { version = "0.22.0", features = ["android_shared_stdcxx"] }
+
+# See https://github.com/bevyengine/bevy/pull/12052
+[target.aarch64-apple-ios-sim.dependencies]
+bevy = { version = "0.15", default-features = false, features = [
+ "ios_simulator",
+] }
+
+[package.metadata.android]
+package = "me.nikl.bevygame" # ToDo
+apk_name = "BevyGame" # ToDo same as GAME_OSX_APP_NAME in release workflow
+assets = "../assets"
+strip = "strip"
+resources = "../build/android/res"
+build_targets = ["aarch64-linux-android"]
+
+[package.metadata.android.sdk]
+target_sdk_version = 33
+
+[package.metadata.android.application]
+icon = "@mipmap/icon"
+label = "Bevy Game" # ToDo
+
+[package.metadata.android.signing.release]
+path = "/home/hatoo/.android/debug.keystore"
+keystore_password = "android"
diff --git a/mobile/Makefile b/mobile/Makefile
new file mode 100644
index 0000000..4fb6908
--- /dev/null
+++ b/mobile/Makefile
@@ -0,0 +1,29 @@
+.PHONY: xcodebuild run install boot-sim generate clean
+
+DEVICE = ${DEVICE_ID}
+ifndef DEVICE_ID
+ DEVICE=$(shell xcrun simctl list devices 'iOS' | grep -v 'unavailable' | grep -v '^--' | grep -v '==' | head -n 1 | grep -E -o -i "([0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12})")
+endif
+
+run: install
+ # Todo: change the bundle identifier (then remove this comment :P)
+ xcrun simctl launch --console $(DEVICE) me.nikl.bevygame
+
+boot-sim:
+ xcrun simctl boot $(DEVICE) || true
+
+install: xcodebuild-simulator boot-sim
+ xcrun simctl install $(DEVICE) build/Build/Products/Debug-iphonesimulator/mobile.app
+
+xcodebuild-simulator:
+ IOS_TARGETS=x86_64-apple-ios xcodebuild -scheme mobile -configuration Debug -derivedDataPath build -destination "id=$(DEVICE)"
+
+xcodebuild-iphone:
+ IOS_TARGETS=aarch64-apple-ios xcodebuild -scheme mobile -configuration Debug -derivedDataPath build -arch arm64
+
+xcodebuild-iphone-release:
+ IOS_TARGETS=aarch64-apple-ios xcodebuild -scheme mobile -configuration Release -derivedDataPath build -arch arm64
+
+clean:
+ rm -r build
+ cargo clean
diff --git a/mobile/build_rust_deps.sh b/mobile/build_rust_deps.sh
new file mode 100644
index 0000000..af8e0a7
--- /dev/null
+++ b/mobile/build_rust_deps.sh
@@ -0,0 +1,55 @@
+#!/usr/bin/env bash
+
+# based on https://github.com/mozilla/glean/blob/main/build-scripts/xc-universal-binary.sh
+
+set -eux
+
+PATH=$PATH:$HOME/.cargo/bin
+
+RELFLAG=
+if [[ "$CONFIGURATION" != "Debug" ]]; then
+ RELFLAG="--profile dist"
+fi
+
+set -euvx
+
+if [[ -n "${DEVELOPER_SDK_DIR:-}" ]]; then
+ # Assume we're in Xcode, which means we're probably cross-compiling.
+ # In this case, we need to add an extra library search path for build scripts and proc-macros,
+ # which run on the host instead of the target.
+ # (macOS Big Sur does not have linkable libraries in /usr/lib/.)
+ export LIBRARY_PATH="${DEVELOPER_SDK_DIR}/MacOSX.sdk/usr/lib:${LIBRARY_PATH:-}"
+fi
+
+# add homebrew bin path, as it's the most commonly used package manager on macOS
+# this is needed for cmake on apple arm processors as it's not available by default
+export PATH="$PATH:/opt/homebrew/bin"
+
+IS_SIMULATOR=0
+if [ "${LLVM_TARGET_TRIPLE_SUFFIX-}" = "-simulator" ]; then
+ IS_SIMULATOR=1
+fi
+
+for arch in $ARCHS; do
+ case "$arch" in
+ x86_64)
+ if [ $IS_SIMULATOR -eq 0 ]; then
+ echo "Building for x86_64, but not a simulator build. What's going on?" >&2
+ exit 2
+ fi
+
+ # Intel iOS simulator
+ export CFLAGS_x86_64_apple_ios="-target x86_64-apple-ios"
+ cargo rustc --crate-type staticlib --lib $RELFLAG --target x86_64-apple-ios
+ ;;
+
+ arm64)
+ if [ $IS_SIMULATOR -eq 0 ]; then
+ # Hardware iOS targets
+ cargo rustc --crate-type staticlib --lib $RELFLAG --target aarch64-apple-ios
+ else
+ # M1 iOS simulator -- currently in Nightly only and requires to build `libstd`
+ cargo rustc --crate-type staticlib --lib $RELFLAG --target aarch64-apple-ios-sim
+ fi
+ esac
+done
diff --git a/mobile/ios-src/Assets.xcassets/AppIcon.appiconset/Contents.json b/mobile/ios-src/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..5a60908
--- /dev/null
+++ b/mobile/ios-src/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,14 @@
+{
+ "images" : [
+ {
+ "filename" : "icon_1024x1024.png",
+ "idiom" : "universal",
+ "platform" : "ios",
+ "size" : "1024x1024"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/mobile/ios-src/Assets.xcassets/AppIcon.appiconset/icon_1024x1024.png b/mobile/ios-src/Assets.xcassets/AppIcon.appiconset/icon_1024x1024.png
new file mode 100644
index 0000000..a79c37d
Binary files /dev/null and b/mobile/ios-src/Assets.xcassets/AppIcon.appiconset/icon_1024x1024.png differ
diff --git a/mobile/ios-src/Assets.xcassets/Contents.json b/mobile/ios-src/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/mobile/ios-src/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/mobile/ios-src/Info.plist b/mobile/ios-src/Info.plist
new file mode 100644
index 0000000..f815a6b
--- /dev/null
+++ b/mobile/ios-src/Info.plist
@@ -0,0 +1,35 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 0.1.1
+ CFBundleIconName
+ AppIcon
+ CFBundleVersion
+ 0.1.1
+ UILaunchStoryboardName
+ LaunchScreen
+ UIRequiresFullScreen
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+ UIInterfaceOrientationPortraitUpsideDown
+
+
+
diff --git a/mobile/ios-src/LaunchScreen.storyboard b/mobile/ios-src/LaunchScreen.storyboard
new file mode 100644
index 0000000..324de1d
--- /dev/null
+++ b/mobile/ios-src/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mobile/ios-src/bindings.h b/mobile/ios-src/bindings.h
new file mode 100644
index 0000000..a705f03
--- /dev/null
+++ b/mobile/ios-src/bindings.h
@@ -0,0 +1 @@
+void main_rs(void);
diff --git a/mobile/ios-src/main.m b/mobile/ios-src/main.m
new file mode 100644
index 0000000..7d152fb
--- /dev/null
+++ b/mobile/ios-src/main.m
@@ -0,0 +1,6 @@
+#import "bindings.h"
+
+int main() {
+ main_rs();
+ return 0;
+}
diff --git a/mobile/manifest.yaml b/mobile/manifest.yaml
new file mode 100644
index 0000000..4be78a5
--- /dev/null
+++ b/mobile/manifest.yaml
@@ -0,0 +1,11 @@
+android:
+ gradle: true
+ # this assets configuration is currently only used without gradle!
+ #assets:
+ # - "../assets/*"
+ icon: "ios-src/Assets.xcassets/AppIcon.appiconset/icon_1024x1024.png"
+ manifest:
+ package: "me.nikl.bevygame" # Todo
+ version_code: 3 # Todo you should start at 1
+ application:
+ label: "Bevy game" # Todo
diff --git a/mobile/mobile.xcodeproj/project.pbxproj b/mobile/mobile.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..4a377e4
--- /dev/null
+++ b/mobile/mobile.xcodeproj/project.pbxproj
@@ -0,0 +1,469 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 51;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 134866208A035F8615C99114 /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 96A1E5B62F48B379829E8A0D /* Metal.framework */; };
+ 2469A4292A6F9AC200ACF4EF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2469A4282A6F9AC200ACF4EF /* Assets.xcassets */; };
+ 2469A42B2A6FAC7000ACF4EF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2469A42A2A6FAC7000ACF4EF /* LaunchScreen.storyboard */; };
+ 2604C99FAB5A8322EDCABB9F /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FE71FBCAA714DB4F42459106 /* UIKit.framework */; };
+ 442540D056ADB9AE61A0A590 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9F1B41978FA53999AA836D0F /* Security.framework */; };
+ 55892F1396056740E1AF9685 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = AF7DE91055EBD05ED77E57F9 /* main.m */; };
+ 55B7188F81C3C4183F81D3AE /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = A39528EB2CCB182F5328223A /* libc++.tbd */; };
+ 57CD6306253C7A940098CD4A /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57CD6305253C7A940098CD4A /* AudioToolbox.framework */; };
+ 57CD630E253C80EC0098CD4A /* assets in Resources */ = {isa = PBXBuildFile; fileRef = 57CD630A253C7F5F0098CD4A /* assets */; };
+ 6ADF1AB92CCDA73A00AF5F8E /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6ADF1AB82CCDA73A00AF5F8E /* QuartzCore.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 26BF2C4863C966DABAB40DC8 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 8DBF1E2B5C613DA41701F6D9 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = D08AEBE0B1A9C9A7B8C7B33F;
+ remoteInfo = cargo_ios;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ 160DB77300A3F1806F024D47 /* bindings.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = bindings.h; sourceTree = ""; };
+ 2469A4282A6F9AC200ACF4EF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "ios-src/Assets.xcassets"; sourceTree = SOURCE_ROOT; };
+ 2469A42A2A6FAC7000ACF4EF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ios-src/LaunchScreen.storyboard; sourceTree = ""; };
+ 55EAC02897847195D2F44C15 /* mobile.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = mobile.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 57CD6305253C7A940098CD4A /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
+ 57CD630A253C7F5F0098CD4A /* assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = assets; path = ../../assets; sourceTree = ""; };
+ 6ADF1AB82CCDA73A00AF5F8E /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
+ 8EE7F1E3B0303533925D7E33 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; };
+ 96A1E5B62F48B379829E8A0D /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; };
+ 9F1B41978FA53999AA836D0F /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
+ A39528EB2CCB182F5328223A /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; };
+ AF7DE91055EBD05ED77E57F9 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
+ FE71FBCAA714DB4F42459106 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ D5A822CB2D6847BA8800BE4C /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 6ADF1AB92CCDA73A00AF5F8E /* QuartzCore.framework in Frameworks */,
+ 442540D056ADB9AE61A0A590 /* Security.framework in Frameworks */,
+ 134866208A035F8615C99114 /* Metal.framework in Frameworks */,
+ 2604C99FAB5A8322EDCABB9F /* UIKit.framework in Frameworks */,
+ 55B7188F81C3C4183F81D3AE /* libc++.tbd in Frameworks */,
+ 57CD6306253C7A940098CD4A /* AudioToolbox.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 321F7D6A765B38E746C35105 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 55EAC02897847195D2F44C15 /* mobile.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 4F1D6F28B8A5D1927AB0ADED /* ios-src */ = {
+ isa = PBXGroup;
+ children = (
+ 2469A4282A6F9AC200ACF4EF /* Assets.xcassets */,
+ 57CD630A253C7F5F0098CD4A /* assets */,
+ 160DB77300A3F1806F024D47 /* bindings.h */,
+ 8EE7F1E3B0303533925D7E33 /* Info.plist */,
+ AF7DE91055EBD05ED77E57F9 /* main.m */,
+ );
+ path = "ios-src";
+ sourceTree = "";
+ };
+ 8F2E3E6040EAD2EC9F3FA530 = {
+ isa = PBXGroup;
+ children = (
+ 2469A42A2A6FAC7000ACF4EF /* LaunchScreen.storyboard */,
+ 4F1D6F28B8A5D1927AB0ADED /* ios-src */,
+ EB028409C2D0655412DA6E44 /* Frameworks */,
+ 321F7D6A765B38E746C35105 /* Products */,
+ );
+ sourceTree = "";
+ };
+ EB028409C2D0655412DA6E44 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 6ADF1AB82CCDA73A00AF5F8E /* QuartzCore.framework */,
+ 57CD6305253C7A940098CD4A /* AudioToolbox.framework */,
+ A39528EB2CCB182F5328223A /* libc++.tbd */,
+ 96A1E5B62F48B379829E8A0D /* Metal.framework */,
+ 9F1B41978FA53999AA836D0F /* Security.framework */,
+ FE71FBCAA714DB4F42459106 /* UIKit.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXLegacyTarget section */
+ D08AEBE0B1A9C9A7B8C7B33F /* cargo_ios */ = {
+ isa = PBXLegacyTarget;
+ buildArgumentsString = build_rust_deps.sh;
+ buildConfigurationList = AA00A0CFDB11F37F2BA3FC2E /* Build configuration list for PBXLegacyTarget "cargo_ios" */;
+ buildPhases = (
+ FE045B3D04D57B713A565FF8 /* Sources */,
+ );
+ buildToolPath = /bin/sh;
+ buildWorkingDirectory = .;
+ dependencies = (
+ );
+ name = cargo_ios;
+ passBuildSettingsInEnvironment = 1;
+ productName = cargo_ios;
+ };
+/* End PBXLegacyTarget section */
+
+/* Begin PBXNativeTarget section */
+ 3BDB8152E4962373181B4FE5 /* mobile */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = E714A1AEAAE517C348B5BD27 /* Build configuration list for PBXNativeTarget "mobile" */;
+ buildPhases = (
+ 9F13800790AD9DBC2BC0F116 /* Sources */,
+ D5A822CB2D6847BA8800BE4C /* Frameworks */,
+ 57CD630D253C80E60098CD4A /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 19D4B9C22ADC6705B5132B4C /* PBXTargetDependency */,
+ );
+ name = mobile;
+ productName = mobile;
+ productReference = 55EAC02897847195D2F44C15 /* mobile.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 8DBF1E2B5C613DA41701F6D9 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 1240;
+ };
+ buildConfigurationList = 9D43D41707A5C30B227B83F9 /* Build configuration list for PBXProject "mobile" */;
+ compatibilityVersion = "Xcode 10.0";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ );
+ mainGroup = 8F2E3E6040EAD2EC9F3FA530;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 3BDB8152E4962373181B4FE5 /* mobile */,
+ D08AEBE0B1A9C9A7B8C7B33F /* cargo_ios */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 57CD630D253C80E60098CD4A /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 2469A42B2A6FAC7000ACF4EF /* LaunchScreen.storyboard in Resources */,
+ 2469A4292A6F9AC200ACF4EF /* Assets.xcassets in Resources */,
+ 57CD630E253C80EC0098CD4A /* assets in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 9F13800790AD9DBC2BC0F116 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 55892F1396056740E1AF9685 /* main.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FE045B3D04D57B713A565FF8 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 19D4B9C22ADC6705B5132B4C /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = D08AEBE0B1A9C9A7B8C7B33F /* cargo_ios */;
+ targetProxy = 26BF2C4863C966DABAB40DC8 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ 4AD7BC6FDD56FF18FA6DA7D7 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "$(inherited)",
+ "DEBUG=1",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = iphoneos;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Debug;
+ };
+ 5B14EC4ADC81FBF1F8CF20E9 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_CXX_LANGUAGE_STANDARD = "c++11";
+ CLANG_CXX_LIBRARY = "libc++";
+ CODE_SIGN_IDENTITY = "";
+ DEVELOPMENT_TEAM = "";
+ ENABLE_BITCODE = NO;
+ HEADER_SEARCH_PATHS = (
+ "$(inherited)",
+ "ios-src/",
+ );
+ INFOPLIST_FILE = "ios-src/Info.plist";
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ "LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = (
+ "$(inherited)",
+ "../target/aarch64-apple-ios/dist",
+ );
+ "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]" = (
+ "$(inherited)",
+ "../target/aarch64-apple-ios-sim/dist",
+ );
+ "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=x86_64]" = (
+ "$(inherited)",
+ "../target/x86_64-apple-ios/dist",
+ );
+ OTHER_LDFLAGS = (
+ "$(inherited)",
+ "-lmobile",
+ "-lc++abi",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER /* Todo */ = me.nikl.bevygame;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+ 79E3C28F06346EA58420A93D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_VERSION = 5.0;
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 8265913A25816D964A847F1B /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = "com.rust.cargo-ios";
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ A2D5B73DD30D562B6F366526 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_CXX_LANGUAGE_STANDARD = "c++11";
+ CLANG_CXX_LIBRARY = "libc++";
+ CODE_SIGN_IDENTITY = "";
+ DEVELOPMENT_TEAM = "";
+ ENABLE_BITCODE = NO;
+ HEADER_SEARCH_PATHS = (
+ "$(inherited)",
+ "ios-src/",
+ );
+ INFOPLIST_FILE = "ios-src/Info.plist";
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ "LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = (
+ "$(inherited)",
+ "../target/aarch64-apple-ios/debug",
+ );
+ "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]" = (
+ "$(inherited)",
+ "../target/aarch64-apple-ios-sim/debug",
+ );
+ "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=x86_64]" = (
+ "$(inherited)",
+ "../target/x86_64-apple-ios/debug",
+ );
+ OTHER_LDFLAGS = (
+ "$(inherited)",
+ "-lmobile",
+ "-lc++abi",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER /* Todo */ = me.nikl.bevygame;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ FEA9B18D9236F9F6DC6DF799 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = "com.rust.cargo-ios";
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 9D43D41707A5C30B227B83F9 /* Build configuration list for PBXProject "mobile" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 4AD7BC6FDD56FF18FA6DA7D7 /* Debug */,
+ 79E3C28F06346EA58420A93D /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Debug;
+ };
+ AA00A0CFDB11F37F2BA3FC2E /* Build configuration list for PBXLegacyTarget "cargo_ios" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 8265913A25816D964A847F1B /* Debug */,
+ FEA9B18D9236F9F6DC6DF799 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Debug;
+ };
+ E714A1AEAAE517C348B5BD27 /* Build configuration list for PBXNativeTarget "mobile" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ A2D5B73DD30D562B6F366526 /* Debug */,
+ 5B14EC4ADC81FBF1F8CF20E9 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Debug;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 8DBF1E2B5C613DA41701F6D9 /* Project object */;
+}
diff --git a/mobile/mobile.xcodeproj/xcshareddata/xcschemes/mobile.xcscheme b/mobile/mobile.xcodeproj/xcshareddata/xcschemes/mobile.xcscheme
new file mode 100644
index 0000000..495aaf6
--- /dev/null
+++ b/mobile/mobile.xcodeproj/xcshareddata/xcschemes/mobile.xcscheme
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mobile/src/lib.rs b/mobile/src/lib.rs
new file mode 100644
index 0000000..cee805c
--- /dev/null
+++ b/mobile/src/lib.rs
@@ -0,0 +1,23 @@
+use bevy::prelude::*;
+use bevy::window::{PresentMode, WindowMode};
+use bevy::winit::WinitSettings;
+use tempo_trainer::GamePlugin;
+
+#[bevy_main]
+fn main() {
+ App::new()
+ .insert_resource(WinitSettings::mobile())
+ .add_plugins((
+ DefaultPlugins.set(WindowPlugin {
+ primary_window: Some(Window {
+ resizable: false,
+ mode: WindowMode::BorderlessFullscreen(MonitorSelection::Current),
+ present_mode: PresentMode::AutoVsync,
+ ..default()
+ }),
+ ..default()
+ }),
+ GamePlugin,
+ ))
+ .run();
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..27da579
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,1170 @@
+use std::collections::VecDeque;
+
+use bevy::utils::{Duration, Instant};
+
+use bevy::{
+ color::palettes::basic::*,
+ diagnostic::{DiagnosticsStore, EntityCountDiagnosticsPlugin, FrameTimeDiagnosticsPlugin},
+ prelude::*,
+ render::{camera::ScalingMode, mesh::CircleMeshBuilder},
+};
+
+const CIRCLE_SIZE: f32 = 400.0;
+const BINS: usize = 16;
+
+const BAR_HEIGHT_MULTIPLIER: f32 = 4000.0;
+
+#[derive(Component)]
+struct StatusText;
+
+#[derive(Component)]
+struct ClockMarker;
+
+#[derive(Resource)]
+struct LastTick(Instant);
+
+#[derive(Resource)]
+struct Division(u32);
+
+struct Delta {
+ delta: f64,
+ division: usize,
+ // 0 to 2pi
+ theta: f64,
+}
+
+#[derive(Resource)]
+// delta and nearest disvision
+struct TapDeltas(VecDeque);
+
+#[derive(Resource, Default)]
+struct Mute {
+ tick_mute: bool,
+ tap_mute: bool,
+}
+
+#[derive(Resource)]
+struct HideClock(bool);
+
+#[derive(Component)]
+struct Clock;
+
+#[derive(Resource)]
+struct AudioHandles {
+ handles: Vec>,
+ tick: usize,
+ tap: usize,
+}
+
+impl AudioHandles {
+ fn tick(&self) -> &Handle {
+ &self.handles[self.tick]
+ }
+
+ fn tap(&self) -> &Handle {
+ &self.handles[self.tap]
+ }
+}
+
+#[derive(Resource)]
+struct ClockResource {
+ mesh_legend: Handle,
+ material_legend: Handle,
+ mesh_delta: Handle,
+ material_delta: Handle,
+ mesh_precision: Handle,
+ material_precision: Handle,
+}
+
+#[derive(Component)]
+enum Index {
+ Tick,
+ Tap,
+}
+
+#[derive(Component)]
+enum IndexButton {
+ TickIncrement,
+ TickDecrement,
+ TapIncrement,
+ TapDecrement,
+}
+
+#[derive(Component)]
+struct Statistics;
+
+pub struct GamePlugin;
+
+impl Plugin for GamePlugin {
+ fn build(&self, app: &mut App) {
+ app.add_plugins((FrameTimeDiagnosticsPlugin, EntityCountDiagnosticsPlugin))
+ .insert_resource(Time::::from_duration(from_bpm(90.0)))
+ .insert_resource(LastTick(Instant::now()))
+ .insert_resource(Division(1))
+ .insert_resource(TapDeltas(VecDeque::new()))
+ .insert_resource(Mute::default())
+ .insert_resource(HideBarChart(false))
+ .insert_resource(HideClock(false))
+ .add_systems(Startup, setup)
+ .add_systems(FixedUpdate, metronome)
+ .add_systems(
+ Update,
+ (
+ control,
+ clock,
+ set_status_text,
+ set_bins,
+ set_clock_legend,
+ diagnostics_text_update_system,
+ hide_bar_chart,
+ hide_clock,
+ button_system,
+ set_audio_indices,
+ set_statistics,
+ set_clock_delta,
+ ),
+ )
+ // Set tap sound before tap
+ .add_systems(Update, (index_button_system, tap).chain());
+ }
+}
+
+fn bpm(time: &Time) -> f32 {
+ 60.0 / time.timestep().as_secs_f32()
+}
+
+fn from_bpm(bpm: f32) -> Duration {
+ Duration::from_secs_f32(60.0 / bpm)
+}
+
+const NORMAL_BUTTON: Color = Color::srgb(0.15, 0.15, 0.15);
+const PRESSED_BUTTON: Color = Color::srgb(0.35, 0.75, 0.35);
+
+#[derive(Component, Clone, Copy)]
+enum ButtonKind {
+ BpmUp1,
+ BpmDown1,
+ BpmUp10,
+ BpmDown10,
+ DivisionUp1,
+ DivisionDown1,
+ TapMute,
+ TickMute,
+ HideClock,
+ HideBarChart,
+}
+
+impl ButtonKind {
+ fn label(&self) -> &str {
+ match self {
+ ButtonKind::BpmUp1 => "BPM+1",
+ ButtonKind::BpmDown1 => "BPM-1",
+ ButtonKind::BpmUp10 => "BPM+10",
+ ButtonKind::BpmDown10 => "BPM-10",
+ ButtonKind::DivisionUp1 => "Div+",
+ ButtonKind::DivisionDown1 => "Div-",
+ ButtonKind::TapMute => "Tap Mute",
+ ButtonKind::TickMute => "Tick Mute",
+ ButtonKind::HideClock => "Clock",
+ ButtonKind::HideBarChart => "Chart",
+ }
+ }
+}
+
+#[derive(Component)]
+struct BarChart;
+
+#[derive(Resource)]
+struct HideBarChart(bool);
+
+fn setup(
+ mut commands: Commands,
+ mut meshes: ResMut>,
+ mut materials: ResMut>,
+ asset_server: Res,
+) {
+ commands.insert_resource(AudioHandles {
+ handles: vec![
+ asset_server.load("sounds/c4.ogg"),
+ asset_server.load("sounds/c5.ogg"),
+ asset_server.load("sounds/808sd.ogg"),
+ asset_server.load("sounds/808cb.ogg"),
+ asset_server.load("sounds/808cp.ogg"),
+ ],
+ tap: 0,
+ tick: 1,
+ });
+
+ commands.insert_resource(ClockResource {
+ mesh_legend: meshes.add(Mesh::from(Circle { radius: 16.0 })),
+ material_legend: materials.add(Color::linear_rgb(0.1, 0.3, 0.1)),
+ mesh_delta: meshes.add(Mesh::from(Circle { radius: 12.0 })),
+ material_delta: materials.add(Color::linear_rgb(0.1, 0.1, 0.3)),
+ mesh_precision: meshes.add(Mesh::from(Rectangle {
+ half_size: Vec2::new(0.5, 0.5),
+ })),
+ material_precision: materials.add(Color::linear_rgb(0.0, 0.0, 0.0)),
+ });
+
+ commands.spawn((
+ Camera2d,
+ Projection::Orthographic(OrthographicProjection {
+ scaling_mode: ScalingMode::AutoMin {
+ min_width: 1200.0,
+ min_height: 1200.0,
+ },
+ ..OrthographicProjection::default_2d()
+ }),
+ ));
+
+ commands
+ .spawn((Clock, Transform::default(), Visibility::Hidden))
+ .with_children(|commands| {
+ commands.spawn((
+ Mesh2d(meshes.add(CircleMeshBuilder {
+ circle: Circle::new(CIRCLE_SIZE),
+ resolution: 128,
+ })),
+ MeshMaterial2d(materials.add(Color::linear_rgb(0.4, 0.4, 0.4))),
+ Transform::from_xyz(0.0, 0.0, 0.0),
+ ));
+
+ commands.spawn((
+ Mesh2d(meshes.add(CircleMeshBuilder {
+ circle: Circle::new(CIRCLE_SIZE + 4.0),
+ resolution: 128,
+ })),
+ MeshMaterial2d(materials.add(Color::linear_rgb(0.1, 0.1, 0.1))),
+ Transform::from_xyz(0.0, 0.0, -1.0),
+ ));
+
+ commands.spawn((
+ ClockMarker,
+ Mesh2d(meshes.add(Mesh::from(Circle::new(CIRCLE_SIZE / 8.0)))),
+ MeshMaterial2d(materials.add(Color::BLACK)),
+ Transform::from_xyz(0.0, 0.0, 1.0),
+ ));
+ });
+
+ commands.spawn(
+ Node {
+ position_type: PositionType::Absolute,
+ display: Display::Flex,
+ flex_direction: FlexDirection::Row,
+ top: Val::Px(24.0),
+ left: Val::Px(0.0),
+ ..Default::default()
+ },
+ ).with_children(|commands| {
+ commands.spawn((
+ Node {
+ display: Display::Flex,
+ flex_direction: FlexDirection::Column,
+ margin: UiRect {
+ left: Val::Px(12.0),
+ ..Default::default()
+ },
+ ..Default::default()
+ },
+ )).with_children(|commands| {
+ commands.spawn((
+ StatusText,
+ Text::new(""),
+ ));
+ commands.spawn((
+ Node {
+ border: UiRect::all(Val::Px(2.0)),
+ justify_content: JustifyContent::Center,
+ align_items: AlignItems::Center,
+ margin: UiRect {
+ top: Val::Px(4.0),
+ ..Default::default()
+ },
+ ..Default::default()
+ },
+ BorderColor(Color::BLACK),
+ BackgroundColor(NORMAL_BUTTON),
+ )).with_children(|commands| {
+ commands.spawn((
+ Button,
+ IndexButton::TickDecrement,
+ Node {
+ position_type: PositionType::Absolute,
+ left: Val::Px(0.0),
+ height: Val::Percent(100.0),
+ width: Val::Percent(50.0),
+ ..default()
+ },
+ )).with_children(|commands| {
+ commands.spawn((
+ Text::new("-"),
+ Node {
+ position_type: PositionType::Absolute,
+ left: Val::Px(12.0),
+ ..default()
+ }
+ ));
+ });
+ commands.spawn((
+ Button,
+ IndexButton::TickIncrement,
+ Node {
+ position_type: PositionType::Absolute,
+ right: Val::Px(0.0),
+ height: Val::Percent(100.0),
+ width: Val::Percent(50.0),
+ ..default()
+ },
+ )).with_children(|commands| {
+ commands.spawn((
+ Text::new("+"),
+ Node {
+ position_type: PositionType::Absolute,
+ right: Val::Px(12.0),
+ ..default()
+ }
+ ));
+ });
+ commands.spawn((
+ Index::Tick,
+ Text::new(""),
+ ));
+ });
+ commands.spawn((
+ Node {
+ border: UiRect::all(Val::Px(2.0)),
+ justify_content: JustifyContent::Center,
+ align_items: AlignItems::Center,
+ margin: UiRect {
+ top: Val::Px(2.0),
+ ..Default::default()
+ },
+ ..Default::default()
+ },
+ BorderColor(Color::BLACK),
+ BackgroundColor(NORMAL_BUTTON),
+ )).with_children(|commands| {
+ commands.spawn((
+ Button,
+ IndexButton::TapDecrement,
+ Node {
+ position_type: PositionType::Absolute,
+ left: Val::Px(0.0),
+ height: Val::Percent(100.0),
+ width: Val::Percent(50.0),
+ ..default()
+ },
+ )).with_children(|commands| {
+ commands.spawn((
+ Text::new("-"),
+ Node {
+ position_type: PositionType::Absolute,
+ left: Val::Px(12.0),
+ ..default()
+ }
+ ));
+ });
+ commands.spawn((
+ Button,
+ IndexButton::TapIncrement,
+ Node {
+ position_type: PositionType::Absolute,
+ right: Val::Px(0.0),
+ height: Val::Percent(100.0),
+ width: Val::Percent(50.0),
+ ..default()
+ },
+ )).with_children(|commands| {
+ commands.spawn((
+ Text::new("+"),
+ Node {
+ position_type: PositionType::Absolute,
+ right: Val::Px(12.0),
+ ..default()
+ }
+ ));
+ });
+ commands.spawn((
+ Index::Tap,
+ Text::new(""),
+ ));
+ });
+ }
+ );
+
+ #[cfg(not(target_os = "android"))]
+ commands.spawn(
+ (
+ Node {
+ margin: UiRect {
+ left: Val::Px(12.0),
+ ..default()
+ },
+ ..default()
+ },
+ Statistics,
+ Text::new("Statistics:"),
+ )
+ );
+
+ #[cfg(not(target_os = "android"))]
+ commands.spawn((
+ Text::new(
+ "up/down: BPM +-1\nleft/right: BPM +-10\n[/]: Division +-1\nn: Tap Mute\nm: Tick Mute\n,: Hide Clock",
+ ),
+ Node {
+ margin: UiRect {
+ left: Val::Px(12.0),
+ ..Default::default()
+ },
+ ..Default::default()
+ },
+ ));
+ });
+
+ // Bar chart
+
+ commands
+ .spawn((
+ BarChart,
+ Visibility::Visible,
+ Node {
+ width: Val::Percent(100.0),
+ height: Val::Percent(100.0),
+ justify_self: JustifySelf::Center,
+ justify_content: JustifyContent::Center,
+ align_items: AlignItems::Center,
+ ..default()
+ },
+ ))
+ .with_children(|commands| {
+ commands
+ .spawn(Node {
+ display: Display::Flex,
+ justify_self: JustifySelf::Center,
+ flex_direction: FlexDirection::Row,
+ width: Val::Percent(80.0),
+ height: Val::Percent(100.0),
+ ..Default::default()
+ })
+ .with_children(|commands| {
+ for (f, height, label) in [
+ (0.0, 4.0, "0"),
+ (1.0, 3.0, "1/60"),
+ (1.5, 2.0, "1.5/60"),
+ (2.0, 1.0, "2/60"),
+ ] {
+ commands.spawn((
+ Node {
+ position_type: PositionType::Absolute,
+ width: Val::Percent(100.0),
+ height: Val::Px(f / 60.0 * BAR_HEIGHT_MULTIPLIER + height / 2.0),
+ bottom: Val::Percent(50.0),
+ border: UiRect {
+ top: Val::Px(height),
+ ..default()
+ },
+ ..default()
+ },
+ BorderColor(Color::BLACK),
+ ));
+ commands.spawn((
+ Node {
+ position_type: PositionType::Absolute,
+ width: Val::Percent(100.0),
+ height: Val::Px(f / 60.0 * BAR_HEIGHT_MULTIPLIER + height / 2.0),
+ top: Val::Percent(50.0),
+ border: UiRect {
+ bottom: Val::Px(height),
+ ..default()
+ },
+ ..default()
+ },
+ BorderColor(Color::BLACK),
+ ));
+
+ commands
+ .spawn((
+ Node {
+ position_type: PositionType::Absolute,
+ left: Val::Px(-12.0),
+ height: Val::Px(f / 60.0 * BAR_HEIGHT_MULTIPLIER),
+ width: Val::Percent(100.0),
+ bottom: Val::Percent(50.0),
+ ..default()
+ },
+ // BackgroundColor(Color::linear_rgba(0.0, 1.0, 0.0, 0.3)),
+ ))
+ .with_children(|commands| {
+ commands.spawn((
+ Node {
+ position_type: PositionType::Absolute,
+ right: Val::Percent(100.0),
+ bottom: Val::Percent(100.0),
+ ..default()
+ },
+ Text::new(label),
+ TextFont {
+ font_size: 10.3,
+ ..Default::default()
+ },
+ ));
+ });
+ }
+
+ for i in 0..BINS {
+ commands
+ .spawn(Node {
+ margin: UiRect {
+ left: Val::Px(4.0),
+ right: Val::Px(4.0),
+ ..default()
+ },
+ flex_grow: 1.0,
+ flex_basis: Val::Px(0.0),
+ justify_content: JustifyContent::Center,
+ justify_self: JustifySelf::Center,
+ align_items: AlignItems::Center,
+ ..default()
+ })
+ .with_children(|commands| {
+ commands
+ .spawn((
+ Node {
+ width: Val::Percent(100.0),
+ height: Val::Percent(100.0),
+ justify_content: JustifyContent::Center,
+ justify_self: JustifySelf::Center,
+ align_items: AlignItems::Center,
+ ..default()
+ },
+ // BackgroundColor(Color::linear_rgba(0.0, 1.0, 0.0, 0.3)),
+ ))
+ .with_children(|commands| {
+ commands.spawn((
+ BinBar,
+ BinIndex(i),
+ Visibility::Inherited,
+ Node {
+ position_type: PositionType::Absolute,
+ width: Val::Percent(100.0),
+ height: Val::Px(100.0),
+ top: Val::Percent(50.0),
+ bottom: Val::DEFAULT,
+ justify_content: JustifyContent::Center,
+ align_items: AlignItems::Center,
+ ..default()
+ },
+ BackgroundColor(Color::linear_rgb(0.0, 0.0, 1.0)),
+ ));
+ commands.spawn((
+ BinIndex(i),
+ Text::new("1.23"),
+ TextFont {
+ font_size: 10.3,
+ ..Default::default()
+ },
+ ));
+ });
+ });
+ }
+ });
+ });
+
+ #[cfg(not(target_os = "android"))]
+ commands.spawn((
+ DiagnosticsText,
+ Text::new(""),
+ Node {
+ position_type: PositionType::Absolute,
+ top: Val::Px(0.0),
+ right: Val::Px(0.0),
+ ..default()
+ },
+ ));
+
+ // UI Buttons
+
+ let mut node = commands.spawn(Node {
+ position_type: PositionType::Absolute,
+ width: Val::Percent(100.),
+ bottom: Val::Px(4.0),
+ display: Display::Flex,
+ flex_direction: FlexDirection::Row,
+ flex_wrap: FlexWrap::Wrap,
+ ..default()
+ });
+
+ node.with_children(|parent| {
+ for button_kind in &[
+ ButtonKind::BpmDown10,
+ ButtonKind::BpmDown1,
+ ButtonKind::BpmUp1,
+ ButtonKind::BpmUp10,
+ ButtonKind::DivisionDown1,
+ ButtonKind::DivisionUp1,
+ ButtonKind::TapMute,
+ ButtonKind::TickMute,
+ ButtonKind::HideBarChart,
+ ButtonKind::HideClock,
+ ] {
+ let kind = *button_kind;
+ parent
+ .spawn((
+ Button,
+ kind,
+ Node {
+ // width: Val::Px(105.0),
+ // height: Val::Px(48.0),
+ border: UiRect::all(Val::Px(2.0)),
+ // horizontally center child text
+ justify_content: JustifyContent::Center,
+ // vertically center child text
+ align_items: AlignItems::Center,
+ margin: UiRect {
+ left: Val::Px(2.0),
+ right: Val::Px(2.0),
+ ..Default::default()
+ },
+ flex_grow: 1.0,
+ ..default()
+ },
+ BorderColor(Color::BLACK),
+ BorderRadius::all(Val::Px(4.0)),
+ BackgroundColor(NORMAL_BUTTON),
+ ))
+ .with_child((
+ Text::new(kind.label()),
+ TextColor(Color::srgb(0.9, 0.9, 0.9)),
+ ));
+ }
+ });
+}
+
+#[allow(clippy::too_many_arguments)]
+fn tap(
+ mut commands: Commands,
+ audio_handles: Res,
+ keyboard_input: Res>,
+ game_pad: Query<&Gamepad>,
+ buttons: Res>,
+ touches: Res,
+ last_tick: Res,
+ timer: Res