From c72b228d5b25978c6690da336f109f902e890956 Mon Sep 17 00:00:00 2001 From: Philipp Mildenberger Date: Sat, 9 Dec 2023 15:19:25 +0100 Subject: [PATCH 1/4] Updated all cargo dependencies and fixed clippy lints --- Cargo.lock | 1213 +++++++++++++++++++---------- loopers-common/Cargo.toml | 4 +- loopers-common/src/api.rs | 42 +- loopers-common/src/config.rs | 15 +- loopers-common/src/gui_channel.rs | 19 +- loopers-common/src/music.rs | 26 +- loopers-engine/Cargo.toml | 14 +- loopers-engine/src/lib.rs | 108 +-- loopers-engine/src/looper.rs | 164 ++-- loopers-engine/src/metronome.rs | 1 + loopers-engine/src/sample.rs | 19 +- loopers-engine/src/session.rs | 5 +- loopers-engine/src/trigger.rs | 2 +- loopers-gui/Cargo.toml | 12 +- loopers-gui/src/app.rs | 254 +++--- loopers-gui/src/lib.rs | 9 +- loopers-gui/src/skia.rs | 64 +- loopers-gui/src/widgets.rs | 80 +- loopers/Cargo.toml | 17 +- loopers/src/loopers_jack.rs | 133 ++-- loopers/src/main.rs | 79 +- 21 files changed, 1323 insertions(+), 957 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dd7570d..cac3b00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,35 +10,89 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" -version = "0.7.18" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] [[package]] -name = "ansi_term" -version = "0.12.1" +name = "android-tzdata" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ - "winapi", + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", ] [[package]] name = "arrayvec" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "atomic" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b88d82667eca772c4aa12f0f1348b3ae643424c8876448f3f7bd5787032e234c" +checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994" dependencies = [ - "autocfg", + "bytemuck", ] [[package]] @@ -60,38 +114,38 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "base64" -version = "0.13.0" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] name = "bindgen" -version = "0.59.2" +version = "0.69.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8" +checksum = "9ffcebc3849946a7170a05992aac39da343a90676ab392c51a4280981d6379c2" dependencies = [ - "bitflags", + "bitflags 2.4.1", "cexpr", "clang-sys", - "clap", - "env_logger", "lazy_static", "lazycell", "log", "peeking_take_while", + "prettyplease", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", + "syn 2.0.39", "which", ] [[package]] name = "bit-set" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ "bit-vec", ] @@ -109,34 +163,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "bstr" -version = "0.2.17" +name = "bitflags" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" -dependencies = [ - "lazy_static", - "memchr", - "regex-automata", - "serde", -] +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "bumpalo" -version = "3.10.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] -name = "byteorder" -version = "1.4.3" +name = "bytemuck" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] [[package]] name = "bytes" -version = "1.1.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cast" @@ -146,9 +208,12 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.73" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] [[package]] name = "cexpr" @@ -167,28 +232,23 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.19" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ - "libc", - "num-integer", + "android-tzdata", + "iana-time-zone", + "js-sys", "num-traits", - "time", - "winapi", + "wasm-bindgen", + "windows-targets 0.48.5", ] -[[package]] -name = "chunked_transfer" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e" - [[package]] name = "clang-sys" -version = "1.3.3" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a050e2153c5be08febd6734e29298e844fdb0fa21aeddd63b4eb7baa106c69b" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" dependencies = [ "glob", "libc", @@ -201,15 +261,44 @@ version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ - "ansi_term", - "atty", - "bitflags", - "strsim", + "bitflags 1.3.2", "textwrap", "unicode-width", - "vec_map", ] +[[package]] +name = "clap" +version = "4.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "convert_case" version = "0.4.0" @@ -218,26 +307,26 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "core-foundation-sys" -version = "0.6.2" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "coreaudio-rs" -version = "0.11.1" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d3719e41553597cc2d53170cdcb5c72a4c0b35d0c95a0fce1e2a7a4c2c74440" +checksum = "321077172d79c662f64f5071a03120748d5bb652f5231570141be24cfcd2bace" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation-sys", "coreaudio-sys", ] [[package]] name = "coreaudio-sys" -version = "0.2.10" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dff444d80630d7073077d38d40b4501fd518bd2b922c2a55edcc8b0f7be57e6" +checksum = "f3120ebb80a9de008e638ad833d4127d50ea3d3a960ea23ea69bc66d9358a028" dependencies = [ "bindgen", ] @@ -259,10 +348,10 @@ checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f" dependencies = [ "atty", "cast", - "clap", + "clap 2.34.0", "criterion-plot", "csv", - "itertools", + "itertools 0.10.5", "lazy_static", "num-traits", "oorandom", @@ -284,14 +373,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" dependencies = [ "cast", - "itertools", + "itertools 0.10.5", ] [[package]] name = "crossbeam-channel" -version = "0.5.5" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ "cfg-if", "crossbeam-utils", @@ -299,9 +388,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ "cfg-if", "crossbeam-epoch", @@ -310,23 +399,22 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.9" +version = "0.9.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", "memoffset", - "once_cell", "scopeguard", ] [[package]] name = "crossbeam-queue" -version = "0.3.5" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f25d8400f4a7a5778f0e4e52384a48cbd9b5c495d110786187fc750075277a2" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" dependencies = [ "cfg-if", "crossbeam-utils", @@ -334,32 +422,30 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.10" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", - "once_cell", ] [[package]] name = "csv" -version = "1.1.6" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" +checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" dependencies = [ - "bstr", "csv-core", - "itoa 0.4.8", + "itoa", "ryu", "serde", ] [[package]] name = "csv-core" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" dependencies = [ "memchr", ] @@ -374,83 +460,84 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn", + "syn 1.0.109", ] [[package]] name = "dirs" -version = "4.0.0" +version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ "dirs-sys", ] [[package]] name = "dirs-sys" -version = "0.3.7" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", + "option-ext", "redox_users", - "winapi", + "windows-sys 0.48.0", ] [[package]] name = "either" -version = "1.7.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] -name = "env_logger" -version = "0.9.0" +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", + "libc", + "windows-sys 0.52.0", ] [[package]] name = "fastrand" -version = "1.7.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" -dependencies = [ - "instant", -] +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "fern" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdd7b0849075e79ee9a1836df22c717d1eba30451796fdc631b04565dd11e2a" +checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee" dependencies = [ "log", ] [[package]] name = "filetime" -version = "0.2.17" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", "redox_syscall", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "flate2" -version = "1.0.24" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", "miniz_oxide", @@ -464,29 +551,111 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.0.1" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ - "matches", "percent-encoding", ] [[package]] name = "futures" -version = "0.1.31" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" + +[[package]] +name = "futures-executor" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" + +[[package]] +name = "futures-macro" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "futures-sink" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" + +[[package]] +name = "futures-task" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" + +[[package]] +name = "futures-util" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] [[package]] name = "getrandom" -version = "0.2.7" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", ] [[package]] @@ -511,9 +680,9 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "half" @@ -521,11 +690,17 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -536,66 +711,95 @@ dependencies = [ "libc", ] +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys 0.48.0", +] + [[package]] name = "hound" -version = "3.4.0" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549" +checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f" [[package]] -name = "humantime" -version = "2.1.0" +name = "iana-time-zone" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] [[package]] name = "idna" -version = "0.2.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] [[package]] -name = "instant" -version = "0.1.12" +name = "indexmap" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ - "cfg-if", + "equivalent", + "hashbrown", ] [[package]] name = "itertools" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] -name = "itoa" -version = "0.4.8" +name = "itertools" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" +checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +dependencies = [ + "either", +] [[package]] name = "itoa" -version = "1.0.2" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "jack" -version = "0.10.0" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce722655a29b13bb98ec7e8ba9dc65d670b9b37c7b1c09775c7f7516811c5a36" +checksum = "0e5a18a3c2aefb354fb77111ade228b20267bdc779de84e7a4ccf7ea96b9a6cd" dependencies = [ - "bitflags", + "bitflags 1.3.2", "jack-sys", "lazy_static", "libc", @@ -604,22 +808,23 @@ dependencies = [ [[package]] name = "jack-sys" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9d70559ff166d148ccb750ddd77702af760718f3a752c731add168c22c16a9f" +checksum = "6013b7619b95a22b576dfb43296faa4ecbe40abbdb97dfd22ead520775fc86ab" dependencies = [ - "bitflags", + "bitflags 1.3.2", "lazy_static", "libc", "libloading", + "log", "pkg-config", ] [[package]] name = "js-sys" -version = "0.3.58" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" dependencies = [ "wasm-bindgen", ] @@ -644,36 +849,56 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "libloading" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" dependencies = [ "cfg-if", "winapi", ] [[package]] -name = "log" -version = "0.4.17" +name = "libm" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" dependencies = [ - "cfg-if", + "bitflags 2.4.1", + "libc", + "redox_syscall", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + [[package]] name = "loopers" version = "0.2.0" dependencies = [ "bytes", "chrono", - "clap", + "clap 4.4.11", "coreaudio-rs", "crossbeam-channel", "crossbeam-queue", @@ -696,6 +921,7 @@ name = "loopers-common" version = "0.2.0" dependencies = [ "arrayvec", + "bytemuck", "bytes", "crossbeam-channel", "crossbeam-queue", @@ -722,7 +948,7 @@ dependencies = [ "fern", "futures", "hound", - "itertools", + "itertools 0.12.0", "lazy_static", "log", "loopers-common", @@ -753,23 +979,17 @@ dependencies = [ "tinyfiledialogs", ] -[[package]] -name = "matches" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" - [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memoffset" -version = "0.6.5" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg", ] @@ -782,57 +1002,38 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.5.3" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] [[package]] name = "nom" -version = "7.1.1" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", ] -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" -dependencies = [ - "hermit-abi", - "libc", + "libm", ] [[package]] name = "once_cell" -version = "1.13.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" @@ -840,6 +1041,12 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "peeking_take_while" version = "0.1.2" @@ -848,21 +1055,33 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "percent-encoding" -version = "2.1.0" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.25" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "plotters" -version = "0.3.2" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9428003b84df1496fb9d6eeee9c5f8145cb41ca375eb0dad204328888832811f" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" dependencies = [ "num-traits", "plotters-backend", @@ -873,52 +1092,62 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" [[package]] name = "plotters-svg" -version = "0.3.2" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0918736323d1baff32ee0eade54984f6f201ad7e97d5cfb5d6ab4a358529615" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" dependencies = [ "plotters-backend", ] [[package]] name = "ppv-lite86" -version = "0.2.16" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "prettyplease" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +dependencies = [ + "proc-macro2", + "syn 2.0.39", +] [[package]] name = "proc-macro2" -version = "1.0.40" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ "unicode-ident", ] [[package]] name = "proptest" -version = "1.0.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0d9cc07f18492d879586c92b485def06bc850da3118075cd45d50e9c95b0e5" +checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" dependencies = [ "bit-set", - "bitflags", - "byteorder", + "bit-vec", + "bitflags 2.4.1", "lazy_static", "num-traits", - "quick-error 2.0.1", "rand", "rand_chacha", "rand_xorshift", "regex-syntax", "rusty-fork", "tempfile", + "unarray", ] [[package]] @@ -927,17 +1156,11 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - [[package]] name = "quote" -version = "1.0.20" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -965,9 +1188,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] @@ -983,93 +1206,85 @@ dependencies = [ [[package]] name = "rayon" -version = "1.5.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" dependencies = [ - "autocfg", - "crossbeam-deque", "either", "rayon-core", ] [[package]] name = "rayon-core" -version = "1.9.3" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "num_cpus", ] [[package]] name = "redox_syscall" -version = "0.2.13" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "redox_users" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ "getrandom", - "redox_syscall", + "libredox", "thiserror", ] [[package]] name = "regex" -version = "1.6.0" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", + "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.1.10" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] [[package]] name = "regex-syntax" -version = "0.6.27" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" - -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "ring" -version = "0.16.20" +version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" dependencies = [ "cc", + "getrandom", "libc", - "once_cell", "spin", "untrusted", - "web-sys", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -1087,16 +1302,39 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.38.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfeae074e687625746172d639330f1de242a178bf3189b51e35a7a21573513ac" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "rustls" -version = "0.20.6" +version = "0.21.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", "ring", + "rustls-webpki", "sct", - "webpki", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", ] [[package]] @@ -1106,16 +1344,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" dependencies = [ "fnv", - "quick-error 1.2.3", + "quick-error", "tempfile", "wait-timeout", ] [[package]] name = "ryu" -version = "1.0.10" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "same-file" @@ -1128,15 +1366,15 @@ dependencies = [ [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ "ring", "untrusted", @@ -1144,11 +1382,11 @@ dependencies = [ [[package]] name = "sdl2" -version = "0.35.2" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7959277b623f1fb9e04aea73686c3ca52f01b2145f8ea16f4ff30d8b7623b1a" +checksum = "8356b2697d1ead5a34f40bcc3c5d3620205fe0c7be0a14656223bfeec0258891" dependencies = [ - "bitflags", + "bitflags 1.3.2", "lazy_static", "libc", "sdl2-sys", @@ -1156,9 +1394,9 @@ dependencies = [ [[package]] name = "sdl2-sys" -version = "0.35.2" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3586be2cf6c0a8099a79a12b4084357aa9b3e0b0d7980e3b67aaf7a9d55f9f0" +checksum = "26bcacfdd45d539fb5785049feb0038a63931aa896c7763a2a12e125ec58bd29" dependencies = [ "cfg-if", "libc", @@ -1167,15 +1405,15 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.12" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.139" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0171ebb889e45aa68b44aee0859b3eede84c6f5f5c228e6f140c0b2a0a46cad6" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] @@ -1192,37 +1430,46 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.139" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1d3230c1de7932af58ad8ffbe1d784bd55efd5a9d84ac24f69c72d83543dfb" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] name = "serde_json" -version = "1.0.82" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ - "itoa 1.0.2", + "itoa", "ryu", "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" +dependencies = [ + "serde", +] + [[package]] name = "shlex" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" [[package]] name = "skia-bindings" -version = "0.50.0" +version = "0.69.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f45439145d90b18bce3dc0bfb50c73b25a75666ac391089b23e4ee245a7f67" +checksum = "8b4b5af96ee7d895763fa606f4531fddfb11de034217edd0c7beb9ea181efe5b" dependencies = [ "bindgen", "cc", @@ -1238,32 +1485,52 @@ dependencies = [ [[package]] name = "skia-safe" -version = "0.50.0" +version = "0.69.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5e8f14ca7a6e7338e7cf6bfdad314c4e735003c1a56001e1e545bd22c994d43" +checksum = "4a3d25acaedea0a8ed1dac52f383fc90276f5679a68e3f84c5fb7f7bde8934ff" dependencies = [ - "bitflags", + "bitflags 2.4.1", "lazy_static", "skia-bindings", ] +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "spin" -version = "0.5.2" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "strsim" -version = "0.8.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] [[package]] name = "syn" -version = "1.0.98" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -1272,9 +1539,9 @@ dependencies = [ [[package]] name = "tar" -version = "0.4.38" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" dependencies = [ "filetime", "libc", @@ -1283,25 +1550,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.3.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if", "fastrand", - "libc", "redox_syscall", - "remove_dir_all", - "winapi", -] - -[[package]] -name = "termcolor" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" -dependencies = [ - "winapi-util", + "rustix", + "windows-sys 0.48.0", ] [[package]] @@ -1315,33 +1572,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.31" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.31" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn", -] - -[[package]] -name = "time" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", + "syn 2.0.39", ] [[package]] @@ -1375,92 +1621,121 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toml" -version = "0.5.9" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" dependencies = [ + "indexmap", "serde", + "serde_spanned", + "toml_datetime", + "winnow", ] +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" [[package]] name = "unicode-ident" -version = "1.0.2" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] [[package]] name = "unicode-width" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "untrusted" -version = "0.7.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "2.5.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97acb4c28a254fd7a4aeec976c46a7fa404eac4d7c134b30c75144846d7cb8f" +checksum = "f8cdd25c339e200129fe4de81451814e5228c9b771d57378817d6117cc2b3f97" dependencies = [ "base64", - "chunked_transfer", "flate2", "log", "once_cell", "rustls", + "rustls-webpki", "url", - "webpki", "webpki-roots", ] [[package]] name = "url" -version = "2.2.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", - "matches", "percent-encoding", ] [[package]] -name = "vec_map" -version = "0.8.2" +name = "utf8parse" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "version-compare" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe88247b92c1df6b6de80ddc290f3976dbdf2f5f5d3fd049a9fb598c6dd5ca73" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" [[package]] name = "wait-timeout" @@ -1473,21 +1748,14 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.3.2" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" dependencies = [ "same-file", - "winapi", "winapi-util", ] -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1496,9 +1764,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.81" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1506,24 +1774,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.81" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.39", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.81" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1531,61 +1799,49 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.81" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.81" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] name = "web-sys" -version = "0.3.58" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" dependencies = [ "js-sys", "wasm-bindgen", ] -[[package]] -name = "webpki" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "webpki-roots" -version = "0.22.4" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1c760f0d366a6c24a02ed7816e23e691f5d92291f94d15e836006fd11b04daf" -dependencies = [ - "webpki", -] +checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" [[package]] name = "which" -version = "4.2.5" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ "either", - "lazy_static", - "libc", + "home", + "once_cell", + "rustix", ] [[package]] @@ -1606,9 +1862,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] @@ -1619,60 +1875,167 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" -version = "0.36.1" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winnow" +version = "0.5.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67b5f0a4e7a27a64c651977932b9dc5667ca7fc31ac44b03ed37a0cf42fdfff" +dependencies = [ + "memchr", +] [[package]] name = "xattr" -version = "0.2.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +checksum = "fbc6ab6ec1907d1a901cdbcd2bd4cb9e7d64ce5c9739cbb97d3c391acd8c7fae" dependencies = [ "libc", ] [[package]] name = "xml-rs" -version = "0.8.4" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" +checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" diff --git a/loopers-common/Cargo.toml b/loopers-common/Cargo.toml index 69fc4d4..cabb2d2 100644 --- a/loopers-common/Cargo.toml +++ b/loopers-common/Cargo.toml @@ -11,8 +11,8 @@ license = "MIT OR Apache-2.0" [dependencies] bytes = "1.0" serde = { version = "1.0", features = ["derive", "rc"] } - -csv = "1.1" +bytemuck = { version = "1", features = ["derive"] } +csv = "1.3" crossbeam-queue = "0.3" crossbeam-channel = "0.5" diff --git a/loopers-common/src/api.rs b/loopers-common/src/api.rs index ab894a4..5fc7e5a 100644 --- a/loopers-common/src/api.rs +++ b/loopers-common/src/api.rs @@ -1,5 +1,5 @@ use crate::gui_channel::WAVEFORM_DOWNSAMPLE; -use crate::music::{SavedMetricStructure}; +use crate::music::SavedMetricStructure; use derive_more::{Add, Div, Mul, Sub}; use serde::{Deserialize, Serialize}; use std::ops::{Index, IndexMut}; @@ -140,7 +140,9 @@ impl LooperCommand { use Command::Looper; use LooperCommand::*; - let target_type = args.get(0).ok_or(format!("{} expects a target", command))?; + let target_type = args + .first() + .ok_or(format!("{} expects a target", command))?; let target = match *target_type { "All" => LooperTarget::All, @@ -173,7 +175,7 @@ impl LooperCommand { } else { let f = f32::from_str(v) .map_err(|_| format!("Invalid value for SetPan: '{}'", v))?; - if f < -1.0 || f > 1.0 { + if !(-1.0..=1.0).contains(&f) { return Err("Value for SetPan must be between -1 and 1".to_string()); } Some(f) @@ -185,7 +187,7 @@ impl LooperCommand { target, ) }) - }, + } "SetLevel" => { let v = args.get(1).ok_or( @@ -197,21 +199,15 @@ impl LooperCommand { } else { let f = f32::from_str(v) .map_err(|_| format!("Invalid value for SetLevel: '{}'", v))?; - if f < 0.0 || f > 1.0 { + if !(0.0..=1.0).contains(&f) { return Err("Value for SetLevel must be between 0 and 1".to_string()); } Some(f) }; - Box::new(move |d| { - Looper( - SetLevel(arg.unwrap_or(d.data as f32 / 127.0)), - target, - ) - }) + Box::new(move |d| Looper(SetLevel(arg.unwrap_or(d.data as f32 / 127.0)), target)) } - "1/2x" => Box::new(move |_| Looper(SetSpeed(LooperSpeed::Half), target)), "1x" => Box::new(move |_| Looper(SetSpeed(LooperSpeed::One), target)), "2x" => Box::new(move |_| Looper(SetSpeed(LooperSpeed::Double), target)), @@ -275,9 +271,9 @@ impl Command { "SetTime" => { let arg = args - .get(0) + .first() .and_then(|s| i64::from_str(s).ok()) - .map(|t| FrameTime(t)) + .map(FrameTime) .ok_or("SetTime expects a single numeric argument, time".to_string())?; Box::new(move |_| Command::SetTime(arg)) } @@ -285,7 +281,7 @@ impl Command { "AddLooper" => Box::new(|_| Command::AddLooper), "SelectLooperById" => { let arg = args - .get(0) + .first() .and_then(|s| u32::from_str(s).ok()) .ok_or( "SelectLooperById expects a single numeric argument, the looper id" @@ -297,7 +293,7 @@ impl Command { } "SelectLooperByIndex" => { - let arg = args.get(0).and_then(|s| u8::from_str(s).ok()).ok_or( + let arg = args.first().and_then(|s| u8::from_str(s).ok()).ok_or( "SelectLooperByIndex expects a single numeric argument, the looper index" .to_string(), )?; @@ -311,8 +307,8 @@ impl Command { "NextPart" => Box::new(|_| Command::NextPart), "GoToPart" => { let arg = args - .get(0) - .and_then(|s| match s.as_ref() { + .first() + .and_then(|s| match *s { "A" => Some(Part::A), "B" => Some(Part::B), "C" => Some(Part::C), @@ -326,8 +322,8 @@ impl Command { "SetQuantizationMode" => { let arg = args - .get(0) - .and_then(|s| match s.as_ref() { + .first() + .and_then(|s| match *s { "Free" => Some(QuantizationMode::Free), "Beat" => Some(QuantizationMode::Beat), "Measure" => Some(QuantizationMode::Measure), @@ -341,7 +337,7 @@ impl Command { } "SetMetronomeLevel" => { - let arg = args.get(0).and_then(|s| u8::from_str(s).ok()).ok_or( + let arg = args.first().and_then(|s| u8::from_str(s).ok()).ok_or( "SetMetronomeLevel expects a single numeric argument, the level between 0-100" .to_string(), )?; @@ -404,6 +400,7 @@ impl PartSet { } pub fn is_empty(&self) -> bool { + #[allow(clippy::nonminimal_bool)] !(self.a || self.b || self.c || self.c) } } @@ -440,7 +437,8 @@ impl IndexMut for PartSet { pub static PARTS: [Part; 4] = [Part::A, Part::B, Part::C, Part::D]; -#[derive(Serialize, Deserialize, Clone, Copy, Debug, Eq, PartialEq, Hash)] +#[derive(bytemuck::NoUninit, Serialize, Deserialize, Clone, Copy, Debug, Eq, PartialEq, Hash)] +#[repr(u8)] pub enum LooperMode { Recording, Overdubbing, diff --git a/loopers-common/src/config.rs b/loopers-common/src/config.rs index 6a06457..23ca120 100644 --- a/loopers-common/src/config.rs +++ b/loopers-common/src/config.rs @@ -34,7 +34,7 @@ mod tests { let mapping = MidiMapping::from_file( &file.path().to_string_lossy(), - &File::open(&file.path()).unwrap(), + &File::open(file.path()).unwrap(), ) .unwrap(); @@ -74,18 +74,11 @@ mod tests { pub static FILE_HEADER: &str = "Channel\tController\tData\tCommand\tArg1\tArg2\tArg3"; +#[derive(Default)] pub struct Config { pub midi_mappings: Vec, } -impl Config { - pub fn new() -> Config { - Config { - midi_mappings: vec![], - } - } -} - #[derive(Debug, PartialEq)] pub enum DataValue { Any, @@ -105,7 +98,7 @@ impl DataValue { } } - let split: Vec = s.split("-").filter_map(|s| u8::from_str(s).ok()).collect(); + let split: Vec = s.split('-').filter_map(|s| u8::from_str(s).ok()).collect(); if split.len() == 2 && split[0] <= 127 && split[1] <= 127 && split[0] < split[1] { return Some(DataValue::Range(split[0], split[1])); @@ -180,7 +173,7 @@ impl MidiMapping { u8::from_str(c) .map_err(|_| "Channel must be * or a number".to_string()) .and_then(|c| { - if c >= 1 && c <= 16 { + if (1..=16).contains(&c) { Ok(c) } else { Err("Channel must be between 1 and 16".to_string()) diff --git a/loopers-common/src/gui_channel.rs b/loopers-common/src/gui_channel.rs index 6853ff8..3ad9bed 100644 --- a/loopers-common/src/gui_channel.rs +++ b/loopers-common/src/gui_channel.rs @@ -65,14 +65,15 @@ pub enum GuiCommand { AddGlobalTrigger(FrameTime, Command), } -#[derive(Clone)] +#[derive(Clone, Default)] pub enum LogLevel { + #[default] Info, Warn, Error, } -#[derive(Clone)] +#[derive(Clone, Default)] pub struct LogMessage { buffer: ArrayVec, len: usize, @@ -81,14 +82,6 @@ pub struct LogMessage { } impl LogMessage { - pub fn new() -> Self { - LogMessage { - buffer: ArrayVec::new(), - len: 0, - level: LogLevel::Info, - } - } - pub fn error() -> Self { LogMessage { buffer: ArrayVec::new(), @@ -131,7 +124,7 @@ impl GuiSender { let sender = GuiSender { cmd_channel: Some(tx), - cur_message: LogMessage::new(), + cur_message: LogMessage::default(), log_channel: Some(log_tx), }; @@ -146,7 +139,7 @@ impl GuiSender { pub fn disconnected() -> GuiSender { GuiSender { cmd_channel: None, - cur_message: LogMessage::new(), + cur_message: LogMessage::default(), log_channel: None, } } @@ -165,7 +158,7 @@ impl GuiSender { } } - pub fn send_log(&mut self, message: LogMessage) -> () { + pub fn send_log(&mut self, message: LogMessage) { if let Err(e) = self.send_log_with_result(message) { warn!("Failed to send message to gui: {}", e); } diff --git a/loopers-common/src/music.rs b/loopers-common/src/music.rs index 2168a30..aff22a8 100644 --- a/loopers-common/src/music.rs +++ b/loopers-common/src/music.rs @@ -82,10 +82,10 @@ mod tests { let next_beat_time = tempo.next_full_beat(FrameTime(time)); assert!( - next_beat_time.0 <= time + frames as i64, + next_beat_time.0 <= time + frames, "{} > {} (time = {})", next_beat_time.0, - time + frames as i64, + time + frames, time ); @@ -143,9 +143,7 @@ pub struct Tempo { impl Tempo { pub fn new(bpm: u64) -> Tempo { assert!(bpm > 0, "bpm must be positive"); - Tempo { - bpm - } + Tempo { bpm } } pub fn from_bpm(bpm: f32) -> Tempo { @@ -192,17 +190,17 @@ impl SavedMetricStructure { pub fn to_ms(&self) -> Result { let bpm = match self.tempo { SavedTempo { bpm: Some(bpm), .. } => Ok(Tempo::new(bpm)), - SavedTempo { samples_per_beat: Some(spb), ..} => - Ok(Tempo::from_bpm( - ((get_sample_rate() as f64) / spb as f64 * 60.0) as f32)), - _ => Err("Neither bpm nor samples_per_beat supplied".to_string()) + SavedTempo { + samples_per_beat: Some(spb), + .. + } => Ok(Tempo::from_bpm( + ((get_sample_rate() as f64) / spb as f64 * 60.0) as f32, + )), + _ => Err("Neither bpm nor samples_per_beat supplied".to_string()), }?; - MetricStructure::new( - self.time_signature.upper, - self.time_signature.lower, - bpm, - ).ok_or("Invalid time signature".to_string()) + MetricStructure::new(self.time_signature.upper, self.time_signature.lower, bpm) + .ok_or("Invalid time signature".to_string()) } } diff --git a/loopers-engine/Cargo.toml b/loopers-engine/Cargo.toml index f9d203e..4c584b2 100644 --- a/loopers-engine/Cargo.toml +++ b/loopers-engine/Cargo.toml @@ -12,18 +12,18 @@ license = "MIT OR Apache-2.0" crossbeam-queue = "0.3" crossbeam-channel = "0.5" crossbeam-utils = "0.8" -atomic = "0.5.0" +atomic = "0.6.0" rand = "0.8" -hound = "3.4.0" -chrono = "0.4.11" +hound = "3.5.0" +chrono = "0.4.31" lazy_static = "1.4.0" bytes = "1.0" -futures = "0.1" -dirs = "4" +futures = "0.3" +dirs = "5" log = "0.4" -toml = "0.5" +toml = "0.8" serde_json = "1.0" -itertools = "0.10" +itertools = "0.12" [dependencies.loopers-common] path = "../loopers-common" diff --git a/loopers-engine/src/lib.rs b/loopers-engine/src/lib.rs index f7fac86..f6a065f 100644 --- a/loopers-engine/src/lib.rs +++ b/loopers-engine/src/lib.rs @@ -87,7 +87,7 @@ fn max_abs(b: &[f32]) -> f32 { } pub fn last_session_path() -> io::Result { - let mut config_path = dirs::config_dir().unwrap_or(PathBuf::new()); + let mut config_path = dirs::config_dir().unwrap_or_default(); config_path.push("loopers"); create_dir_all(&config_path)?; config_path.push(".last-session"); @@ -95,10 +95,10 @@ pub fn last_session_path() -> io::Result { } pub fn read_config() -> Result { - let mut mapping_path = dirs::config_dir().unwrap_or(PathBuf::new()); + let mut mapping_path = dirs::config_dir().unwrap_or_default(); mapping_path.push("loopers/midi_mappings.tsv"); - let mut config = Config::new(); + let mut config = Config::default(); match File::open(&mapping_path) { Ok(file) => match MidiMapping::from_file(&mapping_path.to_string_lossy(), &file) { @@ -141,7 +141,7 @@ impl Engine { gui_sender.send_log(error); } - Config::new() + Config::default() } }; @@ -223,7 +223,7 @@ impl Engine { m.reset(); } self.triggers.clear(); - self.set_time(FrameTime(-(self.measure_len().0 as i64))); + self.set_time(FrameTime(-self.measure_len().0)); for l in &mut self.loopers { l.handle_command(LooperCommand::Play); } @@ -233,8 +233,7 @@ impl Engine { self.loopers .iter_mut() .filter(|l| !l.deleted) - .skip(idx as usize) - .next() + .nth(idx as usize) } fn commands_from_midi<'a, H: Host<'a>>(&mut self, host: &mut H, events: &[MidiEvent]) { @@ -259,7 +258,13 @@ impl Engine { looper: &Looper, ) -> Option { let trigger_condition = match sync_mode { - Free => if time.0 < 0 { Some(TriggerCondition::Beat) } else { None }, + Free => { + if time.0 < 0 { + Some(TriggerCondition::Beat) + } else { + None + } + } QuantizationMode::Beat => Some(TriggerCondition::Beat), QuantizationMode::Measure => Some(TriggerCondition::Measure), }?; @@ -298,6 +303,7 @@ impl Engine { let triggers = &mut self.triggers; let gui_sender = &mut self.gui_sender; + #[allow(clippy::too_many_arguments)] fn handle_or_trigger( triggered: bool, ms: MetricStructure, @@ -345,8 +351,7 @@ impl Engine { .loopers .iter_mut() .filter(|l| !l.deleted) - .skip(idx as usize) - .next() + .nth(idx as usize) { selected = Some(l.id); handle_or_trigger( @@ -388,7 +393,7 @@ impl Engine { host: &mut H, path: &Path, ) -> Result<(), SaveLoadError> { - let mut file = File::open(&path)?; + let mut file = File::open(path)?; let mut contents = String::new(); file.read_to_string(&mut contents)?; @@ -402,13 +407,15 @@ impl Engine { if session.sample_rate != get_sample_rate() { let mut error = LogMessage::error(); - if let Err(_) = write!( + if write!( &mut error, "Session was saved with sample rate {}, but system rate \ is set to {}, playback will be affected", session.sample_rate, get_sample_rate() - ) { + ) + .is_err() + { error!("Different sample rate"); }; self.gui_sender.send_log(error); @@ -416,8 +423,10 @@ impl Engine { debug!("Restoring session: {:?}", session); - self.metric_structure = session.metric_structure.to_ms() - .map_err(|e| SaveLoadError::OtherError(e))?; + self.metric_structure = session + .metric_structure + .to_ms() + .map_err(SaveLoadError::OtherError)?; self.sync_mode = session.sync_mode; if let Some(metronome) = &mut self.metronome { @@ -593,21 +602,17 @@ impl Engine { .iter() .filter(|l| l.parts[engine.current_part]) .filter(|l| !l.deleted) - .skip(next) - .next() - { - engine.active = l.id; - } - } else { - if let Some(l) = engine - .loopers - .iter() - .filter(|l| !l.deleted) - .filter(|l| l.parts[engine.current_part]) - .next() + .nth(next) { engine.active = l.id; } + } else if let Some(l) = engine + .loopers + .iter() + .filter(|l| !l.deleted) + .find(|l| l.parts[engine.current_part]) + { + engine.active = l.id; } }); } @@ -717,13 +722,11 @@ impl Engine { if let Some(l) = self .loopers .iter() - .filter(|l| l.id == self.active && l.parts[self.current_part]) - .next() + .find(|l| l.id == self.active && l.parts[self.current_part]) .or(self .loopers .iter() - .filter(|l| !l.deleted && l.parts[self.current_part]) - .next()) + .find(|l| !l.deleted && l.parts[self.current_part])) { self.active = l.id; } @@ -731,7 +734,7 @@ impl Engine { // returns length fn measure_len(&self) -> FrameTime { - let bps = self.metric_structure.tempo.bpm() as f32 / 60.0; + let bps = self.metric_structure.tempo.bpm() / 60.0; let mspb = 1000.0 / bps; let mspm = mspb * self.metric_structure.time_signature.upper as f32; @@ -818,7 +821,13 @@ impl Engine { } } - fn process_loopers<'a, H: Host<'a>>(&mut self, host: &mut H, in_bufs: &[&[f32]], frames: u64, solo: bool) { + fn process_loopers<'a, H: Host<'a>>( + &mut self, + host: &mut H, + in_bufs: &[&[f32]], + frames: u64, + solo: bool, + ) { let mut time = self.time; let mut idx = 0usize; @@ -834,12 +843,13 @@ impl Engine { let next_time = (self.time + frames as i64) as u64; while time < next_time { - if let Some(_) = self + if self .triggers .iter() .peekable() .peek() .filter(|t| t.triggered_at().0 < next_time as i64) + .is_some() { // The unwrap is safe due to the preceding peek let trigger = self.triggers.pop_front().unwrap(); @@ -873,7 +883,7 @@ impl Engine { self.perform_looper_io( host, - &in_bufs, + in_bufs, FrameTime(time as i64), idx_range.clone(), solo, @@ -887,7 +897,7 @@ impl Engine { // there are no more triggers for this period, so just process the rest and finish self.perform_looper_io( host, - &in_bufs, + in_bufs, FrameTime(time as i64), idx..frames as usize, solo, @@ -943,6 +953,7 @@ impl Engine { // Step 3: Play current samples // Step 4: Record // Step 5: Update GUI + #[allow(clippy::too_many_arguments)] pub fn process<'a, H: Host<'a>>( &mut self, host: &mut H, @@ -957,13 +968,8 @@ impl Engine { self.commands_from_midi(host, midi_events); // Handle commands from the gui - loop { - match self.command_input.try_recv() { - Ok(c) => { - self.handle_command(host, &c, false); - } - Err(_) => break, - } + while let Ok(c) = self.command_input.try_recv() { + self.handle_command(host, &c, false); } // Remove any deleted loopers @@ -994,13 +1000,19 @@ impl Engine { self.output_right[i] = *r as f64; } - if (self.state != EngineState::Active && self.state != EngineState::Paused) && (!self.triggers.is_empty() || - self.loopers.iter().any(|l| l.local_mode() == LooperMode::Recording || - l.local_mode() == LooperMode::Overdubbing)) { + if (self.state != EngineState::Active && self.state != EngineState::Paused) + && (!self.triggers.is_empty() + || self.loopers.iter().any(|l| { + l.local_mode() == LooperMode::Recording + || l.local_mode() == LooperMode::Overdubbing + })) + { self.state = EngineState::Active; } - let solo = self.loopers.iter() + let solo = self + .loopers + .iter() .any(|l| l.parts[self.current_part] && !l.deleted && l.mode() == LooperMode::Soloed); if self.state == EngineState::Active { @@ -1015,9 +1027,11 @@ impl Engine { self.time += frames as i64; } + #[allow(clippy::needless_range_loop)] for i in 0..frames as usize { out_l[i] = self.output_left[i] as f32; } + #[allow(clippy::needless_range_loop)] for i in 0..frames as usize { out_r[i] = self.output_right[i] as f32; } diff --git a/loopers-engine/src/looper.rs b/loopers-engine/src/looper.rs index 3cdbc72..e247e3c 100644 --- a/loopers-engine/src/looper.rs +++ b/loopers-engine/src/looper.rs @@ -22,7 +22,6 @@ use std::mem::swap; use atomic::Atomic; use std::sync::atomic::Ordering; - #[cfg(test)] mod tests { use super::*; @@ -61,10 +60,7 @@ mod tests { expected, "backend has unexpected length" ); - assert_eq!( - looper.length(), expected, - "looper has unexpected length" - ); + assert_eq!(looper.length(), expected, "looper has unexpected length"); } fn looper_for_test() -> Looper { @@ -207,7 +203,7 @@ mod tests { input_right[i] = -(i as f32 + 1.0); } - let mut t = 0 as i64; + let mut t = 0_i64; l.process_input(t as u64, &[&input_left, &input_right], Part::A); process_until_done(&mut l); @@ -280,12 +276,7 @@ mod tests { l.transition_to(LooperMode::Playing); process_until_done(&mut l); - l.process_output( - FrameTime(0), - &mut [&mut o_l, &mut o_r], - Part::A, - true, - ); + l.process_output(FrameTime(0), &mut [&mut o_l, &mut o_r], Part::A, true); for i in 0..128 { assert_eq!(0.0, o_l[i]); @@ -297,12 +288,7 @@ mod tests { l.transition_to(LooperMode::Soloed); process_until_done(&mut l); - l.process_output( - FrameTime(0), - &mut [&mut o_l, &mut o_r], - Part::A, - true, - ); + l.process_output(FrameTime(0), &mut [&mut o_l, &mut o_r], Part::A, true); for i in 0..128 { assert_eq!(1.0, o_l[i]); @@ -316,12 +302,7 @@ mod tests { l.set_time(FrameTime(0)); process_until_done(&mut l); - l.process_output( - FrameTime(0), - &mut [&mut o_l, &mut o_r], - Part::B, - true, - ); + l.process_output(FrameTime(0), &mut [&mut o_l, &mut o_r], Part::B, true); for i in 0..128 { assert_eq!(0.0, o_l[i]); @@ -747,6 +728,7 @@ mod tests { const CROSS_FADE_SAMPLES: usize = 8192; struct StateMachine { + #[allow(clippy::type_complexity)] transitions: Vec<( Vec, Vec, @@ -828,7 +810,7 @@ struct TransferBuf { impl TransferBuf { pub fn contains_t(&self, time: FrameTime) -> bool { - return time.0 >= self.time.0 && time.0 < self.time.0 + self.size as i64; + time.0 >= self.time.0 && time.0 < self.time.0 + self.size as i64 } pub fn get_t(&self, time: FrameTime) -> Option<(DATA, DATA)> { @@ -846,18 +828,19 @@ fn compute_waveform(samples: &[Sample], downsample: usize) -> Waveform { let size = len / downsample + 1; let mut out = [Vec::with_capacity(size), Vec::with_capacity(size)]; + #[allow(clippy::needless_range_loop)] for c in 0..2 { for t in (0..len).step_by(downsample) { let mut p = 0f64; let end = downsample.min(len - t); for s in samples { for j in 0..end { - let i = t as usize + j; + let i = t + j; p += s.buffer[c][i].abs() as f64; } } - out[c].push((p as f64 / (samples.len() as f64 * end as f64)) as f32); + out[c].push((p / (samples.len() as f64 * end as f64)) as f32); } } @@ -903,6 +886,7 @@ impl WaveformGenerator { for i in 0..samples[0].len() { if self.size < WAVEFORM_DOWNSAMPLE { + #[allow(clippy::needless_range_loop)] for c in 0..2 { self.acc[c] += samples[c][i].abs(); } @@ -1038,7 +1022,7 @@ impl LooperBackend { } pub fn mode(&self) -> LooperMode { - return self.mode.load(Ordering::Relaxed); + self.mode.load(Ordering::Relaxed) } fn handle_msg(&mut self, msg: ControlMessage) -> bool /* continue */ { @@ -1131,23 +1115,19 @@ impl LooperBackend { } ControlMessage::SetPan(pan) => { self.pan = pan; - self.gui_sender.send_update(GuiCommand::LooperStateChange( - self.id, - self.current_state(), - )); + self.gui_sender + .send_update(GuiCommand::LooperStateChange(self.id, self.current_state())); } ControlMessage::SetLevel(level) => { self.level = level; - self.gui_sender.send_update(GuiCommand::LooperStateChange( - self.id, self.current_state() - )); + self.gui_sender + .send_update(GuiCommand::LooperStateChange(self.id, self.current_state())); } ControlMessage::SetParts(parts) => { self.parts = parts; - self.gui_sender.send_update(GuiCommand::LooperStateChange( - self.id, self.current_state() - )); - }, + self.gui_sender + .send_update(GuiCommand::LooperStateChange(self.id, self.current_state())); + } ControlMessage::Undo => { info!("Performing Undo on queue: {:?}", self.undo_queue); @@ -1156,9 +1136,8 @@ impl LooperBackend { self.redo_queue.push_back(change); } } - self.gui_sender.send_update(GuiCommand::LooperStateChange( - self.id, self.current_state() - )); + self.gui_sender + .send_update(GuiCommand::LooperStateChange(self.id, self.current_state())); } ControlMessage::Redo => { info!("Performing Redo on queue: {:?}", self.redo_queue); @@ -1167,9 +1146,8 @@ impl LooperBackend { self.undo_queue.push_back(change); } } - self.gui_sender.send_update(GuiCommand::LooperStateChange( - self.id, self.current_state() - )); + self.gui_sender + .send_update(GuiCommand::LooperStateChange(self.id, self.current_state())); } ControlMessage::StopOutput => { self.should_output = false; @@ -1198,7 +1176,8 @@ impl LooperBackend { } } else { t - }.rem_euclid(self.length.load(Ordering::Relaxed) as i64) as usize + } + .rem_euclid(self.length.load(Ordering::Relaxed) as i64) as usize } fn fill_output(&mut self) { @@ -1209,7 +1188,7 @@ impl LooperBackend { // make sure we don't pass our input and don't spend too much time doing this let mut count = 0; let end = self.in_time.0 + sample_len as i64; - while self.out_time.0 + 1 < end as i64 + while self.out_time.0 + 1 < end && count < 32 && self.out_queue.len() < self.out_queue.capacity() / 2 { @@ -1226,10 +1205,12 @@ impl LooperBackend { continue; } + #[allow(clippy::needless_range_loop)] for i in 0..2 { for t in 0..buf.size { - buf.data[i][t] += - b[i][self.time_loop_idx(self.out_time + FrameTime(t as i64), true)] as f64; + buf.data[i][t] += b[i] + [self.time_loop_idx(self.out_time + FrameTime(t as i64), true)] + as f64; } } } @@ -1310,7 +1291,7 @@ impl LooperBackend { fn prepare_for_recording(&mut self, _: LooperMode) { self.samples.clear(); - self.samples.push(Sample::new()); + self.samples.push(Sample::default()); self.length.store(0, Ordering::Relaxed); } @@ -1379,6 +1360,7 @@ impl LooperBackend { // TODO: this logic should probably be abstracted out into Sample so it can be reused // between here and fill_output let mut wv = [vec![0f64; inputs[0].len()], vec![0f64; inputs[0].len()]]; + #[allow(clippy::needless_range_loop)] for c in 0..2 { for i in 0..inputs[0].len() { for s in &self.samples { @@ -1461,30 +1443,29 @@ impl LooperBackend { fn add_change(&mut self, change: LooperChange) { self.undo_queue.push_back(change); self.redo_queue.clear(); - self.gui_sender.send_update(GuiCommand::LooperStateChange( - self.id, self.current_state() - )); + self.gui_sender + .send_update(GuiCommand::LooperStateChange(self.id, self.current_state())); } fn reset_gui(&mut self) { if self.length_in_samples(false) > 0 { - self.gui_sender.send_update(GuiCommand::UpdateLooperWithSamples( - self.id, - self.length_in_samples(true), - Box::new(compute_waveform(&self.samples, WAVEFORM_DOWNSAMPLE)), - self.current_state(), - )); + self.gui_sender + .send_update(GuiCommand::UpdateLooperWithSamples( + self.id, + self.length_in_samples(true), + Box::new(compute_waveform(&self.samples, WAVEFORM_DOWNSAMPLE)), + self.current_state(), + )); } else { - self.gui_sender.send_update(GuiCommand::LooperStateChange( - self.id, self.current_state())) + self.gui_sender + .send_update(GuiCommand::LooperStateChange(self.id, self.current_state())) } } fn undo_change(&mut self, change: LooperChange) -> Option { match change { LooperChange::PushSample => { - let sample = self.samples.pop() - .map(|s| LooperChange::PopSample(s)); + let sample = self.samples.pop().map(LooperChange::PopSample); self.gui_needs_reset = true; sample } @@ -1493,14 +1474,20 @@ impl LooperBackend { self.gui_needs_reset = true; Some(LooperChange::PushSample) } - LooperChange::Clear { samples, in_time, out_time, offset } => { + LooperChange::Clear { + samples, + in_time, + out_time, + offset, + } => { self.samples = samples; self.in_time = in_time; self.out_time = out_time; self.offset = offset; if !self.samples.is_empty() { - self.length.store(self.samples[0].length(), Ordering::Relaxed); + self.length + .store(self.samples[0].length(), Ordering::Relaxed); } self.gui_needs_reset = true; @@ -1562,7 +1549,7 @@ impl LooperBackend { for (i, s) in self.samples.iter().enumerate() { let name = format!("loop_{}_{}.wav", self.id, i); let p = path.join(&name); - let mut writer = hound::WavWriter::create(&p, spec.clone())?; + let mut writer = hound::WavWriter::create(&p, spec)?; for j in 0..s.length() as usize { writer.write_sample(s.buffer[0][j])?; @@ -1589,7 +1576,6 @@ pub struct Looper { pub pan_law: PanLaw, - // this is pretty hacky -- we sometimes need a way to see the mode that has been just set on the // looper, before it's had a chance to make it to the backend local_mode: Option, @@ -1620,6 +1606,7 @@ impl Looper { ) } + #[allow(clippy::too_many_arguments)] fn new_with_samples( id: u32, parts: PartSet, @@ -1636,7 +1623,7 @@ impl Looper { let (s, r) = bounded(1000); - let length = samples.get(0).map(|s| s.length()).unwrap_or(0); + let length = samples.first().map(|s| s.length()).unwrap_or(0); let state = LooperState { mode: LooperMode::Playing, @@ -1661,9 +1648,9 @@ impl Looper { } let mode = Arc::new(Atomic::new(LooperMode::Playing)); - let length = Arc::new(Atomic::new(samples.get(0) - .map(|s| s.length()) - .unwrap_or(0))); + let length = Arc::new(Atomic::new( + samples.first().map(|s| s.length()).unwrap_or(0), + )); let backend = LooperBackend { id, @@ -1716,7 +1703,7 @@ impl Looper { in_progress_output: None, last_time: FrameTime(0), - local_mode: None + local_mode: None, } } @@ -1729,7 +1716,7 @@ impl Looper { for sample_path in &state.samples { let mut reader = hound::WavReader::open(&path.join(sample_path))?; - let mut sample = Sample::new(); + let mut sample = Sample::default(); let mut left = Vec::with_capacity(reader.len() as usize / 2); let mut right = Vec::with_capacity(reader.len() as usize / 2); @@ -1791,7 +1778,7 @@ impl Looper { } pub fn local_mode(&self) -> LooperMode { - return self.local_mode.unwrap_or(self.mode()); + self.local_mode.unwrap_or(self.mode()) } pub fn mode(&self) -> LooperMode { @@ -1872,7 +1859,8 @@ impl Looper { // TODO: this logic is duplicated in the gui, would be good to unify somehow if self.length() == 0 { self.transition_to(LooperMode::Recording); - } else if self.mode() == LooperMode::Recording || self.mode() == LooperMode::Playing { + } else if self.mode() == LooperMode::Recording || self.mode() == LooperMode::Playing + { self.transition_to(LooperMode::Overdubbing); } else { self.transition_to(LooperMode::Playing); @@ -1892,9 +1880,7 @@ impl Looper { } fn output_for_t(&mut self, t: FrameTime) -> Option<(f64, f64)> { - let mut cur = self - .in_progress_output - .or_else(|| self.in_queue.pop())?; + let mut cur = self.in_progress_output.or_else(|| self.in_queue.pop())?; self.in_progress_output = Some(cur); loop { @@ -1923,16 +1909,16 @@ impl Looper { fn should_output(&self, part: Part, solo: bool) -> bool { if !self.parts[part] { - return false + return false; } if solo && self.mode() != LooperMode::Soloed { - return false + return false; } - return self.mode() == LooperMode::Playing || - self.mode() == LooperMode::Overdubbing || - self.mode() == LooperMode::Soloed + self.mode() == LooperMode::Playing + || self.mode() == LooperMode::Overdubbing + || self.mode() == LooperMode::Soloed } // In process_output, we modify the specified output buffers according to our internal state. In @@ -2012,7 +1998,7 @@ impl Looper { let mut buf = TransferBuf { id: msg_id, - time: FrameTime(0 as i64), + time: FrameTime(0), size: 0, data: [[0f32; TRANSFER_BUF_SIZE]; 2], }; @@ -2027,13 +2013,11 @@ impl Looper { if self.parts[part] { // if this is not the current part, send 0s - for i in 0..l.len() { - buf.data[0][i] = l[i]; - buf.data[1][i] = r[i]; - } + buf.data[0][..l.len()].copy_from_slice(l); + buf.data[1][..r.len()].copy_from_slice(r); } - if let Err(_) = self.out_queue.push(buf) { + if self.out_queue.push(buf).is_err() { // TODO: handle error case where our queue is full error!("queue is full on looper {}", self.id); } @@ -2061,7 +2045,7 @@ impl Looper { impl Drop for Looper { fn drop(&mut self) { - if let Err(_) = self.channel.send(ControlMessage::Shutdown) { + if self.channel.send(ControlMessage::Shutdown).is_err() { warn!("failed to shutdown backend because queue was full"); } } diff --git a/loopers-engine/src/metronome.rs b/loopers-engine/src/metronome.rs index 3cae72c..019427f 100644 --- a/loopers-engine/src/metronome.rs +++ b/loopers-engine/src/metronome.rs @@ -6,6 +6,7 @@ use loopers_common::api::FrameTime; use std::sync::Arc; #[cfg(test)] +#[allow(clippy::items_after_test_module)] mod tests { use loopers_common::music::Tempo; use super::*; diff --git a/loopers-engine/src/sample.rs b/loopers-engine/src/sample.rs index fe184d6..10968c9 100644 --- a/loopers-engine/src/sample.rs +++ b/loopers-engine/src/sample.rs @@ -1,7 +1,7 @@ -use std::sync::Arc; -use std::fmt::{Debug, Formatter}; -use loopers_common::api::LooperSpeed; use itertools::Itertools; +use loopers_common::api::LooperSpeed; +use std::fmt::{Debug, Formatter}; +use std::sync::Arc; #[cfg(test)] mod tests { @@ -26,7 +26,7 @@ mod tests { #[test] fn test_record() { - let mut sample = Sample::new(); + let mut sample = Sample::default(); let data = [vec![1.0f32, 1.0], vec![-1.0, -1.0]]; sample.record(&[&data[0], &data[1]]); @@ -108,7 +108,6 @@ mod tests { vec![-1.0f32, -1.0, -2.0, -2.0, -3.0, -3.0, -4.0, -4.0], sample.buffer[1] ); - } #[test] @@ -178,11 +177,13 @@ impl Debug for Sample { } } -impl Sample { - pub fn new() -> Sample { +impl Default for Sample { + fn default() -> Self { Sample::with_size(0) } +} +impl Sample { pub fn with_size(len: usize) -> Sample { Sample { buffer: [vec![0f32; len], vec![0f32; len]], @@ -242,8 +243,7 @@ impl Sample { } } } - }; - + } } pub fn replace(&mut self, time_in_samples: u64, data: &[&[f32]]) { @@ -285,6 +285,7 @@ impl Sample { // assert!(end_time <= xfade_size as u64, // format!("expected {} <= {}", end_time, xfade_size)); + #[allow(clippy::needless_range_loop)] for i in 0..data.len() { for j in 0..data[i].len() { let idx = ((time_in_samples + j as u64) % len) as usize; diff --git a/loopers-engine/src/session.rs b/loopers-engine/src/session.rs index 7bd1beb..56184c1 100644 --- a/loopers-engine/src/session.rs +++ b/loopers-engine/src/session.rs @@ -78,7 +78,7 @@ impl SessionSaver { gui_channel: &mut GuiSender, ) -> Result<(), SaveLoadError> { let now = Local::now(); - let mut path = (&*sd.path).clone(); + let mut path = (*sd.path).clone(); path.push(now.format("%Y-%m-%d_%H:%M:%S").to_string()); create_dir_all(&path)?; @@ -135,8 +135,9 @@ impl SessionSaver { } } - if let Err(_) = write!(gui_channel, "Session saved to {}", path.to_string_lossy()) + if write!(gui_channel, "Session saved to {}", path.to_string_lossy()) .and_then(|_| gui_channel.flush()) + .is_err() { warn!("failed to write gui message"); } diff --git a/loopers-engine/src/trigger.rs b/loopers-engine/src/trigger.rs index f5cabd3..4ea0d51 100644 --- a/loopers-engine/src/trigger.rs +++ b/loopers-engine/src/trigger.rs @@ -174,7 +174,7 @@ impl Eq for Trigger {} impl PartialOrd for Trigger { fn partial_cmp(&self, other: &Self) -> Option { - self.triggered_at.partial_cmp(&other.triggered_at) + Some(self.cmp(other)) } } diff --git a/loopers-gui/Cargo.toml b/loopers-gui/Cargo.toml index 4eeff8e..2477038 100644 --- a/loopers-gui/Cargo.toml +++ b/loopers-gui/Cargo.toml @@ -12,16 +12,16 @@ license = "MIT OR Apache-2.0" log = "0.4" hound = "3.4.0" -bytes = "1.0" -dirs = "4.0" +bytes = "1.5" +dirs = "5.0" -skia-safe = {version = "0.50", features = ["gl", "x11"]} +skia-safe = {version = "0.69", features = ["gl", "x11"]} -sdl2 = "0.35" +sdl2 = "0.36" -tinyfiledialogs = "3.3" +tinyfiledialogs = "3.9" lazy_static = "1" -chrono = "0.4.11" +chrono = "0.4.31" regex = "1" diff --git a/loopers-gui/src/app.rs b/loopers-gui/src/app.rs index 827ea97..fe20751 100644 --- a/loopers-gui/src/app.rs +++ b/loopers-gui/src/app.rs @@ -8,6 +8,7 @@ use loopers_common::api::{ get_sample_rate, Command, FrameTime, LooperCommand, LooperMode, LooperSpeed, LooperTarget, Part, QuantizationMode, PARTS, }; +use loopers_common::clamp; use loopers_common::gui_channel::EngineState; use loopers_common::music::{MetricStructure, TimeSignature}; use regex::Regex; @@ -23,7 +24,6 @@ use std::str::FromStr; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::time::{Duration, Instant}; -use loopers_common::clamp; const LOOP_ICON: &[u8] = include_bytes!("../resources/icons/loop.png"); const METRONOME_ICON: &[u8] = include_bytes!("../resources/icons/metronome.png"); @@ -155,7 +155,7 @@ impl AddButton { fn draw( &mut self, - canvas: &mut Canvas, + canvas: &Canvas, _: &AppData, controller: &mut Controller, last_event: Option, @@ -213,7 +213,7 @@ impl DeleteButton { fn draw( &mut self, - canvas: &mut Canvas, + canvas: &Canvas, looper: &LooperData, size: f32, controller: &mut Controller, @@ -320,7 +320,7 @@ impl MainPage { pub fn draw( &mut self, - canvas: &mut Canvas, + canvas: &Canvas, data: &AppData, w: f32, h: f32, @@ -339,7 +339,7 @@ impl MainPage { .loopers .keys() .filter(|id| !data.loopers.contains_key(id)) - .map(|id| *id) + .copied() .collect(); for id in remove { @@ -347,7 +347,7 @@ impl MainPage { } self.modal_manager - .draw(canvas, w as f32, h as f32, data, controller, last_event); + .draw(canvas, w, h, data, controller, last_event); let mut y = 0.0; let mut visible_loopers = 0; @@ -440,12 +440,12 @@ impl MainPage { canvas.restore(); } - let mut bottom = h as f32; + let mut bottom = h; // draw the message view if one exists canvas.save(); canvas.translate((0.0, bottom - 90.0)); - LogMessageView::draw(canvas, data).width; + LogMessageView::draw(canvas, data); canvas.restore(); // draw the bottom bars @@ -463,7 +463,7 @@ impl MainPage { canvas.translate(Vector::new(0.0, bottom - bar_height)); self.bottom_bar.draw( data, - w as f32, + w, 30.0, canvas, &mut self.modal_manager, @@ -493,12 +493,13 @@ impl BottomBarView { } } + #[allow(clippy::too_many_arguments)] fn draw( &mut self, data: &AppData, _w: f32, h: f32, - canvas: &mut Canvas, + canvas: &Canvas, _modal_manager: &mut ModalManager, controller: &mut Controller, last_event: Option, @@ -520,9 +521,15 @@ impl BottomBarView { let size = self.time_view.draw(h, data, canvas, controller, last_event); canvas.translate((size.width.round() + 20.0, 0.0)); - self.peak_view - .draw(canvas, data.engine_state.input_levels, None, 160.0, h, - |_| {}, last_event); + self.peak_view.draw( + canvas, + data.engine_state.input_levels, + None, + 160.0, + h, + |_| {}, + last_event, + ); canvas.restore(); } @@ -543,7 +550,7 @@ impl TempoView { fn draw( &mut self, - canvas: &mut Canvas, + canvas: &Canvas, data: &AppData, controller: &mut Controller, last_event: Option, @@ -613,7 +620,7 @@ impl Button for TempoView { impl TextEditable for TempoView { fn commit(&mut self, controller: &mut Controller) { if let TextEditState::Editing(_, s) = &self.edit_state { - if let Ok(tempo) = f32::from_str(&s) { + if let Ok(tempo) = f32::from_str(s) { controller.send_command(Command::SetTempoBPM(tempo), "Failed to set tempo"); } else if !s.is_empty() { controller.log(&format!("Tempo {} is not valid", s)); @@ -651,7 +658,7 @@ impl MetronomeView { &mut self, h: f32, data: &AppData, - canvas: &mut Canvas, + canvas: &Canvas, controller: &mut Controller, last_event: Option, ) -> Size { @@ -826,7 +833,7 @@ struct MetronomeButton { impl MetronomeButton { fn new() -> Self { - let icon_data = Data::new_copy(&METRONOME_ICON); + let icon_data = Data::new_copy(METRONOME_ICON); let icon = Image::from_encoded(icon_data).expect("could not decode metronome icon"); Self { @@ -837,7 +844,7 @@ impl MetronomeButton { fn draw( &mut self, - canvas: &mut Canvas, + canvas: &Canvas, data: &AppData, controller: &mut Controller, last_event: Option, @@ -877,15 +884,18 @@ impl MetronomeButton { ButtonState::Default => unreachable!(), }; - canvas.draw_rect(&bounds.with_outset((3.0, 3.0)), &paint); + canvas.draw_rect(bounds.with_outset((3.0, 3.0)), &paint); } paint.set_alpha_f(data.engine_state.metronome_volume.min(1.0).max(0.3)); canvas.draw_image_rect_with_sampling_options( - &self.icon, None, bounds, + &self.icon, + None, + bounds, CubicResampler::catmull_rom(), - &paint); + &paint, + ); Size::new(25.0, 25.0) } @@ -918,7 +928,7 @@ impl TimeView { &mut self, h: f32, data: &AppData, - canvas: &mut Canvas, + canvas: &Canvas, controller: &mut Controller, last_event: Option, ) -> Size { @@ -942,7 +952,7 @@ impl TimeView { text_paint.set_anti_alias(true); let time_blob = TextBlob::new( - &format!("{}{:02}:{:02}:{:02}", negative, hours, minutes, seconds), + format!("{}{:02}:{:02}:{:02}", negative, hours, minutes, seconds), &font, ) .unwrap(); @@ -1035,7 +1045,7 @@ impl PeakMeterView { 2.0 + i as f32 * (h / 2.0 - 3.0) } - fn redraw_if_needed(&mut self, canvas: &mut Canvas, paint: &mut Paint, w: f32, h: f32) { + fn redraw_if_needed(&mut self, canvas: &Canvas, paint: &mut Paint, w: f32, h: f32) { if let Some((_, instant)) = &self.image { if instant.elapsed() < self.update_time { return; @@ -1044,14 +1054,15 @@ impl PeakMeterView { let image_info = ImageInfo::new_n32((w as i32, h as i32), AlphaType::Premul, None); - let mut surface = Surface::new_render_target( + let mut surface = gpu::surfaces::render_target( &mut canvas.recording_context().unwrap(), - Budgeted::Yes, + gpu::Budgeted::Yes, &image_info, None, SurfaceOrigin::TopLeft, None, None, + None, ) .unwrap(); @@ -1070,7 +1081,7 @@ impl PeakMeterView { path.move_to((x, y)); path.line_to((x, y + h / 2.0 - 7.0)); - surface.canvas().draw_path(&path, &paint); + surface.canvas().draw_path(&path, paint); } } self.levels = [0, 0]; @@ -1078,8 +1089,17 @@ impl PeakMeterView { self.image = Some((surface.image_snapshot(), Instant::now())); } - fn draw(&mut self, canvas: &mut Canvas, levels: [u8; 2], set_level: Option, - w: f32, h: f32, new_level: F, last_event: Option) -> Size { + #[allow(clippy::too_many_arguments)] + fn draw( + &mut self, + canvas: &Canvas, + levels: [u8; 2], + set_level: Option, + w: f32, + h: f32, + new_level: F, + last_event: Option, + ) -> Size { let mut paint = Paint::default(); paint.set_anti_alias(true); paint.set_stroke_width(1.5); @@ -1119,9 +1139,11 @@ impl PeakMeterView { paint.set_anti_alias(true); canvas.draw_image_with_sampling_options( - &image, (0.0, 0.0), + image, + (0.0, 0.0), CubicResampler::catmull_rom(), - Some(&paint)); + Some(&paint), + ); } // draw peak @@ -1155,7 +1177,10 @@ impl PeakMeterView { // handle clicks let bounds = Rect::from_size((w, h)); - if let Some(GuiEvent::MouseEvent(MouseEventType::MouseDown(MouseButton::Left), (x, y))) = last_event + if let Some(GuiEvent::MouseEvent( + MouseEventType::MouseDown(MouseButton::Left), + (x, y), + )) = last_event { let point = canvas .local_to_device_as_3x3() @@ -1180,7 +1205,6 @@ impl PeakMeterView { } } - Size::new(w, h) } } @@ -1198,7 +1222,7 @@ impl PlayPauseButton { fn draw( &mut self, - canvas: &mut Canvas, + canvas: &Canvas, data: &AppData, controller: &mut Controller, last_event: Option, @@ -1239,8 +1263,8 @@ impl PlayPauseButton { // draw pause button let rect1 = Rect::new(0.0, 0.0, 7.5, 20.0); let rect2 = Rect::new(12.5, 0.0, 20.0, 20.0); - canvas.draw_rect(&rect1, &paint); - canvas.draw_rect(&rect2, &paint); + canvas.draw_rect(rect1, &paint); + canvas.draw_rect(rect2, &paint); } else { // draw play icon let mut path = Path::new(); @@ -1279,7 +1303,7 @@ impl StopButton { fn draw( &mut self, - canvas: &mut Canvas, + canvas: &Canvas, _data: &AppData, controller: &mut Controller, last_event: Option, @@ -1311,7 +1335,7 @@ impl StopButton { } let rect = Rect::new(0.0, 0.0, 20.0, 20.0); - canvas.draw_rect(&rect, &paint); + canvas.draw_rect(rect, &paint); Size::new(25.0, 25.0) } @@ -1420,11 +1444,11 @@ impl BottomButtonView { ), ( BottomButtonBehavior::Undo, - ControlButton::new("Undo", c, None, 22.0) + ControlButton::new("Undo", c, None, 22.0), ), ( BottomButtonBehavior::Redo, - ControlButton::new("Redo", c, None, 22.0) + ControlButton::new("Redo", c, None, 22.0), ), ], load_window: LoadWindow { @@ -1435,7 +1459,7 @@ impl BottomButtonView { fn draw( &mut self, - canvas: &mut Canvas, + canvas: &Canvas, data: &AppData, controller: &mut Controller, last_event: Option, @@ -1446,7 +1470,7 @@ impl BottomButtonView { canvas.save(); canvas.translate((x, 0.0)); - let behavior = behavior.clone(); + let behavior = *behavior; let on_click = |button: MouseButton| { if button == MouseButton::Left { @@ -1478,26 +1502,30 @@ impl BottomButtonView { BottomButtonBehavior::Undo => { controller.send_command( Command::Looper(LooperCommand::Undo, LooperTarget::Selected), - "Failed to undo"); + "Failed to undo", + ); } BottomButtonBehavior::Redo => { controller.send_command( Command::Looper(LooperCommand::Redo, LooperTarget::Selected), - "Failed to redo"); + "Failed to redo", + ); } }; } }; let disabled = match behavior { - BottomButtonBehavior::Undo => { - data.loopers.get(&data.engine_state.active_looper).map(|l| !l.has_undos) - .unwrap_or(false) - } - BottomButtonBehavior::Redo => { - data.loopers.get(&data.engine_state.active_looper).map(|l| !l.has_redos) - .unwrap_or(true) - } + BottomButtonBehavior::Undo => data + .loopers + .get(&data.engine_state.active_looper) + .map(|l| !l.has_undos) + .unwrap_or(false), + BottomButtonBehavior::Redo => data + .loopers + .get(&data.engine_state.active_looper) + .map(|l| !l.has_redos) + .unwrap_or(true), _ => false, }; @@ -1556,7 +1584,7 @@ impl BottomButtonView { struct LogMessageView {} impl LogMessageView { - fn draw(canvas: &mut Canvas, data: &AppData) -> Size { + fn draw(canvas: &Canvas, data: &AppData) -> Size { let msg = data.messages.cur.as_ref().map(|(_, l)| l.as_str()); if let Some(msg) = msg.as_ref() { let font = Font::new(Typeface::default(), Some(14.0)); @@ -1583,9 +1611,10 @@ impl LogMessageView { struct LooperView { id: u32, waveform_view: WaveformView, + #[allow(clippy::type_complexity)] buttons: Vec< Vec<( - Box) -> Size>, + Box) -> Size>, f32, )>, >, @@ -1638,23 +1667,13 @@ impl LooperView { 15.0, ), ( - Self::new_speed_button( - "½x", - LooperSpeed::Half, - button_height, - 45.0 - ), - 10.0 + Self::new_speed_button("½x", LooperSpeed::Half, button_height, 45.0), + 10.0, ), ( - Self::new_speed_button( - "2x", - LooperSpeed::Double, - button_height, - 45.0 - ), - 15.0 - ) + Self::new_speed_button("2x", LooperSpeed::Double, button_height, 45.0), + 15.0, + ), ], ], state: ButtonState::Default, @@ -1665,13 +1684,14 @@ impl LooperView { } } + #[allow(clippy::type_complexity)] fn new_command_button( name: &str, color: Color, command: Command, h: f32, w: f32, - ) -> Box) -> Size> { + ) -> Box) -> Size> { let mut button = ControlButton::new(name, color, Some(w), h); Box::new(move |canvas, _, controller, last_event| { @@ -1690,12 +1710,13 @@ impl LooperView { }) } + #[allow(clippy::type_complexity)] fn new_speed_button( name: &str, speed: LooperSpeed, h: f32, w: f32, - ) -> Box) -> Size> { + ) -> Box) -> Size> { let mut button = ControlButton::new(name, Color::LIGHT_GRAY, Some(w), h); Box::new(move |canvas, data, controller, last_event| { @@ -1705,11 +1726,14 @@ impl LooperView { false, |button| { if button == MouseButton::Left { - let command = Command::Looper(LooperCommand::SetSpeed(if data.speed == speed { - LooperSpeed::One - } else { - speed - }), LooperTarget::Id(data.id)); + let command = Command::Looper( + LooperCommand::SetSpeed(if data.speed == speed { + LooperSpeed::One + } else { + speed + }), + LooperTarget::Id(data.id), + ); controller.send_command(command, "Failed to send command to engine"); } @@ -1719,10 +1743,11 @@ impl LooperView { }) } + #[allow(clippy::type_complexity)] fn new_part_button( part: Part, h: f32, - ) -> Box) -> Size> { + ) -> Box) -> Size> { let mut button = ControlButton::new(part.name(), Color::from_rgb(78, 78, 78), Some(28.0), h); @@ -1750,11 +1775,12 @@ impl LooperView { }) } + #[allow(clippy::type_complexity)] fn new_state_button( mode: LooperMode, name: &str, h: f32, - ) -> Box) -> Size> { + ) -> Box) -> Size> { let mut button = ControlButton::new(name, color_for_mode(mode), Some(100.0), h); Box::new(move |canvas, looper, controller, last_event| { @@ -1795,7 +1821,7 @@ impl LooperView { fn draw( &mut self, - canvas: &mut Canvas, + canvas: &Canvas, data: &AppData, looper: &LooperData, w: f32, @@ -1848,7 +1874,6 @@ impl LooperView { canvas.draw_str(text, Point::new(x, 55.0), &font, &paint); } - let waveform_width = w - WAVEFORM_OFFSET_X - WAVEFORM_RIGHT_MARGIN; let bounds = Rect::from_size((waveform_width, LOOPER_HEIGHT)) @@ -1879,11 +1904,20 @@ impl LooperView { last_event, ); canvas.translate((0.0, 40.0)); - self.peak.draw(canvas, looper.levels, Some(looper.level), 70.0, 30.0, - |level| controller.send_command( - Command::Looper(LooperCommand::SetLevel(level), LooperTarget::Id(looper.id)), - "Failed to set level" - ), last_event); + self.peak.draw( + canvas, + looper.levels, + Some(looper.level), + 70.0, + 30.0, + |level| { + controller.send_command( + Command::Looper(LooperCommand::SetLevel(level), LooperTarget::Id(looper.id)), + "Failed to set level", + ) + }, + last_event, + ); canvas.restore(); @@ -1928,7 +1962,7 @@ impl LooperView { let mut paint = Paint::default(); paint.set_anti_alias(true); paint.set_color(Color::from_argb(200, 0, 0, 0)); - canvas.draw_rect(&bounds, &paint); + canvas.draw_rect(bounds, &paint); // draw delete button let delete_size = 10.0; @@ -1961,7 +1995,7 @@ impl LooperView { let mut paint = Paint::default(); paint.set_anti_alias(true); paint.set_blend_mode(BlendMode::Darken); - paint.set_color(BACKGROUND_COLOR.clone().with_a(200)); + paint.set_color((*BACKGROUND_COLOR).with_a(200)); canvas.draw_rect( Rect::new( WAVEFORM_OFFSET_X, @@ -1991,14 +2025,8 @@ impl Button for LooperView { const IMAGE_SCALE: f32 = 4.0; -type CacheUpdaterFn = fn( - data: &AppData, - looper: &LooperData, - w: f32, - h: f32, - scale: f32, - canvas: &mut Canvas, -) -> Size; +type CacheUpdaterFn = + fn(data: &AppData, looper: &LooperData, w: f32, h: f32, scale: f32, canvas: &Canvas) -> Size; struct DrawCache { image: Option<(Image, Size)>, @@ -2024,7 +2052,7 @@ impl DrawCache { looper: &LooperData, w: f32, h: f32, - canvas: &mut Canvas, + canvas: &Canvas, ) -> Option { let size = ((w * IMAGE_SCALE) as i32, (h * IMAGE_SCALE) as i32); @@ -2040,14 +2068,15 @@ impl DrawCache { != size { let image_info = ImageInfo::new_n32(size, AlphaType::Premul, None); - let mut surface = Surface::new_render_target( + let mut surface = gpu::surfaces::render_target( &mut canvas.recording_context()?, - Budgeted::Yes, + gpu::Budgeted::Yes, &image_info, None, SurfaceOrigin::TopLeft, None, None, + None, )?; let draw_size = (self.draw_fn)( @@ -2056,7 +2085,7 @@ impl DrawCache { w * IMAGE_SCALE, h * IMAGE_SCALE, IMAGE_SCALE, - &mut surface.canvas(), + surface.canvas(), ); let image = surface.image_snapshot(); @@ -2076,14 +2105,17 @@ impl DrawCache { paint.set_color(Color::from_rgb(255, 255, 0)); canvas.scale((1.0 / IMAGE_SCALE, 1.0 / IMAGE_SCALE)); canvas.draw_image_with_sampling_options( - image, (0.0, 0.0), + image, + (0.0, 0.0), CubicResampler::catmull_rom(), - Some(&paint)); + Some(&paint), + ); canvas.restore(); Some(*size) } + #[allow(clippy::too_many_arguments)] fn draw( &mut self, key: T, @@ -2092,7 +2124,7 @@ impl DrawCache { w: f32, h: f32, use_cache: bool, - canvas: &mut Canvas, + canvas: &Canvas, ) -> Size { if use_cache { if let Some(size) = self.draw_with_cache(key, data, looper, w, h, canvas) { @@ -2100,7 +2132,7 @@ impl DrawCache { } } - return (self.draw_fn)(data, looper, w, h, 1.0, canvas); + (self.draw_fn)(data, looper, w, h, 1.0, canvas) } } @@ -2115,9 +2147,9 @@ impl ActiveButton { } } - fn draw ()>( + fn draw( &mut self, - canvas: &mut Canvas, + canvas: &Canvas, is_active: bool, on_click: F, last_event: Option, @@ -2167,7 +2199,7 @@ struct WaveformView { impl WaveformView { fn new() -> Self { - let loop_icon_data = Data::new_copy(&LOOP_ICON); + let loop_icon_data = Data::new_copy(LOOP_ICON); let loop_icon = Image::from_encoded(loop_icon_data).expect("could not decode loop icon"); Self { @@ -2228,7 +2260,7 @@ impl WaveformView { w: f32, h: f32, _: f32, - canvas: &mut Canvas, + canvas: &Canvas, ) -> Size { let p = Self::path_for_waveform([&looper.waveform[0], &looper.waveform[1]], w, h); @@ -2248,7 +2280,7 @@ impl WaveformView { w: f32, h: f32, scale: f32, - canvas: &mut Canvas, + canvas: &Canvas, ) -> Size { let mut beat_p = Path::new(); let mut bar_p = Path::new(); @@ -2304,7 +2336,7 @@ impl WaveformView { fn draw( &mut self, - canvas: &mut Canvas, + canvas: &Canvas, data: &AppData, looper: &LooperData, w: f32, @@ -2512,7 +2544,7 @@ impl WaveformView { let x = -self.time_to_x(data.engine_state.time - time) as f32; let rect = Rect::new(x, 15.0, w, h - 15.0); - canvas.draw_rect(&rect, &paint); + canvas.draw_rect(rect, &paint); if let Some(text) = text { let font = Font::new(Typeface::default(), 24.0); @@ -2520,7 +2552,7 @@ impl WaveformView { text_paint.set_color(Color::BLACK); text_paint.set_anti_alias(true); - let time_blob = TextBlob::new(&text, &font).unwrap(); + let time_blob = TextBlob::new(text, &font).unwrap(); canvas.draw_text_blob( &time_blob, Point::new(x + 10.0, h / 2.0 + 6.0), diff --git a/loopers-gui/src/lib.rs b/loopers-gui/src/lib.rs index 8723a4a..8db49ea 100644 --- a/loopers-gui/src/lib.rs +++ b/loopers-gui/src/lib.rs @@ -135,7 +135,10 @@ impl Controller { } pub fn log(&mut self, msg: &str) { - if let Err(_) = write!(self.gui_sender, "{}", msg).and_then(|_| self.gui_sender.flush()) { + if write!(self.gui_sender, "{}", msg) + .and_then(|_| self.gui_sender.flush()) + .is_err() + { error!("Failed to write message to gui"); } } @@ -317,7 +320,7 @@ impl Gui { Ok(GuiCommand::AddOverdubSample(id, time, sample)) => { if let Some(l) = self.state.loopers.get_mut(&id) { let time = time - l.offset; - if time.0 >= 0 && l.waveform[0].len() > 0 && l.length > 0 { + if time.0 >= 0 && !l.waveform[0].is_empty() && l.length > 0 { let i = ((time.0 as u64 % l.length) / WAVEFORM_DOWNSAMPLE as u64) as usize; if i < l.waveform[0].len() - 1 { @@ -381,7 +384,7 @@ impl Gui { self.root.min_size(&self.state) } - pub fn draw(&mut self, canvas: &mut Canvas, w: f32, h: f32, last_event: Option) { + pub fn draw(&mut self, canvas: &Canvas, w: f32, h: f32, last_event: Option) { if self.initialized { self.root .draw(canvas, &self.state, w, h, &mut self.controller, last_event); diff --git a/loopers-gui/src/skia.rs b/loopers-gui/src/skia.rs index 949bb6b..5b8396e 100644 --- a/loopers-gui/src/skia.rs +++ b/loopers-gui/src/skia.rs @@ -1,5 +1,5 @@ use skia_safe::gpu::gl::FramebufferInfo; -use skia_safe::gpu::{BackendRenderTarget, DirectContext, SurfaceOrigin}; +use skia_safe::gpu::{self, DirectContext, SurfaceOrigin}; use skia_safe::{ Color, ColorType, Font, Paint, PictureRecorder, Point, Rect, Size, Surface, TextBlob, Typeface, }; @@ -23,6 +23,7 @@ const INITIAL_HEIGHT: i32 = 600; lazy_static! { pub static ref BACKGROUND_COLOR: Color = Color::from_rgb(29, 30, 39); + // pub static ref BACKGROUND_COLOR: Color = Color::GREEN; } fn create_surface( @@ -33,7 +34,7 @@ fn create_surface( scale_factor: f32, ) -> Surface { let backend_render_target = - BackendRenderTarget::new_gl((size.0 as i32, size.1 as i32), 0, 8, fb_info); + gpu::backend_render_targets::make_gl((size.0 as i32, size.1 as i32), 0, 8, fb_info); let color_type = match pixel_format { PixelFormatEnum::RGBA8888 => ColorType::RGBA8888, @@ -45,7 +46,7 @@ fn create_surface( } }; - let mut surface = Surface::from_backend_render_target( + let mut surface = gpu::surfaces::wrap_backend_render_target( gr_context, &backend_render_target, SurfaceOrigin::BottomLeft, @@ -83,7 +84,7 @@ pub fn skia_main(mut gui: Gui) { // must live until window is destroyed let _ctx = window.gl_create_context().unwrap(); - gl::load_with(|name| video_subsystem.gl_get_proc_address(&name) as *const _); + gl::load_with(|name| video_subsystem.gl_get_proc_address(name) as *const _); let debug = std::env::var("DEBUG").is_ok(); @@ -95,6 +96,7 @@ pub fn skia_main(mut gui: Gui) { let fb_info = FramebufferInfo { fboid: fboid.try_into().unwrap(), format: skia_safe::gpu::gl::Format::RGBA8.into(), + protected: gpu::Protected::No, }; let size = window.drawable_size(); @@ -145,31 +147,25 @@ pub fn skia_main(mut gui: Gui) { && keymod.contains(Mod::LSHIFTMOD) { capture_debug_frame = true; - } else { - match keycode { - Some(key) => { - if let Some(c) = char_from_key(key) { - last_event = Some(GuiEvent::KeyEvent( - KeyEventType::Pressed, - KeyEventKey::Char(c), - )); - } else { - let key = match key { - Keycode::Backspace | Keycode::Delete => { - Some(KeyEventKey::Backspace) - } - Keycode::Escape => Some(KeyEventKey::Esc), - Keycode::Return => Some(KeyEventKey::Enter), - _ => None, - }; - - if let Some(key) = key { - last_event = - Some(GuiEvent::KeyEvent(KeyEventType::Pressed, key)); - } + } else if let Some(key) = keycode { + if let Some(c) = char_from_key(key) { + last_event = Some(GuiEvent::KeyEvent( + KeyEventType::Pressed, + KeyEventKey::Char(c), + )); + } else { + let key = match key { + Keycode::Backspace | Keycode::Delete => { + Some(KeyEventKey::Backspace) } + Keycode::Escape => Some(KeyEventKey::Esc), + Keycode::Return => Some(KeyEventKey::Enter), + _ => None, + }; + + if let Some(key) = key { + last_event = Some(GuiEvent::KeyEvent(KeyEventType::Pressed, key)); } - _ => {} } } } @@ -204,20 +200,20 @@ pub fn skia_main(mut gui: Gui) { } } - let mut canvas = surface.canvas(); - canvas.clear(BACKGROUND_COLOR.clone()); + let canvas = surface.canvas(); + canvas.clear(*BACKGROUND_COLOR); let size = window.drawable_size(); if capture_debug_frame { let mut recorder = PictureRecorder::new(); - let mut recording_canvas = + let recording_canvas = recorder.begin_recording(Rect::from_iwh(size.0 as i32, size.1 as i32), None); - canvas.clear(BACKGROUND_COLOR.clone()); + canvas.clear(*BACKGROUND_COLOR); gui.draw( - &mut recording_canvas, + recording_canvas, window.size().0 as f32, window.size().1 as f32, last_event, @@ -237,7 +233,7 @@ pub fn skia_main(mut gui: Gui) { } gui.draw( - &mut canvas, + canvas, window.size().0 as f32, window.size().1 as f32, last_event, @@ -268,7 +264,7 @@ pub fn skia_main(mut gui: Gui) { &paint, ); } - surface.flush(); + gr_context.flush_and_submit(); let new_min_size = gui.min_size(); if new_min_size != min_size { diff --git a/loopers-gui/src/widgets.rs b/loopers-gui/src/widgets.rs index 52c42c4..65cf5ed 100644 --- a/loopers-gui/src/widgets.rs +++ b/loopers-gui/src/widgets.rs @@ -8,7 +8,7 @@ use skia_safe::{ use std::f32::consts::PI; use std::time::UNIX_EPOCH; -pub fn draw_circle_indicator(canvas: &mut Canvas, color: Color, p: f32, x: f32, y: f32, r: f32) { +pub fn draw_circle_indicator(canvas: &Canvas, color: Color, p: f32, x: f32, y: f32, r: f32) { let mut paint = Paint::default(); paint.set_anti_alias(true); paint.set_color(color); @@ -44,36 +44,31 @@ pub trait Button { bounds: &Rect, on_click: F, event: Option, - ) -> () { - if let Some(event) = event { - match event { - GuiEvent::MouseEvent(typ, pos) => { - let point = canvas - .local_to_device_as_3x3() - .invert() - .unwrap() - .map_point((pos.0 as f32, pos.1 as f32)); - if bounds.contains(point) { - match typ { - MouseEventType::MouseDown(MouseButton::Left) => { - self.set_state(ButtonState::Pressed); - } - MouseEventType::MouseUp(button) => { - if self.get_state() == ButtonState::Pressed { - on_click(button); - } - self.set_state(ButtonState::Hover); - } - MouseEventType::Moved => { - self.set_state(ButtonState::Hover); - } - _ => {} + ) { + if let Some(GuiEvent::MouseEvent(typ, pos)) = event { + let point = canvas + .local_to_device_as_3x3() + .invert() + .unwrap() + .map_point((pos.0 as f32, pos.1 as f32)); + if bounds.contains(point) { + match typ { + MouseEventType::MouseDown(MouseButton::Left) => { + self.set_state(ButtonState::Pressed); + } + MouseEventType::MouseUp(button) => { + if self.get_state() == ButtonState::Pressed { + on_click(button); } - } else { - self.set_state(ButtonState::Default); + self.set_state(ButtonState::Hover); } + MouseEventType::Moved => { + self.set_state(ButtonState::Hover); + } + _ => {} } - _ => {} + } else { + self.set_state(ButtonState::Default); } } } @@ -113,9 +108,9 @@ impl ControlButton { } } - pub fn draw ()>( + pub fn draw( &mut self, - canvas: &mut Canvas, + canvas: &Canvas, is_active: bool, disabled: bool, on_click: F, @@ -124,9 +119,9 @@ impl ControlButton { self.draw_with_progress(canvas, is_active, disabled, on_click, last_event, 0.0) } - pub fn draw_with_progress ()>( + pub fn draw_with_progress( &mut self, - canvas: &mut Canvas, + canvas: &Canvas, is_active: bool, disabled: bool, on_click: F, @@ -161,7 +156,7 @@ impl ControlButton { Style::Stroke }); - canvas.draw_rect(&bounds, &paint); + canvas.draw_rect(bounds, &paint); if progress_percent > 0.0 { let progress = Rect { @@ -176,7 +171,7 @@ impl ControlButton { paint.set_stroke_width(2.0); paint.set_color(self.color); paint.set_style(Style::Fill); - canvas.draw_rect(&progress, &paint); + canvas.draw_rect(progress, &paint); } let mut text_paint = Paint::default(); @@ -208,10 +203,11 @@ impl Button for ControlButton { #[allow(dead_code)] pub trait Modal { + #[allow(clippy::too_many_arguments)] fn draw( &mut self, manager: &mut ModalManager, - canvas: &mut Canvas, + canvas: &Canvas, w: f32, h: f32, data: &AppData, @@ -242,7 +238,7 @@ impl ModalManager { pub fn draw( &mut self, - canvas: &mut Canvas, + canvas: &Canvas, w: f32, h: f32, data: &AppData, @@ -279,7 +275,7 @@ pub trait TextEditable { fn draw_edit( &mut self, - canvas: &mut Canvas, + canvas: &Canvas, font: &Font, bounds: &Rect, sender: &mut Controller, @@ -310,7 +306,7 @@ pub trait TextEditable { } edited.push(c); - if !Self::is_valid(&edited) { + if !Self::is_valid(edited) { edited.pop(); } @@ -347,7 +343,7 @@ pub trait TextEditable { if *selected { if !edited.is_empty() { paint.set_color(Color::BLUE); - canvas.draw_rect(&text_bounds, &paint); + canvas.draw_rect(text_bounds, &paint); } } else { text_paint.set_color(Color::BLACK); @@ -371,7 +367,7 @@ pub trait TextEditable { } } - canvas.draw_str(edited, Point::new(15.0, 18.0), &font, &text_paint); + canvas.draw_str(edited, Point::new(15.0, 18.0), font, &text_paint); } if commit { @@ -398,7 +394,7 @@ impl PotWidget { // level is [-1, 1], with 0 centered pub fn draw( &mut self, - canvas: &mut Canvas, + canvas: &Canvas, level: f32, new_level: F, last_event: Option, @@ -434,7 +430,7 @@ impl PotWidget { bg_paint.set_anti_alias(true); bg_paint.set_stroke_width(7.0); bg_paint.set_style(Style::Stroke); - bg_paint.set_color(crate::skia::BACKGROUND_COLOR.clone()); + bg_paint.set_color(*crate::skia::BACKGROUND_COLOR); canvas.draw_path(&path, &bg_paint); canvas.draw_path(&path, &paint); diff --git a/loopers/Cargo.toml b/loopers/Cargo.toml index b8ba58a..23b4105 100644 --- a/loopers/Cargo.toml +++ b/loopers/Cargo.toml @@ -12,13 +12,12 @@ readme = "../README.md" edition = "2018" [dependencies] -jack = "0.10" - -futures = "0.1" -bytes = "1.1" +jack = "0.11" +futures = "0.3" +bytes = "1.5" serde = { version = "1.0", features = ["derive"] } -toml = "0.5" -clap = "2" +toml = "0.8" +clap = "4" log = "0.4" fern = "0.6" @@ -26,9 +25,9 @@ fern = "0.6" crossbeam-queue = "0.3" crossbeam-channel = "0.5" rand = "0.8" -hound = "3.4.0" -chrono = "0.4.11" -dirs = "4" +hound = "3.5.0" +chrono = "0.4.31" +dirs = "5" [target.'cfg(target_os = "macos")'.dependencies] coreaudio-rs = {version = "0.11"} diff --git a/loopers/src/loopers_jack.rs b/loopers/src/loopers_jack.rs index c582cd9..b119720 100644 --- a/loopers/src/loopers_jack.rs +++ b/loopers/src/loopers_jack.rs @@ -1,13 +1,13 @@ -use std::collections::HashMap; -use std::{io, thread}; +use crossbeam_channel::{bounded, Receiver, Sender}; use jack::{AudioOut, Port, ProcessScope}; -use crossbeam_channel::{bounded, Sender, Receiver}; use loopers_common::api::Command; use loopers_common::gui_channel::GuiSender; -use loopers_common::Host; use loopers_common::midi::MidiEvent; +use loopers_common::Host; use loopers_engine::Engine; use loopers_gui::Gui; +use std::collections::HashMap; +use std::{io, thread}; enum ClientChange { AddPort(u32), @@ -39,7 +39,10 @@ impl<'a> Host<'a> for JackHost<'a> { fn remove_looper(&mut self, id: u32) -> Result<(), String> { if let Some([l, r]) = self.looper_ports.remove(&id) { - if let Err(e) = self.port_change_tx.try_send(ClientChange::RemovePort(id, l, r)) { + if let Err(e) = self + .port_change_tx + .try_send(ClientChange::RemovePort(id, l, r)) + { warn!("Failed to send port remove request: {:?}", e); } } @@ -48,14 +51,11 @@ impl<'a> Host<'a> for JackHost<'a> { } fn output_for_looper<'b>(&'b mut self, id: u32) -> Option<[&'b mut [f32]; 2]> - where - 'a: 'b, + where + 'a: 'b, { - match self.port_change_resp.try_recv() { - Ok(ClientChangeResponse::PortAdded(id, l, r)) => { - self.looper_ports.insert(id, [l, r]); - } - Err(_) => {} + if let Ok(ClientChangeResponse::PortAdded(id, l, r)) = self.port_change_resp.try_recv() { + self.looper_ports.insert(id, [l, r]); } let ps = self.ps?; @@ -149,40 +149,34 @@ impl jack::NotificationHandler for Notifications { } } -pub fn jack_main(gui: Option, - gui_sender: GuiSender, - gui_to_engine_receiver: Receiver, - beat_normal: Vec, - beat_emphasis: Vec, - restore: bool) { +pub fn jack_main( + gui: Option, + gui_sender: GuiSender, + gui_to_engine_receiver: Receiver, + beat_normal: Vec, + beat_emphasis: Vec, + restore: bool, +) { // Create client let (client, _status) = jack::Client::new("loopers", jack::ClientOptions::NO_START_SERVER) .expect("Jack server is not running"); // Register ports. They will be used in a callback that will be // called when new data is available. - let in_a = client - .register_port("in_l", jack::AudioIn::default()) - .unwrap(); - let in_b = client - .register_port("in_r", jack::AudioIn::default()) - .unwrap(); - let mut out_a = client - .register_port("main_out_l", jack::AudioOut::default()) - .unwrap(); - let mut out_b = client - .register_port("main_out_r", jack::AudioOut::default()) - .unwrap(); + let in_a = client.register_port("in_l", jack::AudioIn).unwrap(); + let in_b = client.register_port("in_r", jack::AudioIn).unwrap(); + let mut out_a = client.register_port("main_out_l", jack::AudioOut).unwrap(); + let mut out_b = client.register_port("main_out_r", jack::AudioOut).unwrap(); let mut met_out_a = client - .register_port("metronome_out_l", jack::AudioOut::default()) + .register_port("metronome_out_l", jack::AudioOut) .unwrap(); let mut met_out_b = client - .register_port("metronome_out_r", jack::AudioOut::default()) + .register_port("metronome_out_r", jack::AudioOut) .unwrap(); let midi_in = client - .register_port("loopers_midi_in", jack::MidiIn::default()) + .register_port("loopers_midi_in", jack::MidiIn) .unwrap(); let mut looper_ports: HashMap; 2]> = HashMap::new(); @@ -265,46 +259,47 @@ pub fn jack_main(gui: Option, // Activate the client, which starts the processing. let active_client = client.activate_async(Notifications, process).unwrap(); - thread::spawn(move || { - loop { - match port_change_rx.recv() { - Ok(ClientChange::AddPort(id)) => { - let l = active_client - .as_client() - .register_port(&format!("loop{}_out_l", id), jack::AudioOut::default()) - .map_err(|e| format!("could not create jack port: {:?}", e)); - let r = active_client - .as_client() - .register_port(&format!("loop{}_out_r", id), jack::AudioOut::default()) - .map_err(|e| format!("could not create jack port: {:?}", e)); - - match (l, r) { - (Ok(l), Ok(r)) => { - if let Err(_) = port_change_resp_tx.send(ClientChangeResponse::PortAdded(id, l, r)) { - break; - } - } - (Err(e), _) | (_, Err(e)) => { - error!("Failed to register port with jack: {:?}", e); + thread::spawn(move || loop { + match port_change_rx.recv() { + Ok(ClientChange::AddPort(id)) => { + let l = active_client + .as_client() + .register_port(&format!("loop{}_out_l", id), jack::AudioOut) + .map_err(|e| format!("could not create jack port: {:?}", e)); + let r = active_client + .as_client() + .register_port(&format!("loop{}_out_r", id), jack::AudioOut) + .map_err(|e| format!("could not create jack port: {:?}", e)); + + match (l, r) { + (Ok(l), Ok(r)) => { + if port_change_resp_tx + .send(ClientChangeResponse::PortAdded(id, l, r)) + .is_err() + { + break; } } - } - Ok(ClientChange::RemovePort(id, l, r)) => { - if let Err(e) = active_client.as_client() - .unregister_port(l).and_then(|()| { - active_client.as_client() - .unregister_port(r) - }) { - error!("Unable to remove jack outputs: {:?}", e); + (Err(e), _) | (_, Err(e)) => { + error!("Failed to register port with jack: {:?}", e); } - info!("removed ports for looper {}", id); - } - Ok(ClientChange::Shutdown) => { - break; } - Err(_) => { - break; + } + Ok(ClientChange::RemovePort(id, l, r)) => { + if let Err(e) = active_client + .as_client() + .unregister_port(l) + .and_then(|()| active_client.as_client().unregister_port(r)) + { + error!("Unable to remove jack outputs: {:?}", e); } + info!("removed ports for looper {}", id); + } + Ok(ClientChange::Shutdown) => { + break; + } + Err(_) => { + break; } } }); @@ -322,7 +317,7 @@ pub fn jack_main(gui: Option, } } - if let Err(_) = port_change_tx.send(ClientChange::Shutdown) { + if port_change_tx.send(ClientChange::Shutdown).is_err() { warn!("Failed to shutdown worker thread"); } diff --git a/loopers/src/main.rs b/loopers/src/main.rs index 4733b8b..09ee52c 100644 --- a/loopers/src/main.rs +++ b/loopers/src/main.rs @@ -15,13 +15,13 @@ mod loopers_jack; #[cfg(target_os = "macos")] mod looper_coreaudio; -use clap::{App, Arg}; +use crate::loopers_jack::jack_main; +use clap::{arg, Command}; use crossbeam_channel::bounded; use loopers_common::gui_channel::GuiSender; use loopers_gui::Gui; use std::io; use std::process::exit; -use crate::loopers_jack::jack_main; // metronome sounds; included in the binary for now to ease usage of cargo install const SINE_NORMAL: &[u8] = include_bytes!("../resources/sine_normal.wav"); @@ -63,7 +63,6 @@ const DEFAULT_DRIVER: &str = "coreaudio"; #[cfg(not(target_os = "macos"))] const DEFAULT_DRIVER: &str = "jack"; - fn main() { let drivers = if cfg!(feature = "coreaudio-rs") { "coreaudio, jack" @@ -71,36 +70,30 @@ fn main() { "jack" }; - let matches = App::new("loopers") + let matches = Command::new("loopers") .version("0.1.2") .author("Micah Wylde ") .about( "Loopers is a graphical live looper, designed for ease of use and rock-solid stability", ) + .arg(arg!(--restore "Automatically restores the last saved session")) + .arg(arg!(--"no-gui" "Launches in headless mode (without the gui)")) .arg( - Arg::with_name("restore") - .long("restore") - .help("Automatically restores the last saved session"), - ) - .arg( - Arg::with_name("no-gui") - .long("no-gui") - .help("Launches in headless mode (without the gui)"), - ) - .arg( - Arg::with_name("driver") - .long("driver") - .takes_value(true) + arg!(--driver ) .default_value(DEFAULT_DRIVER) - .help(&format!("Controls which audio driver to use (included drivers: {})", drivers))) - .arg(Arg::with_name("debug").long("debug")) + .help(format!( + "Controls which audio driver to use (included drivers: {})", + drivers + )), + ) + .arg(arg!(--debug)) .get_matches(); - if let Err(e) = setup_logger(matches.is_present("debug")) { + if let Err(e) = setup_logger(matches.get_flag("debug")) { eprintln!("Unable to set up logging: {:?}", e); } - let restore = matches.is_present("restore"); + let restore = matches.get_flag("restore"); if restore { info!("Restoring previous session"); @@ -108,7 +101,7 @@ fn main() { let (gui_to_engine_sender, gui_to_engine_receiver) = bounded(100); - let (gui, gui_sender) = if !matches.is_present("no-gui") { + let (gui, gui_sender) = if !matches.get_flag("no-gui") { let (sender, receiver) = GuiSender::new(); ( Some(Gui::new(receiver, gui_to_engine_sender, sender.clone())), @@ -120,30 +113,38 @@ fn main() { // read wav files let reader = hound::WavReader::new(SINE_NORMAL).unwrap(); - let beat_normal: Vec = reader - .into_samples() - .into_iter() - .map(|x| x.unwrap()) - .collect(); + let beat_normal: Vec = reader.into_samples().map(|x| x.unwrap()).collect(); let reader = hound::WavReader::new(SINE_EMPHASIS).unwrap(); - let beat_emphasis: Vec = reader - .into_samples() - .into_iter() - .map(|x| x.unwrap()) - .collect(); - - match matches.value_of("driver") - .unwrap_or(DEFAULT_DRIVER) { + let beat_emphasis: Vec = reader.into_samples().map(|x| x.unwrap()).collect(); + + match matches + .get_one("driver") + .unwrap_or(&DEFAULT_DRIVER.to_string()) + .as_str() + { "jack" => { - jack_main(gui, gui_sender, gui_to_engine_receiver, beat_normal, beat_emphasis, restore); + jack_main( + gui, + gui_sender, + gui_to_engine_receiver, + beat_normal, + beat_emphasis, + restore, + ); } "coreaudio" => { if cfg!(target_os = "macos") { #[cfg(target_os = "macos")] crate::looper_coreaudio::coreaudio_main( - gui, gui_sender, gui_to_engine_receiver, beat_normal, beat_emphasis, restore) - .expect("failed to set up coreaudio"); + gui, + gui_sender, + gui_to_engine_receiver, + beat_normal, + beat_emphasis, + restore, + ) + .expect("failed to set up coreaudio"); } else { eprintln!("Coreaudio is not supported on this system; choose another driver"); exit(1); @@ -154,6 +155,4 @@ fn main() { exit(1); } } - } - From a8db48e1dc2c1d1e6f0cfdfdb31a9b55ea0f9062 Mon Sep 17 00:00:00 2001 From: Micah Wylde Date: Mon, 8 Feb 2021 19:18:33 -0800 Subject: [PATCH 2/4] Initial support for controlling jack transport Co-authored-by: Philipp Mildenberger --- loopers-common/src/lib.rs | 8 +++++ loopers-engine/src/lib.rs | 66 +++++++++++++++++++++++++++---------- loopers/src/loopers_jack.rs | 62 ++++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+), 17 deletions(-) diff --git a/loopers-common/src/lib.rs b/loopers-common/src/lib.rs index 9a5b261..b9185f8 100644 --- a/loopers-common/src/lib.rs +++ b/loopers-common/src/lib.rs @@ -2,6 +2,9 @@ extern crate log; extern crate derive_more; +use crate::api::FrameTime; +use crate::music::MetricStructure; + pub mod api; pub mod config; pub mod gui_channel; @@ -32,4 +35,9 @@ pub trait Host<'a> { fn output_for_looper<'b>(&'b mut self, id: u32) -> Option<[&'b mut [f32]; 2]> where 'a: 'b; + + fn start_transport(&mut self) {} + fn stop_transport(&mut self) {} + #[allow(unused_variables)] + fn set_position(&mut self, time: FrameTime, metric_structure: MetricStructure) {} } diff --git a/loopers-engine/src/lib.rs b/loopers-engine/src/lib.rs index f6a065f..3bcb8ed 100644 --- a/loopers-engine/src/lib.rs +++ b/loopers-engine/src/lib.rs @@ -185,7 +185,7 @@ impl Engine { set_sample_rate(sample_rate); - engine.reset(); + engine.reset(host); for l in &engine.loopers { engine.session_saver.add_looper(l); @@ -218,12 +218,12 @@ impl Engine { triggers.push_back(t); } - fn reset(&mut self) { + fn reset<'a, H: Host<'a>>(&mut self, host: &mut H) { if let Some(m) = &mut self.metronome { m.reset(); } self.triggers.clear(); - self.set_time(FrameTime(-self.measure_len().0)); + self.set_time(FrameTime(-self.measure_len().0), host); for l in &mut self.loopers { l.handle_command(LooperCommand::Play); } @@ -453,7 +453,7 @@ impl Engine { self.id_counter = self.loopers.iter().map(|l| l.id).max().unwrap_or(0) + 1; - self.reset(); + self.reset(host); Ok(()) } @@ -513,33 +513,58 @@ impl Engine { } Start => { self.state = EngineState::Active; + host.set_position( + FrameTime(self.time + self.measure_len().0), + self.metric_structure, + ); + host.start_transport(); } Pause => { self.state = EngineState::Paused; + host.stop_transport(); } Stop => { self.state = EngineState::Stopped; - self.reset(); + self.reset(host); + host.stop_transport(); } StartStop => { self.state = match self.state { - EngineState::Stopped | EngineState::Paused => EngineState::Active, + EngineState::Stopped | EngineState::Paused => { + host.set_position( + FrameTime(self.time + self.measure_len().0), + self.metric_structure, + ); + host.start_transport(); + EngineState::Active + } EngineState::Active => { - self.reset(); + self.reset(host); + host.stop_transport(); EngineState::Stopped } }; } PlayPause => { self.state = match self.state { - EngineState::Stopped | EngineState::Paused => EngineState::Active, - EngineState::Active => EngineState::Paused, + EngineState::Stopped | EngineState::Paused => { + host.set_position( + FrameTime(self.time + self.measure_len().0), + self.metric_structure, + ); + host.start_transport(); + EngineState::Active + } + EngineState::Active => { + host.stop_transport(); + EngineState::Paused + } } } Reset => { - self.reset(); + self.reset(host); } - SetTime(time) => self.set_time(*time), + SetTime(time) => self.set_time(*time, host), AddLooper => { // TODO: make this non-allocating let looper = crate::Looper::new( @@ -703,7 +728,7 @@ impl Engine { if let Some(met) = &mut self.metronome { met.set_metric_structure(self.metric_structure); } - self.reset(); + self.reset(host); } SetTimeSignature(upper, lower) => { if let Some(ts) = TimeSignature::new(*upper, *lower) { @@ -711,7 +736,7 @@ impl Engine { if let Some(met) = &mut self.metronome { met.set_metric_structure(self.metric_structure); } - self.reset(); + self.reset(host); } } } @@ -741,10 +766,17 @@ impl Engine { FrameTime::from_ms(mspm as f64) } - fn set_time(&mut self, time: FrameTime) { - self.time = time.0; - for l in &mut self.loopers { - l.set_time(time); + fn set_time<'a, H: Host<'a>>(&mut self, time: FrameTime, host: &mut H) { + // for now, always send a positive time to the host + let pos_time = time + self.measure_len(); + if pos_time.0 < 0 { + error!("Tried to set time too far back into past, ignoring") + } else { + self.time = time.0; + for l in &mut self.loopers { + l.set_time(time); + } + host.set_position(pos_time, self.metric_structure); } } diff --git a/loopers/src/loopers_jack.rs b/loopers/src/loopers_jack.rs index b119720..e670097 100644 --- a/loopers/src/loopers_jack.rs +++ b/loopers/src/loopers_jack.rs @@ -1,8 +1,10 @@ use crossbeam_channel::{bounded, Receiver, Sender}; use jack::{AudioOut, Port, ProcessScope}; use loopers_common::api::Command; +use loopers_common::api::FrameTime; use loopers_common::gui_channel::GuiSender; use loopers_common::midi::MidiEvent; +use loopers_common::music::MetricStructure; use loopers_common::Host; use loopers_engine::Engine; use loopers_gui::Gui; @@ -12,6 +14,9 @@ use std::{io, thread}; enum ClientChange { AddPort(u32), RemovePort(u32, Port, Port), + TransportStart, + TransportStop, + TransportPosition(FrameTime, MetricStructure), Shutdown, } @@ -62,6 +67,27 @@ impl<'a> Host<'a> for JackHost<'a> { let [l, r] = self.looper_ports.get_mut(&id)?; Some([l.as_mut_slice(ps), r.as_mut_slice(ps)]) } + + fn start_transport(&mut self) { + if let Err(e) = self.port_change_tx.try_send(ClientChange::TransportStart) { + warn!("Failed to send start transport request: {:?}", e); + } + } + + fn stop_transport(&mut self) { + if let Err(e) = self.port_change_tx.try_send(ClientChange::TransportStop) { + warn!("Failed to send stop transport request: {:?}", e); + } + } + + fn set_position(&mut self, time: FrameTime, ms: MetricStructure) { + if let Err(e) = self + .port_change_tx + .try_send(ClientChange::TransportPosition(time, ms)) + { + warn!("Failed to send position transport request: {:?}", e); + } + } } struct Notifications; @@ -295,6 +321,42 @@ pub fn jack_main( } info!("removed ports for looper {}", id); } + Ok(ClientChange::TransportStart) => { + if let Err(e) = active_client.as_client().transport().start() { + error!("Failed to start jack transport: {:?}", e); + } + } + Ok(ClientChange::TransportStop) => { + if let Err(e) = active_client.as_client().transport().stop() { + error!("Failed to stop jack transport: {:?}", e); + } + } + Ok(ClientChange::TransportPosition(time, ms)) => { + let transport = active_client.as_client().transport(); + let mut pos = match transport.query() { + Ok(jack::TransportStatePosition { pos, state: _ }) => pos, + Err(e) => { + error!("Failed to query transport position: {}", e); + break; + } + }; + pos.set_frame(time.0 as u32); + + let mut bbt = pos.bbt().unwrap_or(jack::TransportBBT::default()); + + bbt.with_bpm(ms.tempo.bpm() as f64).with_timesig( + ms.time_signature.upper as f32, + ms.time_signature.lower as f32, + ); + + if let Err(e) = pos.set_bbt(Some(bbt)) { + error!("Invalid metric structure: {}", e); + } + + if let Err(e) = transport.reposition(&pos) { + error!("Failed to reposition transport: {}", e); + } + } Ok(ClientChange::Shutdown) => { break; } From 9c27024b3eb4353151bba7ee10e75738bbc2560f Mon Sep 17 00:00:00 2001 From: Philipp Mildenberger Date: Mon, 11 Dec 2023 00:07:48 +0100 Subject: [PATCH 3/4] Implement Jack Transport as timebase master and transport listener for start/stop --- loopers-common/src/api.rs | 24 ++++--- loopers-common/src/config.rs | 2 +- loopers-common/src/lib.rs | 5 +- loopers-engine/src/lib.rs | 94 ++++++++++++++++++------- loopers-engine/src/trigger.rs | 34 +++++++-- loopers-gui/src/app.rs | 6 +- loopers/src/loopers_jack.rs | 129 ++++++++++++++++++++++++++++------ loopers/src/main.rs | 13 ++-- 8 files changed, 230 insertions(+), 77 deletions(-) diff --git a/loopers-common/src/api.rs b/loopers-common/src/api.rs index 5fc7e5a..63d0da3 100644 --- a/loopers-common/src/api.rs +++ b/loopers-common/src/api.rs @@ -15,7 +15,7 @@ mod tests { #[test] fn test_from_str() { assert_eq!( - Command::Start, + Command::Start(false), Command::from_str("Start", &[][..]).unwrap()(CommandData { data: 0 }) ); @@ -224,12 +224,12 @@ impl LooperCommand { pub enum Command { Looper(LooperCommand, LooperTarget), - Start, - Stop, - Pause, + Start(bool), // set_transport + Stop(bool), // set_transport + Pause(bool), // set_transport - StartStop, - PlayPause, + StartStop(bool), // set_transport + PlayPause(bool), // set_transport Reset, SetTime(FrameTime), @@ -244,6 +244,8 @@ pub enum Command { PreviousPart, NextPart, GoToPart(Part), + SetTransportPosition(FrameTime), + StartTransport, SetQuantizationMode(QuantizationMode), @@ -262,11 +264,11 @@ impl Command { args: &[&str], ) -> Result Command + Send>, String> { Ok(match command { - "Start" => Box::new(|_| Command::Start), - "Stop" => Box::new(|_| Command::Stop), - "Pause" => Box::new(|_| Command::Pause), - "StartStop" => Box::new(|_| Command::StartStop), - "PlayPause" => Box::new(|_| Command::PlayPause), + "Start" => Box::new(|_| Command::Start(false)), + "Stop" => Box::new(|_| Command::Stop(false)), + "Pause" => Box::new(|_| Command::Pause(false)), + "StartStop" => Box::new(|_| Command::StartStop(false)), + "PlayPause" => Box::new(|_| Command::PlayPause(false)), "Reset" => Box::new(|_| Command::Reset), "SetTime" => { diff --git a/loopers-common/src/config.rs b/loopers-common/src/config.rs index 23ca120..f323ff3 100644 --- a/loopers-common/src/config.rs +++ b/loopers-common/src/config.rs @@ -58,7 +58,7 @@ mod tests { assert_eq!(24, mapping[2].controller); assert_eq!(DataValue::Value(6), mapping[2].data); assert_eq!( - Command::Start, + Command::Start(false), (mapping[2].command)(CommandData { data: 39 }) ); diff --git a/loopers-common/src/lib.rs b/loopers-common/src/lib.rs index b9185f8..8eaedc0 100644 --- a/loopers-common/src/lib.rs +++ b/loopers-common/src/lib.rs @@ -28,6 +28,7 @@ pub fn f32_to_i16(v: f32) -> i16 { (v * 32768.0).floor() as i16 } +#[allow(unused_variables)] pub trait Host<'a> { fn add_looper(&mut self, id: u32) -> Result<(), String>; fn remove_looper(&mut self, id: u32) -> Result<(), String>; @@ -38,6 +39,6 @@ pub trait Host<'a> { fn start_transport(&mut self) {} fn stop_transport(&mut self) {} - #[allow(unused_variables)] - fn set_position(&mut self, time: FrameTime, metric_structure: MetricStructure) {} + fn set_transport_position(&mut self, time: FrameTime, metric_structure: MetricStructure) {} + fn set_transport_bpm(&mut self, bpm: f32) {} } diff --git a/loopers-engine/src/lib.rs b/loopers-engine/src/lib.rs index 3bcb8ed..0f47060 100644 --- a/loopers-engine/src/lib.rs +++ b/loopers-engine/src/lib.rs @@ -45,9 +45,9 @@ pub struct Engine { state: EngineState, - time: i64, + pub time: i64, - metric_structure: MetricStructure, + pub metric_structure: MetricStructure, command_input: Receiver, @@ -119,6 +119,40 @@ pub fn read_config() -> Result { Ok(config) } +/// Starts host transport (e.g. jack transport), and sets the transport position to the current time +/// In case the time is negative, use triggers at time 0 instead +fn start_transport<'a, H: Host<'a>>(engine: &mut Engine, host: &mut H) { + if engine.time < 0 { + let start_time = FrameTime(0); + let set_position_trigger = Trigger::new( + TriggerCondition::Immediate, + Command::SetTransportPosition(start_time), + engine.metric_structure, + start_time, + ); + let start_trigger = Trigger::new( + TriggerCondition::Immediate, + Command::StartTransport, + engine.metric_structure, + start_time, + ); + // avoid weird behavior by removing previous start/position transport triggers + // TODO this currently removes all StartTransport/SetTransportPosition triggers, + // this could be more precise, by only removing start_time (0) triggers + engine.triggers.retain(|t| { + !matches!( + t.command, + Command::StartTransport | Command::SetTransportPosition(_) + ) + }); + Engine::add_trigger(&mut engine.triggers, set_position_trigger); + Engine::add_trigger(&mut engine.triggers, start_trigger); + } else { + host.set_transport_position(FrameTime(engine.time), engine.metric_structure); + host.start_transport(); + } +} + impl Engine { pub fn new<'a, H: Host<'a>>( host: &mut H, @@ -224,6 +258,10 @@ impl Engine { } self.triggers.clear(); self.set_time(FrameTime(-self.measure_len().0), host); + host.stop_transport(); + if self.state == EngineState::Active { + start_transport(self, host); + } for l in &mut self.loopers { l.handle_command(LooperCommand::Play); } @@ -511,52 +549,51 @@ impl Engine { Looper(lc, target) => { self.handle_loop_command(*lc, *target, triggered); } - Start => { + Start(set_transport) => { self.state = EngineState::Active; - host.set_position( - FrameTime(self.time + self.measure_len().0), - self.metric_structure, - ); - host.start_transport(); + if *set_transport { + start_transport(self, host); + } } - Pause => { + Pause(set_transport) => { self.state = EngineState::Paused; - host.stop_transport(); + if *set_transport { + host.stop_transport(); + } } - Stop => { + Stop(set_transport) => { self.state = EngineState::Stopped; + if *set_transport { + host.stop_transport(); + } self.reset(host); - host.stop_transport(); } - StartStop => { + StartStop(set_transport) => { self.state = match self.state { EngineState::Stopped | EngineState::Paused => { - host.set_position( - FrameTime(self.time + self.measure_len().0), - self.metric_structure, - ); - host.start_transport(); + if *set_transport { + start_transport(self, host); + } EngineState::Active } EngineState::Active => { self.reset(host); - host.stop_transport(); EngineState::Stopped } }; } - PlayPause => { + PlayPause(set_transport) => { self.state = match self.state { EngineState::Stopped | EngineState::Paused => { - host.set_position( - FrameTime(self.time + self.measure_len().0), - self.metric_structure, - ); - host.start_transport(); + if *set_transport { + start_transport(self, host); + } EngineState::Active } EngineState::Active => { - host.stop_transport(); + if *set_transport { + host.stop_transport(); + } EngineState::Paused } } @@ -739,6 +776,8 @@ impl Engine { self.reset(host); } } + SetTransportPosition(time) => host.set_transport_position(*time, self.metric_structure), + StartTransport => host.start_transport(), } } @@ -776,7 +815,7 @@ impl Engine { for l in &mut self.loopers { l.set_time(time); } - host.set_position(pos_time, self.metric_structure); + host.set_transport_position(pos_time, self.metric_structure); } } @@ -1039,6 +1078,7 @@ impl Engine { || l.local_mode() == LooperMode::Overdubbing })) { + start_transport(self, host); self.state = EngineState::Active; } diff --git a/loopers-engine/src/trigger.rs b/loopers-engine/src/trigger.rs index 4ea0d51..117190a 100644 --- a/loopers-engine/src/trigger.rs +++ b/loopers-engine/src/trigger.rs @@ -33,17 +33,27 @@ mod tests { time_signature: TimeSignature::new(4, 4).unwrap(), }; - let t = Trigger::new(TriggerCondition::Measure, Command::Start, ms, FrameTime(0)); + let t = Trigger::new( + TriggerCondition::Measure, + Command::Start(false), + ms, + FrameTime(0), + ); assert_eq!(FrameTime(0), t.triggered_at()); - let t = Trigger::new(TriggerCondition::Measure, Command::Start, ms, FrameTime(1)); + let t = Trigger::new( + TriggerCondition::Measure, + Command::Start(false), + ms, + FrameTime(1), + ); assert_eq!(FrameTime(88200), t.triggered_at()); let t = Trigger::new( TriggerCondition::Measure, - Command::Start, + Command::Start(false), ms, FrameTime(-30000), ); @@ -52,7 +62,7 @@ mod tests { let t = Trigger::new( TriggerCondition::Measure, - Command::Start, + Command::Start(false), ms, FrameTime(88200), ); @@ -67,11 +77,21 @@ mod tests { time_signature: TimeSignature::new(4, 4).unwrap(), }; - let t = Trigger::new(TriggerCondition::Beat, Command::Start, ms, FrameTime(0)); + let t = Trigger::new( + TriggerCondition::Beat, + Command::Start(false), + ms, + FrameTime(0), + ); assert_eq!(FrameTime(0), t.triggered_at()); - let t = Trigger::new(TriggerCondition::Beat, Command::Start, ms, FrameTime(1)); + let t = Trigger::new( + TriggerCondition::Beat, + Command::Start(false), + ms, + FrameTime(1), + ); assert_eq!(FrameTime(22050), t.triggered_at); } @@ -85,7 +105,7 @@ mod tests { }; let t = Trigger::new(TriggerCondition::Measure, - Command::Start, ms, FrameTime(time)); + Command::Start(false), ms, FrameTime(time)); assert_eq!(correct_measure_trigger(&t), t.triggered_at()); diff --git a/loopers-gui/src/app.rs b/loopers-gui/src/app.rs index fe20751..1f0662d 100644 --- a/loopers-gui/src/app.rs +++ b/loopers-gui/src/app.rs @@ -1235,9 +1235,9 @@ impl PlayPauseButton { |button| { if button == MouseButton::Left { let command = if data.engine_state.engine_state == EngineState::Active { - Command::Pause + Command::Pause(true) } else { - Command::Start + Command::Start(true) }; controller.send_command(command, "Failed to send command to engine"); @@ -1315,7 +1315,7 @@ impl StopButton { &bounds, |button| { if button == MouseButton::Left { - controller.send_command(Command::Stop, "Failed to stop engine"); + controller.send_command(Command::Stop(true), "Failed to stop engine"); } }, last_event, diff --git a/loopers/src/loopers_jack.rs b/loopers/src/loopers_jack.rs index e670097..aee7303 100644 --- a/loopers/src/loopers_jack.rs +++ b/loopers/src/loopers_jack.rs @@ -1,4 +1,7 @@ use crossbeam_channel::{bounded, Receiver, Sender}; +use jack::jack_sys::{ + jack_nframes_t, jack_position_t, jack_set_timebase_callback, jack_transport_state_t, +}; use jack::{AudioOut, Port, ProcessScope}; use loopers_common::api::Command; use loopers_common::api::FrameTime; @@ -9,6 +12,9 @@ use loopers_common::Host; use loopers_engine::Engine; use loopers_gui::Gui; use std::collections::HashMap; +use std::ffi::c_void; +use std::sync::atomic::AtomicBool; +use std::sync::{Arc, Mutex}; use std::{io, thread}; enum ClientChange { @@ -80,7 +86,7 @@ impl<'a> Host<'a> for JackHost<'a> { } } - fn set_position(&mut self, time: FrameTime, ms: MetricStructure) { + fn set_transport_position(&mut self, time: FrameTime, ms: MetricStructure) { if let Err(e) = self .port_change_tx .try_send(ClientChange::TransportPosition(time, ms)) @@ -175,10 +181,83 @@ impl jack::NotificationHandler for Notifications { } } +fn update_transport_position( + pos: &mut jack::TransportPosition, + time: FrameTime, + ms: MetricStructure, +) { + pos.set_frame(time.0 as u32); + + let mut bbt = pos.bbt().unwrap_or_default(); + + let beat = ms.tempo.beat(time); + let beats_per_bar = ms.time_signature.upper; + + if beats_per_bar != 0 && beat >= 0 { + bbt.bar = (beat / beats_per_bar as i64) as usize + 1; + bbt.beat = (beat % beats_per_bar as i64) as usize + 1; + } + + bbt.with_ticks_per_beat(ms.tempo.samples_per_beat() as f64); + bbt.tick = (time.0 - beat * ms.tempo.samples_per_beat() as i64) as usize; + + bbt.with_bpm(ms.tempo.bpm() as f64).with_timesig( + ms.time_signature.upper as f32, + ms.time_signature.lower as f32, + ); + + if let Err(e) = pos.set_bbt(Some(bbt)) { + error!("Invalid metric structure: {}", e); + } +} + +#[no_mangle] +pub extern "C" fn timebase_callback( + _state: jack_transport_state_t, + _nframes: jack_nframes_t, + pos: *mut jack_position_t, + new_pos: std::ffi::c_int, + arg: *mut std::ffi::c_void, +) { + let mut engine = unsafe { &mut *(arg as *mut Arc>) } + .lock() + .unwrap(); + let pos = unsafe { &mut *pos.cast::() }; + if new_pos != 0 { + engine.time = pos.frame() as i64; + } + update_transport_position(pos, FrameTime(engine.time), engine.metric_structure); +} + +fn transport_listener( + transport: jack::Transport, + stop: Arc, + command_sender: Sender, +) { + std::thread::spawn(move || { + let mut state_last: Option = None; + while !stop.load(std::sync::atomic::Ordering::Relaxed) { + std::thread::sleep(std::time::Duration::from_millis(10)); + if let Ok(jack::TransportStatePosition { state, .. }) = transport.query() { + if Some(state) != state_last { + let _ = match state { + jack::TransportState::Starting | jack::TransportState::Rolling => { + command_sender.send(Command::Start(false)) + } + jack::TransportState::Stopped => command_sender.send(Command::Pause(false)), + }; + state_last = Some(state); + } + } + } + }); +} + pub fn jack_main( gui: Option, gui_sender: GuiSender, - gui_to_engine_receiver: Receiver, + command_to_engine_sender: Sender, + command_to_engine_receiver: Receiver, beat_normal: Vec, beat_emphasis: Vec, restore: bool, @@ -217,16 +296,25 @@ pub fn jack_main( port_change_resp: port_change_resp_rx.clone(), }; - let mut engine = Engine::new( + let engine = Arc::new(Mutex::new(Engine::new( &mut host, gui_sender, - gui_to_engine_receiver, + command_to_engine_receiver, beat_normal, beat_emphasis, restore, client.sample_rate(), - ); - + ))); + + let mut engine_clone = engine.clone(); + unsafe { + jack_set_timebase_callback( + client.raw(), + 0, + Some(timebase_callback), + &mut engine_clone as *mut _ as *mut c_void, + ); + } let process_port_change = port_change_tx.clone(); let process_callback = @@ -268,7 +356,7 @@ pub fn jack_main( .filter_map(|e| MidiEvent::from_bytes(e.bytes)) .collect(); - engine.process( + engine.lock().unwrap().process( &mut host, in_bufs, out_l, @@ -285,6 +373,15 @@ pub fn jack_main( // Activate the client, which starts the processing. let active_client = client.activate_async(Notifications, process).unwrap(); + let transport = active_client.as_client().transport(); + let stop_transport_listener = Arc::new(AtomicBool::new(false)); + + transport_listener( + transport, + stop_transport_listener.clone(), + command_to_engine_sender, + ); + thread::spawn(move || loop { match port_change_rx.recv() { Ok(ClientChange::AddPort(id)) => { @@ -340,27 +437,15 @@ pub fn jack_main( break; } }; - pos.set_frame(time.0 as u32); - - let mut bbt = pos.bbt().unwrap_or(jack::TransportBBT::default()); - bbt.with_bpm(ms.tempo.bpm() as f64).with_timesig( - ms.time_signature.upper as f32, - ms.time_signature.lower as f32, - ); - - if let Err(e) = pos.set_bbt(Some(bbt)) { - error!("Invalid metric structure: {}", e); - } + update_transport_position(&mut pos, time, ms); if let Err(e) = transport.reposition(&pos) { error!("Failed to reposition transport: {}", e); } } - Ok(ClientChange::Shutdown) => { - break; - } - Err(_) => { + Ok(ClientChange::Shutdown) | Err(_) => { + stop_transport_listener.store(true, std::sync::atomic::Ordering::Relaxed); break; } } diff --git a/loopers/src/main.rs b/loopers/src/main.rs index 09ee52c..af8dd07 100644 --- a/loopers/src/main.rs +++ b/loopers/src/main.rs @@ -99,12 +99,16 @@ fn main() { info!("Restoring previous session"); } - let (gui_to_engine_sender, gui_to_engine_receiver) = bounded(100); + let (command_to_engine_sender, command_to_engine_receiver) = bounded(100); let (gui, gui_sender) = if !matches.get_flag("no-gui") { let (sender, receiver) = GuiSender::new(); ( - Some(Gui::new(receiver, gui_to_engine_sender, sender.clone())), + Some(Gui::new( + receiver, + command_to_engine_sender.clone(), + sender.clone(), + )), sender, ) } else { @@ -127,7 +131,8 @@ fn main() { jack_main( gui, gui_sender, - gui_to_engine_receiver, + command_to_engine_sender, + command_to_engine_receiver, beat_normal, beat_emphasis, restore, @@ -139,7 +144,7 @@ fn main() { crate::looper_coreaudio::coreaudio_main( gui, gui_sender, - gui_to_engine_receiver, + command_to_engine_receiver, beat_normal, beat_emphasis, restore, From 78a63d4eeb5a9bcc815036480a19a76d030e1298 Mon Sep 17 00:00:00 2001 From: Philipp Mildenberger Date: Mon, 11 Dec 2023 01:07:31 +0100 Subject: [PATCH 4/4] Don't set time via jack transport, when the time is 0 (fixes precount issue) --- loopers/src/loopers_jack.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loopers/src/loopers_jack.rs b/loopers/src/loopers_jack.rs index aee7303..d208178 100644 --- a/loopers/src/loopers_jack.rs +++ b/loopers/src/loopers_jack.rs @@ -223,7 +223,7 @@ pub extern "C" fn timebase_callback( .lock() .unwrap(); let pos = unsafe { &mut *pos.cast::() }; - if new_pos != 0 { + if new_pos != 0 && (engine.time >= 0 || pos.frame() != 0) { engine.time = pos.frame() as i64; } update_transport_position(pos, FrameTime(engine.time), engine.metric_structure);