diff --git a/.gitignore b/.gitignore index dc84dae..386c146 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ /target wasabi_config.toml .direnv -.vscode \ No newline at end of file +.vscode diff --git a/Cargo.lock b/Cargo.lock index 204aff2..7708fb6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,6 +18,16 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" +[[package]] +name = "accesskit" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef7442f1f520649b8e11ee3af6caeec24123fed4b63bc36a85b67308d8514fdf" +dependencies = [ + "enumn", + "serde", +] + [[package]] name = "adler" version = "1.0.2" @@ -33,6 +43,7 @@ dependencies = [ "cfg-if", "getrandom", "once_cell", + "serde", "version_check", "zerocopy", ] @@ -46,6 +57,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "aligned-vec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" + [[package]] name = "alsa" version = "0.9.0" @@ -86,58 +103,37 @@ dependencies = [ ] [[package]] -name = "android-properties" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" - -[[package]] -name = "anstream" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" - -[[package]] -name = "anstyle-parse" -version = "0.2.3" +name = "android-activity" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ - "utf8parse", + "android-properties", + "bitflags 2.5.0", + "cc", + "cesu8", + "jni", + "jni-sys", + "libc", + "log", + "ndk 0.9.0", + "ndk-context", + "ndk-sys 0.6.0+11769913", + "num_enum 0.7.3", + "thiserror", ] [[package]] -name = "anstyle-query" -version = "1.0.2" +name = "android-properties" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" -dependencies = [ - "windows-sys 0.52.0", -] +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" [[package]] -name = "anstyle-wincon" -version = "3.0.2" +name = "anyhow" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" -dependencies = [ - "anstyle", - "windows-sys 0.52.0", -] +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "approx" @@ -148,6 +144,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" + [[package]] name = "arboard" version = "3.3.2" @@ -164,6 +166,17 @@ dependencies = [ "x11rb", ] +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "arrayref" version = "0.3.7" @@ -176,6 +189,12 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" + [[package]] name = "ash" version = "0.37.3+1.3.251" @@ -187,139 +206,101 @@ dependencies = [ [[package]] name = "ashpd" -version = "0.6.8" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac22eda5891cc086690cb6fa10121c0390de0e3b04eb269f2d766b00d3f2d81" +checksum = "bfe7e0dd0ac5a401dc116ed9f9119cf9decc625600474cb41f0fc0a0050abc9a" dependencies = [ - "async-fs 2.1.1", + "async-fs", "async-net", "enumflags2", "futures-channel", "futures-util", - "once_cell", "rand", + "raw-window-handle 0.6.2", "serde", "serde_repr", "url", + "wayland-backend", + "wayland-client 0.31.6", + "wayland-protocols 0.32.4", "zbus", ] [[package]] name = "async-broadcast" -version = "0.5.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" +checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" dependencies = [ - "event-listener 2.5.3", + "event-listener", + "event-listener-strategy", "futures-core", + "pin-project-lite", ] [[package]] name = "async-channel" -version = "2.2.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" dependencies = [ "concurrent-queue", - "event-listener 5.2.0", - "event-listener-strategy 0.5.1", + "event-listener-strategy", "futures-core", "pin-project-lite", ] [[package]] name = "async-executor" -version = "1.9.1" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10b3e585719c2358d2660232671ca8ca4ddb4be4ce8a1842d6c2dc8685303316" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" dependencies = [ - "async-lock 3.3.0", "async-task", "concurrent-queue", - "fastrand 2.0.2", - "futures-lite 2.3.0", + "fastrand", + "futures-lite", "slab", ] [[package]] name = "async-fs" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" -dependencies = [ - "async-lock 2.8.0", - "autocfg", - "blocking", - "futures-lite 1.13.0", -] - -[[package]] -name = "async-fs" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc19683171f287921f2405677dd2ed2549c3b3bda697a563ebc3a121ace2aba1" +checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" dependencies = [ - "async-lock 3.3.0", + "async-lock", "blocking", - "futures-lite 2.3.0", -] - -[[package]] -name = "async-io" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" -dependencies = [ - "async-lock 2.8.0", - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-lite 1.13.0", - "log", - "parking", - "polling 2.8.0", - "rustix 0.37.27", - "slab", - "socket2", - "waker-fn", + "futures-lite", ] [[package]] name = "async-io" -version = "2.3.2" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" +checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" dependencies = [ - "async-lock 3.3.0", + "async-lock", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.3.0", + "futures-lite", "parking", - "polling 3.6.0", - "rustix 0.38.32", + "polling", + "rustix", "slab", "tracing", - "windows-sys 0.52.0", -] - -[[package]] -name = "async-lock" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" -dependencies = [ - "event-listener 2.5.3", + "windows-sys 0.59.0", ] [[package]] name = "async-lock" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener 4.0.3", - "event-listener-strategy 0.4.0", + "event-listener", + "event-listener-strategy", "pin-project-lite", ] @@ -329,33 +310,35 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" dependencies = [ - "async-io 2.3.2", + "async-io", "blocking", - "futures-lite 2.3.0", + "futures-lite", ] [[package]] name = "async-process" -version = "1.8.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" +checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" dependencies = [ - "async-io 1.13.0", - "async-lock 2.8.0", + "async-channel", + "async-io", + "async-lock", "async-signal", + "async-task", "blocking", "cfg-if", - "event-listener 3.1.0", - "futures-lite 1.13.0", - "rustix 0.38.32", - "windows-sys 0.48.0", + "event-listener", + "futures-lite", + "rustix", + "tracing", ] [[package]] name = "async-recursion" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", @@ -364,33 +347,33 @@ dependencies = [ [[package]] name = "async-signal" -version = "0.2.5" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" dependencies = [ - "async-io 2.3.2", - "async-lock 2.8.0", + "async-io", + "async-lock", "atomic-waker", "cfg-if", "futures-core", "futures-io", - "rustix 0.38.32", + "rustix", "signal-hook-registry", "slab", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] name = "async-task" -version = "4.7.0" +version = "4.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.79" +version = "0.1.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" +checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ "proc-macro2", "quote", @@ -405,9 +388,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "atomic_float" -version = "0.1.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62af46d040ba9df09edc6528dae9d8e49f5f3e82f55b7d2ec31a733c38dbc49d" +checksum = "628d228f918ac3b82fe590352cc719d30664a0c13ca3a60266fe02c7132d480a" [[package]] name = "atomic_refcell" @@ -432,6 +415,29 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +[[package]] +name = "av1-grain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876c75a42f6364451a033496a14c44bffe41f5f4a8236f697391f11024e596d2" +dependencies = [ + "arrayvec", +] + [[package]] name = "base64" version = "0.21.7" @@ -453,7 +459,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "shlex", "syn 2.0.58", ] @@ -485,6 +491,12 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +[[package]] +name = "bitstream-io" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b81e1519b0d82120d2fd469d5bfb2919a9361c48b02d82d04befc1cdd2002452" + [[package]] name = "block" version = "0.1.6" @@ -506,7 +518,7 @@ version = "0.1.0-beta.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa55741ee90902547802152aaf3f8e5248aab7e21468089560d4c8840561146" dependencies = [ - "objc-sys", + "objc-sys 0.2.0-beta.2", ] [[package]] @@ -516,36 +528,54 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8dd9e63c1744f755c2f60332b88de39d341e5e86239014ad839bd71c106dec42" dependencies = [ "block-sys", - "objc2-encode", + "objc2-encode 2.0.0-pre.2", +] + +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2 0.5.2", ] [[package]] name = "blocking" -version = "1.5.1" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" dependencies = [ "async-channel", - "async-lock 3.3.0", "async-task", - "fastrand 2.0.2", "futures-io", - "futures-lite 2.3.0", + "futures-lite", "piper", - "tracing", ] +[[package]] +name = "built" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "236e6289eda5a812bc6b53c3b024039382a2895fbbeef2d748b2931546d392c4" + [[package]] name = "bumpalo" version = "3.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" +[[package]] +name = "by_address" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" + [[package]] name = "bytemuck" -version = "1.16.3" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "102087e286b4677862ea56cf8fc58bb2cdfa8725c40ffb80fe3a008eb7f2fc83" +checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" dependencies = [ "bytemuck_derive", ] @@ -567,6 +597,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "1.6.0" @@ -587,14 +623,40 @@ dependencies = [ "vec_map", ] +[[package]] +name = "calloop" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" +dependencies = [ + "bitflags 2.5.0", + "log", + "polling", + "rustix", + "slab", + "thiserror", +] + +[[package]] +name = "calloop-wayland-source" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" +dependencies = [ + "calloop 0.13.0", + "rustix", + "wayland-backend", + "wayland-client 0.31.6", +] + [[package]] name = "cbindgen" version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da6bc11b07529f16944307272d5bd9b22530bc7d05751717c9d416586cedab49" dependencies = [ - "clap 3.2.25", - "heck", + "clap", + "heck 0.4.1", "indexmap 1.9.3", "log", "proc-macro2", @@ -631,6 +693,16 @@ dependencies = [ "nom", ] +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -643,6 +715,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "clang-sys" version = "1.7.0" @@ -662,34 +740,13 @@ checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "atty", "bitflags 1.3.2", - "clap_lex 0.2.4", + "clap_lex", "indexmap 1.9.3", - "strsim 0.10.0", + "strsim", "termcolor", "textwrap", ] -[[package]] -name = "clap" -version = "4.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" -dependencies = [ - "clap_builder", -] - -[[package]] -name = "clap_builder" -version = "4.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" -dependencies = [ - "anstream", - "anstyle", - "clap_lex 0.7.0", - "strsim 0.11.1", -] - [[package]] name = "clap_lex" version = "0.2.4" @@ -699,12 +756,6 @@ dependencies = [ "os_str_bytes", ] -[[package]] -name = "clap_lex" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" - [[package]] name = "clipboard-win" version = "5.3.0" @@ -729,12 +780,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - [[package]] name = "colors-transform" version = "0.2.11" @@ -753,25 +798,13 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ "crossbeam-utils", ] -[[package]] -name = "confy" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e37668cb35145dcfaa1931a5f37fde375eeae8068b4c0d2f289da28a270b2d2c" -dependencies = [ - "directories 4.0.1", - "serde", - "thiserror", - "toml 0.5.11", -] - [[package]] name = "convert_case" version = "0.4.0" @@ -803,7 +836,20 @@ dependencies = [ "bitflags 1.3.2", "core-foundation", "core-graphics-types", - "foreign-types", + "foreign-types 0.3.2", + "libc", +] + +[[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 0.5.0", "libc", ] @@ -838,6 +884,27 @@ dependencies = [ "bindgen", ] +[[package]] +name = "coremidi" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "964eb3e10ea8b0d29c797086aab3ca730f75e06dced0cb980642fd274a5cca30" +dependencies = [ + "block", + "core-foundation", + "core-foundation-sys", + "coremidi-sys", +] + +[[package]] +name = "coremidi-sys" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709d142e542467e028d5dc5f0374392339ab7dead0c48c129504de2ccd667e1b" +dependencies = [ + "core-foundation-sys", +] + [[package]] name = "cpal" version = "0.15.3" @@ -858,7 +925,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows", + "windows 0.54.0", ] [[package]] @@ -938,6 +1005,12 @@ dependencies = [ "typenum", ] +[[package]] +name = "cursor-icon" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" + [[package]] name = "dasp_sample" version = "0.11.0" @@ -951,13 +1024,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5" [[package]] -name = "deranged" -version = "0.3.11" +name = "data-url" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", -] +checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" [[package]] name = "derivative" @@ -980,38 +1050,18 @@ dependencies = [ "crypto-common", ] -[[package]] -name = "directories" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" -dependencies = [ - "dirs-sys 0.3.7", -] - [[package]] name = "directories" version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" dependencies = [ - "dirs-sys 0.4.1", + "dirs-sys", ] [[package]] name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ @@ -1042,53 +1092,82 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "dpi" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" + [[package]] name = "ecolor" -version = "0.21.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f99fe3cac305af9d6d92971af60d0f7ea4d783201ef1673571567b6699964d9" +checksum = "5629649a8ae57c73f175f4a96419905a8102cfbfcbce96ea25a826bbf468e990" +dependencies = [ + "bytemuck", + "emath", + "serde", +] [[package]] name = "egui" -version = "0.21.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6412a21e0bde7c0918f7fb44bbbb86b5e1f88e63c026a4e747cc7af02f76dfbe" +checksum = "26bab3b3572566257a497b5f87d2cccaf7f7f122d4b8b620cba0493becc7955e" dependencies = [ + "accesskit", "ahash", + "emath", "epaint", + "log", "nohash-hasher", - "tracing", + "serde", ] [[package]] name = "egui-winit" -version = "0.21.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab43597ba41f0ce39a364ad83185594578bfd8b3409b99dbcbb01df23afc3dbb" +checksum = "642c749bf221b5a3ecae3144c98b837729d87b9fde6c39a6ad00f07b71dbee94" dependencies = [ - "android-activity", + "ahash", "arboard", "egui", - "instant", + "log", + "raw-window-handle 0.6.2", "smithay-clipboard", - "tracing", + "web-time", "webbrowser", - "winit", + "winit 0.30.5", ] [[package]] -name = "egui_winit_vulkano" -version = "0.24.0" +name = "egui_extras" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "203c1ad521328c178e24d16f5b26ca151a5b1669adafa2c6aa9eb3f904cdfd52" +checksum = "d9f1beb57a3c942fac2f058655188c79ac1cd200555e4f3684cd0c965ceb3a67" +dependencies = [ + "ahash", + "egui", + "enum-map", + "log", + "mime_guess2", + "resvg 0.37.0", +] + +[[package]] +name = "egui_winit_vulkano" +version = "0.27.0" +source = "git+https://github.com/MyBlackMIDIScore/egui_winit_vulkano.git?rev=aa24f97#aa24f979edad909749dc74e78fee5252c650979c" dependencies = [ "ahash", "egui", "egui-winit", "image", + "raw-window-handle 0.6.2", "vulkano", "vulkano-shaders", - "winit", + "winit 0.30.5", ] [[package]] @@ -1099,9 +1178,13 @@ checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "emath" -version = "0.21.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ecd80612937e0267909d5351770fe150004e24dab93954f69ca62eecd3f77e" +checksum = "af86c4efae11da2a3dcbb4afebd0e9ed1916345e8d187b4051d443c8bd79af93" +dependencies = [ + "bytemuck", + "serde", +] [[package]] name = "encoding_rs" @@ -1121,6 +1204,33 @@ dependencies = [ "encoding_rs", ] +[[package]] +name = "endi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + +[[package]] +name = "enum-map" +version = "2.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9" +dependencies = [ + "enum-map-derive", + "serde", +] + +[[package]] +name = "enum-map-derive" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "enum_dispatch" version = "0.3.13" @@ -1135,9 +1245,9 @@ dependencies = [ [[package]] name = "enumflags2" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3278c9d5fb675e0a51dabcf4c0d355f692b064171535ba72361be1528a9d8e8d" +checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d" dependencies = [ "enumflags2_derive", "serde", @@ -1145,9 +1255,20 @@ dependencies = [ [[package]] name = "enumflags2_derive" -version = "0.7.9" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "enumn" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" +checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", @@ -1156,19 +1277,28 @@ dependencies = [ [[package]] name = "epaint" -version = "0.21.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12e78b5c58a1f7f621f9d546add2adce20636422c9b251e29f749e8a2f713c95" +checksum = "445e11ec86a4d85e1350578ba20b2d89977ed937f3faab32e1c3ec81d20c1842" dependencies = [ "ab_glyph", "ahash", - "atomic_refcell", + "bytemuck", "ecolor", "emath", + "epaint_default_fonts", + "log", "nohash-hasher", "parking_lot", + "serde", ] +[[package]] +name = "epaint_default_fonts" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5202b64bef2b2c42a7f6e2e5b40fa83dd04aa61fdb08bfd116553adc149fe47a" + [[package]] name = "equivalent" version = "1.0.1" @@ -1193,60 +1323,22 @@ checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b" [[package]] name = "event-listener" -version = "2.5.3" +version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "event-listener" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" dependencies = [ "concurrent-queue", "parking", "pin-project-lite", ] -[[package]] -name = "event-listener" -version = "4.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener" -version = "5.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b5fb89194fa3cad959b833185b3063ba881dbfc7030680b314250779fb4cc91" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" -dependencies = [ - "event-listener 4.0.3", - "pin-project-lite", -] - [[package]] name = "event-listener-strategy" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "332f51cb23d20b0de8458b86580878211da09bcd4503cb579c225b3d124cabb3" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" dependencies = [ - "event-listener 5.2.0", + "event-listener", "pin-project-lite", ] @@ -1278,15 +1370,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - [[package]] name = "fastrand" version = "2.0.2" @@ -1333,7 +1416,28 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "foreign-types-shared", + "foreign-types-shared 0.1.1", +] + +[[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 0.3.1", +] + +[[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.58", ] [[package]] @@ -1342,6 +1446,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1372,28 +1482,13 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - [[package]] name = "futures-lite" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" dependencies = [ - "fastrand 2.0.2", + "fastrand", "futures-core", "futures-io", "parking", @@ -1521,6 +1616,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -1532,9 +1633,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.9" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" [[package]] name = "hex" @@ -1554,7 +1655,8 @@ dependencies = [ [[package]] name = "ico" version = "0.3.0" -source = "git+https://github.com/StratusFearMe21/rust-ico?branch=patch-1#aa5924babb52ee5559cdb3a376d0c060a478c9f1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3804960be0bb5e4edb1e1ad67afd321a9ecfd875c3e65c099468fd2717d7cae" dependencies = [ "byteorder", "png", @@ -1572,20 +1674,35 @@ dependencies = [ [[package]] name = "image" -version = "0.24.9" +version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10" dependencies = [ "bytemuck", - "byteorder", + "byteorder-lite", "color_quant", "exr", "gif", - "jpeg-decoder", + "image-webp", "num-traits", "png", "qoi", + "ravif", + "rayon", + "rgb", "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79afb8cbee2ef20f59ccd477a218c12a93943d075b492015ecb1bb81f8ee904" +dependencies = [ + "byteorder-lite", + "quick-error", ] [[package]] @@ -1594,6 +1711,18 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b72ad49b554c1728b1e83254a1b1565aea4161e28dabbfa171fc15fe62299caf" +[[package]] +name = "imagesize" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" + +[[package]] +name = "imgref" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126" + [[package]] name = "indexmap" version = "1.9.3" @@ -1606,9 +1735,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -1616,9 +1745,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", "js-sys", @@ -1627,14 +1756,33 @@ dependencies = [ ] [[package]] -name = "io-lifetimes" -version = "1.0.11" +name = "interpolate_name" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ - "hermit-abi 0.3.9", - "libc", - "windows-sys 0.48.0", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", ] [[package]] @@ -1688,9 +1836,6 @@ name = "jpeg-decoder" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" -dependencies = [ - "rayon", -] [[package]] name = "js-sys" @@ -1766,6 +1911,17 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "libfuzzer-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] + [[package]] name = "libloading" version = "0.7.4" @@ -1813,12 +1969,6 @@ dependencies = [ "libc", ] -[[package]] -name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -1841,6 +1991,15 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + [[package]] name = "mach2" version = "0.4.2" @@ -1859,6 +2018,16 @@ dependencies = [ "libc", ] +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + [[package]] name = "memchr" version = "2.7.2" @@ -1875,19 +2044,19 @@ dependencies = [ ] [[package]] -name = "memoffset" -version = "0.6.5" +name = "memmap2" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" dependencies = [ - "autocfg", + "libc", ] [[package]] name = "memoffset" -version = "0.7.1" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ "autocfg", ] @@ -1928,6 +2097,39 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "midir" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe36f39751eb1f449490d4a236e8642c8efee1a87fa81785b063289b689dc84e" +dependencies = [ + "alsa", + "bitflags 1.3.2", + "coremidi", + "js-sys", + "libc", + "parking_lot", + "wasm-bindgen", + "web-sys", + "windows 0.56.0", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess2" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a3333bb1609500601edc766a39b4c1772874a4ce26022f4d866854dc020c41" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1966,7 +2168,7 @@ dependencies = [ "jni-sys", "ndk-sys 0.4.1+23.1.7779620", "num_enum 0.5.11", - "raw-window-handle", + "raw-window-handle 0.5.2", "thiserror", ] @@ -1980,7 +2182,22 @@ dependencies = [ "jni-sys", "log", "ndk-sys 0.5.0+25.2.9519653", - "num_enum 0.7.2", + "num_enum 0.7.3", + "thiserror", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.5.0", + "jni-sys", + "log", + "ndk-sys 0.6.0+11769913", + "num_enum 0.7.3", + "raw-window-handle 0.6.2", "thiserror", ] @@ -2008,6 +2225,21 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "nix" version = "0.24.3" @@ -2035,14 +2267,15 @@ dependencies = [ [[package]] name = "nix" -version = "0.26.4" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", "cfg-if", + "cfg_aliases 0.1.1", "libc", - "memoffset 0.7.1", + "memoffset 0.9.1", ] [[package]] @@ -2062,19 +2295,29 @@ dependencies = [ ] [[package]] -name = "num-complex" +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + +[[package]] +name = "num-bigint" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ + "num-integer", "num-traits", ] [[package]] -name = "num-conv" -version = "0.1.0" +name = "num-complex" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] [[package]] name = "num-derive" @@ -2096,6 +2339,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.18" @@ -2125,11 +2379,11 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" dependencies = [ - "num_enum_derive 0.7.2", + "num_enum_derive 0.7.3", ] [[package]] @@ -2158,9 +2412,9 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", @@ -2169,49 +2423,252 @@ dependencies = [ ] [[package]] -name = "objc" -version = "0.2.7" +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + +[[package]] +name = "objc-sys" +version = "0.2.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7" + +[[package]] +name = "objc-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.3.0-beta.3.patch-leaks.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e01640f9f2cb1220bbe80325e179e532cb3379ebcd1bf2279d703c19fe3a468" +dependencies = [ + "block2 0.2.0-alpha.6", + "objc-sys 0.2.0-beta.2", + "objc2-encode 2.0.0-pre.2", +] + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +dependencies = [ + "objc-sys 0.3.5", + "objc2-encode 4.0.3", +] + +[[package]] +name = "objc2-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.5.0", + "block2 0.5.1", + "libc", + "objc2 0.5.2", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation", + "objc2-quartz-core", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" +dependencies = [ + "bitflags 2.5.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "objc2-contacts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" +dependencies = [ + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.5.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-core-location" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" +dependencies = [ + "block2 0.5.1", + "objc2 0.5.2", + "objc2-contacts", + "objc2-foundation", +] + +[[package]] +name = "objc2-encode" +version = "2.0.0-pre.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512" +dependencies = [ + "objc-sys 0.2.0-beta.2", +] + +[[package]] +name = "objc2-encode" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.5.0", + "block2 0.5.1", + "dispatch", + "libc", + "objc2 0.5.2", +] + +[[package]] +name = "objc2-link-presentation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" +dependencies = [ + "block2 0.5.1", + "objc2 0.5.2", + "objc2-app-kit", + "objc2-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.5.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "malloc_buf", + "bitflags 2.5.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation", + "objc2-metal", ] [[package]] -name = "objc-foundation" -version = "0.1.1" +name = "objc2-symbols" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" dependencies = [ - "block", - "objc", - "objc_id", + "objc2 0.5.2", + "objc2-foundation", ] [[package]] -name = "objc-sys" -version = "0.2.0-beta.2" +name = "objc2-ui-kit" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7" - -[[package]] -name = "objc2" -version = "0.3.0-beta.3.patch-leaks.3" +checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" +dependencies = [ + "bitflags 2.5.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation", + "objc2-link-presentation", + "objc2-quartz-core", + "objc2-symbols", + "objc2-uniform-type-identifiers", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-uniform-type-identifiers" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e01640f9f2cb1220bbe80325e179e532cb3379ebcd1bf2279d703c19fe3a468" +checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" dependencies = [ - "block2", - "objc-sys", - "objc2-encode", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation", ] [[package]] -name = "objc2-encode" -version = "2.0.0-pre.2" +name = "objc2-user-notifications" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512" +checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "objc-sys", + "bitflags 2.5.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-core-location", + "objc2-foundation", ] [[package]] @@ -2252,6 +2709,17 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "open" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a877bf6abd716642a53ef1b89fb498923a4afca5c754f9050b4d081c05c4b3" +dependencies = [ + "is-wsl", + "libc", + "pathdiff", +] + [[package]] name = "option-ext" version = "0.2.0" @@ -2294,9 +2762,9 @@ dependencies = [ [[package]] name = "palette" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfc23a4b76642983d57e4ad00bb4504eb30a8ce3c70f4aee1f725610e36d97a" +checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6" dependencies = [ "approx", "fast-srgb8", @@ -2306,10 +2774,11 @@ dependencies = [ [[package]] name = "palette_derive" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8890702dbec0bad9116041ae586f84805b13eecd1d8b1df27c29998a9969d6d" +checksum = "f5030daf005bface118c096f510ffb781fc28f9ab6a32ab224d8631be6851d30" dependencies = [ + "by_address", "proc-macro2", "quote", "syn 2.0.58", @@ -2317,9 +2786,9 @@ dependencies = [ [[package]] name = "parking" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" @@ -2350,6 +2819,12 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + [[package]] name = "percent-encoding" version = "2.3.1" @@ -2404,6 +2879,26 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "pin-project-lite" version = "0.2.14" @@ -2418,12 +2913,12 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "piper" -version = "0.2.1" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" dependencies = [ "atomic-waker", - "fastrand 2.0.2", + "fastrand", "futures-io", ] @@ -2448,33 +2943,17 @@ dependencies = [ [[package]] name = "polling" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "concurrent-queue", - "libc", - "log", - "pin-project-lite", - "windows-sys 0.48.0", -] - -[[package]] -name = "polling" -version = "3.6.0" +version = "3.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c976a60b2d7e99d6f229e414670a9b85d13ac305cc6d1e9c134de58c5aaaf6" +checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi 0.3.9", + "hermit-abi 0.4.0", "pin-project-lite", - "rustix 0.38.32", + "rustix", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2483,12 +2962,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "ppv-lite86" version = "0.2.17" @@ -2556,6 +3029,25 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "profiling" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd" +dependencies = [ + "quote", + "syn 2.0.58", +] + [[package]] name = "qoi" version = "0.4.1" @@ -2565,6 +3057,21 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quick-xml" +version = "0.36.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.35" @@ -2604,12 +3111,68 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rav1e" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +dependencies = [ + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "once_cell", + "paste", + "profiling", + "rand", + "rand_chacha", + "simd_helpers", + "system-deps", + "thiserror", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67376f469e7e7840d0040bbf4b9b3334005bb167f814621326e4c7ab8cd6e944" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + [[package]] name = "raw-window-handle" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + [[package]] name = "rayon" version = "1.10.0" @@ -2734,29 +3297,40 @@ dependencies = [ "log", "pico-args", "rgb", - "svgtypes", - "tiny-skia", - "usvg", + "svgtypes 0.11.0", + "tiny-skia 0.8.4", + "usvg 0.31.0", +] + +[[package]] +name = "resvg" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cadccb3d99a9efb8e5e00c16fbb732cbe400db2ec7fc004697ee7d97d86cf1f4" +dependencies = [ + "log", + "pico-args", + "rgb", + "svgtypes 0.13.0", + "tiny-skia 0.11.4", + "usvg 0.37.0", ] [[package]] name = "rfd" -version = "0.12.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c9e7b57df6e8472152674607f6cc68aa14a748a3157a857a94f516e11aeacc2" +checksum = "8af382a047821a08aa6bfc09ab0d80ff48d45d8726f7cd8e44891f7cb4a4278e" dependencies = [ "ashpd", - "async-io 1.13.0", - "block", - "dispatch", - "futures-util", + "block2 0.5.1", "js-sys", "log", - "objc", - "objc-foundation", - "objc_id", + "objc2 0.5.2", + "objc2-app-kit", + "objc2-foundation", "pollster", - "raw-window-handle", + "raw-window-handle 0.6.2", "urlencoding", "wasm-bindgen", "wasm-bindgen-futures", @@ -2789,7 +3363,7 @@ dependencies = [ "roxmltree 0.18.1", "simplecss", "siphasher", - "svgtypes", + "svgtypes 0.11.0", ] [[package]] @@ -2810,6 +3384,12 @@ dependencies = [ "xmlparser", ] +[[package]] +name = "roxmltree" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f" + [[package]] name = "rubato" version = "0.15.0" @@ -2828,6 +3408,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + [[package]] name = "rustfft" version = "6.2.0" @@ -2843,20 +3429,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "rustix" -version = "0.37.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", -] - [[package]] name = "rustix" version = "0.38.32" @@ -2866,7 +3438,7 @@ dependencies = [ "bitflags 2.5.0", "errno", "libc", - "linux-raw-sys 0.4.13", + "linux-raw-sys", "windows-sys 0.52.0", ] @@ -2905,25 +3477,38 @@ checksum = "cda4e97be1fd174ccc2aae81c8b694e803fa99b34e8fd0f057a9d70698e3ed09" dependencies = [ "ab_glyph", "log", - "memmap2", - "smithay-client-toolkit", - "tiny-skia", + "memmap2 0.5.10", + "smithay-client-toolkit 0.16.1", + "tiny-skia 0.8.4", +] + +[[package]] +name = "sctk-adwaita" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec" +dependencies = [ + "ab_glyph", + "log", + "memmap2 0.9.5", + "smithay-client-toolkit 0.19.2", + "tiny-skia 0.11.4", ] [[package]] name = "serde" -version = "1.0.197" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -2943,9 +3528,9 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", @@ -2954,9 +3539,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" dependencies = [ "serde", ] @@ -3001,9 +3586,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -3014,6 +3599,15 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + [[package]] name = "simdeez" version = "2.0.0-dev3" @@ -3070,36 +3664,61 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "870427e30b8f2cbe64bf43ec4b86e88fe39b0a84b3f15efd9c9c2d020bc86eb9" dependencies = [ "bitflags 1.3.2", - "calloop", + "calloop 0.10.6", "dlib", "lazy_static", "log", - "memmap2", + "memmap2 0.5.10", "nix 0.24.3", "pkg-config", - "wayland-client", - "wayland-cursor", - "wayland-protocols", + "wayland-client 0.29.5", + "wayland-cursor 0.29.5", + "wayland-protocols 0.29.5", ] [[package]] -name = "smithay-clipboard" -version = "0.6.6" +name = "smithay-client-toolkit" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a345c870a1fae0b1b779085e81b51e614767c239e93503588e54c5b17f4b0e8" +checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "smithay-client-toolkit", - "wayland-client", + "bitflags 2.5.0", + "calloop 0.13.0", + "calloop-wayland-source", + "cursor-icon", + "libc", + "log", + "memmap2 0.9.5", + "rustix", + "thiserror", + "wayland-backend", + "wayland-client 0.31.6", + "wayland-csd-frame", + "wayland-cursor 0.31.6", + "wayland-protocols 0.32.4", + "wayland-protocols-wlr", + "wayland-scanner 0.31.5", + "xkeysym", ] [[package]] -name = "socket2" -version = "0.4.10" +name = "smithay-clipboard" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +checksum = "cc8216eec463674a0e90f29e0ae41a4db573ec5b56b1c6c1c71615d249b6d846" dependencies = [ "libc", - "winapi", + "smithay-client-toolkit 0.19.2", + "wayland-backend", +] + +[[package]] +name = "smol_str" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" +dependencies = [ + "serde", ] [[package]] @@ -3157,16 +3776,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] -name = "strsim" -version = "0.11.1" +name = "svgtypes" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +checksum = "ed4b0611e7f3277f68c0fa18e385d9e2d26923691379690039548f867cef02a7" +dependencies = [ + "kurbo", + "siphasher", +] [[package]] name = "svgtypes" -version = "0.11.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed4b0611e7f3277f68c0fa18e385d9e2d26923691379690039548f867cef02a7" +checksum = "6e44e288cd960318917cbd540340968b90becc8bc81f171345d706e7a89d9d70" dependencies = [ "kurbo", "siphasher", @@ -3327,6 +3950,25 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml 0.8.19", + "version-compare", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + [[package]] name = "tempfile" version = "3.10.1" @@ -3334,8 +3976,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", - "fastrand 2.0.2", - "rustix 0.38.32", + "fastrand", + "rustix", "windows-sys 0.52.0", ] @@ -3396,36 +4038,32 @@ dependencies = [ ] [[package]] -name = "time" -version = "0.3.36" +name = "tiny-skia" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "df8493a203431061e901613751931f047d1971337153f96d0e5e363d6dbf6a67" dependencies = [ - "deranged", - "num-conv", - "powerfmt", - "serde", - "time-core", + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "png", + "tiny-skia-path 0.8.4", ] -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - [[package]] name = "tiny-skia" -version = "0.8.4" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8493a203431061e901613751931f047d1971337153f96d0e5e363d6dbf6a67" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" dependencies = [ "arrayref", "arrayvec", "bytemuck", "cfg-if", + "log", "png", - "tiny-skia-path", + "tiny-skia-path 0.11.4", ] [[package]] @@ -3439,6 +4077,17 @@ dependencies = [ "strict-num", ] +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -3471,21 +4120,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.12" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.9", + "toml_edit 0.22.21", ] [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] @@ -3496,7 +4145,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.5.0", "toml_datetime", "winnow 0.5.40", ] @@ -3507,22 +4156,22 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.5.0", "toml_datetime", "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.22.9" +version = "0.22.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" +checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.5.0", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.5", + "winnow 0.6.18", ] [[package]] @@ -3582,11 +4231,20 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" name = "uds_windows" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset 0.9.1", + "tempfile", + "winapi", +] + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" dependencies = [ - "memoffset 0.9.1", - "tempfile", - "winapi", + "version_check", ] [[package]] @@ -3610,6 +4268,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "url" version = "2.5.0" @@ -3637,8 +4301,22 @@ dependencies = [ "base64", "log", "pico-args", - "usvg-parser", - "usvg-tree", + "usvg-parser 0.31.0", + "usvg-tree 0.31.0", + "xmlwriter", +] + +[[package]] +name = "usvg" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b0a51b72ab80ca511d126b77feeeb4fb1e972764653e61feac30adc161a756" +dependencies = [ + "base64", + "log", + "pico-args", + "usvg-parser 0.37.0", + "usvg-tree 0.37.0", "xmlwriter", ] @@ -3648,15 +4326,33 @@ version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2352a2c05655a7e4d3dca76cf65764efce35527472668bae5c6fc876b4c996d" dependencies = [ - "data-url", + "data-url 0.2.0", "flate2", - "imagesize", + "imagesize 0.11.0", "kurbo", "log", "rosvgtree", "strict-num", - "svgtypes", - "usvg-tree", + "svgtypes 0.11.0", + "usvg-tree 0.31.0", +] + +[[package]] +name = "usvg-parser" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd4e3c291f45d152929a31f0f6c819245e2921bfd01e7bd91201a9af39a2bdc" +dependencies = [ + "data-url 0.3.1", + "flate2", + "imagesize 0.12.0", + "kurbo", + "log", + "roxmltree 0.19.0", + "simplecss", + "siphasher", + "svgtypes 0.13.0", + "usvg-tree 0.37.0", ] [[package]] @@ -3668,14 +4364,31 @@ dependencies = [ "kurbo", "rctree", "strict-num", - "svgtypes", + "svgtypes 0.11.0", ] [[package]] -name = "utf8parse" -version = "0.2.1" +name = "usvg-tree" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee3d202ebdb97a6215604b8f5b4d6ef9024efd623cf2e373a6416ba976ec7d3" +dependencies = [ + "rctree", + "strict-num", + "svgtypes 0.13.0", + "tiny-skia-path 0.11.4", +] + +[[package]] +name = "v_frame" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] [[package]] name = "vec_map" @@ -3683,6 +4396,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + [[package]] name = "version_check" version = "0.9.4" @@ -3691,18 +4410,17 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vk-parse" -version = "0.8.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6a0bda9bbe6b9e50e6456c80aa8fe4cca3b21e4311a1130c41e4915ec2e32a" +checksum = "81086c28be67a8759cd80cbb3c8f7b520e0874605fc5eb74d5a1c9c2d1878e79" dependencies = [ "xml-rs", ] [[package]] name = "vulkano" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e1f15eeb9d93a05eb3c237332a10806eac1eb82444e54485bfcc1859c483c23" +version = "0.34.0" +source = "git+https://github.com/vulkano-rs/vulkano.git?rev=4a77d39#4a77d39b8562c9b8a337ee643c58f4f01ba6079b" dependencies = [ "ahash", "ash", @@ -3710,14 +4428,15 @@ dependencies = [ "core-graphics-types", "crossbeam-queue", "half", - "heck", - "indexmap 1.9.3", - "libloading 0.7.4", + "heck 0.4.1", + "indexmap 2.5.0", + "libloading 0.8.3", "objc", "once_cell", "parking_lot", "proc-macro2", "quote", + "raw-window-handle 0.6.2", "regex", "serde", "serde_json", @@ -3729,62 +4448,41 @@ dependencies = [ [[package]] name = "vulkano-macros" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "895b8a2cac1e7650d2d0552f2392da0970a358515ac11a34adaf19bfdc771b98" +version = "0.34.0" +source = "git+https://github.com/vulkano-rs/vulkano.git?rev=4a77d39#4a77d39b8562c9b8a337ee643c58f4f01ba6079b" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.58", ] [[package]] name = "vulkano-shaders" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f8cf18e9becbc6d39f1c39e26bcf69546c93989553eb5748cd734a8a697a6e5" +version = "0.34.0" +source = "git+https://github.com/vulkano-rs/vulkano.git?rev=4a77d39#4a77d39b8562c9b8a337ee643c58f4f01ba6079b" dependencies = [ "ahash", - "heck", + "heck 0.4.1", "proc-macro2", "quote", "shaderc", - "syn 1.0.109", - "vulkano", -] - -[[package]] -name = "vulkano-util" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a71b6df05a391161c1baec645a918437c2949d3494bf74c8358fde291d37f5f4" -dependencies = [ - "ahash", + "syn 2.0.58", "vulkano", - "vulkano-win", - "winit", ] [[package]] name = "vulkano-win" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "666c77efe5ea82837781961a6bcd957ee2e926777e8de0005f580335d6eaefe7" +version = "0.34.0" +source = "git+https://github.com/vulkano-rs/vulkano.git?rev=4a77d39#4a77d39b8562c9b8a337ee643c58f4f01ba6079b" dependencies = [ "core-graphics-types", "objc", - "raw-window-handle", + "raw-window-handle 0.5.2", "vulkano", - "winit", + "winit 0.28.7", ] -[[package]] -name = "waker-fn" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" - [[package]] name = "walkdir" version = "2.5.0" @@ -3797,39 +4495,40 @@ dependencies = [ [[package]] name = "wasabi" -version = "0.1.4" +version = "1.0.0" dependencies = [ "atomic_float", "bytemuck", - "clap 4.5.4", "colors-transform", - "confy", "crossbeam-channel", - "directories 5.0.1", + "directories", "egui", "egui-winit", + "egui_extras", "egui_winit_vulkano", "enum_dispatch", "gen-iter", "ico", "kdmapi", "midi-toolkit-rs", - "num_enum 0.7.2", + "midir", + "num_enum 0.7.3", + "open", "palette", "rand", + "raw-window-handle 0.6.2", "rayon", - "resvg", + "resvg 0.31.1", "rfd", - "rustc-hash", + "rustc-hash 2.0.0", "serde", "serde_derive", - "time", - "toml 0.8.12", + "serde_json", + "toml 0.8.19", "vulkano", "vulkano-shaders", - "vulkano-util", "vulkano-win", - "winit", + "winit 0.30.5", "winres", "xsynth-core", "xsynth-realtime", @@ -3916,6 +4615,20 @@ dependencies = [ "riff", ] +[[package]] +name = "wayland-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "056535ced7a150d45159d3a8dc30f91a2e2d588ca0b23f70e56033622b8016f6" +dependencies = [ + "cc", + "downcast-rs", + "rustix", + "scoped-tls", + "smallvec", + "wayland-sys 0.31.5", +] + [[package]] name = "wayland-client" version = "0.29.5" @@ -3928,8 +4641,20 @@ dependencies = [ "nix 0.24.3", "scoped-tls", "wayland-commons", - "wayland-scanner", - "wayland-sys", + "wayland-scanner 0.29.5", + "wayland-sys 0.29.5", +] + +[[package]] +name = "wayland-client" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3f45d1222915ef1fd2057220c1d9d9624b7654443ea35c3877f7a52bd0a5a2d" +dependencies = [ + "bitflags 2.5.0", + "rustix", + "wayland-backend", + "wayland-scanner 0.31.5", ] [[package]] @@ -3941,7 +4666,18 @@ dependencies = [ "nix 0.24.3", "once_cell", "smallvec", - "wayland-sys", + "wayland-sys 0.29.5", +] + +[[package]] +name = "wayland-csd-frame" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +dependencies = [ + "bitflags 2.5.0", + "cursor-icon", + "wayland-backend", ] [[package]] @@ -3951,7 +4687,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" dependencies = [ "nix 0.24.3", - "wayland-client", + "wayland-client 0.29.5", + "xcursor", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a94697e66e76c85923b0d28a0c251e8f0666f58fc47d316c0f4da6da75d37cb" +dependencies = [ + "rustix", + "wayland-client 0.31.6", "xcursor", ] @@ -3962,9 +4709,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" dependencies = [ "bitflags 1.3.2", - "wayland-client", + "wayland-client 0.29.5", "wayland-commons", - "wayland-scanner", + "wayland-scanner 0.29.5", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b5755d77ae9040bb872a25026555ce4cb0ae75fd923e90d25fba07d81057de0" +dependencies = [ + "bitflags 2.5.0", + "wayland-backend", + "wayland-client 0.31.6", + "wayland-scanner 0.31.5", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0a41a6875e585172495f7a96dfa42ca7e0213868f4f15c313f7c33221a7eff" +dependencies = [ + "bitflags 2.5.0", + "wayland-backend", + "wayland-client 0.31.6", + "wayland-protocols 0.32.4", + "wayland-scanner 0.31.5", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad87b5fd1b1d3ca2f792df8f686a2a11e3fe1077b71096f7a175ab699f89109" +dependencies = [ + "bitflags 2.5.0", + "wayland-backend", + "wayland-client 0.31.6", + "wayland-protocols 0.32.4", + "wayland-scanner 0.31.5", ] [[package]] @@ -3978,6 +4763,17 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "wayland-scanner" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597f2001b2e5fc1121e3d5b9791d3e78f05ba6bfa4641053846248e3a13661c3" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + [[package]] name = "wayland-sys" version = "0.29.5" @@ -3989,6 +4785,18 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "wayland-sys" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efa8ac0d8e8ed3e3b5c9fc92c7881406a268e11555abe36493efabe649a29e09" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + [[package]] name = "web-sys" version = "0.3.69" @@ -3999,19 +4807,30 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webbrowser" -version = "0.8.13" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1b04c569c83a9bb971dd47ec6fd48753315f4bf989b9b04a2e7ca4d7f0dc950" +checksum = "425ba64c1e13b1c6e8c5d2541c8fac10022ca584f33da781db01b5756aef1f4e" dependencies = [ + "block2 0.5.1", "core-foundation", "home", "jni", "log", "ndk-context", - "objc", - "raw-window-handle", + "objc2 0.5.2", + "objc2-foundation", "url", "web-sys", ] @@ -4059,7 +4878,17 @@ version = "0.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" dependencies = [ - "windows-core", + "windows-core 0.54.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132" +dependencies = [ + "windows-core 0.56.0", "windows-targets 0.52.6", ] @@ -4073,11 +4902,45 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4698e52ed2d08f8658ab0c39512a7c00ee5fe2688c65f8c0a4f06750d729f2a6" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "windows-interface" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "windows-result" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd19df78e5168dfb0aedc343d1d1b8d422ab2db6756d2dc3fef75035402a3f64" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" dependencies = [ "windows-targets 0.52.6", ] @@ -4302,35 +5165,87 @@ version = "0.28.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9596d90b45384f5281384ab204224876e8e8bf7d58366d9b795ad99aa9894b94" dependencies = [ - "android-activity", + "android-activity 0.4.3", "bitflags 1.3.2", - "cfg_aliases", + "cfg_aliases 0.1.1", "core-foundation", - "core-graphics", + "core-graphics 0.22.3", "dispatch", "instant", "libc", "log", "mio", "ndk 0.7.0", - "objc2", + "objc2 0.3.0-beta.3.patch-leaks.3", "once_cell", "orbclient", "percent-encoding", - "raw-window-handle", + "raw-window-handle 0.5.2", "redox_syscall 0.3.5", - "sctk-adwaita", - "smithay-client-toolkit", + "sctk-adwaita 0.5.4", + "smithay-client-toolkit 0.16.1", "wasm-bindgen", - "wayland-client", + "wayland-client 0.29.5", "wayland-commons", - "wayland-protocols", - "wayland-scanner", + "wayland-protocols 0.29.5", + "wayland-scanner 0.29.5", "web-sys", "windows-sys 0.45.0", "x11-dl", ] +[[package]] +name = "winit" +version = "0.30.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0be9e76a1f1077e04a411f0b989cbd3c93339e1771cb41e71ac4aee95bfd2c67" +dependencies = [ + "ahash", + "android-activity 0.6.0", + "atomic-waker", + "bitflags 2.5.0", + "block2 0.5.1", + "bytemuck", + "calloop 0.13.0", + "cfg_aliases 0.2.1", + "concurrent-queue", + "core-foundation", + "core-graphics 0.23.2", + "cursor-icon", + "dpi", + "js-sys", + "libc", + "memmap2 0.9.5", + "ndk 0.9.0", + "objc2 0.5.2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "orbclient", + "percent-encoding", + "pin-project", + "raw-window-handle 0.6.2", + "redox_syscall 0.4.1", + "rustix", + "sctk-adwaita 0.10.1", + "smithay-client-toolkit 0.19.2", + "smol_str", + "tracing", + "unicode-segmentation", + "wasm-bindgen", + "wasm-bindgen-futures", + "wayland-backend", + "wayland-client 0.31.6", + "wayland-protocols 0.32.4", + "wayland-protocols-plasma", + "web-sys", + "web-time", + "windows-sys 0.52.0", + "x11-dl", + "x11rb", + "xkbcommon-dl", +] + [[package]] name = "winnow" version = "0.5.40" @@ -4342,9 +5257,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.5" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" dependencies = [ "memchr", ] @@ -4375,8 +5290,12 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8f25ead8c7e4cba123243a6367da5d3990e0d3affa708ea19dce96356bd9f1a" dependencies = [ + "as-raw-xcb-connection", "gethostname", - "rustix 0.38.32", + "libc", + "libloading 0.8.3", + "once_cell", + "rustix", "x11rb-protocol", ] @@ -4394,14 +5313,33 @@ checksum = "6a0ccd7b4a5345edfcd0c3535718a4e9ff7798ffc536bb5b5a0e26ff84732911" [[package]] name = "xdg-home" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e5a325c3cb8398ad6cf859c1135b25dd29e186679cf2da7581d9679f63b38e" +checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" dependencies = [ "libc", - "winapi", + "windows-sys 0.59.0", +] + +[[package]] +name = "xkbcommon-dl" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" +dependencies = [ + "bitflags 2.5.0", + "dlib", + "log", + "once_cell", + "xkeysym", ] +[[package]] +name = "xkeysym" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" + [[package]] name = "xml-rs" version = "0.8.20" @@ -4433,6 +5371,7 @@ dependencies = [ "lazy_static", "proc-macro2", "rayon", + "serde", "simdeez", "spin_sleep", "symphonia", @@ -4454,6 +5393,7 @@ dependencies = [ "crossbeam-channel", "lazy_static", "rayon", + "serde", "spin_sleep", "to_vec", "wav", @@ -4478,30 +5418,28 @@ dependencies = [ [[package]] name = "zbus" -version = "3.15.2" +version = "4.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "675d170b632a6ad49804c8cf2105d7c31eddd3312555cffd4b740e08e97c25e6" +checksum = "c9ff46f2a25abd690ed072054733e0bc3157e3d4c45f41bd183dce09c2ff8ab9" dependencies = [ "async-broadcast", "async-executor", - "async-fs 1.6.0", - "async-io 1.13.0", - "async-lock 2.8.0", + "async-fs", + "async-io", + "async-lock", "async-process", "async-recursion", "async-task", "async-trait", "blocking", - "byteorder", "derivative", "enumflags2", - "event-listener 2.5.3", + "event-listener", "futures-core", "futures-sink", "futures-util", "hex", - "nix 0.26.4", - "once_cell", + "nix 0.28.0", "ordered-stream", "rand", "serde", @@ -4510,7 +5448,7 @@ dependencies = [ "static_assertions", "tracing", "uds_windows", - "winapi", + "windows-sys 0.52.0", "xdg-home", "zbus_macros", "zbus_names", @@ -4519,11 +5457,11 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "3.15.2" +version = "4.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7131497b0f887e8061b430c530240063d33bf9455fa34438f388a245da69e0a5" +checksum = "4e0e3852c93dcdb49c9462afe67a2a468f7bd464150d866e861eaf06208633e0" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", "regex", @@ -4533,9 +5471,9 @@ dependencies = [ [[package]] name = "zbus_names" -version = "2.6.1" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "437d738d3750bed6ca9b8d423ccc7a8eb284f6b1d6d4e225a0e4e6258d864c8d" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" dependencies = [ "serde", "static_assertions", @@ -4562,6 +5500,12 @@ dependencies = [ "syn 2.0.58", ] +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + [[package]] name = "zune-inflate" version = "0.2.54" @@ -4571,15 +5515,23 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "zune-jpeg" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16099418600b4d8f028622f73ff6e3deaabdff330fb9a2a131dea781ee8b0768" +dependencies = [ + "zune-core", +] + [[package]] name = "zvariant" -version = "3.15.2" +version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eef2be88ba09b358d3b58aca6e41cd853631d44787f319a1383ca83424fb2db" +checksum = "2c1b3ca6db667bfada0f1ebfc94b2b1759ba25472ee5373d4551bb892616389a" dependencies = [ - "byteorder", + "endi", "enumflags2", - "libc", "serde", "static_assertions", "url", @@ -4588,11 +5540,11 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "3.15.2" +version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37c24dc0bed72f5f90d1f8bb5b07228cbf63b3c6e9f82d82559d4bae666e7ed9" +checksum = "b7a4b236063316163b69039f77ce3117accb41a09567fd24c168e43491e521bc" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", "syn 1.0.109", @@ -4601,9 +5553,9 @@ dependencies = [ [[package]] name = "zvariant_utils" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" +checksum = "00bedb16a193cc12451873fee2a1bc6550225acece0e36f333e68326c73c8172" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 580b9c1..acb15b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,55 +1,56 @@ [package] name = "wasabi" -version = "0.1.4" +version = "1.0.0" edition = "2021" [dependencies] -egui_winit_vulkano = "0.24.0" -vulkano-shaders = "0.33.0" -vulkano-win = "0.33.0" -vulkano-util = "0.33.0" -egui-winit = "0.21.1" -bytemuck = "1.13.1" -vulkano = "0.33.0" -kdmapi = { git = "https://github.com/arduano/kdmapi.git", rev = "4116b00" } -egui = "0.21.0" -winit = "0.28.3" -rayon = "1.7.0" +egui = { version = "0.29", features = ["serde"] } +egui_extras = { version = "0.29", features = ["svg"] } +egui-winit = "0.29" +egui_winit_vulkano = { git = "https://github.com/MyBlackMIDIScore/egui_winit_vulkano.git", rev = "aa24f97" } +winit = { version = "0.30", default-features = false } +raw-window-handle = "0.6" +vulkano = { git = "https://github.com/vulkano-rs/vulkano.git", rev = "4a77d39" } +vulkano-shaders = { git = "https://github.com/vulkano-rs/vulkano.git", rev = "4a77d39" } +vulkano-win = { git = "https://github.com/vulkano-rs/vulkano.git", rev = "4a77d39" } midi-toolkit-rs = "0.1.0" -xsynth-core = "0.3.1" -xsynth-realtime = "0.3.1" +xsynth-core = { version = "0.3.1", features = ["serde"] } +xsynth-realtime = { version = "0.3.1", features = ["serde"] } +kdmapi-rs = { package = "kdmapi", git = "https://github.com/arduano/kdmapi.git", rev = "4116b00" } +serde = "1.0.210" +serde_derive = "1.0.210" +serde_json = "1.0" +toml = "0.8.19" +bytemuck = "1.18.0" +rayon = "1.10" +enum_dispatch = "0.3.13" gen-iter = { git = "https://github.com/arduano/gen-iter.git", rev = "64e28bc" } -enum_dispatch = "0.3.11" -palette = "0.7.1" -crossbeam-channel = "0.5.8" +crossbeam-channel = "0.5.13" +rustc-hash = "2.0.0" rand = "0.8.5" -confy = "0.5.1" -serde_derive = "1.0.160" -serde = "1.0.160" -toml = "0.8.0" -colors-transform = "0.2.11" -directories = "5.0.0" -rustc-hash = "1.1.0" -atomic_float = "0.1.0" -ico = { git = "https://github.com/StratusFearMe21/rust-ico", branch = "patch-1" } -clap = "4.2.4" -num_enum = "0.7.0" -rfd = { version = "0.12.0", default-features = false, features = [ - 'xdg-portal', -] } -time = "0.3.36" +directories = "5.0.1" +atomic_float = "1.1.0" +ico = "0.3.0" +rfd = "0.15.0" +open = "5.3.0" -[profile.dev] -opt-level = 2 +num_enum = "0.7.3" +palette = "0.7.6" +colors-transform = "0.2" +midir = "0.10.0" -[profile.release] -opt-level = 3 -codegen-units = 1 -lto = true [build-dependencies] resvg = { version = "0.31.0", default-features = false } -ico = { git = "https://github.com/StratusFearMe21/rust-ico", branch = "patch-1" } +ico = "0.3.0" [target.'cfg(windows)'.build-dependencies] winres = "0.1.12" + +[profile.dev] +opt-level = 2 + +[profile.release] +opt-level = 3 +codegen-units = 1 +lto = true diff --git a/assets/Poppins-Bold.ttf b/assets/Poppins-Bold.ttf new file mode 100644 index 0000000..00559ee Binary files /dev/null and b/assets/Poppins-Bold.ttf differ diff --git a/assets/Poppins-Medium.ttf b/assets/Poppins-Medium.ttf new file mode 100644 index 0000000..6bcdcc2 Binary files /dev/null and b/assets/Poppins-Medium.ttf differ diff --git a/assets/UbuntuSansMono-Medium.ttf b/assets/UbuntuSansMono-Medium.ttf new file mode 100644 index 0000000..147bc5a Binary files /dev/null and b/assets/UbuntuSansMono-Medium.ttf differ diff --git a/assets/folder.svg b/assets/folder.svg new file mode 100644 index 0000000..4bf65d8 --- /dev/null +++ b/assets/folder.svg @@ -0,0 +1,41 @@ + + + + + + + + diff --git a/assets/logo.svg b/assets/logo.svg new file mode 100644 index 0000000..e4a6e4f --- /dev/null +++ b/assets/logo.svg @@ -0,0 +1,493 @@ + + + + diff --git a/assets/logo_16.svg b/assets/logo_16.svg new file mode 100644 index 0000000..47fcd9f --- /dev/null +++ b/assets/logo_16.svg @@ -0,0 +1,493 @@ + + + + diff --git a/assets/options.svg b/assets/options.svg new file mode 100644 index 0000000..2721548 --- /dev/null +++ b/assets/options.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + diff --git a/assets/pause.svg b/assets/pause.svg new file mode 100644 index 0000000..88b8442 --- /dev/null +++ b/assets/pause.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + diff --git a/assets/pin.svg b/assets/pin.svg new file mode 100644 index 0000000..d50933b --- /dev/null +++ b/assets/pin.svg @@ -0,0 +1,44 @@ + + + + + + + + + diff --git a/assets/play.svg b/assets/play.svg new file mode 100644 index 0000000..d253bd0 --- /dev/null +++ b/assets/play.svg @@ -0,0 +1,45 @@ + + + + + + + + + + diff --git a/assets/stop.svg b/assets/stop.svg new file mode 100644 index 0000000..bb2d1dc --- /dev/null +++ b/assets/stop.svg @@ -0,0 +1,48 @@ + + + + + + + + + + diff --git a/src/audio_playback/empty.rs b/src/audio_playback/empty.rs new file mode 100644 index 0000000..add11c7 --- /dev/null +++ b/src/audio_playback/empty.rs @@ -0,0 +1,23 @@ +use super::*; + +pub struct EmptyPlayer {} + +impl EmptyPlayer { + pub fn new() -> Self { + Self {} + } +} + +impl MidiAudioPlayer for EmptyPlayer { + fn reset(&mut self) {} + + fn push_event(&mut self, _data: u32) {} + + fn voice_count(&self) -> u64 { + 0 + } + + fn configure(&mut self, _settings: &SynthSettings) {} + + fn set_soundfonts(&mut self, _soundfonts: &Vec) {} +} diff --git a/src/audio_playback/kdmapi.rs b/src/audio_playback/kdmapi.rs new file mode 100644 index 0000000..672407b --- /dev/null +++ b/src/audio_playback/kdmapi.rs @@ -0,0 +1,40 @@ +use super::*; +use kdmapi_rs::{KDMAPIStream, KDMAPI}; + +pub struct KdmapiPlayer { + stream: KDMAPIStream, + use_om_list: bool, +} + +impl KdmapiPlayer { + pub fn new() -> Self { + Self { + stream: KDMAPI.open_stream(), + use_om_list: false, + } + } +} + +impl MidiAudioPlayer for KdmapiPlayer { + fn reset(&mut self) { + self.stream.reset(); + } + + fn push_event(&mut self, data: u32) { + self.stream.send_direct_data(data); + } + + fn voice_count(&self) -> u64 { + 0 + } + + fn configure(&mut self, settings: &SynthSettings) { + self.use_om_list = settings.kdmapi.use_om_sflist; + } + + fn set_soundfonts(&mut self, _soundfonts: &Vec) { + if !self.use_om_list { + // TODO: Create OM compatible SF list to be sent through "LoadCustomSoundFontsList" + } + } +} diff --git a/src/audio_playback/midiout.rs b/src/audio_playback/midiout.rs new file mode 100644 index 0000000..2312390 --- /dev/null +++ b/src/audio_playback/midiout.rs @@ -0,0 +1,60 @@ +use std::thread; + +use super::*; +use crossbeam_channel::Sender; +use midir::MidiOutput; + +pub struct MidiDevicePlayer { + sender: Sender, +} + +impl MidiDevicePlayer { + pub fn new(device: String) -> Result { + let out = MidiOutput::new("wasabi").map_err(|e| format!("{:?}", e))?; + let ports = out.ports(); + if ports.is_empty() { + return Err("No MIDI devices available.".into()); + } + + let find = ports.iter().find(|d| { + if let Ok(name) = out.port_name(d) { + name == device + } else { + false + } + }); + let found = find.unwrap_or(&ports[0]); + let mut connection = out + .connect(found, "wasabi") + .map_err(|e| format!("{:?}", e))?; + + let (sender, receiver) = crossbeam_channel::bounded::(1000); + + thread::spawn(move || { + for data in receiver { + let message = data.to_le_bytes(); + connection.send(&message).unwrap_or_default(); + } + }); + + Ok(Self { sender }) + } +} + +impl MidiAudioPlayer for MidiDevicePlayer { + fn reset(&mut self) { + // TODO: With CC maybe? + } + + fn push_event(&mut self, data: u32) { + self.sender.send(data).unwrap(); + } + + fn voice_count(&self) -> u64 { + 0 + } + + fn configure(&mut self, _settings: &SynthSettings) {} + + fn set_soundfonts(&mut self, _soundfonts: &Vec) {} +} diff --git a/src/audio_playback/mod.rs b/src/audio_playback/mod.rs index dceb4c5..1d46af5 100644 --- a/src/audio_playback/mod.rs +++ b/src/audio_playback/mod.rs @@ -1,123 +1,54 @@ -use kdmapi::{KDMAPIStream, KDMAPI}; -use std::ops::RangeInclusive; -use xsynth_core::{channel::ChannelInitOptions, soundfont::SoundfontInitOptions}; -pub mod xsynth; - -#[derive(Clone)] -pub enum AudioPlayerType { - XSynth { - buffer: f64, - use_threadpool: bool, - ignore_range: RangeInclusive, - options: ChannelInitOptions, - }, - Kdmapi, +use crate::settings::{SynthSettings, WasabiSoundfont}; + +mod xsynth; +pub use xsynth::*; +mod kdmapi; +pub use kdmapi::*; +mod midiout; +pub use midiout::*; +mod empty; +pub use empty::*; + +pub trait MidiAudioPlayer: Send + Sync { + fn voice_count(&self) -> u64; + fn push_event(&mut self, data: u32); + fn configure(&mut self, settings: &SynthSettings); + fn set_soundfonts(&mut self, soundfonts: &Vec); + fn reset(&mut self); } -pub struct SimpleTemporaryPlayer { - player_type: AudioPlayerType, - xsynth: Option, - kdmapi: Option, +pub struct WasabiAudioPlayer { + player: Box, } -impl SimpleTemporaryPlayer { - pub fn new(player_type: AudioPlayerType) -> Self { - let (xsynth, kdmapi) = match player_type.clone() { - AudioPlayerType::XSynth { - buffer, - use_threadpool, - ignore_range, - options, - } => { - let xsynth = - xsynth::XSynthPlayer::new(buffer, use_threadpool, ignore_range, options); - (Some(xsynth), None) - } - AudioPlayerType::Kdmapi => { - let kdmapi = KDMAPI.open_stream(); - (None, Some(kdmapi)) - } - }; - Self { - player_type, - xsynth, - kdmapi, - } +impl WasabiAudioPlayer { + pub fn new(player: Box) -> Self { + Self { player: player } } - pub fn switch_player(&mut self, player_type: AudioPlayerType) { - self.reset(); - self.xsynth = None; - self.kdmapi = None; - let new_player = Self::new(player_type); - - self.player_type = new_player.player_type; - self.xsynth = new_player.xsynth; - self.kdmapi = new_player.kdmapi; + pub fn switch(&mut self, new_player: Box) { + self.player = new_player; } - pub fn get_voice_count(&self) -> u64 { - match self.player_type { - AudioPlayerType::XSynth { .. } => { - if let Some(xsynth) = &self.xsynth { - xsynth.get_voice_count() - } else { - 0 - } - } - AudioPlayerType::Kdmapi => 0, - } + pub fn voice_count(&self) -> u64 { + self.player.voice_count() } pub fn push_events(&mut self, data: impl Iterator) { - for e in data { - self.push_event(e); - } - } - - pub fn push_event(&mut self, data: u32) { - match self.player_type { - AudioPlayerType::XSynth { .. } => { - if let Some(xsynth) = self.xsynth.as_mut() { - xsynth.push_event(data); - } - } - AudioPlayerType::Kdmapi => { - if let Some(kdmapi) = self.kdmapi.as_mut() { - kdmapi.send_direct_data(data); - } - } + for ev in data { + self.player.push_event(ev); } } - pub fn reset(&mut self) { - match self.player_type { - AudioPlayerType::XSynth { .. } => { - if let Some(xsynth) = self.xsynth.as_mut() { - xsynth.reset(); - } - } - AudioPlayerType::Kdmapi => { - if let Some(kdmapi) = self.kdmapi.as_mut() { - kdmapi.reset(); - } - } - } + pub fn configure(&mut self, settings: &SynthSettings) { + self.player.configure(settings); } - pub fn set_layer_count(&mut self, layers: Option) { - if let AudioPlayerType::XSynth { .. } = self.player_type { - if let Some(xsynth) = self.xsynth.as_mut() { - xsynth.set_layer_count(layers); - } - } + pub fn set_soundfonts(&mut self, soundfonts: &Vec) { + self.player.set_soundfonts(soundfonts); } - pub fn set_soundfont(&mut self, path: &str, options: SoundfontInitOptions) { - if let AudioPlayerType::XSynth { .. } = self.player_type { - if let Some(xsynth) = self.xsynth.as_mut() { - xsynth.set_soundfont(path, options); - } - } + pub fn reset(&mut self) { + self.player.reset(); } } diff --git a/src/audio_playback/xsynth.rs b/src/audio_playback/xsynth.rs index fda330d..8e9e982 100644 --- a/src/audio_playback/xsynth.rs +++ b/src/audio_playback/xsynth.rs @@ -1,21 +1,21 @@ use std::{ - ops::{Deref, DerefMut, RangeInclusive}, - path::Path, + ops::{Deref, DerefMut}, sync::Arc, }; -use crate::WasabiSettings; +use crate::settings::WasabiSoundfont; use xsynth_core::{ - channel::{ChannelConfigEvent, ChannelEvent, ChannelInitOptions}, - soundfont::{EnvelopeOptions, SampleSoundfont, SoundfontBase, SoundfontInitOptions}, + channel::{ChannelConfigEvent, ChannelEvent}, + soundfont::{SampleSoundfont, SoundfontBase}, AudioStreamParams, }; use xsynth_realtime::{ - RealtimeEventSender, RealtimeSynth, RealtimeSynthStatsReader, SynthEvent, ThreadCount, - XSynthRealtimeConfig, + RealtimeEventSender, RealtimeSynth, RealtimeSynthStatsReader, SynthEvent, XSynthRealtimeConfig, }; +use super::*; + #[repr(transparent)] struct FuckYouImSend(T); @@ -38,30 +38,13 @@ impl DerefMut for FuckYouImSend { pub struct XSynthPlayer { sender: RealtimeEventSender, - pub stats: RealtimeSynthStatsReader, + stats: RealtimeSynthStatsReader, stream_params: AudioStreamParams, _synth: FuckYouImSend, } impl XSynthPlayer { - pub fn new( - buffer: f64, - use_threadpool: bool, - ignore_range: RangeInclusive, - options: ChannelInitOptions, - ) -> Self { - let config = XSynthRealtimeConfig { - render_window_ms: buffer, - channel_init_options: options, - ignore_range, - multithreading: if use_threadpool { - ThreadCount::Auto - } else { - ThreadCount::None - }, - ..Default::default() - }; - + pub fn new(config: XSynthRealtimeConfig) -> Self { let synth = FuckYouImSend(RealtimeSynth::open_with_default_output(config)); let sender = synth.get_senders(); let stream_params = synth.stream_params(); @@ -74,50 +57,51 @@ impl XSynthPlayer { _synth: synth, } } +} - pub fn get_voice_count(&self) -> u64 { +impl MidiAudioPlayer for XSynthPlayer { + fn voice_count(&self) -> u64 { self.stats.voice_count() } - pub fn push_event(&mut self, data: u32) { + fn push_event(&mut self, data: u32) { self.sender.send_event_u32(data); } - pub fn reset(&mut self) { + fn reset(&mut self) { self.sender.reset_synth(); } - pub fn set_layer_count(&mut self, layers: Option) { + fn configure(&mut self, settings: &SynthSettings) { + let layers = if settings.xsynth.limit_layers { + Some(settings.xsynth.layers) + } else { + None + }; + self.sender .send_event(SynthEvent::AllChannels(ChannelEvent::Config( ChannelConfigEvent::SetLayerCount(layers), ))); } - pub fn set_soundfont(&mut self, path: &str, options: SoundfontInitOptions) { - if !path.is_empty() && Path::new(path).exists() { - let samplesf = SampleSoundfont::new(path, self.stream_params, options); - if let Ok(sf) = samplesf { - let soundfont: Arc = Arc::new(sf); - self.sender - .send_event(SynthEvent::AllChannels(ChannelEvent::Config( - ChannelConfigEvent::SetSoundfonts(vec![soundfont]), - ))); + fn set_soundfonts(&mut self, soundfonts: &Vec) { + // TODO: Load in thread + let mut out: Vec> = Vec::new(); + + for sf in soundfonts { + if sf.enabled { + if let Ok(sf) = + SampleSoundfont::new(sf.path.clone(), self.stream_params, sf.options) + { + out.push(Arc::new(sf)); + } } } - } -} - -pub fn convert_to_sf_init(settings: &WasabiSettings) -> SoundfontInitOptions { - SoundfontInitOptions { - vol_envelope_options: EnvelopeOptions::default(), - use_effects: settings.synth.use_effects, - ..Default::default() - } -} -pub fn convert_to_channel_init(settings: &WasabiSettings) -> ChannelInitOptions { - ChannelInitOptions { - fade_out_killing: settings.synth.fade_out_kill, + self.sender + .send_event(SynthEvent::AllChannels(ChannelEvent::Config( + ChannelConfigEvent::SetSoundfonts(out), + ))); } } diff --git a/src/gui/window.rs b/src/gui/window.rs index 7f8bceb..3501d65 100644 --- a/src/gui/window.rs +++ b/src/gui/window.rs @@ -4,68 +4,57 @@ mod keyboard_layout; mod scene; mod stats; -mod settings_window; -mod top_panel; -mod xsynth_settings; +mod about; +mod playback_panel; +mod settings; +mod shortcuts; -use std::{ - path::PathBuf, - sync::{Arc, RwLock}, -}; -use time::Duration; +use std::path::Path; +use std::sync::{Arc, RwLock}; +use std::thread; +use std::{path::PathBuf, time::Duration}; -use egui::{style::Margin, Frame, Visuals}; +use crossbeam_channel::{Receiver, Sender}; +use egui::FontFamily::{Monospace, Proportional}; +use egui::FontId; +use egui::Frame; +use settings::SettingsWindow; +use crate::audio_playback::{EmptyPlayer, MidiDevicePlayer}; use crate::{ - audio_playback::{ - xsynth::{convert_to_channel_init, convert_to_sf_init}, - AudioPlayerType, SimpleTemporaryPlayer, - }, + audio_playback::{KdmapiPlayer, MidiAudioPlayer, WasabiAudioPlayer, XSynthPlayer}, gui::window::{keyboard::GuiKeyboard, scene::GuiRenderScene}, midi::{CakeMIDIFile, InRamMIDIFile, LiveLoadMIDIFile, MIDIFileBase, MIDIFileUnion}, - settings::{MidiLoading, Synth, WasabiSettings}, + settings::{MidiParsing, Synth, WasabiSettings}, state::WasabiState, GuiRenderer, GuiState, }; +const WIN_MARGIN: egui::Margin = egui::Margin::same(12.0); +const SPACE: f32 = 12.0; + pub struct GuiWasabiWindow { render_scene: GuiRenderScene, keyboard_layout: keyboard_layout::KeyboardLayout, keyboard: GuiKeyboard, midi_file: Option, - synth: Arc>, + synth: Arc>, fps: fps::Fps, + + settings_win: SettingsWindow, + midi_picker: (Sender, Receiver), } impl GuiWasabiWindow { pub fn new(renderer: &mut GuiRenderer, settings: &mut WasabiSettings) -> GuiWasabiWindow { - let synth = match settings.synth.synth { - Synth::Kdmapi => Arc::new(RwLock::new(SimpleTemporaryPlayer::new( - AudioPlayerType::Kdmapi, - ))), - Synth::XSynth => { - let synth = Arc::new(RwLock::new(SimpleTemporaryPlayer::new( - AudioPlayerType::XSynth { - buffer: settings.synth.buffer_ms, - use_threadpool: settings.synth.use_threadpool, - ignore_range: settings.synth.vel_ignore.clone(), - options: convert_to_channel_init(settings), - }, - ))); - synth - .write() - .unwrap() - .set_soundfont(&settings.synth.sfz_path, convert_to_sf_init(settings)); - synth - .write() - .unwrap() - .set_layer_count(match settings.synth.layer_count { - 0 => None, - _ => Some(settings.synth.layer_count), - }); - synth - } - }; + let synth = Self::create_synth(settings); + let synth = Arc::new(RwLock::new(WasabiAudioPlayer::new(synth))); + + let mut settings_win = SettingsWindow::new(settings); + settings_win.load_palettes(); + settings_win.load_midi_devices(settings); + + let midi_picker = crossbeam_channel::unbounded(); GuiWasabiWindow { render_scene: GuiRenderScene::new(renderer), @@ -74,32 +63,143 @@ impl GuiWasabiWindow { midi_file: None, synth, fps: fps::Fps::new(), + + settings_win, + midi_picker, } } + #[inline(always)] + fn set_style(ctx: &egui::Context, _settings: &WasabiSettings) { + // Set theme + ctx.style_mut(|style| { + style.visuals.panel_fill = egui::Color32::from_rgb(18, 18, 18); + style.visuals.window_fill = style.visuals.panel_fill; + style.visuals.widgets.inactive.weak_bg_fill = style.visuals.panel_fill; + style.visuals.widgets.hovered.bg_stroke = egui::Stroke::NONE; + style.visuals.widgets.active.bg_stroke = egui::Stroke::NONE; + + style.visuals.selection.bg_fill = style.visuals.widgets.active.weak_bg_fill; + style.visuals.selection.stroke.color = egui::Color32::TRANSPARENT; + + style.visuals.override_text_color = Some(egui::Color32::from_rgb(210, 210, 210)); + + style.spacing.menu_margin = egui::Margin::same(8.0); + style.spacing.interact_size.y = 26.0; + }); + + // Set fonts + let mut fonts = egui::FontDefinitions::default(); + + fonts.font_data.insert( + "poppins".to_owned(), + egui::FontData::from_static(include_bytes!("../../assets/Poppins-Medium.ttf")), + ); + fonts + .families + .get_mut(&egui::FontFamily::Proportional) + .unwrap() + .insert(0, "poppins".to_owned()); + + fonts.font_data.insert( + "ubuntu".to_owned(), + egui::FontData::from_static(include_bytes!("../../assets/UbuntuSansMono-Medium.ttf")), + ); + fonts + .families + .get_mut(&egui::FontFamily::Monospace) + .unwrap() + .insert(0, "ubuntu".to_owned()); + + ctx.set_fonts(fonts); + + // Set font size + let mut style = (*ctx.style()).clone(); + style.text_styles = [ + (egui::TextStyle::Heading, FontId::new(26.0, Proportional)), + (egui::TextStyle::Body, FontId::new(16.0, Proportional)), + (egui::TextStyle::Monospace, FontId::new(12.0, Monospace)), + (egui::TextStyle::Button, FontId::new(16.0, Proportional)), + (egui::TextStyle::Small, FontId::new(12.0, Proportional)), + ( + egui::TextStyle::Name("monospace big".into()), + FontId::new(22.0, Monospace), + ), + ] + .into(); + ctx.set_style(style); + } + /// Defines the layout of our UI pub fn layout( &mut self, - state: &mut GuiState, + gui_state: &mut GuiState, settings: &mut WasabiSettings, - wasabi_state: &mut WasabiState, + state: &mut WasabiState, ) { - let ctx = state.renderer.gui.context(); - self.fps.update(); - ctx.set_visuals(Visuals::dark()); + let ctx = gui_state.renderer.gui.context(); + + let fps_limit = match settings.gui.fps_limit { + 0 => None, + f => Some(f), + }; + self.fps.update(fps_limit); + Self::set_style(&ctx, settings); - if wasabi_state.settings_visible { - settings_window::draw_settings(self, settings, wasabi_state, &ctx); + { + let recv = self.midi_picker.1.clone(); + if !recv.is_empty() { + for midi in recv { + state.last_location = midi.clone(); + self.load_midi(settings, midi); + break; + } + } } - if wasabi_state.xsynth_settings_visible { - xsynth_settings::draw_xsynth_settings(self, settings, wasabi_state, &ctx); + + // Other windows + if state.show_settings { + self.settings_win + .show(&ctx, settings, state, self.synth.clone()); } - let height_prev = ctx.available_rect().height(); - if settings.visual.show_top_pannel { - top_panel::draw_panel(self, settings, wasabi_state, &ctx); + if state.show_about { + self.show_about(&ctx, state); + } + + if state.show_shortcuts { + self.show_shortcuts(&ctx, state); } + // Set global keyboard shortcuts + ctx.input(|events| { + for event in &events.events { + if let egui::Event::Key { + key, + pressed, + modifiers, + .. + } = event + { + if *pressed && modifiers.ctrl { + match key { + egui::Key::F => state.panel_pinned = !state.panel_pinned, + egui::Key::G => state.stats_visible = !state.stats_visible, + egui::Key::O => self.open_midi_dialog(state), + _ => {} + } + } + if *pressed && modifiers.alt && key == &egui::Key::Enter { + state.fullscreen = !state.fullscreen + } + } + } + }); + + // Render the panel + let height_prev = ctx.available_rect().height(); + self.show_playback_panel(&ctx, state); + // Calculate available space left for keyboard and notes // We must render notes before keyboard because the notes // renderer tells us the key colors @@ -107,21 +207,21 @@ impl GuiWasabiWindow { let height = available.height(); let panel_height = height_prev - height; let keyboard_height = - (11.6 / settings.midi.key_range.len() as f32 * available.width()).min(height / 2.0); + (11.6 / settings.scene.key_range.len() as f32 * available.width()).min(height / 2.0); let notes_height = height - keyboard_height; let key_view = self.keyboard_layout.get_view_for_keys( - *settings.midi.key_range.start() as usize, - *settings.midi.key_range.end() as usize, + *settings.scene.key_range.start() as usize, + *settings.scene.key_range.end() as usize, ); let no_frame = Frame::default() - .inner_margin(Margin::same(0.0)) - .fill(settings.visual.bg_color); + .inner_margin(egui::Margin::same(0.0)) + .fill(settings.scene.bg_color); let mut stats = stats::GuiMidiStats::empty(); - let mut render_result_data = None; + let mut render_result_data: Option = None; // Render the notes egui::TopBottomPanel::top("Note panel") @@ -130,31 +230,45 @@ impl GuiWasabiWindow { .show_separator_line(false) .show(&ctx, |ui| { if let Some(midi_file) = self.midi_file.as_mut() { - let one_sec = Duration::seconds(1); + let skip_dur = Duration::from_secs_f64(settings.gui.skip_control); let time = midi_file.timer().get_time(); + // Set playback keyboard shortcuts ui.input(|events| { for event in &events.events { - if let egui::Event::Key { key, pressed, .. } = event { + if let egui::Event::Key { + key, + pressed, + modifiers, + .. + } = event + { if pressed == &true { match key { egui::Key::ArrowRight => { - midi_file.timer_mut().seek(time + one_sec) + midi_file.timer_mut().seek(time + skip_dur) } egui::Key::ArrowLeft => { if midi_file.allows_seeking_backward() { - midi_file.timer_mut().seek(if time <= one_sec { - Duration::seconds(0) + midi_file.timer_mut().seek(if time <= skip_dur { + // FIXME: Start deplay + Duration::from_secs(0) } else { - time - one_sec + time - skip_dur }) } } egui::Key::ArrowUp => { - settings.midi.note_speed += 0.05; + if modifiers.ctrl { + settings.scene.note_speed += + settings.gui.speed_control; + } } egui::Key::ArrowDown => { - settings.midi.note_speed -= 0.05; + if modifiers.ctrl { + settings.scene.note_speed -= + settings.gui.speed_control; + } } egui::Key::Space => midi_file.timer_mut().toggle_pause(), _ => {} @@ -165,11 +279,11 @@ impl GuiWasabiWindow { }); let result = self.render_scene.draw( - state, + gui_state, ui, &key_view, midi_file, - settings.midi.note_speed, + settings.scene.note_speed, ); stats.set_rendered_note_count(result.notes_rendered); render_result_data = Some(result); @@ -182,36 +296,6 @@ impl GuiWasabiWindow { .frame(no_frame) .show_separator_line(false) .show(&ctx, |ui| { - ui.input(|events| { - for event in &events.events { - if let egui::Event::Key { - key, - pressed, - modifiers, - .. - } = event - { - if *pressed && modifiers.ctrl { - match key { - egui::Key::F => { - settings.visual.show_top_pannel = - !settings.visual.show_top_pannel - } - egui::Key::G => { - settings.visual.show_statistics = - !settings.visual.show_statistics - } - //egui::Key::O => self.open_midi_dialog(wasabi_state), - _ => {} - } - } - if *pressed && modifiers.alt && key == &egui::Key::Enter { - wasabi_state.fullscreen = !wasabi_state.fullscreen - } - } - } - }); - let colors = if let Some(data) = render_result_data { data.key_colors } else { @@ -219,33 +303,42 @@ impl GuiWasabiWindow { }; self.keyboard - .draw(ui, &key_view, &colors, &settings.visual.bar_color); + .draw(ui, &key_view, &colors, &settings.scene.bar_color); }); // Render the stats - if settings.visual.show_statistics { - let voice_count = self.synth.read().unwrap().get_voice_count(); + if state.stats_visible { + let voice_count = self.synth.read().unwrap().voice_count(); stats.set_voice_count(voice_count); - let pos = egui::Pos2::new(10.0, panel_height + 10.0); - stats::draw_stats(self, &ctx, pos, stats); + let pad = if settings.scene.statistics.floating { + 12.0 + } else { + 0.0 + }; + let pos = egui::Pos2::new(pad, panel_height + pad); + stats::draw_stats(self, &ctx, pos, stats, settings); } } - #[allow(unused_variables)] - pub fn open_midi_dialog(&mut self, settings: &mut WasabiSettings, state: &mut WasabiState) { - // If windows, just use the native dialog - let midi_path = rfd::FileDialog::new() - .add_filter("mid", &["mid"]) - .pick_file(); + pub fn open_midi_dialog(&mut self, state: &mut WasabiState) { + let sender = self.midi_picker.0.clone(); + let last_location = state.last_location.clone(); - if let Some(midi_path) = midi_path { - state.last_midi_file = Some(midi_path.clone()); - self.load_midi(settings, midi_path); - } + thread::spawn(move || { + let midi_path = rfd::FileDialog::new() + .add_filter("mid", &["mid", "MID"]) + .set_directory(last_location.parent().unwrap_or(Path::new("./"))) + .pick_file(); + + if let Some(midi_path) = midi_path { + sender.send(midi_path).unwrap_or_default(); + } + }); } pub fn load_midi(&mut self, settings: &mut WasabiSettings, midi_path: PathBuf) { + // TODO: Load in thread if let Some(midi_file) = self.midi_file.as_mut() { midi_file.timer_mut().pause(); } @@ -253,30 +346,30 @@ impl GuiWasabiWindow { self.midi_file = None; if let Some(midi_path) = midi_path.to_str() { - match settings.midi.midi_loading { - MidiLoading::Ram => { + match settings.midi.parsing { + MidiParsing::Ram => { let mut midi_file = MIDIFileUnion::InRam(InRamMIDIFile::load_from_file( midi_path, self.synth.clone(), - settings.midi.random_colors, + settings, )); midi_file.timer_mut().play(); self.midi_file = Some(midi_file); } - MidiLoading::Live => { + MidiParsing::Live => { let mut midi_file = MIDIFileUnion::Live(LiveLoadMIDIFile::load_from_file( midi_path, self.synth.clone(), - settings.midi.random_colors, + settings, )); midi_file.timer_mut().play(); self.midi_file = Some(midi_file); } - MidiLoading::Cake => { + MidiParsing::Cake => { let mut midi_file = MIDIFileUnion::Cake(CakeMIDIFile::load_from_file( midi_path, self.synth.clone(), - settings.midi.random_colors, + settings, )); midi_file.timer_mut().play(); self.midi_file = Some(midi_file); @@ -284,4 +377,23 @@ impl GuiWasabiWindow { } } } + + pub fn create_synth(settings: &WasabiSettings) -> Box { + let mut synth: Box = match settings.synth.synth { + Synth::XSynth => Box::new(XSynthPlayer::new(settings.synth.xsynth.config.clone())), + Synth::Kdmapi => Box::new(KdmapiPlayer::new()), + Synth::MidiDevice => { + if let Ok(midiout) = MidiDevicePlayer::new(settings.synth.midi_device.clone()) { + Box::new(midiout) + } else { + Box::new(EmptyPlayer::new()) + } + } + Synth::None => Box::new(EmptyPlayer::new()), + }; + synth.set_soundfonts(&settings.synth.soundfonts); + synth.configure(&settings.synth); + + synth + } } diff --git a/src/gui/window/about.rs b/src/gui/window/about.rs new file mode 100644 index 0000000..0f724cb --- /dev/null +++ b/src/gui/window/about.rs @@ -0,0 +1,152 @@ +use crate::state::WasabiState; +use std::env::consts::{ARCH, OS}; + +use super::GuiWasabiWindow; + +impl GuiWasabiWindow { + pub fn show_about(&mut self, ctx: &egui::Context, state: &mut WasabiState) { + let frame = + egui::Frame::inner_margin(egui::Frame::window(ctx.style().as_ref()), super::WIN_MARGIN); + let size = [600.0, 450.0]; + + egui::Window::new("About Wasabi") + .resizable(true) + .collapsible(false) + .title_bar(true) + .scroll([false, true]) + .enabled(true) + .frame(frame) + .fixed_size(size) + .open(&mut state.show_about) + .show(ctx, |ui| { + ui.add_space(4.0); + ui.horizontal(|ui| { + let image_size = 100.0; + + let title_size = 46.0; + let titleid = egui::FontId { + size: title_size, + ..Default::default() + }; + + let title_text = format!("Wasabi"); + let title_galley = ui.painter().layout_no_wrap( + title_text.to_owned(), + titleid, + egui::Color32::WHITE, + ); + + let logo_width = + image_size + ui.spacing().item_spacing.x + title_galley.size().x; + let space = ui.available_width() / 2.0 - (logo_width + 4.0) / 2.0; + + ui.add_space(space); + ui.add( + egui::Image::new(egui::include_image!("../../../assets/logo.svg")) + .fit_to_exact_size(egui::Vec2::new(image_size, image_size)), + ); + ui.add_space(4.0); + + ui.vertical(|ui| { + let text_height = title_galley.size().y; + let space = (image_size - text_height) / 2.0; + ui.add_space(space); + + ui.label(egui::RichText::new(title_text).size(title_size)); + }) + }); + + ui.add_space(10.0); + ui.separator(); + ui.add_space(10.0); + + let col_width = size[0] / 2.0; + ui.heading("Build Information"); + egui::Grid::new("buildinfo_grid") + .num_columns(2) + .min_col_width(col_width) + .striped(true) + .show(ui, |ui| { + ui.label("Version:"); + ui.label(env!("CARGO_PKG_VERSION")); + ui.end_row(); + + ui.label("Operating System:"); + ui.label(OS.to_string()); + ui.end_row(); + + ui.label("Architecture:"); + ui.label(ARCH.to_string()); + ui.end_row(); + }); + + ui.add_space(20.0); + + ui.heading("Libraries"); + egui::Grid::new("libraries_grid") + .num_columns(2) + .min_col_width(col_width) + .striped(true) + .show(ui, |ui| { + ui.label("Egui Version:"); + ui.label("0.29"); + ui.end_row(); + + ui.label("XSynth Version:"); + ui.label("0.3.1"); + ui.end_row(); + + ui.label("MIDI Toolkit Version:"); + ui.label("0.1.0"); + ui.end_row(); + }); + + let gh_text = "\u{1F310} GitHub"; + let gh_galley = ui.painter().layout_no_wrap( + gh_text.to_owned(), + ctx.style() + .text_styles + .iter() + .find(|v| v.0 == &egui::TextStyle::Button) + .unwrap() + .1 + .clone(), + egui::Color32::WHITE, + ); + + let upd_text = "\u{1F310} Check for updates"; + let upd_galley = ui.painter().layout_no_wrap( + upd_text.to_owned(), + ctx.style() + .text_styles + .iter() + .find(|v| v.0 == &egui::TextStyle::Button) + .unwrap() + .1 + .clone(), + egui::Color32::WHITE, + ); + + let mut h = ui.available_height(); + + let button_height = ui.spacing().button_padding.y * 2.0 + gh_galley.size().y; + h -= button_height; + ui.add_space(h); + + ui.horizontal(|ui| { + let w = ui.available_width(); + + let button_width = gh_galley.size().x + + upd_galley.size().x + + ui.spacing().button_padding.x * 4.0; + let w = w / 2.0 - button_width / 2.0; + ui.add_space(w); + + if ui.button(gh_text).clicked() { + open::that("https://github.com/BlackMIDIDevs/wasabi").unwrap(); + } + if ui.button(upd_text).clicked() {} + }); + }); + } +} diff --git a/src/gui/window/fps.rs b/src/gui/window/fps.rs index 380db49..8b19a5a 100644 --- a/src/gui/window/fps.rs +++ b/src/gui/window/fps.rs @@ -9,9 +9,10 @@ impl Fps { Self(VecDeque::new()) } - pub fn update(&mut self) { + pub fn update(&mut self, _limit: Option) { self.0.push_back(Instant::now()); while let Some(front) = self.0.front() { + // TODO: FPS limit if front.elapsed().as_secs_f64() > FPS_WINDOW { self.0.pop_front(); } else { diff --git a/src/gui/window/playback_panel.rs b/src/gui/window/playback_panel.rs new file mode 100644 index 0000000..53741ce --- /dev/null +++ b/src/gui/window/playback_panel.rs @@ -0,0 +1,220 @@ +use std::time::Duration; + +use egui::{popup_below_widget, PopupCloseBehavior}; + +use super::{GuiWasabiWindow, SPACE, WIN_MARGIN}; +use crate::{midi::MIDIFileBase, state::WasabiState, utils::convert_seconds_to_time_string}; + +impl GuiWasabiWindow { + pub fn show_playback_panel(&mut self, ctx: &egui::Context, state: &mut WasabiState) { + let mut mouse_over_panel = false; + if let Some(mouse) = ctx.pointer_latest_pos() { + if mouse.y < 60.0 { + mouse_over_panel = true; + } + } + let button_size = egui::Vec2::new(26.0, 26.0); + let icon_color = ctx.style().visuals.strong_text_color(); + let button_rounding = 8.0; + + let is_popup_open = ctx.memory(|mem| mem.is_popup_open(state.panel_popup_id)); + + let frame = egui::Frame::side_top_panel(&ctx.style()).inner_margin(WIN_MARGIN); + egui::TopBottomPanel::top("panel") + .frame(frame) + .show_separator_line(false) + .show_animated( + ctx, + state.panel_pinned || mouse_over_panel || is_popup_open, + |ui| { + ui.horizontal(|ui| { + let folder_img = + egui::Image::new(egui::include_image!("../../../assets/folder.svg")) + .fit_to_exact_size(button_size) + .tint(icon_color) + .rounding(button_rounding); + + if ui + .add(egui::ImageButton::new(folder_img)) + .on_hover_text("Open MIDI") + .clicked() + { + self.open_midi_dialog(state); + } + + let stop_img = + egui::Image::new(egui::include_image!("../../../assets/stop.svg")) + .fit_to_exact_size(button_size) + .tint(icon_color) + .rounding(button_rounding); + + if ui + .add(egui::ImageButton::new(stop_img)) + .on_hover_text("Unload") + .clicked() + { + if let Some(midi) = self.midi_file.take().as_mut() { + midi.timer_mut().pause(); + self.synth.write().unwrap().reset(); + } + } + + let playing = if let Some(midi) = self.midi_file.as_ref() { + !midi.timer().is_paused() + } else { + false + }; + + // FIXME + if playing { + let pause_img = + egui::Image::new(egui::include_image!("../../../assets/pause.svg")) + .fit_to_exact_size(button_size) + .tint(icon_color) + .rounding(button_rounding); + + if ui + .add(egui::ImageButton::new(pause_img)) + .on_hover_text("Pause") + .clicked() + { + if let Some(midi_file) = self.midi_file.as_mut() { + midi_file.timer_mut().pause(); + } + } + } else { + let play_img = + egui::Image::new(egui::include_image!("../../../assets/play.svg")) + .fit_to_exact_size(button_size) + .tint(icon_color) + .rounding(button_rounding); + + if ui + .add(egui::ImageButton::new(play_img)) + .on_hover_text("Play") + .clicked() + { + if let Some(midi_file) = self.midi_file.as_mut() { + midi_file.timer_mut().play(); + } + } + } + + ui.add_space(SPACE); + ui.separator(); + ui.add_space(SPACE); + + let mut time_passed = 0.0; + let mut time_total = 0.0; + + if let Some(midi) = self.midi_file.as_ref() { + time_total = midi.midi_length().unwrap_or(0.0); + time_passed = midi.timer().get_time().as_secs_f64(); + } + + let mut timeid = ui + .style() + .text_styles + .get(&egui::TextStyle::Monospace) + .unwrap() + .clone(); + timeid.size = 16.0; + let time_text = convert_seconds_to_time_string(time_passed); + let time_galley = ui.painter().layout_no_wrap( + time_text.clone(), + timeid.clone(), + egui::Color32::WHITE, + ); + + let length_text = convert_seconds_to_time_string(time_total); + let length_galley = ui.painter().layout_no_wrap( + length_text.clone(), + timeid.clone(), + egui::Color32::WHITE, + ); + + ui.spacing_mut().slider_width = ui.available_width() + - time_galley.size().x + - length_galley.size().x + - ui.spacing().item_spacing.x * 8.0 + - button_size.x * 2.0 + - ui.spacing().button_padding.x * 2.0 + - SPACE; + + ui.label(egui::RichText::new(time_text).font(timeid.clone())); + let mut empty_slider = + || ui.add(egui::Slider::new(&mut 0.0, 0.0..=1.0).show_value(false)); + if let Some(midi_file) = self.midi_file.as_mut() { + if let Some(length) = midi_file.midi_length() { + let mut time = midi_file.timer().get_time().as_secs_f64(); + let time_prev = time; + + ui.add( + egui::Slider::new(&mut time, 0.0..=length).show_value(false), + ); + if (time_prev != time) + && (midi_file.allows_seeking_backward() || time_prev < time) + { + midi_file.timer_mut().seek(Duration::from_secs_f64(time)); + } + } else { + empty_slider(); + } + } else { + empty_slider(); + } + ui.label(egui::RichText::new(length_text).font(timeid.clone())); + + ui.add_space(SPACE); + ui.separator(); + ui.add_space(SPACE); + + let options_img = + egui::Image::new(egui::include_image!("../../../assets/options.svg")) + .fit_to_exact_size(button_size) + .tint(icon_color) + .rounding(button_rounding); + + let options = ui.add(egui::ImageButton::new(options_img)); + + if options.clicked() { + ui.memory_mut(|mem| mem.toggle_popup(state.panel_popup_id)); + } + popup_below_widget( + ui, + state.panel_popup_id, + &options, + PopupCloseBehavior::CloseOnClick, + |ui| { + ui.set_min_width(130.0); + + if ui.button("Settings").clicked() { + state.show_settings = true; + } + if ui.button("Shortcuts").clicked() { + state.show_shortcuts = true; + } + if ui.button("About").clicked() { + state.show_about = true; + } + }, + ); + + let arrow_img = + egui::Image::new(egui::include_image!("../../../assets/pin.svg")) + .fit_to_exact_size(button_size) + .tint(icon_color) + .rounding(button_rounding); + + if ui + .add(egui::ImageButton::new(arrow_img).selected(state.panel_pinned)) + .on_hover_text("Pin Panel") + .clicked() + { + state.panel_pinned = !state.panel_pinned; + } + }); + }, + ); + } +} diff --git a/src/gui/window/scene.rs b/src/gui/window/scene.rs index c78fb28..9a35c4a 100644 --- a/src/gui/window/scene.rs +++ b/src/gui/window/scene.rs @@ -1,7 +1,7 @@ mod cake_system; mod note_list_system; -use egui::Ui; +use egui::{Image, Ui}; use crate::{ midi::{MIDIColor, MIDIFileUnion}, @@ -97,7 +97,8 @@ impl GuiRenderScene { .draw(key_view, frame, file, view_range), }; - ui.image(scene_image.id, [size[0] as f32, size[1] as f32]); + let img = Image::new((scene_image.id, [size[0] as f32, size[1] as f32].into())); + ui.add(img); result } diff --git a/src/gui/window/scene/cake_system/mod.rs b/src/gui/window/scene/cake_system/mod.rs index d6779ab..6c54300 100644 --- a/src/gui/window/scene/cake_system/mod.rs +++ b/src/gui/window/scene/cake_system/mod.rs @@ -5,23 +5,29 @@ use vulkano::{ buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer}, command_buffer::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, - RenderPassBeginInfo, SubpassContents, + RenderPassBeginInfo, SubpassBeginInfo, SubpassContents, }, descriptor_set::{ allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet, }, device::{Device, Queue}, format::Format, - image::{view::ImageView, AttachmentImage, ImageAccess, ImageViewAbstract}, - memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, + image::{view::ImageView, Image, ImageCreateInfo, ImageUsage}, + memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}, pipeline::{ graphics::{ + color_blend::{ColorBlendAttachmentState, ColorBlendState}, depth_stencil::DepthStencilState, input_assembly::{InputAssemblyState, PrimitiveTopology}, - vertex_input::Vertex, + multisample::MultisampleState, + rasterization::RasterizationState, + vertex_input::{Vertex, VertexDefinition}, viewport::{Viewport, ViewportState}, + GraphicsPipelineCreateInfo, }, - GraphicsPipeline, Pipeline, PipelineBindPoint, + layout::PipelineDescriptorSetLayoutCreateInfo, + GraphicsPipeline, Pipeline, PipelineBindPoint, PipelineLayout, + PipelineShaderStageCreateInfo, }, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, sync::{self, GpuFuture}, @@ -73,18 +79,18 @@ impl BufferSet { fn add_buffer( &mut self, - allocator: &StandardMemoryAllocator, + allocator: Arc, block: &CakeBlock, _key: &KeyPosition, ) { let data = Buffer::from_iter( - allocator, + allocator.clone(), BufferCreateInfo { usage: BufferUsage::STORAGE_BUFFER, ..Default::default() }, AllocationCreateInfo { - usage: MemoryUsage::Upload, + memory_type_filter: MemoryTypeFilter::PREFER_HOST, ..Default::default() }, block.tree.iter().copied(), @@ -110,8 +116,8 @@ pub struct CakeRenderer { buffers: BufferSet, pipeline_clear: Arc, render_pass_clear: Arc, - allocator: StandardMemoryAllocator, - depth_buffer: Arc>, + allocator: Arc, + depth_buffer: Arc, cb_allocator: StandardCommandBufferAllocator, sd_allocator: StandardDescriptorSetAllocator, buffers_init: Subbuffer<[CakeNoteColumn]>, @@ -120,23 +126,25 @@ pub struct CakeRenderer { impl CakeRenderer { pub fn new(renderer: &GuiRenderer) -> CakeRenderer { - let allocator = StandardMemoryAllocator::new_default(renderer.device.clone()); + let allocator = Arc::new(StandardMemoryAllocator::new_default( + renderer.device.clone(), + )); let gfx_queue = renderer.queue.clone(); let render_pass_clear = vulkano::ordered_passes_renderpass!(gfx_queue.device().clone(), attachments: { final_color: { - load: Clear, - store: Store, format: renderer.format, samples: 1, + load_op: Clear, + store_op: Store, }, depth: { - load: Clear, - store: Store, format: Format::D16_UNORM, samples: 1, + load_op: Clear, + store_op: Store, } }, passes: [ @@ -150,38 +158,93 @@ impl CakeRenderer { .unwrap(); let depth_buffer = ImageView::new_default( - AttachmentImage::transient_input_attachment(&allocator, [1, 1], Format::D16_UNORM) - .unwrap(), + Image::new( + allocator.clone(), + ImageCreateInfo { + extent: [1, 1, 1], + format: Format::D16_UNORM, + usage: ImageUsage::SAMPLED, + ..Default::default() + }, + Default::default(), + ) + .unwrap(), ) .unwrap(); - let vs = vs::load(gfx_queue.device().clone()).expect("failed to create shader module"); - let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module"); - let gs = gs::load(gfx_queue.device().clone()).expect("failed to create shader module"); - - let pipeline_base = GraphicsPipeline::start() - .input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::PointList)) - .vertex_input_state(CakeNoteColumn::per_vertex()) - .vertex_shader(vs.entry_point("main").unwrap(), ()) - .fragment_shader(fs.entry_point("main").unwrap(), ()) - .geometry_shader(gs.entry_point("main").unwrap(), ()) - .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .depth_stencil_state(DepthStencilState::simple_depth_test()); - - let pipeline_clear = pipeline_base - .clone() - .render_pass(Subpass::from(render_pass_clear.clone(), 0).unwrap()) - .build(gfx_queue.device().clone()) + let vs = vs::load(gfx_queue.device().clone()) + .expect("failed to create shader module") + .entry_point("main") + .unwrap(); + let fs = fs::load(gfx_queue.device().clone()) + .expect("failed to create shader module") + .entry_point("main") + .unwrap(); + let gs = gs::load(gfx_queue.device().clone()) + .expect("failed to create shader module") + .entry_point("main") .unwrap(); + let vertex_input_state = CakeNoteColumn::per_vertex() + .definition(&vs.info().input_interface) + .unwrap(); + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + PipelineShaderStageCreateInfo::new(gs), + ]; + let layout = PipelineLayout::new( + renderer.device.clone(), + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) + .into_pipeline_layout_create_info(renderer.device.clone()) + .unwrap(), + ) + .unwrap(); + let subpass = Subpass::from(render_pass_clear.clone(), 0).unwrap(); + + let pipeline_clear = GraphicsPipeline::new( + renderer.device.clone(), + None, + GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState { + topology: PrimitiveTopology::PointList, + ..Default::default() + }), + viewport_state: Some(ViewportState { + viewports: [Viewport { + offset: [0.0, 0.0], + extent: [1280.0, 720.0], + depth_range: 0.0..=1.0, + }] + .into_iter() + .collect(), + ..Default::default() + }), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.num_color_attachments(), + ColorBlendAttachmentState::default(), + )), + depth_stencil_state: Some(DepthStencilState::default()), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }, + ) + .unwrap(); + let buffers = Buffer::new_slice( - &allocator, + allocator.clone(), BufferCreateInfo { - usage: BufferUsage::VERTEX_BUFFER, + usage: BufferUsage::STORAGE_BUFFER + | BufferUsage::TRANSFER_DST + | BufferUsage::VERTEX_BUFFER, ..Default::default() }, AllocationCreateInfo { - usage: MemoryUsage::Upload, + memory_type_filter: MemoryTypeFilter::PREFER_DEVICE, ..Default::default() }, BUFFER_ARRAY_LEN, @@ -199,7 +262,10 @@ impl CakeRenderer { renderer.device.clone(), Default::default(), ), - sd_allocator: StandardDescriptorSetAllocator::new(renderer.device.clone()), + sd_allocator: StandardDescriptorSetAllocator::new( + renderer.device.clone(), + Default::default(), + ), buffers_init: buffers, current_file_signature: None, } @@ -208,19 +274,19 @@ impl CakeRenderer { pub fn draw( &mut self, key_view: &KeyboardView, - final_image: Arc, + final_image: Arc, midi_file: &mut CakeMIDIFile, view_range: f64, ) -> RenderResultData { - let img_dims = final_image.image().dimensions().width_height(); - if self.depth_buffer.image().dimensions().width_height() != img_dims { + let img_dims = final_image.image().extent(); + if self.depth_buffer.image().extent() != img_dims { + let mut create_info: ImageCreateInfo = Default::default(); + create_info.extent = [img_dims[0], img_dims[1], 1]; + create_info.format = Format::D16_UNORM; + create_info.usage = ImageUsage::SAMPLED; + self.depth_buffer = ImageView::new_default( - AttachmentImage::transient_input_attachment( - &self.allocator, - img_dims, - Format::D16_UNORM, - ) - .unwrap(), + Image::new(self.allocator.clone(), create_info, Default::default()).unwrap(), ) .unwrap(); } @@ -231,7 +297,7 @@ impl CakeRenderer { self.buffers.clear(); for (i, block) in midi_file.key_blocks().iter().enumerate() { let key = key_view.key(i); - self.buffers.add_buffer(&self.allocator, block, &key); + self.buffers.add_buffer(self.allocator.clone(), block, &key); } } @@ -247,7 +313,7 @@ impl CakeRenderer { }; let border_width = crate::utils::calculate_border_width( - final_image.image().dimensions().width() as f32, + final_image.image().extent()[0] as f32, key_view.visible_range.len() as f32, ) as i32; @@ -318,41 +384,53 @@ impl CakeRenderer { 0, self.buffers.buffers.iter().map(|b| b.data.clone()), )], + [], ) .unwrap(); + let mut subpassbegininfo = SubpassBeginInfo::default(); + subpassbegininfo.contents = SubpassContents::Inline; + command_buffer_builder .begin_render_pass( RenderPassBeginInfo { clear_values: clears, ..RenderPassBeginInfo::framebuffer(framebuffer) }, - SubpassContents::Inline, + subpassbegininfo, ) .unwrap(); command_buffer_builder .bind_pipeline_graphics(pipeline.clone()) + .unwrap() .set_viewport( 0, - [Viewport { - origin: [0.0, 0.0], - dimensions: [img_dims[0] as f32, img_dims[1] as f32], - depth_range: 0.0..1.0, - }], + vec![Viewport { + offset: [0.0, 0.0], + extent: [img_dims[0] as f32, img_dims[1] as f32], + depth_range: 0.0..=1.0, + }] + .into(), ) + .unwrap() .push_constants(pipeline_layout.clone(), 0, push_constants) + .unwrap() .bind_descriptor_sets( PipelineBindPoint::Graphics, pipeline_layout.clone(), 0, data_descriptor, ) + .unwrap() .bind_vertex_buffers(0, self.buffers_init.clone()) + .unwrap() .draw(written_instances as u32, 1, 0, 0) .unwrap(); - command_buffer_builder.end_render_pass().unwrap(); + command_buffer_builder + .end_render_pass(Default::default()) + .unwrap(); let command_buffer = command_buffer_builder.build().unwrap(); let now = sync::now(self.gfx_queue.device().clone()).boxed(); diff --git a/src/gui/window/scene/note_list_system/mod.rs b/src/gui/window/scene/note_list_system/mod.rs index 29a270e..f5cceaf 100644 --- a/src/gui/window/scene/note_list_system/mod.rs +++ b/src/gui/window/scene/note_list_system/mod.rs @@ -3,7 +3,7 @@ mod notes_render_pass; use std::{cell::UnsafeCell, sync::Arc}; use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator}; -use vulkano::image::ImageViewAbstract; +use vulkano::image::view::ImageView; use crate::{ gui::{window::keyboard_layout::KeyboardView, GuiRenderer}, @@ -47,7 +47,7 @@ impl NoteRenderer { pub fn draw( &mut self, key_view: &KeyboardView, - final_image: Arc, + final_image: Arc, midi_file: &mut impl MIDIFile, view_range: f64, ) -> RenderResultData { @@ -69,7 +69,7 @@ impl NoteRenderer { let mut columns_view_info = Vec::new(); let border_width = utils::calculate_border_width( - final_image.dimensions().width() as f32, + final_image.image().extent()[0] as f32, key_view.visible_range.len() as f32, ); diff --git a/src/gui/window/scene/note_list_system/notes_render_pass.rs b/src/gui/window/scene/note_list_system/notes_render_pass.rs index 9d8ff72..38f7778 100644 --- a/src/gui/window/scene/note_list_system/notes_render_pass.rs +++ b/src/gui/window/scene/note_list_system/notes_render_pass.rs @@ -5,23 +5,30 @@ use vulkano::{ buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer}, command_buffer::{ allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, - RenderPassBeginInfo, SubpassContents, + RenderPassBeginInfo, SubpassBeginInfo, SubpassContents, }, descriptor_set::{ allocator::StandardDescriptorSetAllocator, PersistentDescriptorSet, WriteDescriptorSet, }, device::{Device, Queue}, format::Format, - image::{view::ImageView, AttachmentImage, ImageAccess, ImageViewAbstract}, - memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, + image::{view::ImageView, Image, ImageCreateInfo, ImageUsage}, + memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}, pipeline::{ graphics::{ + color_blend::{ColorBlendAttachmentState, ColorBlendState}, depth_stencil::DepthStencilState, input_assembly::{InputAssemblyState, PrimitiveTopology}, - vertex_input::Vertex, + multisample::MultisampleState, + rasterization::RasterizationState, + subpass::PipelineSubpassType, + vertex_input::{Vertex, VertexDefinition}, viewport::{Viewport, ViewportState}, + GraphicsPipelineCreateInfo, }, - GraphicsPipeline, Pipeline, PipelineBindPoint, + layout::PipelineDescriptorSetLayoutCreateInfo, + GraphicsPipeline, Pipeline, PipelineBindPoint, PipelineLayout, + PipelineShaderStageCreateInfo, }, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, sync::{self, future::FenceSignalFuture, GpuFuture}, @@ -58,16 +65,16 @@ struct BufferSet { } fn get_buffer(device: &Arc) -> (Subbuffer<[NoteVertex]>, Subbuffer<[NoteVertex]>) { - let allocator = StandardMemoryAllocator::new_default(device.clone()); + let allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); Buffer::new_slice( - &allocator, + allocator.clone(), BufferCreateInfo { usage: BufferUsage::VERTEX_BUFFER, ..Default::default() }, AllocationCreateInfo { - usage: MemoryUsage::Upload, + memory_type_filter: MemoryTypeFilter::PREFER_HOST, ..Default::default() }, NOTE_BUFFER_SIZE * 2, @@ -113,31 +120,33 @@ pub struct NoteRenderPass { render_pass_clear: Arc, render_pass_draw_over: Arc, key_locations: Subbuffer<[[KeyPosition; 256]]>, - depth_buffer: Arc>, - allocator: StandardMemoryAllocator, + depth_buffer: Arc, + allocator: Arc, cb_allocator: StandardCommandBufferAllocator, sd_allocator: StandardDescriptorSetAllocator, } impl NoteRenderPass { pub fn new(renderer: &GuiRenderer) -> NoteRenderPass { - let allocator = StandardMemoryAllocator::new_default(renderer.device.clone()); + let allocator = Arc::new(StandardMemoryAllocator::new_default( + renderer.device.clone(), + )); let gfx_queue = renderer.queue.clone(); let render_pass_clear = vulkano::ordered_passes_renderpass!(gfx_queue.device().clone(), attachments: { final_color: { - load: Clear, - store: Store, format: renderer.format, samples: 1, + load_op: Clear, + store_op: Store, }, depth: { - load: Clear, - store: Store, format: Format::D16_UNORM, samples: 1, + load_op: Clear, + store_op: Store, } }, passes: [ @@ -153,16 +162,16 @@ impl NoteRenderPass { let render_pass_draw_over = vulkano::ordered_passes_renderpass!(gfx_queue.device().clone(), attachments: { final_color: { - load: DontCare, - store: Store, format: renderer.format, samples: 1, + load_op: DontCare, + store_op: Store, }, depth: { - load: DontCare, - store: Store, format: Format::D16_UNORM, samples: 1, + load_op: DontCare, + store_op: Store, } }, passes: [ @@ -176,48 +185,102 @@ impl NoteRenderPass { .unwrap(); let depth_buffer = ImageView::new_default( - AttachmentImage::transient_input_attachment(&allocator, [1, 1], Format::D16_UNORM) - .unwrap(), + Image::new( + allocator.clone(), + ImageCreateInfo { + extent: [1, 1, 1], + format: Format::D16_UNORM, + usage: ImageUsage::SAMPLED, + ..Default::default() + }, + Default::default(), + ) + .unwrap(), ) .unwrap(); let key_locations = Buffer::from_iter( - &allocator, + allocator.clone(), BufferCreateInfo { - usage: BufferUsage::UNIFORM_BUFFER, + usage: BufferUsage::STORAGE_BUFFER + | BufferUsage::TRANSFER_DST + | BufferUsage::VERTEX_BUFFER, ..Default::default() }, AllocationCreateInfo { - usage: MemoryUsage::Upload, + memory_type_filter: MemoryTypeFilter::PREFER_HOST, ..Default::default() }, [[Default::default(); 256]], ) .unwrap(); - let vs = vs::load(gfx_queue.device().clone()).expect("failed to create shader module"); - let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module"); - let gs = gs::load(gfx_queue.device().clone()).expect("failed to create shader module"); - - let pipeline_base = GraphicsPipeline::start() - .input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::PointList)) - .vertex_input_state(NoteVertex::per_vertex()) - .vertex_shader(vs.entry_point("main").unwrap(), ()) - .geometry_shader(gs.entry_point("main").unwrap(), ()) - .fragment_shader(fs.entry_point("main").unwrap(), ()) - .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .depth_stencil_state(DepthStencilState::simple_depth_test()); - - let pipeline_clear = pipeline_base - .clone() - .render_pass(Subpass::from(render_pass_clear.clone(), 0).unwrap()) - .build(gfx_queue.device().clone()) + let vs = vs::load(gfx_queue.device().clone()) + .expect("failed to create shader module") + .entry_point("main") + .unwrap(); + let fs = fs::load(gfx_queue.device().clone()) + .expect("failed to create shader module") + .entry_point("main") + .unwrap(); + let gs = gs::load(gfx_queue.device().clone()) + .expect("failed to create shader module") + .entry_point("main") .unwrap(); - let pipeline_draw_over = pipeline_base - .render_pass(Subpass::from(render_pass_draw_over.clone(), 0).unwrap()) - .build(gfx_queue.device().clone()) + let vertex_input_state = NoteVertex::per_vertex() + .definition(&vs.info().input_interface) .unwrap(); + let stages = [ + PipelineShaderStageCreateInfo::new(vs), + PipelineShaderStageCreateInfo::new(fs), + PipelineShaderStageCreateInfo::new(gs), + ]; + let layout = PipelineLayout::new( + renderer.device.clone(), + PipelineDescriptorSetLayoutCreateInfo::from_stages(&stages) + .into_pipeline_layout_create_info(renderer.device.clone()) + .unwrap(), + ) + .unwrap(); + let subpass = Subpass::from(render_pass_clear.clone(), 0).unwrap(); + + let mut create_info = GraphicsPipelineCreateInfo { + stages: stages.into_iter().collect(), + vertex_input_state: Some(vertex_input_state), + input_assembly_state: Some(InputAssemblyState { + topology: PrimitiveTopology::PointList, + ..Default::default() + }), + viewport_state: Some(ViewportState { + viewports: [Viewport { + offset: [0.0, 0.0], + extent: [1280.0, 720.0], + depth_range: 0.0..=1.0, + }] + .into_iter() + .collect(), + ..Default::default() + }), + rasterization_state: Some(RasterizationState::default()), + multisample_state: Some(MultisampleState::default()), + color_blend_state: Some(ColorBlendState::with_attachment_states( + subpass.num_color_attachments(), + ColorBlendAttachmentState::default(), + )), + depth_stencil_state: Some(DepthStencilState::default()), + subpass: Some(subpass.into()), + ..GraphicsPipelineCreateInfo::layout(layout) + }; + + let pipeline_clear = + GraphicsPipeline::new(renderer.device.clone(), None, create_info.clone()).unwrap(); + + create_info.subpass = Some(PipelineSubpassType::BeginRenderPass( + Subpass::from(render_pass_draw_over.clone(), 0).unwrap(), + )); + let pipeline_draw_over = + GraphicsPipeline::new(renderer.device.clone(), None, create_info).unwrap(); NoteRenderPass { gfx_queue, @@ -233,24 +296,32 @@ impl NoteRenderPass { renderer.device.clone(), Default::default(), ), - sd_allocator: StandardDescriptorSetAllocator::new(renderer.device.clone()), + sd_allocator: StandardDescriptorSetAllocator::new( + renderer.device.clone(), + Default::default(), + ), } } pub fn draw( &mut self, - final_image: Arc, + final_image: Arc, key_view: &KeyboardView, view_range: f32, mut fill_buffer: impl FnMut(&Subbuffer<[NoteVertex]>) -> NotePassStatus, ) { - let img_dims = final_image.image().dimensions().width_height(); - if self.depth_buffer.image().dimensions().width_height() != img_dims { + let img_dims = final_image.image().extent(); + if self.depth_buffer.image().extent() != img_dims { self.depth_buffer = ImageView::new_default( - AttachmentImage::transient_input_attachment( - &self.allocator, - img_dims, - Format::D16_UNORM, + Image::new( + self.allocator.clone(), + ImageCreateInfo { + extent: [img_dims[0], img_dims[1], 1], + format: Format::D16_UNORM, + usage: ImageUsage::SAMPLED, + ..Default::default() + }, + Default::default(), ) .unwrap(), ) @@ -321,20 +392,25 @@ impl NoteRenderPass { let pipeline_layout = pipeline.layout(); let desc_layout = pipeline_layout.set_layouts().first().unwrap(); + let write_descriptor_set = WriteDescriptorSet::buffer(0, self.key_locations.clone()); let set = PersistentDescriptorSet::new( &self.sd_allocator, desc_layout.clone(), - [WriteDescriptorSet::buffer(0, self.key_locations.clone())], + [write_descriptor_set], + [], ) .unwrap(); + let mut subpassbegininfo = SubpassBeginInfo::default(); + subpassbegininfo.contents = SubpassContents::Inline; + command_buffer_builder .begin_render_pass( RenderPassBeginInfo { clear_values: clears, ..RenderPassBeginInfo::framebuffer(framebuffer) }, - SubpassContents::Inline, + subpassbegininfo, ) .unwrap(); @@ -346,26 +422,34 @@ impl NoteRenderPass { command_buffer_builder .bind_pipeline_graphics(pipeline.clone()) + .unwrap() .set_viewport( 0, - [Viewport { - origin: [0.0, 0.0], - dimensions: [img_dims[0] as f32, img_dims[1] as f32], - depth_range: 0.0..1.0, - }], + vec![Viewport { + offset: [0.0, 0.0], + extent: [img_dims[0] as f32, img_dims[1] as f32], + depth_range: 0.0..=1.0, + }] + .into(), ) + .unwrap() .push_constants(pipeline_layout.clone().clone(), 0, push_constants) + .unwrap() .bind_descriptor_sets( PipelineBindPoint::Graphics, pipeline_layout.clone(), 0, set.clone(), ) + .unwrap() .bind_vertex_buffers(0, buffer.clone()) + .unwrap() .draw(items_to_render, 1, 0, 0) .unwrap(); - command_buffer_builder.end_render_pass().unwrap(); + command_buffer_builder + .end_render_pass(Default::default()) + .unwrap(); let command_buffer = command_buffer_builder.build().unwrap(); if let Some(prev_future) = prev_future.take() { diff --git a/src/gui/window/settings.rs b/src/gui/window/settings.rs new file mode 100644 index 0000000..a651360 --- /dev/null +++ b/src/gui/window/settings.rs @@ -0,0 +1,181 @@ +use std::{ + path::PathBuf, + sync::{Arc, RwLock}, +}; + +use soundfonts::EguiSFList; + +use crate::{ + audio_playback::WasabiAudioPlayer, + settings::WasabiSettings, + state::{SettingsTab, WasabiState}, +}; + +mod midi; +mod soundfonts; +mod synth; +mod visual; + +const CATEG_SPACE: f32 = 12.0; +const SPACING: [f32; 2] = [40.0, 12.0]; + +#[derive(Clone)] +struct FilePalette { + pub path: PathBuf, + pub selected: bool, +} + +#[derive(Clone)] +struct MidiDevice { + pub name: String, + pub selected: bool, +} + +pub struct SettingsWindow { + palettes: Vec, + midi_devices: Vec, + sf_list: EguiSFList, +} + +impl SettingsWindow { + pub fn new(settings: &WasabiSettings) -> Self { + let mut sf_list = EguiSFList::new(); + for sf in settings.synth.soundfonts.iter() { + sf_list.add_item(sf.clone()).unwrap_or_default(); + } + + Self { + palettes: Vec::new(), + midi_devices: Vec::new(), + sf_list, + } + } + + pub fn show( + &mut self, + ctx: &egui::Context, + settings: &mut WasabiSettings, + state: &mut WasabiState, + synth: Arc>, + ) { + let frame = + egui::Frame::inner_margin(egui::Frame::window(ctx.style().as_ref()), super::WIN_MARGIN); + + egui::Window::new("Settings") + .resizable(true) + .collapsible(false) + .title_bar(true) + .anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0]) + .movable(false) + .enabled(true) + .frame(frame) + .default_size([600.0, 400.0]) + .min_size([500.0, 200.0]) + .open(&mut state.show_settings) + .show(ctx, |ui| { + egui::TopBottomPanel::top("settings_tab_selector") + .resizable(false) + .show_inside(ui, |ui| { + ui.style_mut() + .text_styles + .get_mut(&egui::TextStyle::Button) + .unwrap() + .size = 20.0; + + ui.horizontal(|ui| { + ui.selectable_value( + &mut state.settings_tab, + SettingsTab::Visual, + "\u{1f4bb} Visual", + ); + ui.selectable_value( + &mut state.settings_tab, + SettingsTab::Midi, + "\u{1f3b5} MIDI", + ); + ui.selectable_value( + &mut state.settings_tab, + SettingsTab::Synth, + "\u{1f3b9} Synth", + ); + ui.selectable_value( + &mut state.settings_tab, + SettingsTab::SoundFonts, + "\u{1f50a} SoundFonts", + ); + }); + ui.add_space(4.0); + }); + + egui::TopBottomPanel::bottom("settings_save_panel") + .resizable(false) + .show_inside(ui, |ui| { + ui.add_space(4.0); + ui.centered_and_justified(|ui| { + if ui.button("Save").clicked() { + settings.save_to_file(); + } + }); + }); + + let width = ui.available_width() - 40.0; + egui::CentralPanel::default().show_inside(ui, |ui| { + egui::ScrollArea::vertical().animated(true).show(ui, |ui| { + match state.settings_tab { + SettingsTab::Visual => self.show_visual_settings(ui, settings, width), + SettingsTab::Midi => self.show_midi_settings(ui, settings, width), + SettingsTab::Synth => { + self.show_synth_settings(ui, settings, width, synth) + } + SettingsTab::SoundFonts => { + self.show_soundfont_settings(ui, settings, width, synth) + } + } + }) + }); + }); + } + + pub fn load_palettes(&mut self) { + self.palettes.clear(); + + let files = std::fs::read_dir(WasabiSettings::get_palettes_dir()).unwrap(); + + for file in files.filter_map(|i| i.ok()) { + if let Ok(ftype) = file.file_type() { + if ftype.is_file() { + self.palettes.push(FilePalette { + path: file.path(), + selected: false, + }); + } + } + } + } + + pub fn load_midi_devices(&mut self, settings: &mut WasabiSettings) { + self.midi_devices.clear(); + if let Ok(con) = midir::MidiOutput::new("wasabi") { + let ports = con.ports(); + for port in ports.iter() { + if let Ok(name) = con.port_name(&port) { + self.midi_devices.push(MidiDevice { + name, + selected: false, + }); + } + } + } + + let saved = settings.synth.midi_device.clone(); + if let Some(found) = self.midi_devices.iter_mut().find(|d| d.name == saved) { + found.selected = true; + return; + } + + if !self.midi_devices.is_empty() { + self.midi_devices[0].selected = true; + settings.synth.midi_device = self.midi_devices[0].name.clone(); + } + } +} diff --git a/src/gui/window/settings/midi.rs b/src/gui/window/settings/midi.rs new file mode 100644 index 0000000..5eeea9c --- /dev/null +++ b/src/gui/window/settings/midi.rs @@ -0,0 +1,149 @@ +use egui_extras::{Column, TableBuilder}; + +use crate::settings::{Colors, MidiParsing, WasabiSettings}; + +use super::SettingsWindow; + +impl SettingsWindow { + pub fn show_midi_settings( + &mut self, + ui: &mut egui::Ui, + settings: &mut WasabiSettings, + width: f32, + ) { + ui.heading("Settings"); + egui::Grid::new("midi_settings_grid") + .num_columns(2) + .spacing(super::SPACING) + .striped(true) + .min_col_width(width / 2.0) + .show(ui, |ui| { + ui.horizontal(|ui| { + ui.label("MIDI Parsing Algorithm:"); + ui.monospace("\u{2139}").on_hover_text( + "\ + - Cake\n\ + \0 The most efficient loading and displaying algorithm.\n\ + \0 The notes will be stored in binary trees and will be\n\ + \0 displayed dynamically.\n\ + - Standard (RAM)\n\ + \0 The MIDI will be loaded in the RAM and all the notes\n\ + \0 will be rendered normally by the GPU.\n\ + - Standard (Live)\n\ + \0 The MIDI will be streamed live from the disk and all\n\ + \0 the notes will be rendered normally by the GPU.\ + ", + ); + }); + egui::ComboBox::from_id_salt("midi_parsing_select") + .selected_text(settings.midi.parsing.as_str()) + .show_ui(ui, |ui| { + ui.selectable_value( + &mut settings.midi.parsing, + MidiParsing::Cake, + MidiParsing::Cake.as_str(), + ); + ui.selectable_value( + &mut settings.midi.parsing, + MidiParsing::Ram, + MidiParsing::Ram.as_str(), + ); + ui.selectable_value( + &mut settings.midi.parsing, + MidiParsing::Live, + MidiParsing::Live.as_str(), + ); + }); + ui.end_row(); + }); + ui.vertical_centered(|ui| { + ui.small("Changes to this setting will be applied when a new MIDI is loaded."); + }); + + ui.horizontal(|ui| ui.add_space(width + 40.0)); + ui.add_space(super::CATEG_SPACE); + ui.heading("Color Palette"); + egui::Frame::default() + .rounding(egui::Rounding::same(8.0)) + .stroke(ui.style().visuals.widgets.noninteractive.bg_stroke) + .show(ui, |ui| { + TableBuilder::new(ui) + .striped(true) + .cell_layout(egui::Layout::centered_and_justified( + egui::Direction::LeftToRight, + )) + .resizable(true) + .column(Column::exact(width).resizable(false)) + .body(|mut body| { + let row_height = super::CATEG_SPACE * 3.0; + body.row(row_height, |mut row| { + row.col(|ui| { + if ui + .selectable_label( + settings.midi.colors == Colors::Rainbow, + Colors::Rainbow.as_str(), + ) + .clicked() + { + settings.midi.colors = Colors::Rainbow; + } + }); + }); + body.row(row_height, |mut row| { + row.col(|ui| { + if ui + .selectable_label( + settings.midi.colors == Colors::Random, + Colors::Random.as_str(), + ) + .clicked() + { + settings.midi.colors = Colors::Random; + } + }); + }); + let mut temp = self.palettes.clone(); + for i in temp.iter_mut() { + i.selected = false; + } + let mut changed = false; + for (i, palette) in self.palettes.iter_mut().enumerate() { + body.row(row_height, |mut row| { + row.col(|ui| { + if ui + .selectable_label( + settings.midi.colors == Colors::Palette + && palette.selected, + palette + .path + .file_name() + .unwrap_or_default() + .to_str() + .unwrap_or_default(), + ) + .clicked() + { + settings.midi.colors = Colors::Palette; + temp[i].selected = true; + settings.midi.palette_path = palette.path.clone(); + changed = true; + } + }); + }); + } + if changed { + self.palettes = temp; + } + }); + }); + ui.add_space(4.0); + ui.horizontal(|ui| { + if ui.button("Refresh List").clicked() { + self.load_palettes(); + } + if ui.button("Open Palettes Directory").clicked() { + open::that(WasabiSettings::get_palettes_dir()).unwrap_or_default(); + } + }); + } +} diff --git a/src/gui/window/settings/soundfonts.rs b/src/gui/window/settings/soundfonts.rs new file mode 100644 index 0000000..c28f2ba --- /dev/null +++ b/src/gui/window/settings/soundfonts.rs @@ -0,0 +1,22 @@ +use std::sync::{Arc, RwLock}; + +use crate::{audio_playback::WasabiAudioPlayer, settings::WasabiSettings}; + +use super::SettingsWindow; + +mod list; +pub use list::*; +mod cfg; +pub use cfg::*; + +impl SettingsWindow { + pub fn show_soundfont_settings( + &mut self, + ui: &mut egui::Ui, + settings: &mut WasabiSettings, + width: f32, + synth: Arc>, + ) { + self.sf_list.show(ui, settings, width, synth); + } +} diff --git a/src/gui/window/settings/soundfonts/cfg.rs b/src/gui/window/settings/soundfonts/cfg.rs new file mode 100644 index 0000000..a183d62 --- /dev/null +++ b/src/gui/window/settings/soundfonts/cfg.rs @@ -0,0 +1,122 @@ +use crate::gui::window::WIN_MARGIN; + +use super::{SFFormat, SFListItem}; +use egui::{Context, Window}; +use xsynth_core::soundfont::Interpolator; + +#[derive(Clone)] +pub struct SoundfontConfigWindow { + pub visible: bool, + id: usize, +} + +impl SoundfontConfigWindow { + pub fn new(id: usize) -> Self { + Self { visible: true, id } + } + + pub fn id(&self) -> usize { + self.id + } + + pub fn show(&mut self, ctx: &Context, item: &mut SFListItem) { + let title = if let Some(path) = item.item.path.file_name() { + format!("Config for {path:?}") + } else { + format!("Config for {}", self.id) + }; + + let frame = + egui::Frame::inner_margin(egui::Frame::window(ctx.style().as_ref()), WIN_MARGIN); + + Window::new(title) + .id(egui::Id::new(self.id)) + .collapsible(false) + .title_bar(true) + .enabled(true) + .frame(frame) + .open(&mut self.visible) + .show(ctx, |ui| { + let col_width = 80.0; + + ui.heading("Instrument"); + ui.separator(); + egui::Grid::new("sfconfig_window_instr") + .num_columns(2) + .min_col_width(col_width) + .show(ui, |ui| { + let mut modify = item.item.options.bank.is_some(); + + ui.label("Override Instrument: "); + let allow_override = !(item.format == SFFormat::Sfz); + ui.add_enabled(allow_override, egui::Checkbox::without_text(&mut modify)); + ui.end_row(); + + if modify && item.item.options.bank.is_none() { + item.item.options.bank = Some(0); + item.item.options.preset = Some(0); + } else if !modify { + item.item.options.bank = None; + item.item.options.preset = None; + } + + let mut bank = item.item.options.bank.unwrap_or(0); + + ui.label("Bank: "); + ui.add_enabled( + modify, + egui::DragValue::new(&mut bank).speed(1).range(0..=128), + ); + ui.end_row(); + + if bank != item.item.options.bank.unwrap_or(0) { + item.item.options.bank = Some(bank) + } + + let mut preset = item.item.options.preset.unwrap_or(0); + + ui.label("Preset: "); + ui.add_enabled( + modify, + egui::DragValue::new(&mut preset).speed(1).range(0..=127), + ); + ui.end_row(); + + if preset != item.item.options.preset.unwrap_or(0) { + item.item.options.preset = Some(preset) + } + }); + + ui.heading("Settings"); + ui.separator(); + egui::Grid::new("sfconfig_window_settings") + .num_columns(2) + .min_col_width(col_width) + .show(ui, |ui| { + // ui.label("Linear Release Envelope: "); + // ui.checkbox(&mut item.init.linear_release, ""); + // ui.end_row(); + + let interp = ["Nearest Neighbor", "Linear"]; + let mut interp_idx = item.item.options.interpolator as usize; + + ui.label("Interpolation:"); + egui::ComboBox::from_id_salt("interpolation").show_index( + ui, + &mut interp_idx, + interp.len(), + |i| interp[i].to_owned(), + ); + ui.end_row(); + + if interp_idx != item.item.options.interpolator as usize { + match interp_idx { + 0 => item.item.options.interpolator = Interpolator::Nearest, + 1 => item.item.options.interpolator = Interpolator::Linear, + _ => item.item.options.interpolator = Interpolator::Nearest, + }; + } + }); + }); + } +} diff --git a/src/gui/window/settings/soundfonts/list.rs b/src/gui/window/settings/soundfonts/list.rs new file mode 100644 index 0000000..6d0b943 --- /dev/null +++ b/src/gui/window/settings/soundfonts/list.rs @@ -0,0 +1,391 @@ +use std::{ + path::PathBuf, + sync::{Arc, RwLock}, + thread, +}; + +use crossbeam_channel::{Receiver, Sender}; +use egui::WidgetText; +use egui_extras::{Column, TableBuilder}; +use serde::{Deserialize, Serialize}; + +use crate::{ + audio_playback::WasabiAudioPlayer, + settings::{WasabiSettings, WasabiSoundfont}, +}; + +use super::SoundfontConfigWindow; + +#[derive(Default, Clone, PartialEq, Serialize, Deserialize)] +pub enum SFFormat { + #[default] + Sfz, + Sf2, +} + +#[derive(Default, Clone, Serialize, Deserialize)] +#[serde(default)] +pub struct SFListItem { + pub item: WasabiSoundfont, + pub id: usize, + pub selected: bool, + pub format: SFFormat, +} + +pub struct EguiSFList { + list: Vec, + id_count: usize, + + sf_picker: (Sender, Receiver), + sf_cfg_win: Vec, +} + +impl EguiSFList { + pub fn new() -> Self { + let sf_picker = crossbeam_channel::unbounded(); + + Self { + list: Vec::new(), + id_count: 0, + sf_picker, + sf_cfg_win: Vec::new(), + } + } + + pub fn add_item(&mut self, sf: WasabiSoundfont) -> Result<(), String> { + let err = Err(format!( + "The selected soundfont does not have the correct format: {:?}", + sf.path + )); + + if let Some(ext) = sf.path.extension() { + let format = match ext.to_str().unwrap().to_lowercase().as_str() { + "sfz" => SFFormat::Sfz, + "sf2" => SFFormat::Sf2, + _ => return err, + }; + + let item = SFListItem { + item: sf, + id: self.id_count, + selected: false, + format, + }; + self.list.push(item); + self.id_count += 1; + return Ok(()); + } + + err + } + + pub fn add_path(&mut self, path: PathBuf) -> Result<(), String> { + if !path.exists() { + return Err(format!("File not found: {:?}", path)); + } + + let item = WasabiSoundfont { + path, + enabled: true, + options: Default::default(), + }; + + self.add_item(item) + } + + pub fn select_all(&mut self) { + self.list = self + .list + .clone() + .into_iter() + .map(|mut item| { + item.selected = true; + item + }) + .collect(); + } + + pub fn remove_selected_items(&mut self) { + self.list = self + .list + .clone() + .into_iter() + .filter(|item| !item.selected) + .collect(); + + // I'm bored to make it close only the windows needed, so instead I'll close all of them + self.sf_cfg_win.clear(); + } + + pub fn clear(&mut self) { + self.list.clear(); + self.sf_cfg_win.clear(); + } + + pub fn as_vec(&self) -> Vec { + self.list.iter().map(|sf| sf.item.clone()).collect() + } + + pub fn show( + &mut self, + ui: &mut egui::Ui, + settings: &mut WasabiSettings, + width: f32, + synth: Arc>, + ) { + let events = ui.input(|i| i.events.clone()); + for event in &events { + if let egui::Event::Key { + key, + modifiers, + pressed, + .. + } = event + { + match *key { + egui::Key::A => { + if *pressed && modifiers.ctrl { + self.select_all(); + } + } + egui::Key::Delete => { + self.remove_selected_items(); + } + _ => {} + } + } + } + + { + let recv = self.sf_picker.1.clone(); + if !recv.is_empty() { + for path in recv { + //state.last_location = path.clone(); + if path.is_file() { + if let Err(error) = self.add_path(path.clone()) { + let title = if let Some(filen) = path.file_name() { + format!( + "There was an error adding \"{}\" to the list.", + filen.to_str().unwrap() + ) + } else { + "There was an error adding the selected soundfont to the list." + .to_string() + }; + // TODO: errors + } + } + break; + } + } + } + + // if !ui.input(|i| i.raw.dropped_files.is_empty()) { + // let dropped_files = ui.input(|i| { + // i.raw + // .dropped_files + // .clone() + // .iter() + // .map(|file| file.path.as_ref().unwrap().clone()) + // .collect::>() + // }); + + // for file in dropped_files { + // if let Err(error) = self.add_item(file.clone()) { + // let title = if let Some(filen) = file.file_name() { + // // Not a safe unwrap but things must be very wrong for it to panic so idc + // format!( + // "There was an error adding \"{}\" to the list.", + // filen.to_str().unwrap() + // ) + // } else { + // "There was an error adding the selected soundfont to the list.".to_string() + // }; + // // TODO: errors + // } + // } + // } + + self.sf_cfg_win = self + .sf_cfg_win + .clone() + .into_iter() + .filter(|item| item.visible) + .collect(); + + for cfg in self.sf_cfg_win.iter_mut() { + let index = self.list.iter().position(|item| item.id == cfg.id()); + if let Some(index) = index { + cfg.show(ui.ctx(), &mut self.list[index]); + } + } + + egui::TopBottomPanel::bottom("bottom_panel") + .resizable(false) + .show_inside(ui, |ui| { + ui.add_space(5.0); + ui.horizontal(|ui| { + if ui + .button( + WidgetText::from(" \u{2795} ") + .text_style(egui::TextStyle::Name("monospace big".into())), + ) + .on_hover_text("Add SoundFont(s)") + .clicked() + { + let sender = self.sf_picker.0.clone(); + //let last_location = state.last_location.clone(); + + thread::spawn(move || { + let midi_path = rfd::FileDialog::new() + .add_filter("Supported SoundFonts", &["sfz", "SFZ", "sf2", "SF2"]) + //.set_directory(last_location.parent().unwrap_or(Path::new("./"))) + .pick_file(); + + if let Some(midi_path) = midi_path { + sender.send(midi_path).unwrap_or_default(); + } + }); + } + if ui + .button( + WidgetText::from(" \u{2796} ") + .text_style(egui::TextStyle::Name("monospace big".into())), + ) + .on_hover_text("Remove Selected") + .clicked() + { + self.remove_selected_items(); + } + if ui + .button( + WidgetText::from(" \u{2716} ") + .text_style(egui::TextStyle::Name("monospace big".into())), + ) + .on_hover_text("Clear List") + .clicked() + { + self.clear(); + } + if ui + .button( + WidgetText::from(" \u{1F503} ") + .text_style(egui::TextStyle::Name("monospace big".into())), + ) + .on_hover_text("Apply SoundFont List") + .clicked() + { + synth + .write() + .unwrap() + .set_soundfonts(&settings.synth.soundfonts); + } + }); + ui.small("Loading order is top to bottom. Supported formats: SFZ, SF2"); + }); + + egui::ScrollArea::both().show(ui, |ui| { + let events = ui.input(|i| i.events.clone()); + for event in &events { + if let egui::Event::Key { + key, + modifiers, + pressed, + .. + } = event + { + match *key { + egui::Key::A => { + if *pressed && modifiers.ctrl { + self.select_all(); + } + } + egui::Key::Delete => { + self.remove_selected_items(); + } + _ => {} + } + } + } + + TableBuilder::new(ui) + .striped(true) + .cell_layout(egui::Layout::centered_and_justified( + egui::Direction::LeftToRight, + )) + .resizable(true) + .column(Column::exact(20.0).resizable(false)) + .column(Column::initial(width - 200.0).at_least(50.0).clip(true)) + .columns(Column::auto().at_least(40.0).clip(true), 2) + .column(Column::auto().at_least(40.0).clip(true).resizable(false)) + .header(20.0, |mut header| { + header.col(|_ui| {}); + header.col(|ui| { + ui.strong("Filename"); + }); + header.col(|ui| { + ui.strong("Format"); + }); + header.col(|ui| { + ui.strong("Bank"); + }); + header.col(|ui| { + ui.strong("Preset"); + }); + }) + .body(|mut body| { + let row_height = super::super::CATEG_SPACE * 3.0; + for item in self.list.iter_mut() { + body.row(row_height, |mut row| { + row.col(|ui| { + ui.checkbox(&mut item.item.enabled, ""); + }); + row.col(|ui| { + let selectable = if let Some(path) = item.item.path.to_str() { + ui.selectable_label(item.selected, path) + } else { + ui.selectable_label(item.selected, "error") + }; + + if selectable.clicked() { + item.selected = !item.selected; + } + if selectable.double_clicked() + && !self.sf_cfg_win.iter().any(|cfg| cfg.id() == item.id) + { + self.sf_cfg_win.push(SoundfontConfigWindow::new(item.id)) + } + }); + row.col(|ui| { + ui.label(match item.format { + SFFormat::Sfz => "SFZ", + SFFormat::Sf2 => "SF2", + }); + }); + + let bank_txt = if let Some(bank) = item.item.options.bank { + format!("{}", bank) + } else { + "None".to_owned() + }; + row.col(|ui| { + ui.label(bank_txt.to_string()); + }); + + let preset_txt = if let Some(preset) = item.item.options.preset { + format!("{}", preset) + } else { + "None".to_owned() + }; + row.col(|ui| { + ui.label(preset_txt.to_string()); + }); + }); + } + }); + ui.allocate_space(ui.available_size()); + }); + + settings.synth.soundfonts = self.as_vec(); + } +} diff --git a/src/gui/window/settings/synth.rs b/src/gui/window/settings/synth.rs new file mode 100644 index 0000000..1cc8c09 --- /dev/null +++ b/src/gui/window/settings/synth.rs @@ -0,0 +1,96 @@ +use std::sync::{Arc, RwLock}; + +use crate::{ + audio_playback::WasabiAudioPlayer, + gui::window::GuiWasabiWindow, + settings::{Synth, WasabiSettings}, +}; + +use super::SettingsWindow; + +mod kdmapi; +mod mididevice; +mod xsynth; + +impl SettingsWindow { + pub fn show_synth_settings( + &mut self, + ui: &mut egui::Ui, + settings: &mut WasabiSettings, + width: f32, + synth: Arc>, + ) { + egui::Grid::new("synth_settings_grid") + .num_columns(2) + .spacing(super::SPACING) + .striped(true) + .min_col_width(width / 2.0) + .show(ui, |ui| { + let synth_prev = settings.synth.synth; + ui.label("Synthesizer:"); + ui.horizontal(|ui| { + egui::ComboBox::from_id_salt("synth_select") + .selected_text(settings.synth.synth.as_str()) + .show_ui(ui, |ui| { + ui.selectable_value( + &mut settings.synth.synth, + Synth::XSynth, + Synth::XSynth.as_str(), + ); + ui.selectable_value( + &mut settings.synth.synth, + Synth::Kdmapi, + Synth::Kdmapi.as_str(), + ); + ui.selectable_value( + &mut settings.synth.synth, + Synth::MidiDevice, + Synth::MidiDevice.as_str(), + ); + ui.selectable_value( + &mut settings.synth.synth, + Synth::None, + Synth::None.as_str(), + ); + }); + + if ui + .button( + egui::WidgetText::from(" \u{1F503} ") + .text_style(egui::TextStyle::Name("monospace big".into())), + ) + .on_hover_text("Reload Synth") + .clicked() + { + synth + .write() + .unwrap() + .switch(GuiWasabiWindow::create_synth(settings)); + } + }); + ui.end_row(); + + if settings.synth.synth != synth_prev { + let new_player = GuiWasabiWindow::create_synth(settings); + synth.write().unwrap().switch(new_player); + } + }); + + ui.add_space(8.0); + ui.vertical_centered(|ui| { + ui.small("Options marked with (*) will apply when the synth is reloaded."); + }); + + ui.add_space(super::CATEG_SPACE); + ui.heading("Synth Settings"); + + match settings.synth.synth { + Synth::XSynth => self.show_xsynth_settings(ui, settings, width, synth), + Synth::Kdmapi => self.show_kdmapi_settings(ui, settings, width), + Synth::MidiDevice => self.show_mididevice_settings(ui, settings, width, synth), + Synth::None => { + ui.label("No Settings"); + } + } + } +} diff --git a/src/gui/window/settings/synth/kdmapi.rs b/src/gui/window/settings/synth/kdmapi.rs new file mode 100644 index 0000000..0a95268 --- /dev/null +++ b/src/gui/window/settings/synth/kdmapi.rs @@ -0,0 +1,23 @@ +use crate::settings::WasabiSettings; + +use super::SettingsWindow; + +impl SettingsWindow { + pub fn show_kdmapi_settings( + &mut self, + ui: &mut egui::Ui, + settings: &mut WasabiSettings, + width: f32, + ) { + egui::Grid::new("kdmapi_settings_grid") + .num_columns(2) + .spacing(super::super::SPACING) + .striped(true) + .min_col_width(width / 2.0) + .show(ui, |ui| { + ui.label("Use the driver's soundfont list*:"); + ui.checkbox(&mut settings.synth.kdmapi.use_om_sflist, ""); + ui.end_row(); + }); + } +} diff --git a/src/gui/window/settings/synth/mididevice.rs b/src/gui/window/settings/synth/mididevice.rs new file mode 100644 index 0000000..4f2a0fa --- /dev/null +++ b/src/gui/window/settings/synth/mididevice.rs @@ -0,0 +1,66 @@ +use std::sync::{Arc, RwLock}; + +use egui_extras::{Column, TableBuilder}; + +use crate::{ + audio_playback::WasabiAudioPlayer, gui::window::GuiWasabiWindow, settings::WasabiSettings, +}; + +use super::SettingsWindow; + +impl SettingsWindow { + pub fn show_mididevice_settings( + &mut self, + ui: &mut egui::Ui, + settings: &mut WasabiSettings, + width: f32, + synth: Arc>, + ) { + egui::Frame::default() + .rounding(egui::Rounding::same(8.0)) + .stroke(ui.style().visuals.widgets.noninteractive.bg_stroke) + .show(ui, |ui| { + TableBuilder::new(ui) + .striped(true) + .cell_layout(egui::Layout::centered_and_justified( + egui::Direction::LeftToRight, + )) + .resizable(true) + .column(Column::exact(width).resizable(false)) + .body(|mut body| { + let row_height = super::super::CATEG_SPACE * 3.0; + + let mut temp = self.midi_devices.clone(); + for i in temp.iter_mut() { + i.selected = false; + } + let mut changed = false; + for (i, device) in self.midi_devices.iter_mut().enumerate() { + body.row(row_height, |mut row| { + row.col(|ui| { + if ui + .selectable_label(device.selected, device.name.clone()) + .clicked() + { + temp[i].selected = true; + settings.synth.midi_device = device.name.clone(); + changed = true; + } + }); + }); + } + if changed { + self.midi_devices = temp; + synth + .write() + .unwrap() + .switch(GuiWasabiWindow::create_synth(settings)); + } + }); + }); + ui.add_space(4.0); + if ui.button("Refresh List").clicked() { + self.load_midi_devices(settings); + } + } +} diff --git a/src/gui/window/settings/synth/xsynth.rs b/src/gui/window/settings/synth/xsynth.rs new file mode 100644 index 0000000..05fc5c6 --- /dev/null +++ b/src/gui/window/settings/synth/xsynth.rs @@ -0,0 +1,102 @@ +use std::sync::{Arc, RwLock}; + +use xsynth_realtime::ThreadCount; + +use crate::{audio_playback::WasabiAudioPlayer, settings::WasabiSettings}; + +use super::SettingsWindow; + +impl SettingsWindow { + pub fn show_xsynth_settings( + &mut self, + ui: &mut egui::Ui, + settings: &mut WasabiSettings, + width: f32, + synth: Arc>, + ) { + egui::Grid::new("xsynth_settings_grid") + .num_columns(2) + .spacing(super::super::SPACING) + .striped(true) + .min_col_width(width / 2.0) + .show(ui, |ui| { + let layer_limit_prev = settings.synth.xsynth.limit_layers; + + ui.label("Enable Layer Limiting: "); + ui.checkbox(&mut settings.synth.xsynth.limit_layers, ""); + ui.end_row(); + + let layer_count_prev = settings.synth.xsynth.layers; + + ui.horizontal(|ui| { + ui.label("Layer Limit:"); + ui.monospace("\u{2139}") + .on_hover_text("One layer is one voice per key per channel."); + }); + ui.add_enabled( + settings.synth.xsynth.limit_layers, + egui::DragValue::new(&mut settings.synth.xsynth.layers) + .speed(1) + .range(1..=usize::MAX), + ); + ui.end_row(); + + if settings.synth.xsynth.layers != layer_count_prev + || layer_limit_prev != settings.synth.xsynth.limit_layers + { + synth.write().unwrap().configure(&settings.synth); + } + + let buffer_prev = settings.synth.xsynth.config.render_window_ms; + ui.label("Render Buffer (ms):"); + ui.add( + egui::DragValue::new(&mut settings.synth.xsynth.config.render_window_ms) + .speed(0.1) + .range(0.0001..=1000.0), + ); + ui.end_row(); + if settings.synth.xsynth.config.render_window_ms != buffer_prev { + synth.write().unwrap().configure(&settings.synth); + } + + ui.label("Ignore velocities between:"); + let mut lovel = *settings.synth.xsynth.config.ignore_range.start(); + let mut hivel = *settings.synth.xsynth.config.ignore_range.end(); + ui.horizontal(|ui| { + ui.add(egui::DragValue::new(&mut lovel).speed(1).range(0..=127)); + ui.label("and"); + ui.add(egui::DragValue::new(&mut hivel).speed(1).range(lovel..=127)); + }); + ui.end_row(); + if lovel != *settings.synth.xsynth.config.ignore_range.start() + || hivel != *settings.synth.xsynth.config.ignore_range.end() + { + settings.synth.xsynth.config.ignore_range = lovel..=hivel; + synth.write().unwrap().configure(&settings.synth); + } + + ui.label("Fade out voice when killing it*: "); + ui.checkbox( + &mut settings + .synth + .xsynth + .config + .channel_init_options + .fade_out_killing, + "", + ); + ui.end_row(); + + let mut threading = + settings.synth.xsynth.config.multithreading == ThreadCount::Auto; + ui.label("Enable multithreading*: "); + ui.checkbox(&mut threading, ""); + ui.end_row(); + if threading { + settings.synth.xsynth.config.multithreading = ThreadCount::Auto; + } else { + settings.synth.xsynth.config.multithreading = ThreadCount::None; + } + }); + } +} diff --git a/src/gui/window/settings/visual.rs b/src/gui/window/settings/visual.rs new file mode 100644 index 0000000..6ba4e00 --- /dev/null +++ b/src/gui/window/settings/visual.rs @@ -0,0 +1,175 @@ +use egui::WidgetText; +use egui_extras::{Column, TableBuilder}; + +use crate::settings::WasabiSettings; + +use super::SettingsWindow; + +impl SettingsWindow { + pub fn show_visual_settings( + &mut self, + ui: &mut egui::Ui, + settings: &mut WasabiSettings, + width: f32, + ) { + ui.heading("General"); + egui::Grid::new("general_visual_settings_grid") + .num_columns(2) + .spacing(super::SPACING) + .striped(true) + .min_col_width(width / 2.0) + .show(ui, |ui| { + ui.label("Check for updates on launch:"); + ui.checkbox(&mut settings.gui.check_for_updates, ""); + ui.end_row(); + + ui.label("FPS Limit:"); + ui.add( + egui::DragValue::new(&mut settings.gui.fps_limit) + .speed(1) + .range(0..=usize::MAX), + ); + ui.end_row(); + + ui.label("Skip Control:"); + ui.add( + egui::DragValue::new(&mut settings.gui.skip_control) + .speed(0.5) + .range(0.0..=f64::MAX), + ); + ui.end_row(); + + ui.label("Speed Control:"); + ui.add( + egui::DragValue::new(&mut settings.gui.speed_control) + .speed(0.5) + .range(0.0..=f64::MAX), + ); + ui.end_row(); + }); + + ui.add_space(super::CATEG_SPACE); + ui.heading("Scene"); + + egui::Grid::new("scene_visual_settings_grid") + .num_columns(2) + .spacing(super::SPACING) + .striped(true) + .min_col_width(width / 2.0) + .show(ui, |ui| { + ui.label("Background Color: "); + ui.color_edit_button_srgba(&mut settings.scene.bg_color); + ui.end_row(); + + ui.label("Bar Color: "); + ui.color_edit_button_srgba(&mut settings.scene.bar_color); + ui.end_row(); + + ui.label("Keyboard Range: "); + let mut firstkey = *settings.scene.key_range.start(); + let mut lastkey = *settings.scene.key_range.end(); + ui.horizontal(|ui| { + ui.add(egui::DragValue::new(&mut firstkey).speed(1).range(0..=253)); + ui.add( + egui::DragValue::new(&mut lastkey) + .speed(1) + .range(firstkey + 1..=254), + ); + }); + ui.end_row(); + if firstkey != *settings.scene.key_range.start() + || lastkey != *settings.scene.key_range.end() + { + settings.scene.key_range = firstkey..=lastkey; + } + + ui.label("Note Speed: "); + ui.spacing_mut().slider_width = width / 2.0 - 100.0; + ui.add( + egui::Slider::new(&mut settings.scene.note_speed, 20.0..=0.0001) + .logarithmic(true), + ); + ui.end_row(); + }); + + ui.add_space(super::CATEG_SPACE); + ui.heading("Statistics"); + + egui::Grid::new("stats_visual_settings_grid") + .num_columns(2) + .spacing(super::SPACING) + .striped(true) + .min_col_width(width / 2.0) + .show(ui, |ui| { + ui.label("Floating:"); + ui.checkbox(&mut settings.scene.statistics.floating, ""); + ui.end_row(); + + ui.label("Border:"); + ui.checkbox(&mut settings.scene.statistics.border, ""); + ui.end_row(); + + ui.label("Background Opacity: "); + ui.spacing_mut().slider_width = width / 2.0 - 100.0; + ui.add(egui::Slider::new( + &mut settings.scene.statistics.opacity, + 0.0..=1.0, + )); + ui.end_row(); + }); + + ui.add_space(4.0); + egui::Frame::default() + .rounding(egui::Rounding::same(8.0)) + .stroke(ui.style().visuals.widgets.noninteractive.bg_stroke) + .show(ui, |ui| { + TableBuilder::new(ui) + .striped(true) + .cell_layout(egui::Layout::centered_and_justified( + egui::Direction::LeftToRight, + )) + .resizable(true) + .column(Column::exact(40.0).resizable(false)) + .column(Column::exact(width - 100.0).resizable(false)) + .column(Column::exact(60.0).resizable(false)) + .body(|mut body| { + let row_height = super::CATEG_SPACE * 3.0; + let mut temp = settings.scene.statistics.order.clone(); + for (i, item) in settings.scene.statistics.order.iter_mut().enumerate() { + body.row(row_height, |mut row| { + row.col(|ui| { + ui.checkbox(&mut temp[i].1, ""); + }); + row.col(|ui| { + ui.label(item.0.as_str()); + }); + row.col(|ui| { + ui.horizontal(|ui| { + if ui + .button(WidgetText::from("\u{2191}").text_style( + egui::TextStyle::Name("monospace big".into()), + )) + .clicked() + && i > 0 + { + temp.swap(i, i - 1); + } + + if ui + .button(WidgetText::from("\u{2193}").text_style( + egui::TextStyle::Name("monospace big".into()), + )) + .clicked() + && i < temp.len() - 1 + { + temp.swap(i, i + 1); + } + }); + }); + }); + } + settings.scene.statistics.order = temp; + }); + }); + } +} diff --git a/src/gui/window/settings_window.rs b/src/gui/window/settings_window.rs deleted file mode 100644 index f345483..0000000 --- a/src/gui/window/settings_window.rs +++ /dev/null @@ -1,186 +0,0 @@ -use egui::Context; - -use std::ops::RangeInclusive; - -use crate::{ - audio_playback::{ - xsynth::{convert_to_channel_init, convert_to_sf_init}, - AudioPlayerType, - }, - gui::window::GuiWasabiWindow, - settings::{MidiLoading, Synth, WasabiSettings}, - state::WasabiState, -}; - -pub fn draw_settings( - win: &mut GuiWasabiWindow, - settings: &mut WasabiSettings, - state: &mut WasabiState, - ctx: &Context, -) { - egui::Window::new("Settings") - .resizable(true) - .collapsible(true) - .title_bar(true) - .scroll2([false, true]) - .enabled(true) - .open(&mut state.settings_visible) - .show(ctx, |ui| { - let col_width = 160.0; - - // Synth settings section - ui.heading("Synth"); - ui.separator(); - - egui::Grid::new("synth_settings_grid") - .num_columns(2) - .spacing([40.0, 4.0]) - .min_col_width(col_width) - .show(ui, |ui| { - ui.label("Synth: "); - let synth_prev = settings.synth.synth; - egui::ComboBox::from_id_source("synth_select") - .selected_text(settings.synth.synth.as_str()) - .show_ui(ui, |ui| { - ui.selectable_value(&mut settings.synth.synth, Synth::XSynth, "XSynth"); - ui.selectable_value(&mut settings.synth.synth, Synth::Kdmapi, "KDMAPI"); - }); - if settings.synth.synth != synth_prev { - match settings.synth.synth { - Synth::Kdmapi => { - win.synth - .write() - .unwrap() - .switch_player(AudioPlayerType::Kdmapi); - } - Synth::XSynth => { - win.synth - .write() - .unwrap() - .switch_player(AudioPlayerType::XSynth { - buffer: settings.synth.buffer_ms, - use_threadpool: settings.synth.use_threadpool, - ignore_range: settings.synth.vel_ignore.clone(), - options: convert_to_channel_init(settings), - }); - win.synth.write().unwrap().set_soundfont( - &settings.synth.sfz_path, - convert_to_sf_init(settings), - ); - win.synth.write().unwrap().set_layer_count( - match settings.synth.layer_count { - 0 => None, - _ => Some(settings.synth.layer_count), - }, - ); - } - } - } - ui.end_row(); - - ui.label("Configure:"); - if ui.button("Open Synth Settings").clicked() { - state.xsynth_settings_visible = true; - } - ui.end_row(); - }); - - // MIDI settings section - ui.add_space(6.0); - ui.heading("MIDI"); - ui.separator(); - - egui::Grid::new("midi_settings_grid") - .num_columns(2) - .spacing([40.0, 4.0]) - .min_col_width(col_width) - .show(ui, |ui| { - ui.label("Note speed: "); - ui.spacing_mut().slider_width = 150.0; - ui.add(egui::Slider::new( - &mut settings.midi.note_speed, - 2.0..=0.001, - )); - ui.end_row(); - - ui.label("Random Track Colors*: "); - ui.checkbox(&mut settings.midi.random_colors, ""); - ui.end_row(); - - ui.label("Keyboard Range: "); - let mut firstkey = *settings.midi.key_range.start(); - let mut lastkey = *settings.midi.key_range.end(); - ui.horizontal(|ui| { - ui.add( - egui::DragValue::new(&mut firstkey) - .speed(1) - .clamp_range(RangeInclusive::new(0, 253)), - ); - ui.add( - egui::DragValue::new(&mut lastkey) - .speed(1) - .clamp_range(RangeInclusive::new(firstkey + 1, 254)), - ); - }); - ui.end_row(); - if firstkey != *settings.midi.key_range.start() - || lastkey != *settings.midi.key_range.end() - { - settings.midi.key_range = firstkey..=lastkey; - } - - ui.label("MIDI Loading*: "); - egui::ComboBox::from_id_source("midiload_select") - .selected_text(settings.midi.midi_loading.as_str()) - .show_ui(ui, |ui| { - ui.selectable_value( - &mut settings.midi.midi_loading, - MidiLoading::Ram, - "In RAM", - ); - ui.selectable_value( - &mut settings.midi.midi_loading, - MidiLoading::Live, - "Live", - ); - ui.selectable_value( - &mut settings.midi.midi_loading, - MidiLoading::Cake, - "Cake", - ); - }); - }); - - // Visual settings section - ui.add_space(6.0); - ui.heading("Visual"); - ui.separator(); - - egui::Grid::new("visual_settings_grid") - .num_columns(2) - .spacing([40.0, 4.0]) - .min_col_width(col_width) - .show(ui, |ui| { - if ui.button("Toggle Fullscreen").clicked() { - state.fullscreen = true; - } - ui.end_row(); - - ui.label("Background Color: "); - ui.color_edit_button_srgba(&mut settings.visual.bg_color); - ui.end_row(); - - ui.label("Bar Color: "); - ui.color_edit_button_srgba(&mut settings.visual.bar_color); - ui.end_row(); - }); - - ui.separator(); - ui.vertical_centered(|ui| { - ui.label("Options marked with (*) will apply when a new MIDI is loaded."); - if ui.button("Save").clicked() { - settings.save_to_file(); - } - }); - }); -} diff --git a/src/gui/window/shortcuts.rs b/src/gui/window/shortcuts.rs new file mode 100644 index 0000000..0d08cd7 --- /dev/null +++ b/src/gui/window/shortcuts.rs @@ -0,0 +1,60 @@ +use crate::state::WasabiState; + +use super::GuiWasabiWindow; + +impl GuiWasabiWindow { + pub fn show_shortcuts(&mut self, ctx: &egui::Context, state: &mut WasabiState) { + let frame = + egui::Frame::inner_margin(egui::Frame::window(ctx.style().as_ref()), super::WIN_MARGIN); + let size = [400.0, 210.0]; + + egui::Window::new("Keyboard Shortcuts") + .collapsible(false) + .title_bar(true) + .scroll([false, true]) + .enabled(true) + .frame(frame) + .fixed_size(size) + .open(&mut state.show_shortcuts) + .show(ctx, |ui| { + let col_width = size[0] / 2.0; + egui::Grid::new("shortcuts_grid") + .num_columns(2) + .min_col_width(col_width) + .striped(true) + .show(ui, |ui| { + ui.label("Play / Pause Playback"); + ui.label("Space"); + ui.end_row(); + + ui.label("Skip Forward"); + ui.label("Right Arrow"); + ui.end_row(); + + ui.label("Go Back"); + ui.label("Left Arrow"); + ui.end_row(); + + ui.label("Slower Note Speed"); + ui.label("Ctrl + Up Arrow"); + ui.end_row(); + + ui.label("Faster Note Speed"); + ui.label("Ctrl + Down Arrow"); + ui.end_row(); + + ui.label("Toggle Fullscreen"); + ui.label("Alt + Enter"); + ui.end_row(); + + ui.label("Toggle Panel"); + ui.label("Ctrl + F"); + ui.end_row(); + + ui.label("Toggle Statistics"); + ui.label("Ctrl + G"); + ui.end_row(); + }); + }); + } +} diff --git a/src/gui/window/stats.rs b/src/gui/window/stats.rs index 75c9ba9..2ac93d9 100644 --- a/src/gui/window/stats.rs +++ b/src/gui/window/stats.rs @@ -1,6 +1,11 @@ use egui::{Context, Frame, Pos2}; -use crate::{gui::window::GuiWasabiWindow, midi::MIDIFileBase}; +use crate::{ + gui::window::GuiWasabiWindow, + midi::{MIDIFileBase, MIDIFileStats}, + settings::{Statistics, WasabiSettings}, + utils::convert_seconds_to_time_string, +}; pub struct GuiMidiStats { time_passed: f64, @@ -28,37 +33,63 @@ impl GuiMidiStats { } } -pub fn draw_stats(win: &mut GuiWasabiWindow, ctx: &Context, pos: Pos2, mut stats: GuiMidiStats) { +fn num_or_q(num: Option) -> String { + if let Some(num) = num { + num.to_string() + } else { + "?".to_string() + } +} + +pub fn draw_stats( + win: &mut GuiWasabiWindow, + ctx: &Context, + pos: Pos2, + mut stats: GuiMidiStats, + settings: &WasabiSettings, +) { let onepx = ctx.pixels_per_point(); - let stats_frame = Frame::default() - .inner_margin(egui::style::Margin::same(7.0)) - .fill(egui::Color32::from_rgba_unmultiplied(7, 7, 7, 200)) - .stroke(egui::Stroke::new( + let opacity = settings.scene.statistics.opacity.clamp(0.0, 1.0); + let alpha = (u8::MAX as f32 * opacity).round() as u8; + + let round = 8.0; + + let mut stats_frame = Frame::default() + .inner_margin(egui::Margin::same(7.0)) + .fill(egui::Color32::from_rgba_unmultiplied(7, 7, 7, alpha)); + + if settings.scene.statistics.floating { + stats_frame = stats_frame.rounding(egui::Rounding::same(round)); + } else { + stats_frame = stats_frame.rounding(egui::Rounding { + ne: 0.0, + nw: 0.0, + sw: 0.0, + se: round, + }); + } + + if settings.scene.statistics.border { + stats_frame = stats_frame.stroke(egui::Stroke::new( onepx, egui::Color32::from_rgb(50, 50, 50), - )) - .rounding(egui::Rounding::same(6.0)); + )); + } egui::Window::new("Stats") .resizable(false) .collapsible(false) .title_bar(false) - .scroll2([false, false]) + .scroll([false, false]) .enabled(true) .frame(stats_frame) .fixed_pos(pos) .fixed_size(egui::Vec2::new(200.0, 128.0)) .show(ctx, |ui| { - let mut time_millis: i64 = 0; - let mut time_sec: i64 = 0; - let mut time_min: i64 = 0; - let mut length_millis: u64 = 0; - let mut length_sec: u64 = 0; - let mut length_min: u64 = 0; - - let mut note_stats = Default::default(); + ui.spacing_mut().interact_size.y = 16.0; + let mut note_stats = MIDIFileStats::default(); if let Some(midi_file) = win.midi_file.as_mut() { stats.time_total = midi_file.midi_length().unwrap_or(0.0); let time = midi_file.timer().get_time().as_seconds_f64(); @@ -73,69 +104,49 @@ pub fn draw_stats(win: &mut GuiWasabiWindow, ctx: &Context, pos: Pos2, mut stats stats.time_passed = time; } - time_millis = (stats.time_passed * 10.0) as i64 % 10; - time_sec = stats.time_passed as i64 % 60; - time_min = stats.time_passed as i64 / 60; - note_stats = midi_file.stats(); } - ui.horizontal(|ui| { - ui.monospace("Time:"); - ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { - ui.monospace(format!( - "{}{:0width$}:{:0width$}.{} / {:0width$}:{:0width$}.{}", - if time_sec + time_millis < 0 { - '-' - } else { - '\0' - }, - time_min.abs(), - time_sec.abs(), - time_millis.abs(), - length_min, - length_sec, - length_millis, - width = 2 - )); - }); - }); - - ui.horizontal(|ui| { - ui.monospace("FPS:"); - ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { - ui.monospace(format!("{}", win.fps.get_fps().round())); - }); - }); - - ui.horizontal(|ui| { - ui.monospace("Voice Count:"); - ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { - ui.monospace(format!("{}", stats.voice_count)); - }); - }); - - ui.horizontal(|ui| { - ui.monospace("Rendered:"); - ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { - ui.monospace(format!("{}", stats.notes_on_screen)); - }); - }); - - fn num_or_q(num: Option) -> String { - if let Some(num) = num { - num.to_string() - } else { - "?".to_string() - } + for i in settings.scene.statistics.order.iter().filter(|i| i.1) { + match i.0 { + Statistics::Time => ui.horizontal(|ui| { + ui.monospace("Time:"); + ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { + ui.monospace(format!( + "{} / {}", + convert_seconds_to_time_string(stats.time_passed), + convert_seconds_to_time_string(stats.time_total) + )); + }); + }), + Statistics::Fps => ui.horizontal(|ui| { + ui.monospace("FPS:"); + ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { + ui.monospace(format!("{}", win.fps.get_fps().round())); + }); + }), + Statistics::VoiceCount => ui.horizontal(|ui| { + ui.monospace("Voice Count:"); + ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { + ui.monospace(format!("{}", stats.voice_count)); + }); + }), + Statistics::Rendered => ui.horizontal(|ui| { + ui.monospace("Rendered:"); + ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { + ui.monospace(format!("{}", stats.notes_on_screen)); + }); + }), + Statistics::NoteCount => { + ui.with_layout(egui::Layout::top_down(egui::Align::Center), |ui| { + ui.monospace(format!( + "{} / {}", + num_or_q(note_stats.passed_notes), + num_or_q(note_stats.total_notes) + )); + }) + } + }; } - - ui.with_layout(egui::Layout::top_down(egui::Align::Center), |ui| { - ui.monospace(format!( - "{} / {}", - num_or_q(note_stats.passed_notes), - num_or_q(note_stats.total_notes) - )); - }); }); } diff --git a/src/gui/window/top_panel.rs b/src/gui/window/top_panel.rs deleted file mode 100644 index 2a42a51..0000000 --- a/src/gui/window/top_panel.rs +++ /dev/null @@ -1,91 +0,0 @@ -use egui::{Context, Frame}; - -use time::Duration; - -use crate::{ - gui::window::GuiWasabiWindow, midi::MIDIFileBase, settings::WasabiSettings, state::WasabiState, -}; - -pub fn draw_panel( - win: &mut GuiWasabiWindow, - settings: &mut WasabiSettings, - state: &mut WasabiState, - ctx: &Context, -) { - let panel_frame = Frame::default() - .inner_margin(egui::style::Margin::same(10.0)) - .fill(egui::Color32::from_rgb(42, 42, 42)); - - egui::TopBottomPanel::top("Top panel") - .frame(panel_frame) - .show_separator_line(false) - .show(ctx, |ui| { - ui.horizontal(|ui| { - if ui.button("Open").clicked() { - win.open_midi_dialog(settings, state); - } - - if let Some(midi_file) = win.midi_file.as_mut() { - if ui.button("Unload").clicked() { - midi_file.timer_mut().pause(); - win.synth.write().unwrap().reset(); - win.midi_file = None; - } - } - - ui.add_space(10.0); - - if ui.button("Settings").clicked() { - match state.settings_visible { - true => state.settings_visible = false, - false => state.settings_visible = true, - } - } - - ui.add_space(10.0); - - if ui.button("Play").clicked() { - if let Some(midi_file) = win.midi_file.as_mut() { - midi_file.timer_mut().play(); - } - } - if ui.button("Pause").clicked() { - if let Some(midi_file) = win.midi_file.as_mut() { - midi_file.timer_mut().pause(); - } - } - - ui.add_space(10.0); - - ui.horizontal(|ui| { - ui.label("Note speed: "); - ui.add( - egui::Slider::new(&mut settings.midi.note_speed, 2.0..=0.001) - .show_value(false), - ); - }) - }); - - ui.spacing_mut().slider_width = ctx.available_rect().width() - 20.0; - let mut empty_slider = - || ui.add(egui::Slider::new(&mut 0.0, 0.0..=1.0).show_value(false)); - if let Some(midi_file) = win.midi_file.as_mut() { - if let Some(length) = midi_file.midi_length() { - let mut time = midi_file.timer().get_time().as_seconds_f64(); - let time_prev = time; - - let start_delay = crate::midi::START_DELAY.as_seconds_f64(); - ui.add(egui::Slider::new(&mut time, -start_delay..=length).show_value(false)); - if (time_prev != time) - && (midi_file.allows_seeking_backward() || time_prev < time) - { - midi_file.timer_mut().seek(Duration::seconds_f64(time)); - } - } else { - empty_slider(); - } - } else { - empty_slider(); - } - }); -} diff --git a/src/gui/window/xsynth_settings.rs b/src/gui/window/xsynth_settings.rs deleted file mode 100644 index 7a12399..0000000 --- a/src/gui/window/xsynth_settings.rs +++ /dev/null @@ -1,174 +0,0 @@ -use egui::Context; - -use std::ops::RangeInclusive; - -use crate::{ - audio_playback::{ - xsynth::{convert_to_channel_init, convert_to_sf_init}, - AudioPlayerType, - }, - gui::window::GuiWasabiWindow, - settings::WasabiSettings, - state::WasabiState, -}; - -pub fn draw_xsynth_settings( - win: &mut GuiWasabiWindow, - settings: &mut WasabiSettings, - state: &mut WasabiState, - ctx: &Context, -) { - egui::Window::new("XSynth Settings") - .resizable(true) - .collapsible(true) - .title_bar(true) - .scroll2([false, true]) - .enabled(true) - .open(&mut state.xsynth_settings_visible) - .show(ctx, |ui| { - let col_width = 240.0; - - ui.heading("Synth"); - ui.separator(); - - egui::Grid::new("synth_settings_grid") - .num_columns(2) - .spacing([40.0, 4.0]) - .min_col_width(col_width) - .show(ui, |ui| { - ui.label("Synth Render Buffer (ms)*: "); - ui.add( - egui::DragValue::new(&mut settings.synth.buffer_ms) - .speed(0.1) - .clamp_range(RangeInclusive::new(0.001, 1000.0)), - ); - ui.end_row(); - - ui.label("SoundFont Path: "); - ui.horizontal(|ui| { - ui.add(egui::TextEdit::singleline(&mut settings.synth.sfz_path)); - - if ui.button("Browse...").clicked() { - // If windows, just use the native dialog - let sfz_path = rfd::FileDialog::new() - .add_filter("Supported SoundFonts", &["sfz", "sf2", "SF2"]) - .pick_file(); - - if let Some(sfz_path) = sfz_path { - if let Ok(path) = sfz_path.into_os_string().into_string() { - settings.synth.sfz_path = path; - } - } - } - - if ui.button("Load").clicked() { - win.synth.write().unwrap().set_soundfont( - &settings.synth.sfz_path, - convert_to_sf_init(settings), - ); - } - }); - ui.end_row(); - - ui.label("Limit Layers: "); - let layer_limit_prev = settings.synth.limit_layers; - ui.checkbox(&mut settings.synth.limit_layers, ""); - ui.end_row(); - - ui.label("Synth Layer Count: "); - let layer_count_prev = settings.synth.layer_count; - ui.add_enabled_ui(settings.synth.limit_layers, |ui| { - ui.add( - egui::DragValue::new(&mut settings.synth.layer_count) - .speed(1) - .clamp_range(RangeInclusive::new(1, 200)), - ); - }); - if settings.synth.layer_count != layer_count_prev - || layer_limit_prev != settings.synth.limit_layers - { - win.synth.write().unwrap().set_layer_count( - if settings.synth.limit_layers { - Some(settings.synth.layer_count) - } else { - None - }, - ); - } - ui.end_row(); - - ui.label("Ignore notes with velocities between*: "); - let mut lovel = *settings.synth.vel_ignore.start(); - let mut hivel = *settings.synth.vel_ignore.end(); - ui.horizontal(|ui| { - ui.add( - egui::DragValue::new(&mut lovel) - .speed(1) - .clamp_range(RangeInclusive::new(0, 127)), - ); - ui.label("and"); - ui.add( - egui::DragValue::new(&mut hivel) - .speed(1) - .clamp_range(RangeInclusive::new(lovel, 127)), - ); - }); - ui.end_row(); - if lovel != *settings.synth.vel_ignore.start() - || hivel != *settings.synth.vel_ignore.end() - { - settings.synth.vel_ignore = lovel..=hivel; - } - }); - - ui.add_space(6.0); - ui.heading("Engine"); - ui.separator(); - - egui::Grid::new("engine_settings_grid") - .num_columns(2) - .spacing([40.0, 4.0]) - .min_col_width(col_width) - .show(ui, |ui| { - ui.label("Fade out voice when killing it*: "); - ui.checkbox(&mut settings.synth.fade_out_kill, ""); - ui.end_row(); - - ui.label("Use Effects*: "); - ui.checkbox(&mut settings.synth.use_effects, ""); - ui.end_row(); - - ui.label("Use Threadpool*: "); - ui.checkbox(&mut settings.synth.use_threadpool, ""); - ui.end_row(); - }); - - ui.separator(); - ui.vertical_centered(|ui| { - ui.label("Options marked with (*) will apply when the synth is reloaded."); - if ui.button("Reload XSynth").clicked() { - win.synth - .write() - .unwrap() - .switch_player(AudioPlayerType::XSynth { - buffer: settings.synth.buffer_ms, - use_threadpool: settings.synth.use_threadpool, - ignore_range: settings.synth.vel_ignore.clone(), - options: convert_to_channel_init(settings), - }); - win.synth - .write() - .unwrap() - .set_soundfont(&settings.synth.sfz_path, convert_to_sf_init(settings)); - win.synth - .write() - .unwrap() - .set_layer_count(if settings.synth.limit_layers { - Some(settings.synth.layer_count) - } else { - None - }); - } - }); - }); -} diff --git a/src/main.rs b/src/main.rs index 4f0e8ea..a3c27b2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,13 +16,14 @@ use gui::{window::GuiWasabiWindow, GuiRenderer, GuiState}; use renderer::Renderer; use vulkano::swapchain::PresentMode; -use settings::WasabiSettings; -use state::WasabiState; -use winit::{ +use egui_winit::winit::{ dpi::{LogicalSize, Size}, event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, + event_loop::EventLoop, }; +use settings::WasabiSettings; +use state::WasabiState; +use winit::event_loop::ControlFlow; pub const WINDOW_SIZE: Size = Size::Logical(LogicalSize { width: 1280.0, @@ -34,34 +35,25 @@ pub const WAYLAND_PRESENT_MODE: PresentMode = PresentMode::Mailbox; pub fn main() { // Winit event loop - let event_loop = EventLoop::new(); - let monitor = event_loop - .available_monitors() - .next() - .expect("no monitor found!"); - - let mode = monitor.video_modes().next().expect("no mode found"); + let event_loop = EventLoop::new().unwrap(); + event_loop.set_control_flow(ControlFlow::Poll); // Load the settings values let mut settings = WasabiSettings::new_or_load(); + settings.save_to_file(); let mut wasabi_state = WasabiState::default(); // Create renderer for our scene & ui - let mut renderer = Renderer::new( - &event_loop, - "Wasabi", - settings.visual.fullscreen, - mode.clone(), - ); + let mut renderer = Renderer::new(&event_loop, "Wasabi"); // Vulkano & Winit & egui integration let mut gui = Gui::new( &event_loop, renderer.surface(), renderer.queue(), + renderer.format(), GuiConfig { is_overlay: true, - preferred_format: Some(renderer.format()), ..Default::default() }, ); @@ -75,62 +67,75 @@ pub fn main() { let mut gui_state = GuiWasabiWindow::new(&mut gui_render_data, &mut settings); - event_loop.run(move |event, _, control_flow| { - let device = renderer.device(); - let queue = renderer.queue(); - let format = renderer.format(); - - // Update Egui integration so the UI works! - match event { - Event::WindowEvent { event, window_id } if window_id == renderer.window().id() => { - let _pass_events_to_game = !gui.update(&event); - match event { - WindowEvent::Resized(size) => { - renderer.resize(Some(size)); + event_loop + .run(move |event, target| { + let device = renderer.device(); + let queue = renderer.queue(); + let format = renderer.format(); + + // Update Egui integration so the UI works! + match event { + Event::WindowEvent { event, window_id } if window_id == renderer.window().id() => { + let _pass_events_to_game = !gui.update(&event); + match event { + WindowEvent::Resized(size) => { + renderer.resize(Some(size)); + } + WindowEvent::ScaleFactorChanged { .. } => { + renderer.resize(None); + } + WindowEvent::CloseRequested => { + target.exit(); + } + WindowEvent::DroppedFile(path) => { + gui_state.load_midi(&mut settings, path); + } + WindowEvent::RedrawRequested => { + renderer.render(|frame, future| { + // Generate egui layouts + gui.immediate_ui(|gui| { + let mut gui_render_data = GuiRenderer { + gui, + device, + queue, + format, + }; + + let mut state = GuiState { + renderer: &mut gui_render_data, + frame, + }; + egui_extras::install_image_loaders( + &state.renderer.gui.context(), + ); + gui_state.layout(&mut state, &mut settings, &mut wasabi_state); + }); + + // Render the layouts + gui.draw_on_image(future, frame.image.clone()) + }); + } + _ => (), } - WindowEvent::ScaleFactorChanged { .. } => { - renderer.resize(None); - } - WindowEvent::CloseRequested => { - *control_flow = ControlFlow::Exit; - } - WindowEvent::DroppedFile(path) => { - gui_state.load_midi(&mut settings, path); - } - _ => (), } + Event::NewEvents(..) => { + renderer.window().request_redraw(); + } + _ => (), } - Event::RedrawRequested(_) => { - renderer.render(|frame, future| { - // Generate egui layouts - gui.immediate_ui(|gui| { - let mut gui_render_data = GuiRenderer { - gui, - device, - queue, - format, - }; - - let mut state = GuiState { - renderer: &mut gui_render_data, - frame, - }; - gui_state.layout(&mut state, &mut settings, &mut wasabi_state); - }); - - // Render the layouts - gui.draw_on_image(future, frame.image.clone()) - }); - } - Event::MainEventsCleared => { - renderer.window().request_redraw(); + + let mode = target + .available_monitors() + .next() + .unwrap() + .video_modes() + .next() + .unwrap(); + + if wasabi_state.fullscreen { + renderer.set_fullscreen(mode); + wasabi_state.fullscreen = false; } - _ => (), - } - - if wasabi_state.fullscreen { - renderer.set_fullscreen(mode.clone()); - wasabi_state.fullscreen = false; - } - }); + }) + .unwrap(); } diff --git a/src/midi/audio/live.rs b/src/midi/audio/live.rs index 7812d84..1b2c655 100644 --- a/src/midi/audio/live.rs +++ b/src/midi/audio/live.rs @@ -7,7 +7,7 @@ use time::Duration; use crossbeam_channel::Receiver; use crate::{ - audio_playback::SimpleTemporaryPlayer, + audio_playback::WasabiAudioPlayer, midi::shared::{ audio::CompressedAudio, timer::{TimeListener, UnpauseWaitResult, WaitResult}, @@ -17,14 +17,14 @@ use crate::{ pub struct LiveAudioPlayer { events: Receiver, timer: TimeListener, - player: Arc>, + player: Arc>, } impl LiveAudioPlayer { pub fn new( events: Receiver, timer: TimeListener, - player: Arc>, + player: Arc>, ) -> Self { LiveAudioPlayer { events, diff --git a/src/midi/audio/ram.rs b/src/midi/audio/ram.rs index 1228eb9..5c6632f 100644 --- a/src/midi/audio/ram.rs +++ b/src/midi/audio/ram.rs @@ -5,7 +5,7 @@ use std::{ use time::Duration; use crate::{ - audio_playback::SimpleTemporaryPlayer, + audio_playback::WasabiAudioPlayer, midi::shared::{ audio::CompressedAudio, timer::{SeekWaitResult, TimeListener, UnpauseWaitResult, WaitResult}, @@ -15,7 +15,7 @@ use crate::{ pub struct InRamAudioPlayer { events: Vec, timer: TimeListener, - player: Arc>, + player: Arc>, index: usize, } @@ -23,7 +23,7 @@ impl InRamAudioPlayer { pub fn new( events: Vec, timer: TimeListener, - player: Arc>, + player: Arc>, ) -> Self { InRamAudioPlayer { events, diff --git a/src/midi/cake/mod.rs b/src/midi/cake/mod.rs index 0effc44..ef5c07d 100644 --- a/src/midi/cake/mod.rs +++ b/src/midi/cake/mod.rs @@ -15,7 +15,7 @@ use midi_toolkit::{ }; use crate::{ - audio_playback::SimpleTemporaryPlayer, + audio_playback::WasabiAudioPlayer, midi::{ audio::ram::InRamAudioPlayer, cake::tree_threader::{NoteEvent, ThreadedTreeSerializers}, @@ -23,6 +23,7 @@ use crate::{ shared::{audio::CompressedAudio, timer::TimeKeeper}, MIDIColor, }, + settings::WasabiSettings, }; use self::blocks::CakeBlock; @@ -47,8 +48,8 @@ pub struct CakeMIDIFile { impl CakeMIDIFile { pub fn load_from_file( path: &str, - player: Arc>, - random_colors: bool, + player: Arc>, + settings: &WasabiSettings, ) -> Self { let ticks_per_second = 10000; @@ -64,12 +65,7 @@ impl CakeMIDIFile { |>unwrap_items() ); - let track_count = midi.track_count(); - let colors = if random_colors { - MIDIColor::new_random_vec_for_tracks(track_count) - } else { - MIDIColor::new_vec_for_tracks(track_count) - }; + let colors = MIDIColor::new_vec_from_settings(midi.track_count(), settings); type Ev = Delta>>; let (key_snd, key_rcv) = crossbeam_channel::bounded::>(1000); diff --git a/src/midi/live/mod.rs b/src/midi/live/mod.rs index 8c46ce8..406c0cc 100644 --- a/src/midi/live/mod.rs +++ b/src/midi/live/mod.rs @@ -5,7 +5,7 @@ use std::{ use midi_toolkit::{io::MIDIFile as TKMIDIFile, sequence::event::get_channels_array_statistics}; -use crate::audio_playback::SimpleTemporaryPlayer; +use crate::{audio_playback::WasabiAudioPlayer, settings::WasabiSettings}; use self::{ parse::LiveMidiParser, @@ -13,8 +13,8 @@ use self::{ }; use super::{ - open_file_and_signature, shared::timer::TimeKeeper, MIDIFile, MIDIFileBase, MIDIFileStats, - MIDIFileUniqueSignature, MIDIViewRange, + open_file_and_signature, shared::timer::TimeKeeper, MIDIColor, MIDIFile, MIDIFileBase, + MIDIFileStats, MIDIFileUniqueSignature, MIDIViewRange, }; pub mod block; @@ -37,8 +37,8 @@ pub struct LiveLoadMIDIFile { impl LiveLoadMIDIFile { pub fn load_from_file( path: &str, - player: Arc>, - random_colors: bool, + player: Arc>, + settings: &WasabiSettings, ) -> Self { let (file, signature) = open_file_and_signature(path); @@ -62,8 +62,10 @@ impl LiveLoadMIDIFile { let mut timer = TimeKeeper::new(); - let parer = LiveMidiParser::init(&midi, player, &mut timer); - let file = LiveNoteViewData::new(parer, midi.track_count(), random_colors); + let colors = MIDIColor::new_vec_from_settings(midi.track_count(), settings); + + let parser = LiveMidiParser::init(&midi, player, &mut timer); + let file = LiveNoteViewData::new(parser, colors); LiveLoadMIDIFile { view_data: file, diff --git a/src/midi/live/parse.rs b/src/midi/live/parse.rs index f378413..c0dbde0 100644 --- a/src/midi/live/parse.rs +++ b/src/midi/live/parse.rs @@ -17,7 +17,7 @@ use midi_toolkit::{ }; use crate::{ - audio_playback::SimpleTemporaryPlayer, + audio_playback::WasabiAudioPlayer, midi::{ audio::live::LiveAudioPlayer, shared::timer::{TimeKeeper, WaitResult}, @@ -46,7 +46,7 @@ pub struct LiveMidiParser { impl LiveMidiParser { pub fn init( midi: &TKMIDIFile, - player: Arc>, + player: Arc>, timer: &mut TimeKeeper, ) -> Self { let ppq = midi.ppq(); diff --git a/src/midi/live/view.rs b/src/midi/live/view.rs index f720a9c..ff5885f 100644 --- a/src/midi/live/view.rs +++ b/src/midi/live/view.rs @@ -25,7 +25,7 @@ impl<'a> LiveCurrentNoteViews<'a> { } impl LiveNoteViewData { - pub fn new(parser: LiveMidiParser, track_count: usize, random_colors: bool) -> Self { + pub fn new(parser: LiveMidiParser, colors: Vec) -> Self { let mut columns = Vec::with_capacity(256); columns.resize_with(256, LiveNoteColumn::new); LiveNoteViewData { @@ -35,11 +35,7 @@ impl LiveNoteViewData { start: f64::NEG_INFINITY, end: f64::NEG_INFINITY, }, - default_track_colors: if random_colors { - MIDIColor::new_random_vec_for_tracks(track_count) - } else { - MIDIColor::new_vec_for_tracks(track_count) - }, + default_track_colors: colors, } } diff --git a/src/midi/mod.rs b/src/midi/mod.rs index ebe98d6..4df0360 100644 --- a/src/midi/mod.rs +++ b/src/midi/mod.rs @@ -8,7 +8,7 @@ mod ram; mod audio; mod shared; -use std::{fs::File, time::UNIX_EPOCH}; +use std::{fs::File, path::PathBuf, time::UNIX_EPOCH}; use enum_dispatch::enum_dispatch; use palette::{convert::FromColorUnclamped, Hsv, Srgb}; @@ -19,6 +19,8 @@ pub use live::LiveLoadMIDIFile; pub use ram::InRamMIDIFile; pub use shared::timer::START_DELAY; +use crate::settings::{Colors, WasabiSettings}; + use self::shared::timer::TimeKeeper; #[derive(Debug, Clone, Copy, Default)] @@ -91,7 +93,7 @@ impl MIDIColor { ) } - pub fn new_vec_for_tracks(tracks: usize) -> Vec { + pub fn new_vec(tracks: usize) -> Vec { let count = tracks * 16; let mut vec = Vec::with_capacity(count); @@ -105,7 +107,7 @@ impl MIDIColor { vec } - pub fn new_random_vec_for_tracks(tracks: usize) -> Vec { + pub fn new_random_vec(tracks: usize) -> Vec { let count = tracks * 16; let mut vec = Vec::with_capacity(count); @@ -119,6 +121,25 @@ impl MIDIColor { vec } + pub fn new_vec_from_palette(tracks: usize, path: impl Into) -> Vec { + let path: PathBuf = path.into(); + if path.exists() { + return Vec::new(); + } + + Self::new_vec(tracks) + } + + pub fn new_vec_from_settings(tracks: usize, settings: &WasabiSettings) -> Vec { + match settings.midi.colors { + Colors::Rainbow => MIDIColor::new_vec(tracks), + Colors::Random => MIDIColor::new_random_vec(tracks), + Colors::Palette => { + MIDIColor::new_vec_from_palette(tracks, settings.midi.palette_path.clone()) + } + } + } + pub fn as_u32(&self) -> u32 { self.0 } diff --git a/src/midi/ram/parse.rs b/src/midi/ram/parse.rs index e318334..9f86b54 100644 --- a/src/midi/ram/parse.rs +++ b/src/midi/ram/parse.rs @@ -16,13 +16,15 @@ use midi_toolkit::{ use rustc_hash::FxHashMap; use crate::{ - audio_playback::SimpleTemporaryPlayer, + audio_playback::WasabiAudioPlayer, midi::{ audio::ram::InRamAudioPlayer, open_file_and_signature, ram::{column::InRamNoteColumn, view::InRamNoteViewData}, shared::{audio::CompressedAudio, timer::TimeKeeper, track_channel::TrackAndChannel}, + MIDIColor, }, + settings::WasabiSettings, }; use super::{block::InRamNoteBlock, InRamMIDIFile}; @@ -98,8 +100,8 @@ impl Key { impl InRamMIDIFile { pub fn load_from_file( path: &str, - player: Arc>, - random_colors: bool, + player: Arc>, + settings: &WasabiSettings, ) -> Self { let (file, signature) = open_file_and_signature(path); let midi = TKMIDIFile::open_from_stream(file, None).unwrap(); @@ -192,8 +194,10 @@ impl InRamMIDIFile { .map(|key| InRamNoteColumn::new(key.column)) .collect(); + let colors = MIDIColor::new_vec_from_settings(midi.track_count(), settings); + InRamMIDIFile { - view_data: InRamNoteViewData::new(columns, midi.track_count(), random_colors), + view_data: InRamNoteViewData::new(columns, colors), timer, length, note_count, diff --git a/src/midi/ram/view.rs b/src/midi/ram/view.rs index dbbeaa4..fce8085 100644 --- a/src/midi/ram/view.rs +++ b/src/midi/ram/view.rs @@ -22,18 +22,14 @@ impl<'a> InRamCurrentNoteViews<'a> { } impl InRamNoteViewData { - pub fn new(columns: Vec, track_count: usize, random_colors: bool) -> Self { + pub fn new(columns: Vec, colors: Vec) -> Self { InRamNoteViewData { columns, view_range: MIDIViewRange { start: 0.0, end: 0.0, }, - default_track_colors: if random_colors { - MIDIColor::new_random_vec_for_tracks(track_count) - } else { - MIDIColor::new_vec_for_tracks(track_count) - }, + default_track_colors: colors, } } diff --git a/src/renderer.rs b/src/renderer.rs index 8e54a55..b18ae72 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -2,6 +2,7 @@ pub mod swapchain; use std::sync::Arc; +use raw_window_handle::RawDisplayHandle; use vulkano::{ device::{ physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, Features, Queue, @@ -14,16 +15,12 @@ use vulkano::{ Version, VulkanLibrary, }; -use vulkano_win::create_surface_from_winit; -#[cfg(target_os = "linux")] -use winit::platform::wayland::EventLoopWindowTargetExtWayland; -#[cfg(target_os = "linux")] -use winit::platform::wayland::WindowExtWayland; +use raw_window_handle::HasDisplayHandle; use winit::{ dpi::PhysicalSize, event_loop::EventLoop, - monitor::VideoMode, - window::{Fullscreen, Icon, Window, WindowBuilder}, + monitor::VideoModeHandle, + window::{Fullscreen, Icon, Window, WindowAttributes}, }; use self::swapchain::{ManagedSwapchain, SwapchainFrame}; @@ -40,13 +37,13 @@ pub struct Renderer { } impl Renderer { - pub fn new(event_loop: &EventLoop<()>, name: &str, fullscreen: bool, mode: VideoMode) -> Self { + pub fn new(event_loop: &EventLoop<()>, name: &str) -> Self { // Why let library = VulkanLibrary::new().unwrap(); // Add instance extensions based on needs let instance_extensions = InstanceExtensions { - ..vulkano_win::required_extensions(&library) + ..Surface::required_extensions(event_loop).unwrap() }; // Create instance @@ -61,30 +58,17 @@ impl Renderer { .expect("Failed to create instance"); // Create rendering surface along with window - let window = WindowBuilder::new() + let win_attr = WindowAttributes::default() .with_window_icon(Some(Icon::from_rgba(ICON.to_vec(), 16, 16).unwrap())) - .with_fullscreen({ - if fullscreen { - #[cfg(target_os = "linux")] - let fullscreen = if event_loop.is_wayland() { - Some(Fullscreen::Borderless(None)) - } else { - Some(Fullscreen::Exclusive(mode)) - }; - #[cfg(not(target_os = "linux"))] - let fullscreen = Some(Fullscreen::Exclusive(mode)); - fullscreen - } else { - None - } - }) .with_inner_size(crate::WINDOW_SIZE) - .with_title(name) - .build(event_loop) + .with_title(name); + let window = event_loop + .create_window(win_attr) .expect("Failed to create vulkan surface & window"); + let window = Arc::new(window); - let surface = create_surface_from_winit(window.clone(), instance.clone()) + let surface = Surface::from_window(instance.clone(), window.clone()) .expect("Failed to create surface"); // Get most performant physical device (device with most memory) @@ -149,7 +133,10 @@ impl Renderer { physical_device, device.clone(), #[cfg(target_os = "linux")] - if event_loop.is_wayland() { + if matches!( + event_loop.display_handle().unwrap().as_raw(), + RawDisplayHandle::Wayland(..) + ) { println!("Present Mode: {:?}", crate::WAYLAND_PRESENT_MODE); crate::WAYLAND_PRESENT_MODE } else { @@ -196,17 +183,16 @@ impl Renderer { self.swap_chain.resize(size); } - pub fn set_fullscreen(&self, mode: VideoMode) { + pub fn set_fullscreen(&self, mode: VideoModeHandle) { if self.window.fullscreen().is_none() { - #[cfg(target_os = "linux")] - let fullscreen = if self.window.wayland_display().is_some() { + let fullscreen = if matches!( + self.window.display_handle().unwrap().as_raw(), + RawDisplayHandle::Wayland(..) + ) { Some(Fullscreen::Borderless(None)) } else { Some(Fullscreen::Exclusive(mode)) }; - #[cfg(not(target_os = "linux"))] - let fullscreen = Some(Fullscreen::Exclusive(mode)); - self.window.set_fullscreen(fullscreen); } else { self.window.set_fullscreen(None); diff --git a/src/renderer/swapchain.rs b/src/renderer/swapchain.rs index 9aea4a2..b28b8db 100644 --- a/src/renderer/swapchain.rs +++ b/src/renderer/swapchain.rs @@ -1,16 +1,17 @@ use std::sync::Arc; +use egui_winit::winit::{dpi::PhysicalSize, window::Window}; use vulkano::{ device::{physical::PhysicalDevice, Device, Queue}, format::Format, - image::{view::ImageView, ImageUsage, SwapchainImage}, + image::{view::ImageView, ImageUsage}, swapchain::{ - AcquireError, PresentMode, Surface, Swapchain, SwapchainAcquireFuture, SwapchainCreateInfo, - SwapchainCreationError, SwapchainPresentInfo, + PresentMode, Surface, Swapchain, SwapchainAcquireFuture, SwapchainCreateInfo, + SwapchainPresentInfo, }, - sync::{self, FlushError, GpuFuture}, + sync::{self, GpuFuture}, + Validated, VulkanError, }; -use winit::{dpi::PhysicalSize, window::Window}; #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub struct ImagesState { @@ -26,7 +27,7 @@ pub struct SwapchainState { pub struct ManagedSwapchain { state: SwapchainState, swap_chain: Arc, - image_views: Vec>>, + image_views: Vec>, previous_frame_end: Option>, device: Arc, recreate_on_next_frame: bool, @@ -49,8 +50,9 @@ impl ManagedSwapchain { .unwrap(); let image_format = formats .iter() - .find(|v| v.0 == Format::B8G8R8A8_SRGB) - .map(|v| v.0); + .find(|v| v.0 == Format::B8G8R8A8_UNORM) + .map(|v| v.0) + .unwrap_or(Format::B8G8R8A8_UNORM); let image_extent = window.inner_size().into(); let (swapchain, images) = Swapchain::new( @@ -110,7 +112,7 @@ impl ManagedSwapchain { ..self.swap_chain.create_info() }) { Ok(r) => r, - Err(SwapchainCreationError::ImageExtentNotSupported { .. }) => return, + Err(Validated::Error { .. }) => return, Err(e) => panic!("Failed to recreate swapchain: {e:?}"), }; self.swap_chain = new_swapchain; @@ -144,11 +146,15 @@ impl ManagedSwapchain { let (image_num, suboptimal, acquire_future) = match next { Ok(r) => r, // TODO: Handle more errors, e.g. DeviceLost, by re-creating the entire graphics chain - Err(AcquireError::OutOfDate) => { - self.recreate(); - continue; + Err(Validated::Error(e)) => { + if e == VulkanError::OutOfDate { + self.recreate(); + continue; + } else { + panic!("Failed to acquire next image: {e:?}"); + } } - Err(e) => panic!("Failed to acquire next image: {e:?}"), + Err(e) => panic!("Unknown error: {e:?}"), }; if suboptimal { @@ -172,7 +178,7 @@ pub struct SwapchainFrame<'a> { presented: bool, pub image_num: u32, - pub image: Arc>, + pub image: Arc, managed_swap_chain: &'a mut ManagedSwapchain, } @@ -200,12 +206,17 @@ impl<'a> SwapchainFrame<'a> { } sc.previous_frame_end = Some(future.boxed()); } - Err(FlushError::OutOfDate) => { - sc.recreate_on_next_frame = true; - sc.previous_frame_end = Some(sync::now(sc.device.clone()).boxed()); + Err(Validated::Error(e)) => { + if e == VulkanError::OutOfDate { + sc.recreate_on_next_frame = true; + sc.previous_frame_end = Some(sync::now(sc.device.clone()).boxed()); + } else { + println!("Failed to flush future: {e:?}"); + sc.previous_frame_end = Some(sync::now(sc.device.clone()).boxed()); + } } Err(e) => { - println!("Failed to flush future: {e:?}"); + println!("Unknown error: {e:?}"); sc.previous_frame_end = Some(sync::now(sc.device.clone()).boxed()); } } diff --git a/src/scenes.rs b/src/scenes.rs index 283b238..4d348b5 100644 --- a/src/scenes.rs +++ b/src/scenes.rs @@ -2,14 +2,14 @@ use std::sync::Arc; use vulkano::{ device::Device, - image::{view::ImageView, AttachmentImage}, + image::{view::ImageView, Image, ImageCreateInfo, ImageUsage}, memory::allocator::StandardMemoryAllocator, }; use crate::{gui::GuiState, renderer::swapchain::ImagesState}; pub struct SceneImage { - pub image: Arc>, + pub image: Arc, pub id: egui::TextureId, } @@ -39,17 +39,24 @@ impl SceneSwapchain { state.renderer.gui.unregister_user_image(image.id); } - let allocator = StandardMemoryAllocator::new_default(self.device.clone()); + let allocator = Arc::new(StandardMemoryAllocator::new_default(self.device.clone())); + let mut create_info: ImageCreateInfo = Default::default(); + create_info.format = image_state.format; + create_info.extent = [size[0], size[1], 1]; + create_info.usage = ImageUsage::SAMPLED; // Create new images for _ in 0..image_state.count { let image = ImageView::new_default( - AttachmentImage::sampled_input_attachment(&allocator, size, image_state.format) + Image::new(allocator.clone(), create_info.clone(), Default::default()) .expect("Failed to create scene image"), ) .expect("Failed to create scene image view"); - let id = state.renderer.gui.register_user_image_view(image.clone()); + let id = state + .renderer + .gui + .register_user_image_view(image.clone(), Default::default()); self.scene_images.push(SceneImage { image, id }); } diff --git a/src/settings/enums.rs b/src/settings/enums.rs new file mode 100644 index 0000000..f741d4d --- /dev/null +++ b/src/settings/enums.rs @@ -0,0 +1,167 @@ +use num_enum::FromPrimitive; +use serde_derive::{Deserialize, Serialize}; +use std::{fmt::Debug, slice::Iter, str::FromStr}; + +#[repr(usize)] +#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone, Copy, FromPrimitive)] +#[serde(rename_all = "lowercase")] +pub enum MidiParsing { + #[default] + Ram = 0, + Live = 1, + Cake = 2, +} + +impl MidiParsing { + pub const fn as_str(self) -> &'static str { + match self { + MidiParsing::Ram => "Standard (RAM)", + MidiParsing::Live => "Standard (Live)", + MidiParsing::Cake => "Cake", + } + } +} + +impl FromStr for MidiParsing { + type Err = String; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "ram" => Ok(MidiParsing::Ram), + "live" => Ok(MidiParsing::Live), + "cake" => Ok(MidiParsing::Cake), + s => Err(format!( + "{} was not expected. Expected one of `ram`, `live` or `cake`", + s + )), + } + } +} + +#[repr(usize)] +#[derive(Debug, Default, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, FromPrimitive)] +#[serde(rename_all = "lowercase")] +pub enum Synth { + #[default] + XSynth = 0, + Kdmapi = 1, + MidiDevice = 2, + None = 3, +} + +impl Synth { + #[inline] + pub const fn as_str(self) -> &'static str { + match self { + Synth::XSynth => "Built-In (XSynth)", + Synth::Kdmapi => "KDMAPI", + Synth::MidiDevice => "MIDI Device", + Synth::None => "None", + } + } +} + +impl FromStr for Synth { + type Err = String; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "xsynth" => Ok(Synth::XSynth), + "kdmapi" => Ok(Synth::Kdmapi), + "mididevice" => Ok(Synth::MidiDevice), + "none" => Ok(Synth::None), + s => Err(format!( + "{} was not expected. Expected one of `xsynth`, `kdmapi`, `mididevice` or `none`", + s + )), + } + } +} + +#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, FromPrimitive)] +#[repr(usize)] +#[serde(rename_all = "lowercase")] +pub enum Statistics { + #[default] + Time = 0, + Fps = 1, + VoiceCount = 2, + Rendered = 3, + NoteCount = 4, +} + +impl Statistics { + #[inline] + pub const fn as_str(self) -> &'static str { + match self { + Statistics::Time => "Time", + Statistics::Fps => "FPS", + Statistics::VoiceCount => "Voice Count", + Statistics::Rendered => "Rendered", + Statistics::NoteCount => "Note Count", + } + } + + pub fn iter() -> Iter<'static, Statistics> { + static STATISTICS: [Statistics; 5] = [ + Statistics::Time, + Statistics::Fps, + Statistics::VoiceCount, + Statistics::Rendered, + Statistics::NoteCount, + ]; + STATISTICS.iter() + } +} + +impl FromStr for Statistics { + type Err = String; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "time" => Ok(Statistics::Time), + "fps" => Ok(Statistics::Fps), + "voicecount" => Ok(Statistics::VoiceCount), + "rendered" => Ok(Statistics::Rendered), + "notecount" => Ok(Statistics::NoteCount), + s => Err(format!("{} was not expected.", s)), + } + } +} + +#[repr(usize)] +#[derive(Debug, Default, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, FromPrimitive)] +#[serde(rename_all = "lowercase")] +pub enum Colors { + #[default] + Rainbow = 0, + Random = 1, + Palette = 2, +} + +impl Colors { + #[inline] + pub const fn as_str(self) -> &'static str { + match self { + Colors::Rainbow => "Rainbow", + Colors::Random => "Random", + Colors::Palette => "Palette", + } + } +} + +impl FromStr for Colors { + type Err = String; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "rainbow" => Ok(Colors::Rainbow), + "random" => Ok(Colors::Random), + "palette" => Ok(Colors::Palette), + s => Err(format!( + "{} was not expected. Expected one of `ranbow`, `random` or `palette`", + s + )), + } + } +} diff --git a/src/settings/migrations.rs b/src/settings/migrations.rs index ea63efb..7d667d7 100644 --- a/src/settings/migrations.rs +++ b/src/settings/migrations.rs @@ -1,77 +1,6 @@ -use colors_transform::{Color, Rgb}; -use egui::Color32; -use serde_derive::Deserialize; -use std::fs; +mod serializers; +mod v0; +mod v1; -use super::{MidiLoading, MidiSettings, Synth, SynthSettings, VisualSettings, WasabiSettings}; - -#[derive(Deserialize)] -pub struct WasabiConfigFileV0 { - note_speed: f64, - bg_color: String, - bar_color: String, - random_colors: bool, - sfz_path: String, - first_key: u8, - last_key: u8, - midi_loading: usize, - buffer_ms: f64, - use_threadpool: bool, - limit_layers: bool, - layer_count: usize, - fade_out_kill: bool, - use_effects: bool, - vel_ignore_lo: u8, - vel_ignore_hi: u8, - synth: usize, -} - -impl WasabiConfigFileV0 { - pub fn migrate() -> Result { - let config_path = WasabiSettings::get_config_path(); - let content = fs::read_to_string(config_path).unwrap_or_default(); - let cfg = toml::from_str::(&content)?; - if let (Ok(bg), Ok(bar)) = ( - Rgb::from_hex_str(&cfg.bg_color), - Rgb::from_hex_str(&cfg.bar_color), - ) { - Ok(WasabiSettings { - synth: SynthSettings { - synth: Synth::from(cfg.synth), - buffer_ms: cfg.buffer_ms, - use_threadpool: cfg.use_threadpool, - limit_layers: cfg.limit_layers, - layer_count: cfg.layer_count, - fade_out_kill: cfg.fade_out_kill, - use_effects: cfg.use_effects, - sfz_path: cfg.sfz_path, - vel_ignore: cfg.vel_ignore_lo..=cfg.vel_ignore_hi, - }, - midi: MidiSettings { - note_speed: cfg.note_speed, - random_colors: cfg.random_colors, - key_range: cfg.first_key..=cfg.last_key, - midi_loading: MidiLoading::from(cfg.midi_loading), - }, - visual: VisualSettings { - bg_color: Color32::from_rgb( - bg.get_red() as u8, - bg.get_green() as u8, - bg.get_blue() as u8, - ), - bar_color: Color32::from_rgb( - bar.get_red() as u8, - bar.get_green() as u8, - bar.get_blue() as u8, - ), - show_top_pannel: true, - show_statistics: true, - fullscreen: false, - }, - load_midi_file: None, - }) - } else { - Ok(WasabiSettings::default()) - } - } -} +pub use v0::*; +pub use v1::*; diff --git a/src/settings/migrations/serializers.rs b/src/settings/migrations/serializers.rs new file mode 100644 index 0000000..9339616 --- /dev/null +++ b/src/settings/migrations/serializers.rs @@ -0,0 +1,104 @@ +pub mod color32_serde { + use colors_transform::{Color, Rgb}; + use egui::Color32; + use serde::{de::Visitor, Deserializer}; + + #[inline(always)] + pub fn color_parser(s: &str) -> Result { + let rgb = Rgb::from_hex_str(s).map_err(|e| e.message)?; + Ok(Color32::from_rgb( + rgb.get_red() as u8, + rgb.get_green() as u8, + rgb.get_blue() as u8, + )) + } + + pub struct ColorVisitor; + + impl<'de> Visitor<'de> for ColorVisitor { + type Value = Color32; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("A color encoded as a hex string") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + color_parser(v) + .map_err(|e| E::invalid_value(serde::de::Unexpected::Str(v), &e.as_str())) + } + } + + pub fn deserialize<'de, D>(de: D) -> Result + where + D: Deserializer<'de>, + { + de.deserialize_str(ColorVisitor) + } +} + +pub mod range_serde { + use std::ops::RangeInclusive; + + use serde::{ + de::{self, Visitor}, + Deserializer, + }; + + use serde_derive::Deserialize; + + #[derive(Deserialize)] + #[serde(field_identifier, rename_all = "lowercase")] + enum Field { + Hi, + Lo, + } + + pub struct RangeVisitor; + + impl<'de> Visitor<'de> for RangeVisitor { + type Value = RangeInclusive; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("A color encoded as a hex string") + } + + fn visit_map(self, mut map: A) -> Result + where + A: serde::de::MapAccess<'de>, + { + let mut hi = None; + let mut lo = None; + while let Some(key) = map.next_key::()? { + match key { + Field::Hi => { + if hi.is_some() { + return Err(de::Error::duplicate_field("hi")); + } + hi = Some(map.next_value::()?); + } + Field::Lo => { + if lo.is_some() { + return Err(de::Error::duplicate_field("lo")); + } + lo = Some(map.next_value::()?); + } + } + } + + let hi = hi.ok_or_else(|| de::Error::missing_field("hi"))?; + let lo = lo.ok_or_else(|| de::Error::missing_field("lo"))?; + + Ok(hi..=lo) + } + } + + pub fn deserialize<'de, D>(de: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + de.deserialize_struct("range", &["hi", "lo"], RangeVisitor) + } +} diff --git a/src/settings/migrations/v0.rs b/src/settings/migrations/v0.rs new file mode 100644 index 0000000..0a796e2 --- /dev/null +++ b/src/settings/migrations/v0.rs @@ -0,0 +1,71 @@ +use colors_transform::{Color, Rgb}; +use egui::Color32; +use serde_derive::Deserialize; + +use super::v1::{MidiSettingsV1, SynthSettingsV1, VisualSettingsV1}; +use super::WasabiConfigFileV1; +use crate::settings::enums::{MidiParsing, Synth}; + +#[allow(dead_code)] +#[derive(Deserialize)] +pub struct WasabiConfigFileV0 { + note_speed: f64, + bg_color: String, + bar_color: String, + random_colors: bool, + sfz_path: String, + first_key: u8, + last_key: u8, + midi_loading: usize, + buffer_ms: f64, + limit_layers: bool, + layer_count: usize, + fade_out_kill: bool, + linear_envelope: bool, + use_effects: bool, + vel_ignore_lo: u8, + vel_ignore_hi: u8, + synth: usize, +} + +impl WasabiConfigFileV0 { + pub fn migrate_to_v1(content: String) -> Result { + let cfg = toml::from_str::(&content)?; + let bg = Rgb::from_hex_str(&cfg.bg_color).unwrap_or(Rgb::from(0.1, 0.1, 0.1)); + let bar = Rgb::from_hex_str(&cfg.bar_color).unwrap_or(Rgb::from(0.56, 0.0, 0.0)); + Ok(WasabiConfigFileV1 { + synth: SynthSettingsV1 { + synth: Synth::from(cfg.synth), + buffer_ms: cfg.buffer_ms, + limit_layers: cfg.limit_layers, + layer_count: cfg.layer_count, + fade_out_kill: cfg.fade_out_kill, + use_effects: cfg.use_effects, + sfz_path: cfg.sfz_path, + vel_ignore: cfg.vel_ignore_lo..=cfg.vel_ignore_hi, + }, + midi: MidiSettingsV1 { + note_speed: cfg.note_speed, + random_colors: cfg.random_colors, + key_range: cfg.first_key..=cfg.last_key, + midi_loading: MidiParsing::from(cfg.midi_loading), + }, + visual: VisualSettingsV1 { + bg_color: Color32::from_rgb( + bg.get_red() as u8, + bg.get_green() as u8, + bg.get_blue() as u8, + ), + bar_color: Color32::from_rgb( + bar.get_red() as u8, + bar.get_green() as u8, + bar.get_blue() as u8, + ), + show_top_pannel: true, + show_statistics: true, + fullscreen: false, + }, + load_midi_file: None, + }) + } +} diff --git a/src/settings/migrations/v1.rs b/src/settings/migrations/v1.rs new file mode 100644 index 0000000..f209e63 --- /dev/null +++ b/src/settings/migrations/v1.rs @@ -0,0 +1,101 @@ +use egui::Color32; +use serde_derive::Deserialize; +use std::ops::RangeInclusive; +use xsynth_realtime::{ChannelInitOptions, XSynthRealtimeConfig}; + +use super::serializers::{color32_serde, range_serde}; +use crate::settings::{ + MidiParsing, MidiSettings, SceneSettings, Synth, SynthSettings, WasabiSettings, + WasabiSoundfont, XSynthSettings, +}; + +#[allow(dead_code)] +#[derive(Deserialize)] +pub struct VisualSettingsV1 { + #[serde(with = "color32_serde")] + pub bg_color: Color32, + #[serde(with = "color32_serde")] + pub bar_color: Color32, + pub show_top_pannel: bool, + pub show_statistics: bool, + pub fullscreen: bool, +} + +#[allow(dead_code)] +#[derive(Deserialize)] +pub struct MidiSettingsV1 { + pub note_speed: f64, + pub random_colors: bool, + #[serde(with = "range_serde")] + pub key_range: RangeInclusive, + pub midi_loading: MidiParsing, +} + +#[allow(dead_code)] +#[derive(Deserialize)] +pub struct SynthSettingsV1 { + pub synth: Synth, + pub buffer_ms: f64, + pub sfz_path: String, + pub limit_layers: bool, + pub layer_count: usize, + #[serde(with = "range_serde")] + pub vel_ignore: RangeInclusive, + pub fade_out_kill: bool, + pub use_effects: bool, +} + +#[allow(dead_code)] +#[derive(Deserialize)] +pub struct WasabiConfigFileV1 { + pub synth: SynthSettingsV1, + pub midi: MidiSettingsV1, + pub visual: VisualSettingsV1, + #[serde(skip_serializing_if = "Option::is_none")] + pub load_midi_file: Option, +} + +impl WasabiConfigFileV1 { + pub fn migrate_to_v2(content: String) -> Result { + let cfg = toml::from_str::(&content)?; + Ok(Self::migrate_to_v2_raw(cfg)) + } + + pub fn migrate_to_v2_raw(cfg: Self) -> WasabiSettings { + WasabiSettings { + scene: SceneSettings { + bg_color: cfg.visual.bg_color, + bar_color: cfg.visual.bar_color, + statistics: Default::default(), + note_speed: cfg.midi.note_speed, + key_range: cfg.midi.key_range, + }, + midi: MidiSettings { + parsing: cfg.midi.midi_loading, + ..Default::default() + }, + synth: SynthSettings { + synth: cfg.synth.synth, + soundfonts: vec![WasabiSoundfont { + path: cfg.synth.sfz_path.into(), + enabled: true, + options: Default::default(), + }], + xsynth: XSynthSettings { + layers: cfg.synth.layer_count, + limit_layers: cfg.synth.limit_layers, + config: XSynthRealtimeConfig { + render_window_ms: cfg.synth.buffer_ms, + channel_init_options: ChannelInitOptions { + fade_out_killing: cfg.synth.fade_out_kill, + }, + ignore_range: cfg.synth.vel_ignore, + ..Default::default() + }, + }, + ..Default::default() + }, + ..Default::default() + } + } +} diff --git a/src/settings/mod.rs b/src/settings/mod.rs index 58f8923..1429424 100644 --- a/src/settings/mod.rs +++ b/src/settings/mod.rs @@ -1,8 +1,5 @@ -use clap::{parser::ValueSource, value_parser, Arg, ArgAction, Command, ValueHint}; -use colors_transform::{Color, Rgb}; use directories::BaseDirs; use egui::Color32; -use num_enum::FromPrimitive; use serde_derive::{Deserialize, Serialize}; use std::{ fmt::Debug, @@ -10,639 +7,247 @@ use std::{ io::Write, ops::RangeInclusive, path::{Path, PathBuf}, - str::FromStr, }; -use xsynth_core::{channel::ChannelInitOptions, soundfont::SoundfontInitOptions}; +use xsynth_core::soundfont::SoundfontInitOptions; use xsynth_realtime::XSynthRealtimeConfig; +mod enums; mod migrations; -#[inline(always)] -fn f64_parser(s: &str) -> Result { - s.parse().map_err(|e| format!("{}", e)) -} +pub use enums::*; -#[inline(always)] -fn note_speed(s: &str) -> Result { - let num: f64 = f64_parser(s)?; - if (0.0001..=2.0).contains(&num) { - Ok(2.0001 - num) - } else { - Err(String::from("Number must be between >0 and 2.0")) - } -} +// region: gui -#[inline(always)] -fn color_parser(s: &str) -> Result { - let rgb = Rgb::from_hex_str(s).map_err(|e| e.message)?; - Ok(Color32::from_rgb( - rgb.get_red() as u8, - rgb.get_green() as u8, - rgb.get_blue() as u8, - )) -} - -#[inline(always)] -fn range_parser(s: &str) -> Result, String> { - let range = s - .split_once(',') - .ok_or_else(|| String::from("This argument requires 2 numbers, comma seperated"))?; - - Ok(range.0.parse().map_err(|e| format!("{}", e))? - ..=range.1.parse().map_err(|e| format!("{}", e))?) +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(default)] +pub struct GuiSettings { + pub check_for_updates: bool, + pub fps_limit: usize, + pub skip_control: f64, + pub speed_control: f64, } -mod color32_serde { - use colors_transform::Rgb; - use egui::Color32; - use serde::{de::Visitor, Deserializer, Serializer}; - - use super::color_parser; - - pub fn serialize(color: &Color32, ser: S) -> Result - where - S: Serializer, - { - let hex_color = - Rgb::from(color.r() as f32, color.g() as f32, color.b() as f32).to_css_hex_string(); - - ser.serialize_str(&hex_color) - } - - pub struct ColorVisitor; - - impl<'de> Visitor<'de> for ColorVisitor { - type Value = Color32; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("A color encoded as a hex string") - } - - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - color_parser(v) - .map_err(|e| E::invalid_value(serde::de::Unexpected::Str(v), &e.as_str())) +impl Default for GuiSettings { + fn default() -> Self { + Self { + check_for_updates: true, + fps_limit: 0, + skip_control: 1.0, + speed_control: 0.05, } } - - pub fn deserialize<'de, D>(de: D) -> Result - where - D: Deserializer<'de>, - { - de.deserialize_str(ColorVisitor) - } } -mod range_serde { - use std::ops::RangeInclusive; - - use serde::{ - de::{self, Visitor}, - ser::SerializeStruct, - Deserializer, Serializer, - }; - - use serde_derive::Deserialize; - - pub fn serialize(range: &RangeInclusive, ser: S) -> Result - where - S: Serializer, - { - let mut ser_struct = ser.serialize_struct("range", 2)?; - ser_struct.serialize_field("hi", range.start())?; - ser_struct.serialize_field("lo", range.end())?; - ser_struct.end() - } - - #[derive(Deserialize)] - #[serde(field_identifier, rename_all = "lowercase")] - enum Field { - Hi, - Lo, - } - - pub struct RangeVisitor; - - impl<'de> Visitor<'de> for RangeVisitor { - type Value = RangeInclusive; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("A color encoded as a hex string") - } - - fn visit_map(self, mut map: A) -> Result - where - A: serde::de::MapAccess<'de>, - { - let mut hi = None; - let mut lo = None; - while let Some(key) = map.next_key::()? { - match key { - Field::Hi => { - if hi.is_some() { - return Err(de::Error::duplicate_field("hi")); - } - hi = Some(map.next_value::()?); - } - Field::Lo => { - if lo.is_some() { - return Err(de::Error::duplicate_field("lo")); - } - lo = Some(map.next_value::()?); - } - } - } - - let hi = hi.ok_or_else(|| de::Error::missing_field("hi"))?; - let lo = lo.ok_or_else(|| de::Error::missing_field("lo"))?; +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(default)] +pub struct StatisticsSettings { + pub border: bool, + pub floating: bool, + pub opacity: f32, + pub order: Vec<(Statistics, bool)>, +} - Ok(hi..=lo) +impl Default for StatisticsSettings { + fn default() -> Self { + Self { + border: true, + floating: true, + opacity: 0.5, + order: Statistics::iter().map(|i| (*i, true)).collect(), } } - - pub fn deserialize<'de, D>(de: D) -> Result, D::Error> - where - D: Deserializer<'de>, - { - de.deserialize_struct("range", &["hi", "lo"], RangeVisitor) - } } -#[repr(usize)] -#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone, Copy, FromPrimitive)] -#[serde(rename_all = "lowercase")] -pub enum MidiLoading { - #[default] - Ram = 0, - Live = 1, - Cake = 2, +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(default)] +pub struct SceneSettings { + pub bg_color: Color32, + pub bar_color: Color32, + pub statistics: StatisticsSettings, + pub note_speed: f64, + pub key_range: RangeInclusive, } -impl MidiLoading { - pub const fn as_str(self) -> &'static str { - match self { - MidiLoading::Ram => "In RAM", - MidiLoading::Live => "Live", - MidiLoading::Cake => "Cake", +impl Default for SceneSettings { + fn default() -> Self { + Self { + bg_color: Color32::from_rgb(30, 30, 30), + bar_color: Color32::from_rgb(145, 0, 0), + statistics: Default::default(), + note_speed: 0.25, + key_range: 0..=127, } } } -impl FromStr for MidiLoading { - type Err = String; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - "ram" => Ok(MidiLoading::Ram), - "live" => Ok(MidiLoading::Live), - "cake" => Ok(MidiLoading::Cake), - s => Err(format!( - "{} was not expected. Expected one of `ram`, `live` or `cake`", - s - )), - } - } -} +// endregion -#[repr(usize)] -#[derive(Debug, Default, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, FromPrimitive)] -#[serde(rename_all = "lowercase")] -pub enum Synth { - #[default] - XSynth = 0, - Kdmapi = 1, -} +// region: midi -impl Synth { - #[inline] - pub const fn as_str(self) -> &'static str { - match self { - Synth::XSynth => "XSynth", - Synth::Kdmapi => "KDMAPI", - } - } +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(default)] +pub struct MidiSettings { + pub parsing: MidiParsing, + pub colors: Colors, + pub palette_path: PathBuf, } -impl FromStr for Synth { - type Err = String; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - "xsynth" => Ok(Synth::XSynth), - "kdmapi" => Ok(Synth::Kdmapi), - s => Err(format!( - "{} was not expected. Expected one of `xsynth`, or `kdmapi`", - s - )), +impl Default for MidiSettings { + fn default() -> Self { + Self { + parsing: MidiParsing::Cake, + colors: Colors::Rainbow, + palette_path: PathBuf::new(), } } } -#[derive(Debug, Deserialize, Serialize)] +// endregion + +// region: synth + +#[derive(Debug, Serialize, Deserialize, Clone)] #[serde(default)] -pub struct VisualSettings { - #[serde(with = "color32_serde")] - pub bg_color: Color32, - #[serde(with = "color32_serde")] - pub bar_color: Color32, - pub show_top_pannel: bool, - pub show_statistics: bool, - pub fullscreen: bool, +pub struct XSynthSettings { + pub config: XSynthRealtimeConfig, + pub limit_layers: bool, + pub layers: usize, } -impl Default for VisualSettings { +impl Default for XSynthSettings { fn default() -> Self { - VisualSettings { - bg_color: Color32::from_rgb(30, 30, 30), - bar_color: Color32::from_rgb(145, 0, 0), - show_top_pannel: true, - show_statistics: true, - fullscreen: false, + Self { + config: Default::default(), + limit_layers: true, + layers: 4, } } } -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Serialize, Deserialize, Clone, Default)] #[serde(default)] -pub struct MidiSettings { - pub note_speed: f64, - pub random_colors: bool, - #[serde(with = "range_serde")] - pub key_range: RangeInclusive, - pub midi_loading: MidiLoading, +pub struct KdmapiSettings { + pub use_om_sflist: bool, } -impl Default for MidiSettings { - fn default() -> Self { - MidiSettings { - note_speed: 0.25, - random_colors: false, - key_range: 0..=127, - midi_loading: MidiLoading::Cake, - } - } +#[derive(Debug, Serialize, Deserialize, Clone, Default)] +#[serde(default)] +pub struct WasabiSoundfont { + pub path: PathBuf, + pub enabled: bool, + pub options: SoundfontInitOptions, } -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] #[serde(default)] pub struct SynthSettings { pub synth: Synth, - pub buffer_ms: f64, - pub use_threadpool: bool, - pub sfz_path: String, - pub limit_layers: bool, - pub layer_count: usize, - #[serde(with = "range_serde")] - pub vel_ignore: RangeInclusive, - pub fade_out_kill: bool, - pub use_effects: bool, + pub soundfonts: Vec, + + pub xsynth: XSynthSettings, + pub kdmapi: KdmapiSettings, + pub midi_device: String, } impl Default for SynthSettings { fn default() -> Self { - SynthSettings { + Self { synth: Synth::XSynth, - buffer_ms: XSynthRealtimeConfig::default().render_window_ms, - use_threadpool: false, - sfz_path: String::new(), - limit_layers: true, - layer_count: 4, - vel_ignore: 0..=0, - fade_out_kill: ChannelInitOptions::default().fade_out_killing, - use_effects: SoundfontInitOptions::default().use_effects, + soundfonts: Vec::new(), + xsynth: Default::default(), + kdmapi: Default::default(), + midi_device: String::new(), } } } -#[derive(Debug, Deserialize, Serialize, Default)] +// endregion + +// region: general + +#[derive(Default, Debug, Serialize, Deserialize, Clone)] #[serde(default)] pub struct WasabiSettings { - pub synth: SynthSettings, + pub gui: GuiSettings, + pub scene: SceneSettings, pub midi: MidiSettings, - pub visual: VisualSettings, - #[serde(skip_serializing_if = "Option::is_none")] - pub load_midi_file: Option, + pub synth: SynthSettings, } -static CONFIG_PATH: &str = "wasabi-config.toml"; - impl WasabiSettings { + const VERSION_TEXT: &str = "# DON'T EDIT THIS LINE; Version: 2\n"; + pub fn new_or_load() -> Self { let config_path = Self::get_config_path(); - let mut config = if !Path::new(&config_path).exists() { - Self::load_and_save_defaults() - } else { - let config = fs::read_to_string(&config_path).unwrap(); - if config.starts_with('#') { - if let Ok(config) = toml::from_str(&config) { - config - } else { - Self::load_and_save_defaults() + if !Path::new(&config_path).exists() { + return Self::load_and_save_defaults(); + } else if let Ok(config) = fs::read_to_string(&config_path) { + if config.starts_with(Self::VERSION_TEXT) { + let offset = Self::VERSION_TEXT.len(); + if let Ok(config) = serde_json::from_str(&config[offset..]) { + return config; + } + } else if config.starts_with("# DON'T EDIT THIS LINE; Version: 1") { + if let Ok(cfg) = migrations::WasabiConfigFileV1::migrate_to_v2(config) { + cfg.save_to_file(); + return cfg; } } else { - let config = migrations::WasabiConfigFileV0::migrate().unwrap_or_default(); - config.save_to_file(); - config + if let Ok(v1) = migrations::WasabiConfigFileV0::migrate_to_v1(config) { + let cfg = migrations::WasabiConfigFileV1::migrate_to_v2_raw(v1); + cfg.save_to_file(); + return cfg; + } } - }; + } - config.augment_from_args(); - config + println!("Error loading config. Resetting."); + Self::load_and_save_defaults() } pub fn save_to_file(&self) { let config_path = Self::get_config_path(); - let toml: String = toml::to_string(&self).unwrap(); - if Path::new(&config_path).exists() { - fs::remove_file(&config_path).expect("Error deleting old config"); - } - let mut file = fs::File::create(&config_path).unwrap(); - file.write_all(b"# DON'T EDIT THIS LINE; Version: 1\n\n") - .unwrap(); - file.write_all(toml.as_bytes()) - .expect("Error creating config"); - } - - fn augment_from_args(&mut self) { - let matches = Command::new("wasabi") - .version(env!("CARGO_PKG_VERSION")) - .author(env!("CARGO_PKG_AUTHORS")) - .about(env!("CARGO_PKG_DESCRIPTION")) - .arg( - Arg::new("synth") - .help("The synthesizer to use") - .long_help( - "The synthesizer that is used to play the MIDI. \ - This can either be XSynth (recommended) or KDMAPI. KDMAPI \ - only works if you have OmniMIDI installed, and are using Windows", - ) - .short('S') - .long("synth") - .value_parser(Synth::from_str), - ) - .arg( - Arg::new("buffer-ms") - .help("The amount of time events are held in the buffer") - .long_help( - "The amount of time that events are held in the \ - buffer before being played. Higher numbers may increase \ - performance but also increase latency.", - ) - .short('b') - .long("buffer-ms") - .value_parser(f64_parser), - ) - .arg( - Arg::new("sfz-path") - .help("The path to an SFZ SoundFont") - .long_help( - "The path to any SFZ soundfont. In audio only mode \ - a soundfont must be passed either via the config file, or - this command line option. In the GUI you can set this under \ - `Open Synth Settings > SFZ Path`", - ) - .short('s') - .long("sfz-path") - .value_hint(ValueHint::FilePath), - ) - .arg( - Arg::new("dont-limit-layers") - .help("Do not apply the layer limit to the synth") - .long_help( - "This allows the synth to create as many layers as it \ - needs to play the MIDI file faithfully. Only turn this on if your \ - MIDI sounds bad, or your computer is running on GFuel", - ) - .long("dont-limit-layers") - .action(ArgAction::SetFalse), - ) - .arg( - Arg::new("layer-count") - .help("The amount of layers that the synthesizer can create") - .long_help( - "The maximum amount of voices the synth is \ - allowed to create per key per channel", - ) - .short('l') - .long("layer-count") - .value_parser(value_parser!(usize)), - ) - .arg( - Arg::new("vel-ignore") - .help("The range of note velocities that the synth will discard") - .long_help( - "Two numbers, comma seperated, that represent a range of velocities \ - that the synth will discard, making notes in the range inaudible.", - ) - .short('v') - .long("vel-ignore") - .value_parser(range_parser), - ) - .arg( - Arg::new("fade-out-kill") - .help("Once a voice is killed, fade it out") - .long_help( - "Once the synthesizer kills one of it's voices, it will fade it \ - out as opposed to simply cutting it off", - ) - .short('F') - .long("fade-out-kill") - .action(ArgAction::SetTrue), - ) - .arg( - Arg::new("no-effects") - .help("Disables the soundfont's effects") - .long_help( - "Disables soundfont audio effects. \ - This may improve the performance.", - ) - .short('N') - .long("no-effects") - .action(ArgAction::SetFalse), - ) - .arg( - Arg::new("note-speed") - .help("The speed that the notes travel on-screen") - .long_help( - "The speed at which the notes will move across the screen. This makes \ - the notes physically longer, causing them to move faster on-screen", - ) - .short('n') - .long("note-speed") - .value_parser(note_speed), - ) - .arg( - Arg::new("random-colors") - .help("Make each channel a random color") - .long_help("This causes each of the note channels to become a random color") - .short('r') - .long("random-colors") - .action(ArgAction::SetTrue), - ) - .arg( - Arg::new("key-range") - .help("The key range of the on-screen piano keyboard") - .long_help( - "Two numbers, comma seperated, that describe the range \ - of keys to be shown on the on-screen piano keyboard, the range must \ - be less than 255 and more than 0", - ) - .short('k') - .long("key-range") - .value_parser(range_parser), - ) - .arg( - Arg::new("midi-loading") - .help("How the MIDI is loaded into `wasabi`") - .long_help( - "The method in which the MIDI file is loaded into `wasabi`, the \ - two possible options are `ram`, which loads the MIDI file entirely into \ - RAM before beginning playback; and `live` which will read the MIDI file \ - as it's being played back. The latter method is for using with systems \ - with low memory", - ) - .short('m') - .long("midi-loading") - .value_parser(MidiLoading::from_str), - ) - .arg( - Arg::new("bg-color") - .help("The window background") - .long_help("A hex color string describing the background color of the window") - .long("bg-color") - .value_parser(color_parser), - ) - .arg( - Arg::new("bar-color") - .help("The color of the bar just above the piano") - .long_help( - "A hex color string describing the color of the bar just above \ - the on-screen piano keyboard", - ) - .long("bar-color") - .value_parser(color_parser), - ) - .arg( - Arg::new("hide-top-pannel") - .long_help( - "Hides the top panel from view when the app opens. It can be un-hidden \ - with Ctrl+F", - ) - .help("Hide the top panel") - .long("hide-top-pannel") - .action(ArgAction::SetFalse), - ) - .arg( - Arg::new("hide-statistics") - .help("Hide the statistics window") - .long_help( - "Hides the statistics window from view when the app opens. It can be \ - un-hidden with Ctrl+G", - ) - .long("hide-statistics") - .action(ArgAction::SetFalse), - ) - .arg( - Arg::new("fullscreen") - .help("Start `wasabi` in fullscreen") - .long_help( - "Starts `wasabi` in fullscreen mode. `wasabi` will use \ - borderless fullscreen mode on Linux systems running Wayland, \ - and exclusive fullscreen mode for everyone else", - ) - .short('f') - .long("fullscreen") - .action(ArgAction::SetTrue), - ) - .arg( - Arg::new("midi-file") - .value_hint(ValueHint::FilePath) - .help("The MIDI file to immediately begin playing") - .long_help( - "This MIDI file is played immediately after the app's launch. \ - This argument is required to use the `--audio-only` option", - ), - ) - .get_matches(); - - macro_rules! set { - ($one:ident.$two:ident,$value:expr) => { - if let Some(value) = matches.get_one($value) { - self.$one.$two = *value; - } - }; - } - - macro_rules! set_flag { - ($one:ident.$two:ident,$value:expr) => { - if matches!(matches.value_source($value), Some(ValueSource::CommandLine)) { - if let Some(value) = matches.get_one($value) { - self.$one.$two = *value; - } - } - }; - } - - macro_rules! set_owned { - ($one:ident.$two:ident,$value:expr,$type:ty) => { - if let Some(value) = matches.get_one::<$type>($value) { - self.$one.$two = value.to_owned(); - } - }; + let cfg: String = serde_json::to_string_pretty(&self).unwrap(); + if let Ok(mut file) = fs::File::create(&config_path) { + file.write_all(Self::VERSION_TEXT.as_bytes()).unwrap(); + file.write_all(cfg.as_bytes()) + .expect("Error creating config"); } - - self.load_midi_file = matches.get_one::("midi-file").map(|f| f.to_owned()); - - // Synth settings - set!(synth.synth, "synth"); - set!(synth.buffer_ms, "buffer-ms"); - set_owned!(synth.sfz_path, "sfz-path", String); - set_flag!(synth.limit_layers, "dont-limit-layers"); - set!(synth.layer_count, "layer-count"); - set_owned!(synth.vel_ignore, "vel-ignore", RangeInclusive); - set_flag!(synth.fade_out_kill, "fade-out-kill"); - set_flag!(synth.use_effects, "no-effects"); - - // MIDI settings - set!(midi.note_speed, "note-speed"); - set_flag!(midi.random_colors, "random-colors"); - set_owned!(midi.key_range, "key-range", RangeInclusive); - set!(midi.midi_loading, "midi-loading"); - - // Visual settings - set!(visual.bg_color, "bg-color"); - set!(visual.bar_color, "bar-color"); - set_flag!(visual.show_top_pannel, "hide-top-pannel"); - set_flag!(visual.show_statistics, "hide-statistics"); - set_flag!(visual.fullscreen, "fullscreen"); } fn load_and_save_defaults() -> Self { - let _ = fs::remove_file(Self::get_config_path()); let cfg = Self::default(); Self::save_to_file(&cfg); cfg } - fn get_config_path() -> String { + fn get_config_dir() -> PathBuf { if let Some(base_dirs) = BaseDirs::new() { let mut path: PathBuf = base_dirs.config_dir().to_path_buf(); path.push("wasabi"); - path.push(CONFIG_PATH); - if std::fs::create_dir_all(path.parent().unwrap()).is_ok() { - if let Some(path) = path.to_str() { - path.to_string() - } else { - CONFIG_PATH.to_string() - } - } else { - CONFIG_PATH.to_string() + if std::fs::create_dir_all(&path).is_ok() { + return path; } - } else { - CONFIG_PATH.to_string() } + + PathBuf::from("./") + } + + fn get_config_path() -> PathBuf { + let mut path = Self::get_config_dir(); + path.push("wasabi-config.toml"); + + path + } + + pub fn get_palettes_dir() -> PathBuf { + let mut path = Self::get_config_dir(); + path.push("palettes"); + std::fs::create_dir_all(&path).unwrap_or_default(); + + path } } + +// endregion diff --git a/src/state.rs b/src/state.rs index c652a76..d9c5dbe 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,10 +1,46 @@ use std::path::PathBuf; -#[derive(Clone, Default)] +#[derive(Default, PartialEq)] +pub enum SettingsTab { + #[default] + Visual, + Midi, + Synth, + SoundFonts, +} + pub struct WasabiState { pub fullscreen: bool, - pub settings_visible: bool, - pub xsynth_settings_visible: bool, - pub last_midi_file: Option, - // pub last_sfz_file: Option, + + pub panel_pinned: bool, + pub panel_popup_id: egui::Id, + pub stats_visible: bool, + + pub show_settings: bool, + pub show_shortcuts: bool, + pub show_about: bool, + + pub settings_tab: SettingsTab, + + pub last_location: PathBuf, +} + +impl Default for WasabiState { + fn default() -> Self { + Self { + fullscreen: false, + + panel_pinned: true, + panel_popup_id: egui::Id::new("options_popup"), + stats_visible: true, + + show_settings: false, + show_shortcuts: false, + show_about: false, + + settings_tab: SettingsTab::default(), + + last_location: PathBuf::default(), + } + } } diff --git a/src/utils.rs b/src/utils.rs index a441e8e..81b564e 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,3 +1,22 @@ pub fn calculate_border_width(width_pixels: f32, keys_len: f32) -> f32 { ((width_pixels / keys_len) / 12.0).clamp(1.0, 5.0).round() * 2.0 } + +pub fn convert_seconds_to_time_string(sec: f64) -> String { + let time_millis = (sec * 10.0) as i64 % 10; + let time_sec = sec as i64 % 60; + let time_min = sec as i64 / 60; + + format!( + "{}{:0width$}:{:0width$}.{}", + if time_sec + time_millis < 0 { + '-' + } else { + '\0' + }, + time_min.abs(), + time_sec.abs(), + time_millis.abs(), + width = 2 + ) +}