mirror of
https://github.com/storytold/soundcraft.git
synced 2026-10-26 03:26:49 -06:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b2cac81ed |
Generated
+208
-139
@@ -4,18 +4,18 @@ version = 4
|
||||
|
||||
[[package]]
|
||||
name = "accesskit"
|
||||
version = "0.25.2"
|
||||
version = "0.25.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e883d1d28c5fd5568c79ed2d2bd48d9f2fd3945c8974ed0f53690ceddb051be"
|
||||
checksum = "ad442f58ee04714aaa0ba0a2768c1ea1935b29507bb22ddece8cbbc76db02932"
|
||||
dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_atspi_common"
|
||||
version = "0.21.1"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24f5baf62e34a4a6c30e3d697e4782f821a7337049c44c3b40576a27dfbb580d"
|
||||
checksum = "52c182f9c282ac9c5638d876d551d15e5f7d397ec263349a0c6a2b61595dd5e4"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_consumer",
|
||||
@@ -27,9 +27,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_consumer"
|
||||
version = "0.39.2"
|
||||
version = "0.39.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3da8f27636015191b2264ee1df681503170a632cafd832fdc1edfdd4e3a2fd3"
|
||||
checksum = "8a45e2bb300bea6d5f33a82c3e051c3ddc6f41c209750f68a0acbe991a284344"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"hashbrown 0.17.1",
|
||||
@@ -37,9 +37,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_ios"
|
||||
version = "0.2.2"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5280724673bbfa8b30a838c013500b719392989be3b97c5a9964062c252c232"
|
||||
checksum = "28e539d64b27fd4c14de8f78ada8f7c944c092f52f7e49fb63316507a57f6937"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_consumer",
|
||||
@@ -51,9 +51,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_macos"
|
||||
version = "0.27.2"
|
||||
version = "0.27.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "123828ceeec755b05255e3976cb0852993202de98a0626c97dd8d34246991f84"
|
||||
checksum = "5701624bf6a11efb6d4c8e922bf44126ed96250e5faa9a7a0468c979635839a4"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_consumer",
|
||||
@@ -65,9 +65,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_unix"
|
||||
version = "0.24.1"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30d5192871228c54f1b0fb4a85cef000e11a3804f25d2165f14afa0daa67f3f6"
|
||||
checksum = "202f24df034a7476d07b7f74284de84f6d62aabd858dbe7ee9cad3b7ad6f8f9d"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_atspi_common",
|
||||
@@ -83,9 +83,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_windows"
|
||||
version = "0.35.2"
|
||||
version = "0.35.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "498668b90da9995302a0055aaecad4756932333da2ddef6baee1c00844c430fc"
|
||||
checksum = "ce63f35d6bdcf59f26b76b3379063f738e6412cef46999cc772d46aa3de35adb"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_consumer",
|
||||
@@ -97,9 +97,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "accesskit_winit"
|
||||
version = "0.34.2"
|
||||
version = "0.34.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd4ab4437b520ff03088fbe48676a11e7d44a929535e64b31432c1073e48dbcc"
|
||||
checksum = "ee96dd150375c05912d8a473f98c2fb155fb3234fac88292d396548a2460fa77"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_ios",
|
||||
@@ -208,9 +208,9 @@ checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470"
|
||||
|
||||
[[package]]
|
||||
name = "arboard"
|
||||
version = "3.7.0"
|
||||
version = "3.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aeb9553e62312c8756a5979254fc6d7da8bbeb5986a1e17037fab932f7037917"
|
||||
checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf"
|
||||
dependencies = [
|
||||
"clipboard-win",
|
||||
"image",
|
||||
@@ -222,7 +222,7 @@ dependencies = [
|
||||
"objc2-foundation 0.3.2",
|
||||
"parking_lot",
|
||||
"percent-encoding",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
"wl-clipboard-rs",
|
||||
"x11rb",
|
||||
]
|
||||
@@ -354,7 +354,7 @@ checksum = "4f8abc12baad266b1c8cec146854c195b5864b4221d4b2ca7296a7ae82d9e451"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.9",
|
||||
"syn 3.0.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -389,7 +389,7 @@ checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.9",
|
||||
"syn 3.0.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -574,7 +574,7 @@ dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.9",
|
||||
"syn 3.0.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -600,7 +600,7 @@ checksum = "6a1f896587b6f2c069c73d2f0913e2d590c3990285cd2f0b6aa02b786b4c679c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.9",
|
||||
"syn 3.0.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -667,9 +667,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.10.0"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86e055fa6e3e40ad6187ba25b6855eeb635d5271f8e0f0e1e9a5826b8d75bea0"
|
||||
checksum = "f74872d07caf508b30a21f6836e7d7016a2eaf7d9ff4f48deaa58cd8a0407630"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"jobserver",
|
||||
@@ -746,9 +746,9 @@ checksum = "a76abbdb2907f6fd97fb6bc0b7be96b77d328f2dd9669d1075cc03369ed22154"
|
||||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
version = "5.4.3"
|
||||
version = "5.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "052af7878466feccda12ba7a6a67d019d712f296809559b47c12c8cc196d3a9a"
|
||||
checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4"
|
||||
dependencies = [
|
||||
"error-code",
|
||||
]
|
||||
@@ -791,9 +791,9 @@ checksum = "5ce183b975b8fc736a20919c4002717255fbd867df575c792aeefa9a56a73ad0"
|
||||
|
||||
[[package]]
|
||||
name = "combine"
|
||||
version = "4.6.9"
|
||||
version = "4.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4356eba6976435498d05a311ee9fc486da3bc81c200d3f0d7dd9703b41913f92"
|
||||
checksum = "cfc320937d09e6de266b31b9afb480f197d7a861be86be7cb2ea7e5d1bfffc5e"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"memchr",
|
||||
@@ -984,9 +984,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.5.3"
|
||||
version = "1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ba106e844045ddc02c20768ac4b0b7cb45f2cc2ecbd833ac9f5dacb183c2055"
|
||||
checksum = "01a7799fd6b852db0e61728dde9a204c423b44d689dbd432522543614b490e78"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
@@ -1108,7 +1108,7 @@ checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.9",
|
||||
"syn 3.0.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1362,9 +1362,9 @@ source = "git+https://github.com/storytold/egui.git?rev=6d518fc799681571963842f9
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.3"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
@@ -1456,9 +1456,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.15"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c66915cc5391097de7a2b0b972e8505aa33c0df401fd3a6811dbdb32881ba48"
|
||||
checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484"
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
@@ -1497,9 +1497,9 @@ checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
||||
|
||||
[[package]]
|
||||
name = "font-types"
|
||||
version = "0.12.7"
|
||||
version = "0.12.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "835ae9d781464a79026cde387f9cbc650c9a4909d7cf7e658b127e3c35d2f35d"
|
||||
checksum = "6c92d9081c7ab3bd83ef1eecdfad090f3d966e3ce74106779664114cfe1a6274"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
@@ -1522,7 +1522,7 @@ checksum = "ea5190182e6915eb873ddbc16e23b711b6eb1f9c00a0d0a3a91b5f6228475225"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.9",
|
||||
"syn 3.0.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1582,7 +1582,7 @@ checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.9",
|
||||
"syn 3.0.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1663,11 +1663,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"r-efi 6.0.0",
|
||||
"rand_core",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1887,9 +1885,9 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
version = "2.4.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe090a42a0d3140fed8c63263ed1fecc02422cb2f4bfe55cd8de2de1b8e105dd"
|
||||
checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"potential_utf",
|
||||
@@ -1901,9 +1899,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "icu_locale_core"
|
||||
version = "2.4.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25d29b57c17c6528d0dc4c2141cfa2d08200465acb537a3909c8b9b8a4a984c0"
|
||||
checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"litemap",
|
||||
@@ -1914,9 +1912,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer"
|
||||
version = "2.4.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "404a3b8c486f205c87fe7f23ab6894d84a4ad76d6e4671ae1981ba7def87c599"
|
||||
checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f"
|
||||
dependencies = [
|
||||
"icu_collections",
|
||||
"icu_normalizer_data",
|
||||
@@ -1928,15 +1926,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer_data"
|
||||
version = "2.4.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20f0088bfb7441f7a37fa0c9e20cb1e9cba1cac6b9ce8cd2a6dc44294f8e8cbc"
|
||||
checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0"
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties"
|
||||
version = "2.4.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ea83d991c56158f7cf898bf86436c1dfe7c07ee82fe88951f2695c9f37a379f"
|
||||
checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
@@ -1949,15 +1947,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties_data"
|
||||
version = "2.4.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b98f29af82d02b163fe50161388350e743bb3107cc8e8c2f7314cb505faddfcf"
|
||||
checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa"
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider"
|
||||
version = "2.4.0"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413099315636c588ee8f011190b329b971a09a8fae9d157e01dcae8379468514"
|
||||
checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locale_core",
|
||||
@@ -2194,9 +2192,9 @@ checksum = "20870f649af7073d53e38067b2a84312175d56ea15217e1b15bc83506ec50afb"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.191"
|
||||
version = "0.2.190"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a"
|
||||
checksum = "ce5d3ddc6d3fa000eb1536d85e147bfe31aacaba692ed6a876f95cb7c855be78"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
@@ -2233,7 +2231,7 @@ dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
"libc",
|
||||
"plain",
|
||||
"redox_syscall 0.9.5",
|
||||
"redox_syscall 0.9.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2262,9 +2260,9 @@ checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae"
|
||||
|
||||
[[package]]
|
||||
name = "litrs"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4744e383959f0db86ede514b809b1c53251889093803c05267acc7d4e7030d70"
|
||||
checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
@@ -2295,9 +2293,9 @@ checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
||||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
version = "0.9.12"
|
||||
version = "0.9.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b0ca1155a9ecc25aa12e8313a119f32edf0255fea2f44839c4ac0a12b2ac157"
|
||||
checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -2908,9 +2906,9 @@ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||
|
||||
[[package]]
|
||||
name = "open"
|
||||
version = "5.4.5"
|
||||
version = "5.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d8c8b8e725ad26404d6896eb2f064294a0a7b4f1d9120210d85544684c4ca95"
|
||||
checksum = "aa576c76302b7b808eecc68061e67336c47833ef9d22caa74dda10fa9675eebc"
|
||||
dependencies = [
|
||||
"is-wsl",
|
||||
"libc",
|
||||
@@ -2934,9 +2932,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ordered-float"
|
||||
version = "5.5.1"
|
||||
version = "5.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f59006ce1e204c30956638535b74d317bca99477b9356dfaec54c37f6f600ab"
|
||||
checksum = "8c7c9e0d9b23589f26070720bac724174bfec1083e82f7854cdd0267518343c0"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
@@ -3429,9 +3427,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.9.5"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "580c875845b41ecb1564ad1ae4703044cef774145e3d7a94dc2b3fa7c97868fd"
|
||||
checksum = "737970939a87c6fa31e7acad13307bccbb017a073b695b6089a2c484f929e20e"
|
||||
dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
]
|
||||
@@ -3773,7 +3771,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.9",
|
||||
"syn 3.0.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3797,14 +3795,14 @@ checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.9",
|
||||
"syn 3.0.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "1.1.2"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e7523beb55eece201a2356bee0bbca0d1ab466c14c07703b2e0ee6d42cb0c2c"
|
||||
checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
@@ -3897,9 +3895,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.16.4"
|
||||
version = "1.16.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0d5d4b1981b8bbbb7aed204cae5726d683d5d6ac7e74ec54122ea28f315e7c8"
|
||||
checksum = "f9395f0f0eee849a9b707b2f06bb92a6a422090e2123bb2ef8e87a0e61892a8e"
|
||||
|
||||
[[package]]
|
||||
name = "smithay-client-toolkit"
|
||||
@@ -3990,7 +3988,6 @@ dependencies = [
|
||||
"soundcraft-engine",
|
||||
"soundcraft-playback",
|
||||
"soundcraft-ui-egui",
|
||||
"winit",
|
||||
"winresource",
|
||||
]
|
||||
|
||||
@@ -4467,9 +4464,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "3.0.9"
|
||||
version = "3.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b"
|
||||
checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -4484,7 +4481,7 @@ checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.9",
|
||||
"syn 3.0.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4556,7 +4553,7 @@ checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.9",
|
||||
"syn 3.0.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4605,9 +4602,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.8.5"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abc840c238154d58f9e86b2792261edfde565a17696e1cda1a449de27979801a"
|
||||
checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"zerovec",
|
||||
@@ -4615,18 +4612,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.54.1"
|
||||
version = "1.53.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b52fd3255258d26fac7264ab5a76b53c13367136dc011d3d6ad4f8ed070396a3"
|
||||
checksum = "e95f91fcc7a621e8b030f6aa23c71fe9838ae2fb4d8118b75602a328f5144044"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "1.1.8+spec-1.1.0"
|
||||
version = "1.1.6+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20489e00e4d8741d6be680764cc12e270655e375a20d1011e844a9c3379e678d"
|
||||
checksum = "920602543f0911ab71da12c50d59701da54c196d1a2bf5cb4b75667f137a406a"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde_core",
|
||||
@@ -4639,18 +4636,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "1.1.2+spec-1.1.0"
|
||||
version = "1.1.1+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b86d767906c6c42421dcba507eb9d203e779497710a47782a224bb871653053"
|
||||
checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.25.17+spec-1.1.0"
|
||||
version = "0.25.15+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3641d5bbb5349a79e1020a242d251efbc546ad8048d133958323ce9c40a9c9c"
|
||||
checksum = "1340ea94a5856333492c9064b02c778b191dd2c853778d9609debdcdfea3a614"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"toml_datetime",
|
||||
@@ -4660,18 +4657,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.1.5+spec-1.1.0"
|
||||
version = "1.1.3+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baa693a8032d7e1cada7d0041e96126df243179ff061456783ac7f12bda4744c"
|
||||
checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_writer"
|
||||
version = "1.1.3+spec-1.1.0"
|
||||
version = "1.1.2+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06bdbd8cfc056b8d2e2e85f29b56a3bdbecb527cef81eb39e3e7b98af4652770"
|
||||
checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
@@ -4737,9 +4734,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "typed-path"
|
||||
version = "0.13.0"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ce898823d8cd0f028662d2f349694b1af0a5cc9be83f1d213a00d3147445e1e"
|
||||
checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
@@ -4778,9 +4775,9 @@ checksum = "0b993bddc193ae5bd0d623b49ec06ac3e9312875fdae725a975c51db1cc1677f"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.27"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563"
|
||||
checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-properties"
|
||||
@@ -4861,9 +4858,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.29.0"
|
||||
version = "1.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a0e2d11620c50695fe12bbbfde96b3477fe606e7284c22d505c12ce51214514"
|
||||
checksum = "97277d36b9c3ace13e58fa6e753f8b0bbbf302a18dd193240d70f9e29681059a"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"serde_core",
|
||||
@@ -4999,7 +4996,7 @@ dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.9",
|
||||
"syn 3.0.6",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -5169,9 +5166,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "webbrowser"
|
||||
version = "1.2.5"
|
||||
version = "1.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2c8ddfbbf66e501434657400268cefe51b95140a0266236415cda7627732df7"
|
||||
checksum = "62c35be770821a214dbc362fc26908c853e776c0004294d0b10b8a6bad582f94"
|
||||
dependencies = [
|
||||
"jni",
|
||||
"log",
|
||||
@@ -5497,7 +5494,7 @@ version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5506,7 +5503,16 @@ version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets 0.53.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5524,14 +5530,31 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm 0.52.6",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows_aarch64_gnullvm 0.53.1",
|
||||
"windows_aarch64_msvc 0.53.1",
|
||||
"windows_i686_gnu 0.53.1",
|
||||
"windows_i686_gnullvm 0.53.1",
|
||||
"windows_i686_msvc 0.53.1",
|
||||
"windows_x86_64_gnu 0.53.1",
|
||||
"windows_x86_64_gnullvm 0.53.1",
|
||||
"windows_x86_64_msvc 0.53.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5549,48 +5572,96 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||
|
||||
[[package]]
|
||||
name = "winit"
|
||||
version = "0.30.13"
|
||||
@@ -5778,9 +5849,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.8.4"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "417ef3fcf6c300b7dbd25c85531a746931c02ad536f3cf4786ed1e88223340b3"
|
||||
checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5"
|
||||
dependencies = [
|
||||
"stable_deref_trait",
|
||||
"yoke-derive",
|
||||
@@ -5795,7 +5866,7 @@ checksum = "ec8ebde2db3681e8c9980cc27822030e68752690ddfa9473e739aeb4dbde6d71"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.9",
|
||||
"syn 3.0.6",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -5867,7 +5938,7 @@ dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.9",
|
||||
"syn 3.0.6",
|
||||
"zbus_names",
|
||||
"zvariant",
|
||||
"zvariant_utils",
|
||||
@@ -5875,9 +5946,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zbus_names"
|
||||
version = "4.3.5"
|
||||
version = "4.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "812a12be4ead7a59e18ea8f047d08add0066b73bc62fc1467066a2472ae088dd"
|
||||
checksum = "d8bf88b4a3ff53e883001e0e0115b297a9d53c31b9c1edd2bfdd853e3428624e"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"winnow",
|
||||
@@ -5907,18 +5978,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.63"
|
||||
version = "0.8.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5fe1f8f1b06191a00962174c61aa5005e0bb391a6d80d07e24d115c01a92ed8"
|
||||
checksum = "6400fc4bb7426f6faac3fb07566122bb18622e3022bd2c36426a92ed489b1dbe"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.63"
|
||||
version = "0.8.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "863ad3ac83293fb4d740aedbfdc9240dd8d1a50c1099acd76ce80ce7c7230c7f"
|
||||
checksum = "99c27cc7525bad4df59a45e073c45b2c2aa820efb588234cc23a547ec69a34db"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -5942,7 +6013,7 @@ checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.9",
|
||||
"syn 3.0.6",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -5965,9 +6036,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.11.9"
|
||||
version = "0.11.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f58a2799cfe9a1670c7ea8edbd52ef8edb93cb37a7f83bf31ba9e35d9dae6a40"
|
||||
checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8"
|
||||
dependencies = [
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
@@ -5976,29 +6047,27 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerovec-derive"
|
||||
version = "0.11.7"
|
||||
version = "0.11.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3743ddbf47738d929cd1ae2e199d40d0162979dfe31aef8ae646d2bf00bf97a"
|
||||
checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.9",
|
||||
"syn 3.0.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "9.1.1"
|
||||
version = "9.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "518b847fd864440c379748ad684c449f293af68978a485a4ff16f7c901c1f7c4"
|
||||
checksum = "6e04976da629d3b04204ba8105b396a193af8ad2583b2c79bfb3ab8af580a11e"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"flate2",
|
||||
"getrandom 0.4.3",
|
||||
"indexmap",
|
||||
"memchr",
|
||||
"nix",
|
||||
"typed-path",
|
||||
"windows-sys 0.61.2",
|
||||
"zopfli",
|
||||
]
|
||||
|
||||
@@ -6043,9 +6112,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zvariant"
|
||||
version = "5.15.1"
|
||||
version = "5.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80bf6d6cd6a4b601acb64cfb8284cdc643fcfa75ef26ae3c46ec8aa4e8a1fbb7"
|
||||
checksum = "c1d34c27cc6cdd1f458427519dd6b8612f7b7e3f7b9a0b2355d041dda9869147"
|
||||
dependencies = [
|
||||
"endi",
|
||||
"enumflags2",
|
||||
@@ -6058,14 +6127,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zvariant_derive"
|
||||
version = "5.15.1"
|
||||
version = "5.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef2672456ba1cb54b5eaec136e63135b3c7495fb938e53b28a62e633496d486d"
|
||||
checksum = "864155e69b4352db0c7f374917bf45d1e0c8d17659c8b3dbf9795f3673f8c497"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.9",
|
||||
"syn 3.0.6",
|
||||
"zvariant_utils",
|
||||
]
|
||||
|
||||
@@ -6078,7 +6147,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"syn 3.0.9",
|
||||
"syn 3.0.6",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
|
||||
@@ -24,9 +24,6 @@ serde_json.workspace = true
|
||||
log.workspace = true
|
||||
chrono.workspace = true
|
||||
|
||||
[target.'cfg(all(unix, not(target_os = "macos")))'.dependencies]
|
||||
winit = { version = "0.30", default-features = false, features = ["x11"] }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
rfd.workspace = true
|
||||
|
||||
|
||||
@@ -274,16 +274,6 @@ fn main() -> eframe::Result {
|
||||
// Before eframe creates the wgpu instance: default Windows to DirectX 12 with FXC (see graphics.rs).
|
||||
#[cfg(target_os = "windows")]
|
||||
graphics::configure(&mut options, eframe::wgpu::Backends::from_env(), eframe::wgpu::Dx12Compiler::from_env());
|
||||
#[cfg(all(unix, not(target_os = "macos")))]
|
||||
// winit has no file drag-and-drop on Wayland (only on X11), so dropping files from the file
|
||||
// manager showed a "no" cursor. Run through XWayland when it's there; SOUNDCRAFT_WAYLAND=1
|
||||
// keeps the native Wayland backend.
|
||||
if std::env::var_os("DISPLAY").is_some_and(|d| !d.is_empty()) && std::env::var_os("SOUNDCRAFT_WAYLAND").is_none() {
|
||||
options.event_loop_builder = Some(Box::new(|b| {
|
||||
use winit::platform::x11::EventLoopBuilderExtX11;
|
||||
b.with_x11();
|
||||
}));
|
||||
}
|
||||
eframe::run_native(
|
||||
"SoundCraft",
|
||||
options,
|
||||
|
||||
@@ -456,39 +456,6 @@ fn info_of(reader: &dyn FormatReader, detected: FileFormat) -> Result<(u32, Audi
|
||||
))
|
||||
}
|
||||
|
||||
/// A demux error ends the decode; it is only an error when nothing was decoded yet.
|
||||
fn demux_failure(frames: u64, e: SymError) -> Result<()> {
|
||||
if frames == 0 {
|
||||
return Err(map_err(e));
|
||||
}
|
||||
log::warn!("stopping decode after demux error: {e}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Note a corrupt packet (the `errors`-th); a stream that never decodes is malformed.
|
||||
fn corrupt_packet(errors: usize, frames: u64, msg: &str) -> Result<()> {
|
||||
log::debug!("skipping corrupt packet: {msg}");
|
||||
if errors > 1000 && frames == 0 {
|
||||
return Err(AudioError::Malformed(format!("no decodable packets: {msg}")));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The decoded stream is authoritative for the channel count: record it and allocate the planes.
|
||||
fn init_planar(info: &mut AudioInfo, ch: usize) -> Result<Vec<Vec<f32>>> {
|
||||
info.channels = u16::try_from(ch).map_err(|_| AudioError::Unsupported("too many channels".into()))?;
|
||||
Ok((0..ch).map(|_| Vec::new()).collect())
|
||||
}
|
||||
|
||||
/// Append one interleaved block to the planes, replacing non-finite samples with silence.
|
||||
fn append_interleaved(planar: &mut [Vec<f32>], interleaved: &[f32], ch: usize) {
|
||||
for frame in interleaved.chunks_exact(ch) {
|
||||
for (dst, &s) in planar.iter_mut().zip(frame) {
|
||||
dst.push(if s.is_finite() { s } else { 0.0 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Decode the whole first audio track. When `count_only`, samples are counted but not stored.
|
||||
fn run(bytes: &[u8], detected: FileFormat, count_only: bool) -> Result<(AudioInfo, AudioBuffer)> {
|
||||
let mut reader = match open(bytes, detected)? {
|
||||
@@ -524,7 +491,10 @@ fn run(bytes: &[u8], detected: FileFormat, count_only: bool) -> Result<(AudioInf
|
||||
Err(SymError::IoError(e)) if e.kind() == std::io::ErrorKind::UnexpectedEof => break,
|
||||
Err(SymError::ResetRequired) => break,
|
||||
Err(e) => {
|
||||
demux_failure(frames, e)?;
|
||||
if frames == 0 {
|
||||
return Err(map_err(e));
|
||||
}
|
||||
log::warn!("stopping decode after demux error: {e}");
|
||||
break;
|
||||
}
|
||||
};
|
||||
@@ -535,7 +505,10 @@ fn run(bytes: &[u8], detected: FileFormat, count_only: bool) -> Result<(AudioInf
|
||||
Ok(d) => d,
|
||||
Err(SymError::DecodeError(m)) => {
|
||||
errors += 1;
|
||||
corrupt_packet(errors, frames, m)?;
|
||||
log::debug!("skipping corrupt packet: {m}");
|
||||
if errors > 1000 && frames == 0 {
|
||||
return Err(AudioError::Malformed(format!("no decodable packets: {m}")));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
Err(SymError::IoError(_)) => break,
|
||||
@@ -548,7 +521,9 @@ fn run(bytes: &[u8], detected: FileFormat, count_only: bool) -> Result<(AudioInf
|
||||
continue;
|
||||
}
|
||||
if planar.is_empty() {
|
||||
planar = init_planar(&mut info, ch)?;
|
||||
// The decoded stream is authoritative for the channel count.
|
||||
info.channels = u16::try_from(ch).map_err(|_| AudioError::Unsupported("too many channels".into()))?;
|
||||
planar = (0..ch).map(|_| Vec::new()).collect();
|
||||
}
|
||||
let n = decoded.frames() as u64;
|
||||
frames = frames.saturating_add(n);
|
||||
@@ -567,7 +542,11 @@ fn run(bytes: &[u8], detected: FileFormat, count_only: bool) -> Result<(AudioInf
|
||||
interleaved.resize(needed, 0.0);
|
||||
}
|
||||
decoded.copy_to_slice_interleaved(&mut interleaved[..needed]);
|
||||
append_interleaved(&mut planar, &interleaved[..needed], ch);
|
||||
for frame in interleaved[..needed].chunks_exact(ch) {
|
||||
for (dst, &s) in planar.iter_mut().zip(frame) {
|
||||
dst.push(if s.is_finite() { s } else { 0.0 });
|
||||
}
|
||||
}
|
||||
}
|
||||
if info.channels == 0 {
|
||||
return Err(AudioError::Malformed("stream contains no audio".into()));
|
||||
|
||||
+21
-41
@@ -178,45 +178,6 @@ fn only_chunks_follow(b: &[u8], from: usize) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// A `data` chunk header as found in the file, for [`data_chunk_len`].
|
||||
struct DataChunk {
|
||||
body: usize,
|
||||
size32: u32,
|
||||
is_rf64: bool,
|
||||
remaining: usize,
|
||||
declared: usize,
|
||||
}
|
||||
|
||||
/// Bytes of audio a `data` chunk holds: leniently, whatever is present when the size is wrong or
|
||||
/// the recording was never finalised.
|
||||
fn data_chunk_len(b: &[u8], c: DataChunk) -> usize {
|
||||
let finished = u64::from(le_u32(b, 4).unwrap_or(0)) + 8 == b.len() as u64;
|
||||
let empty = c.size32 == 0 && !c.is_rf64 && finished && only_chunks_follow(b, c.body);
|
||||
if c.size32 == 0 && !c.is_rf64 && !empty { c.remaining } else { c.declared.min(c.remaining) }
|
||||
}
|
||||
|
||||
/// The sample layout of a `fmt` chunk, `None` for a format we do not read natively.
|
||||
fn pcm_layout(fmt: &Fmt) -> Result<Option<(PcmKind, SampleFormat)>> {
|
||||
Ok(Some(match (fmt.tag, fmt.bits) {
|
||||
(WAVE_FORMAT_PCM, bits @ 1..=32) => {
|
||||
let bytes = usize::from(bits.div_ceil(8));
|
||||
let sf = match fmt.valid_bits.min(bits) {
|
||||
0..=8 => SampleFormat::Int8,
|
||||
9..=16 => SampleFormat::Int16,
|
||||
17..=24 => SampleFormat::Int24,
|
||||
_ => SampleFormat::Int32,
|
||||
};
|
||||
(PcmKind::Int { bytes, big_endian: false, unsigned: bytes == 1 }, sf)
|
||||
}
|
||||
(WAVE_FORMAT_IEEE_FLOAT, 32) => (PcmKind::Float { bytes: 4, big_endian: false }, SampleFormat::Float32),
|
||||
(WAVE_FORMAT_IEEE_FLOAT, 64) => (PcmKind::Float { bytes: 8, big_endian: false }, SampleFormat::Float64),
|
||||
(WAVE_FORMAT_PCM | WAVE_FORMAT_IEEE_FLOAT, bits) => {
|
||||
return Err(AudioError::Unsupported(format!("WAV format {} with {bits} bits per sample", fmt.tag)));
|
||||
}
|
||||
_ => return Ok(None),
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) fn parse(b: &[u8]) -> Result<WavParse> {
|
||||
if !is_wav(b) {
|
||||
return Err(AudioError::Malformed("not a RIFF/RF64 WAVE file".into()));
|
||||
@@ -248,7 +209,9 @@ pub(crate) fn parse(b: &[u8]) -> Result<WavParse> {
|
||||
// A size of 0 in a file whose RIFF size is right and that has only chunks after it is an
|
||||
// empty data chunk followed by metadata (LIST, bext, …); otherwise it is a recording
|
||||
// that was never finalised, and what follows is audio.
|
||||
let len = data_chunk_len(b, DataChunk { body, size32, is_rf64, remaining, declared });
|
||||
let finished = u64::from(le_u32(b, 4).unwrap_or(0)) + 8 == b.len() as u64;
|
||||
let empty = size32 == 0 && !is_rf64 && finished && only_chunks_follow(b, body);
|
||||
let len = if size32 == 0 && !is_rf64 && !empty { remaining } else { declared.min(remaining) };
|
||||
if data.is_none() {
|
||||
data = Some((body, len));
|
||||
}
|
||||
@@ -283,7 +246,24 @@ pub(crate) fn parse(b: &[u8]) -> Result<WavParse> {
|
||||
return Err(AudioError::Malformed("WAV declares a zero sample rate".into()));
|
||||
}
|
||||
let channels = usize::from(fmt.channels);
|
||||
let Some((kind, sample_format)) = pcm_layout(&fmt)? else { return Ok(WavParse::Foreign { bwf }) };
|
||||
let (kind, sample_format) = match (fmt.tag, fmt.bits) {
|
||||
(WAVE_FORMAT_PCM, bits @ 1..=32) => {
|
||||
let bytes = usize::from(bits.div_ceil(8));
|
||||
let sf = match fmt.valid_bits.min(bits) {
|
||||
0..=8 => SampleFormat::Int8,
|
||||
9..=16 => SampleFormat::Int16,
|
||||
17..=24 => SampleFormat::Int24,
|
||||
_ => SampleFormat::Int32,
|
||||
};
|
||||
(PcmKind::Int { bytes, big_endian: false, unsigned: bytes == 1 }, sf)
|
||||
}
|
||||
(WAVE_FORMAT_IEEE_FLOAT, 32) => (PcmKind::Float { bytes: 4, big_endian: false }, SampleFormat::Float32),
|
||||
(WAVE_FORMAT_IEEE_FLOAT, 64) => (PcmKind::Float { bytes: 8, big_endian: false }, SampleFormat::Float64),
|
||||
(WAVE_FORMAT_PCM | WAVE_FORMAT_IEEE_FLOAT, bits) => {
|
||||
return Err(AudioError::Unsupported(format!("WAV format {} with {bits} bits per sample", fmt.tag)));
|
||||
}
|
||||
_ => return Ok(WavParse::Foreign { bwf }),
|
||||
};
|
||||
let block = kind.bytes().checked_mul(channels).ok_or_else(|| AudioError::Malformed("block size overflow".into()))?;
|
||||
if usize::from(fmt.block_align) != block {
|
||||
log::debug!("WAV block_align {} disagrees with computed {block}; using computed", fmt.block_align);
|
||||
|
||||
@@ -102,35 +102,6 @@ fn note_mode(p: Option<NotePort>) -> NoteMode {
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy `n` frames from `start` of the host buffers into the plugin's input `port` (mono sources
|
||||
/// fill every port channel).
|
||||
fn feed_input(inputs: &mut PortBuffers, port: usize, io: &[Vec<f32>], ch: usize, (start, n): (usize, usize)) {
|
||||
for c in 0..inputs.channels(port) {
|
||||
let src = io.get(c.min(ch - 1)).and_then(|s| s.get(start..start + n));
|
||||
if let (Some(src), Some(dst)) = (src, inputs.channel_mut(port, c).and_then(|d| d.get_mut(..n))) {
|
||||
dst.copy_from_slice(src);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy `n` frames of the plugin's output `port` into the host buffers from `start`; a mono host
|
||||
/// buffer takes the average of a stereo port.
|
||||
fn read_output(outputs: &PortBuffers, port: usize, io: &mut [Vec<f32>], ch: usize, (start, n): (usize, usize)) {
|
||||
let oc = outputs.channels(port);
|
||||
for (c, dst) in io.iter_mut().take(ch).enumerate() {
|
||||
let Some(dst) = dst.get_mut(start..start + n) else { continue };
|
||||
if ch == 1 && oc >= 2 {
|
||||
if let (Some(l), Some(r)) = (outputs.channel(port, 0), outputs.channel(port, 1)) {
|
||||
for ((d, a), b) in dst.iter_mut().zip(l).zip(r) {
|
||||
*d = 0.5 * (a + b);
|
||||
}
|
||||
}
|
||||
} else if let Some(src) = outputs.channel(port, c.min(oc.saturating_sub(1))).and_then(|s| s.get(..n)) {
|
||||
dst.copy_from_slice(src);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Plugin for ClapPlugin {
|
||||
fn info(&self) -> &'static PluginInfo {
|
||||
self.info
|
||||
@@ -226,7 +197,12 @@ impl Plugin for ClapPlugin {
|
||||
let end = u32::try_from(start + n).unwrap_or(u32::MAX);
|
||||
// Feed the main input port (mono sources fill every port channel).
|
||||
if let Some(p) = self.in_main {
|
||||
feed_input(&mut self.inputs, p, io, ch, (start, n));
|
||||
for c in 0..self.inputs.channels(p) {
|
||||
let src = io.get(c.min(ch - 1)).and_then(|s| s.get(start..start + n));
|
||||
if let (Some(src), Some(dst)) = (src, self.inputs.channel_mut(p, c).and_then(|d| d.get_mut(..n))) {
|
||||
dst.copy_from_slice(src);
|
||||
}
|
||||
}
|
||||
}
|
||||
let k = self.queue.iter().position(|e| e.time() >= end).unwrap_or(self.queue.len());
|
||||
let offset = u32::try_from(start).unwrap_or(u32::MAX);
|
||||
@@ -236,7 +212,19 @@ impl Plugin for ClapPlugin {
|
||||
self.steady = self.steady.saturating_add(i64::try_from(n).unwrap_or(0));
|
||||
match (r, self.out_main) {
|
||||
(Ok(_), Some(p)) => {
|
||||
read_output(&self.outputs, p, io, ch, (start, n));
|
||||
let oc = self.outputs.channels(p);
|
||||
for (c, dst) in io.iter_mut().take(ch).enumerate() {
|
||||
let Some(dst) = dst.get_mut(start..start + n) else { continue };
|
||||
if ch == 1 && oc >= 2 {
|
||||
if let (Some(l), Some(r)) = (self.outputs.channel(p, 0), self.outputs.channel(p, 1)) {
|
||||
for ((d, a), b) in dst.iter_mut().zip(l).zip(r) {
|
||||
*d = 0.5 * (a + b);
|
||||
}
|
||||
}
|
||||
} else if let Some(src) = self.outputs.channel(p, c.min(oc.saturating_sub(1))).and_then(|s| s.get(..n)) {
|
||||
dst.copy_from_slice(src);
|
||||
}
|
||||
}
|
||||
}
|
||||
(Err(e), _) => log::debug!("{}: {e}", self.info.id),
|
||||
_ => {}
|
||||
|
||||
@@ -15,11 +15,6 @@ fn block(p: &mut Box<dyn Plugin>) -> Vec<Vec<f32>> {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// nextest lists custom-harness tests with `--list`; each binary here is one test.
|
||||
if std::env::args().any(|a| a == "--list") {
|
||||
println!("editor_main_thread: test");
|
||||
return;
|
||||
}
|
||||
common::fixture_dir();
|
||||
let mut p = soundcraft_clap_host::create(GAIN).expect("fixture");
|
||||
p.prepare(48_000.0, 64, 2);
|
||||
|
||||
@@ -189,61 +189,6 @@ fn apply(f: &mut Fx, h: &clap_event_header) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Fill every output channel with 1.0 when all ports have the wide layout, else -1.0.
|
||||
unsafe fn fill_wide(pr: &clap_process, ports: u32, channels: u32, n: usize) {
|
||||
let fits = |bufs: *const clap_audio_buffer, n: u32| {
|
||||
n == ports && (0..n as usize).all(|i| unsafe { &*bufs.add(i) }.channel_count == channels)
|
||||
};
|
||||
let ok = fits(pr.audio_inputs, pr.audio_inputs_count) && fits(pr.audio_outputs, pr.audio_outputs_count);
|
||||
if pr.audio_outputs_count > 0 {
|
||||
let out = unsafe { &*pr.audio_outputs };
|
||||
for c in 0..out.channel_count as usize {
|
||||
let o = unsafe { *out.data32.add(c) };
|
||||
for i in 0..n {
|
||||
unsafe { *o.add(i) = if ok { 1.0 } else { -1.0 } };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Push the host-visible parameter edit and an ignorable note event onto the output queue.
|
||||
unsafe fn push_gui_edit(pr: &clap_process) {
|
||||
let e = clap_event_param_value {
|
||||
header: clap_event_header {
|
||||
size: std::mem::size_of::<clap_event_param_value>() as u32,
|
||||
time: 0,
|
||||
space_id: CLAP_CORE_EVENT_SPACE_ID,
|
||||
type_: CLAP_EVENT_PARAM_VALUE,
|
||||
flags: 0,
|
||||
},
|
||||
param_id: 7,
|
||||
cookie: std::ptr::null_mut(),
|
||||
note_id: -1,
|
||||
port_index: -1,
|
||||
channel: -1,
|
||||
key: -1,
|
||||
value: 0.25,
|
||||
};
|
||||
let out = unsafe { &*pr.out_events };
|
||||
assert!(unsafe { (out.try_push.unwrap())(pr.out_events, &e.header) });
|
||||
// Other event types are accepted and ignored by the host.
|
||||
let note = clap_event_note {
|
||||
header: clap_event_header {
|
||||
size: std::mem::size_of::<clap_event_note>() as u32,
|
||||
time: 0,
|
||||
space_id: CLAP_CORE_EVENT_SPACE_ID,
|
||||
type_: CLAP_EVENT_NOTE_END,
|
||||
flags: 0,
|
||||
},
|
||||
note_id: -1,
|
||||
port_index: 0,
|
||||
channel: 0,
|
||||
key: 60,
|
||||
velocity: 0.0,
|
||||
};
|
||||
assert!(unsafe { (out.try_push.unwrap())(pr.out_events, ¬e.header) });
|
||||
}
|
||||
|
||||
unsafe extern "C" fn process(p: *const clap_plugin, pr: *const clap_process) -> clap_process_status {
|
||||
let f = unsafe { fx(p) };
|
||||
let pr = unsafe { &*pr };
|
||||
@@ -252,7 +197,19 @@ unsafe extern "C" fn process(p: *const clap_plugin, pr: *const clap_process) ->
|
||||
let ev = unsafe { &*pr.in_events };
|
||||
let count = unsafe { (ev.size.unwrap())(pr.in_events) };
|
||||
if let Some((ports, channels)) = f.wide {
|
||||
unsafe { fill_wide(pr, ports, channels, n) };
|
||||
let fits = |bufs: *const clap_audio_buffer, n: u32| {
|
||||
n == ports && (0..n as usize).all(|i| unsafe { &*bufs.add(i) }.channel_count == channels)
|
||||
};
|
||||
let ok = fits(pr.audio_inputs, pr.audio_inputs_count) && fits(pr.audio_outputs, pr.audio_outputs_count);
|
||||
if pr.audio_outputs_count > 0 {
|
||||
let out = unsafe { &*pr.audio_outputs };
|
||||
for c in 0..out.channel_count as usize {
|
||||
let o = unsafe { *out.data32.add(c) };
|
||||
for i in 0..n {
|
||||
unsafe { *o.add(i) = if ok { 1.0 } else { -1.0 } };
|
||||
}
|
||||
}
|
||||
}
|
||||
return CLAP_PROCESS_CONTINUE;
|
||||
}
|
||||
if pr.audio_outputs_count != 1 {
|
||||
@@ -273,7 +230,40 @@ unsafe extern "C" fn process(p: *const clap_plugin, pr: *const clap_process) ->
|
||||
if f.gui_edit_pending {
|
||||
f.gui_edit_pending = false;
|
||||
f.gain = 0.25;
|
||||
unsafe { push_gui_edit(pr) };
|
||||
let e = clap_event_param_value {
|
||||
header: clap_event_header {
|
||||
size: std::mem::size_of::<clap_event_param_value>() as u32,
|
||||
time: 0,
|
||||
space_id: CLAP_CORE_EVENT_SPACE_ID,
|
||||
type_: CLAP_EVENT_PARAM_VALUE,
|
||||
flags: 0,
|
||||
},
|
||||
param_id: 7,
|
||||
cookie: std::ptr::null_mut(),
|
||||
note_id: -1,
|
||||
port_index: -1,
|
||||
channel: -1,
|
||||
key: -1,
|
||||
value: 0.25,
|
||||
};
|
||||
let out = unsafe { &*pr.out_events };
|
||||
assert!(unsafe { (out.try_push.unwrap())(pr.out_events, &e.header) });
|
||||
// Other event types are accepted and ignored by the host.
|
||||
let note = clap_event_note {
|
||||
header: clap_event_header {
|
||||
size: std::mem::size_of::<clap_event_note>() as u32,
|
||||
time: 0,
|
||||
space_id: CLAP_CORE_EVENT_SPACE_ID,
|
||||
type_: CLAP_EVENT_NOTE_END,
|
||||
flags: 0,
|
||||
},
|
||||
note_id: -1,
|
||||
port_index: 0,
|
||||
channel: 0,
|
||||
key: 60,
|
||||
velocity: 0.0,
|
||||
};
|
||||
assert!(unsafe { (out.try_push.unwrap())(pr.out_events, ¬e.header) });
|
||||
}
|
||||
let mut next = 0u32;
|
||||
let mut last_time = 0u32;
|
||||
|
||||
+63
-74
@@ -322,77 +322,6 @@ pub fn resample(ch: &[Vec<f32>], from: u32, to: u32) -> Vec<Vec<f32>> {
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Time stretch (WSOLA) and pitch shift
|
||||
|
||||
/// Start position in the guide signal whose `hop` samples best match the natural continuation at
|
||||
/// `natural`, searched within `tol` of `nominal` (coarse stride 4, then refined +-3).
|
||||
fn best_start(g: &impl Fn(i64) -> f32, nominal: i64, natural: i64, tol: i64, hop: usize) -> i64 {
|
||||
// Similarity of candidate start `p` with the natural continuation (stride 2).
|
||||
let score = |p: i64| -> f32 {
|
||||
let (mut xy, mut yy) = (0.0f32, 1e-9f32);
|
||||
let mut i = 0i64;
|
||||
while i < hop as i64 {
|
||||
let a = g(natural + i);
|
||||
let b = g(p + i);
|
||||
xy += a * b;
|
||||
yy += b * b;
|
||||
i += 2;
|
||||
}
|
||||
xy / yy.sqrt()
|
||||
};
|
||||
let lo = (nominal - tol).max(0);
|
||||
let hi = nominal + tol;
|
||||
let mut best = nominal.max(0);
|
||||
let mut best_s = f32::NEG_INFINITY;
|
||||
let mut p = lo;
|
||||
while p <= hi {
|
||||
let s = score(p);
|
||||
if s > best_s {
|
||||
best_s = s;
|
||||
best = p;
|
||||
}
|
||||
p += 4;
|
||||
}
|
||||
let (rlo, rhi) = ((best - 3).max(lo), (best + 3).min(hi));
|
||||
for p in rlo..=rhi {
|
||||
let s = score(p);
|
||||
if s > best_s {
|
||||
best_s = s;
|
||||
best = p;
|
||||
}
|
||||
}
|
||||
best
|
||||
}
|
||||
|
||||
/// Add one windowed frame of every channel, read from `at.0`, into `out` at `at.1`, and the window
|
||||
/// into `wsum`.
|
||||
fn overlap_add(ch: &[Vec<f32>], out: &mut [Vec<f32>], wsum: &mut [f32], window: &[f32], at: (usize, usize)) {
|
||||
let (in_pos, out_pos) = at;
|
||||
for (c, o) in ch.iter().zip(out.iter_mut()) {
|
||||
for (i, w) in window.iter().enumerate() {
|
||||
let v = c.get(in_pos + i).copied().unwrap_or(0.0);
|
||||
if let Some(d) = o.get_mut(out_pos + i) {
|
||||
*d += v * w;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i, w) in window.iter().enumerate() {
|
||||
if let Some(d) = wsum.get_mut(out_pos + i) {
|
||||
*d += w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Divide out the accumulated window weights and cut each channel to `out_len`.
|
||||
fn normalise(out: &mut [Vec<f32>], wsum: &[f32], out_len: usize) {
|
||||
for o in out.iter_mut() {
|
||||
for (s, w) in o.iter_mut().zip(wsum) {
|
||||
if *w > 1e-3 {
|
||||
*s /= w;
|
||||
}
|
||||
}
|
||||
o.truncate(out_len);
|
||||
}
|
||||
}
|
||||
|
||||
/// Pitch-preserving time stretch by WSOLA (waveform-similarity overlap-add).
|
||||
/// `ratio` = output length / input length, clamped to 0.1 ..= 10. All channels share the same
|
||||
/// segment choices so the stereo image stays coherent.
|
||||
@@ -425,12 +354,72 @@ pub fn time_stretch(ch: &[Vec<f32>], ratio: f64, sample_rate: f32) -> Vec<Vec<f3
|
||||
if out_pos >= out_len {
|
||||
break;
|
||||
}
|
||||
let in_pos = if k == 0 { 0 } else { best_start(&g, (out_pos as f64 / ratio).round() as i64, prev_in + hop as i64, tol, hop) };
|
||||
overlap_add(ch, &mut out, &mut wsum, &window, (in_pos as usize, out_pos));
|
||||
let in_pos = if k == 0 {
|
||||
0
|
||||
} else {
|
||||
let nominal = (out_pos as f64 / ratio).round() as i64;
|
||||
let natural = prev_in + hop as i64;
|
||||
// Similarity of candidate start `p` with the natural continuation (stride 2).
|
||||
let score = |p: i64| -> f32 {
|
||||
let (mut xy, mut yy) = (0.0f32, 1e-9f32);
|
||||
let mut i = 0i64;
|
||||
while i < hop as i64 {
|
||||
let a = g(natural + i);
|
||||
let b = g(p + i);
|
||||
xy += a * b;
|
||||
yy += b * b;
|
||||
i += 2;
|
||||
}
|
||||
xy / yy.sqrt()
|
||||
};
|
||||
let lo = (nominal - tol).max(0);
|
||||
let hi = nominal + tol;
|
||||
let mut best = nominal.max(0);
|
||||
let mut best_s = f32::NEG_INFINITY;
|
||||
let mut p = lo;
|
||||
while p <= hi {
|
||||
let s = score(p);
|
||||
if s > best_s {
|
||||
best_s = s;
|
||||
best = p;
|
||||
}
|
||||
p += 4;
|
||||
}
|
||||
let (rlo, rhi) = ((best - 3).max(lo), (best + 3).min(hi));
|
||||
for p in rlo..=rhi {
|
||||
let s = score(p);
|
||||
if s > best_s {
|
||||
best_s = s;
|
||||
best = p;
|
||||
}
|
||||
}
|
||||
best
|
||||
};
|
||||
for (c, o) in ch.iter().zip(out.iter_mut()) {
|
||||
for (i, w) in window.iter().enumerate() {
|
||||
let src = in_pos as usize + i;
|
||||
let v = c.get(src).copied().unwrap_or(0.0);
|
||||
if let Some(d) = o.get_mut(out_pos + i) {
|
||||
*d += v * w;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i, w) in window.iter().enumerate() {
|
||||
if let Some(d) = wsum.get_mut(out_pos + i) {
|
||||
*d += w;
|
||||
}
|
||||
}
|
||||
prev_in = in_pos;
|
||||
k += 1;
|
||||
}
|
||||
normalise(&mut out, &wsum, out_len);
|
||||
for o in out.iter_mut() {
|
||||
for (s, w) in o.iter_mut().zip(&wsum) {
|
||||
if *w > 1e-3 {
|
||||
*s /= w;
|
||||
}
|
||||
}
|
||||
o.truncate(out_len);
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
|
||||
@@ -94,29 +94,6 @@ fn duration_bars(duration: usize) -> Option<i64> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes one frame: a constant-power pan for stereo and wider, plain gain for mono. Channels past
|
||||
/// the second only get the output gain.
|
||||
fn write_frame(io: &mut [Vec<f32>], n: usize, ch: usize, left: f32, right: f32, position: f32, out: f32) {
|
||||
if ch >= 2 {
|
||||
// Constant power: sin² + cos² = 1 across the field, and both gains are 1 at the center.
|
||||
let theta = (position.clamp(-1.0, 1.0) + 1.0) * FRAC_PI_4;
|
||||
let (l, r) = (theta.cos().max(0.0), theta.sin().max(0.0));
|
||||
if let Some(x) = io.get_mut(0).and_then(|b| b.get_mut(n)) {
|
||||
*x = left * l * SQRT_2 * out;
|
||||
}
|
||||
if let Some(x) = io.get_mut(1).and_then(|b| b.get_mut(n)) {
|
||||
*x = right * r * SQRT_2 * out;
|
||||
}
|
||||
for c in 2..ch {
|
||||
if let Some(x) = io.get_mut(c).and_then(|b| b.get_mut(n)) {
|
||||
*x = if x.is_finite() { *x * out } else { 0.0 };
|
||||
}
|
||||
}
|
||||
} else if let Some(x) = io.get_mut(0).and_then(|b| b.get_mut(n)) {
|
||||
*x = left * out;
|
||||
}
|
||||
}
|
||||
|
||||
/// Moves the balance of the first two channels with a constant-power law: as the pan position
|
||||
/// moves right the left channel's gain falls and the right's rises, and each channel stays on its
|
||||
/// own side. The pan position is `manual · (1 - width) + modulator · width`, where the modulator is
|
||||
@@ -253,63 +230,6 @@ impl AutoPan {
|
||||
let ratio = hz * dur * 60.0 / t.tempo;
|
||||
Some((into * ratio).rem_euclid(1.0))
|
||||
}
|
||||
|
||||
/// Advances the envelope follower by one sample. `level` is the louder channel's magnitude.
|
||||
fn follow_envelope(&mut self, level: f32, attack: f32, release: f32) {
|
||||
let k = if level > self.env { attack } else { release };
|
||||
self.env = level + (self.env - level) * k;
|
||||
if self.env < 1.0e-12 {
|
||||
self.env = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Arms the Envelope trigger on a rising threshold crossing and re-arms it after a Release of silence.
|
||||
fn envelope_trigger(&mut self, threshold: f32, release_samples: u64) {
|
||||
if self.armed && self.env >= threshold {
|
||||
self.armed = false;
|
||||
self.phase = 0.0;
|
||||
self.holding = false;
|
||||
self.below = 0;
|
||||
} else if !self.armed {
|
||||
if self.env < threshold {
|
||||
self.below = self.below.saturating_add(1);
|
||||
self.armed = self.below >= release_samples;
|
||||
} else {
|
||||
self.below = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The LFO phase for sample `n` of the block. Advances the phase unless a held or Beat Clock position sets it.
|
||||
fn lfo_phase(&mut self, n: usize, trigger: usize, hz: f64, inc: f64) -> f64 {
|
||||
match (trigger == TRIGGER_BEAT_CLOCK).then(|| self.beat_clock_phase(n, hz)).flatten() {
|
||||
Some(phase) => phase,
|
||||
None if self.holding => self.phase,
|
||||
None => {
|
||||
let current = self.phase;
|
||||
self.phase += inc;
|
||||
if self.phase >= 1.0 {
|
||||
if matches!(trigger, TRIGGER_FREE | TRIGGER_BEAT_CLOCK) {
|
||||
self.phase -= 1.0;
|
||||
} else {
|
||||
self.phase = 1.0;
|
||||
self.holding = true;
|
||||
}
|
||||
}
|
||||
current
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The modulator in -1..=1: the envelope level above the threshold, or the LFO shape.
|
||||
fn modulator(&self, envelope_source: bool, threshold_db: f32, waveform: usize, lfo_phase: f64) -> f32 {
|
||||
if envelope_source {
|
||||
let above = (gain_to_db(self.env) - threshold_db) / -threshold_db;
|
||||
2.0 * above.clamp(0.0, 1.0) - 1.0
|
||||
} else {
|
||||
wave_at(waveform, lfo_phase as f32)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Plugin for AutoPan {
|
||||
@@ -363,12 +283,50 @@ impl Plugin for AutoPan {
|
||||
let left = sample(io, 0);
|
||||
let right = if ch >= 2 { sample(io, 1) } else { left };
|
||||
// The louder channel drives the follower, so anti-phase material still has a level.
|
||||
self.follow_envelope(left.abs().max(right.abs()), attack, release);
|
||||
if trigger == TRIGGER_ENVELOPE {
|
||||
self.envelope_trigger(threshold, release_samples);
|
||||
let level = left.abs().max(right.abs());
|
||||
let k = if level > self.env { attack } else { release };
|
||||
self.env = level + (self.env - level) * k;
|
||||
if self.env < 1.0e-12 {
|
||||
self.env = 0.0;
|
||||
}
|
||||
let lfo_phase = self.lfo_phase(n, trigger, hz, inc);
|
||||
let modulator = self.modulator(envelope_source, threshold_db, waveform, lfo_phase);
|
||||
if trigger == TRIGGER_ENVELOPE {
|
||||
if self.armed && self.env >= threshold {
|
||||
self.armed = false;
|
||||
self.phase = 0.0;
|
||||
self.holding = false;
|
||||
self.below = 0;
|
||||
} else if !self.armed {
|
||||
if self.env < threshold {
|
||||
self.below = self.below.saturating_add(1);
|
||||
self.armed = self.below >= release_samples;
|
||||
} else {
|
||||
self.below = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
let lfo_phase = match (trigger == TRIGGER_BEAT_CLOCK).then(|| self.beat_clock_phase(n, hz)).flatten() {
|
||||
Some(phase) => phase,
|
||||
None if self.holding => self.phase,
|
||||
None => {
|
||||
let current = self.phase;
|
||||
self.phase += inc;
|
||||
if self.phase >= 1.0 {
|
||||
if matches!(trigger, TRIGGER_FREE | TRIGGER_BEAT_CLOCK) {
|
||||
self.phase -= 1.0;
|
||||
} else {
|
||||
self.phase = 1.0;
|
||||
self.holding = true;
|
||||
}
|
||||
}
|
||||
current
|
||||
}
|
||||
};
|
||||
let modulator = if envelope_source {
|
||||
let above = (gain_to_db(self.env) - threshold_db) / -threshold_db;
|
||||
2.0 * above.clamp(0.0, 1.0) - 1.0
|
||||
} else {
|
||||
wave_at(waveform, lfo_phase as f32)
|
||||
};
|
||||
let width = self.width.next();
|
||||
let manual = self.manual.next();
|
||||
self.position.set(manual * (1.0 - width) + sign * modulator * width);
|
||||
@@ -378,7 +336,24 @@ impl Plugin for AutoPan {
|
||||
}
|
||||
let position = self.position.next();
|
||||
let out = self.output.next();
|
||||
write_frame(io, n, ch, left, right, position, out);
|
||||
if ch >= 2 {
|
||||
// Constant power: sin² + cos² = 1 across the field, and both gains are 1 at the center.
|
||||
let theta = (position.clamp(-1.0, 1.0) + 1.0) * FRAC_PI_4;
|
||||
let (l, r) = (theta.cos().max(0.0), theta.sin().max(0.0));
|
||||
if let Some(x) = io.get_mut(0).and_then(|b| b.get_mut(n)) {
|
||||
*x = left * l * SQRT_2 * out;
|
||||
}
|
||||
if let Some(x) = io.get_mut(1).and_then(|b| b.get_mut(n)) {
|
||||
*x = right * r * SQRT_2 * out;
|
||||
}
|
||||
for c in 2..ch {
|
||||
if let Some(x) = io.get_mut(c).and_then(|b| b.get_mut(n)) {
|
||||
*x = if x.is_finite() { *x * out } else { 0.0 };
|
||||
}
|
||||
}
|
||||
} else if let Some(x) = io.get_mut(0).and_then(|b| b.get_mut(n)) {
|
||||
*x = left * out;
|
||||
}
|
||||
}
|
||||
self.since_transport = self.since_transport.saturating_add(frames as u64);
|
||||
}
|
||||
|
||||
@@ -135,65 +135,6 @@ impl Default for DrumSynth {
|
||||
}
|
||||
}
|
||||
|
||||
/// One output sample of a sounding voice of `kind`, advancing its envelopes.
|
||||
fn voice_sample(v: &mut DrumVoice, kind: Kind, sr: f32, clap_gap: usize) -> f32 {
|
||||
match kind {
|
||||
Kind::Kick | Kind::Tom(_) => {
|
||||
v.pitch_env *= v.pitch_coef;
|
||||
let f = v.f_end + (v.f_start - v.f_end) * v.pitch_env;
|
||||
v.phase += f / sr;
|
||||
v.phase -= v.phase.floor();
|
||||
let tone = (v.phase * TAU).sin() * v.amp;
|
||||
let click = v.filt.process_sample(v.rng.bipolar()) * v.noise_amp;
|
||||
v.amp *= v.amp_coef;
|
||||
v.noise_amp *= v.noise_coef;
|
||||
tone + click
|
||||
}
|
||||
Kind::Snare => {
|
||||
v.pitch_env *= v.pitch_coef;
|
||||
let f = v.f_end + (v.f_start - v.f_end) * v.pitch_env;
|
||||
v.phase += f / sr;
|
||||
v.phase -= v.phase.floor();
|
||||
let tone = (v.phase * TAU).sin() * v.amp * 0.6;
|
||||
let n = v.filt2.process_sample(v.filt.process_sample(v.rng.bipolar())) * v.noise_amp * 0.7;
|
||||
v.amp *= v.amp_coef;
|
||||
v.noise_amp *= v.noise_coef;
|
||||
tone + n
|
||||
}
|
||||
Kind::Clap => {
|
||||
// Three quick bursts then a diffuse tail.
|
||||
let burst = if v.t < clap_gap * 3 {
|
||||
if v.t.is_multiple_of(clap_gap) {
|
||||
v.amp = 1.0;
|
||||
}
|
||||
v.amp
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let env = burst.max(if v.t >= clap_gap * 2 { v.noise_amp * 0.6 } else { 0.0 });
|
||||
let y = v.filt.process_sample(v.rng.bipolar()) * env * 2.0;
|
||||
v.amp *= v.amp_coef;
|
||||
if v.t >= clap_gap * 2 {
|
||||
v.noise_amp *= v.noise_coef;
|
||||
}
|
||||
y
|
||||
}
|
||||
Kind::ClosedHat | Kind::OpenHat | Kind::Crash | Kind::Ride => {
|
||||
let scale = if matches!(kind, Kind::Crash | Kind::Ride) { 1.0 } else { 1.9 };
|
||||
let mut metal = 0.0;
|
||||
for (m, f) in v.metal.iter_mut().zip(METAL) {
|
||||
*m += f * scale / sr;
|
||||
*m -= m.floor();
|
||||
metal += if *m < 0.5 { 1.0 } else { -1.0 };
|
||||
}
|
||||
let raw = metal / 6.0 * (1.0 - v.noise_amp * 0.5) + v.rng.bipolar() * v.noise_amp;
|
||||
let y = v.filt2.process_sample(v.filt.process_sample(raw)) * v.amp;
|
||||
v.amp *= v.amp_coef;
|
||||
y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DrumSynth {
|
||||
pub fn new() -> Self {
|
||||
let mut voices = [DrumVoice::new(1); VOICES];
|
||||
@@ -305,7 +246,61 @@ impl DrumSynth {
|
||||
let mut out = 0.0;
|
||||
for v in &mut self.voices {
|
||||
let Some(kind) = v.kind else { continue };
|
||||
let y = voice_sample(v, kind, sr, clap_gap);
|
||||
let y = match kind {
|
||||
Kind::Kick | Kind::Tom(_) => {
|
||||
v.pitch_env *= v.pitch_coef;
|
||||
let f = v.f_end + (v.f_start - v.f_end) * v.pitch_env;
|
||||
v.phase += f / sr;
|
||||
v.phase -= v.phase.floor();
|
||||
let tone = (v.phase * TAU).sin() * v.amp;
|
||||
let click = v.filt.process_sample(v.rng.bipolar()) * v.noise_amp;
|
||||
v.amp *= v.amp_coef;
|
||||
v.noise_amp *= v.noise_coef;
|
||||
tone + click
|
||||
}
|
||||
Kind::Snare => {
|
||||
v.pitch_env *= v.pitch_coef;
|
||||
let f = v.f_end + (v.f_start - v.f_end) * v.pitch_env;
|
||||
v.phase += f / sr;
|
||||
v.phase -= v.phase.floor();
|
||||
let tone = (v.phase * TAU).sin() * v.amp * 0.6;
|
||||
let n = v.filt2.process_sample(v.filt.process_sample(v.rng.bipolar())) * v.noise_amp * 0.7;
|
||||
v.amp *= v.amp_coef;
|
||||
v.noise_amp *= v.noise_coef;
|
||||
tone + n
|
||||
}
|
||||
Kind::Clap => {
|
||||
// Three quick bursts then a diffuse tail.
|
||||
let burst = if v.t < clap_gap * 3 {
|
||||
if v.t % clap_gap == 0 {
|
||||
v.amp = 1.0;
|
||||
}
|
||||
v.amp
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let env = burst.max(if v.t >= clap_gap * 2 { v.noise_amp * 0.6 } else { 0.0 });
|
||||
let y = v.filt.process_sample(v.rng.bipolar()) * env * 2.0;
|
||||
v.amp *= v.amp_coef;
|
||||
if v.t >= clap_gap * 2 {
|
||||
v.noise_amp *= v.noise_coef;
|
||||
}
|
||||
y
|
||||
}
|
||||
Kind::ClosedHat | Kind::OpenHat | Kind::Crash | Kind::Ride => {
|
||||
let scale = if matches!(kind, Kind::Crash | Kind::Ride) { 1.0 } else { 1.9 };
|
||||
let mut metal = 0.0;
|
||||
for (m, f) in v.metal.iter_mut().zip(METAL) {
|
||||
*m += f * scale / sr;
|
||||
*m -= m.floor();
|
||||
metal += if *m < 0.5 { 1.0 } else { -1.0 };
|
||||
}
|
||||
let raw = metal / 6.0 * (1.0 - v.noise_amp * 0.5) + v.rng.bipolar() * v.noise_amp;
|
||||
let y = v.filt2.process_sample(v.filt.process_sample(raw)) * v.amp;
|
||||
v.amp *= v.amp_coef;
|
||||
y
|
||||
}
|
||||
};
|
||||
v.t += 1;
|
||||
let alive = match kind {
|
||||
Kind::Clap => v.t < clap_gap * 3 || v.noise_amp > OFF,
|
||||
|
||||
@@ -201,31 +201,6 @@ impl Reverb {
|
||||
}
|
||||
}
|
||||
|
||||
/// Input sample `n` of the left and right channels (a mono input feeds both); non-finite reads as 0.
|
||||
fn input_pair(io: &[Vec<f32>], ch: usize, n: usize) -> (f32, f32) {
|
||||
let xl = io.first().and_then(|b| b.get(n)).copied().filter(|v| v.is_finite()).unwrap_or(0.0);
|
||||
let xr = if ch > 1 { io.get(1).and_then(|b| b.get(n)).copied().filter(|v| v.is_finite()).unwrap_or(0.0) } else { xl };
|
||||
(xl, xr)
|
||||
}
|
||||
|
||||
/// Blend the wet signal `[mid, left, right]` into sample `n` of the first `ch` channels at mix `m`.
|
||||
fn mix_out(io: &mut [Vec<f32>], ch: usize, n: usize, wet: [f32; 3], m: f32) {
|
||||
let [mid, wl, wr] = wet;
|
||||
for (c, b) in io.iter_mut().take(ch).enumerate() {
|
||||
let wet = if ch == 1 {
|
||||
mid
|
||||
} else if c % 2 == 0 {
|
||||
wl
|
||||
} else {
|
||||
wr
|
||||
};
|
||||
if let Some(x) = b.get_mut(n) {
|
||||
let dry = if x.is_finite() { *x } else { 0.0 };
|
||||
*x = dry * (1.0 - m) + wet * m;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Plugin for Reverb {
|
||||
fn info(&self) -> &'static PluginInfo {
|
||||
self.info
|
||||
@@ -304,7 +279,8 @@ impl Plugin for Reverb {
|
||||
let size = self.size.value();
|
||||
let base: [f32; LINES] = self.cfg.delays_ms.map(|ms| ms_to_samples(ms * size, sr));
|
||||
for n in 0..frames {
|
||||
let (xl, xr) = input_pair(io, ch, n);
|
||||
let xl = io.first().and_then(|b| b.get(n)).copied().filter(|v| v.is_finite()).unwrap_or(0.0);
|
||||
let xr = if ch > 1 { io.get(1).and_then(|b| b.get(n)).copied().filter(|v| v.is_finite()).unwrap_or(0.0) } else { xl };
|
||||
let pre = ms_to_samples(self.pre_ms.next(), sr);
|
||||
let mut ins = [xl, xr];
|
||||
for (side, x) in ins.iter_mut().enumerate() {
|
||||
@@ -343,7 +319,19 @@ impl Plugin for Reverb {
|
||||
let side = 0.5 * (wl - wr) * w;
|
||||
let (wl, wr) = (mid + side, mid - side);
|
||||
let m = self.mix.next();
|
||||
mix_out(io, ch, n, [mid, wl, wr], m);
|
||||
for (c, b) in io.iter_mut().take(ch).enumerate() {
|
||||
let wet = if ch == 1 {
|
||||
mid
|
||||
} else if c % 2 == 0 {
|
||||
wl
|
||||
} else {
|
||||
wr
|
||||
};
|
||||
if let Some(x) = b.get_mut(n) {
|
||||
let dry = if x.is_finite() { *x } else { 0.0 };
|
||||
*x = dry * (1.0 - m) + wet * m;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,18 +20,6 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
)]
|
||||
}
|
||||
|
||||
/// The most frames a process can take in: these processes cut their output at MAX_OFFLINE_SAMPLES frames.
|
||||
fn frame_limit(name: &str, ratio: f64, semitones: f32, sr: f64, to: u32, selection: SpeedRampSelection) -> usize {
|
||||
let longest = |grows: f64| (soundcraft_dsp::offline::MAX_OFFLINE_SAMPLES as f64 / grows) as usize;
|
||||
match name {
|
||||
"time_stretch" => longest(ratio),
|
||||
"pitch_shift" => longest(2f64.powf(f64::from(semitones) / 12.0)),
|
||||
"varispeed" => longest(f64::from(to) / sr),
|
||||
"speed_ramp" => longest(if selection == SpeedRampSelection::Extend { 1.5 } else { 1.0 }),
|
||||
_ => usize::MAX,
|
||||
}
|
||||
}
|
||||
|
||||
/// Process each target clip's audio and replace the clip with one backed by a new source.
|
||||
fn process(e: &mut Engine, p: &Value) -> Result<Value> {
|
||||
let name = str_param(p, "process").or_else(|| str_param(p, "plugin")).ok_or_else(|| bad("audiosuite.process", "`process` required"))?.to_string();
|
||||
@@ -75,7 +63,15 @@ fn process(e: &mut Engine, p: &Value) -> Result<Value> {
|
||||
let change = if get("change", 0.0) >= 0.5 { SpeedRampChange::SpeedUp } else { SpeedRampChange::SlowDown };
|
||||
let selection = if get("selection", 0.0) >= 0.5 { SpeedRampSelection::Extend } else { SpeedRampSelection::FitTo };
|
||||
let fades = get("fades", 1.0) >= 0.5;
|
||||
let most = frame_limit(&name, ratio, semitones, sr.as_f64(), to, selection);
|
||||
// These processes cut their output at MAX_OFFLINE_SAMPLES frames.
|
||||
let longest = |grows: f64| (soundcraft_dsp::offline::MAX_OFFLINE_SAMPLES as f64 / grows) as usize;
|
||||
let most = match name.as_str() {
|
||||
"time_stretch" => longest(ratio),
|
||||
"pitch_shift" => longest(2f64.powf(f64::from(semitones) / 12.0)),
|
||||
"varispeed" => longest(f64::from(to) / sr.as_f64()),
|
||||
"speed_ramp" => longest(if selection == SpeedRampSelection::Extend { 1.5 } else { 1.0 }),
|
||||
_ => usize::MAX,
|
||||
};
|
||||
let mut done = 0;
|
||||
for id in ids {
|
||||
let Some((track, clip)) = e.session().find_clip(id).map(|(t, c)| (t, c.clone())) else { continue };
|
||||
|
||||
@@ -475,38 +475,6 @@ fn select_all(e: &mut Engine, _: &Value) -> Result<Value> {
|
||||
Ok(json!({"tracks": s.edit.selected_tracks.len(), "end": end}))
|
||||
}
|
||||
|
||||
/// Adds the members of every active edit group that touches `t`, then puts `t` in track order.
|
||||
fn add_group_members(s: &Session, t: &mut Vec<TrackId>) {
|
||||
let extra: Vec<TrackId> =
|
||||
s.groups.iter().filter(|g| g.active && g.edit && g.members.iter().any(|m| t.contains(m))).flat_map(|g| g.members.clone()).collect();
|
||||
for m in extra {
|
||||
if !t.contains(&m) {
|
||||
t.push(m);
|
||||
}
|
||||
}
|
||||
let order: Vec<TrackId> = s.tracks.iter().map(|x| x.id).collect();
|
||||
t.sort_by_key(|x| order.iter().position(|o| o == x).unwrap_or(usize::MAX));
|
||||
}
|
||||
|
||||
/// Selecting clips also selects their range and tracks (Grabber click).
|
||||
fn select_clip_ids(s: &Session, ed: &mut soundcraft_model::EditState, c: Vec<ClipId>) {
|
||||
let mut range: Option<soundcraft_time::Range> = None;
|
||||
let mut trs = Vec::new();
|
||||
for id in &c {
|
||||
if let Some((tid, clip)) = s.find_clip(*id) {
|
||||
range = Some(range.map_or(clip.range(), |r| soundcraft_time::Range::new(r.start.min(clip.start), r.end.max(clip.end()))));
|
||||
if !trs.contains(&tid) {
|
||||
trs.push(tid);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(r) = range {
|
||||
ed.selection = r;
|
||||
ed.selected_tracks = trs;
|
||||
}
|
||||
ed.selected_clips = c;
|
||||
}
|
||||
|
||||
fn select(e: &mut Engine, p: &Value) -> Result<Value> {
|
||||
let tracks = if p.get("tracks").is_some() || p.get("track").is_some() { Some(tracks_param(e, "edit.select", p)?) } else { None };
|
||||
let st = position_param(e, "edit.select", p, "start")?;
|
||||
@@ -522,7 +490,15 @@ fn select(e: &mut Engine, p: &Value) -> Result<Value> {
|
||||
if let Some(mut t) = tracks {
|
||||
// Active edit groups select all their members together.
|
||||
if !exact {
|
||||
add_group_members(s, &mut t);
|
||||
let extra: Vec<soundcraft_model::TrackId> =
|
||||
s.groups.iter().filter(|g| g.active && g.edit && g.members.iter().any(|m| t.contains(m))).flat_map(|g| g.members.clone()).collect();
|
||||
for m in extra {
|
||||
if !t.contains(&m) {
|
||||
t.push(m);
|
||||
}
|
||||
}
|
||||
let order: Vec<soundcraft_model::TrackId> = s.tracks.iter().map(|x| x.id).collect();
|
||||
t.sort_by_key(|x| order.iter().position(|o| o == x).unwrap_or(usize::MAX));
|
||||
}
|
||||
ed.selected_tracks = t;
|
||||
}
|
||||
@@ -532,7 +508,22 @@ fn select(e: &mut Engine, p: &Value) -> Result<Value> {
|
||||
_ => {}
|
||||
}
|
||||
if let Some(c) = clips {
|
||||
select_clip_ids(s, &mut ed, c);
|
||||
// Selecting clips also selects their range and tracks (Grabber click).
|
||||
let mut range: Option<soundcraft_time::Range> = None;
|
||||
let mut trs = Vec::new();
|
||||
for id in &c {
|
||||
if let Some((tid, clip)) = s.find_clip(*id) {
|
||||
range = Some(range.map_or(clip.range(), |r| soundcraft_time::Range::new(r.start.min(clip.start), r.end.max(clip.end()))));
|
||||
if !trs.contains(&tid) {
|
||||
trs.push(tid);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(r) = range {
|
||||
ed.selection = r;
|
||||
ed.selected_tracks = trs;
|
||||
}
|
||||
ed.selected_clips = c;
|
||||
} else if list_changes {
|
||||
ed.selected_clips.clear();
|
||||
}
|
||||
|
||||
@@ -857,57 +857,6 @@ fn coalesce_clip_gain_to_volume(e: &mut Engine, p: &Value) -> Result<Value> {
|
||||
Ok(json!({"clips": n}))
|
||||
}
|
||||
|
||||
/// The automation parameters a trim/glide over a range touches on `tr`.
|
||||
fn trim_params(tr: &soundcraft_model::Track, pan_only: bool) -> Vec<AutoParam> {
|
||||
let mut params: Vec<AutoParam> = tr
|
||||
.automation
|
||||
.iter()
|
||||
.filter(|l| l.enabled && !matches!(l.param, AutoParam::Mute | AutoParam::SendMute(_)))
|
||||
.filter(|l| !pan_only || matches!(l.param, AutoParam::Pan(_)))
|
||||
.map(|l| l.param.clone())
|
||||
.collect();
|
||||
if pan_only {
|
||||
for i in 0..tr.mixer.pan.len().min(2) {
|
||||
let pp = AutoParam::Pan(i as u8);
|
||||
if !params.contains(&pp) {
|
||||
params.push(pp);
|
||||
}
|
||||
}
|
||||
}
|
||||
params
|
||||
}
|
||||
|
||||
/// Trim (shift the points inside `r` by the change at its end) or glide (ramp across `r`) one lane.
|
||||
fn trim_lane(tr: &mut soundcraft_model::Track, param: &AutoParam, r: Range, glide: bool) {
|
||||
let cur = super::mix::current_value(tr, param);
|
||||
let (lo, hi, _) = param.range();
|
||||
let l = tr.lane_mut(param);
|
||||
let before = l.value_at(r.start - 1, cur);
|
||||
let after = l.value_at(r.end, cur);
|
||||
let from = l.value_at(r.start, cur);
|
||||
if glide {
|
||||
l.clear_range(r.start, r.end.saturating_add(2));
|
||||
if r.start > 0 {
|
||||
l.set_point(r.start - 1, before);
|
||||
}
|
||||
l.set_point(r.start, from);
|
||||
l.set_point(r.end, cur);
|
||||
l.set_point(r.end.saturating_add(1), after);
|
||||
} else {
|
||||
let delta = cur - from;
|
||||
let last = l.value_at(r.end - 1, cur);
|
||||
if r.start > 0 {
|
||||
l.set_point(r.start - 1, before);
|
||||
}
|
||||
l.set_point(r.start, from);
|
||||
l.set_point(r.end - 1, last);
|
||||
l.set_point(r.end, after);
|
||||
for pt in l.points.iter_mut().filter(|pt| pt.at >= r.start && pt.at < r.end) {
|
||||
pt.value = (pt.value + delta).clamp(lo, hi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn trim_glide(e: &mut Engine, p: &Value, glide: bool, pan_only: bool, id: &str) -> Result<Value> {
|
||||
let tracks = tracks_required(e, id, p)?;
|
||||
let r = nonempty_range(e, p, id)?;
|
||||
@@ -915,8 +864,49 @@ fn trim_glide(e: &mut Engine, p: &Value, glide: bool, pan_only: bool, id: &str)
|
||||
let mut lanes = 0;
|
||||
for t in &tracks {
|
||||
let Some(tr) = s.track_mut(*t) else { continue };
|
||||
for param in trim_params(tr, pan_only) {
|
||||
trim_lane(tr, ¶m, r, glide);
|
||||
let mut params: Vec<AutoParam> = tr
|
||||
.automation
|
||||
.iter()
|
||||
.filter(|l| l.enabled && !matches!(l.param, AutoParam::Mute | AutoParam::SendMute(_)))
|
||||
.filter(|l| !pan_only || matches!(l.param, AutoParam::Pan(_)))
|
||||
.map(|l| l.param.clone())
|
||||
.collect();
|
||||
if pan_only {
|
||||
for i in 0..tr.mixer.pan.len().min(2) {
|
||||
let pp = AutoParam::Pan(i as u8);
|
||||
if !params.contains(&pp) {
|
||||
params.push(pp);
|
||||
}
|
||||
}
|
||||
}
|
||||
for param in params {
|
||||
let cur = super::mix::current_value(tr, ¶m);
|
||||
let (lo, hi, _) = param.range();
|
||||
let l = tr.lane_mut(¶m);
|
||||
let before = l.value_at(r.start - 1, cur);
|
||||
let after = l.value_at(r.end, cur);
|
||||
let from = l.value_at(r.start, cur);
|
||||
if glide {
|
||||
l.clear_range(r.start, r.end.saturating_add(2));
|
||||
if r.start > 0 {
|
||||
l.set_point(r.start - 1, before);
|
||||
}
|
||||
l.set_point(r.start, from);
|
||||
l.set_point(r.end, cur);
|
||||
l.set_point(r.end.saturating_add(1), after);
|
||||
} else {
|
||||
let delta = cur - from;
|
||||
let last = l.value_at(r.end - 1, cur);
|
||||
if r.start > 0 {
|
||||
l.set_point(r.start - 1, before);
|
||||
}
|
||||
l.set_point(r.start, from);
|
||||
l.set_point(r.end - 1, last);
|
||||
l.set_point(r.end, after);
|
||||
for pt in l.points.iter_mut().filter(|pt| pt.at >= r.start && pt.at < r.end) {
|
||||
pt.value = (pt.value + delta).clamp(lo, hi);
|
||||
}
|
||||
}
|
||||
lanes += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -564,11 +564,18 @@ pub fn chord_name(weights: &[f64; 12], bass: usize) -> Option<String> {
|
||||
const MAX_CHORD_BARS: usize = 100_000;
|
||||
const MAX_CHORD_NOTES: usize = 10_000_000;
|
||||
|
||||
/// Absolute `(start, end, pitch)` tick notes from every MIDI clip on `tracks`, cut to their clip
|
||||
/// and sorted by start.
|
||||
fn midi_tick_notes(s: &Session, tracks: &[TrackId]) -> Vec<(i64, i64, u8)> {
|
||||
fn extract_chords(e: &mut Engine, p: &Value) -> Result<Value> {
|
||||
let id = "event.extract_chords";
|
||||
let (_, t0, t1) = selection_ticks(e, p, id)?;
|
||||
let s = e.session();
|
||||
let picked = tracks_param(e, id, p)?;
|
||||
let mut tracks: Vec<TrackId> = picked.into_iter().filter(|t| s.track(*t).is_some_and(|x| x.kind.is_midi())).collect();
|
||||
if tracks.is_empty() {
|
||||
tracks = s.tracks.iter().filter(|t| t.kind.is_midi()).map(|t| t.id).collect();
|
||||
}
|
||||
// Absolute (tick) notes from every MIDI clip on those tracks, cut to their clip.
|
||||
let mut notes: Vec<(i64, i64, u8)> = Vec::new();
|
||||
for t in tracks {
|
||||
for t in &tracks {
|
||||
for c in s.track(*t).map(|tr| tr.clips()).unwrap_or(&[]) {
|
||||
let ClipContent::Midi { sequence } = &c.content else { continue };
|
||||
let base = s.tempo.samples_to_ticks(c.start, s.sample_rate);
|
||||
@@ -583,36 +590,6 @@ fn midi_tick_notes(s: &Session, tracks: &[TrackId]) -> Vec<(i64, i64, u8)> {
|
||||
}
|
||||
}
|
||||
notes.sort_unstable_by_key(|n| n.0);
|
||||
notes
|
||||
}
|
||||
|
||||
/// Duration-weighted pitch classes of the notes sounding in `a..b`, and the lowest pitch among them.
|
||||
fn pitch_class_weights(sounding: &[(i64, i64, u8)], a: i64, b: i64) -> ([f64; 12], Option<u8>) {
|
||||
let mut w = [0.0f64; 12];
|
||||
let mut bass: Option<u8> = None;
|
||||
for (na, nb, pitch) in sounding {
|
||||
let ov = (*nb).min(b) - (*na).max(a);
|
||||
if ov > 0 {
|
||||
if let Some(x) = w.get_mut(usize::from(*pitch) % 12) {
|
||||
*x += ov as f64;
|
||||
}
|
||||
bass = Some(bass.map_or(*pitch, |bp| bp.min(*pitch)));
|
||||
}
|
||||
}
|
||||
(w, bass)
|
||||
}
|
||||
|
||||
fn extract_chords(e: &mut Engine, p: &Value) -> Result<Value> {
|
||||
let id = "event.extract_chords";
|
||||
let (_, t0, t1) = selection_ticks(e, p, id)?;
|
||||
let s = e.session();
|
||||
let picked = tracks_param(e, id, p)?;
|
||||
let mut tracks: Vec<TrackId> = picked.into_iter().filter(|t| s.track(*t).is_some_and(|x| x.kind.is_midi())).collect();
|
||||
if tracks.is_empty() {
|
||||
tracks = s.tracks.iter().filter(|t| t.kind.is_midi()).map(|t| t.id).collect();
|
||||
}
|
||||
// Absolute (tick) notes from every MIDI clip on those tracks, cut to their clip.
|
||||
let notes = midi_tick_notes(s, &tracks);
|
||||
let mut bar = s.tempo.bar_beat_at_tick(t0).bar;
|
||||
let mut sounding: Vec<(i64, i64, u8)> = Vec::new();
|
||||
let (mut next, mut bars, mut heard) = (0, 0, 0);
|
||||
@@ -639,7 +616,17 @@ fn extract_chords(e: &mut Engine, p: &Value) -> Result<Value> {
|
||||
}
|
||||
bars += 1;
|
||||
heard += sounding.len();
|
||||
let (w, bass) = pitch_class_weights(&sounding, a, b);
|
||||
let mut w = [0.0f64; 12];
|
||||
let mut bass: Option<u8> = None;
|
||||
for (na, nb, pitch) in &sounding {
|
||||
let ov = (*nb).min(b) - (*na).max(a);
|
||||
if ov > 0 {
|
||||
if let Some(x) = w.get_mut(usize::from(*pitch) % 12) {
|
||||
*x += ov as f64;
|
||||
}
|
||||
bass = Some(bass.map_or(*pitch, |bp| bp.min(*pitch)));
|
||||
}
|
||||
}
|
||||
if let Some(name) = bass.and_then(|bp| chord_name(&w, usize::from(bp) % 12)) {
|
||||
let at = s.tempo.tick_to_samples(a, s.sample_rate);
|
||||
chords.push(json!({"bar": bar, "chord": name, "at": at}));
|
||||
|
||||
@@ -612,76 +612,6 @@ fn env_db(env: &[(i64, f32)], at: i64) -> f32 {
|
||||
prev.map_or(0.0, |(_, v)| v)
|
||||
}
|
||||
|
||||
/// Fold the volume `lane` into the gain envelope of clip `c` over the selection `r`.
|
||||
fn bake_volume_into_clip(c: &mut soundcraft_model::Clip, lane: &soundcraft_model::AutomationLane, base: f32, r: soundcraft_time::Range) {
|
||||
let delta = |at: i64| lane.value_at(at, base) - base;
|
||||
let sel0 = r.start.max(c.start);
|
||||
let sel1 = r.end.min(c.end());
|
||||
if sel1 <= sel0 {
|
||||
return;
|
||||
}
|
||||
let rel = |at: i64| at - c.start;
|
||||
let rel0 = rel(sel0);
|
||||
let rel1 = rel(sel1);
|
||||
// Later writes win, so the selection anchors replace any point they land on.
|
||||
let mut pts: Vec<(i64, f32)> =
|
||||
c.gain_env.iter().copied().filter(|&(at, _)| at < rel0.saturating_sub(1) || (sel1 < c.end() && at > rel1)).collect();
|
||||
if rel0 > 0 {
|
||||
pts.push((rel0 - 1, env_db(&c.gain_env, rel0 - 1)));
|
||||
}
|
||||
pts.push((rel0, env_db(&c.gain_env, rel0) + delta(sel0)));
|
||||
for pt in lane.points.iter().filter(|pt| pt.at > sel0 && pt.at < sel1) {
|
||||
let at = rel(pt.at);
|
||||
pts.push((at, env_db(&c.gain_env, at) + delta(pt.at)));
|
||||
}
|
||||
// The clip's own gain points inside the selection keep their shape, offset by the lane.
|
||||
for &(at, _) in c.gain_env.iter().filter(|&&(at, _)| at > rel0 && at < rel1) {
|
||||
pts.push((at, env_db(&c.gain_env, at) + delta(c.start.saturating_add(at))));
|
||||
}
|
||||
if sel1 - 1 > sel0 {
|
||||
let at = rel(sel1 - 1);
|
||||
pts.push((at, env_db(&c.gain_env, at) + delta(sel1 - 1)));
|
||||
}
|
||||
if sel1 < c.end() {
|
||||
pts.push((rel1, env_db(&c.gain_env, rel1)));
|
||||
}
|
||||
pts.sort_by_key(|p| p.0);
|
||||
let mut env = Vec::new();
|
||||
for p in pts {
|
||||
if env.last().is_some_and(|q: &(i64, f32)| q.0 == p.0) {
|
||||
env.pop();
|
||||
}
|
||||
env.push(p);
|
||||
}
|
||||
c.gain_env = env;
|
||||
}
|
||||
|
||||
/// Clear the volume lane over `r` when the clips all sit inside it, else hold the fader there.
|
||||
fn reset_volume_lane(tr: &mut soundcraft_model::Track, lane: &soundcraft_model::AutomationLane, base: f32, r: soundcraft_time::Range) {
|
||||
// Clearing the lane is only safe when no audio plays outside the selection; otherwise the
|
||||
// held edge values outside would fall back to the fader level.
|
||||
let clips_inside = tr.clips().iter().all(|c| c.start >= r.start && c.end() <= r.end);
|
||||
let covers_all = clips_inside && lane.points.iter().all(|pt| pt.at >= r.start && pt.at < r.end);
|
||||
let lane_m = tr.lane_mut(&AutoParam::Volume);
|
||||
if covers_all {
|
||||
lane_m.clear_range(r.start, r.end);
|
||||
} else {
|
||||
// Hold the fader across the selection and anchor the samples on either side, so the
|
||||
// curve outside does not interpolate through the gap or keep the old level inside.
|
||||
let before = (r.start > 0).then(|| lane.value_at(r.start - 1, base));
|
||||
let after = lane.value_at(r.end, base);
|
||||
lane_m.clear_range(r.start, r.end);
|
||||
if let Some(v) = before {
|
||||
lane_m.set_point(r.start - 1, v);
|
||||
}
|
||||
lane_m.set_point(r.start, base);
|
||||
if r.end - 1 > r.start {
|
||||
lane_m.set_point(r.end - 1, base);
|
||||
}
|
||||
lane_m.set_point(r.end, after);
|
||||
}
|
||||
}
|
||||
|
||||
fn vol_to_clip_gain(e: &mut Engine, p: &Value) -> Result<Value> {
|
||||
let tracks = tracks_required(e, "automation.volume_to_clip_gain", p)?;
|
||||
let r = range_param(e, "automation.volume_to_clip_gain", p)?;
|
||||
@@ -690,12 +620,72 @@ fn vol_to_clip_gain(e: &mut Engine, p: &Value) -> Result<Value> {
|
||||
let Some(tr) = s.track_mut(*t) else { continue };
|
||||
let Some(lane) = tr.lane(&AutoParam::Volume).cloned().filter(|l| !l.points.is_empty()) else { continue };
|
||||
let base = tr.mixer.volume_db;
|
||||
let delta = |at: i64| lane.value_at(at, base) - base;
|
||||
if let Some(pl) = tr.playlist_mut() {
|
||||
for c in pl.clips.iter_mut().filter(|c| c.range().overlaps(&r)) {
|
||||
bake_volume_into_clip(c, &lane, base, r);
|
||||
let sel0 = r.start.max(c.start);
|
||||
let sel1 = r.end.min(c.end());
|
||||
if sel1 <= sel0 {
|
||||
continue;
|
||||
}
|
||||
let rel = |at: i64| at - c.start;
|
||||
let rel0 = rel(sel0);
|
||||
let rel1 = rel(sel1);
|
||||
// Later writes win, so the selection anchors replace any point they land on.
|
||||
let mut pts: Vec<(i64, f32)> =
|
||||
c.gain_env.iter().copied().filter(|&(at, _)| at < rel0.saturating_sub(1) || (sel1 < c.end() && at > rel1)).collect();
|
||||
if rel0 > 0 {
|
||||
pts.push((rel0 - 1, env_db(&c.gain_env, rel0 - 1)));
|
||||
}
|
||||
pts.push((rel0, env_db(&c.gain_env, rel0) + delta(sel0)));
|
||||
for pt in lane.points.iter().filter(|pt| pt.at > sel0 && pt.at < sel1) {
|
||||
let at = rel(pt.at);
|
||||
pts.push((at, env_db(&c.gain_env, at) + delta(pt.at)));
|
||||
}
|
||||
// The clip's own gain points inside the selection keep their shape, offset by the lane.
|
||||
for &(at, _) in c.gain_env.iter().filter(|&&(at, _)| at > rel0 && at < rel1) {
|
||||
pts.push((at, env_db(&c.gain_env, at) + delta(c.start.saturating_add(at))));
|
||||
}
|
||||
if sel1 - 1 > sel0 {
|
||||
let at = rel(sel1 - 1);
|
||||
pts.push((at, env_db(&c.gain_env, at) + delta(sel1 - 1)));
|
||||
}
|
||||
if sel1 < c.end() {
|
||||
pts.push((rel1, env_db(&c.gain_env, rel1)));
|
||||
}
|
||||
pts.sort_by_key(|p| p.0);
|
||||
let mut env = Vec::new();
|
||||
for p in pts {
|
||||
if env.last().is_some_and(|q: &(i64, f32)| q.0 == p.0) {
|
||||
env.pop();
|
||||
}
|
||||
env.push(p);
|
||||
}
|
||||
c.gain_env = env;
|
||||
}
|
||||
}
|
||||
reset_volume_lane(tr, &lane, base, r);
|
||||
// Clearing the lane is only safe when no audio plays outside the selection; otherwise the
|
||||
// held edge values outside would fall back to the fader level.
|
||||
let clips_inside = tr.clips().iter().all(|c| c.start >= r.start && c.end() <= r.end);
|
||||
let covers_all = clips_inside && lane.points.iter().all(|pt| pt.at >= r.start && pt.at < r.end);
|
||||
let lane_m = tr.lane_mut(&AutoParam::Volume);
|
||||
if covers_all {
|
||||
lane_m.clear_range(r.start, r.end);
|
||||
} else {
|
||||
// Hold the fader across the selection and anchor the samples on either side, so the
|
||||
// curve outside does not interpolate through the gap or keep the old level inside.
|
||||
let before = (r.start > 0).then(|| lane.value_at(r.start - 1, base));
|
||||
let after = lane.value_at(r.end, base);
|
||||
lane_m.clear_range(r.start, r.end);
|
||||
if let Some(v) = before {
|
||||
lane_m.set_point(r.start - 1, v);
|
||||
}
|
||||
lane_m.set_point(r.start, base);
|
||||
if r.end - 1 > r.start {
|
||||
lane_m.set_point(r.end - 1, base);
|
||||
}
|
||||
lane_m.set_point(r.end, after);
|
||||
}
|
||||
}
|
||||
Ok(json!({}))
|
||||
}
|
||||
|
||||
@@ -221,23 +221,6 @@ fn io_state(s: &Session) -> Value {
|
||||
json!({"busses": s.busses, "outputs": s.outputs})
|
||||
}
|
||||
|
||||
/// Disconnect every track input, output and send that used bus `bid`.
|
||||
fn unroute_bus(s: &mut Session, bid: BusId) {
|
||||
for t in &mut s.tracks {
|
||||
if t.mixer.input == Route::Bus(bid) {
|
||||
t.mixer.input = Route::None;
|
||||
}
|
||||
if t.mixer.output == Route::Bus(bid) {
|
||||
t.mixer.output = Route::None;
|
||||
}
|
||||
for slot in &mut t.mixer.sends {
|
||||
if slot.as_ref().is_some_and(|x| x.target == Route::Bus(bid)) {
|
||||
*slot = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn io_setup(e: &mut Engine, p: &Value) -> Result<Value> {
|
||||
let id = "setup.io";
|
||||
let action = str_param(p, "action").unwrap_or("list");
|
||||
@@ -269,7 +252,19 @@ fn io_setup(e: &mut Engine, p: &Value) -> Result<Value> {
|
||||
"delete_bus" => {
|
||||
let bid: BusId = s.bus_by_name(&name).map(|b| b.id).ok_or_else(|| bad(id, format!("no bus `{name}`")))?;
|
||||
s.busses.retain(|b| b.id != bid);
|
||||
unroute_bus(s, bid);
|
||||
for t in &mut s.tracks {
|
||||
if t.mixer.input == Route::Bus(bid) {
|
||||
t.mixer.input = Route::None;
|
||||
}
|
||||
if t.mixer.output == Route::Bus(bid) {
|
||||
t.mixer.output = Route::None;
|
||||
}
|
||||
for slot in &mut t.mixer.sends {
|
||||
if slot.as_ref().is_some_and(|x| x.target == Route::Bus(bid)) {
|
||||
*slot = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"create_output" => {
|
||||
if s.outputs.iter().any(|o| o.name.eq_ignore_ascii_case(&name)) {
|
||||
|
||||
@@ -671,35 +671,6 @@ fn change_width(e: &mut Engine, p: &Value) -> Result<Value> {
|
||||
Ok(json!({"format": f}))
|
||||
}
|
||||
|
||||
/// The nearest ancestor folder of `folder` that is not itself being deleted (walks at most 64 levels).
|
||||
fn surviving_parent(s: &Session, deleted: &[TrackId], folder: TrackId) -> Option<TrackId> {
|
||||
let mut parent = s.track(folder).and_then(|folder| folder.folder);
|
||||
for _ in 0..64 {
|
||||
match parent {
|
||||
Some(candidate) if deleted.contains(&candidate) => parent = s.track(candidate).and_then(|folder| folder.folder),
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
parent
|
||||
}
|
||||
|
||||
/// Clear inputs and sends that used the removed bus; outputs that used it move to `replacement`.
|
||||
fn reroute_deleted_bus(s: &mut Session, bus: soundcraft_model::BusId, replacement: &Route) {
|
||||
for track in &mut s.tracks {
|
||||
if track.mixer.input == Route::Bus(bus) {
|
||||
track.mixer.input = Route::None;
|
||||
}
|
||||
if track.mixer.output == Route::Bus(bus) {
|
||||
track.mixer.output = replacement.clone();
|
||||
}
|
||||
for send in &mut track.mixer.sends {
|
||||
if send.as_ref().is_some_and(|send| send.target == Route::Bus(bus)) {
|
||||
*send = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn delete(e: &mut Engine, p: &Value) -> Result<Value> {
|
||||
let tracks = tracks_required(e, "track.delete", p)?;
|
||||
let s = e.session_mut();
|
||||
@@ -707,7 +678,13 @@ fn delete(e: &mut Engine, p: &Value) -> Result<Value> {
|
||||
.iter()
|
||||
.filter_map(|folder| {
|
||||
let bus = owned_folder_bus(s, *folder)?;
|
||||
let parent = surviving_parent(s, &tracks, *folder);
|
||||
let mut parent = s.track(*folder).and_then(|folder| folder.folder);
|
||||
for _ in 0..64 {
|
||||
match parent {
|
||||
Some(candidate) if tracks.contains(&candidate) => parent = s.track(candidate).and_then(|folder| folder.folder),
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
Some((bus, parent.and_then(|parent| folder_bus(s, parent)).unwrap_or(Route::Main)))
|
||||
})
|
||||
.collect();
|
||||
@@ -719,7 +696,13 @@ fn delete(e: &mut Engine, p: &Value) -> Result<Value> {
|
||||
continue;
|
||||
}
|
||||
let Some(folder) = child.folder.filter(|folder| tracks.contains(folder)) else { continue };
|
||||
let parent = surviving_parent(s, &tracks, folder);
|
||||
let mut parent = s.track(folder).and_then(|folder| folder.folder);
|
||||
for _ in 0..64 {
|
||||
match parent {
|
||||
Some(candidate) if tracks.contains(&candidate) => parent = s.track(candidate).and_then(|folder| folder.folder),
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
promoted.push((child.id, folder_bus(s, folder), parent, parent.and_then(|parent| folder_bus(s, parent))));
|
||||
}
|
||||
for (child, old_bus, parent, parent_bus) in promoted {
|
||||
@@ -737,7 +720,19 @@ fn delete(e: &mut Engine, p: &Value) -> Result<Value> {
|
||||
}
|
||||
for (bus, replacement) in removed_busses {
|
||||
s.busses.retain(|candidate| candidate.id != bus);
|
||||
reroute_deleted_bus(s, bus, &replacement);
|
||||
for track in &mut s.tracks {
|
||||
if track.mixer.input == Route::Bus(bus) {
|
||||
track.mixer.input = Route::None;
|
||||
}
|
||||
if track.mixer.output == Route::Bus(bus) {
|
||||
track.mixer.output = replacement.clone();
|
||||
}
|
||||
for send in &mut track.mixer.sends {
|
||||
if send.as_ref().is_some_and(|send| send.target == Route::Bus(bus)) {
|
||||
*send = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(json!({"deleted": tracks.len()}))
|
||||
}
|
||||
|
||||
+55
-66
@@ -439,70 +439,6 @@ fn write_track(out: &mut Vec<u8>, mut events: Vec<WEvent>) -> Result<(), MidiErr
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The conductor track: tempos, meters, key signatures and markers.
|
||||
fn conductor_events(smf: &Smf) -> Result<Vec<WEvent>, MidiError> {
|
||||
let mut ev = Vec::new();
|
||||
for &(t, bpm) in &smf.tempos {
|
||||
if !bpm.is_finite() || bpm <= 0.0 {
|
||||
return Err(MidiError::Write(format!("invalid tempo {bpm} bpm")));
|
||||
}
|
||||
let us = (60_000_000.0 / bpm).round().clamp(1.0, f64::from(0x00FF_FFFFu32)) as u32;
|
||||
let [_, a, b, c] = us.to_be_bytes();
|
||||
ev.push(WEvent { tick: clamp_tick(t), order: 0, bytes: meta(0x51, &[a, b, c])? });
|
||||
}
|
||||
for &(t, num, den) in &smf.meters {
|
||||
if den == 0 || !den.is_power_of_two() {
|
||||
return Err(MidiError::Write(format!("meter denominator {den} is not a power of two")));
|
||||
}
|
||||
let dd = den.trailing_zeros() as u8;
|
||||
ev.push(WEvent { tick: clamp_tick(t), order: 1, bytes: meta(0x58, &[num, dd, 24, 8])? });
|
||||
}
|
||||
for &(t, sf, minor) in &smf.key_sigs {
|
||||
let sf = sf.clamp(-7, 7);
|
||||
ev.push(WEvent { tick: clamp_tick(t), order: 2, bytes: meta(0x59, &[sf as u8, u8::from(minor)])? });
|
||||
}
|
||||
for (t, text) in &smf.markers {
|
||||
ev.push(WEvent { tick: clamp_tick(*t), order: 3, bytes: meta(0x06, text.as_bytes())? });
|
||||
}
|
||||
Ok(ev)
|
||||
}
|
||||
|
||||
/// Trim notes still sounding when the next same-pitch note starts, and drop duplicates.
|
||||
fn trim_overlaps(notes: &[Note], ends: &mut [u64], dropped: &mut [bool]) {
|
||||
// The reader pairs offs with ons first-in first-out, so a note still sounding when the next
|
||||
// same-pitch note starts would swap ends with it: trim it to that start. When two same-pitch
|
||||
// notes start on the same tick they cannot be told apart, so only the longer one is written.
|
||||
let mut order: Vec<usize> = (0..notes.len()).collect();
|
||||
order.sort_by_key(|&i| {
|
||||
notes.get(i).map_or((0, 0, 0, std::cmp::Reverse(0)), |n| {
|
||||
(n.channel & 0x0F, n.pitch.min(127), clamp_tick(n.start), std::cmp::Reverse(ends.get(i).copied().unwrap_or(0)))
|
||||
})
|
||||
});
|
||||
let mut prev: Option<usize> = None;
|
||||
for &b in &order {
|
||||
let Some(nb) = notes.get(b) else { continue };
|
||||
if let Some(a) = prev
|
||||
&& let Some(na) = notes.get(a)
|
||||
&& na.channel & 0x0F == nb.channel & 0x0F
|
||||
&& na.pitch.min(127) == nb.pitch.min(127)
|
||||
{
|
||||
let (sa, sb) = (clamp_tick(na.start), clamp_tick(nb.start));
|
||||
if sa == sb {
|
||||
if let Some(d) = dropped.get_mut(b) {
|
||||
*d = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if let Some(ea) = ends.get_mut(a)
|
||||
&& sb < *ea
|
||||
{
|
||||
*ea = sb;
|
||||
}
|
||||
}
|
||||
prev = Some(b);
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes a format-1 Standard MIDI File at [`PPQ`] (960) ticks per quarter note.
|
||||
///
|
||||
/// Track 1 is a conductor track holding tempos, meters, key signatures and markers; every
|
||||
@@ -527,7 +463,29 @@ pub fn write_smf(smf: &Smf) -> Result<Vec<u8>, MidiError> {
|
||||
out.extend_from_slice(&u16::try_from(PPQ).unwrap_or(960).to_be_bytes());
|
||||
|
||||
// Conductor track.
|
||||
let ev = conductor_events(smf)?;
|
||||
let mut ev = Vec::new();
|
||||
for &(t, bpm) in &smf.tempos {
|
||||
if !bpm.is_finite() || bpm <= 0.0 {
|
||||
return Err(MidiError::Write(format!("invalid tempo {bpm} bpm")));
|
||||
}
|
||||
let us = (60_000_000.0 / bpm).round().clamp(1.0, f64::from(0x00FF_FFFFu32)) as u32;
|
||||
let [_, a, b, c] = us.to_be_bytes();
|
||||
ev.push(WEvent { tick: clamp_tick(t), order: 0, bytes: meta(0x51, &[a, b, c])? });
|
||||
}
|
||||
for &(t, num, den) in &smf.meters {
|
||||
if den == 0 || !den.is_power_of_two() {
|
||||
return Err(MidiError::Write(format!("meter denominator {den} is not a power of two")));
|
||||
}
|
||||
let dd = den.trailing_zeros() as u8;
|
||||
ev.push(WEvent { tick: clamp_tick(t), order: 1, bytes: meta(0x58, &[num, dd, 24, 8])? });
|
||||
}
|
||||
for &(t, sf, minor) in &smf.key_sigs {
|
||||
let sf = sf.clamp(-7, 7);
|
||||
ev.push(WEvent { tick: clamp_tick(t), order: 2, bytes: meta(0x59, &[sf as u8, u8::from(minor)])? });
|
||||
}
|
||||
for (t, text) in &smf.markers {
|
||||
ev.push(WEvent { tick: clamp_tick(*t), order: 3, bytes: meta(0x06, text.as_bytes())? });
|
||||
}
|
||||
if has_conductor {
|
||||
write_track(&mut out, ev)?;
|
||||
}
|
||||
@@ -540,7 +498,38 @@ pub fn write_smf(smf: &Smf) -> Result<Vec<u8>, MidiError> {
|
||||
let notes = &track.sequence.notes;
|
||||
let mut ends: Vec<u64> = notes.iter().map(|n| clamp_tick(n.end()).max(clamp_tick(n.start).saturating_add(1))).collect();
|
||||
let mut dropped = vec![false; notes.len()];
|
||||
trim_overlaps(notes, &mut ends, &mut dropped);
|
||||
// The reader pairs offs with ons first-in first-out, so a note still sounding when the next
|
||||
// same-pitch note starts would swap ends with it: trim it to that start. When two same-pitch
|
||||
// notes start on the same tick they cannot be told apart, so only the longer one is written.
|
||||
let mut order: Vec<usize> = (0..notes.len()).collect();
|
||||
order.sort_by_key(|&i| {
|
||||
notes.get(i).map_or((0, 0, 0, std::cmp::Reverse(0)), |n| {
|
||||
(n.channel & 0x0F, n.pitch.min(127), clamp_tick(n.start), std::cmp::Reverse(ends.get(i).copied().unwrap_or(0)))
|
||||
})
|
||||
});
|
||||
let mut prev: Option<usize> = None;
|
||||
for &b in &order {
|
||||
let Some(nb) = notes.get(b) else { continue };
|
||||
if let Some(a) = prev
|
||||
&& let Some(na) = notes.get(a)
|
||||
&& na.channel & 0x0F == nb.channel & 0x0F
|
||||
&& na.pitch.min(127) == nb.pitch.min(127)
|
||||
{
|
||||
let (sa, sb) = (clamp_tick(na.start), clamp_tick(nb.start));
|
||||
if sa == sb {
|
||||
if let Some(d) = dropped.get_mut(b) {
|
||||
*d = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if let Some(ea) = ends.get_mut(a)
|
||||
&& sb < *ea
|
||||
{
|
||||
*ea = sb;
|
||||
}
|
||||
}
|
||||
prev = Some(b);
|
||||
}
|
||||
for (i, n) in notes.iter().enumerate() {
|
||||
if dropped.get(i).copied().unwrap_or(false) {
|
||||
continue;
|
||||
|
||||
+25
-31
@@ -439,40 +439,34 @@ impl AudioState {
|
||||
let b = if i0 + 1 < self.pending_len { ch.and_then(|v| v.get(i0 + 1)).copied().unwrap_or(a) } else { a };
|
||||
a * (1.0 - fr) + b * fr
|
||||
};
|
||||
write_frame(frame, channels, nch, sounding, &self.fold, sample);
|
||||
self.read += step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Write one device frame from `nch` rendered channels: fold surround to stereo (a mono device
|
||||
/// averages the two sides), or copy the channels straight through. `sample(c)` is channel `c`.
|
||||
fn write_frame(frame: &mut [f32], channels: usize, nch: usize, sounding: bool, fold: &[(f32, f32)], sample: impl Fn(usize) -> f32) {
|
||||
if channels < nch {
|
||||
// Fold surround to stereo first; a mono device averages the two sides.
|
||||
let (mut l, mut r) = (0.0f32, 0.0f32);
|
||||
if sounding {
|
||||
if nch == 2 {
|
||||
(l, r) = (sample(0), sample(1));
|
||||
if channels < nch {
|
||||
// Fold surround to stereo first; a mono device averages the two sides.
|
||||
let (mut l, mut r) = (0.0f32, 0.0f32);
|
||||
if sounding {
|
||||
if nch == 2 {
|
||||
(l, r) = (sample(0), sample(1));
|
||||
} else {
|
||||
for (c, (gl, gr)) in self.fold.iter().enumerate().take(nch) {
|
||||
let v = sample(c);
|
||||
l += v * gl;
|
||||
r += v * gr;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (c, o) in frame.iter_mut().enumerate() {
|
||||
*o = match c {
|
||||
0 if channels == 1 => out_sample(0.5 * l + 0.5 * r),
|
||||
0 => out_sample(l),
|
||||
1 => out_sample(r),
|
||||
_ => 0.0,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
for (c, (gl, gr)) in fold.iter().enumerate().take(nch) {
|
||||
let v = sample(c);
|
||||
l += v * gl;
|
||||
r += v * gr;
|
||||
for (c, o) in frame.iter_mut().enumerate() {
|
||||
*o = if c < nch.max(1) && sounding { out_sample(sample(c)) } else { 0.0 };
|
||||
}
|
||||
}
|
||||
}
|
||||
for (c, o) in frame.iter_mut().enumerate() {
|
||||
*o = match c {
|
||||
0 if channels == 1 => out_sample(0.5 * l + 0.5 * r),
|
||||
0 => out_sample(l),
|
||||
1 => out_sample(r),
|
||||
_ => 0.0,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
for (c, o) in frame.iter_mut().enumerate() {
|
||||
*o = if c < nch.max(1) && sounding { out_sample(sample(c)) } else { 0.0 };
|
||||
self.read += step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,35 +194,6 @@ fn keep_releases(notes: &mut Vec<NoteEvent>) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy `n` frames from `start` of the host buffers into the plugin's input `port` (mono sources
|
||||
/// fill every port channel).
|
||||
fn feed_input(inputs: &mut BusBuffers, port: usize, io: &[Vec<f32>], ch: usize, (start, n): (usize, usize)) {
|
||||
for c in 0..inputs.channels(port) {
|
||||
let src = io.get(c.min(ch - 1)).and_then(|s| s.get(start..start + n));
|
||||
if let (Some(src), Some(dst)) = (src, inputs.channel_mut(port, c).and_then(|d| d.get_mut(..n))) {
|
||||
dst.copy_from_slice(src);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy `n` frames of the plugin's output `port` into the host buffers from `start`; a mono host
|
||||
/// buffer takes the average of a stereo port.
|
||||
fn read_output(outputs: &BusBuffers, port: usize, io: &mut [Vec<f32>], ch: usize, (start, n): (usize, usize)) {
|
||||
let oc = outputs.channels(port);
|
||||
for (c, dst) in io.iter_mut().take(ch).enumerate() {
|
||||
let Some(dst) = dst.get_mut(start..start + n) else { continue };
|
||||
if ch == 1 && oc >= 2 {
|
||||
if let (Some(l), Some(r)) = (outputs.channel(port, 0), outputs.channel(port, 1)) {
|
||||
for ((d, a), b) in dst.iter_mut().zip(l).zip(r) {
|
||||
*d = 0.5 * (a + b);
|
||||
}
|
||||
}
|
||||
} else if let Some(src) = outputs.channel(port, c.min(oc.saturating_sub(1))).and_then(|s| s.get(..n)) {
|
||||
dst.copy_from_slice(src);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Plugin for Vst3Plugin {
|
||||
fn info(&self) -> &'static PluginInfo {
|
||||
self.info
|
||||
@@ -319,7 +290,12 @@ impl Plugin for Vst3Plugin {
|
||||
let end = u32::try_from(start + n).unwrap_or(u32::MAX);
|
||||
// Feed the main input bus (mono sources fill every bus channel).
|
||||
if self.has_input {
|
||||
feed_input(&mut self.inputs, 0, io, ch, (start, n));
|
||||
for c in 0..self.inputs.channels(0) {
|
||||
let src = io.get(c.min(ch - 1)).and_then(|s| s.get(start..start + n));
|
||||
if let (Some(src), Some(dst)) = (src, self.inputs.channel_mut(0, c).and_then(|d| d.get_mut(..n))) {
|
||||
dst.copy_from_slice(src);
|
||||
}
|
||||
}
|
||||
}
|
||||
let k = self.notes.iter().position(|e| e.time() >= end).unwrap_or(self.notes.len());
|
||||
let offset = u32::try_from(start).unwrap_or(u32::MAX);
|
||||
@@ -340,7 +316,19 @@ impl Plugin for Vst3Plugin {
|
||||
self.position = self.position.saturating_add(i64::try_from(n).unwrap_or(0));
|
||||
match r {
|
||||
Ok(()) if self.has_output => {
|
||||
read_output(&self.outputs, 0, io, ch, (start, n));
|
||||
let oc = self.outputs.channels(0);
|
||||
for (c, dst) in io.iter_mut().take(ch).enumerate() {
|
||||
let Some(dst) = dst.get_mut(start..start + n) else { continue };
|
||||
if ch == 1 && oc >= 2 {
|
||||
if let (Some(l), Some(r)) = (self.outputs.channel(0, 0), self.outputs.channel(0, 1)) {
|
||||
for ((d, a), b) in dst.iter_mut().zip(l).zip(r) {
|
||||
*d = 0.5 * (a + b);
|
||||
}
|
||||
}
|
||||
} else if let Some(src) = self.outputs.channel(0, c.min(oc.saturating_sub(1))).and_then(|s| s.get(..n)) {
|
||||
dst.copy_from_slice(src);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => log::debug!("{}: {e}", self.info.id),
|
||||
_ => {}
|
||||
|
||||
@@ -6,11 +6,6 @@
|
||||
mod common;
|
||||
|
||||
fn main() {
|
||||
// nextest lists custom-harness tests with `--list`; each binary here is one test.
|
||||
if std::env::args().any(|a| a == "--list") {
|
||||
println!("editor_main_thread: test");
|
||||
return;
|
||||
}
|
||||
let dir = common::fixture_dir();
|
||||
let gain = soundcraft_vst3_host::scan_paths(&[dir.to_path_buf()]).into_iter().find(|d| d.name == "Fixture Gain").expect("fixture").id;
|
||||
let mut p = soundcraft_vst3_host::instantiate_plugin(&gain).expect("instantiate");
|
||||
|
||||
@@ -6,11 +6,6 @@
|
||||
mod common;
|
||||
|
||||
fn main() {
|
||||
// nextest lists custom-harness tests with `--list`; each binary here is one test.
|
||||
if std::env::args().any(|a| a == "--list") {
|
||||
println!("module_exit: test");
|
||||
return;
|
||||
}
|
||||
let dir = common::fixture_dir();
|
||||
let gain = soundcraft_vst3_host::scan_paths(&[dir.to_path_buf()]).into_iter().find(|d| d.name == "Fixture Gain").expect("fixture").id;
|
||||
let p = soundcraft_vst3_host::instantiate_plugin(&gain).expect("instantiate");
|
||||
|
||||
@@ -5,11 +5,6 @@
|
||||
mod common;
|
||||
|
||||
fn main() {
|
||||
// nextest lists custom-harness tests with `--list`; each binary here is one test.
|
||||
if std::env::args().any(|a| a == "--list") {
|
||||
println!("module_exit_live_instance: test");
|
||||
return;
|
||||
}
|
||||
let dir = common::fixture_dir();
|
||||
let gain = soundcraft_vst3_host::scan_paths(&[dir.to_path_buf()]).into_iter().find(|d| d.name == "Fixture Gain").expect("fixture").id;
|
||||
let p = soundcraft_vst3_host::instantiate_plugin(&gain).expect("instantiate");
|
||||
|
||||
@@ -29,11 +29,6 @@ fn run(case: &str) -> Output {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// nextest lists custom-harness tests with `--list`; each binary here is one test.
|
||||
if std::env::args().any(|a| a == "--list") {
|
||||
println!("module_exit_shutdown: test");
|
||||
return;
|
||||
}
|
||||
match std::env::args().nth(1).as_deref() {
|
||||
Some("shutdown") => return child(false),
|
||||
Some("exit-handler") => return child(true),
|
||||
|
||||
@@ -6,11 +6,6 @@
|
||||
mod common;
|
||||
|
||||
fn main() {
|
||||
// nextest lists custom-harness tests with `--list`; each binary here is one test.
|
||||
if std::env::args().any(|a| a == "--list") {
|
||||
println!("module_exit_worker_thread: test");
|
||||
return;
|
||||
}
|
||||
std::thread::spawn(|| {
|
||||
let dir = common::fixture_dir();
|
||||
let gain = soundcraft_vst3_host::scan_paths(&[dir.to_path_buf()]).into_iter().find(|d| d.name == "Fixture Gain").expect("fixture").id;
|
||||
|
||||
Reference in New Issue
Block a user