From 52471b37e957feaea56dfd48932ef16d314cc74a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Oct 2026 23:36:57 -0400 Subject: [PATCH 01/12] chore(deps): bump the cargo-dependencies group across 1 directory with 11 updates (#767) Bumps the cargo-dependencies group with 11 updates in the / directory: | Package | From | To | | --- | --- | --- | | [serde_json](https://github.com/serde-rs/json) | `1.0.151` | `1.0.154` | | [proptest](https://github.com/proptest-rs/proptest) | `1.11.0` | `1.12.0` | | [symphonia](https://github.com/pdeljanov/Symphonia) | `0.5.5` | `0.6.1` | | [zip](https://github.com/zip-rs/zip2) | `9.0.1` | `9.0.5` | | [cpal](https://github.com/RustAudio/cpal) | `0.16.0` | `0.18.2` | | [egui](https://github.com/emilk/egui) | `0.36.2` | `0.37.0` | | [eframe](https://github.com/emilk/egui) | `0.36.2` | `0.37.0` | | [egui_kittest](https://github.com/emilk/egui) | `0.36.2` | `0.37.0` | | [libloading](https://github.com/nagisa/rust_libloading) | `0.8.9` | `0.9.0` | | [muda](https://github.com/tauri-apps/muda) | `0.19.3` | `0.21.2` | | [winresource](https://github.com/BenjaminRi/winresource) | `0.1.31` | `0.1.34` | Updates `serde_json` from 1.0.151 to 1.0.154 - [Release notes](https://github.com/serde-rs/json/releases) - [Commits](https://github.com/serde-rs/json/compare/v1.0.151...v1.0.154) Updates `proptest` from 1.11.0 to 1.12.0 - [Release notes](https://github.com/proptest-rs/proptest/releases) - [Changelog](https://github.com/proptest-rs/proptest/blob/main/CHANGELOG.md) - [Commits](https://github.com/proptest-rs/proptest/compare/v1.11.0...v1.12.0) Updates `symphonia` from 0.5.5 to 0.6.1 - [Release notes](https://github.com/pdeljanov/Symphonia/releases) - [Commits](https://github.com/pdeljanov/Symphonia/compare/v0.5.5...v0.6.1) Updates `zip` from 9.0.1 to 9.0.5 - [Release notes](https://github.com/zip-rs/zip2/releases) - [Changelog](https://github.com/zip-rs/zip2/blob/master/CHANGELOG.md) - [Commits](https://github.com/zip-rs/zip2/compare/v9.0.1...v9.0.5) Updates `cpal` from 0.16.0 to 0.18.2 - [Release notes](https://github.com/RustAudio/cpal/releases) - [Changelog](https://github.com/RustAudio/cpal/blob/master/CHANGELOG.md) - [Commits](https://github.com/RustAudio/cpal/compare/v0.16.0...v0.18.2) Updates `egui` from 0.36.2 to 0.37.0 - [Release notes](https://github.com/emilk/egui/releases) - [Changelog](https://github.com/emilk/egui/blob/main/CHANGELOG.md) - [Commits](https://github.com/emilk/egui/compare/0.36.2...0.37.0) Updates `eframe` from 0.36.2 to 0.37.0 - [Release notes](https://github.com/emilk/egui/releases) - [Changelog](https://github.com/emilk/egui/blob/main/CHANGELOG.md) - [Commits](https://github.com/emilk/egui/compare/0.36.2...0.37.0) Updates `egui_kittest` from 0.36.2 to 0.37.0 - [Release notes](https://github.com/emilk/egui/releases) - [Changelog](https://github.com/emilk/egui/blob/main/CHANGELOG.md) - [Commits](https://github.com/emilk/egui/compare/0.36.2...0.37.0) Updates `libloading` from 0.8.9 to 0.9.0 - [Commits](https://github.com/nagisa/rust_libloading/compare/0.8.9...0.9.0) Updates `muda` from 0.19.3 to 0.21.2 - [Release notes](https://github.com/tauri-apps/muda/releases) - [Changelog](https://github.com/tauri-apps/muda/blob/dev/CHANGELOG.md) - [Commits](https://github.com/tauri-apps/muda/compare/muda-v0.19.3...muda-v0.21.2) Updates `winresource` from 0.1.31 to 0.1.34 - [Commits](https://github.com/BenjaminRi/winresource/commits) --- updated-dependencies: - dependency-name: cpal dependency-version: 0.18.2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: cargo-dependencies - dependency-name: eframe dependency-version: 0.37.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: cargo-dependencies - dependency-name: egui dependency-version: 0.37.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: cargo-dependencies - dependency-name: egui_kittest dependency-version: 0.37.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: cargo-dependencies - dependency-name: libloading dependency-version: 0.9.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: cargo-dependencies - dependency-name: muda dependency-version: 0.21.2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: cargo-dependencies - dependency-name: proptest dependency-version: 1.12.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: cargo-dependencies - dependency-name: serde_json dependency-version: 1.0.154 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: cargo-dependencies - dependency-name: symphonia dependency-version: 0.6.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: cargo-dependencies - dependency-name: winresource dependency-version: 0.1.34 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: cargo-dependencies - dependency-name: zip dependency-version: 9.0.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: cargo-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 1004 ++++++++++++++++++----------------- Cargo.toml | 10 +- apps/soundcraft/Cargo.toml | 2 +- crates/clap-host/Cargo.toml | 2 +- crates/vst3-host/Cargo.toml | 2 +- 5 files changed, 528 insertions(+), 492 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 848fa2c2..d006e25c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,13 +12,22 @@ dependencies = [ ] [[package]] -name = "accesskit_atspi_common" -version = "0.18.1" +name = "accesskit" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e8c61bee90b42a772d39d06a740207dc71a4e780004ace1db8d99fb1baaa954" +checksum = "ad442f58ee04714aaa0ba0a2768c1ea1935b29507bb22ddece8cbbc76db02932" dependencies = [ - "accesskit", - "accesskit_consumer 0.36.0", + "uuid", +] + +[[package]] +name = "accesskit_atspi_common" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52c182f9c282ac9c5638d876d551d15e5f7d397ec263349a0c6a2b61595dd5e4" +dependencies = [ + "accesskit 0.25.1", + "accesskit_consumer", "atspi-common", "phf", "serde", @@ -27,43 +36,37 @@ dependencies = [ [[package]] name = "accesskit_consumer" -version = "0.35.0" +version = "0.39.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53cf47daed85312e763fbf85ceca136e0d7abc68e0a7e12abe11f48172bc3b10" +checksum = "8a45e2bb300bea6d5f33a82c3e051c3ddc6f41c209750f68a0acbe991a284344" dependencies = [ - "accesskit", - "hashbrown 0.16.1", + "accesskit 0.25.1", + "hashbrown 0.17.1", ] [[package]] -name = "accesskit_consumer" -version = "0.36.0" +name = "accesskit_ios" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25e0d7e25d06f4dc21d1774d67146e9e80d6789216cbd4d1e88185b0095dba60" +checksum = "28e539d64b27fd4c14de8f78ada8f7c944c092f52f7e49fb63316507a57f6937" dependencies = [ - "accesskit", - "hashbrown 0.16.1", -] - -[[package]] -name = "accesskit_consumer" -version = "0.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d10a236f96f87d70732e44520046785431ef01d5bcd6b041317bfadd2f88245" -dependencies = [ - "accesskit", - "hashbrown 0.16.1", + "accesskit 0.25.1", + "accesskit_consumer", + "hashbrown 0.17.1", + "objc2 0.5.3", + "objc2-foundation 0.2.2", + "objc2-ui-kit 0.2.2", ] [[package]] name = "accesskit_macos" -version = "0.26.3" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce02dc63b43f0c9296af9ac946312a2dc8814427d7a64d2d600971dac55b6076" +checksum = "5701624bf6a11efb6d4c8e922bf44126ed96250e5faa9a7a0468c979635839a4" dependencies = [ - "accesskit", - "accesskit_consumer 0.38.0", - "hashbrown 0.16.1", + "accesskit 0.25.1", + "accesskit_consumer", + "hashbrown 0.17.1", "objc2 0.5.3", "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", @@ -71,11 +74,11 @@ dependencies = [ [[package]] name = "accesskit_unix" -version = "0.21.1" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b016ca8db0ea0ea2ceff29a9d6240391492d960716aa471967c00e8cc8cb197c" +checksum = "202f24df034a7476d07b7f74284de84f6d62aabd858dbe7ee9cad3b7ad6f8f9d" dependencies = [ - "accesskit", + "accesskit 0.25.1", "accesskit_atspi_common", "async-channel", "async-executor", @@ -89,25 +92,26 @@ dependencies = [ [[package]] name = "accesskit_windows" -version = "0.32.1" +version = "0.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff7009f1a532e917d66970a1e80c965140c6cfbbabbdde3d64e5431e6c78e21" +checksum = "ce63f35d6bdcf59f26b76b3379063f738e6412cef46999cc772d46aa3de35adb" dependencies = [ - "accesskit", - "accesskit_consumer 0.35.0", - "hashbrown 0.16.1", + "accesskit 0.25.1", + "accesskit_consumer", + "hashbrown 0.17.1", "static_assertions", - "windows 0.62.2", - "windows-core 0.62.2", + "windows", + "windows-core", ] [[package]] name = "accesskit_winit" -version = "0.32.2" +version = "0.34.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fe9a94394896352cc4660ca2288bd4ef883d83238853c038b44070c8f134313" +checksum = "ee96dd150375c05912d8a473f98c2fb155fb3234fac88292d396548a2460fa77" dependencies = [ - "accesskit", + "accesskit 0.25.1", + "accesskit_ios", "accesskit_macos", "accesskit_unix", "accesskit_windows", @@ -151,9 +155,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alsa" -version = "0.9.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43" +checksum = "812947049edcd670a82cd5c73c3661d2e58468577ba8489de58e1a73c04cbd5d" dependencies = [ "alsa-sys", "bitflags 2.13.2", @@ -163,9 +167,9 @@ dependencies = [ [[package]] name = "alsa-sys" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527" +checksum = "ad7569085a265dd3f607ebecce7458eaab2132a84393534c95b18dcbc3f31e04" dependencies = [ "libc", "pkg-config", @@ -180,7 +184,7 @@ dependencies = [ "android-properties", "bitflags 2.13.2", "cc", - "jni 0.22.4", + "jni", "libc", "log", "ndk", @@ -228,6 +232,7 @@ dependencies = [ "parking_lot", "percent-encoding", "windows-sys 0.60.2", + "wl-clipboard-rs", "x11rb", ] @@ -249,20 +254,19 @@ version = "0.38.0+1.3.281" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" dependencies = [ - "libloading", + "libloading 0.8.9", ] [[package]] name = "asio-sys" -version = "0.2.6" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826194e1612938c9be09b78b58323fbb2e326de3d491b4230186cf6e832d8ded" +checksum = "477f1cf5b65679fc2e5422af426f87becba79d1cfa2cd77eff3433e36c8021b9" dependencies = [ "bindgen", "cc", "num-derive", "num-traits", - "parse_cfg", "walkdir", ] @@ -466,15 +470,6 @@ dependencies = [ "syn 2.0.119", ] -[[package]] -name = "bit-set" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" -dependencies = [ - "bit-vec 0.8.0", -] - [[package]] name = "bit-set" version = "0.10.0" @@ -485,10 +480,13 @@ dependencies = [ ] [[package]] -name = "bit-vec" -version = "0.8.0" +name = "bit-set" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" +checksum = "56d87354e4229f54a44f7bf2435906a4656dba36026ab6eaca629a2c436a691c" +dependencies = [ + "bit-vec 0.10.1", +] [[package]] name = "bit-vec" @@ -496,6 +494,16 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51" +[[package]] +name = "bit-vec" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5727b15fa97d4f4fee0a3b7c3d550ed0269f54329207b86388de918604e31269" +dependencies = [ + "borsh", + "serde", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -507,9 +515,6 @@ name = "bitflags" version = "2.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" -dependencies = [ - "serde_core", -] [[package]] name = "block2" @@ -542,6 +547,30 @@ dependencies = [ "piper", ] +[[package]] +name = "borsh" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "553c5d846a6ba5150c65e3b1b8ec073bcf1abc20f9b7220de384a4443ea4e20a" +dependencies = [ + "borsh-derive", + "bytes", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12cdfe656708a01f89b451a7d36466e6fe6c414de0aa18fc54f864f6f9ca9f56" +dependencies = [ + "once_cell", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 3.0.6", +] + [[package]] name = "bumpalo" version = "3.20.3" @@ -594,42 +623,18 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "calloop" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b32313e937c8721c9d17ee9bf60699a6402b4b7c2cb6ac1bee0ffcaa4b4e5e" -dependencies = [ - "bitflags 2.13.2", - "polling", - "rustix 1.1.5", - "tracing", -] - [[package]] name = "calloop-wayland-source" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" dependencies = [ - "calloop 0.13.0", + "calloop", "rustix 0.38.44", "wayland-backend", "wayland-client", ] -[[package]] -name = "calloop-wayland-source" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138efcf0940a02ebf0cc8d1eff41a1682a46b431630f4c52450d6265876021fa" -dependencies = [ - "calloop 0.14.5", - "rustix 1.1.5", - "wayland-backend", - "wayland-client", -] - [[package]] name = "cc" version = "1.6.0" @@ -642,19 +647,13 @@ dependencies = [ "shlex 2.0.1", ] -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - [[package]] name = "cexpr" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "nom", + "nom 7.1.3", ] [[package]] @@ -678,6 +677,17 @@ dependencies = [ "libc", ] +[[package]] +name = "chacha20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" +dependencies = [ + "cfg-if", + "cpufeatures", + "rand_core", +] + [[package]] name = "clang-sys" version = "1.9.1" @@ -686,7 +696,7 @@ checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a" dependencies = [ "glob", "libc", - "libloading", + "libloading 0.8.9", ] [[package]] @@ -807,11 +817,11 @@ checksum = "7f8f80099a98041a3d1622845c271458a2d73e688351bf3cb999266764b81d48" [[package]] name = "coreaudio-rs" -version = "0.13.0" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aae284fbaf7d27aa0e292f7677dfbe26503b0d555026f702940805a630eac17" +checksum = "7d5d7dca3ebcf65a035582c9ad4385371a9d9ee6537474d2a278f4e1e475bb58" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.13.2", "libc", "objc2-audio-toolbox", "objc2-core-audio", @@ -821,15 +831,18 @@ dependencies = [ [[package]] name = "cpal" -version = "0.16.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbd307f43cc2a697e2d1f8bc7a1d824b5269e052209e28883e5bc04d095aaa3f" +checksum = "6f02e8d0327b42d3e2e4ab2119af397344eb9fc54a34bf0ddeaa1277af8681f1" dependencies = [ "alsa", "asio-sys", + "block2 0.6.2", "coreaudio-rs", "dasp_sample", - "jni 0.21.1", + "futures-channel", + "futures-util", + "jni", "js-sys", "libc", "mach2", @@ -837,13 +850,27 @@ dependencies = [ "ndk-context", "num-derive", "num-traits", + "objc2 0.6.5", "objc2-audio-toolbox", + "objc2-avf-audio", "objc2-core-audio", "objc2-core-audio-types", + "objc2-core-foundation", + "objc2-foundation 0.3.2", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows 0.54.0", + "windows", + "windows-core", +] + +[[package]] +name = "cpufeatures" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" +dependencies = [ + "libc", ] [[package]] @@ -856,7 +883,7 @@ name = "craft-ui" version = "0.1.0" source = "git+https://github.com/storytold/craft-libs?rev=5c874d51eeb3de70e735c8ce87a59a7dedd47675#5c874d51eeb3de70e735c8ce87a59a7dedd47675" dependencies = [ - "egui", + "egui 0.36.2", "serde", ] @@ -969,7 +996,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" dependencies = [ - "libloading", + "libloading 0.8.9", ] [[package]] @@ -998,21 +1025,30 @@ name = "ecolor" version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0656a7530b9e2841ee7a5f983965bdd9840c32f4012f759cfd44b912cced6b34" +dependencies = [ + "emath 0.36.2", +] + +[[package]] +name = "ecolor" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31bcbef0d4263a63eb32c4834e5d711613035ce1333468e107d928923e21e6e2" dependencies = [ "bytemuck", - "emath", + "emath 0.37.0", ] [[package]] name = "eframe" -version = "0.36.2" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ad41fac6e3149abb4866e93df37dbd33baa95db525c3368c944584e95a5dc2" +checksum = "9c5642501a65ca657d05f00c643a5c14ff2346cb3818032bfe23d58e1f8e5f84" dependencies = [ "ahash", "bytemuck", "document-features", - "egui", + "egui 0.37.0", "egui-wgpu", "egui-winit", "egui_glow", @@ -1029,6 +1065,7 @@ dependencies = [ "pollster 1.0.1", "profiling", "raw-window-handle", + "send_wrapper", "static_assertions", "wasm-bindgen", "web-sys", @@ -1044,11 +1081,31 @@ version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc938cc27cd911415e1e4d151bc56ff9df063d8db081374e87aeb805ea74b2ba" dependencies = [ - "accesskit", + "accesskit 0.24.1", "ahash", "bitflags 2.13.2", - "emath", - "epaint", + "emath 0.36.2", + "epaint 0.36.2", + "itertools 0.15.0", + "log", + "nohash-hasher", + "profiling", + "smallvec", + "unicode-segmentation", + "web-sys", +] + +[[package]] +name = "egui" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec9144f49b7a28b5f395d3147e325b4fe3e77ba686eac11d1bdf0a7da3f375f" +dependencies = [ + "accesskit 0.25.1", + "ahash", + "bitflags 2.13.2", + "emath 0.37.0", + "epaint 0.37.0", "itertools 0.15.0", "log", "nohash-hasher", @@ -1060,15 +1117,15 @@ dependencies = [ [[package]] name = "egui-wgpu" -version = "0.36.2" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cef1f6d1bba40b09a4d0d62cb822b81030922cfbf65c5373398c3a3caafc5bd" +checksum = "0309cbe1febed48af5b974e4bcc424b35e9664863a4d402697147b57a488c864" dependencies = [ "ahash", "bytemuck", "document-features", - "egui", - "epaint", + "egui 0.37.0", + "epaint 0.37.0", "log", "profiling", "thiserror 2.0.21", @@ -1080,34 +1137,36 @@ dependencies = [ [[package]] name = "egui-winit" -version = "0.36.2" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98466000559d66ba4db786a30448b01483946add4188e7f10f522a27b4fb89cc" +checksum = "e4e449f4c4a2a60c7ccb4ba7fa4078417bfe1f9ddc03b5e05ce75748e33a70de" dependencies = [ "accesskit_winit", "arboard", + "block2 0.6.2", "bytemuck", - "egui", + "egui 0.37.0", "log", "objc2 0.6.5", + "objc2-app-kit 0.3.2", "objc2-foundation 0.3.2", "objc2-ui-kit 0.3.2", "profiling", "raw-window-handle", - "smithay-clipboard", "web-time", "webbrowser", + "windows-sys 0.61.2", "winit", ] [[package]] name = "egui_glow" -version = "0.36.2" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "606ed4167daf341e5753d703285c4c1d562324b2c07aa8f405cd7d066fe58f3b" +checksum = "9275ca5cc35a6b7c94e0e8cedf22542f66cb0cf45ed8ab2657c78ff49fec7268" dependencies = [ "bytemuck", - "egui", + "egui 0.37.0", "glow", "log", "profiling", @@ -1116,13 +1175,13 @@ dependencies = [ [[package]] name = "egui_kittest" -version = "0.36.2" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb3f3effb2726fee23449293e67acbf6cc6ce35525b1722bd5743bb348114d5" +checksum = "0979c2e37860fcf13a2bea77508607f067a9a6a84ba9bd47b5e298582fb59b86" dependencies = [ "dify", "eframe", - "egui", + "egui 0.37.0", "egui-wgpu", "image", "kittest", @@ -1146,22 +1205,14 @@ name = "emath" version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52c8d4141cf0f60fb2f7aefaf352d3ffa5171ef0ab09731c64391801e95e48f1" -dependencies = [ - "bytemuck", -] [[package]] -name = "encoding_rs" -version = "0.8.42" +name = "emath" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e985e0451871ad22fb8d2b6b076e2028a502a0d3950998c2c5c0a4f9b5d9679" +checksum = "e73ce90f54750be8672c02be0743fe47530e6f251e066f0ee2869b59eb297af5" dependencies = [ - "cfg-if", - "core_detect", - "multiversion_no_op", - "rustversion", - "scopeguard", - "simdutf8", + "bytemuck", ] [[package]] @@ -1198,10 +1249,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0ec0308dc23130fc1a7d4623a8534c2ed069c6dc84e258ee4f3de99615aa1a3" dependencies = [ "ahash", - "bytemuck", - "ecolor", - "emath", - "epaint_default_fonts", + "ecolor 0.36.2", + "emath 0.36.2", "font-types", "harfrust", "log", @@ -1213,14 +1262,40 @@ dependencies = [ "smallvec", "unicode-general-category", "unicode-segmentation", - "vello_cpu", + "vello_cpu 0.1.0", +] + +[[package]] +name = "epaint" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97bac86ae3dde93fc3951e790951caea739ff39a6c89d909cdaf1d9278dbb1bd" +dependencies = [ + "ahash", + "bytemuck", + "ecolor 0.37.0", + "emath 0.37.0", + "epaint_default_fonts", + "font-types", + "harfrust", + "log", + "nohash-hasher", + "parking_lot", + "profiling", + "self_cell", + "skrifa", + "smallvec", + "unicode-general-category", + "unicode-properties", + "unicode-segmentation", + "vello_cpu 0.2.0", ] [[package]] name = "epaint_default_fonts" -version = "0.36.2" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773fa9c96dd0dbef887e39d0ed6177f141cce4d68e6041df77570aa3702dfa13" +checksum = "cfc1cc505827f4c23eedc297975a0fd038268d41293f389dd8ff3b574aefab19" [[package]] name = "equivalent" @@ -1312,6 +1387,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484" +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "flate2" version = "1.1.10" @@ -1329,6 +1410,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foldhash" version = "0.2.0" @@ -1380,6 +1467,15 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures-channel" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" +dependencies = [ + "futures-core", +] + [[package]] name = "futures-core" version = "0.3.34" @@ -1475,6 +1571,7 @@ dependencies = [ "cfg-if", "libc", "r-efi 6.0.0", + "rand_core", ] [[package]] @@ -1495,13 +1592,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed4a1bb24121291d27230c1b1b44e07d6a9b28cefdb32fe1581dfb84e14f940a" dependencies = [ "bytemuck", - "foldhash", + "foldhash 0.2.0", "hashbrown 0.17.1", "log", "peniko", "skrifa", "smallvec", - "vello_common", + "vello_common 0.1.0", +] + +[[package]] +name = "glifo" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282a26c1e23de04bdab3e34a21b6f877a479b96737792516b9b8b8f69b6661be" +dependencies = [ + "bytemuck", + "foldhash 0.2.0", + "hashbrown 0.17.1", + "log", + "peniko", + "skrifa", + "smallvec", + "vello_common 0.2.0", ] [[package]] @@ -1535,7 +1648,7 @@ dependencies = [ "glutin_egl_sys", "glutin_glx_sys", "glutin_wgl_sys", - "libloading", + "libloading 0.8.9", "objc2 0.6.5", "objc2-app-kit 0.3.2", "objc2-core-foundation", @@ -1599,7 +1712,7 @@ dependencies = [ "log", "presser", "thiserror 2.0.21", - "windows 0.62.2", + "windows", ] [[package]] @@ -1635,6 +1748,15 @@ dependencies = [ "smallvec", ] +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + [[package]] name = "hashbrown" version = "0.16.1" @@ -1643,7 +1765,7 @@ checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.2.0", ] [[package]] @@ -1652,7 +1774,7 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" dependencies = [ - "foldhash", + "foldhash 0.2.0", ] [[package]] @@ -1840,22 +1962,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys 0.3.1", - "log", - "thiserror 1.0.69", - "walkdir", - "windows-sys 0.45.0", -] - [[package]] name = "jni" version = "0.22.4" @@ -1937,13 +2043,12 @@ dependencies = [ [[package]] name = "keyboard-types" -version = "0.7.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +checksum = "0fbe853b403ae61a04233030ae8a79d94975281ed9770a1f9e246732b534b28d" dependencies = [ "bitflags 2.13.2", "serde", - "unicode-segmentation", ] [[package]] @@ -1953,7 +2058,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" dependencies = [ "libc", - "libloading", + "libloading 0.8.9", "pkg-config", ] @@ -1965,12 +2070,12 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "kittest" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ceaa75eb0036a32b6b9833962eb18137449e9817e2e586006471925b727fd5" +checksum = "0bb81a721ba76aa095b7f45deb180504f786904e56ca4b8e141f66eb89a78b75" dependencies = [ - "accesskit", - "accesskit_consumer 0.35.0", + "accesskit 0.25.1", + "accesskit_consumer", ] [[package]] @@ -2007,6 +2112,16 @@ dependencies = [ "windows-link", ] +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + [[package]] name = "libm" version = "0.2.16" @@ -2072,12 +2187,9 @@ checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" [[package]] name = "mach2" -version = "0.4.3" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" -dependencies = [ - "libc", -] +checksum = "dae608c151f68243f2b000364e1f7b186d9c29845f7d2d85bd31b9ad77ad552b" [[package]] name = "memchr" @@ -2141,9 +2253,9 @@ dependencies = [ [[package]] name = "muda" -version = "0.19.3" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd04e60bc0b07438a6771710ee1698f98f6ebbc7f89b61264af1563b8aeb878" +checksum = "4340e89c178916e62879c5c36b7f699019369be413e496178a6983ce91b8bc03" dependencies = [ "crossbeam-channel", "dpi", @@ -2152,18 +2264,11 @@ dependencies = [ "objc2-app-kit 0.3.2", "objc2-core-foundation", "objc2-foundation 0.3.2", - "once_cell", "png", "thiserror 2.0.21", "windows-sys 0.61.2", ] -[[package]] -name = "multiversion_no_op" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743fb55ba31b18fb1ecef6bdc9aa2743314978ac084044301a7eee33fb99a20d" - [[package]] name = "naga" version = "30.0.1" @@ -2260,6 +2365,15 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + [[package]] name = "num-complex" version = "0.4.6" @@ -2391,6 +2505,17 @@ dependencies = [ "objc2-foundation 0.3.2", ] +[[package]] +name = "objc2-avf-audio" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13a380031deed8e99db00065c45937da434ca987c034e13b87e4441f9e4090be" +dependencies = [ + "bitflags 2.13.2", + "objc2 0.6.5", + "objc2-foundation 0.3.2", +] + [[package]] name = "objc2-cloud-kit" version = "0.2.2" @@ -2425,6 +2550,7 @@ dependencies = [ "objc2 0.6.5", "objc2-core-audio-types", "objc2-core-foundation", + "objc2-foundation 0.3.2", ] [[package]] @@ -2456,7 +2582,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ "bitflags 2.13.2", + "block2 0.6.2", "dispatch2", + "libc", "objc2 0.6.5", ] @@ -2524,6 +2652,7 @@ checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ "bitflags 2.13.2", "block2 0.6.2", + "libc", "objc2 0.6.5", "objc2-core-foundation", ] @@ -2714,6 +2843,16 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "os_pipe" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + [[package]] name = "parking" version = "2.2.1" @@ -2743,15 +2882,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "parse_cfg" -version = "4.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "905787a434a2c721408e7c9a252e85f3d93ca0f118a5283022636c0e05a7ea49" -dependencies = [ - "nom", -] - [[package]] name = "peniko" version = "0.6.1" @@ -2772,10 +2902,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] -name = "phf" -version = "0.13.1" +name = "petgraph" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" +dependencies = [ + "fixedbitset", + "hashbrown 0.15.5", + "indexmap", +] + +[[package]] +name = "phf" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "010378780309880b08997fae13be7834dba947d36393bd372f2b1556deb2a2f6" dependencies = [ "phf_macros", "phf_shared", @@ -2784,9 +2925,9 @@ dependencies = [ [[package]] name = "phf_generator" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +checksum = "aeb62e0959d5a1bebc965f4d15d9e2b7cea002b6b0f5ba8cde6cc26738467100" dependencies = [ "fastrand", "phf_shared", @@ -2794,9 +2935,9 @@ dependencies = [ [[package]] name = "phf_macros" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +checksum = "5fa8d0ca26d424d27630da600c6624696e7dec8bf7b3b492b383c5dc49e5e085" dependencies = [ "phf_generator", "phf_shared", @@ -2807,9 +2948,9 @@ dependencies = [ [[package]] name = "phf_shared" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +checksum = "c6fd9027e2d9319be6349febd1db4e8d02aa544921200c9b777720ac34a3aa89" dependencies = [ "siphasher", ] @@ -2935,15 +3076,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - [[package]] name = "presser" version = "0.3.1" @@ -2995,16 +3127,17 @@ checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" [[package]] name = "proptest" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" +checksum = "b8530004ccb15eae51c7e40009fbe317f341f804db54dc033eec1c50be28cfa0" dependencies = [ - "bit-set 0.8.0", - "bit-vec 0.8.0", + "bit-set 0.11.1", + "bit-vec 0.10.1", "bitflags 2.13.2", + "chacha20", + "core_detect", "num-traits", "rand", - "rand_chacha", "rand_xorshift", "regex-syntax", "rusty-fork", @@ -3062,38 +3195,25 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.9.5" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" +checksum = "65c9fb96cbc91e3478eaae79a69fcd3f1ae4ad052e471fe6732fff548984b4af" dependencies = [ - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", + "getrandom 0.4.3", "rand_core", ] [[package]] name = "rand_core" -version = "0.9.5" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" [[package]] name = "rand_xorshift" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +checksum = "60aa6af80be32871323012e02e6e65f8a7cc7890931ae421d217ad8fe0df2ccf" dependencies = [ "rand_core", ] @@ -3203,6 +3323,12 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-lite" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973" + [[package]] name = "regex-syntax" version = "0.8.11" @@ -3354,6 +3480,12 @@ version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" + [[package]] name = "serde" version = "1.0.229" @@ -3386,9 +3518,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.151" +version = "1.0.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6" dependencies = [ "itoa", "memchr", @@ -3505,8 +3637,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ "bitflags 2.13.2", - "calloop 0.13.0", - "calloop-wayland-source 0.3.0", + "calloop", + "calloop-wayland-source", "cursor-icon", "libc", "log", @@ -3523,44 +3655,6 @@ dependencies = [ "xkeysym", ] -[[package]] -name = "smithay-client-toolkit" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0" -dependencies = [ - "bitflags 2.13.2", - "calloop 0.14.5", - "calloop-wayland-source 0.4.1", - "cursor-icon", - "libc", - "log", - "memmap2", - "rustix 1.1.5", - "thiserror 2.0.21", - "wayland-backend", - "wayland-client", - "wayland-csd-frame", - "wayland-cursor", - "wayland-protocols", - "wayland-protocols-experimental", - "wayland-protocols-misc", - "wayland-protocols-wlr", - "wayland-scanner", - "xkeysym", -] - -[[package]] -name = "smithay-clipboard" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71704c03f739f7745053bde45fa203a46c58d25bc5c4efba1d9a60e9dba81226" -dependencies = [ - "libc", - "smithay-client-toolkit 0.20.0", - "wayland-backend", -] - [[package]] name = "smol_str" version = "0.2.2" @@ -3575,7 +3669,7 @@ name = "soundcraft" version = "0.12.1" dependencies = [ "eframe", - "egui", + "egui 0.37.0", "log", "muda", "objc2 0.6.5", @@ -3625,7 +3719,7 @@ name = "soundcraft-clap-host" version = "0.12.1" dependencies = [ "clap-sys", - "libloading", + "libloading 0.9.0", "log", "serde", "soundcraft-dsp", @@ -3747,7 +3841,7 @@ name = "soundcraft-ui-egui" version = "0.12.1" dependencies = [ "craft-ui", - "egui", + "egui 0.37.0", "egui_kittest", "image", "log", @@ -3783,7 +3877,7 @@ dependencies = [ name = "soundcraft-vst3-host" version = "0.12.1" dependencies = [ - "libloading", + "libloading 0.9.0", "log", "objc2 0.6.5", "objc2-app-kit 0.3.2", @@ -3800,7 +3894,7 @@ name = "soundcraft-web" version = "0.12.1" dependencies = [ "eframe", - "egui", + "egui 0.37.0", "js-sys", "log", "rfd", @@ -3843,9 +3937,9 @@ checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" [[package]] name = "symphonia" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5773a4c030a19d9bfaa090f49746ff35c75dfddfa700df7a5939d5e076a57039" +checksum = "a7edef6a96b696d4e0cab5ee9ebb7ca155ed95f30a6b45bbb8b97d2727f02424" dependencies = [ "lazy_static", "symphonia-bundle-flac", @@ -3866,44 +3960,44 @@ dependencies = [ [[package]] name = "symphonia-bundle-flac" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91565e180aea25d9b80a910c546802526ffd0072d0b8974e3ebe59b686c9976" +checksum = "6405d5c34ff6f8f7ca08a4101efe66d21e180f7322ef9359900a0e55698a7892" dependencies = [ "log", + "symphonia-common", "symphonia-core", "symphonia-metadata", - "symphonia-utils-xiph", ] [[package]] name = "symphonia-bundle-mp3" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4872dd6bb56bf5eac799e3e957aa1981086c3e613b27e0ac23b176054f7c57ed" +checksum = "98ea5ffc8716bff677dfb3b01b420c7b758de901a72b8c330bf2040ab74b4add" dependencies = [ "lazy_static", "log", "symphonia-core", - "symphonia-metadata", ] [[package]] name = "symphonia-codec-aac" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c263845aa86881416849c1729a54c7f55164f8b96111dba59de46849e73a790" +checksum = "f5bf8e39552d34a3c4c98333370e62f48c92456d2e814f273b5c3ad7c4a5f45c" dependencies = [ "lazy_static", "log", + "symphonia-common", "symphonia-core", ] [[package]] name = "symphonia-codec-adpcm" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dddc50e2bbea4cfe027441eece77c46b9f319748605ab8f3443350129ddd07f" +checksum = "445932ecb0c59362fde9c082dd63a75380650c5077fdb8b80b8cac850442a74c" dependencies = [ "log", "symphonia-core", @@ -3911,19 +4005,20 @@ dependencies = [ [[package]] name = "symphonia-codec-alac" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8413fa754942ac16a73634c9dfd1500ed5c61430956b33728567f667fdd393ab" +checksum = "920a78f96f3cf62932d0c497959c0cc2100ef36b03c7ca1417b260f432a482d8" dependencies = [ "log", + "symphonia-common", "symphonia-core", ] [[package]] name = "symphonia-codec-pcm" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e89d716c01541ad3ebe7c91ce4c8d38a7cf266a3f7b2f090b108fb0cb031d95" +checksum = "e04ba75686acbe43542fdd374571195f0530c0b7785ca25cc6840e9c6c4b6eea" dependencies = [ "log", "symphonia-core", @@ -3931,82 +4026,92 @@ dependencies = [ [[package]] name = "symphonia-codec-vorbis" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f025837c309cd69ffef572750b4a2257b59552c5399a5e49707cc5b1b85d1c73" +checksum = "73d90b4fcf796137cc683c538282804ff9629f8ad9dbfd881fcbba331ac4e986" +dependencies = [ + "log", + "symphonia-common", + "symphonia-core", +] + +[[package]] +name = "symphonia-common" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acc3fcc18ec9b8cdd48614e259c4cf0d27b71d41e5d9b120b42c5adab12d7c4" dependencies = [ "log", "symphonia-core", - "symphonia-utils-xiph", + "symphonia-metadata", ] [[package]] name = "symphonia-core" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea00cc4f79b7f6bb7ff87eddc065a1066f3a43fe1875979056672c9ef948c2af" +checksum = "01c412864d599d4750d0c3d684d7e093ec05e5309681ef5252cc1096a437f6e0" dependencies = [ - "arrayvec", - "bitflags 1.3.2", + "bitflags 2.13.2", "bytemuck", "lazy_static", "log", + "num-complex", + "smallvec", ] [[package]] name = "symphonia-format-caf" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8faf379316b6b6e6bbc274d00e7a592e0d63ff1a7e182ce8ba25e24edd3d096" +checksum = "ab64327ee1920531c5bf86cf7e9cd1a599d57013ddc30691da62683cefc65191" dependencies = [ "log", + "symphonia-common", "symphonia-core", - "symphonia-metadata", ] [[package]] name = "symphonia-format-isomp4" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243739585d11f81daf8dac8d9f3d18cc7898f6c09a259675fc364b382c30e0a5" +checksum = "0e681a70e1870d34e02abf1dbc51e4267c3f1827801474e8870be8c689fc4dc3" dependencies = [ - "encoding_rs", "log", + "symphonia-common", "symphonia-core", "symphonia-metadata", - "symphonia-utils-xiph", ] [[package]] name = "symphonia-format-mkv" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122d786d2c43a49beb6f397551b4a050d8229eaa54c7ddf9ee4b98899b8742d0" +checksum = "d015c5c0558864665894b3f4cbd95e10abb01b9c868e751c72670f326a56360e" dependencies = [ "lazy_static", "log", + "symphonia-common", "symphonia-core", - "symphonia-metadata", - "symphonia-utils-xiph", ] [[package]] name = "symphonia-format-ogg" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b4955c67c1ed3aa8ae8428d04ca8397fbef6a19b2b051e73b5da8b1435639cb" +checksum = "0b5495e7f7e3c7035328d82b6d6e377eef289bb0c4105bdeb557fc93a833f994" dependencies = [ "log", + "symphonia-common", "symphonia-core", "symphonia-metadata", - "symphonia-utils-xiph", ] [[package]] name = "symphonia-format-riff" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2d7c3df0e7d94efb68401d81906eae73c02b40d5ec1a141962c592d0f11a96f" +checksum = "1ff70929083a8c1a5f6cd7c904b6071c7914ad04739b510c2f7239dfc9b7dabe" dependencies = [ "extended", "log", @@ -4016,26 +4121,17 @@ dependencies = [ [[package]] name = "symphonia-metadata" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36306ff42b9ffe6e5afc99d49e121e0bd62fe79b9db7b9681d48e29fa19e6b16" +checksum = "83713a97705d77bdef7cdbc0768fd6e5a54e4cd7e48d60a806ae85639e2c87c6" dependencies = [ - "encoding_rs", "lazy_static", "log", + "regex-lite", + "smallvec", "symphonia-core", ] -[[package]] -name = "symphonia-utils-xiph" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27c85ab799a338446b68eec77abf42e1a6f1bb490656e121c6e27bfbab9f16" -dependencies = [ - "symphonia-core", - "symphonia-metadata", -] - [[package]] name = "syn" version = "2.0.119" @@ -4221,7 +4317,6 @@ version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -4257,6 +4352,17 @@ dependencies = [ "strength_reduce", ] +[[package]] +name = "tree_magic_mini" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8765b90061cba6c22b5831f675da109ae5561588290f9fa2317adab2714d5a6" +dependencies = [ + "memchr", + "nom 8.0.0", + "petgraph", +] + [[package]] name = "type-map" version = "0.5.1" @@ -4301,6 +4407,12 @@ version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954" +[[package]] +name = "unicode-properties" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" + [[package]] name = "unicode-segmentation" version = "1.13.3" @@ -4357,6 +4469,21 @@ dependencies = [ "thiserror 2.0.21", ] +[[package]] +name = "vello_common" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb2141a2bca6e6d598e471fd4d1d7eed8e020aad6a28187edda07f091a325dd" +dependencies = [ + "bytemuck", + "fearless_simd", + "guillotiere", + "log", + "peniko", + "smallvec", + "thiserror 2.0.21", +] + [[package]] name = "vello_cpu" version = "0.1.0" @@ -4364,9 +4491,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac7349e1f55f6b801c7c277958df4ea53e7f20f21e8014910ad888b2ecda93ea" dependencies = [ "bytemuck", - "glifo", + "glifo 0.2.0", "hashbrown 0.17.1", - "vello_common", + "vello_common 0.1.0", +] + +[[package]] +name = "vello_cpu" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace506ca414548966fcf3c283be94eb1cad8254de488791ee698bf438b0890a9" +dependencies = [ + "bytemuck", + "glifo 0.3.0", + "hashbrown 0.17.1", + "vello_common 0.2.0", ] [[package]] @@ -4528,32 +4667,6 @@ dependencies = [ "wayland-scanner", ] -[[package]] -name = "wayland-protocols-experimental" -version = "20250721.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1" -dependencies = [ - "bitflags 2.13.2", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "wayland-scanner", -] - -[[package]] -name = "wayland-protocols-misc" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e9567599ef23e09b8dad6e429e5738d4509dfc46b3b21f32841a304d16b29c8" -dependencies = [ - "bitflags 2.13.2", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "wayland-scanner", -] - [[package]] name = "wayland-protocols-plasma" version = "0.3.12" @@ -4629,7 +4742,7 @@ version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62c35be770821a214dbc362fc26908c853e776c0004294d0b10b8a6bad582f94" dependencies = [ - "jni 0.22.4", + "jni", "log", "ndk-context", "objc2 0.6.5", @@ -4768,7 +4881,7 @@ dependencies = [ "js-sys", "khronos-egl", "libc", - "libloading", + "libloading 0.8.9", "log", "naga", "naga-types", @@ -4797,9 +4910,9 @@ dependencies = [ "web-sys", "wgpu-naga-bridge", "wgpu-types", - "windows 0.62.2", - "windows-core 0.62.2", - "windows-result 0.4.1", + "windows", + "windows-core", + "windows-result", ] [[package]] @@ -4837,16 +4950,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "windows" -version = "0.54.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" -dependencies = [ - "windows-core 0.54.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows" version = "0.62.2" @@ -4854,7 +4957,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" dependencies = [ "windows-collections", - "windows-core 0.62.2", + "windows-core", "windows-future", "windows-numerics", ] @@ -4865,17 +4968,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" dependencies = [ - "windows-core 0.62.2", -] - -[[package]] -name = "windows-core" -version = "0.54.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" -dependencies = [ - "windows-result 0.1.2", - "windows-targets 0.52.6", + "windows-core", ] [[package]] @@ -4887,7 +4980,7 @@ dependencies = [ "windows-implement", "windows-interface", "windows-link", - "windows-result 0.4.1", + "windows-result", "windows-strings", ] @@ -4897,7 +4990,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" dependencies = [ - "windows-core 0.62.2", + "windows-core", "windows-link", "windows-threading", ] @@ -4936,19 +5029,10 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" dependencies = [ - "windows-core 0.62.2", + "windows-core", "windows-link", ] -[[package]] -name = "windows-result" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-result" version = "0.4.1" @@ -4967,15 +5051,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -5012,21 +5087,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -5069,12 +5129,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -5087,12 +5141,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -5105,12 +5153,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -5135,12 +5177,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -5153,12 +5189,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -5171,12 +5201,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -5189,12 +5213,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -5219,7 +5237,7 @@ dependencies = [ "bitflags 2.13.2", "block2 0.5.1", "bytemuck", - "calloop 0.13.0", + "calloop", "cfg_aliases", "concurrent-queue", "core-foundation", @@ -5240,7 +5258,7 @@ dependencies = [ "raw-window-handle", "redox_syscall 0.4.1", "rustix 0.38.44", - "smithay-client-toolkit 0.19.2", + "smithay-client-toolkit", "smol_str", "tracing", "unicode-segmentation", @@ -5269,9 +5287,9 @@ dependencies = [ [[package]] name = "winresource" -version = "0.1.31" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0986a8b1d586b7d3e4fe3d9ea39fb451ae22869dcea4aa109d287a374d866087" +checksum = "a2bc6d36480b829272a5bdabf08e6dca8004761c1da7e35efef800540c66221b" dependencies = [ "toml", "version_check", @@ -5283,6 +5301,24 @@ version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" +[[package]] +name = "wl-clipboard-rs" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d7888ccd4896447b2d14d3a9350a85df2aeb6f181e2e7a31349d104ac46cac1" +dependencies = [ + "libc", + "log", + "os_pipe", + "rustix 1.1.5", + "thiserror 2.0.21", + "tree_magic_mini", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-wlr", +] + [[package]] name = "writeable" version = "0.6.4" @@ -5309,7 +5345,7 @@ dependencies = [ "as-raw-xcb-connection", "gethostname", "libc", - "libloading", + "libloading 0.8.9", "once_cell", "rustix 1.1.5", "x11rb-protocol", @@ -5564,9 +5600,9 @@ dependencies = [ [[package]] name = "zip" -version = "9.0.1" +version = "9.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e49ade69c4434c55dd651d36fe05f38e0578db948b773cbc01c48c4b5e560692" +checksum = "6e04976da629d3b04204ba8105b396a193af8ad2583b2c79bfb3ab8af580a11e" dependencies = [ "crc32fast", "indexmap", diff --git a/Cargo.toml b/Cargo.toml index 6668c3f7..146f58d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,20 +36,20 @@ rayon = "1" proptest = "1" # audio -symphonia = { version = "0.5", default-features = false, features = ["mp3", "flac", "vorbis", "ogg", "wav", "pcm", "aiff", "isomp4", "aac", "alac", "adpcm", "caf", "mkv"] } +symphonia = { version = "0.6", default-features = false, features = ["mp3", "flac", "vorbis", "ogg", "wav", "pcm", "aiff", "isomp4", "aac", "alac", "adpcm", "caf", "mkv"] } rustfft = "6" # Session bundles (one file holding a session and its audio, for the browser): stored entries only. zip = { version = "9", default-features = false } -cpal = "0.16" +cpal = "0.18" # Which file paths stored in a session may be opened. craft-docpath = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "82a24b006eefa036b43c09a67fb9a3fbd17ffe63" } # ui craft-ui = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "5c874d51eeb3de70e735c8ce87a59a7dedd47675", features = ["serde"] } -egui = "0.36" -eframe = { version = "0.36", default-features = false } -egui_kittest = "0.36" +egui = "0.37" +eframe = { version = "0.37", default-features = false } +egui_kittest = "0.37" rfd = "0.17" image = { version = "0.25", default-features = false, features = ["png"] } diff --git a/apps/soundcraft/Cargo.toml b/apps/soundcraft/Cargo.toml index 0f30c7a9..634bf218 100644 --- a/apps/soundcraft/Cargo.toml +++ b/apps/soundcraft/Cargo.toml @@ -31,6 +31,6 @@ rfd.workspace = true winresource = "0.1" [target.'cfg(target_os = "macos")'.dependencies] -muda = { version = "0.19.2", default-features = false } +muda = { version = "0.21.2", default-features = false } objc2 = "0.6" objc2-app-kit = { version = "0.3", default-features = false, features = ["NSApplication", "NSResponder"] } diff --git a/crates/clap-host/Cargo.toml b/crates/clap-host/Cargo.toml index e08bbdfa..96c7de7c 100644 --- a/crates/clap-host/Cargo.toml +++ b/crates/clap-host/Cargo.toml @@ -23,7 +23,7 @@ harness = false [target.'cfg(not(target_arch = "wasm32"))'.dependencies] clap-sys = "0.5" -libloading = "0.8" +libloading = "0.9" # The isolated unsafe helper crate (craftrules never-crash): this crate opts out of the workspace # `unsafe_code = "forbid"` for itself only. It denies unsafe everywhere and allows it solely on the diff --git a/crates/vst3-host/Cargo.toml b/crates/vst3-host/Cargo.toml index 1c1583a6..00ace628 100644 --- a/crates/vst3-host/Cargo.toml +++ b/crates/vst3-host/Cargo.toml @@ -15,7 +15,7 @@ log.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies] vst3 = "0.3" -libloading = "0.8" +libloading = "0.9" # The macOS editor host window (an NSWindow whose content view the plugin's IPlugView attaches to). [target.'cfg(target_os = "macos")'.dependencies] From 44067c8779eb364b10efd0ca59c28beff10c4280 Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Fri, 23 Oct 2026 09:29:32 -0400 Subject: [PATCH 02/12] style: normalize the prores DCT basis literals (SonarCloud rust:S7454) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 0.490_392_64 and 0.49039264 are the same f32 value; the underscored form reads as a numeric suffix to the analyzer. 🤖 Generated with Codebuff Co-Authored-By: Codebuff --- crates/video/src/prores/tables.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/video/src/prores/tables.rs b/crates/video/src/prores/tables.rs index a395e906..975e9d3f 100644 --- a/crates/video/src/prores/tables.rs +++ b/crates/video/src/prores/tables.rs @@ -88,13 +88,13 @@ pub(crate) fn qscale(qidx: u8) -> u32 { /// Orthonormal 1-D DCT-II basis: `BASIS[u][x] = C(u)/2 · cos((2x+1)uÏ€/16)`, `C(0) = 1/√2`. pub(crate) const BASIS: [[f32; 8]; 8] = [ [0.353_553_39, 0.353_553_39, 0.353_553_39, 0.353_553_39, 0.353_553_39, 0.353_553_39, 0.353_553_39, 0.353_553_39], - [0.490_392_64, 0.415_734_8, 0.277_785_12, 0.097_545_16, -0.097_545_16, -0.277_785_12, -0.415_734_8, -0.490_392_64], + [0.49039264, 0.415_734_8, 0.277_785_12, 0.097_545_16, -0.097_545_16, -0.277_785_12, -0.415_734_8, -0.49039264], [0.461_939_77, 0.191_341_72, -0.191_341_72, -0.461_939_77, -0.461_939_77, -0.191_341_72, 0.191_341_72, 0.461_939_77], - [0.415_734_8, -0.097_545_16, -0.490_392_64, -0.277_785_12, 0.277_785_12, 0.490_392_64, 0.097_545_16, -0.415_734_8], + [0.415_734_8, -0.097_545_16, -0.49039264, -0.277_785_12, 0.277_785_12, 0.49039264, 0.097_545_16, -0.415_734_8], [0.353_553_39, -0.353_553_39, -0.353_553_39, 0.353_553_39, 0.353_553_39, -0.353_553_39, -0.353_553_39, 0.353_553_39], - [0.277_785_12, -0.490_392_64, 0.097_545_16, 0.415_734_8, -0.415_734_8, -0.097_545_16, 0.490_392_64, -0.277_785_12], + [0.277_785_12, -0.49039264, 0.097_545_16, 0.415_734_8, -0.415_734_8, -0.097_545_16, 0.49039264, -0.277_785_12], [0.191_341_72, -0.461_939_77, 0.461_939_77, -0.191_341_72, -0.191_341_72, 0.461_939_77, -0.461_939_77, 0.191_341_72], - [0.097_545_16, -0.277_785_12, 0.415_734_8, -0.490_392_64, 0.490_392_64, -0.415_734_8, 0.277_785_12, -0.097_545_16], + [0.097_545_16, -0.277_785_12, 0.415_734_8, -0.49039264, 0.49039264, -0.415_734_8, 0.277_785_12, -0.097_545_16], ]; #[cfg(test)] From f431791c26fcd5971c9b503f82ae299a7cccf56b Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Fri, 23 Oct 2026 11:17:44 -0400 Subject: [PATCH 03/12] style: use [[ ]], named locals and explicit returns in packaging scripts (SonarCloud S7688/S7679/S7682) (#950) Bash scripts under packaging/ now use [[ ]] for tests, copy positional parameters into named locals inside functions, and end env.sh helpers with an explicit return (bare, so each keeps its exit status). shellcheck clean. --- packaging/env.sh | 15 ++++++++---- packaging/freebsd/package.sh | 4 ++-- packaging/icons.sh | 5 +++- packaging/linux/flatpak-bundle.sh | 8 +++---- packaging/linux/package.sh | 21 +++++++++-------- packaging/macos/import-cert.sh | 6 ++--- packaging/macos/package.sh | 38 +++++++++++++++---------------- packaging/web/package.sh | 4 ++-- 8 files changed, 56 insertions(+), 45 deletions(-) diff --git a/packaging/env.sh b/packaging/env.sh index cb4bcdb6..abb99b52 100644 --- a/packaging/env.sh +++ b/packaging/env.sh @@ -19,10 +19,11 @@ workspace_version() { /^\[/ { in_pkg = ($0 == "[workspace.package]") ; next } in_pkg && $1 == "version" { gsub(/[" ]/, "", $3); print $3; exit } ' "$ROOT/Cargo.toml" + return } VERSION="${SOUNDCRAFT_VERSION:-$(workspace_version)}" -if [ -z "$VERSION" ]; then +if [[ -z "$VERSION" ]]; then echo "error: could not read [workspace.package] version from $ROOT/Cargo.toml" >&2 exit 1 fi @@ -32,7 +33,7 @@ DIST="${DIST:-$ROOT/dist/release}" mkdir -p "$DIST" export DIST -if [ -z "${SOUNDCRAFT_BUILD_SHA:-}" ]; then +if [[ -z "${SOUNDCRAFT_BUILD_SHA:-}" ]]; then SOUNDCRAFT_BUILD_SHA="$(git -C "$ROOT" rev-parse HEAD 2>/dev/null || true)" fi export SOUNDCRAFT_BUILD_SHA @@ -41,19 +42,23 @@ export CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-$ROOT/target}" # Emit a GitHub Actions warning (plain stderr outside Actions). warn() { - if [ -n "${GITHUB_ACTIONS:-}" ]; then echo "::warning::$*"; else echo "warning: $*" >&2; fi + if [[ -n "${GITHUB_ACTIONS:-}" ]]; then echo "::warning::$*"; else echo "warning: $*" >&2; fi + return } # Copy licence and readme files that exist into a package directory. copy_docs() { local dest="$1" f for f in README.md LICENSE LICENSE-MIT LICENSE-APACHE NOTICE ATTRIBUTION.md COPYRIGHT; do - if [ -f "$ROOT/$f" ]; then cp "$ROOT/$f" "$dest/"; fi + if [[ -f "$ROOT/$f" ]]; then cp "$ROOT/$f" "$dest/"; fi done + return } # Portable SHA-256 of a file (prints just the hash). sha256() { - if command -v sha256sum >/dev/null; then sha256sum "$1" | cut -d' ' -f1; else shasum -a 256 "$1" | cut -d' ' -f1; fi + local file="$1" + if command -v sha256sum >/dev/null; then sha256sum "$file" | cut -d' ' -f1; else shasum -a 256 "$file" | cut -d' ' -f1; fi + return } diff --git a/packaging/freebsd/package.sh b/packaging/freebsd/package.sh index 71af80c3..61679c8e 100755 --- a/packaging/freebsd/package.sh +++ b/packaging/freebsd/package.sh @@ -20,7 +20,7 @@ LINUX="$ROOT/packaging/linux" APP_ID=ai.storyteller.soundcraft SKIP_BUILD=0 -while [ $# -gt 0 ]; do +while [[ $# -gt 0 ]]; do case "$1" in --skip-build) SKIP_BUILD=1; shift ;; -h | --help) sed -n '2,15p' "$0"; exit 0 ;; @@ -38,7 +38,7 @@ BASENAME="soundcraft-$VERSION-freebsd-$ARCH" echo "==> SoundCraft $VERSION for FreeBSD $ARCH" -if [ "$SKIP_BUILD" = 0 ]; then +if [[ "$SKIP_BUILD" = 0 ]]; then (cd "$ROOT" && cargo build --release --locked -p soundcraft -p soundcraft-cli) fi BIN="$CARGO_TARGET_DIR/release" diff --git a/packaging/icons.sh b/packaging/icons.sh index 7308e799..d32e610f 100755 --- a/packaging/icons.sh +++ b/packaging/icons.sh @@ -21,7 +21,10 @@ MAC="$TMP/mac.svg" sed 's/viewBox="0 0 512 512"/viewBox="-62.136 -62.136 636.272 636.272"/' "$SVG" >"$MAC" grep -q 'viewBox="-62.136' "$MAC" || { echo "error: expected viewBox=\"0 0 512 512\" in $SVG" >&2; exit 1; } -render() { resvg -w "$2" -h "$2" "$1" "$3" &2; exit 1; } +[[ -f "$TARBALL" ]] || { echo "error: $TARBALL not found (run packaging/linux/package.sh --formats tar first)" >&2; exit 1; } for tool in flatpak flatpak-builder; do command -v "$tool" >/dev/null || { echo "error: $tool not found (apt install flatpak flatpak-builder librsvg2-common)" >&2; exit 1; } done @@ -49,7 +49,7 @@ WORK="$CARGO_TARGET_DIR/flatpak-bundle" rm -rf "$WORK" mkdir -p "$WORK/stage" tar -xzf "$TARBALL" -C "$WORK/stage" --strip-components=1 -[ -x "$WORK/stage/bin/soundcraft" ] || { echo "error: $TARBALL has no bin/soundcraft" >&2; exit 1; } +[[ -x "$WORK/stage/bin/soundcraft" ]] || { echo "error: $TARBALL has no bin/soundcraft" >&2; exit 1; } cp "$HERE/flatpak/$APP_ID.bundle.yml" "$WORK/$APP_ID.yml" flatpak remote-add --user --if-not-exists flathub "$FLATHUB" @@ -62,7 +62,7 @@ OUT="$DIST/$BASENAME.flatpak" flatpak build-bundle --runtime-repo="$FLATHUB" "$WORK/repo" "$OUT" "$APP_ID" stable echo "wrote $OUT" -if [ "$TEST" = 1 ]; then +if [[ "$TEST" = 1 ]]; then flatpak install --user -y --noninteractive --reinstall "$OUT" flatpak info --user "$APP_ID" flatpak run --command=soundcraft-cli "$APP_ID" --version diff --git a/packaging/linux/package.sh b/packaging/linux/package.sh index 8f7c9160..fb677dd7 100755 --- a/packaging/linux/package.sh +++ b/packaging/linux/package.sh @@ -25,7 +25,7 @@ APP_ID=ai.storyteller.soundcraft SKIP_BUILD=0 FORMATS="appimage deb rpm tar" -while [ $# -gt 0 ]; do +while [[ $# -gt 0 ]]; do case "$1" in --skip-build) SKIP_BUILD=1; shift ;; --formats) FORMATS="$2"; shift 2 ;; @@ -46,8 +46,8 @@ BASENAME="soundcraft-$VERSION-linux-$ARCH" echo "==> SoundCraft $VERSION for Linux $ARCH ($FORMATS)" -if [ "$SKIP_BUILD" = 0 ]; then - if [ -n "${CROSS_TARGET:-}" ]; then +if [[ "$SKIP_BUILD" = 0 ]]; then + if [[ -n "${CROSS_TARGET:-}" ]]; then (cd "$ROOT" && cargo build --release --locked -p soundcraft -p soundcraft-cli --target "$CROSS_TARGET") else (cd "$ROOT" && cargo build --release --locked -p soundcraft -p soundcraft-cli) @@ -79,7 +79,10 @@ if command -v appstreamcli >/dev/null; then appstreamcli validate --no-net --explain "$STAGE/usr/share/metainfo/$APP_ID.metainfo.xml" fi -has() { case " $FORMATS " in *" $1 "*) return 0 ;; *) return 1 ;; esac; } +has() { + local format="$1" + case " $FORMATS " in *" $format "*) return 0 ;; *) return 1 ;; esac +} # ---- .tar.gz ------------------------------------------------------------------------------------ if has tar; then @@ -112,9 +115,9 @@ if has appimage; then ln -s "$APP_ID.png" "$APPDIR/.DirIcon" TOOL="${APPIMAGETOOL:-$(command -v appimagetool || true)}" - if [ -z "$TOOL" ]; then + if [[ -z "$TOOL" ]]; then TOOL="$CARGO_TARGET_DIR/appimagetool-$ARCH.AppImage" - if [ ! -x "$TOOL" ]; then + if [[ ! -x "$TOOL" ]]; then curl --proto '=https' -fsSL -o "$TOOL" "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-$ARCH.AppImage" chmod +x "$TOOL" fi @@ -137,16 +140,16 @@ if has appimage; then # package) appimagetool also writes the .zsync, into its working directory, hence the cd. (cd "$DIST" && ARCH="$ARCH" APPIMAGE_EXTRACT_AND_RUN=1 "$TOOL" --no-appstream -u "$UPDATE_INFO" "$APPDIR" "$OUT") echo "wrote $OUT" - if [ -s "$OUT.zsync" ]; then + if [[ -s "$OUT.zsync" ]]; then echo "wrote $OUT.zsync" else warn "zsyncmake not found, so $OUT.zsync was not written; AppImage delta updates need it" fi fi -if [ -z "${CROSS_TARGET:-}" ]; then +if [[ -z "${CROSS_TARGET:-}" ]]; then "$STAGE/usr/bin/soundcraft-cli" --version -elif [ -n "${EMULATOR:-}" ]; then +elif [[ -n "${EMULATOR:-}" ]]; then "$EMULATOR" "$STAGE/usr/bin/soundcraft-cli" --version fi echo "==> done" diff --git a/packaging/macos/import-cert.sh b/packaging/macos/import-cert.sh index 17a1a7f8..91e422f5 100755 --- a/packaging/macos/import-cert.sh +++ b/packaging/macos/import-cert.sh @@ -12,7 +12,7 @@ umask 077 # shellcheck source=../env.sh . "$(dirname "${BASH_SOURCE[0]}")/../env.sh" -if [ -z "${APPLE_CERTIFICATE:-}" ]; then +if [[ -z "${APPLE_CERTIFICATE:-}" ]]; then warn "APPLE_CERTIFICATE is not set: macOS builds will be ad-hoc signed and not notarized" exit 0 fi @@ -36,13 +36,13 @@ security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KC_PASS" security list-keychains -d user -s "$KEYCHAIN" $(security list-keychains -d user | tr -d '"') IDENTITY="$(security find-identity -v -p codesigning "$KEYCHAIN" | awk '/Developer ID Application/ { print $2; exit }')" -if [ -z "$IDENTITY" ]; then +if [[ -z "$IDENTITY" ]]; then security find-identity -v -p codesigning "$KEYCHAIN" >&2 echo "error: no 'Developer ID Application' identity in APPLE_CERTIFICATE" >&2 exit 1 fi echo "Imported signing identity $IDENTITY" -if [ -n "${GITHUB_ENV:-}" ]; then +if [[ -n "${GITHUB_ENV:-}" ]]; then { echo "MACOS_SIGN_IDENTITY=$IDENTITY"; echo "MACOS_KEYCHAIN=$KEYCHAIN"; } >>"$GITHUB_ENV" else echo "MACOS_SIGN_IDENTITY=$IDENTITY" diff --git a/packaging/macos/package.sh b/packaging/macos/package.sh index ff7aba25..dd3676d9 100755 --- a/packaging/macos/package.sh +++ b/packaging/macos/package.sh @@ -19,7 +19,7 @@ HERE="$ROOT/packaging/macos" ARCH=universal SKIP_BUILD=0 -while [ $# -gt 0 ]; do +while [[ $# -gt 0 ]]; do case "$1" in --arch) ARCH="$2"; shift 2 ;; --skip-build) SKIP_BUILD=1; shift ;; @@ -44,9 +44,9 @@ DMG="$DIST/soundcraft-$VERSION-macos-$ARCH.dmg" CLI_ZIP="$DIST/soundcraft-cli-$VERSION-macos-$ARCH.zip" NOTARIZE=0 -if [ "$IDENTITY" = "-" ]; then +if [[ "$IDENTITY" = "-" ]]; then warn "macOS: ad-hoc signing (no MACOS_SIGN_IDENTITY); artifacts are not notarized" -elif [ -n "${APPLE_ID:-}" ] && [ -n "${APPLE_PASSWORD:-}" ] && [ -n "${APPLE_TEAM_ID:-}" ]; then +elif [[ -n "${APPLE_ID:-}" ]] && [[ -n "${APPLE_PASSWORD:-}" ]] && [[ -n "${APPLE_TEAM_ID:-}" ]]; then NOTARIZE=1 else warn "macOS: APPLE_ID / APPLE_PASSWORD / APPLE_TEAM_ID incomplete; signed but not notarized" @@ -55,7 +55,7 @@ fi echo "==> SoundCraft $VERSION for macOS ($ARCH), identity: $IDENTITY, notarize: $NOTARIZE" # ---- build ------------------------------------------------------------------------------------- -if [ "$SKIP_BUILD" = 0 ]; then +if [[ "$SKIP_BUILD" = 0 ]]; then args=() for t in "${TARGETS[@]}"; do args+=(--target "$t"); done (cd "$ROOT" && cargo build --release --locked -p soundcraft -p soundcraft-cli "${args[@]}") @@ -74,22 +74,22 @@ done sign() { # Hardened runtime + secure timestamp for a real identity; ad-hoc can't be timestamped. local ts=(--timestamp) - [ "$IDENTITY" = "-" ] && ts=(--timestamp=none) + [[ "$IDENTITY" = "-" ]] && ts=(--timestamp=none) local kc=() - [ -n "${MACOS_KEYCHAIN:-}" ] && kc=(--keychain "$MACOS_KEYCHAIN") + [[ -n "${MACOS_KEYCHAIN:-}" ]] && kc=(--keychain "$MACOS_KEYCHAIN") codesign --force --sign "$IDENTITY" ${kc[@]+"${kc[@]}"} "${ts[@]}" "$@" } # stapler fetches the ticket from Apple's CloudKit, which sometimes drops the connection ("The # network connection was lost", Error 68). Stapling and validating are idempotent, so retry. stapler_retry() { - local attempt + local action="$1" attempt for attempt in 1 2 3 4 5; do if xcrun stapler "$@"; then return 0 fi - [ "$attempt" = 5 ] && { echo "stapler $1 failed 5 times" >&2; return 1; } - warn "stapler $1 failed (attempt $attempt of 5); retrying in $((attempt * 15))s" + [[ "$attempt" = 5 ]] && { echo "stapler $action failed 5 times" >&2; return 1; } + warn "stapler $action failed (attempt $attempt of 5); retrying in $((attempt * 15))s" sleep $((attempt * 15)) done } @@ -102,8 +102,8 @@ notarize() { echo "$out" id="$(printf '%s' "$out" | plutil -extract id raw -o - - 2>/dev/null || true)" status="$(printf '%s' "$out" | plutil -extract status raw -o - - 2>/dev/null || true)" - if [ "$status" != "Accepted" ]; then - if [ -n "$id" ]; then + if [[ "$status" != "Accepted" ]]; then + if [[ -n "$id" ]]; then xcrun notarytool log "$id" --apple-id "$APPLE_ID" --password "$APPLE_PASSWORD" --team-id "$APPLE_TEAM_ID" || true fi echo "error: notarization of $(basename "$file") failed (status: ${status:-unknown})" >&2 @@ -133,7 +133,7 @@ sign --options runtime --entitlements "$HERE/entitlements.plist" "$APP/Contents/ sign --options runtime --entitlements "$HERE/entitlements.plist" "$APP" codesign --verify --strict --deep --verbose=2 "$APP" -if [ "$NOTARIZE" = 1 ]; then +if [[ "$NOTARIZE" = 1 ]]; then ditto -c -k --keepParent "$APP" "$WORK/SoundCraft-notarize.zip" notarize "$WORK/SoundCraft-notarize.zip" stapler_retry staple "$APP" @@ -166,7 +166,7 @@ hdiutil_retry() { if hdiutil "$@"; then return 0 fi - [ "$attempt" = 5 ] && { echo "hdiutil $what failed 5 times" >&2; return 1; } + [[ "$attempt" = 5 ]] && { echo "hdiutil $what failed 5 times" >&2; return 1; } warn "hdiutil $what failed (attempt $attempt of 5); retrying in $((attempt * 10))s" sleep $((attempt * 10)) done @@ -183,21 +183,21 @@ mkdir -p "$MOUNT" "$COPY" hdiutil_retry attach attach -nobrowse -readonly -noautoopen -mountpoint "$MOUNT" "$DMG" >/dev/null dmg_ok=1 codesign --verify --strict --deep --verbose=2 "$MOUNT/SoundCraft.app" || dmg_ok=0 -if [ "$dmg_ok" = 1 ]; then +if [[ "$dmg_ok" = 1 ]]; then { ditto "$MOUNT/SoundCraft.app" "$COPY/SoundCraft.app" && codesign --verify --strict --deep --verbose=2 "$COPY/SoundCraft.app"; } || dmg_ok=0 fi -if [ "$dmg_ok" = 1 ] && [ "$NOTARIZE" = 1 ]; then +if [[ "$dmg_ok" = 1 ]] && [[ "$NOTARIZE" = 1 ]]; then spctl --assess --type execute -vvv "$COPY/SoundCraft.app" || dmg_ok=0 fi -[ "$dmg_ok" = 1 ] || xattr -lr "$MOUNT/SoundCraft.app" >&2 || true +[[ "$dmg_ok" = 1 ]] || xattr -lr "$MOUNT/SoundCraft.app" >&2 || true hdiutil_retry detach detach "$MOUNT" >/dev/null || dmg_ok=0 rm -rf "$COPY" -if [ "$dmg_ok" != 1 ]; then +if [[ "$dmg_ok" != 1 ]]; then echo "error: SoundCraft.app in $(basename "$DMG") fails strict verification" >&2 exit 1 fi -if [ "$NOTARIZE" = 1 ]; then +if [[ "$NOTARIZE" = 1 ]]; then notarize "$DMG" stapler_retry staple "$DMG" stapler_retry validate "$DMG" @@ -215,7 +215,7 @@ codesign --verify --strict --verbose=2 "$CLI_DIR/soundcraft-cli" rm -f "$CLI_ZIP" ditto -c -k --keepParent "$CLI_DIR" "$CLI_ZIP" # A bare Mach-O can't carry a stapled ticket; Gatekeeper looks the notarization up online. -if [ "$NOTARIZE" = 1 ]; then notarize "$CLI_ZIP"; fi +if [[ "$NOTARIZE" = 1 ]]; then notarize "$CLI_ZIP"; fi "$WORK/bin/soundcraft-cli" --version echo "==> done" diff --git a/packaging/web/package.sh b/packaging/web/package.sh index a35fa9ef..5038508a 100755 --- a/packaging/web/package.sh +++ b/packaging/web/package.sh @@ -11,13 +11,13 @@ set -euo pipefail . "$(dirname "${BASH_SOURCE[0]}")/../env.sh" HERE="$ROOT/packaging/web" -if [ "${1:-}" != "--skip-build" ]; then +if [[ "${1:-}" != "--skip-build" ]]; then command -v trunk >/dev/null || { echo "error: trunk not found (cargo install trunk --locked)" >&2; exit 1; } (cd "$ROOT/apps/soundcraft-web" && trunk build --release) fi SITE="$ROOT/dist/web" -[ -f "$SITE/index.html" ] || { echo "error: $SITE/index.html missing; run without --skip-build" >&2; exit 1; } +[[ -f "$SITE/index.html" ]] || { echo "error: $SITE/index.html missing; run without --skip-build" >&2; exit 1; } # Paths must be relative so the site works under any prefix (public_url = "./" in Trunk.toml). if grep -Eq '(src|href)="/[^/]' "$SITE/index.html"; then echo "error: $SITE/index.html has root-absolute URLs; it would break when served from a sub-path" >&2 From d47abe484d4f9ef0cc9f8e872e0b3880a61a4588 Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Fri, 23 Oct 2026 11:35:06 -0400 Subject: [PATCH 04/12] Use chrono instead of hand-rolled UTC conversion (#844) * Use chrono for UTC/calendar conversion instead of hand-rolled copies Replaces the civil_from_days copy behind the desktop app's log timestamp with chrono as a workspace dependency. The output format is unchanged and pinned by the existing test. AGENTS.md now says time goes through chrono. Follow-up to storytold/cadcraft#1785 * Never panic logging on a clock outside chrono's range chrono's From unwraps internally, so a clock past year 262143 panicked inside the logger. timestamp now reads the duration since the epoch and goes through the fallible DateTime::from_timestamp, reading an out-of-range clock as the epoch, exactly as the pre-epoch clamp already did. The year is written with {:04} rather than %Y, which would prefix a '+' past year 9999. Tests cover the far-future and year-10000 cases. * Don't build an unrepresentable SystemTime in the far-future test UNIX_EPOCH + Duration::from_secs(u64::MAX / 2) overflows Windows' SystemTime and panicked before timestamp() ran, failing the Windows test job. Build it with checked_add and only assert where it exists. * time: preserve old out-of-range log timestamp (no epoch fallback) `DateTime::from_timestamp(...).unwrap_or_default()` silently reset any clock past chrono's year range to 1970-01-01, changing output the old hand-rolled `civil_from_days` converted exactly. Restore the old conversion domain with a cycle-based helper that routes the calendar through chrono (the calendar repeats every 400 years = 146_097 days, so month/day/time come from chrono and only the year is carried in i128, keeping every i64 exact). Keep the pre-1970 epoch clamp; u64 seconds past i64::MAX saturate rather than falling back. Pinned old outputs in the test (replacing the u64::MAX/2 -> epoch assertion): u64::MAX/2 s -> 292277026596-12-04T15:30:07.000Z 10_000_000_000_000 s -> 318857-05-20T17:46:40.000Z * Don't build a year-318857 SystemTime unchecked in the logging test Windows' SystemTime only reaches about year 30828, so UNIX_EPOCH + Duration::from_secs(10_000_000_000_000) panicked before timestamp() ran and failed the Windows test job. Build it with checked_add and assert where the platform can hold it. The year-10000 case fits everywhere and stays unconditional. * Name the 400-year range extension as the one exception to the time rule The rule said never to hand-roll calendar math, but the range fix (shifting by whole 400-year Gregorian cycles so dates past chrono's year range keep their old output) is cycle arithmetic, so the rule and the code disagreed. The rule now names that exception: the library still computes month, day and time, and only whole cycles are carried in the year. * Say the library computes month and day; time-of-day is plain arithmetic Narrows the range-extension wording: the date library computes the calendar fields, and splitting seconds into hours, minutes and seconds is ordinary arithmetic, which is what the code does. --- AGENTS.md | 1 + Cargo.lock | 10 ++++++ Cargo.toml | 2 ++ apps/soundcraft/Cargo.toml | 1 + apps/soundcraft/src/logging.rs | 58 +++++++++++++++++++++++----------- 5 files changed, 54 insertions(+), 18 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 4fdf3cb7..76338a2b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,6 +32,7 @@ People trust SoundCraft with their recordings; a crash loses takes. **This outra - The audio callback never blocks, allocates in steady state, or panics. - Every production crate root carries `#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]`. - `Engine::execute` catches escaped panics and restores the document. Every crash fix lands with a regression test. +- **Time goes through `chrono`** (workspace dependency). Never hand-roll UTC/calendar conversion (days-to-date algorithms, leap-year math); a crate owns only its output format string. The one exception is range extension: past the years chrono can represent, shift the date by whole 400-year Gregorian cycles (146,097 days, after which the calendar repeats), let chrono compute the month and day, and add the cycles back to the year (splitting seconds into hours, minutes and seconds is plain arithmetic). ## Architecture (layers enforced by `cargo xtask layers`) diff --git a/Cargo.lock b/Cargo.lock index d006e25c..eb578a28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -688,6 +688,15 @@ dependencies = [ "rand_core", ] +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "num-traits", +] + [[package]] name = "clang-sys" version = "1.9.1" @@ -3668,6 +3677,7 @@ dependencies = [ name = "soundcraft" version = "0.12.1" dependencies = [ + "chrono", "eframe", "egui 0.37.0", "log", diff --git a/Cargo.toml b/Cargo.toml index 146f58d8..b0fda5df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,8 @@ thiserror = "2" serde = { version = "1", features = ["derive", "rc"] } serde_json = { version = "1", features = ["float_roundtrip"] } log = "0.4" +# All date/time conversion goes through chrono (no hand-rolled calendar code beyond the 400-year range extension; AGENTS.md). +chrono = { version = "0.4.45", default-features = false, features = ["std"] } rayon = "1" proptest = "1" diff --git a/apps/soundcraft/Cargo.toml b/apps/soundcraft/Cargo.toml index 634bf218..5dd042b9 100644 --- a/apps/soundcraft/Cargo.toml +++ b/apps/soundcraft/Cargo.toml @@ -22,6 +22,7 @@ eframe = { workspace = true, features = ["default_fonts", "wgpu", "accesskit", " egui.workspace = true serde_json.workspace = true log.workspace = true +chrono.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies] rfd.workspace = true diff --git a/apps/soundcraft/src/logging.rs b/apps/soundcraft/src/logging.rs index 53be177b..8209f3c2 100644 --- a/apps/soundcraft/src/logging.rs +++ b/apps/soundcraft/src/logging.rs @@ -112,28 +112,36 @@ fn matches(name: &str, target: &str) -> bool { } /// `2026-10-08T07:59:17.728Z` (UTC, milliseconds). A clock before 1970 reads as the epoch. +/// A clock far past chrono's year range still converts (the calendar repeats every 400 years), so +/// logging never panics and never silently resets to the epoch for a convertible instant. pub fn timestamp(t: SystemTime) -> String { let since = t.duration_since(std::time::UNIX_EPOCH).unwrap_or_default(); - let secs = since.as_secs(); - let (days, rem) = (secs / 86_400, secs % 86_400); - let (y, m, d) = civil_from_days(days); - format!("{y:04}-{m:02}-{d:02}T{:02}:{:02}:{:02}.{:03}Z", rem / 3600, rem % 3600 / 60, rem % 60, since.subsec_millis()) + // Pre-1970 is clamped to the epoch by `unwrap_or_default` above. `as_secs` is `u64`; a value + // past `i64::MAX` seconds saturates rather than falling back to the epoch. + let secs = i64::try_from(since.as_secs()).unwrap_or(i64::MAX); + let (y, t) = utc_parts(secs, since.subsec_nanos()); + // `{:04}` rather than `%Y`, which writes a `+` before years past 9999. + format!("{y:04}-{}", t.format("%m-%dT%H:%M:%S%.3fZ")) } -/// Days since 1970-01-01 to a proleptic Gregorian (year, month, day) (Howard Hinnant's -/// `civil_from_days`, unsigned because `timestamp` clamps at the epoch; saturating so an absurd -/// clock can't overflow). -fn civil_from_days(days: u64) -> (u64, u64, u64) { - let z = days.saturating_add(719_468); - let era = z / 146_097; - let doe = z % 146_097; - let yoe = (doe - doe / 1460 + doe / 36_524 - doe / 146_096) / 365; - let doy = doe - (365 * yoe + yoe / 4 - yoe / 100); - let mp = (5 * doy + 2) / 153; - let d = doy - (153 * mp + 2) / 5 + 1; - let m = if mp < 10 { mp + 3 } else { mp - 9 }; - let y = era.saturating_mul(400).saturating_add(yoe).saturating_add(u64::from(m <= 2)); - (y, m, d) +/// Unix seconds (+ nanos) → (proleptic Gregorian year, the same wall-clock instant moved into +/// chrono's range by whole 400-year cycles). The calendar repeats every 400 years (146_097 days), +/// so month, day and time of day come from chrono and only the year is carried, which keeps every +/// `i64` exact. Replaces the old hand-rolled `civil_from_days`; chrono is the only calendar logic. +fn utc_parts(secs: i64, nanos: u32) -> (i64, chrono::NaiveDateTime) { + use chrono::Datelike; + let (days, sod) = (secs.div_euclid(86_400), secs.rem_euclid(86_400)); + let (cycles, rest) = (days.div_euclid(146_097), days.rem_euclid(146_097)); + // `rest + 719_163` (1970-01-01 is CE day 719_163) is always a valid CE day, and `sod` < 86_400. + let date = i32::try_from(rest + 719_163).ok().and_then(chrono::NaiveDate::from_num_days_from_ce_opt); + let time = u32::try_from(sod).ok().and_then(|s| chrono::NaiveTime::from_num_seconds_from_midnight_opt(s, nanos.min(999_999_999))); + match (date, time) { + (Some(d), Some(t)) => { + let y = i128::from(d.year()) + i128::from(cycles) * 400; + (i64::try_from(y).unwrap_or(if y < 0 { i64::MIN } else { i64::MAX }), d.and_time(t)) + } + _ => (1970, chrono::NaiveDateTime::default()), // unreachable + } } fn numbered(dir: &Path, n: usize) -> PathBuf { @@ -533,6 +541,20 @@ mod tests { assert_eq!(timestamp(UNIX_EPOCH + Duration::from_secs(4_107_542_400)), "2100-03-01T00:00:00.000Z"); // Before the epoch (a clock set wrong) clamps instead of panicking. assert_eq!(timestamp(UNIX_EPOCH - Duration::from_secs(5)), "1970-01-01T00:00:00.000Z"); + // Past chrono's year range still converts via the 400-year cycle (the old code did too, + // through `civil_from_days`): never a silent reset to the epoch. Platform-safe: Windows' + // SystemTime can't hold this instant, so it is only checked where it exists. + // `u64::MAX / 2` seconds == `i64::MAX` seconds → year 292277026596 (old output). + if let Some(far) = UNIX_EPOCH.checked_add(Duration::from_secs(u64::MAX / 2)) { + assert_eq!(timestamp(far), "292277026596-12-04T15:30:07.000Z"); + } + // Years past 9999 keep the plain zero-padded year (no `+`). + assert_eq!(timestamp(UNIX_EPOCH + Duration::from_secs(253_402_300_800)), "10000-01-01T00:00:00.000Z"); + // 10_000_000_000_000 s is far beyond chrono's range; old output is year 318857. + // Windows' SystemTime holds only up to about year 30828, so this is checked where it fits. + if let Some(far) = UNIX_EPOCH.checked_add(Duration::from_secs(10_000_000_000_000)) { + assert_eq!(timestamp(far), "318857-05-20T17:46:40.000Z"); + } } #[test] From f0d5acb800c65bcd2a9d637cd1726cf6597cb629 Mon Sep 17 00:00:00 2001 From: Superior <57851423+wayward-L@users.noreply.github.com> Date: Sat, 24 Oct 2026 00:15:56 +0800 Subject: [PATCH 05/12] Deps: fix the workspace build after the dependency bump (#954) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Audio: migrate file decoding to symphonia 0.6 Upstream bumped symphonia to 0.6 without adapting crates/audio-io, so `soundcraft-audio-io` no longer built: the probe module, codec parameters and decoded-buffer APIs all moved. - Probe with `default::get_probe().probe(&Hint, …)`. The 0.5 prober's detection-only path is gone, so scan for the container marker ourselves before instantiating a reader, and keep running the existing size checks (CAF/MP4/FLAC/WAV) on the first marker — the damaged-file regression tests depend on those checks firing before any allocation. - Skip leading ID3v2 tags with `ReadBytes::ignore_bytes` as before. - Read `Track::codec_params` (now an `Option`), decode through `make_audio_decoder` and `GenericAudioBufferRef::copy_to_vecs_planar`, and read the new public `Packet` fields. - Keep the per-packet frame limit and the sample sanitisation. * Playback: migrate the audio device layer to cpal 0.18 - Sample rates are plain `u32` now: drop `.0` and the `cpal::SampleRate` constructor. - One `cpal::Error` replaces the build/stream/play/pause error types; detect `DeviceNotAvailable` through `error.kind()`. - Stream builders take `StreamConfig` by value, and device names come from `Device::description()` (the deprecated `name()` is gone). - The test `Fake` implements the new `StreamTrait::{buffer_size, now}`. * Deps: pin egui back to 0.36 until craft-ui supports 0.37 The dependabot group bump (#767) moved the workspace to egui, eframe and egui_kittest 0.37, but the pinned `craft-ui` revision still builds against egui 0.36, so the graph carries two incompatible egui versions and `soundcraft-ui-egui` does not compile. craft-libs#199 updates craft-ui to 0.37 and is still open; pin 0.36 and refresh the lockfile meanwhile. The other version bumps from #767 stay. Re-land egui 0.37 together with the craft-ui update. * Audio: refuse a size-checked container the probe did not validate The dependency-migration review showed the preflight size checks can validate one container marker while symphonia's probe selects a different container, so a misleading marker could skip the FLAC/MP4/CAF/WAVE checks. Probe 0.6 reports the selected format only through the reader it returns, so `open()` now records which container's checks ran (including the RF64/BW64 RIFF forms) and refuses the stream when a size-checked reader was chosen without its checks, before any packet is decoded. Markers found in audio data of non-container streams (MP3, Ogg, AIFF) do not reject valid files. Adds a regression test with a fake RIFF prefix followed by a valid FLAC stream, and doc comments for the functions touched by the dependency migration (the review's docstring-coverage warning). --- Cargo.lock | 477 +++++++++++------------------ Cargo.toml | 7 +- crates/audio-io/src/symph.rs | 253 +++++++++------ crates/audio-io/tests/malformed.rs | 16 + crates/playback/src/devices.rs | 8 +- crates/playback/src/lib.rs | 41 ++- crates/playback/src/record.rs | 21 +- 7 files changed, 408 insertions(+), 415 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eb578a28..f36f359d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,23 +11,14 @@ dependencies = [ "uuid", ] -[[package]] -name = "accesskit" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad442f58ee04714aaa0ba0a2768c1ea1935b29507bb22ddece8cbbc76db02932" -dependencies = [ - "uuid", -] - [[package]] name = "accesskit_atspi_common" -version = "0.21.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52c182f9c282ac9c5638d876d551d15e5f7d397ec263349a0c6a2b61595dd5e4" +checksum = "1e8c61bee90b42a772d39d06a740207dc71a4e780004ace1db8d99fb1baaa954" dependencies = [ - "accesskit 0.25.1", - "accesskit_consumer", + "accesskit", + "accesskit_consumer 0.36.0", "atspi-common", "phf", "serde", @@ -36,37 +27,43 @@ dependencies = [ [[package]] name = "accesskit_consumer" -version = "0.39.1" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a45e2bb300bea6d5f33a82c3e051c3ddc6f41c209750f68a0acbe991a284344" +checksum = "53cf47daed85312e763fbf85ceca136e0d7abc68e0a7e12abe11f48172bc3b10" dependencies = [ - "accesskit 0.25.1", - "hashbrown 0.17.1", + "accesskit", + "hashbrown 0.16.1", ] [[package]] -name = "accesskit_ios" -version = "0.2.1" +name = "accesskit_consumer" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28e539d64b27fd4c14de8f78ada8f7c944c092f52f7e49fb63316507a57f6937" +checksum = "25e0d7e25d06f4dc21d1774d67146e9e80d6789216cbd4d1e88185b0095dba60" dependencies = [ - "accesskit 0.25.1", - "accesskit_consumer", - "hashbrown 0.17.1", - "objc2 0.5.3", - "objc2-foundation 0.2.2", - "objc2-ui-kit 0.2.2", + "accesskit", + "hashbrown 0.16.1", +] + +[[package]] +name = "accesskit_consumer" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d10a236f96f87d70732e44520046785431ef01d5bcd6b041317bfadd2f88245" +dependencies = [ + "accesskit", + "hashbrown 0.16.1", ] [[package]] name = "accesskit_macos" -version = "0.27.1" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5701624bf6a11efb6d4c8e922bf44126ed96250e5faa9a7a0468c979635839a4" +checksum = "ce02dc63b43f0c9296af9ac946312a2dc8814427d7a64d2d600971dac55b6076" dependencies = [ - "accesskit 0.25.1", - "accesskit_consumer", - "hashbrown 0.17.1", + "accesskit", + "accesskit_consumer 0.38.0", + "hashbrown 0.16.1", "objc2 0.5.3", "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", @@ -74,11 +71,11 @@ dependencies = [ [[package]] name = "accesskit_unix" -version = "0.24.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "202f24df034a7476d07b7f74284de84f6d62aabd858dbe7ee9cad3b7ad6f8f9d" +checksum = "b016ca8db0ea0ea2ceff29a9d6240391492d960716aa471967c00e8cc8cb197c" dependencies = [ - "accesskit 0.25.1", + "accesskit", "accesskit_atspi_common", "async-channel", "async-executor", @@ -92,13 +89,13 @@ dependencies = [ [[package]] name = "accesskit_windows" -version = "0.35.1" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce63f35d6bdcf59f26b76b3379063f738e6412cef46999cc772d46aa3de35adb" +checksum = "eff7009f1a532e917d66970a1e80c965140c6cfbbabbdde3d64e5431e6c78e21" dependencies = [ - "accesskit 0.25.1", - "accesskit_consumer", - "hashbrown 0.17.1", + "accesskit", + "accesskit_consumer 0.35.0", + "hashbrown 0.16.1", "static_assertions", "windows", "windows-core", @@ -106,12 +103,11 @@ dependencies = [ [[package]] name = "accesskit_winit" -version = "0.34.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee96dd150375c05912d8a473f98c2fb155fb3234fac88292d396548a2460fa77" +checksum = "1fe9a94394896352cc4660ca2288bd4ef883d83238853c038b44070c8f134313" dependencies = [ - "accesskit 0.25.1", - "accesskit_ios", + "accesskit", "accesskit_macos", "accesskit_unix", "accesskit_windows", @@ -232,7 +228,6 @@ dependencies = [ "parking_lot", "percent-encoding", "windows-sys 0.60.2", - "wl-clipboard-rs", "x11rb", ] @@ -623,18 +618,42 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "calloop" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b32313e937c8721c9d17ee9bf60699a6402b4b7c2cb6ac1bee0ffcaa4b4e5e" +dependencies = [ + "bitflags 2.13.2", + "polling", + "rustix 1.1.5", + "tracing", +] + [[package]] name = "calloop-wayland-source" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" dependencies = [ - "calloop", + "calloop 0.13.0", "rustix 0.38.44", "wayland-backend", "wayland-client", ] +[[package]] +name = "calloop-wayland-source" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138efcf0940a02ebf0cc8d1eff41a1682a46b431630f4c52450d6265876021fa" +dependencies = [ + "calloop 0.14.5", + "rustix 1.1.5", + "wayland-backend", + "wayland-client", +] + [[package]] name = "cc" version = "1.6.0" @@ -653,7 +672,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "nom 7.1.3", + "nom", ] [[package]] @@ -892,7 +911,7 @@ name = "craft-ui" version = "0.1.0" source = "git+https://github.com/storytold/craft-libs?rev=5c874d51eeb3de70e735c8ce87a59a7dedd47675#5c874d51eeb3de70e735c8ce87a59a7dedd47675" dependencies = [ - "egui 0.36.2", + "egui", "serde", ] @@ -1034,30 +1053,21 @@ name = "ecolor" version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0656a7530b9e2841ee7a5f983965bdd9840c32f4012f759cfd44b912cced6b34" -dependencies = [ - "emath 0.36.2", -] - -[[package]] -name = "ecolor" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31bcbef0d4263a63eb32c4834e5d711613035ce1333468e107d928923e21e6e2" dependencies = [ "bytemuck", - "emath 0.37.0", + "emath", ] [[package]] name = "eframe" -version = "0.37.0" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c5642501a65ca657d05f00c643a5c14ff2346cb3818032bfe23d58e1f8e5f84" +checksum = "55ad41fac6e3149abb4866e93df37dbd33baa95db525c3368c944584e95a5dc2" dependencies = [ "ahash", "bytemuck", "document-features", - "egui 0.37.0", + "egui", "egui-wgpu", "egui-winit", "egui_glow", @@ -1074,7 +1084,6 @@ dependencies = [ "pollster 1.0.1", "profiling", "raw-window-handle", - "send_wrapper", "static_assertions", "wasm-bindgen", "web-sys", @@ -1090,31 +1099,11 @@ version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc938cc27cd911415e1e4d151bc56ff9df063d8db081374e87aeb805ea74b2ba" dependencies = [ - "accesskit 0.24.1", + "accesskit", "ahash", "bitflags 2.13.2", - "emath 0.36.2", - "epaint 0.36.2", - "itertools 0.15.0", - "log", - "nohash-hasher", - "profiling", - "smallvec", - "unicode-segmentation", - "web-sys", -] - -[[package]] -name = "egui" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec9144f49b7a28b5f395d3147e325b4fe3e77ba686eac11d1bdf0a7da3f375f" -dependencies = [ - "accesskit 0.25.1", - "ahash", - "bitflags 2.13.2", - "emath 0.37.0", - "epaint 0.37.0", + "emath", + "epaint", "itertools 0.15.0", "log", "nohash-hasher", @@ -1126,15 +1115,15 @@ dependencies = [ [[package]] name = "egui-wgpu" -version = "0.37.0" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0309cbe1febed48af5b974e4bcc424b35e9664863a4d402697147b57a488c864" +checksum = "5cef1f6d1bba40b09a4d0d62cb822b81030922cfbf65c5373398c3a3caafc5bd" dependencies = [ "ahash", "bytemuck", "document-features", - "egui 0.37.0", - "epaint 0.37.0", + "egui", + "epaint", "log", "profiling", "thiserror 2.0.21", @@ -1146,36 +1135,34 @@ dependencies = [ [[package]] name = "egui-winit" -version = "0.37.0" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4e449f4c4a2a60c7ccb4ba7fa4078417bfe1f9ddc03b5e05ce75748e33a70de" +checksum = "98466000559d66ba4db786a30448b01483946add4188e7f10f522a27b4fb89cc" dependencies = [ "accesskit_winit", "arboard", - "block2 0.6.2", "bytemuck", - "egui 0.37.0", + "egui", "log", "objc2 0.6.5", - "objc2-app-kit 0.3.2", "objc2-foundation 0.3.2", "objc2-ui-kit 0.3.2", "profiling", "raw-window-handle", + "smithay-clipboard", "web-time", "webbrowser", - "windows-sys 0.61.2", "winit", ] [[package]] name = "egui_glow" -version = "0.37.0" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9275ca5cc35a6b7c94e0e8cedf22542f66cb0cf45ed8ab2657c78ff49fec7268" +checksum = "606ed4167daf341e5753d703285c4c1d562324b2c07aa8f405cd7d066fe58f3b" dependencies = [ "bytemuck", - "egui 0.37.0", + "egui", "glow", "log", "profiling", @@ -1184,13 +1171,13 @@ dependencies = [ [[package]] name = "egui_kittest" -version = "0.37.0" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0979c2e37860fcf13a2bea77508607f067a9a6a84ba9bd47b5e298582fb59b86" +checksum = "5eb3f3effb2726fee23449293e67acbf6cc6ce35525b1722bd5743bb348114d5" dependencies = [ "dify", "eframe", - "egui 0.37.0", + "egui", "egui-wgpu", "image", "kittest", @@ -1214,12 +1201,6 @@ name = "emath" version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52c8d4141cf0f60fb2f7aefaf352d3ffa5171ef0ab09731c64391801e95e48f1" - -[[package]] -name = "emath" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e73ce90f54750be8672c02be0743fe47530e6f251e066f0ee2869b59eb297af5" dependencies = [ "bytemuck", ] @@ -1256,34 +1237,11 @@ name = "epaint" version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0ec0308dc23130fc1a7d4623a8534c2ed069c6dc84e258ee4f3de99615aa1a3" -dependencies = [ - "ahash", - "ecolor 0.36.2", - "emath 0.36.2", - "font-types", - "harfrust", - "log", - "nohash-hasher", - "parking_lot", - "profiling", - "self_cell", - "skrifa", - "smallvec", - "unicode-general-category", - "unicode-segmentation", - "vello_cpu 0.1.0", -] - -[[package]] -name = "epaint" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97bac86ae3dde93fc3951e790951caea739ff39a6c89d909cdaf1d9278dbb1bd" dependencies = [ "ahash", "bytemuck", - "ecolor 0.37.0", - "emath 0.37.0", + "ecolor", + "emath", "epaint_default_fonts", "font-types", "harfrust", @@ -1295,16 +1253,15 @@ dependencies = [ "skrifa", "smallvec", "unicode-general-category", - "unicode-properties", "unicode-segmentation", - "vello_cpu 0.2.0", + "vello_cpu", ] [[package]] name = "epaint_default_fonts" -version = "0.37.0" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfc1cc505827f4c23eedc297975a0fd038268d41293f389dd8ff3b574aefab19" +checksum = "773fa9c96dd0dbef887e39d0ed6177f141cce4d68e6041df77570aa3702dfa13" [[package]] name = "equivalent" @@ -1396,12 +1353,6 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484" -[[package]] -name = "fixedbitset" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" - [[package]] name = "flate2" version = "1.1.10" @@ -1419,12 +1370,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - [[package]] name = "foldhash" version = "0.2.0" @@ -1601,29 +1546,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed4a1bb24121291d27230c1b1b44e07d6a9b28cefdb32fe1581dfb84e14f940a" dependencies = [ "bytemuck", - "foldhash 0.2.0", + "foldhash", "hashbrown 0.17.1", "log", "peniko", "skrifa", "smallvec", - "vello_common 0.1.0", -] - -[[package]] -name = "glifo" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282a26c1e23de04bdab3e34a21b6f877a479b96737792516b9b8b8f69b6661be" -dependencies = [ - "bytemuck", - "foldhash 0.2.0", - "hashbrown 0.17.1", - "log", - "peniko", - "skrifa", - "smallvec", - "vello_common 0.2.0", + "vello_common", ] [[package]] @@ -1757,15 +1686,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "foldhash 0.1.5", -] - [[package]] name = "hashbrown" version = "0.16.1" @@ -1774,7 +1694,7 @@ checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "allocator-api2", "equivalent", - "foldhash 0.2.0", + "foldhash", ] [[package]] @@ -1783,7 +1703,7 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" dependencies = [ - "foldhash 0.2.0", + "foldhash", ] [[package]] @@ -2079,12 +1999,12 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "kittest" -version = "0.5.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb81a721ba76aa095b7f45deb180504f786904e56ca4b8e141f66eb89a78b75" +checksum = "90ceaa75eb0036a32b6b9833962eb18137449e9817e2e586006471925b727fd5" dependencies = [ - "accesskit 0.25.1", - "accesskit_consumer", + "accesskit", + "accesskit_consumer 0.35.0", ] [[package]] @@ -2374,15 +2294,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nom" -version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" -dependencies = [ - "memchr", -] - [[package]] name = "num-complex" version = "0.4.6" @@ -2852,16 +2763,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "os_pipe" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - [[package]] name = "parking" version = "2.2.1" @@ -2910,22 +2811,11 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" -[[package]] -name = "petgraph" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" -dependencies = [ - "fixedbitset", - "hashbrown 0.15.5", - "indexmap", -] - [[package]] name = "phf" -version = "0.14.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "010378780309880b08997fae13be7834dba947d36393bd372f2b1556deb2a2f6" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" dependencies = [ "phf_macros", "phf_shared", @@ -2934,9 +2824,9 @@ dependencies = [ [[package]] name = "phf_generator" -version = "0.14.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aeb62e0959d5a1bebc965f4d15d9e2b7cea002b6b0f5ba8cde6cc26738467100" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" dependencies = [ "fastrand", "phf_shared", @@ -2944,9 +2834,9 @@ dependencies = [ [[package]] name = "phf_macros" -version = "0.14.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fa8d0ca26d424d27630da600c6624696e7dec8bf7b3b492b383c5dc49e5e085" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" dependencies = [ "phf_generator", "phf_shared", @@ -2957,9 +2847,9 @@ dependencies = [ [[package]] name = "phf_shared" -version = "0.14.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fd9027e2d9319be6349febd1db4e8d02aa544921200c9b777720ac34a3aa89" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" dependencies = [ "siphasher", ] @@ -3489,12 +3379,6 @@ version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" -[[package]] -name = "send_wrapper" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" - [[package]] name = "serde" version = "1.0.229" @@ -3646,8 +3530,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ "bitflags 2.13.2", - "calloop", - "calloop-wayland-source", + "calloop 0.13.0", + "calloop-wayland-source 0.3.0", "cursor-icon", "libc", "log", @@ -3664,6 +3548,44 @@ dependencies = [ "xkeysym", ] +[[package]] +name = "smithay-client-toolkit" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0" +dependencies = [ + "bitflags 2.13.2", + "calloop 0.14.5", + "calloop-wayland-source 0.4.1", + "cursor-icon", + "libc", + "log", + "memmap2", + "rustix 1.1.5", + "thiserror 2.0.21", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-experimental", + "wayland-protocols-misc", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", +] + +[[package]] +name = "smithay-clipboard" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71704c03f739f7745053bde45fa203a46c58d25bc5c4efba1d9a60e9dba81226" +dependencies = [ + "libc", + "smithay-client-toolkit 0.20.0", + "wayland-backend", +] + [[package]] name = "smol_str" version = "0.2.2" @@ -3679,7 +3601,7 @@ version = "0.12.1" dependencies = [ "chrono", "eframe", - "egui 0.37.0", + "egui", "log", "muda", "objc2 0.6.5", @@ -3851,7 +3773,7 @@ name = "soundcraft-ui-egui" version = "0.12.1" dependencies = [ "craft-ui", - "egui 0.37.0", + "egui", "egui_kittest", "image", "log", @@ -3904,7 +3826,7 @@ name = "soundcraft-web" version = "0.12.1" dependencies = [ "eframe", - "egui 0.37.0", + "egui", "js-sys", "log", "rfd", @@ -4327,6 +4249,7 @@ version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -4362,17 +4285,6 @@ dependencies = [ "strength_reduce", ] -[[package]] -name = "tree_magic_mini" -version = "3.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8765b90061cba6c22b5831f675da109ae5561588290f9fa2317adab2714d5a6" -dependencies = [ - "memchr", - "nom 8.0.0", - "petgraph", -] - [[package]] name = "type-map" version = "0.5.1" @@ -4417,12 +4329,6 @@ version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954" -[[package]] -name = "unicode-properties" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" - [[package]] name = "unicode-segmentation" version = "1.13.3" @@ -4479,21 +4385,6 @@ dependencies = [ "thiserror 2.0.21", ] -[[package]] -name = "vello_common" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb2141a2bca6e6d598e471fd4d1d7eed8e020aad6a28187edda07f091a325dd" -dependencies = [ - "bytemuck", - "fearless_simd", - "guillotiere", - "log", - "peniko", - "smallvec", - "thiserror 2.0.21", -] - [[package]] name = "vello_cpu" version = "0.1.0" @@ -4501,21 +4392,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac7349e1f55f6b801c7c277958df4ea53e7f20f21e8014910ad888b2ecda93ea" dependencies = [ "bytemuck", - "glifo 0.2.0", + "glifo", "hashbrown 0.17.1", - "vello_common 0.1.0", -] - -[[package]] -name = "vello_cpu" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace506ca414548966fcf3c283be94eb1cad8254de488791ee698bf438b0890a9" -dependencies = [ - "bytemuck", - "glifo 0.3.0", - "hashbrown 0.17.1", - "vello_common 0.2.0", + "vello_common", ] [[package]] @@ -4677,6 +4556,32 @@ dependencies = [ "wayland-scanner", ] +[[package]] +name = "wayland-protocols-experimental" +version = "20250721.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1" +dependencies = [ + "bitflags 2.13.2", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-misc" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9567599ef23e09b8dad6e429e5738d4509dfc46b3b21f32841a304d16b29c8" +dependencies = [ + "bitflags 2.13.2", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + [[package]] name = "wayland-protocols-plasma" version = "0.3.12" @@ -5247,7 +5152,7 @@ dependencies = [ "bitflags 2.13.2", "block2 0.5.1", "bytemuck", - "calloop", + "calloop 0.13.0", "cfg_aliases", "concurrent-queue", "core-foundation", @@ -5268,7 +5173,7 @@ dependencies = [ "raw-window-handle", "redox_syscall 0.4.1", "rustix 0.38.44", - "smithay-client-toolkit", + "smithay-client-toolkit 0.19.2", "smol_str", "tracing", "unicode-segmentation", @@ -5311,24 +5216,6 @@ version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" -[[package]] -name = "wl-clipboard-rs" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d7888ccd4896447b2d14d3a9350a85df2aeb6f181e2e7a31349d104ac46cac1" -dependencies = [ - "libc", - "log", - "os_pipe", - "rustix 1.1.5", - "thiserror 2.0.21", - "tree_magic_mini", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "wayland-protocols-wlr", -] - [[package]] name = "writeable" version = "0.6.4" diff --git a/Cargo.toml b/Cargo.toml index b0fda5df..b5ea0ddc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,9 +49,10 @@ craft-docpath = { version = "0.1.0", git = "https://github.com/storytold/craft-l # ui craft-ui = { version = "0.1.0", git = "https://github.com/storytold/craft-libs", rev = "5c874d51eeb3de70e735c8ce87a59a7dedd47675", features = ["serde"] } -egui = "0.37" -eframe = { version = "0.37", default-features = false } -egui_kittest = "0.37" +# egui 0.37 needs craft-libs#199 (craft-ui) first; pin 0.36 until it lands. +egui = "0.36" +eframe = { version = "0.36", default-features = false } +egui_kittest = "0.36" rfd = "0.17" image = { version = "0.25", default-features = false, features = ["png"] } diff --git a/crates/audio-io/src/symph.rs b/crates/audio-io/src/symph.rs index 5bfe91da..db6efbbb 100644 --- a/crates/audio-io/src/symph.rs +++ b/crates/audio-io/src/symph.rs @@ -3,18 +3,18 @@ use std::io::Cursor; use std::panic::{AssertUnwindSafe, catch_unwind}; -use symphonia::core::audio::SampleBuffer; -use symphonia::core::codecs::{ - CODEC_TYPE_AAC, CODEC_TYPE_ADPCM_G722, CODEC_TYPE_ADPCM_G726, CODEC_TYPE_ADPCM_G726LE, CODEC_TYPE_ADPCM_IMA_QT, CODEC_TYPE_ADPCM_IMA_WAV, - CODEC_TYPE_ADPCM_MS, CODEC_TYPE_ALAC, CODEC_TYPE_FLAC, CODEC_TYPE_MP1, CODEC_TYPE_MP2, CODEC_TYPE_MP3, CODEC_TYPE_NULL, CODEC_TYPE_OPUS, - CODEC_TYPE_PCM_ALAW, CODEC_TYPE_PCM_F32BE, CODEC_TYPE_PCM_F32LE, CODEC_TYPE_PCM_F64BE, CODEC_TYPE_PCM_F64LE, CODEC_TYPE_PCM_MULAW, - CODEC_TYPE_VORBIS, CodecParameters, CodecType, DecoderOptions, +use symphonia::core::codecs::CodecParameters; +use symphonia::core::codecs::audio::well_known::{ + CODEC_ID_AAC, CODEC_ID_ADPCM_G722, CODEC_ID_ADPCM_G726, CODEC_ID_ADPCM_G726LE, CODEC_ID_ADPCM_IMA_QT, CODEC_ID_ADPCM_IMA_WAV, CODEC_ID_ADPCM_MS, + CODEC_ID_ALAC, CODEC_ID_FLAC, CODEC_ID_MP1, CODEC_ID_MP2, CODEC_ID_MP3, CODEC_ID_OPUS, CODEC_ID_PCM_ALAW, CODEC_ID_PCM_F32BE, CODEC_ID_PCM_F32LE, + CODEC_ID_PCM_F64BE, CODEC_ID_PCM_F64LE, CODEC_ID_PCM_MULAW, CODEC_ID_VORBIS, }; +use symphonia::core::codecs::audio::{AudioCodecId, AudioCodecParameters, AudioDecoderOptions, CODEC_ID_NULL_AUDIO}; use symphonia::core::errors::Error as SymError; +use symphonia::core::formats::probe::Hint; use symphonia::core::formats::{FormatOptions, FormatReader}; use symphonia::core::io::{MediaSourceStream, MediaSourceStreamOptions, ReadBytes}; use symphonia::core::meta::MetadataOptions; -use symphonia::core::probe::Instantiate; use crate::pcm::{MAX_DECODE_SAMPLES, be_u32}; use crate::{AudioBuffer, AudioError, AudioInfo, FileFormat, Result, SampleFormat}; @@ -23,6 +23,8 @@ use crate::{AudioBuffer, AudioError, AudioInfo, FileFormat, Result, SampleFormat /// takes its frame length from the file (encoders use 4096). const MAX_PACKET_FRAMES: u64 = 1 << 20; +/// Turn a symphonia error into the audio layer's error, keeping "unsupported" and "too large" +/// distinct from a malformed file. fn map_err(e: SymError) -> AudioError { match e { SymError::Unsupported(m) => AudioError::Unsupported(m.to_string()), @@ -42,80 +44,160 @@ enum Opened { Empty(AudioInfo), } -/// Symphonia's probe loop (`Probe::format`, whose hint 0.5 ignores), checking the container it -/// finds before its reader allocates whatever the file claims. -fn open(bytes: &[u8]) -> Result { - let mut mss = MediaSourceStream::new(Box::new(Cursor::new(bytes.to_vec())), MediaSourceStreamOptions::default()); - let fmt_opts = FormatOptions { enable_gapless: true, ..FormatOptions::default() }; +/// Skip a leading ID3v2 tag before probing. +/// +/// Tags aren't used, and symphonia allocates whatever an ID3v2 frame claims, so the tag is skipped +/// with [`ReadBytes::ignore_bytes`] (which allocates nothing) instead of being handed to the +/// metadata reader. Returns the stream position the container starts at. +fn skip_leading_id3(mss: &mut MediaSourceStream<'_>, bytes: &[u8]) -> Result { loop { - let found = symphonia::default::get_probe().next(&mut mss).map_err(map_err)?; let at = usize::try_from(mss.pos()).unwrap_or(usize::MAX); let head = bytes.get(at..).unwrap_or_default(); - match (found, head.get(..10)) { - (Instantiate::Format(reader), _) => { - match head.get(..4) { - Some(b"caff") => crate::caf::check(bytes, at)?, - Some(b"ftyp") => crate::mp4::check(bytes, at.saturating_sub(4))?, - Some(b"fLaC") => { - crate::flac::check_metadata(bytes, at)?; - if let Some(info) = crate::flac::empty_stream_info(bytes, at) { - return Ok(Opened::Empty(info)); - } - } - Some(b"RIFF") => crate::wav::check_info(bytes, at)?, - _ => {} - } - return reader(mss, &fmt_opts).map(Opened::Reader).map_err(map_err); - } - // Tags aren't used, and symphonia allocates whatever an ID3v2 frame claims: skip it. - (Instantiate::Metadata(_), Some([b'I', b'D', b'3', _, _, _, size @ ..])) => { - let size = size.iter().fold(0u64, |v, &x| (v << 7) | u64::from(x & 0x7f)); - mss.ignore_bytes(10 + size).map_err(|e| AudioError::Malformed(e.to_string()))?; - } - (Instantiate::Metadata(tag), _) => { - tag(&MetadataOptions::default()).read_all(&mut mss).map_err(map_err)?; - } + if head.len() < 10 || head.get(..3) != Some(b"ID3") { + return Ok(at); } + let size = head.get(6..10).map(|size| size.iter().fold(0u64, |v, &x| (v << 7) | u64::from(x & 0x7f))).unwrap_or(0); + mss.ignore_bytes(10 + size).map_err(|e| AudioError::Malformed(e.to_string()))?; } } -fn format_from_codec(codec: CodecType, detected: FileFormat) -> FileFormat { +/// How far symphonia's probe scans for a format marker before giving up. +const MAX_PROBE_DEPTH: usize = 1024 * 1024; + +/// The container marker each detected format starts with, if its reader needs size checks first. +fn container_markers(detected: FileFormat) -> &'static [&'static [u8; 4]] { + match detected { + // RF64 and BW64 are the >4 GiB RIFF forms. + FileFormat::Wav => &[b"RIFF", b"RF64", b"BW64"], + FileFormat::Flac => &[b"fLaC"], + FileFormat::Caf => &[b"caff"], + // MP4/M4A put `ftyp` after the first atom's size field. + FileFormat::Aac | FileFormat::Alac => &[b"ftyp"], + // Unknown files may carry junk before the container; symphonia's probe scans for markers. + FileFormat::Other => &[b"caff", b"ftyp", b"fLaC", b"RIFF"], + FileFormat::Aiff | FileFormat::Mp3 | FileFormat::Ogg => &[], + } +} + +/// The probe short names a container marker stands for. +fn expected_probe_format(marker: &[u8; 4]) -> &'static [&'static str] { + match marker { + b"caff" => &["caf"], + // The isomp4 reader reads the `ftyp` atom. + b"ftyp" => &["isomp4"], + b"fLaC" => &["flac"], + b"RIFF" | b"RF64" | b"BW64" => &["wave"], + _ => &[], + } +} + +/// Probe short names whose demuxer allocates from sizes in the file, so the matching container +/// check has to run before the stream is decoded. +const SIZE_CHECKED_PROBE_FORMATS: &[&str] = &["caf", "flac", "isomp4", "wave"]; + +/// Find the first container marker at or after `at`, within symphonia's probe depth. +fn find_marker<'a>(bytes: &[u8], at: usize, markers: &'a [&'static [u8; 4]]) -> Option<(usize, &'a [u8; 4])> { + let end = bytes.len().min(at.saturating_add(MAX_PROBE_DEPTH)); + for pos in at..end.saturating_sub(3) { + let window = bytes.get(pos..pos + 4)?; + if let Some(marker) = markers.iter().find(|marker| window == marker.as_slice()) { + return Some((pos, *marker)); + } + } + None +} + +/// Probe for a container, checking the container it finds before its reader allocates whatever the +/// file claims. +/// +/// The check has to run on the container the probe actually selects, but the probe reports that +/// container only through the reader it returns. The check therefore runs on the first marker the +/// detected format allows, and the reader is refused if its format is not the checked one: a +/// misleading marker must not let a later container slip through unchecked. +fn open(bytes: &[u8], detected: FileFormat) -> Result { + let mut mss = MediaSourceStream::new(Box::new(Cursor::new(bytes.to_vec())), MediaSourceStreamOptions::default()); + let at = skip_leading_id3(&mut mss, bytes)?; + let mut checked: Option<&'static [&'static str]> = None; + if let Some((found, marker)) = find_marker(bytes, at, container_markers(detected)) { + match marker { + b"caff" => { + crate::caf::check(bytes, found)?; + checked = Some(expected_probe_format(marker)); + } + b"ftyp" => { + crate::mp4::check(bytes, found.saturating_sub(4))?; + checked = Some(expected_probe_format(marker)); + } + b"fLaC" => { + crate::flac::check_metadata(bytes, found)?; + if let Some(info) = crate::flac::empty_stream_info(bytes, found) { + return Ok(Opened::Empty(info)); + } + checked = Some(expected_probe_format(marker)); + } + b"RIFF" | b"RF64" | b"BW64" => { + crate::wav::check_info(bytes, found)?; + checked = Some(expected_probe_format(marker)); + } + _ => {} + } + } + let reader = symphonia::default::get_probe().probe(&Hint::new(), mss, FormatOptions::default(), MetadataOptions::default()).map_err(map_err)?; + let probed = reader.format_info().short_name; + // Only containers whose readers allocate from sizes in the file need the check. A stream the + // probe reads with another reader (MP3, Ogg, AIFF, ...) has no such size fields, so a marker + // found in its audio data must not reject it. + if let Some(names) = checked.filter(|_| SIZE_CHECKED_PROBE_FORMATS.contains(&probed)) { + if !names.contains(&probed) { + return Err(AudioError::Malformed(format!("the probed container ({probed}) is not the one whose size checks ran"))); + } + } else if checked.is_none() && SIZE_CHECKED_PROBE_FORMATS.contains(&probed) { + return Err(AudioError::Malformed(format!("the probed container ({probed}) was not size-checked"))); + } + Ok(Opened::Reader(reader)) +} + +/// The container `detected` stands for: the detection wins when it knows the file, otherwise the +/// decoded codec names the family. +fn format_from_codec(codec: AudioCodecId, detected: FileFormat) -> FileFormat { if detected != FileFormat::Other { return detected; } match codec { - c if c == CODEC_TYPE_FLAC => FileFormat::Flac, - c if c == CODEC_TYPE_MP3 || c == CODEC_TYPE_MP2 || c == CODEC_TYPE_MP1 => FileFormat::Mp3, - c if c == CODEC_TYPE_AAC => FileFormat::Aac, - c if c == CODEC_TYPE_ALAC => FileFormat::Alac, - c if c == CODEC_TYPE_VORBIS => FileFormat::Ogg, + c if c == CODEC_ID_FLAC => FileFormat::Flac, + c if c == CODEC_ID_MP3 || c == CODEC_ID_MP2 || c == CODEC_ID_MP1 => FileFormat::Mp3, + c if c == CODEC_ID_AAC => FileFormat::Aac, + c if c == CODEC_ID_ALAC => FileFormat::Alac, + c if c == CODEC_ID_VORBIS => FileFormat::Ogg, _ => FileFormat::Other, } } -fn sample_format_of(p: &CodecParameters) -> SampleFormat { +/// The stored sample format, using the codec and its declared bit depth. Lossy and unusual +/// codecs decode to `Compressed`. +fn sample_format_of(p: &AudioCodecParameters) -> SampleFormat { let c = p.codec; - if c == CODEC_TYPE_PCM_F32LE || c == CODEC_TYPE_PCM_F32BE { + if c == CODEC_ID_PCM_F32LE || c == CODEC_ID_PCM_F32BE { return SampleFormat::Float32; } - if c == CODEC_TYPE_PCM_F64LE || c == CODEC_TYPE_PCM_F64BE { + if c == CODEC_ID_PCM_F64LE || c == CODEC_ID_PCM_F64BE { return SampleFormat::Float64; } let lossy = [ - CODEC_TYPE_MP1, - CODEC_TYPE_MP2, - CODEC_TYPE_MP3, - CODEC_TYPE_AAC, - CODEC_TYPE_VORBIS, - CODEC_TYPE_OPUS, - CODEC_TYPE_ADPCM_MS, - CODEC_TYPE_ADPCM_IMA_WAV, - CODEC_TYPE_ADPCM_IMA_QT, - CODEC_TYPE_ADPCM_G722, - CODEC_TYPE_ADPCM_G726, - CODEC_TYPE_ADPCM_G726LE, - CODEC_TYPE_PCM_ALAW, - CODEC_TYPE_PCM_MULAW, + CODEC_ID_MP1, + CODEC_ID_MP2, + CODEC_ID_MP3, + CODEC_ID_AAC, + CODEC_ID_VORBIS, + CODEC_ID_OPUS, + CODEC_ID_ADPCM_MS, + CODEC_ID_ADPCM_IMA_WAV, + CODEC_ID_ADPCM_IMA_QT, + CODEC_ID_ADPCM_G722, + CODEC_ID_ADPCM_G726, + CODEC_ID_ADPCM_G726LE, + CODEC_ID_PCM_ALAW, + CODEC_ID_PCM_MULAW, ]; let lossless = !lossy.contains(&c); match (lossless, p.bits_per_sample) { @@ -127,15 +209,18 @@ fn sample_format_of(p: &CodecParameters) -> SampleFormat { } } +/// The first track with a decodable audio codec, with the stream information the UI shows. fn info_of(reader: &dyn FormatReader, detected: FileFormat) -> Result<(u32, AudioInfo)> { - let track = reader + let (track, p) = reader .tracks() .iter() - .find(|t| t.codec_params.codec != CODEC_TYPE_NULL) + .find_map(|t| match t.codec_params.as_ref().and_then(CodecParameters::audio) { + Some(p) if p.codec != CODEC_ID_NULL_AUDIO => Some((t, p)), + _ => None, + }) .ok_or_else(|| AudioError::Unsupported("no decodable audio track".into()))?; - let p = &track.codec_params; let sample_rate = p.sample_rate.filter(|&r| r > 0).ok_or_else(|| AudioError::Malformed("unknown sample rate".into()))?; - let channels = p.channels.map(|c| c.count()).unwrap_or(0); + let channels = p.channels.as_ref().map(|c| c.count()).unwrap_or(0); let channels = u16::try_from(channels).map_err(|_| AudioError::Unsupported("too many channels".into()))?; Ok(( track.id, @@ -144,7 +229,7 @@ fn info_of(reader: &dyn FormatReader, detected: FileFormat) -> Result<(u32, Audi sample_format: sample_format_of(p), sample_rate, channels, - frames: p.n_frames.unwrap_or(0), + frames: track.num_frames.unwrap_or(0), bwf: None, }, )) @@ -152,7 +237,7 @@ fn info_of(reader: &dyn FormatReader, detected: FileFormat) -> Result<(u32, Audi /// 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)? { + let mut reader = match open(bytes, detected)? { Opened::Reader(reader) => reader, Opened::Empty(info) => { let buf = AudioBuffer::new(info.sample_rate, usize::from(info.channels), 0); @@ -160,7 +245,7 @@ fn run(bytes: &[u8], detected: FileFormat, count_only: bool) -> Result<(AudioInf } }; let (track_id, mut info) = info_of(reader.as_ref(), detected)?; - if let Some(n) = reader.tracks().iter().find(|t| t.id == track_id).and_then(|t| t.codec_params.n_frames) { + if let Some(n) = reader.tracks().iter().find(|t| t.id == track_id).and_then(|t| t.num_frames) { let total = n.saturating_mul(u64::from(info.channels.max(1))); if !count_only && total > MAX_DECODE_SAMPLES { return Err(AudioError::TooLarge(format!("{total} samples exceeds the decode limit"))); @@ -170,21 +255,24 @@ fn run(bytes: &[u8], detected: FileFormat, count_only: bool) -> Result<(AudioInf .tracks() .iter() .find(|t| t.id == track_id) - .map(|t| t.codec_params.clone()) + .and_then(|t| t.codec_params.as_ref()) + .and_then(CodecParameters::audio) + .cloned() .ok_or_else(|| AudioError::Malformed("track vanished".into()))?; - let alac = params.extra_data.as_deref().filter(|_| params.codec == CODEC_TYPE_ALAC).and_then(|d| be_u32(d, 0)); + let alac = params.extra_data.as_deref().filter(|_| params.codec == CODEC_ID_ALAC).and_then(|d| be_u32(d, 0)); if params.max_frames_per_packet.into_iter().chain(alac.map(u64::from)).any(|n| n > MAX_PACKET_FRAMES) { return Err(AudioError::Malformed("the file declares packets far longer than any codec uses".into())); } - let mut decoder = symphonia::default::get_codecs().make(¶ms, &DecoderOptions::default()).map_err(map_err)?; + let mut decoder = symphonia::default::get_codecs().make_audio_decoder(¶ms, &AudioDecoderOptions::default()).map_err(map_err)?; let mut planar: Vec> = Vec::new(); + let mut scratch: Vec> = Vec::new(); let mut frames: u64 = 0; - let mut sbuf: Option> = None; let mut errors = 0usize; loop { let packet = match reader.next_packet() { - Ok(p) => p, + Ok(Some(packet)) => packet, + Ok(None) => break, Err(SymError::IoError(e)) if e.kind() == std::io::ErrorKind::UnexpectedEof => break, Err(SymError::ResetRequired) => break, Err(e) => { @@ -195,7 +283,7 @@ fn run(bytes: &[u8], detected: FileFormat, count_only: bool) -> Result<(AudioInf break; } }; - if packet.track_id() != track_id { + if packet.track_id != track_id { continue; } let decoded = match decoder.decode(&packet) { @@ -211,8 +299,8 @@ fn run(bytes: &[u8], detected: FileFormat, count_only: bool) -> Result<(AudioInf Err(SymError::IoError(_)) => break, Err(e) => return Err(map_err(e)), }; - let spec = *decoded.spec(); - let ch = spec.channels.count(); + let spec = decoded.spec(); + let ch = spec.channels().count(); if ch == 0 { continue; } @@ -229,16 +317,9 @@ fn run(bytes: &[u8], detected: FileFormat, count_only: bool) -> Result<(AudioInf if count_only { continue; } - let needed = decoded.capacity() as u64; - let sb = match &mut sbuf { - Some(sb) if sb.capacity() >= decoded.capacity() * ch => sb, - _ => sbuf.insert(SampleBuffer::::new(needed, spec)), - }; - sb.copy_interleaved_ref(decoded); - for frame in sb.samples().chunks_exact(ch) { - for (dst, &s) in planar.iter_mut().zip(frame) { - dst.push(if s.is_finite() { s } else { 0.0 }); - } + decoded.copy_to_vecs_planar::(&mut scratch); + for (dst, src) in planar.iter_mut().zip(&scratch) { + dst.extend(src.iter().map(|&s| if s.is_finite() { s } else { 0.0 })); } } if info.channels == 0 { @@ -254,7 +335,7 @@ fn run(bytes: &[u8], detected: FileFormat, count_only: bool) -> Result<(AudioInf pub(crate) fn probe(bytes: &[u8], detected: FileFormat) -> Result { guarded(|| { - let reader = match open(bytes)? { + let reader = match open(bytes, detected)? { Opened::Reader(reader) => reader, Opened::Empty(info) => return Ok(info), }; diff --git a/crates/audio-io/tests/malformed.rs b/crates/audio-io/tests/malformed.rs index ed0d6e31..f7da8746 100644 --- a/crates/audio-io/tests/malformed.rs +++ b/crates/audio-io/tests/malformed.rs @@ -451,6 +451,22 @@ fn garbage_in_compressed_containers_errors() { assert!(decode(&[], None).is_err()); } +/// A marker that passes its container check must not let the probe select a different container +/// without its own checks: the reader is refused when the probed format disagrees. +#[test] +fn a_probed_container_that_disagrees_with_the_checked_marker_is_refused() { + let flac = small(FileFormat::Flac, BitDepth::Int24, 2); + let mut bytes = b"RIFF".to_vec(); + bytes.extend_from_slice(&3u32.to_le_bytes()); + bytes.extend_from_slice(b"FAKE"); + bytes.extend_from_slice(&flac); + match decode(&bytes, None) { + Err(AudioError::Malformed(message)) => assert!(message.contains("probed container"), "unexpected message: {message}"), + Err(other) => panic!("unexpected error kind: {other:?}"), + Ok(_) => panic!("the mismatched container must be refused"), + } +} + proptest! { #![proptest_config(ProptestConfig { cases: 256, ..ProptestConfig::default() })] diff --git a/crates/playback/src/devices.rs b/crates/playback/src/devices.rs index 19df07b6..a2847097 100644 --- a/crates/playback/src/devices.rs +++ b/crates/playback/src/devices.rs @@ -64,9 +64,9 @@ pub struct AudioSystem { pub inputs: Vec, } -#[allow(deprecated)] -fn device_name(d: &cpal::Device) -> Option { - d.name().ok() +/// The name the host gives `d`, if it can produce a description for it. +pub(crate) fn device_name(d: &cpal::Device) -> Option { + d.description().ok().map(|description| description.name().to_owned()) } fn describe(d: &cpal::Device, default_name: Option<&str>, output: bool) -> Option { @@ -78,7 +78,7 @@ fn describe(d: &cpal::Device, default_name: Option<&str>, output: bool) -> Optio }; let max_channels = ranges.iter().map(cpal::SupportedStreamConfigRange::channels).max().unwrap_or(0); let sample_rates = - STANDARD_RATES.iter().copied().filter(|r| ranges.iter().any(|c| c.min_sample_rate().0 <= *r && c.max_sample_rate().0 >= *r)).collect(); + STANDARD_RATES.iter().copied().filter(|r| ranges.iter().any(|c| c.min_sample_rate() <= *r && c.max_sample_rate() >= *r)).collect(); let (mut buffer_min, mut buffer_max) = (None::, None::); for c in &ranges { if let cpal::SupportedBufferSize::Range { min, max } = c.buffer_size() { diff --git a/crates/playback/src/lib.rs b/crates/playback/src/lib.rs index 3821e266..41bb89ed 100644 --- a/crates/playback/src/lib.rs +++ b/crates/playback/src/lib.rs @@ -522,14 +522,14 @@ impl Player { match open_device(want, want_ch, cfg) { Ok((device, config, format, name)) => { // The chosen buffer size first; if the device refuses it, its default. - let mut attempts = vec![config.clone()]; + let mut attempts = vec![config]; if config.buffer_size != cpal::BufferSize::Default { attempts.push(cpal::StreamConfig { buffer_size: cpal::BufferSize::Default, ..config }); } let opened = first_started( attempts, - |config: &cpal::StreamConfig| -> Result { - let rate = config.sample_rate.0; + |config: &cpal::StreamConfig| -> Result { + let rate = config.sample_rate; let channels = usize::from(config.channels); let (tx, rx) = channel(); let (gtx, grx) = channel(); @@ -546,7 +546,7 @@ impl Player { ); match opened { Some((config, Opened { stream, tx, grx, fault })) => { - let rate = config.sample_rate.0; + let rate = config.sample_rate; let channels = usize::from(config.channels); let buffer = match config.buffer_size { cpal::BufferSize::Fixed(n) => Some(n), @@ -926,7 +926,7 @@ fn build_output( channels: usize, fault: &Arc, shared: &Arc, -) -> Result +) -> Result where T: cpal::SizedSample + cpal::FromSample, { @@ -936,7 +936,7 @@ where let chunk = scratch.len(); let (failed, guard, gone) = (Arc::clone(fault), Arc::clone(shared), Arc::clone(fault)); device.build_output_stream( - config, + *config, move |data: &mut [T], _: &cpal::OutputCallbackInfo| { mark_audio_thread(); for out in data.chunks_mut(chunk) { @@ -949,9 +949,9 @@ where }, // cpal may call this on the audio thread (on ALSA in a loop while the device stays // broken): marked, so the logger hands the record off instead of writing it. - move |e: cpal::StreamError| { + move |e: cpal::Error| { mark_audio_thread(); - if matches!(e, cpal::StreamError::DeviceNotAvailable) { + if e.kind() == cpal::ErrorKind::DeviceNotAvailable { gone.gone.store(true, Ordering::Relaxed); } log::warn!("audio stream error: {e}"); @@ -968,8 +968,7 @@ fn open_device(want_rate: u32, want_ch: u16, cfg: &AudioConfig) -> Result<(cpal: use cpal::traits::DeviceTrait; let host = devices::host(cfg); let device = devices::device(&host, cfg.output.as_deref(), true).ok_or_else(|| "no output device".to_string())?; - #[allow(deprecated)] - let name = device.name().unwrap_or_else(|_| "Audio device".into()); + let name = devices::device_name(&device).unwrap_or_else(|| "Audio device".into()); // Prefer a config at the chosen (else session) rate; else the device default (we resample). let want_ch = want_ch.max(2); let rates: Vec = cfg.sample_rate.into_iter().chain(Some(want_rate)).collect(); @@ -984,13 +983,13 @@ fn open_device(want_rate: u32, want_ch: u16, cfg: &AudioConfig) -> Result<(cpal: for rate in rates { for c in &configs { if devices::format_rank(c.sample_format()).is_some() - && c.min_sample_rate().0 <= rate - && c.max_sample_rate().0 >= rate + && c.min_sample_rate() <= rate + && c.max_sample_rate() >= rate && c.channels() >= 2 && c.channels() <= want_ch && chosen.as_ref().is_none_or(|x| better(c, x)) { - chosen = Some((c.with_sample_rate(cpal::SampleRate(rate)).config(), *c.buffer_size(), c.sample_format())); + chosen = Some((c.with_sample_rate(rate).config(), *c.buffer_size(), c.sample_format())); } } if chosen.is_some() { @@ -1029,12 +1028,22 @@ mod tests { } impl cpal::traits::StreamTrait for Fake { - fn play(&self) -> Result<(), cpal::PlayStreamError> { - if self.starts { Ok(()) } else { Err(cpal::PlayStreamError::DeviceNotAvailable) } + /// Play reports the device's refusal when `starts` is false. + fn play(&self) -> Result<(), cpal::Error> { + if self.starts { Ok(()) } else { Err(cpal::Error::new(cpal::ErrorKind::DeviceNotAvailable)) } } - fn pause(&self) -> Result<(), cpal::PauseStreamError> { + /// Pause always succeeds on the fake stream. + fn pause(&self) -> Result<(), cpal::Error> { Ok(()) } + /// The fake stream claims one fixed buffer. + fn buffer_size(&self) -> Result { + Ok(1024) + } + /// The fake stream has no clock. + fn now(&self) -> cpal::StreamInstant { + cpal::StreamInstant::new(0, 0) + } } #[test] diff --git a/crates/playback/src/record.rs b/crates/playback/src/record.rs index 8072adac..a49b643c 100644 --- a/crates/playback/src/record.rs +++ b/crates/playback/src/record.rs @@ -145,7 +145,7 @@ fn build_input( config: &cpal::StreamConfig, shared: &Arc, monitor: &Arc, -) -> Result +) -> Result where T: cpal::SizedSample, f32: cpal::FromSample, @@ -158,7 +158,7 @@ where let chunk = scratch.len(); let mut owed = 0; device.build_input_stream( - config, + *config, move |data: &[T], _: &cpal::InputCallbackInfo| { crate::mark_audio_thread(); sh.guarded(|| { @@ -176,10 +176,10 @@ where } }); }, - move |e| { + move |e: cpal::Error| { // Possibly the audio thread (see `crate::mark_audio_thread`). crate::mark_audio_thread(); - if matches!(e, cpal::StreamError::DeviceNotAvailable) { + if e.kind() == cpal::ErrorKind::DeviceNotAvailable { let _ = errors.fault.compare_exchange(0, GONE, Ordering::Relaxed, Ordering::Relaxed); } log::warn!("input stream error: {e}"); @@ -202,17 +202,16 @@ impl Recorder { use cpal::traits::{DeviceTrait, StreamTrait}; let host = crate::devices::host(audio); let device = crate::devices::device(&host, audio.input.as_deref(), false).ok_or("no audio input device")?; - #[allow(deprecated)] - let name = device.name().unwrap_or_else(|_| "Input".into()); + let name = crate::devices::device_name(&device).unwrap_or_else(|| "Input".into()); // At the chosen rate when the device offers it, so input and output run together: the // best format first, then the most channels. let at_rate = audio.sample_rate.and_then(|r| { device .supported_input_configs() .ok()? - .filter(|c| crate::devices::format_rank(c.sample_format()).is_some() && c.min_sample_rate().0 <= r && c.max_sample_rate().0 >= r) + .filter(|c| crate::devices::format_rank(c.sample_format()).is_some() && c.min_sample_rate() <= r && c.max_sample_rate() >= r) .min_by_key(|c| (crate::devices::format_rank(c.sample_format()), std::cmp::Reverse(c.channels()))) - .map(|c| c.with_sample_rate(cpal::SampleRate(r))) + .map(|c| c.with_sample_rate(r)) }); let cfg = match at_rate { Some(c) => c, @@ -225,9 +224,9 @@ impl Recorder { let mut config = cfg.config(); config.buffer_size = crate::devices::buffer_size(audio.buffer_size, cfg.buffer_size()); let channels = usize::from(config.channels).max(1); - let ring = CaptureRing::new((config.sample_rate.0 as usize).saturating_mul(channels * RING_SECONDS).min(1 << 25)); + let ring = CaptureRing::new((config.sample_rate as usize).saturating_mul(channels * RING_SECONDS).min(1 << 25)); let shared = Arc::new(Shared::new(ring)); - let monitor = InputRing::new(usize::from(config.channels), config.sample_rate.0); + let monitor = InputRing::new(usize::from(config.channels), config.sample_rate); let build = |config: &cpal::StreamConfig| match format { cpal::SampleFormat::I32 => build_input::(&device, config, &shared, &monitor), cpal::SampleFormat::I16 => build_input::(&device, config, &shared, &monitor), @@ -245,7 +244,7 @@ impl Recorder { }; stream.play().map_err(|e| e.to_string())?; let seen = (0, Instant::now()); - Ok(Recorder { monitor, shared, _stream: Some(stream), device_name: name, sample_rate: config.sample_rate.0, channels, writer: None, seen }) + Ok(Recorder { monitor, shared, _stream: Some(stream), device_name: name, sample_rate: config.sample_rate, channels, writer: None, seen }) } #[cfg(target_os = "freebsd")] From ef8a63f8c3bd1e1694910ec47f7e7f2c16055cd4 Mon Sep 17 00:00:00 2001 From: aiongg <67543387+aiongg@users.noreply.github.com> Date: Fri, 23 Oct 2026 13:35:58 -0400 Subject: [PATCH 06/12] Packaging: pin the signing client and appimagetool, and check their hashes (#827) * Packaging: pin the Windows signing client and check its hash * Packaging: pin appimagetool and check its hash * Reject AppImage download for architectures without a pinned appimagetool * Pin the AppImage runtime --- packaging/linux/package.sh | 46 +++++++++++++++++++++++++++++++++----- packaging/windows/sign.ps1 | 13 ++++++++--- 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/packaging/linux/package.sh b/packaging/linux/package.sh index fb677dd7..72d95f8b 100755 --- a/packaging/linux/package.sh +++ b/packaging/linux/package.sh @@ -106,6 +106,23 @@ fi # ---- AppImage ----------------------------------------------------------------------------------- if has appimage; then + APPIMAGETOOL_VERSION=1.9.1 + case "$ARCH" in + x86_64) APPIMAGETOOL_SHA256=ed4ce84f0d9caff66f50bcca6ff6f35aae54ce8135408b3fa33abfc3cb384eb0 ;; + aarch64) APPIMAGETOOL_SHA256=f0837e7448a0c1e4e650a93bb3e85802546e60654ef287576f46c71c126a9158 ;; + *) APPIMAGETOOL_SHA256='' ;; + esac + # appimagetool otherwise fetches the unpinned type2-runtime `continuous` build and embeds it. + APPIMAGE_RUNTIME_VERSION=20251108 + case "$ARCH" in + x86_64) APPIMAGE_RUNTIME_SHA256=2fca8b443c92510f1483a883f60061ad09b46b978b2631c807cd873a47ec260d ;; + aarch64) APPIMAGE_RUNTIME_SHA256=00cbdfcf917cc6c0ff6d3347d59e0ca1f7f45a6df1a428a0d6d8a78664d87444 ;; + *) APPIMAGE_RUNTIME_SHA256='' ;; + esac + if [[ -z "$APPIMAGE_RUNTIME_SHA256" ]]; then + echo "no pinned AppImage runtime for $ARCH; build the AppImage on x86_64 or aarch64" >&2 + exit 2 + fi APPDIR="$WORK/SoundCraft.AppDir" cp -R "$STAGE" "$APPDIR" mv "$APPDIR/usr/share/doc" "$WORK/doc-unused" @@ -116,17 +133,36 @@ if has appimage; then TOOL="${APPIMAGETOOL:-$(command -v appimagetool || true)}" if [[ -z "$TOOL" ]]; then - TOOL="$CARGO_TARGET_DIR/appimagetool-$ARCH.AppImage" - if [[ ! -x "$TOOL" ]]; then - curl --proto '=https' -fsSL -o "$TOOL" "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-$ARCH.AppImage" - chmod +x "$TOOL" + if [[ -z "$APPIMAGETOOL_SHA256" ]]; then + echo "no pinned appimagetool for $ARCH; set APPIMAGETOOL or put appimagetool on PATH" >&2 + exit 2 fi + TOOL="$CARGO_TARGET_DIR/appimagetool-$APPIMAGETOOL_VERSION-$ARCH.AppImage" + if [[ ! -x "$TOOL" ]]; then + download="$TOOL.download" + curl --proto '=https' -fsSL -o "$download" \ + "https://github.com/AppImage/appimagetool/releases/download/$APPIMAGETOOL_VERSION/appimagetool-$ARCH.AppImage" + printf '%s %s\n' "$APPIMAGETOOL_SHA256" "$download" | sha256sum -c - + chmod 755 "$download" + mv "$download" "$TOOL" + fi + printf '%s %s\n' "$APPIMAGETOOL_SHA256" "$TOOL" | sha256sum -c - fi + RUNTIME="$CARGO_TARGET_DIR/appimage-runtime-$APPIMAGE_RUNTIME_VERSION-$ARCH" + if [[ ! -f "$RUNTIME" ]]; then + download="$RUNTIME.download" + curl --proto '=https' -fsSL -o "$download" \ + "https://github.com/AppImage/type2-runtime/releases/download/$APPIMAGE_RUNTIME_VERSION/runtime-$ARCH" + printf '%s %s\n' "$APPIMAGE_RUNTIME_SHA256" "$download" | sha256sum -c - + mv "$download" "$RUNTIME" + fi + printf '%s %s\n' "$APPIMAGE_RUNTIME_SHA256" "$RUNTIME" | sha256sum -c - # Absolute, because appimagetool runs in $DIST below (CARGO_TARGET_DIR or APPIMAGETOOL may be # relative). OUT="$(cd "$DIST" && pwd)/$BASENAME.AppImage" APPDIR="$(cd "$APPDIR" && pwd)" TOOL="$(cd "$(dirname "$TOOL")" && pwd)/$(basename "$TOOL")" + RUNTIME="$(cd "$(dirname "$RUNTIME")" && pwd)/$(basename "$RUNTIME")" # A .zsync left by an earlier run would hide a missing zsyncmake and describe another file. rm -f "$OUT.zsync" # Update information: AppImageUpdate, AppImageLauncher and the like read it from the file and @@ -138,7 +174,7 @@ if has appimage; then # Extract-and-run: works without FUSE (containers, CI). The output embeds the static runtime, # so users don't need libfuse2 either. With zsyncmake on the host (CI installs the zsync # package) appimagetool also writes the .zsync, into its working directory, hence the cd. - (cd "$DIST" && ARCH="$ARCH" APPIMAGE_EXTRACT_AND_RUN=1 "$TOOL" --no-appstream -u "$UPDATE_INFO" "$APPDIR" "$OUT") + (cd "$DIST" && ARCH="$ARCH" APPIMAGE_EXTRACT_AND_RUN=1 "$TOOL" --no-appstream --runtime-file "$RUNTIME" -u "$UPDATE_INFO" "$APPDIR" "$OUT") echo "wrote $OUT" if [[ -s "$OUT.zsync" ]]; then echo "wrote $OUT.zsync" diff --git a/packaging/windows/sign.ps1 b/packaging/windows/sign.ps1 index 448af0f2..40d2692e 100644 --- a/packaging/windows/sign.ps1 +++ b/packaging/windows/sign.ps1 @@ -56,8 +56,8 @@ if (-not $haveCert -and -not $haveAzure) { $script:SignTool = Find-SignTool $common = @('sign', '/v', '/fd', 'SHA256', '/td', 'SHA256', '/d', 'SoundCraft', '/du', 'https://github.com/storytold/soundcraft') -$tmp = Join-Path ([IO.Path]::GetTempPath()) "soundcraft-sign-$PID" -New-Item -ItemType Directory -Force -Path $tmp | Out-Null +$tmp = Join-Path ([IO.Path]::GetTempPath()) "soundcraft-sign-$([IO.Path]::GetRandomFileName())" +New-Item -ItemType Directory -Path $tmp | Out-Null try { if ($haveCert) { @@ -73,8 +73,15 @@ try { Write-Output "Signing with Azure Trusted Signing ($env:AZURE_SIGNING_ACCOUNT / $env:AZURE_CERT_PROFILE): $($Files -join ', ')" # The dlib authenticates with DefaultAzureCredential, which reads AZURE_TENANT_ID, # AZURE_CLIENT_ID and AZURE_CLIENT_SECRET from the environment. + $trustedSigningVersion = '1.0.95' + $trustedSigningSha256 = '3bfcf1e0a3cb42af1692f0a8ed45c15de070c2de86f28a59b2795d904d8a920f' $pkg = Join-Path $tmp 'trusted-signing.zip' - Invoke-WebRequest -Uri 'https://www.nuget.org/api/v2/package/Microsoft.Trusted.Signing.Client' -OutFile $pkg + $uri = "https://api.nuget.org/v3-flatcontainer/microsoft.trusted.signing.client/$trustedSigningVersion/microsoft.trusted.signing.client.$trustedSigningVersion.nupkg" + Invoke-WebRequest -Uri $uri -OutFile $pkg + $actualHash = (Get-FileHash -Path $pkg -Algorithm SHA256).Hash.ToLowerInvariant() + if ($actualHash -ne $trustedSigningSha256) { + throw "Microsoft.Trusted.Signing.Client $trustedSigningVersion checksum mismatch" + } Expand-Archive -Path $pkg -DestinationPath (Join-Path $tmp 'client') -Force $dlib = Get-ChildItem -Path (Join-Path $tmp 'client') -Recurse -Filter Azure.CodeSigning.Dlib.dll | Where-Object { $_.FullName -match '\\x64\\' } | Select-Object -First 1 From 9b6898273816ed44a37171d8ca9b6e54808657fe Mon Sep 17 00:00:00 2001 From: Gert Pellin - GPE Date: Fri, 23 Oct 2026 19:36:57 +0200 Subject: [PATCH 07/12] autosave: give every session its own folder; keep the tests apart from coarse mtimes (#579) (#594) With /tmp on tmpfs, four autosave and recovery tests failed: - Autosave folders were named by millisecond and process id, and created with create_dir_all, so two sessions started in the same millisecond shared one folder and the second overwrote the first's unsaved work (recovering_keeps_the_unsaved_work_it_replaces, closing_the_session_or_quitting_drops_its_autosave). Autosave::create now creates the folder exclusively and adds a -2, -3... suffix when the name is taken. - leftovers() offers an autosave only when it is strictly newer than its session file. A save and the autosave right after it can share one coarse mtime, so the tests that save and then autosave now move the saved file's mtime a second back (unsaved_work_is_offered_once_its_session_closes, an_exit_without_quit_offers_unsaved_changes_at_the_next_launch). --- crates/engine/src/autosave.rs | 45 +++++++++++++++++++++++++++++--- crates/ui-egui/tests/recovery.rs | 4 +++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/crates/engine/src/autosave.rs b/crates/engine/src/autosave.rs index 01899d2f..fd56478b 100644 --- a/crates/engine/src/autosave.rs +++ b/crates/engine/src/autosave.rs @@ -176,11 +176,25 @@ struct Autosave { } impl Autosave { - /// A new folder under `root` for the session called `name`. + /// A new folder under `root` for the session called `name`. Two sessions started within the + /// same millisecond get folders of their own, never a shared one. fn create(root: &Path, name: &str) -> Result { let stamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default().as_millis(); - let dir = root.join(format!("{} {stamp}-{}", sanitize_name(name), std::process::id())); - let lock = std::fs::create_dir_all(&dir).and_then(|()| std::fs::File::create(dir.join(LOCK))).map_err(|err| io_error(&dir, err))?; + let base = format!("{} {stamp}-{}", sanitize_name(name), std::process::id()); + std::fs::create_dir_all(root).map_err(|err| io_error(root, err))?; + let mut dir = root.join(&base); + let mut n = 1; + loop { + match std::fs::create_dir(&dir) { + Ok(()) => break, + Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => { + n += 1; + dir = root.join(format!("{base}-{n}")); + } + Err(err) => return Err(io_error(&dir, err)), + } + } + let lock = std::fs::File::create(dir.join(LOCK)).map_err(|err| io_error(&dir, err))?; lock.try_lock().map_err(|err| EngineError::Io(format!("{}: {err}", dir.display())))?; Ok(Autosave { dir, lock: Some(lock), copies: BTreeMap::new(), next: 1, recording: false }) } @@ -427,6 +441,14 @@ mod tests { dir } + /// Moves a saved session file's mtime a second into the past. Filesystems with coarse + /// timestamps (tmpfs) can give it and an autosave written right after it the same mtime, + /// and `leftovers` only offers autosaves strictly newer than the file they came from. + fn backdate(path: &Path) { + let then = SystemTime::now() - Duration::from_secs(1); + std::fs::File::options().write(true).open(path).unwrap().set_modified(then).unwrap(); + } + fn record(e: &mut Engine, level: f32) { let before = e.session_arc(); let s = e.session_mut(); @@ -453,6 +475,7 @@ mod tests { let mut e = Engine::default(); record(&mut e, 0.5); crate::io::save_session(&mut e, &path.to_string_lossy()).unwrap(); + backdate(&path); record(&mut e, -0.25); let mut a = Autosave::create(&dir.join("Autosave"), "Song").unwrap(); a.write(e.session(), e.path.as_deref(), None).unwrap(); @@ -590,6 +613,21 @@ mod tests { let _ = std::fs::remove_dir_all(&dir); } + #[test] + fn sessions_started_together_get_folders_of_their_own() { + // #579: the folder name has millisecond resolution, so two sessions created back to back + // shared one folder and the second overwrote the first's unsaved work. + let dir = folder("autosave-unique"); + let root = dir.join("Autosave"); + let made: Vec = (0..20).map(|_| Autosave::create(&root, "Song").unwrap()).collect(); + let mut dirs: Vec<&PathBuf> = made.iter().map(|a| &a.dir).collect(); + dirs.sort(); + dirs.dedup(); + assert_eq!(dirs.len(), made.len()); + drop(made); + let _ = std::fs::remove_dir_all(&dir); + } + #[test] fn unsaved_work_is_offered_once_its_session_closes() { let dir = folder("autosave-leftovers"); @@ -598,6 +636,7 @@ mod tests { std::fs::create_dir_all(&dir).unwrap(); let mut e = Engine::default(); crate::io::save_session(&mut e, &path).unwrap(); + backdate(Path::new(&path)); record(&mut e, 0.5); let mut a = Autosave::create(&root, "Song").unwrap(); a.write(e.session(), e.path.as_deref(), None).unwrap(); diff --git a/crates/ui-egui/tests/recovery.rs b/crates/ui-egui/tests/recovery.rs index 00ea2654..5c295c80 100644 --- a/crates/ui-egui/tests/recovery.rs +++ b/crates/ui-egui/tests/recovery.rs @@ -52,6 +52,10 @@ fn an_exit_without_quit_offers_unsaved_changes_at_the_next_launch() { let path = dir.join("Song.scraft"); let mut first = launch(&root); first.run("session.save_as", json!({"path": path})).unwrap(); + // Leftovers are offered only when newer than their session file; on filesystems with coarse + // timestamps (tmpfs) the save and the autosave right after it can share one mtime (#579). + let then = std::time::SystemTime::now() - std::time::Duration::from_secs(1); + std::fs::File::options().write(true).open(&path).unwrap().set_modified(then).unwrap(); first.run("track.new", json!({"name": "Vocal"})).unwrap(); first.close_autosave(); drop(first); From 265645a8fc5db0098b97b7c09786f6ec60550bf5 Mon Sep 17 00:00:00 2001 From: aiongg <67543387+aiongg@users.noreply.github.com> Date: Fri, 23 Oct 2026 13:37:13 -0400 Subject: [PATCH 08/12] Sessions: resolve stored source paths with craft-docpath's resolve_absolute (#826) * Sessions: use resolve_absolute for stored source paths * CI: check that git dependencies come from storytold * CI: release only with git dependencies from storytold * Parse Cargo.lock as TOML when checking git sources * Use the shared git-sources check in CI * CI: parse cargo config, including the legacy .cargo/config, in the git-sources check --- .github/workflows/nix.yml | 38 +++++++++++++++++++++++++++++++++ .github/workflows/release.yml | 40 +++++++++++++++++++++++++++++++++++ crates/engine/src/io.rs | 18 ++++++++++------ 3 files changed, 89 insertions(+), 7 deletions(-) diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index 01cb9cd6..318f6bba 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -21,3 +21,41 @@ jobs: nix: # Intentionally tracked at @main (not SHA-pinned) so org-wide CI improvements, security fixes, and build optimizations propagate without PR churn across all craftapp repos. uses: storytold/craft-common-actions/.github/workflows/nix.yml@main + + git-sources: + name: Git dependencies come from storytold + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Git dependencies come from storytold + run: | + set -euo pipefail + test -f Cargo.lock || { echo "Cargo.lock is missing"; exit 1; } + # Source replacement, patches and path overrides in cargo config never show up + # in Cargo.lock. Parsed rather than grepped: TOML has many spellings of one key. + # Cargo still reads the legacy .cargo/config. + python3 - <<'PY' + import os, sys, tomllib + for path in (".cargo/config", ".cargo/config.toml"): + if not os.path.exists(path): + continue + config = tomllib.load(open(path, "rb")) + replaced = [name for name, source in config.get("source", {}).items() + if isinstance(source, dict) and "replace-with" in source] + if replaced or "patch" in config or "paths" in config: + print(f"{path} replaces, patches or overrides a source") + sys.exit(1) + PY + # Fails when Cargo.lock is out of date with the manifests. + cargo metadata --locked --format-version 1 > /dev/null + python3 - <<'PY' + import sys, tomllib + lock = tomllib.load(open("Cargo.lock", "rb")) + bad = sorted({p["source"] for p in lock.get("package", []) + if p.get("source", "").startswith("git+") + and not p["source"].startswith("git+https://github.com/storytold/")}) + if bad: + print("Git dependencies outside github.com/storytold:", *bad, sep="\n") + sys.exit(1) + PY diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d60e8202..28e47f7a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,47 @@ concurrency: cancel-in-progress: false jobs: + git-sources: + name: Git dependencies come from storytold + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Git dependencies come from storytold + run: | + set -euo pipefail + test -f Cargo.lock || { echo "Cargo.lock is missing"; exit 1; } + # Source replacement, patches and path overrides in cargo config never show up + # in Cargo.lock. Parsed rather than grepped: TOML has many spellings of one key. + # Cargo still reads the legacy .cargo/config. + python3 - <<'PY' + import os, sys, tomllib + for path in (".cargo/config", ".cargo/config.toml"): + if not os.path.exists(path): + continue + config = tomllib.load(open(path, "rb")) + replaced = [name for name, source in config.get("source", {}).items() + if isinstance(source, dict) and "replace-with" in source] + if replaced or "patch" in config or "paths" in config: + print(f"{path} replaces, patches or overrides a source") + sys.exit(1) + PY + # Fails when Cargo.lock is out of date with the manifests. + cargo metadata --locked --format-version 1 > /dev/null + python3 - <<'PY' + import sys, tomllib + lock = tomllib.load(open("Cargo.lock", "rb")) + bad = sorted({p["source"] for p in lock.get("package", []) + if p.get("source", "").startswith("git+") + and not p["source"].startswith("git+https://github.com/storytold/")}) + if bad: + print("Git dependencies outside github.com/storytold:", *bad, sep="\n") + sys.exit(1) + PY + release: + needs: git-sources # Intentionally tracked at @main (not SHA-pinned) so org-wide CI improvements, security fixes, and build optimizations propagate without PR churn across all craftapp repos. uses: storytold/craft-common-actions/.github/workflows/release.yml@main with: diff --git a/crates/engine/src/io.rs b/crates/engine/src/io.rs index eff105ec..4b6601b6 100644 --- a/crates/engine/src/io.rs +++ b/crates/engine/src/io.rs @@ -68,13 +68,7 @@ pub fn file_found(path: impl AsRef) -> bool { /// Paths the person picks in this session (imports) are not stored paths and skip this. pub fn stored_path(stored: &str, session: &Path) -> std::result::Result { let session = std::path::absolute(session).unwrap_or_else(|_| session.to_path_buf()); - let path = craft_docpath::resolve(stored, Some(&session)).map_err(|why| format!("not opened: {why}"))?; - // A path that is still relative would be taken from the working directory, which the session - // does not choose. - if path.is_relative() { - return Err("not opened: the path is not absolute".into()); - } - Ok(path) + craft_docpath::resolve_absolute(stored, Some(&session)).map_err(|why| format!("not opened: {why}")) } /// Create `dir` and its parents, inside the automation write root when there is one. @@ -1883,6 +1877,16 @@ mod open_tests { assert_eq!(std::fs::read_dir(dir.join("copy/Audio Files")).unwrap().count(), 1); let _ = std::fs::remove_dir_all(&dir); } + #[test] + fn stored_paths_are_taken_from_the_session_folder() { + let session = std::env::temp_dir().join("s").join("Song.scraft"); + assert_eq!(stored_path("Audio Files/k.wav", &session), Ok(session.with_file_name("Audio Files").join("k.wav"))); + #[cfg(windows)] + assert_eq!(stored_path("Audio Files/take:2.wav", &session), Err("not opened: the path is not valid".into())); + #[cfg(not(windows))] + assert_eq!(stored_path(r"C:\Audio\k.wav", &session), Err("not opened: the path is not valid".into())); + } + #[test] fn stored_media_paths_are_followed_only_on_this_computer() { let dir = std::env::temp_dir().join(format!("soundcraft-stored-paths-{}", std::process::id())); From ab1849cbf8f3f046ad626124edce25436367b45e Mon Sep 17 00:00:00 2001 From: aiongg <67543387+aiongg@users.noreply.github.com> Date: Fri, 23 Oct 2026 13:37:39 -0400 Subject: [PATCH 09/12] Audio: check the WAVE behind a skipped RIFF form; hold egui at 0.36 in Dependabot (#978) * Audio: check the WAVE behind a RIFF form the probe skips The probe skips a RIFF form other than WAVE and reads a WAVE behind it, so the marker search skips it too and the INFO check runs on the WAVE the probe reads. The mismatched-container test moves to an input that still mismatches: a hinted FLAC behind a WAVE. * Dependabot: hold egui, eframe and egui_kittest at 0.36 craft-ui stays on egui 0.36 until storytold/craft-libs#199 lands; the daily group would otherwise propose 0.37 again. --- .github/dependabot.yml | 8 ++++++++ crates/audio-io/src/symph.rs | 6 +++++- crates/audio-io/tests/malformed.rs | 22 ++++++++++++++++------ 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2c4b8cb1..b19dc402 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,6 +12,14 @@ updates: cargo-dependencies: patterns: - "*" + # craft-ui is on egui 0.36 until storytold/craft-libs#199 lands. + ignore: + - dependency-name: "egui" + update-types: ["version-update:semver-major", "version-update:semver-minor"] + - dependency-name: "eframe" + update-types: ["version-update:semver-major", "version-update:semver-minor"] + - dependency-name: "egui_kittest" + update-types: ["version-update:semver-major", "version-update:semver-minor"] commit-message: prefix: "chore(deps)" diff --git a/crates/audio-io/src/symph.rs b/crates/audio-io/src/symph.rs index db6efbbb..d6563e1a 100644 --- a/crates/audio-io/src/symph.rs +++ b/crates/audio-io/src/symph.rs @@ -95,12 +95,16 @@ fn expected_probe_format(marker: &[u8; 4]) -> &'static [&'static str] { /// check has to run before the stream is decoded. const SIZE_CHECKED_PROBE_FORMATS: &[&str] = &["caf", "flac", "isomp4", "wave"]; -/// Find the first container marker at or after `at`, within symphonia's probe depth. +/// Find the first container marker at or after `at`, within symphonia's probe depth. A RIFF form +/// other than WAVE is skipped, as the probe skips it and reads a WAVE behind it. fn find_marker<'a>(bytes: &[u8], at: usize, markers: &'a [&'static [u8; 4]]) -> Option<(usize, &'a [u8; 4])> { let end = bytes.len().min(at.saturating_add(MAX_PROBE_DEPTH)); for pos in at..end.saturating_sub(3) { let window = bytes.get(pos..pos + 4)?; if let Some(marker) = markers.iter().find(|marker| window == marker.as_slice()) { + if matches!(*marker, b"RIFF" | b"RF64" | b"BW64") && bytes.get(pos + 8..pos + 12) != Some(&b"WAVE"[..]) { + continue; + } return Some((pos, *marker)); } } diff --git a/crates/audio-io/tests/malformed.rs b/crates/audio-io/tests/malformed.rs index f7da8746..f029b537 100644 --- a/crates/audio-io/tests/malformed.rs +++ b/crates/audio-io/tests/malformed.rs @@ -328,6 +328,18 @@ fn wav_info_entries_beyond_the_file_are_refused() { assert!(matches!(decode(&f, None), Err(AudioError::Malformed(m)) if m.contains("larger than the file"))); } +#[test] +fn a_wave_behind_another_riff_form_is_checked() { + let info = [&b"INFO"[..], b"INAM", &0x7fff_fff0u32.to_le_bytes(), b"hi"].concat(); + let mut wav = riff(&[(b"fmt ", [fmt(6, 1, 8_000, 8), vec![0, 0]].concat()), (b"LIST", info), (b"data", vec![0xD5; 800])]); + wav[4..8].copy_from_slice(&u32::MAX.to_le_bytes()); + let list = wav.windows(4).position(|w| w == b"LIST").unwrap(); + wav[list + 4..list + 8].copy_from_slice(&0x7fff_fffcu32.to_le_bytes()); + // The probe skips the AVI form and reads the WAVE behind it. + let f = [&b"RIFF"[..], &4u32.to_le_bytes(), b"AVI ", &wav].concat(); + assert!(matches!(decode(&f, None), Err(AudioError::Malformed(m)) if m.contains("larger than the file"))); +} + #[test] fn wav_info_entries_are_found_where_symphonia_reads_them() { let list = [&b"LIST"[..], &0x7fff_fffcu32.to_le_bytes(), b"INFO", b"INAM", &0x7fff_fff0u32.to_le_bytes(), b"hi"].concat(); @@ -455,12 +467,10 @@ fn garbage_in_compressed_containers_errors() { /// without its own checks: the reader is refused when the probed format disagrees. #[test] fn a_probed_container_that_disagrees_with_the_checked_marker_is_refused() { - let flac = small(FileFormat::Flac, BitDepth::Int24, 2); - let mut bytes = b"RIFF".to_vec(); - bytes.extend_from_slice(&3u32.to_le_bytes()); - bytes.extend_from_slice(b"FAKE"); - bytes.extend_from_slice(&flac); - match decode(&bytes, None) { + // The junk byte hides both magics, so the hint picks the marker: the FLAC is checked, but the + // probe reads the WAVE before it. + let bytes = [vec![0], small(FileFormat::Wav, BitDepth::Int24, 2), small(FileFormat::Flac, BitDepth::Int24, 2)].concat(); + match decode(&bytes, Some("flac")) { Err(AudioError::Malformed(message)) => assert!(message.contains("probed container"), "unexpected message: {message}"), Err(other) => panic!("unexpected error kind: {other:?}"), Ok(_) => panic!("the mismatched container must be refused"), From e8abd447128f604fc49251bc6b790644c1e14507 Mon Sep 17 00:00:00 2001 From: aiongg <67543387+aiongg@users.noreply.github.com> Date: Fri, 23 Oct 2026 14:00:38 -0400 Subject: [PATCH 10/12] CI: one test binary per crate, nextest, line tables only (#985) * Build: line tables only for workspace crates, no debug info for dependencies Full debug info made most of the dev and test target size and link time. Line tables keep file and line numbers in backtraces; dependency frames lose them. * Tests: build each crate's integration tests as one binary Each tests/*.rs file was its own test binary: 98 binaries, each linking the crate and its dependencies. The files are now modules of tests/it/main.rs, one binary per crate, which cuts link time and target size. Filter by module: `cargo test -p --test it ::`. Binaries with their own main (harness = false) in clap-host and vst3-host stay separate, as do the ui-egui tests that switch the process-wide Light/Dark palette and recovery.rs, whose autosave locks leak into child processes other tests start. * Xtask: run tests with nextest, doctests apart, and no examples in the test build cargo xtask ci runs the tests with cargo nextest when it is installed, else cargo test with a notice, and doctests with cargo test --doc. The test build leaves out examples (--lib --bins --tests); clippy --all-targets still compiles them. Test binaries with harness = false run under cargo test: their own main cannot answer nextest's --list. clippy and the wasm check keep going past a failing crate. cargo xtask test runs the test step alone. --- Cargo.toml | 2 + apps/soundcraft-cli/tests/{ => it}/cli.rs | 1 - .../tests/{ => it}/closed_stdout.rs | 0 apps/soundcraft-cli/tests/it/main.rs | 5 + crates/au-host/tests/it/main.rs | 4 + crates/au-host/tests/{ => it}/system_units.rs | 1 - .../audio-io/tests/{ => it}/encode_format.rs | 0 crates/audio-io/tests/it/main.rs | 7 ++ crates/audio-io/tests/{ => it}/malformed.rs | 0 crates/audio-io/tests/{ => it}/roundtrip.rs | 0 crates/audio-io/tests/{ => it}/stream.rs | 0 .../audio-io/tests/{ => it}/wav_empty_data.rs | 0 .../automation/tests/{ => it}/agent_tasks.rs | 0 .../tests/{ => it}/automation_roots.rs | 1 - crates/automation/tests/it/main.rs | 5 + .../clap-host/tests/{ => it}/fixture_e2e.rs | 3 +- crates/clap-host/tests/it/main.rs | 6 ++ crates/dsp/tests/{ => it}/fuzz.rs | 0 crates/dsp/tests/it/main.rs | 7 ++ crates/dsp/tests/{ => it}/offline_meters.rs | 0 .../dsp/tests/{ => it}/offline_normalize.rs | 0 crates/dsp/tests/{ => it}/plugins.rs | 0 crates/dsp/tests/{ => it}/transients.rs | 0 crates/engine/tests/{ => it}/bounce.rs | 0 crates/engine/tests/{ => it}/clap_e2e.rs | 0 .../tests/{ => it}/consolidate_selection.rs | 0 .../{ => it}/current_feet_frames_position.rs | 0 crates/engine/tests/{ => it}/edit_modes.rs | 0 .../{ => it}/fader_merge_full_history.rs | 0 .../hostile_params.proptest-regressions | 0 .../engine/tests/{ => it}/hostile_params.rs | 0 .../hostile_sessions.proptest-regressions | 0 .../engine/tests/{ => it}/hostile_sessions.rs | 0 crates/engine/tests/{ => it}/long_renders.rs | 0 crates/engine/tests/it/main.rs | 17 +++ crates/engine/tests/{ => it}/midi_offset.rs | 0 .../tests/{ => it}/mix_required_params.rs | 0 .../random_edits.proptest-regressions | 0 crates/engine/tests/{ => it}/random_edits.rs | 0 crates/engine/tests/{ => it}/recording.rs | 0 .../tests/{ => it}/session_transport.rs | 0 .../tests/{ => it}/trim_to_selection.rs | 0 crates/midi/tests/it/main.rs | 4 + crates/midi/tests/{ => it}/ops.rs | 0 crates/midi/tests/{ => it}/smf.rs | 0 crates/mix/tests/{ => it}/bus_aux_interval.rs | 1 - crates/mix/tests/it/main.rs | 6 ++ .../tests/{ => it}/master_insert_latency.rs | 1 - crates/time/tests/it/main.rs | 4 + .../tests/{ => it}/meter_grid_contract.rs | 0 .../time/tests/{ => it}/min_secs_exact_ms.rs | 0 crates/ui-egui/tests/{ => it}/appearance.rs | 0 crates/ui-egui/tests/{ => it}/audio_device.rs | 0 crates/ui-egui/tests/{ => it}/bounce.rs | 0 .../ui-egui/tests/{ => it}/bounce_dialog.rs | 0 .../ui-egui/tests/{ => it}/clip_gain_fader.rs | 0 crates/ui-egui/tests/{ => it}/clip_menu.rs | 0 .../tests/{ => it}/command_reachability.rs | 0 .../ui-egui/tests/{ => it}/commands_filter.rs | 0 .../tests/{ => it}/docking_workflows.rs | 0 crates/ui-egui/tests/{ => it}/edit_menu.rs | 0 crates/ui-egui/tests/{ => it}/edit_modes.rs | 0 .../tests/{ => it}/edit_window_headers.rs | 0 crates/ui-egui/tests/{ => it}/group_dialog.rs | 0 .../ui-egui/tests/{ => it}/hide_floating.rs | 0 crates/ui-egui/tests/{ => it}/idle.rs | 0 crates/ui-egui/tests/{ => it}/key_families.rs | 0 crates/ui-egui/tests/it/main.rs | 53 +++++++++ .../ui-egui/tests/{ => it}/marker_rename.rs | 0 crates/ui-egui/tests/{ => it}/menu_search.rs | 0 .../tests/{ => it}/midi_double_click.rs | 0 .../tests/{ => it}/midi_editor_keys.rs | 0 .../tests/{ => it}/midi_front_visibility.rs | 0 crates/ui-egui/tests/{ => it}/midi_panel.rs | 0 .../ui-egui/tests/{ => it}/midi_quantize.rs | 0 .../tests/{ => it}/mix_narrow_headers.rs | 0 crates/ui-egui/tests/{ => it}/mix_separate.rs | 0 .../ui-egui/tests/{ => it}/new_midi_clip.rs | 0 crates/ui-egui/tests/{ => it}/new_tracks.rs | 0 .../ui-egui/tests/{ => it}/open_on_insert.rs | 0 .../ui-egui/tests/{ => it}/plugin_window.rs | 0 .../ui-egui/tests/{ => it}/pro_tools_keys.rs | 0 crates/ui-egui/tests/{ => it}/record_hint.rs | 0 .../ui-egui/tests/{ => it}/rename_dialog.rs | 0 .../ui-egui/tests/{ => it}/sampler_window.rs | 0 .../ui-egui/tests/{ => it}/save_as_replace.rs | 0 crates/ui-egui/tests/{ => it}/save_changes.rs | 0 crates/ui-egui/tests/{ => it}/send_window.rs | 0 .../ui-egui/tests/{ => it}/shared_controls.rs | 0 crates/ui-egui/tests/{ => it}/shortcuts.rs | 0 .../tests/{ => it}/side_lists_access.rs | 0 .../ui-egui/tests/{ => it}/toolbar_tempo.rs | 0 crates/ui-egui/tests/{ => it}/track_height.rs | 0 crates/ui-egui/tests/{ => it}/track_list.rs | 0 .../ui-egui/tests/{ => it}/track_name_menu.rs | 0 crates/ui-egui/tests/{ => it}/trackpad.rs | 0 .../tests/{ => it}/tracks_scroll_bar.rs | 0 crates/ui-egui/tests/{ => it}/waveform.rs | 0 .../tests/{ => it}/window_shortcuts.rs | 0 crates/ui-egui/tests/{ => it}/zoom_toggle.rs | 0 .../vst3-host/tests/{ => it}/fixture_e2e.rs | 3 +- crates/vst3-host/tests/it/main.rs | 6 ++ docs/mcp.md | 2 +- xtask/src/main.rs | 102 ++++++++++++++++-- 104 files changed, 223 insertions(+), 18 deletions(-) rename apps/soundcraft-cli/tests/{ => it}/cli.rs (99%) rename apps/soundcraft-cli/tests/{ => it}/closed_stdout.rs (100%) create mode 100644 apps/soundcraft-cli/tests/it/main.rs create mode 100644 crates/au-host/tests/it/main.rs rename crates/au-host/tests/{ => it}/system_units.rs (99%) rename crates/audio-io/tests/{ => it}/encode_format.rs (100%) create mode 100644 crates/audio-io/tests/it/main.rs rename crates/audio-io/tests/{ => it}/malformed.rs (100%) rename crates/audio-io/tests/{ => it}/roundtrip.rs (100%) rename crates/audio-io/tests/{ => it}/stream.rs (100%) rename crates/audio-io/tests/{ => it}/wav_empty_data.rs (100%) rename crates/automation/tests/{ => it}/agent_tasks.rs (100%) rename crates/automation/tests/{ => it}/automation_roots.rs (99%) create mode 100644 crates/automation/tests/it/main.rs rename crates/clap-host/tests/{ => it}/fixture_e2e.rs (99%) create mode 100644 crates/clap-host/tests/it/main.rs rename crates/dsp/tests/{ => it}/fuzz.rs (100%) create mode 100644 crates/dsp/tests/it/main.rs rename crates/dsp/tests/{ => it}/offline_meters.rs (100%) rename crates/dsp/tests/{ => it}/offline_normalize.rs (100%) rename crates/dsp/tests/{ => it}/plugins.rs (100%) rename crates/dsp/tests/{ => it}/transients.rs (100%) rename crates/engine/tests/{ => it}/bounce.rs (100%) rename crates/engine/tests/{ => it}/clap_e2e.rs (100%) rename crates/engine/tests/{ => it}/consolidate_selection.rs (100%) rename crates/engine/tests/{ => it}/current_feet_frames_position.rs (100%) rename crates/engine/tests/{ => it}/edit_modes.rs (100%) rename crates/engine/tests/{ => it}/fader_merge_full_history.rs (100%) rename crates/engine/tests/{ => it}/hostile_params.proptest-regressions (100%) rename crates/engine/tests/{ => it}/hostile_params.rs (100%) rename crates/engine/tests/{ => it}/hostile_sessions.proptest-regressions (100%) rename crates/engine/tests/{ => it}/hostile_sessions.rs (100%) rename crates/engine/tests/{ => it}/long_renders.rs (100%) create mode 100644 crates/engine/tests/it/main.rs rename crates/engine/tests/{ => it}/midi_offset.rs (100%) rename crates/engine/tests/{ => it}/mix_required_params.rs (100%) rename crates/engine/tests/{ => it}/random_edits.proptest-regressions (100%) rename crates/engine/tests/{ => it}/random_edits.rs (100%) rename crates/engine/tests/{ => it}/recording.rs (100%) rename crates/engine/tests/{ => it}/session_transport.rs (100%) rename crates/engine/tests/{ => it}/trim_to_selection.rs (100%) create mode 100644 crates/midi/tests/it/main.rs rename crates/midi/tests/{ => it}/ops.rs (100%) rename crates/midi/tests/{ => it}/smf.rs (100%) rename crates/mix/tests/{ => it}/bus_aux_interval.rs (98%) create mode 100644 crates/mix/tests/it/main.rs rename crates/mix/tests/{ => it}/master_insert_latency.rs (97%) create mode 100644 crates/time/tests/it/main.rs rename crates/time/tests/{ => it}/meter_grid_contract.rs (100%) rename crates/time/tests/{ => it}/min_secs_exact_ms.rs (100%) rename crates/ui-egui/tests/{ => it}/appearance.rs (100%) rename crates/ui-egui/tests/{ => it}/audio_device.rs (100%) rename crates/ui-egui/tests/{ => it}/bounce.rs (100%) rename crates/ui-egui/tests/{ => it}/bounce_dialog.rs (100%) rename crates/ui-egui/tests/{ => it}/clip_gain_fader.rs (100%) rename crates/ui-egui/tests/{ => it}/clip_menu.rs (100%) rename crates/ui-egui/tests/{ => it}/command_reachability.rs (100%) rename crates/ui-egui/tests/{ => it}/commands_filter.rs (100%) rename crates/ui-egui/tests/{ => it}/docking_workflows.rs (100%) rename crates/ui-egui/tests/{ => it}/edit_menu.rs (100%) rename crates/ui-egui/tests/{ => it}/edit_modes.rs (100%) rename crates/ui-egui/tests/{ => it}/edit_window_headers.rs (100%) rename crates/ui-egui/tests/{ => it}/group_dialog.rs (100%) rename crates/ui-egui/tests/{ => it}/hide_floating.rs (100%) rename crates/ui-egui/tests/{ => it}/idle.rs (100%) rename crates/ui-egui/tests/{ => it}/key_families.rs (100%) create mode 100644 crates/ui-egui/tests/it/main.rs rename crates/ui-egui/tests/{ => it}/marker_rename.rs (100%) rename crates/ui-egui/tests/{ => it}/menu_search.rs (100%) rename crates/ui-egui/tests/{ => it}/midi_double_click.rs (100%) rename crates/ui-egui/tests/{ => it}/midi_editor_keys.rs (100%) rename crates/ui-egui/tests/{ => it}/midi_front_visibility.rs (100%) rename crates/ui-egui/tests/{ => it}/midi_panel.rs (100%) rename crates/ui-egui/tests/{ => it}/midi_quantize.rs (100%) rename crates/ui-egui/tests/{ => it}/mix_narrow_headers.rs (100%) rename crates/ui-egui/tests/{ => it}/mix_separate.rs (100%) rename crates/ui-egui/tests/{ => it}/new_midi_clip.rs (100%) rename crates/ui-egui/tests/{ => it}/new_tracks.rs (100%) rename crates/ui-egui/tests/{ => it}/open_on_insert.rs (100%) rename crates/ui-egui/tests/{ => it}/plugin_window.rs (100%) rename crates/ui-egui/tests/{ => it}/pro_tools_keys.rs (100%) rename crates/ui-egui/tests/{ => it}/record_hint.rs (100%) rename crates/ui-egui/tests/{ => it}/rename_dialog.rs (100%) rename crates/ui-egui/tests/{ => it}/sampler_window.rs (100%) rename crates/ui-egui/tests/{ => it}/save_as_replace.rs (100%) rename crates/ui-egui/tests/{ => it}/save_changes.rs (100%) rename crates/ui-egui/tests/{ => it}/send_window.rs (100%) rename crates/ui-egui/tests/{ => it}/shared_controls.rs (100%) rename crates/ui-egui/tests/{ => it}/shortcuts.rs (100%) rename crates/ui-egui/tests/{ => it}/side_lists_access.rs (100%) rename crates/ui-egui/tests/{ => it}/toolbar_tempo.rs (100%) rename crates/ui-egui/tests/{ => it}/track_height.rs (100%) rename crates/ui-egui/tests/{ => it}/track_list.rs (100%) rename crates/ui-egui/tests/{ => it}/track_name_menu.rs (100%) rename crates/ui-egui/tests/{ => it}/trackpad.rs (100%) rename crates/ui-egui/tests/{ => it}/tracks_scroll_bar.rs (100%) rename crates/ui-egui/tests/{ => it}/waveform.rs (100%) rename crates/ui-egui/tests/{ => it}/window_shortcuts.rs (100%) rename crates/ui-egui/tests/{ => it}/zoom_toggle.rs (100%) rename crates/vst3-host/tests/{ => it}/fixture_e2e.rs (99%) create mode 100644 crates/vst3-host/tests/it/main.rs diff --git a/Cargo.toml b/Cargo.toml index b5ea0ddc..d2fe3187 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,9 +67,11 @@ type_complexity = "allow" [profile.dev.package."*"] opt-level = 2 +debug = false [profile.dev] opt-level = 1 +debug = "line-tables-only" [profile.release] opt-level = 3 diff --git a/apps/soundcraft-cli/tests/cli.rs b/apps/soundcraft-cli/tests/it/cli.rs similarity index 99% rename from apps/soundcraft-cli/tests/cli.rs rename to apps/soundcraft-cli/tests/it/cli.rs index ef2fb881..c10bcf60 100644 --- a/apps/soundcraft-cli/tests/cli.rs +++ b/apps/soundcraft-cli/tests/it/cli.rs @@ -1,6 +1,5 @@ //! `soundcraft-cli mcp` with the automation root flags, run as the real binary. // Integration tests: unwrapping and panicking on failure is fine here, unlike in shipped code (AGENTS.md). -#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] use std::io::Write; use std::path::PathBuf; diff --git a/apps/soundcraft-cli/tests/closed_stdout.rs b/apps/soundcraft-cli/tests/it/closed_stdout.rs similarity index 100% rename from apps/soundcraft-cli/tests/closed_stdout.rs rename to apps/soundcraft-cli/tests/it/closed_stdout.rs diff --git a/apps/soundcraft-cli/tests/it/main.rs b/apps/soundcraft-cli/tests/it/main.rs new file mode 100644 index 00000000..c573712b --- /dev/null +++ b/apps/soundcraft-cli/tests/it/main.rs @@ -0,0 +1,5 @@ +//! Integration tests, built as one test binary: each file is a module. + +#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] +mod cli; +mod closed_stdout; diff --git a/crates/au-host/tests/it/main.rs b/crates/au-host/tests/it/main.rs new file mode 100644 index 00000000..a4505476 --- /dev/null +++ b/crates/au-host/tests/it/main.rs @@ -0,0 +1,4 @@ +//! Integration tests, built as one test binary: each file is a module. + +#[cfg(target_os = "macos")] +mod system_units; diff --git a/crates/au-host/tests/system_units.rs b/crates/au-host/tests/it/system_units.rs similarity index 99% rename from crates/au-host/tests/system_units.rs rename to crates/au-host/tests/it/system_units.rs index 8f4e6924..3d16749e 100644 --- a/crates/au-host/tests/system_units.rs +++ b/crates/au-host/tests/it/system_units.rs @@ -1,6 +1,5 @@ //! End to end against the Audio Units Apple ships with macOS: scan → instantiate → params → //! process → state, plus an instrument playing a note. Skips (passes) when a unit is missing. -#![cfg(target_os = "macos")] use soundcraft_dsp::{Category, Plugin}; diff --git a/crates/audio-io/tests/encode_format.rs b/crates/audio-io/tests/it/encode_format.rs similarity index 100% rename from crates/audio-io/tests/encode_format.rs rename to crates/audio-io/tests/it/encode_format.rs diff --git a/crates/audio-io/tests/it/main.rs b/crates/audio-io/tests/it/main.rs new file mode 100644 index 00000000..36b5129f --- /dev/null +++ b/crates/audio-io/tests/it/main.rs @@ -0,0 +1,7 @@ +//! Integration tests, built as one test binary: each file is a module. + +mod encode_format; +mod malformed; +mod roundtrip; +mod stream; +mod wav_empty_data; diff --git a/crates/audio-io/tests/malformed.rs b/crates/audio-io/tests/it/malformed.rs similarity index 100% rename from crates/audio-io/tests/malformed.rs rename to crates/audio-io/tests/it/malformed.rs diff --git a/crates/audio-io/tests/roundtrip.rs b/crates/audio-io/tests/it/roundtrip.rs similarity index 100% rename from crates/audio-io/tests/roundtrip.rs rename to crates/audio-io/tests/it/roundtrip.rs diff --git a/crates/audio-io/tests/stream.rs b/crates/audio-io/tests/it/stream.rs similarity index 100% rename from crates/audio-io/tests/stream.rs rename to crates/audio-io/tests/it/stream.rs diff --git a/crates/audio-io/tests/wav_empty_data.rs b/crates/audio-io/tests/it/wav_empty_data.rs similarity index 100% rename from crates/audio-io/tests/wav_empty_data.rs rename to crates/audio-io/tests/it/wav_empty_data.rs diff --git a/crates/automation/tests/agent_tasks.rs b/crates/automation/tests/it/agent_tasks.rs similarity index 100% rename from crates/automation/tests/agent_tasks.rs rename to crates/automation/tests/it/agent_tasks.rs diff --git a/crates/automation/tests/automation_roots.rs b/crates/automation/tests/it/automation_roots.rs similarity index 99% rename from crates/automation/tests/automation_roots.rs rename to crates/automation/tests/it/automation_roots.rs index 2c4fa160..58d4aad2 100644 --- a/crates/automation/tests/automation_roots.rs +++ b/crates/automation/tests/it/automation_roots.rs @@ -1,7 +1,6 @@ //! `Headless::with_automation_roots` through the real MCP protocol: every file a command reads //! stays inside the read root, every file it writes inside the write root. // Integration tests: unwrapping and panicking on failure is fine here, unlike in shipped code (AGENTS.md). -#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] use serde_json::{Value, json}; use soundcraft_automation::{Headless, mcp::Server}; diff --git a/crates/automation/tests/it/main.rs b/crates/automation/tests/it/main.rs new file mode 100644 index 00000000..fc738242 --- /dev/null +++ b/crates/automation/tests/it/main.rs @@ -0,0 +1,5 @@ +//! Integration tests, built as one test binary: each file is a module. + +mod agent_tasks; +#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] +mod automation_roots; diff --git a/crates/clap-host/tests/fixture_e2e.rs b/crates/clap-host/tests/it/fixture_e2e.rs similarity index 99% rename from crates/clap-host/tests/fixture_e2e.rs rename to crates/clap-host/tests/it/fixture_e2e.rs index 8525d740..f531db54 100644 --- a/crates/clap-host/tests/fixture_e2e.rs +++ b/crates/clap-host/tests/it/fixture_e2e.rs @@ -3,8 +3,7 @@ use soundcraft_dsp::{Category, Unit}; -mod common; -use common::fixture_dir; +use crate::common::fixture_dir; const GAIN: &str = "clap:org.soundcraft.test.gain"; const SYNTH: &str = "clap:org.soundcraft.test.synth"; diff --git a/crates/clap-host/tests/it/main.rs b/crates/clap-host/tests/it/main.rs new file mode 100644 index 00000000..ce956746 --- /dev/null +++ b/crates/clap-host/tests/it/main.rs @@ -0,0 +1,6 @@ +//! Integration tests, built as one test binary: each file is a module. + +#[path = "../common/mod.rs"] +mod common; + +mod fixture_e2e; diff --git a/crates/dsp/tests/fuzz.rs b/crates/dsp/tests/it/fuzz.rs similarity index 100% rename from crates/dsp/tests/fuzz.rs rename to crates/dsp/tests/it/fuzz.rs diff --git a/crates/dsp/tests/it/main.rs b/crates/dsp/tests/it/main.rs new file mode 100644 index 00000000..c214cd3e --- /dev/null +++ b/crates/dsp/tests/it/main.rs @@ -0,0 +1,7 @@ +//! Integration tests, built as one test binary: each file is a module. + +mod fuzz; +mod offline_meters; +mod offline_normalize; +mod plugins; +mod transients; diff --git a/crates/dsp/tests/offline_meters.rs b/crates/dsp/tests/it/offline_meters.rs similarity index 100% rename from crates/dsp/tests/offline_meters.rs rename to crates/dsp/tests/it/offline_meters.rs diff --git a/crates/dsp/tests/offline_normalize.rs b/crates/dsp/tests/it/offline_normalize.rs similarity index 100% rename from crates/dsp/tests/offline_normalize.rs rename to crates/dsp/tests/it/offline_normalize.rs diff --git a/crates/dsp/tests/plugins.rs b/crates/dsp/tests/it/plugins.rs similarity index 100% rename from crates/dsp/tests/plugins.rs rename to crates/dsp/tests/it/plugins.rs diff --git a/crates/dsp/tests/transients.rs b/crates/dsp/tests/it/transients.rs similarity index 100% rename from crates/dsp/tests/transients.rs rename to crates/dsp/tests/it/transients.rs diff --git a/crates/engine/tests/bounce.rs b/crates/engine/tests/it/bounce.rs similarity index 100% rename from crates/engine/tests/bounce.rs rename to crates/engine/tests/it/bounce.rs diff --git a/crates/engine/tests/clap_e2e.rs b/crates/engine/tests/it/clap_e2e.rs similarity index 100% rename from crates/engine/tests/clap_e2e.rs rename to crates/engine/tests/it/clap_e2e.rs diff --git a/crates/engine/tests/consolidate_selection.rs b/crates/engine/tests/it/consolidate_selection.rs similarity index 100% rename from crates/engine/tests/consolidate_selection.rs rename to crates/engine/tests/it/consolidate_selection.rs diff --git a/crates/engine/tests/current_feet_frames_position.rs b/crates/engine/tests/it/current_feet_frames_position.rs similarity index 100% rename from crates/engine/tests/current_feet_frames_position.rs rename to crates/engine/tests/it/current_feet_frames_position.rs diff --git a/crates/engine/tests/edit_modes.rs b/crates/engine/tests/it/edit_modes.rs similarity index 100% rename from crates/engine/tests/edit_modes.rs rename to crates/engine/tests/it/edit_modes.rs diff --git a/crates/engine/tests/fader_merge_full_history.rs b/crates/engine/tests/it/fader_merge_full_history.rs similarity index 100% rename from crates/engine/tests/fader_merge_full_history.rs rename to crates/engine/tests/it/fader_merge_full_history.rs diff --git a/crates/engine/tests/hostile_params.proptest-regressions b/crates/engine/tests/it/hostile_params.proptest-regressions similarity index 100% rename from crates/engine/tests/hostile_params.proptest-regressions rename to crates/engine/tests/it/hostile_params.proptest-regressions diff --git a/crates/engine/tests/hostile_params.rs b/crates/engine/tests/it/hostile_params.rs similarity index 100% rename from crates/engine/tests/hostile_params.rs rename to crates/engine/tests/it/hostile_params.rs diff --git a/crates/engine/tests/hostile_sessions.proptest-regressions b/crates/engine/tests/it/hostile_sessions.proptest-regressions similarity index 100% rename from crates/engine/tests/hostile_sessions.proptest-regressions rename to crates/engine/tests/it/hostile_sessions.proptest-regressions diff --git a/crates/engine/tests/hostile_sessions.rs b/crates/engine/tests/it/hostile_sessions.rs similarity index 100% rename from crates/engine/tests/hostile_sessions.rs rename to crates/engine/tests/it/hostile_sessions.rs diff --git a/crates/engine/tests/long_renders.rs b/crates/engine/tests/it/long_renders.rs similarity index 100% rename from crates/engine/tests/long_renders.rs rename to crates/engine/tests/it/long_renders.rs diff --git a/crates/engine/tests/it/main.rs b/crates/engine/tests/it/main.rs new file mode 100644 index 00000000..f021c292 --- /dev/null +++ b/crates/engine/tests/it/main.rs @@ -0,0 +1,17 @@ +//! Integration tests, built as one test binary: each file is a module. + +mod bounce; +mod clap_e2e; +mod consolidate_selection; +mod current_feet_frames_position; +mod edit_modes; +mod fader_merge_full_history; +mod hostile_params; +mod hostile_sessions; +mod long_renders; +mod midi_offset; +mod mix_required_params; +mod random_edits; +mod recording; +mod session_transport; +mod trim_to_selection; diff --git a/crates/engine/tests/midi_offset.rs b/crates/engine/tests/it/midi_offset.rs similarity index 100% rename from crates/engine/tests/midi_offset.rs rename to crates/engine/tests/it/midi_offset.rs diff --git a/crates/engine/tests/mix_required_params.rs b/crates/engine/tests/it/mix_required_params.rs similarity index 100% rename from crates/engine/tests/mix_required_params.rs rename to crates/engine/tests/it/mix_required_params.rs diff --git a/crates/engine/tests/random_edits.proptest-regressions b/crates/engine/tests/it/random_edits.proptest-regressions similarity index 100% rename from crates/engine/tests/random_edits.proptest-regressions rename to crates/engine/tests/it/random_edits.proptest-regressions diff --git a/crates/engine/tests/random_edits.rs b/crates/engine/tests/it/random_edits.rs similarity index 100% rename from crates/engine/tests/random_edits.rs rename to crates/engine/tests/it/random_edits.rs diff --git a/crates/engine/tests/recording.rs b/crates/engine/tests/it/recording.rs similarity index 100% rename from crates/engine/tests/recording.rs rename to crates/engine/tests/it/recording.rs diff --git a/crates/engine/tests/session_transport.rs b/crates/engine/tests/it/session_transport.rs similarity index 100% rename from crates/engine/tests/session_transport.rs rename to crates/engine/tests/it/session_transport.rs diff --git a/crates/engine/tests/trim_to_selection.rs b/crates/engine/tests/it/trim_to_selection.rs similarity index 100% rename from crates/engine/tests/trim_to_selection.rs rename to crates/engine/tests/it/trim_to_selection.rs diff --git a/crates/midi/tests/it/main.rs b/crates/midi/tests/it/main.rs new file mode 100644 index 00000000..26fb0ad9 --- /dev/null +++ b/crates/midi/tests/it/main.rs @@ -0,0 +1,4 @@ +//! Integration tests, built as one test binary: each file is a module. + +mod ops; +mod smf; diff --git a/crates/midi/tests/ops.rs b/crates/midi/tests/it/ops.rs similarity index 100% rename from crates/midi/tests/ops.rs rename to crates/midi/tests/it/ops.rs diff --git a/crates/midi/tests/smf.rs b/crates/midi/tests/it/smf.rs similarity index 100% rename from crates/midi/tests/smf.rs rename to crates/midi/tests/it/smf.rs diff --git a/crates/mix/tests/bus_aux_interval.rs b/crates/mix/tests/it/bus_aux_interval.rs similarity index 98% rename from crates/mix/tests/bus_aux_interval.rs rename to crates/mix/tests/it/bus_aux_interval.rs index ff3b6813..e672e536 100644 --- a/crates/mix/tests/bus_aux_interval.rs +++ b/crates/mix/tests/it/bus_aux_interval.rs @@ -1,6 +1,5 @@ //! A bounce of a bus keeps the requested timeline interval when an aux returns onto the bus //! (its insert's lookahead is compensated like the main mix's). -#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] use soundcraft_audio_io::AudioBuffer; use soundcraft_mix::{render_bus, render_range}; diff --git a/crates/mix/tests/it/main.rs b/crates/mix/tests/it/main.rs new file mode 100644 index 00000000..77997334 --- /dev/null +++ b/crates/mix/tests/it/main.rs @@ -0,0 +1,6 @@ +//! Integration tests, built as one test binary: each file is a module. + +#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] +mod bus_aux_interval; +#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] +mod master_insert_latency; diff --git a/crates/mix/tests/master_insert_latency.rs b/crates/mix/tests/it/master_insert_latency.rs similarity index 97% rename from crates/mix/tests/master_insert_latency.rs rename to crates/mix/tests/it/master_insert_latency.rs index d4aed976..981c080f 100644 --- a/crates/mix/tests/master_insert_latency.rs +++ b/crates/mix/tests/it/master_insert_latency.rs @@ -1,6 +1,5 @@ //! Delay compensation covers the inserts of the master faders: a bounce of the main mix lines up //! with the same mix without them. -#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] use soundcraft_audio_io::AudioBuffer; use soundcraft_mix::{MixEngine, render_range}; diff --git a/crates/time/tests/it/main.rs b/crates/time/tests/it/main.rs new file mode 100644 index 00000000..a43e92bf --- /dev/null +++ b/crates/time/tests/it/main.rs @@ -0,0 +1,4 @@ +//! Integration tests, built as one test binary: each file is a module. + +mod meter_grid_contract; +mod min_secs_exact_ms; diff --git a/crates/time/tests/meter_grid_contract.rs b/crates/time/tests/it/meter_grid_contract.rs similarity index 100% rename from crates/time/tests/meter_grid_contract.rs rename to crates/time/tests/it/meter_grid_contract.rs diff --git a/crates/time/tests/min_secs_exact_ms.rs b/crates/time/tests/it/min_secs_exact_ms.rs similarity index 100% rename from crates/time/tests/min_secs_exact_ms.rs rename to crates/time/tests/it/min_secs_exact_ms.rs diff --git a/crates/ui-egui/tests/appearance.rs b/crates/ui-egui/tests/it/appearance.rs similarity index 100% rename from crates/ui-egui/tests/appearance.rs rename to crates/ui-egui/tests/it/appearance.rs diff --git a/crates/ui-egui/tests/audio_device.rs b/crates/ui-egui/tests/it/audio_device.rs similarity index 100% rename from crates/ui-egui/tests/audio_device.rs rename to crates/ui-egui/tests/it/audio_device.rs diff --git a/crates/ui-egui/tests/bounce.rs b/crates/ui-egui/tests/it/bounce.rs similarity index 100% rename from crates/ui-egui/tests/bounce.rs rename to crates/ui-egui/tests/it/bounce.rs diff --git a/crates/ui-egui/tests/bounce_dialog.rs b/crates/ui-egui/tests/it/bounce_dialog.rs similarity index 100% rename from crates/ui-egui/tests/bounce_dialog.rs rename to crates/ui-egui/tests/it/bounce_dialog.rs diff --git a/crates/ui-egui/tests/clip_gain_fader.rs b/crates/ui-egui/tests/it/clip_gain_fader.rs similarity index 100% rename from crates/ui-egui/tests/clip_gain_fader.rs rename to crates/ui-egui/tests/it/clip_gain_fader.rs diff --git a/crates/ui-egui/tests/clip_menu.rs b/crates/ui-egui/tests/it/clip_menu.rs similarity index 100% rename from crates/ui-egui/tests/clip_menu.rs rename to crates/ui-egui/tests/it/clip_menu.rs diff --git a/crates/ui-egui/tests/command_reachability.rs b/crates/ui-egui/tests/it/command_reachability.rs similarity index 100% rename from crates/ui-egui/tests/command_reachability.rs rename to crates/ui-egui/tests/it/command_reachability.rs diff --git a/crates/ui-egui/tests/commands_filter.rs b/crates/ui-egui/tests/it/commands_filter.rs similarity index 100% rename from crates/ui-egui/tests/commands_filter.rs rename to crates/ui-egui/tests/it/commands_filter.rs diff --git a/crates/ui-egui/tests/docking_workflows.rs b/crates/ui-egui/tests/it/docking_workflows.rs similarity index 100% rename from crates/ui-egui/tests/docking_workflows.rs rename to crates/ui-egui/tests/it/docking_workflows.rs diff --git a/crates/ui-egui/tests/edit_menu.rs b/crates/ui-egui/tests/it/edit_menu.rs similarity index 100% rename from crates/ui-egui/tests/edit_menu.rs rename to crates/ui-egui/tests/it/edit_menu.rs diff --git a/crates/ui-egui/tests/edit_modes.rs b/crates/ui-egui/tests/it/edit_modes.rs similarity index 100% rename from crates/ui-egui/tests/edit_modes.rs rename to crates/ui-egui/tests/it/edit_modes.rs diff --git a/crates/ui-egui/tests/edit_window_headers.rs b/crates/ui-egui/tests/it/edit_window_headers.rs similarity index 100% rename from crates/ui-egui/tests/edit_window_headers.rs rename to crates/ui-egui/tests/it/edit_window_headers.rs diff --git a/crates/ui-egui/tests/group_dialog.rs b/crates/ui-egui/tests/it/group_dialog.rs similarity index 100% rename from crates/ui-egui/tests/group_dialog.rs rename to crates/ui-egui/tests/it/group_dialog.rs diff --git a/crates/ui-egui/tests/hide_floating.rs b/crates/ui-egui/tests/it/hide_floating.rs similarity index 100% rename from crates/ui-egui/tests/hide_floating.rs rename to crates/ui-egui/tests/it/hide_floating.rs diff --git a/crates/ui-egui/tests/idle.rs b/crates/ui-egui/tests/it/idle.rs similarity index 100% rename from crates/ui-egui/tests/idle.rs rename to crates/ui-egui/tests/it/idle.rs diff --git a/crates/ui-egui/tests/key_families.rs b/crates/ui-egui/tests/it/key_families.rs similarity index 100% rename from crates/ui-egui/tests/key_families.rs rename to crates/ui-egui/tests/it/key_families.rs diff --git a/crates/ui-egui/tests/it/main.rs b/crates/ui-egui/tests/it/main.rs new file mode 100644 index 00000000..d9eb9fe6 --- /dev/null +++ b/crates/ui-egui/tests/it/main.rs @@ -0,0 +1,53 @@ +//! Integration tests, built as one test binary: each file is a module. +//! +//! Tests that switch the process-wide Light/Dark palette, and `recovery.rs` (whose autosave locks +//! leak into child processes other tests start), are separate binaries in `tests/`. + +mod appearance; +mod audio_device; +mod bounce; +mod bounce_dialog; +mod clip_gain_fader; +mod clip_menu; +mod command_reachability; +mod commands_filter; +mod docking_workflows; +mod edit_menu; +mod edit_modes; +mod edit_window_headers; +mod group_dialog; +mod hide_floating; +mod idle; +mod key_families; +mod marker_rename; +mod menu_search; +mod midi_double_click; +mod midi_editor_keys; +mod midi_front_visibility; +mod midi_panel; +mod midi_quantize; +mod mix_narrow_headers; +mod mix_separate; +mod new_midi_clip; +mod new_tracks; +mod open_on_insert; +mod plugin_window; +mod pro_tools_keys; +mod record_hint; +mod rename_dialog; +mod sampler_window; +mod save_as_replace; +mod save_changes; +mod send_window; +mod shared_controls; +mod shortcuts; +mod side_lists_access; +mod toolbar_tempo; +mod track_height; +mod track_list; +mod track_name_menu; +mod trackpad; +mod tracks_scroll_bar; +mod waveform; +mod window_shortcuts; +mod zoom_toggle; diff --git a/crates/ui-egui/tests/marker_rename.rs b/crates/ui-egui/tests/it/marker_rename.rs similarity index 100% rename from crates/ui-egui/tests/marker_rename.rs rename to crates/ui-egui/tests/it/marker_rename.rs diff --git a/crates/ui-egui/tests/menu_search.rs b/crates/ui-egui/tests/it/menu_search.rs similarity index 100% rename from crates/ui-egui/tests/menu_search.rs rename to crates/ui-egui/tests/it/menu_search.rs diff --git a/crates/ui-egui/tests/midi_double_click.rs b/crates/ui-egui/tests/it/midi_double_click.rs similarity index 100% rename from crates/ui-egui/tests/midi_double_click.rs rename to crates/ui-egui/tests/it/midi_double_click.rs diff --git a/crates/ui-egui/tests/midi_editor_keys.rs b/crates/ui-egui/tests/it/midi_editor_keys.rs similarity index 100% rename from crates/ui-egui/tests/midi_editor_keys.rs rename to crates/ui-egui/tests/it/midi_editor_keys.rs diff --git a/crates/ui-egui/tests/midi_front_visibility.rs b/crates/ui-egui/tests/it/midi_front_visibility.rs similarity index 100% rename from crates/ui-egui/tests/midi_front_visibility.rs rename to crates/ui-egui/tests/it/midi_front_visibility.rs diff --git a/crates/ui-egui/tests/midi_panel.rs b/crates/ui-egui/tests/it/midi_panel.rs similarity index 100% rename from crates/ui-egui/tests/midi_panel.rs rename to crates/ui-egui/tests/it/midi_panel.rs diff --git a/crates/ui-egui/tests/midi_quantize.rs b/crates/ui-egui/tests/it/midi_quantize.rs similarity index 100% rename from crates/ui-egui/tests/midi_quantize.rs rename to crates/ui-egui/tests/it/midi_quantize.rs diff --git a/crates/ui-egui/tests/mix_narrow_headers.rs b/crates/ui-egui/tests/it/mix_narrow_headers.rs similarity index 100% rename from crates/ui-egui/tests/mix_narrow_headers.rs rename to crates/ui-egui/tests/it/mix_narrow_headers.rs diff --git a/crates/ui-egui/tests/mix_separate.rs b/crates/ui-egui/tests/it/mix_separate.rs similarity index 100% rename from crates/ui-egui/tests/mix_separate.rs rename to crates/ui-egui/tests/it/mix_separate.rs diff --git a/crates/ui-egui/tests/new_midi_clip.rs b/crates/ui-egui/tests/it/new_midi_clip.rs similarity index 100% rename from crates/ui-egui/tests/new_midi_clip.rs rename to crates/ui-egui/tests/it/new_midi_clip.rs diff --git a/crates/ui-egui/tests/new_tracks.rs b/crates/ui-egui/tests/it/new_tracks.rs similarity index 100% rename from crates/ui-egui/tests/new_tracks.rs rename to crates/ui-egui/tests/it/new_tracks.rs diff --git a/crates/ui-egui/tests/open_on_insert.rs b/crates/ui-egui/tests/it/open_on_insert.rs similarity index 100% rename from crates/ui-egui/tests/open_on_insert.rs rename to crates/ui-egui/tests/it/open_on_insert.rs diff --git a/crates/ui-egui/tests/plugin_window.rs b/crates/ui-egui/tests/it/plugin_window.rs similarity index 100% rename from crates/ui-egui/tests/plugin_window.rs rename to crates/ui-egui/tests/it/plugin_window.rs diff --git a/crates/ui-egui/tests/pro_tools_keys.rs b/crates/ui-egui/tests/it/pro_tools_keys.rs similarity index 100% rename from crates/ui-egui/tests/pro_tools_keys.rs rename to crates/ui-egui/tests/it/pro_tools_keys.rs diff --git a/crates/ui-egui/tests/record_hint.rs b/crates/ui-egui/tests/it/record_hint.rs similarity index 100% rename from crates/ui-egui/tests/record_hint.rs rename to crates/ui-egui/tests/it/record_hint.rs diff --git a/crates/ui-egui/tests/rename_dialog.rs b/crates/ui-egui/tests/it/rename_dialog.rs similarity index 100% rename from crates/ui-egui/tests/rename_dialog.rs rename to crates/ui-egui/tests/it/rename_dialog.rs diff --git a/crates/ui-egui/tests/sampler_window.rs b/crates/ui-egui/tests/it/sampler_window.rs similarity index 100% rename from crates/ui-egui/tests/sampler_window.rs rename to crates/ui-egui/tests/it/sampler_window.rs diff --git a/crates/ui-egui/tests/save_as_replace.rs b/crates/ui-egui/tests/it/save_as_replace.rs similarity index 100% rename from crates/ui-egui/tests/save_as_replace.rs rename to crates/ui-egui/tests/it/save_as_replace.rs diff --git a/crates/ui-egui/tests/save_changes.rs b/crates/ui-egui/tests/it/save_changes.rs similarity index 100% rename from crates/ui-egui/tests/save_changes.rs rename to crates/ui-egui/tests/it/save_changes.rs diff --git a/crates/ui-egui/tests/send_window.rs b/crates/ui-egui/tests/it/send_window.rs similarity index 100% rename from crates/ui-egui/tests/send_window.rs rename to crates/ui-egui/tests/it/send_window.rs diff --git a/crates/ui-egui/tests/shared_controls.rs b/crates/ui-egui/tests/it/shared_controls.rs similarity index 100% rename from crates/ui-egui/tests/shared_controls.rs rename to crates/ui-egui/tests/it/shared_controls.rs diff --git a/crates/ui-egui/tests/shortcuts.rs b/crates/ui-egui/tests/it/shortcuts.rs similarity index 100% rename from crates/ui-egui/tests/shortcuts.rs rename to crates/ui-egui/tests/it/shortcuts.rs diff --git a/crates/ui-egui/tests/side_lists_access.rs b/crates/ui-egui/tests/it/side_lists_access.rs similarity index 100% rename from crates/ui-egui/tests/side_lists_access.rs rename to crates/ui-egui/tests/it/side_lists_access.rs diff --git a/crates/ui-egui/tests/toolbar_tempo.rs b/crates/ui-egui/tests/it/toolbar_tempo.rs similarity index 100% rename from crates/ui-egui/tests/toolbar_tempo.rs rename to crates/ui-egui/tests/it/toolbar_tempo.rs diff --git a/crates/ui-egui/tests/track_height.rs b/crates/ui-egui/tests/it/track_height.rs similarity index 100% rename from crates/ui-egui/tests/track_height.rs rename to crates/ui-egui/tests/it/track_height.rs diff --git a/crates/ui-egui/tests/track_list.rs b/crates/ui-egui/tests/it/track_list.rs similarity index 100% rename from crates/ui-egui/tests/track_list.rs rename to crates/ui-egui/tests/it/track_list.rs diff --git a/crates/ui-egui/tests/track_name_menu.rs b/crates/ui-egui/tests/it/track_name_menu.rs similarity index 100% rename from crates/ui-egui/tests/track_name_menu.rs rename to crates/ui-egui/tests/it/track_name_menu.rs diff --git a/crates/ui-egui/tests/trackpad.rs b/crates/ui-egui/tests/it/trackpad.rs similarity index 100% rename from crates/ui-egui/tests/trackpad.rs rename to crates/ui-egui/tests/it/trackpad.rs diff --git a/crates/ui-egui/tests/tracks_scroll_bar.rs b/crates/ui-egui/tests/it/tracks_scroll_bar.rs similarity index 100% rename from crates/ui-egui/tests/tracks_scroll_bar.rs rename to crates/ui-egui/tests/it/tracks_scroll_bar.rs diff --git a/crates/ui-egui/tests/waveform.rs b/crates/ui-egui/tests/it/waveform.rs similarity index 100% rename from crates/ui-egui/tests/waveform.rs rename to crates/ui-egui/tests/it/waveform.rs diff --git a/crates/ui-egui/tests/window_shortcuts.rs b/crates/ui-egui/tests/it/window_shortcuts.rs similarity index 100% rename from crates/ui-egui/tests/window_shortcuts.rs rename to crates/ui-egui/tests/it/window_shortcuts.rs diff --git a/crates/ui-egui/tests/zoom_toggle.rs b/crates/ui-egui/tests/it/zoom_toggle.rs similarity index 100% rename from crates/ui-egui/tests/zoom_toggle.rs rename to crates/ui-egui/tests/it/zoom_toggle.rs diff --git a/crates/vst3-host/tests/fixture_e2e.rs b/crates/vst3-host/tests/it/fixture_e2e.rs similarity index 99% rename from crates/vst3-host/tests/fixture_e2e.rs rename to crates/vst3-host/tests/it/fixture_e2e.rs index eb9037f5..4f913231 100644 --- a/crates/vst3-host/tests/fixture_e2e.rs +++ b/crates/vst3-host/tests/it/fixture_e2e.rs @@ -4,8 +4,7 @@ use soundcraft_dsp::{Category, Taper, Unit}; -mod common; -use common::fixture_dir; +use crate::common::fixture_dir; fn id_of(name: &str) -> String { let found = soundcraft_vst3_host::scan_paths(&[fixture_dir().to_path_buf()]); diff --git a/crates/vst3-host/tests/it/main.rs b/crates/vst3-host/tests/it/main.rs new file mode 100644 index 00000000..ce956746 --- /dev/null +++ b/crates/vst3-host/tests/it/main.rs @@ -0,0 +1,6 @@ +//! Integration tests, built as one test binary: each file is a module. + +#[path = "../common/mod.rs"] +mod common; + +mod fixture_e2e; diff --git a/docs/mcp.md b/docs/mcp.md index 558852c1..2a08e879 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -107,6 +107,6 @@ Resources: `soundcraft://session` (full session JSON), `soundcraft://commands`. - Tracks by name or id; clips by id. - Every command is undoable through `edit.undo` unless it is a query or a view setting. -The acceptance test `crates/automation/tests/agent_tasks.rs` drives a realistic session end to end +The acceptance test `crates/automation/tests/it/agent_tasks.rs` drives a realistic session end to end through MCP only (mixing, plugins, routing, editing with undo, markers, tempo, automation, bounce, save and reopen). diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 8727c05f..c2da6561 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,18 +1,22 @@ -//! `cargo xtask ` — repository gates. +//! `cargo xtask ` — repository gates. mod ico; mod version; +use std::collections::BTreeSet; use std::path::{Path, PathBuf}; -use std::process::{Command, ExitCode}; +use std::process::{Command, ExitCode, Stdio}; fn root() -> PathBuf { Path::new(env!("CARGO_MANIFEST_DIR")).parent().map(Path::to_path_buf).unwrap_or_else(|| PathBuf::from(".")) } +fn cargo_bin() -> String { + std::env::var("CARGO").unwrap_or_else(|_| "cargo".into()) +} + fn cargo(args: &[&str]) -> Result<(), String> { - let st = - Command::new(std::env::var("CARGO").unwrap_or_else(|_| "cargo".into())).args(args).current_dir(root()).status().map_err(|e| e.to_string())?; + let st = Command::new(cargo_bin()).args(args).current_dir(root()).status().map_err(|e| e.to_string())?; if st.success() { Ok(()) } else { Err(format!("cargo {} failed", args.join(" "))) } } @@ -132,7 +136,7 @@ const WASM: &[&str] = &[ ]; fn wasm() -> Result<(), String> { - let mut args = vec!["check", "--target", "wasm32-unknown-unknown"]; + let mut args = vec!["check", "--target", "wasm32-unknown-unknown", "--keep-going"]; for c in WASM { args.push("-p"); args.push(c); @@ -144,11 +148,83 @@ fn wasm() -> Result<(), String> { cargo(&args) } +/// Test targets for the test build. Examples are left out so they aren't linked; clippy +/// `--all-targets` still compiles them. +const TEST_TARGETS: [&str; 4] = ["--workspace", "--lib", "--bins", "--tests"]; + +fn nextest_available() -> bool { + Command::new(cargo_bin()).args(["nextest", "--version"]).stdout(Stdio::null()).stderr(Stdio::null()).status().is_ok_and(|s| s.success()) +} + +/// `[[test]]` targets with `harness = false`, as (package, target). Their own `main` cannot answer +/// nextest's `--list`, so they run under `cargo test`. +fn own_main_tests() -> Result, String> { + let out = + Command::new(cargo_bin()).args(["metadata", "--format-version", "1", "--no-deps"]).current_dir(root()).output().map_err(|e| e.to_string())?; + let v: serde_json::Value = serde_json::from_slice(&out.stdout).map_err(|e| e.to_string())?; + let mut found = Vec::new(); + for p in v["packages"].as_array().cloned().unwrap_or_default() { + let (Some(name), Some(manifest)) = (p["name"].as_str(), p["manifest_path"].as_str()) else { continue }; + let text = std::fs::read_to_string(manifest).map_err(|e| format!("{manifest}: {e}"))?; + found.extend(harness_false_tests(&text).into_iter().map(|t| (name.to_string(), t))); + } + Ok(found) +} + +/// Names of the `[[test]]` tables with `harness = false` in a Cargo.toml. +fn harness_false_tests(manifest: &str) -> Vec { + // Each `[[test]]` table runs up to the next table header. + let tables = manifest.split("\n[").filter(|t| t.starts_with("[test]]")); + tables + .filter_map(|table| { + let value = + |key: &str| table.lines().find_map(|l| l.split_once('=').filter(|(k, _)| k.trim() == key).map(|(_, v)| v.trim().trim_matches('"'))); + (value("harness") == Some("false")).then(|| value("name").map(str::to_string)).flatten() + }) + .collect() +} + +/// Workspace tests: cargo nextest when installed, else cargo test. Doctests run apart. +fn test() -> Result<(), String> { + if !nextest_available() { + eprintln!("note: cargo-nextest is not installed, running cargo test (cargo install cargo-nextest --locked)"); + return cargo(&[&["test"][..], &TEST_TARGETS[..]].concat()); + } + let own = own_main_tests()?; + let mut args = vec!["nextest", "run"]; + args.extend(TEST_TARGETS); + let skip = format!("not ({})", own.iter().map(|(p, t)| format!("binary_id(={p}::{t})")).collect::>().join(" | ")); + if !own.is_empty() { + // A binary-level filter keeps nextest from running these binaries to list their tests. + args.extend(["-E", &skip]); + } + cargo(&args)?; + if own.is_empty() { + return Ok(()); + } + let packages: BTreeSet<&str> = own.iter().map(|(p, _)| p.as_str()).collect(); + let targets: BTreeSet<&str> = own.iter().map(|(_, t)| t.as_str()).collect(); + let mut args = vec!["test"]; + for p in packages { + args.extend(["-p", p]); + } + for t in targets { + args.extend(["--test", t]); + } + cargo(&args) +} + +/// nextest does not run doctests. +fn doctest() -> Result<(), String> { + cargo(&["test", "--workspace", "--doc"]) +} + fn ci() -> Result<(), String> { let steps: Vec<(&str, Box Result<(), String>>)> = vec![ ("fmt", Box::new(|| cargo(&["fmt", "--all", "--", "--check"]))), - ("clippy", Box::new(|| cargo(&["clippy", "--workspace", "--all-targets", "--", "-D", "warnings"]))), - ("test", Box::new(|| cargo(&["test", "--workspace"]))), + ("clippy", Box::new(|| cargo(&["clippy", "--workspace", "--all-targets", "--keep-going", "--", "-D", "warnings"]))), + ("test", Box::new(test)), + ("doctest", Box::new(doctest)), ("assets", Box::new(assets)), ("layers", Box::new(layers)), ("wasm", Box::new(wasm)), @@ -172,13 +248,14 @@ fn main() -> ExitCode { let args: Vec = std::env::args().skip(1).collect(); let r = match args.first().map(String::as_str) { Some("ci") => ci(), + Some("test") => test(), Some("layers") => layers(), Some("assets") => assets(), Some("parity") => parity(), Some("wasm") => wasm(), Some("version") => version::run(args.get(1..).unwrap_or(&[])), Some("ico") => ico::run(args.get(1..).unwrap_or(&[])), - _ => Err("usage: cargo xtask ".into()), + _ => Err("usage: cargo xtask ".into()), }; match r { Ok(()) => ExitCode::SUCCESS, @@ -188,3 +265,12 @@ fn main() -> ExitCode { } } } + +#[cfg(test)] +mod tests { + #[test] + fn harness_false_tests_are_found_by_table() { + let manifest = "[package]\nname = \"x\"\n\n[[test]]\nname = \"own_main\"\nharness = false\n\n[[test]]\nname = \"plain\"\n\n[[test]]\nharness = false\nname = \"second\"\n\n[dependencies]\nharness = false\n"; + assert_eq!(super::harness_false_tests(manifest), ["own_main", "second"]); + } +} From a4d3d8541ff60f819d17333d8921968e79aecb48 Mon Sep 17 00:00:00 2001 From: aiongg <67543387+aiongg@users.noreply.github.com> Date: Fri, 23 Oct 2026 14:17:28 -0400 Subject: [PATCH 11/12] CI: run the gate through craft-common-actions' shared gate.yml; FreeBSD nightly and on release (#955) ci.yml is new: it calls the shared workflow (cached apt packages, nextest, main-only cache saves, Cargo.lock check). FreeBSD runs nightly and on pushes to release instead of on pull requests. Depends on storytold/craft-common-actions#69. --- .github/workflows/ci.yml | 27 +++++++++++++++++++++++++++ .github/workflows/freebsd.yml | 11 +++++------ 2 files changed, 32 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..2095acad --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,27 @@ +name: CI + +# Quality gates for every push to main and every pull request (`cargo xtask ci`), through the +# suite's shared gate workflow in craft-common-actions. +on: + push: + branches: [main] + pull_request: + workflow_dispatch: + +permissions: + contents: read + +# A newer push to a PR branch cancels its older run; every push to main runs to the end. +concurrency: + group: ci-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + gate: + # Tracks @main so suite-wide CI fixes reach every app without a PR in each repository. + uses: storytold/craft-common-actions/.github/workflows/gate.yml@main + with: + packages: >- + pkg-config libxkbcommon-dev libwayland-dev libx11-dev libxcb1-dev libxcursor-dev + libxrandr-dev libxi-dev libfontconfig1-dev libfreetype-dev mesa-vulkan-drivers + libgl1-mesa-dev libasound2-dev diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index d819e138..cfcae78b 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -3,20 +3,19 @@ # player falls back to its silent clock when the VM has no sound device. name: FreeBSD -# The VM build is slow, so it runs nightly on main and on pushes to `release`, not on every push -# or dependency change: a drifted dependency still shows up within a day, and always before a -# release ships. PRs that change the FreeBSD packaging itself still run it. +# The VM build is slow, so it runs nightly on main and on pushes to `release`, not on every pull +# request: a drifted dependency still shows up within a day, and always before a release ships. +# PRs that change the FreeBSD workflow or packaging itself still run it. on: - schedule: - - cron: "47 3 * * *" push: branches: [release] pull_request: paths: - - "crates/playback/**" - .github/workflows/freebsd.yml - packaging/freebsd/** - packaging/env.sh + schedule: + - cron: "47 3 * * *" workflow_dispatch: concurrency: From fe4bb3fe0cb3cf8363f243e3e4a6c607cdbd83e0 Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Fri, 23 Oct 2026 14:53:00 -0400 Subject: [PATCH 12/12] style: replace flagged glob imports with explicit uses (SonarCloud rust:S2208); move release workflow's write permission to the job (S8233) (#988) * security: move the release workflow's write permission to the job level (SonarCloud githubactions:S8233) * style: replace wildcard imports with explicit uses (SonarCloud rust:S2208) Rewrites every glob `use` Sonar flags into explicit imports, so what each command module consumes is visible at the top of the file. * release: the git-sources gate moves into craft-common-actions (PR #78) The check is shared at release.yml@main now; keep only the reusable call. --------- Co-authored-by: Dylan Taylor --- .github/workflows/release.yml | 43 --------------------------- crates/au-host/src/lib.rs | 4 +-- crates/clap-host/src/lib.rs | 4 +-- crates/engine/src/cmd/audio_midi.rs | 6 ++-- crates/engine/src/cmd/audiosuite.rs | 7 +++-- crates/engine/src/cmd/clap.rs | 4 +-- crates/engine/src/cmd/clip.rs | 8 +++-- crates/engine/src/cmd/clip_list.rs | 8 ++--- crates/engine/src/cmd/clip_more.rs | 6 ++-- crates/engine/src/cmd/edit.rs | 12 +++++--- crates/engine/src/cmd/edit_more.rs | 8 +++-- crates/engine/src/cmd/event.rs | 6 ++-- crates/engine/src/cmd/event_more.rs | 12 ++++---- crates/engine/src/cmd/file.rs | 11 ++++--- crates/engine/src/cmd/health.rs | 6 ++-- crates/engine/src/cmd/instrument.rs | 6 ++-- crates/engine/src/cmd/midi.rs | 6 ++-- crates/engine/src/cmd/mix.rs | 8 +++-- crates/engine/src/cmd/more_util.rs | 6 ++-- crates/engine/src/cmd/options.rs | 6 ++-- crates/engine/src/cmd/plugin_state.rs | 5 ++-- crates/engine/src/cmd/query.rs | 4 +-- crates/engine/src/cmd/sampler.rs | 8 ++--- crates/engine/src/cmd/setup_more.rs | 7 ++--- crates/engine/src/cmd/surround.rs | 6 ++-- crates/engine/src/cmd/tab.rs | 10 +++---- crates/engine/src/cmd/track.rs | 11 +++---- crates/engine/src/cmd/track_more.rs | 7 ++--- crates/engine/src/cmd/transport.rs | 6 ++-- crates/engine/src/cmd/video.rs | 7 +++-- crates/engine/src/cmd/view.rs | 11 +++---- crates/engine/src/cmd/view_more.rs | 7 ++--- crates/engine/src/cmd/vst3.rs | 4 +-- crates/engine/src/cmd/zoom_toggle.rs | 13 ++++---- crates/mix/src/lib.rs | 4 ++- crates/video/src/h264/cabac_mb.rs | 2 +- crates/video/src/h264/cavlc.rs | 2 +- crates/video/src/h264/params.rs | 2 +- crates/video/src/h264/slicedec.rs | 6 ++-- crates/video/src/prores/decode.rs | 4 ++- crates/video/src/prores/encode.rs | 2 +- crates/vst3-host/src/lib.rs | 4 +-- 42 files changed, 145 insertions(+), 164 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 28e47f7a..bf2e1796 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,55 +10,12 @@ on: type: string default: "" -permissions: - contents: write - concurrency: group: release-${{ github.ref }} cancel-in-progress: false jobs: - git-sources: - name: Git dependencies come from storytold - runs-on: ubuntu-latest - permissions: - contents: read - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - name: Git dependencies come from storytold - run: | - set -euo pipefail - test -f Cargo.lock || { echo "Cargo.lock is missing"; exit 1; } - # Source replacement, patches and path overrides in cargo config never show up - # in Cargo.lock. Parsed rather than grepped: TOML has many spellings of one key. - # Cargo still reads the legacy .cargo/config. - python3 - <<'PY' - import os, sys, tomllib - for path in (".cargo/config", ".cargo/config.toml"): - if not os.path.exists(path): - continue - config = tomllib.load(open(path, "rb")) - replaced = [name for name, source in config.get("source", {}).items() - if isinstance(source, dict) and "replace-with" in source] - if replaced or "patch" in config or "paths" in config: - print(f"{path} replaces, patches or overrides a source") - sys.exit(1) - PY - # Fails when Cargo.lock is out of date with the manifests. - cargo metadata --locked --format-version 1 > /dev/null - python3 - <<'PY' - import sys, tomllib - lock = tomllib.load(open("Cargo.lock", "rb")) - bad = sorted({p["source"] for p in lock.get("package", []) - if p.get("source", "").startswith("git+") - and not p["source"].startswith("git+https://github.com/storytold/")}) - if bad: - print("Git dependencies outside github.com/storytold:", *bad, sep="\n") - sys.exit(1) - PY - release: - needs: git-sources # Intentionally tracked at @main (not SHA-pinned) so org-wide CI improvements, security fixes, and build optimizations propagate without PR churn across all craftapp repos. uses: storytold/craft-common-actions/.github/workflows/release.yml@main with: diff --git a/crates/au-host/src/lib.rs b/crates/au-host/src/lib.rs index 39172c0b..3a664a4d 100644 --- a/crates/au-host/src/lib.rs +++ b/crates/au-host/src/lib.rs @@ -192,7 +192,7 @@ pub enum AuError { #[cfg(target_os = "macos")] mod api { - use super::*; + use super::{AuDescriptor, AuError, Plugin, PluginInfo}; pub fn scan() -> Vec { registry::scan() @@ -210,7 +210,7 @@ mod api { #[cfg(not(target_os = "macos"))] mod api { - use super::*; + use super::{AuDescriptor, AuError, Plugin, PluginInfo}; pub fn scan() -> Vec { Vec::new() diff --git a/crates/clap-host/src/lib.rs b/crates/clap-host/src/lib.rs index 955b6614..a5e042a3 100644 --- a/crates/clap-host/src/lib.rs +++ b/crates/clap-host/src/lib.rs @@ -88,7 +88,7 @@ pub enum ClapError { #[cfg(not(target_arch = "wasm32"))] mod api { - use super::*; + use super::{ClapDescriptor, ClapError, Path, PathBuf, Plugin, PluginInfo, registry}; pub fn cached() -> Option> { registry::cached() @@ -118,7 +118,7 @@ mod api { #[cfg(target_arch = "wasm32")] mod api { - use super::*; + use super::{ClapDescriptor, ClapError, Path, PathBuf, Plugin, PluginInfo}; pub fn cached() -> Option> { Some(Vec::new()) diff --git a/crates/engine/src/cmd/audio_midi.rs b/crates/engine/src/cmd/audio_midi.rs index 218e3953..267f4789 100644 --- a/crates/engine/src/cmd/audio_midi.rs +++ b/crates/engine/src/cmd/audio_midi.rs @@ -1,9 +1,9 @@ //! Edit › Copy Special › Audio as MIDI: pitch-track the selected audio into MIDI notes on the //! clipboard (paste them onto a MIDI or instrument track). +use super::{CommandSpec, bad, has_range, range_param, tracks_required}; -use super::*; -use crate::{Clipboard, cmd}; -use serde_json::json; +use crate::{Clipboard, Engine, Result, cmd}; +use serde_json::{Value, json}; use soundcraft_midi::{Note, Sequence}; use soundcraft_model::{Clip, ClipId}; diff --git a/crates/engine/src/cmd/audiosuite.rs b/crates/engine/src/cmd/audiosuite.rs index 911ccca5..302d1eb3 100644 --- a/crates/engine/src/cmd/audiosuite.rs +++ b/crates/engine/src/cmd/audiosuite.rs @@ -1,8 +1,9 @@ //! AudioSuite-style offline processing of selected clips. +use super::{CommandSpec, bad, clip_ids_param, has_selection, range_param, str_param, tracks_param}; +use soundcraft_time::Range; -use super::*; -use crate::cmd; -use serde_json::json; +use crate::{Engine, Result, cmd}; +use serde_json::{Value, json}; use soundcraft_audio_io::{AudioBuffer, FileFormat}; use soundcraft_model::{Clip, ClipContent, TrackKind}; diff --git a/crates/engine/src/cmd/clap.rs b/crates/engine/src/cmd/clap.rs index 15ddcd74..4dfade07 100644 --- a/crates/engine/src/cmd/clap.rs +++ b/crates/engine/src/cmd/clap.rs @@ -1,9 +1,9 @@ //! Third-party CLAP plugins: discovery (`engine.clap_plugins`) and id lookup shared by the mixer //! commands. Hosting itself lives in `soundcraft-clap-host`. +use super::{CommandSpec, always}; -use super::*; use crate::cmd; -use serde_json::json; +use serde_json::{Value, json}; use soundcraft_dsp::PluginInfo; pub fn specs() -> Vec { diff --git a/crates/engine/src/cmd/clip.rs b/crates/engine/src/cmd/clip.rs index aff6d8cc..916c439e 100644 --- a/crates/engine/src/cmd/clip.rs +++ b/crates/engine/src/cmd/clip.rs @@ -1,8 +1,10 @@ //! Clip menu commands. +use super::{CommandSpec, always, bad, clip_ids_param, f64_or, has_range, has_selection, i64_or, position_param, str_param, track_param}; +use soundcraft_model::ClipId; +use soundcraft_time::Samples; -use super::*; -use crate::cmd; -use serde_json::json; +use crate::{Engine, Result, cmd}; +use serde_json::{Value, json}; pub fn specs() -> Vec { vec![ diff --git a/crates/engine/src/cmd/clip_list.rs b/crates/engine/src/cmd/clip_list.rs index a8529881..ae9cd499 100644 --- a/crates/engine/src/cmd/clip_list.rs +++ b/crates/engine/src/cmd/clip_list.rs @@ -1,9 +1,9 @@ //! Clip List commands. +use super::{CommandSpec, bad, bool_or}; -use super::*; -use crate::cmd; -use serde_json::json; -use soundcraft_model::SourceId; +use crate::{Engine, Result, cmd}; +use serde_json::{Value, json}; +use soundcraft_model::{ClipId, SourceId}; use std::collections::BTreeSet; const CLEAR: &str = "clip.clear"; diff --git a/crates/engine/src/cmd/clip_more.rs b/crates/engine/src/cmd/clip_more.rs index 59e57179..98bed8c7 100644 --- a/crates/engine/src/cmd/clip_more.rs +++ b/crates/engine/src/cmd/clip_more.rs @@ -1,10 +1,10 @@ //! More Clip menu commands: Regroup, Unloop, Clip Effects bypass/render, Conform to Tempo and //! Remove Pitch Shift. +use super::{CommandSpec, bad, clip_ids_param, has_selection, str_param}; use super::more_util::{render_stretched, replace_clip}; -use super::*; -use crate::cmd; -use serde_json::json; +use crate::{Engine, Result, cmd}; +use serde_json::{Value, json}; use soundcraft_model::{Clip, ClipContent, ClipId, Session, TrackId}; use soundcraft_time::Samples; diff --git a/crates/engine/src/cmd/edit.rs b/crates/engine/src/cmd/edit.rs index 468170ca..5536668b 100644 --- a/crates/engine/src/cmd/edit.rs +++ b/crates/engine/src/cmd/edit.rs @@ -1,11 +1,15 @@ //! Edit menu commands. +use super::{ + CommandSpec, always, bad, bool_or, can_redo, can_undo, clip_id, clip_ids_param, f32_or, f64_or, has_clipboard, has_range, has_selection, + has_tracks, i64_or, position_param, range_param, str_param, track_param, tracks_param, tracks_required, +}; +use soundcraft_time::Samples; use super::more_util::toggle_flag; -use super::*; use crate::edit as ops; -use crate::{Clipboard, cmd}; -use serde_json::json; -use soundcraft_model::{EditMode, Fade, FadeShape, Session}; +use crate::{Clipboard, Engine, Result, cmd}; +use serde_json::{Value, json}; +use soundcraft_model::{ClipId, EditMode, Fade, FadeShape, Session, TrackId}; pub fn specs() -> Vec { vec![ diff --git a/crates/engine/src/cmd/edit_more.rs b/crates/engine/src/cmd/edit_more.rs index fdd265c1..57e134aa 100644 --- a/crates/engine/src/cmd/edit_more.rs +++ b/crates/engine/src/cmd/edit_more.rs @@ -1,11 +1,13 @@ //! More Edit menu commands: Cut/Copy/Clear Special variants, Paste Special, selection helpers, //! Space Clips, Snap to, Copy/Move Selection to playlists, TCE, and automation coalesce/trim/glide. +use super::{ + CommandSpec, always, bad, clip_ids_param, has_clipboard, has_range, has_selection, position_param, range_param, str_param, tracks_required, +}; use super::more_util::{clip_db, render_stretched, replace_clip, set_clip_env, target_playlist}; -use super::*; use crate::edit as ops; -use crate::{Clipboard, TransportRequest, cmd}; -use serde_json::json; +use crate::{Clipboard, Engine, Result, TransportRequest, cmd}; +use serde_json::{Value, json}; use soundcraft_model::{AutoParam, AutomationLane, Clip, ClipContent, ClipId, EditMode, Playlist, Session}; use soundcraft_time::{Range, Samples}; diff --git a/crates/engine/src/cmd/event.rs b/crates/engine/src/cmd/event.rs index 80af311f..018209b6 100644 --- a/crates/engine/src/cmd/event.rs +++ b/crates/engine/src/cmd/event.rs @@ -1,8 +1,8 @@ //! Event menu: time/tempo operations, MIDI operations, markers. +use super::{CommandSpec, always, bad, clip_ids_param, f32_or, f64_or, has_selection, has_tracks, i64_or, position_param, range_param, str_param}; -use super::*; -use crate::cmd; -use serde_json::json; +use crate::{Engine, Result, cmd}; +use serde_json::{Value, json}; use soundcraft_midi::ops::{DurationOp, QuantizeOptions, VelocityOp}; use soundcraft_model::{ClipContent, MarkerKind}; use soundcraft_time::TICKS_PER_QUARTER; diff --git a/crates/engine/src/cmd/event_more.rs b/crates/engine/src/cmd/event_more.rs index 7e7d3e86..51016627 100644 --- a/crates/engine/src/cmd/event_more.rs +++ b/crates/engine/src/cmd/event_more.rs @@ -2,15 +2,18 @@ //! operations (split notes, input quantize, step input, restore/flatten performance), MIDI track //! offsets and real-time properties, chord extraction, Beat Detective, Identify Beat and //! Retrospective Record. +use super::{ + CommandSpec, always, bad, bool_or, clip_ids_param, f32_or, f64_or, has_range, has_selection, has_tracks, i64_or, position_param, range_param, + str_param, track_param, tracks_param, tracks_required, +}; use super::more_util::{store_value, track_key, values_with_prefix}; -use super::*; -use crate::cmd; -use serde_json::json; +use crate::{Engine, Result, cmd}; +use serde_json::{Value, json}; use soundcraft_midi::Note; use soundcraft_midi::ops::QuantizeOptions; use soundcraft_model::{ChannelFormat, Clip, ClipContent, ClipId, MarkerKind, Session, TrackId, TrackKind}; -use soundcraft_time::{Range, Samples, TICKS_PER_QUARTER, TempoMap}; +use soundcraft_time::{MAX_POSITION, Range, Samples, TICKS_PER_QUARTER, TempoMap}; /// Most tempo events one curve may write. const MAX_TEMPO_EVENTS: i64 = 10_000; @@ -782,7 +785,6 @@ fn retro_record(e: &mut Engine, p: &Value) -> Result { #[cfg(test)] mod tests { use super::*; - fn midi_engine() -> (Engine, TrackId) { let mut e = Engine::default(); e.execute("track.new", &json!({"kind": "midi"})).unwrap(); diff --git a/crates/engine/src/cmd/file.rs b/crates/engine/src/cmd/file.rs index 87bba997..40e3d3db 100644 --- a/crates/engine/src/cmd/file.rs +++ b/crates/engine/src/cmd/file.rs @@ -1,9 +1,12 @@ //! File menu commands. +use super::{ + CommandSpec, always, bad, bool_or, clip_ids_param, has_selection, has_tracks, i64_or, position_param, range_param, str_param, track_param, + tracks_param, tracks_required, +}; -use super::*; -use crate::cmd; -use serde_json::json; -use soundcraft_model::{Session, TrackKind}; +use crate::{Engine, EngineError, Result, cmd}; +use serde_json::{Value, json}; +use soundcraft_model::{Session, TrackId, TrackKind}; use soundcraft_time::SampleRate; pub fn specs() -> Vec { diff --git a/crates/engine/src/cmd/health.rs b/crates/engine/src/cmd/health.rs index d2971e48..260503ec 100644 --- a/crates/engine/src/cmd/health.rs +++ b/crates/engine/src/cmd/health.rs @@ -1,8 +1,8 @@ //! Media availability checks without decoding files or changing the session. +use super::{CommandSpec, always}; -use super::*; -use crate::cmd; -use serde_json::json; +use crate::{Engine, Result, cmd}; +use serde_json::{Value, json}; use soundcraft_model::ClipContent; use std::collections::BTreeMap; diff --git a/crates/engine/src/cmd/instrument.rs b/crates/engine/src/cmd/instrument.rs index c7e51f11..2a71ba50 100644 --- a/crates/engine/src/cmd/instrument.rs +++ b/crates/engine/src/cmd/instrument.rs @@ -1,10 +1,10 @@ //! An instrument track's instrument (`mix.instrument`): the plugin that turns the track's MIDI //! into audio, built in or hosted (CLAP, VST3, Audio Units). The mixer plays `Track::instrument`; //! the instrument is otherwise only set when the track is created (`track.new`). +use super::{CommandSpec, bad, has_selection, str_param, tracks_required}; -use super::*; -use crate::cmd; -use serde_json::json; +use crate::{Engine, Result, cmd}; +use serde_json::{Value, json}; use soundcraft_model::{Insert, TrackKind}; pub fn specs() -> Vec { diff --git a/crates/engine/src/cmd/midi.rs b/crates/engine/src/cmd/midi.rs index 129fd070..1d2584e4 100644 --- a/crates/engine/src/cmd/midi.rs +++ b/crates/engine/src/cmd/midi.rs @@ -1,8 +1,8 @@ //! MIDI note editing (MIDI Editor, pencil on MIDI tracks). +use super::{CommandSpec, always, bad, clip_id, has_tracks, i64_or, range_param, tracks_required}; -use super::*; -use crate::cmd; -use serde_json::json; +use crate::{Engine, Result, cmd}; +use serde_json::{Value, json}; use soundcraft_midi::{Note, Sequence}; use soundcraft_model::{Clip, ClipContent, ClipId}; use soundcraft_time::TICKS_PER_QUARTER; diff --git a/crates/engine/src/cmd/mix.rs b/crates/engine/src/cmd/mix.rs index 7502b4f2..8fb5f1f4 100644 --- a/crates/engine/src/cmd/mix.rs +++ b/crates/engine/src/cmd/mix.rs @@ -1,8 +1,10 @@ //! Mixer, insert, send and automation commands. +use super::{ + CommandSpec, always, bad, bool_or, f32_or, has_range, has_selection, has_tracks, i64_or, position_param, range_param, str_param, tracks_required, +}; -use super::*; -use crate::cmd; -use serde_json::json; +use crate::{Engine, EngineError, Result, cmd}; +use serde_json::{Value, json}; use soundcraft_model::{AutoParam, AutomationMode, INSERT_SLOTS, Insert, SEND_SLOTS, SendSlot, Session, TrackId}; pub fn specs() -> Vec { diff --git a/crates/engine/src/cmd/more_util.rs b/crates/engine/src/cmd/more_util.rs index bf169821..3d68b6c0 100644 --- a/crates/engine/src/cmd/more_util.rs +++ b/crates/engine/src/cmd/more_util.rs @@ -1,8 +1,9 @@ //! Helpers shared by the newer command modules: settings kept in `EditState.flags`/`values`, //! per-track settings, target playlists, clip-gain envelopes and stretch rendering. +use super::bad; +use crate::{Engine, Result}; -use super::*; -use serde_json::json; +use serde_json::{Value, json}; use soundcraft_audio_io::{AudioBuffer, FileFormat}; use soundcraft_model::{Clip, ClipContent, EditState, Session, SourceId, TrackId}; use soundcraft_time::Samples; @@ -187,7 +188,6 @@ pub fn replace_clip(s: &mut Session, t: TrackId, old: soundcraft_model::ClipId, #[cfg(test)] mod tests { use super::*; - #[test] fn text_settings_round_trip() { let mut ed = EditState::default(); diff --git a/crates/engine/src/cmd/options.rs b/crates/engine/src/cmd/options.rs index 47356f54..45c4b63a 100644 --- a/crates/engine/src/cmd/options.rs +++ b/crates/engine/src/cmd/options.rs @@ -1,8 +1,8 @@ //! Options menu toggles and Setup › Session settings. +use super::{CommandSpec, always, bad, bool_or, position_param, str_param}; -use super::*; -use crate::cmd; -use serde_json::json; +use crate::{Engine, Result, cmd}; +use serde_json::{Value, json}; use soundcraft_model::{BitDepthSetting, EditState}; use soundcraft_time::FrameRate; diff --git a/crates/engine/src/cmd/plugin_state.rs b/crates/engine/src/cmd/plugin_state.rs index e999c863..bc86ff17 100644 --- a/crates/engine/src/cmd/plugin_state.rs +++ b/crates/engine/src/cmd/plugin_state.rs @@ -1,9 +1,10 @@ //! Third-party plugin state in the session (`mix.insert_state`): the UI reads each live plugin's //! state right before saving and stores it on its insert (base64), from where the mixer restores //! it into new instances. +use super::{CommandSpec, bad, has_tracks, track_param}; +use crate::{Engine, Result}; -use super::*; -use serde_json::json; +use serde_json::{Value, json}; use soundcraft_model::{INSERT_SLOTS, b64}; pub fn specs() -> Vec { diff --git a/crates/engine/src/cmd/query.rs b/crates/engine/src/cmd/query.rs index 72eb09ea..3aefa852 100644 --- a/crates/engine/src/cmd/query.rs +++ b/crates/engine/src/cmd/query.rs @@ -1,8 +1,8 @@ //! Read-only queries for agents. +use super::{CommandInfo, CommandSpec, always, bad, command_specs, has_tracks, position_param, str_param, track_param}; -use super::*; use crate::cmd; -use serde_json::json; +use serde_json::{Value, json}; pub fn specs() -> Vec { vec![ diff --git a/crates/engine/src/cmd/sampler.rs b/crates/engine/src/cmd/sampler.rs index fc3bf182..eb401bbd 100644 --- a/crates/engine/src/cmd/sampler.rs +++ b/crates/engine/src/cmd/sampler.rs @@ -4,11 +4,11 @@ //! The source is saved in `Audio Files/` with the session like any other audio, so a session that //! uses a sample reopens with it. Sampler parameters (root note, key range, ADSR, loop…) are set //! with `mix.instrument_param`, or all at once with `params` here. +use super::{CommandSpec, bad, has_selection, str_param, tracks_required}; -use super::*; -use crate::cmd; -use serde_json::json; -use soundcraft_model::{Insert, TrackKind}; +use crate::{Engine, EngineError, Result, cmd}; +use serde_json::{Value, json}; +use soundcraft_model::{Insert, TrackId, TrackKind}; use std::path::Path; const SAMPLER: &str = "sampler"; diff --git a/crates/engine/src/cmd/setup_more.rs b/crates/engine/src/cmd/setup_more.rs index aee13ba1..369e1542 100644 --- a/crates/engine/src/cmd/setup_more.rs +++ b/crates/engine/src/cmd/setup_more.rs @@ -1,10 +1,10 @@ //! More Options toggles, the Setup menu (hardware, playback engine, I/O, timecode, click, //! shortcuts, MIDI setup) and File › Import › Session Data. +use super::{CommandSpec, always, bad, command_specs, i64_or, str_param, track_param, tracks_param}; use super::more_util::{set_text, store_value, text, toggle_flag, values_with_prefix}; -use super::*; -use crate::cmd; -use serde_json::json; +use crate::{Engine, EngineError, Result, cmd}; +use serde_json::{Value, json}; use soundcraft_model::{BusId, ChannelFormat, ClipContent, OutputPath, Route, Session, SourceId, TrackId, TrackKind}; use soundcraft_time::Timecode; use std::path::Path; @@ -503,7 +503,6 @@ fn import_session_data(e: &mut Engine, p: &Value) -> Result { #[cfg(test)] mod tests { use super::*; - #[test] fn option_flags_toggle() { let mut e = Engine::default(); diff --git a/crates/engine/src/cmd/surround.rs b/crates/engine/src/cmd/surround.rs index bd23be54..6d6f2138 100644 --- a/crates/engine/src/cmd/surround.rs +++ b/crates/engine/src/cmd/surround.rs @@ -1,8 +1,8 @@ //! Surround mixing: the main output format and the surround panners of tracks and sends. +use super::{CommandSpec, always, bad, bool_or, f32_or, has_selection, str_param, tracks_required}; -use super::*; -use crate::cmd; -use serde_json::json; +use crate::{Engine, Result, cmd}; +use serde_json::{Value, json}; use soundcraft_model::{ChannelFormat, Mixer, OutputPath, Route, SEND_SLOTS, Session, SurroundPan, TrackKind}; pub fn specs() -> Vec { diff --git a/crates/engine/src/cmd/tab.rs b/crates/engine/src/cmd/tab.rs index af4c783e..432a6386 100644 --- a/crates/engine/src/cmd/tab.rs +++ b/crates/engine/src/cmd/tab.rs @@ -1,10 +1,10 @@ //! Tab and Tab to Transient: the insertion point or selection end to the next or previous clip boundary, transient or note. +use super::{CommandSpec, always, bool_or}; -use super::*; -use crate::{TransportRequest, cmd}; -use serde_json::json; -use soundcraft_model::{Clip, ClipContent, Session, SourceAudio}; -use soundcraft_time::SampleRate; +use crate::{Engine, Result, TransportRequest, cmd}; +use serde_json::{Value, json}; +use soundcraft_model::{Clip, ClipContent, Session, SourceAudio, TrackId}; +use soundcraft_time::{Range, SampleRate, Samples}; use std::sync::{Arc, Mutex, PoisonError, Weak}; /// Transients are found in fixed blocks of each clip, so a long clip costs a block per press and diff --git a/crates/engine/src/cmd/track.rs b/crates/engine/src/cmd/track.rs index 4929b468..f76664a0 100644 --- a/crates/engine/src/cmd/track.rs +++ b/crates/engine/src/cmd/track.rs @@ -1,9 +1,11 @@ //! Track menu commands. +use super::{ + CommandSpec, always, bad, bool_or, has_selection, has_tracks, i64_or, range_param, str_param, track_param, tracks_param, tracks_required, +}; -use super::*; -use crate::cmd; -use serde_json::json; -use soundcraft_model::{ChannelFormat, Group, GroupId, Playlist, Route, Session, Track, TrackHeight, TrackKind}; +use crate::{Engine, Result, cmd}; +use serde_json::{Value, json}; +use soundcraft_model::{ChannelFormat, Group, GroupId, Playlist, Route, Session, Track, TrackHeight, TrackId, TrackKind}; pub fn specs() -> Vec { vec![ @@ -944,7 +946,6 @@ fn route(e: &mut Engine, p: &Value, input: bool) -> Result { #[cfg(test)] mod tests { use super::*; - #[test] fn comp_from_alternate_playlist() { let mut e = crate::demo::demo_engine(); diff --git a/crates/engine/src/cmd/track_more.rs b/crates/engine/src/cmd/track_more.rs index 9f547597..99255344 100644 --- a/crates/engine/src/cmd/track_more.rs +++ b/crates/engine/src/cmd/track_more.rs @@ -1,10 +1,10 @@ //! More Track menu commands: Convert Aux to Routing Folder, Extract MIDI, track presets, insert / //! send bypass subsets, MIDI real-time properties, trim/VCA coalescing and target playlists. +use super::{CommandSpec, bad, has_selection, has_tracks, range_param, str_param, tracks_param, tracks_required}; use super::more_util::{activate_playlist, target_playlist, track_key}; -use super::*; -use crate::cmd; -use serde_json::json; +use crate::{Engine, EngineError, Result, cmd}; +use serde_json::{Value, json}; use soundcraft_dsp::Category; use soundcraft_model::{AutoParam, AutomationLane, ChannelFormat, Clip, ClipContent, Insert, Route, Session, Track, TrackId, TrackKind}; use soundcraft_time::Samples; @@ -547,7 +547,6 @@ fn toggle_recent(e: &mut Engine, p: &Value) -> Result { #[cfg(test)] mod tests { use super::*; - #[test] fn bypass_by_category_and_range() { let mut e = crate::demo::demo_engine(); diff --git a/crates/engine/src/cmd/transport.rs b/crates/engine/src/cmd/transport.rs index 5fbd48c0..50827802 100644 --- a/crates/engine/src/cmd/transport.rs +++ b/crates/engine/src/cmd/transport.rs @@ -1,9 +1,9 @@ //! Transport commands. They update the session's stopped playhead and queue requests for the //! host's audio engine (which may be absent when headless). +use super::{CommandSpec, always, bad, f64_or, position_param}; -use super::*; -use crate::{TransportRequest, cmd}; -use serde_json::json; +use crate::{Engine, Result, TransportRequest, cmd}; +use serde_json::{Value, json}; pub fn specs() -> Vec { vec![ diff --git a/crates/engine/src/cmd/video.rs b/crates/engine/src/cmd/video.rs index c65e40d6..b3a6402a 100644 --- a/crates/engine/src/cmd/video.rs +++ b/crates/engine/src/cmd/video.rs @@ -2,10 +2,11 @@ //! //! Movies are imported with `file.import_video`; the session only references them //! (`Session::videos`). Decoding happens in the UI (`soundcraft-video`), never in the engine. +use super::{CommandSpec, always, bad, position_param, track_param}; +use soundcraft_time::MAX_POSITION; -use super::*; -use crate::cmd; -use serde_json::json; +use crate::{Engine, Result, cmd}; +use serde_json::{Value, json}; use soundcraft_model::{ClipContent, TrackKind}; /// `edit.flags` key set while the Video track is offline (online is the default). diff --git a/crates/engine/src/cmd/view.rs b/crates/engine/src/cmd/view.rs index 6dee816c..b214a81f 100644 --- a/crates/engine/src/cmd/view.rs +++ b/crates/engine/src/cmd/view.rs @@ -1,9 +1,10 @@ //! View menu commands that change saved view state (zoom, rulers, counters, grid/nudge). +use super::{CommandSpec, always, bad, bool_or, f32_or, f64_or, i64_or, position_param, str_param}; -use super::*; -use crate::cmd; -use serde_json::json; -use soundcraft_time::{GridValue, NoteValue, TimeFormat}; +use crate::{Engine, Result, cmd}; +use serde_json::{Value, json}; +use soundcraft_time::TimeFormat; +use soundcraft_time::{GridValue, MAX_POSITION, NoteValue}; pub fn specs() -> Vec { vec![ @@ -160,7 +161,7 @@ pub fn grid_param(p: &Value, key: &str) -> Option { #[cfg(test)] mod tests { use super::*; - + use soundcraft_time::Samples; #[test] fn grid_values_parse() { assert_eq!(grid_param(&json!({"v": "1/16"}), "v"), Some(GridValue::Note { value: NoteValue::Sixteenth, dotted: false, triplet: false })); diff --git a/crates/engine/src/cmd/view_more.rs b/crates/engine/src/cmd/view_more.rs index 79997ed2..e5bd9313 100644 --- a/crates/engine/src/cmd/view_more.rs +++ b/crates/engine/src/cmd/view_more.rs @@ -1,11 +1,11 @@ //! More View menu commands: Edit-window column views, extra rulers, marker displays, clip //! display options, waveform styles, expanded sends, transport views and other view toggles. //! Every setting is saved with the session in `edit.flags` (or `edit.rulers` for rulers). +use super::{CommandSpec, always, str_param}; use super::more_util::{exclusive_flag, set_flags, toggle_flag}; -use super::*; -use crate::cmd; -use serde_json::json; +use crate::{Engine, Result, cmd}; +use serde_json::{Value, json}; /// A view toggle: `{value?: bool}` sets it, otherwise it flips. macro_rules! flag { @@ -219,7 +219,6 @@ fn toggle_ruler(e: &mut Engine, p: &Value, id: &str) -> Result { #[cfg(test)] mod tests { use super::*; - #[test] fn flags_toggle_and_persist() { let mut e = Engine::default(); diff --git a/crates/engine/src/cmd/vst3.rs b/crates/engine/src/cmd/vst3.rs index 927bb433..338c466c 100644 --- a/crates/engine/src/cmd/vst3.rs +++ b/crates/engine/src/cmd/vst3.rs @@ -1,10 +1,10 @@ //! Third-party VST3 plugins: discovery (`engine.vst3_plugins`). Hosting itself lives in //! `soundcraft-vst3-host`; id lookup for the mixer commands is shared with CLAP in //! `cmd::clap::plugin_info`. +use super::{CommandSpec, always}; -use super::*; use crate::cmd; -use serde_json::json; +use serde_json::{Value, json}; pub fn specs() -> Vec { vec![cmd!( diff --git a/crates/engine/src/cmd/zoom_toggle.rs b/crates/engine/src/cmd/zoom_toggle.rs index d91d515e..24781759 100644 --- a/crates/engine/src/cmd/zoom_toggle.rs +++ b/crates/engine/src/cmd/zoom_toggle.rs @@ -1,9 +1,10 @@ //! Zoom Toggle: zoom in on the edit selection, then back out to the view before. - -use super::*; -use crate::cmd; -use serde_json::json; +use super::{CommandSpec, always, f64_or}; use soundcraft_model::{TrackHeight, ZoomToggle}; +use soundcraft_time::MAX_POSITION; + +use crate::{Engine, Result, cmd}; +use serde_json::{Value, json}; pub fn specs() -> Vec { vec![cmd!(noundo "view.zoom_toggle", "Zoom Toggle", [], None, "{width_px?: 1200, height_px?: 600}", always, toggle)] @@ -37,8 +38,10 @@ fn toggle(e: &mut Engine, p: &Value) -> Result { #[cfg(test)] mod tests { + use super::TrackHeight::{Fit, Large, Medium}; use super::*; - use TrackHeight::{Fit, Large, Medium}; + use soundcraft_model::TrackId; + use soundcraft_time::Samples; fn engine(select: Value) -> Engine { let mut e = Engine::default(); diff --git a/crates/mix/src/lib.rs b/crates/mix/src/lib.rs index c04559af..f73f0a71 100644 --- a/crates/mix/src/lib.rs +++ b/crates/mix/src/lib.rs @@ -1360,7 +1360,9 @@ fn receives_solo(s: &Session, t: &Track) -> bool { /// Metronome: a short synthesized blip on every beat of the tempo map, accented on beat 1 of /// each bar. Settings come from `setup.click_countoff` (`click.*` values). mod click { - use super::*; + use super::db_to_gain; + use soundcraft_model::Session; + use soundcraft_time::Samples; /// Blip length in seconds. const LEN_S: f64 = 0.03; diff --git a/crates/video/src/h264/cabac_mb.rs b/crates/video/src/h264/cabac_mb.rs index 4a3125e5..76c9c9a5 100644 --- a/crates/video/src/h264/cabac_mb.rs +++ b/crates/video/src/h264/cabac_mb.rs @@ -3,7 +3,7 @@ use crate::h264::cabac::Cabac; use crate::h264::error::{Result, ensure, invalid}; -use crate::h264::mbtypes::*; +use crate::h264::mbtypes::{MbTypeInfo, Part, SubMbInfo, b_mb_type, b_sub_mb_type, intra_mb_type, p_mb_type, p_sub_mb_type}; use crate::h264::picture::MbKind; use crate::h264::slice::SliceType; use crate::h264::slicedec::{MbCur, SliceDecoder}; diff --git a/crates/video/src/h264/cavlc.rs b/crates/video/src/h264/cavlc.rs index 2126a451..9611868e 100644 --- a/crates/video/src/h264/cavlc.rs +++ b/crates/video/src/h264/cavlc.rs @@ -1,7 +1,7 @@ //! CAVLC residual block parsing (9.2). use crate::bitstream::BitReader; -use crate::h264::cavlc_tables::*; +use crate::h264::cavlc_tables::{COEFF_TOKEN, RUN_BEFORE, TOTAL_ZEROS_2X2, TOTAL_ZEROS_2X4, TOTAL_ZEROS_4X4}; use crate::h264::error::{Result, ensure}; use std::sync::OnceLock; diff --git a/crates/video/src/h264/params.rs b/crates/video/src/h264/params.rs index 583c96bd..99232ac7 100644 --- a/crates/video/src/h264/params.rs +++ b/crates/video/src/h264/params.rs @@ -2,7 +2,7 @@ use crate::bitstream::BitReader; use crate::h264::error::{Error, Result, ensure, unsupported}; -use crate::h264::tables::*; +use crate::h264::tables::{DEFAULT_4X4_INTER, DEFAULT_4X4_INTRA, DEFAULT_8X8_INTER, DEFAULT_8X8_INTRA, ZIGZAG4, ZIGZAG8}; /// `v + k` for a coded `*_minus*` field, or the error its range check gives. fn plus(v: u32, k: u32, err: &str) -> Result { diff --git a/crates/video/src/h264/slicedec.rs b/crates/video/src/h264/slicedec.rs index a71a6b4e..dfab85b5 100644 --- a/crates/video/src/h264/slicedec.rs +++ b/crates/video/src/h264/slicedec.rs @@ -6,11 +6,11 @@ use crate::h264::deblock; use crate::h264::error::{Result, ensure, invalid}; use crate::h264::inter::{self, Weight}; use crate::h264::intra::{self, Avail}; -use crate::h264::mbtypes::*; +use crate::h264::mbtypes::{CBP_ME, MbTypeInfo, Part, SubMbInfo, SubPart, b_mb_type, b_sub_mb_type, intra_mb_type, p_mb_type, p_sub_mb_type}; use crate::h264::params::{Pps, Sps}; -use crate::h264::picture::*; +use crate::h264::picture::{Frame, FrameRef, MbKind, MbState, Planes, RefPic}; use crate::h264::slice::{SliceHeader, SliceType}; -use crate::h264::tables::*; +use crate::h264::tables::{BLK4_XY, QPC_TABLE, RASTER_TO_BLK4, ZIGZAG4, ZIGZAG8}; use crate::h264::transform::{self, LevelScale}; /// Per-slice data kept with the picture (deblocking, motion field export). diff --git a/crates/video/src/prores/decode.rs b/crates/video/src/prores/decode.rs index 305a5b1b..fcb8b07f 100644 --- a/crates/video/src/prores/decode.rs +++ b/crates/video/src/prores/decode.rs @@ -3,7 +3,9 @@ use crate::prores::bits::Reader; use crate::prores::dct::idct8x8; use crate::prores::header::{FrameHeader, PictureHeader, row_slice_widths}; -use crate::prores::tables::*; +use crate::prores::tables::{ + DC_CB, DC_CTX_INIT, FIRST_DC_CB, LEVEL_CB, LEVEL_CTX_INIT, RUN_CB, RUN_CTX_INIT, SCAN_INTERLACED, SCAN_PROGRESSIVE, qscale, +}; use crate::prores::{AlphaType, ChromaFormat, Error, Frame, Interlace, Result}; /// Decoder options. diff --git a/crates/video/src/prores/encode.rs b/crates/video/src/prores/encode.rs index 3e5a1f4e..fd4d931a 100644 --- a/crates/video/src/prores/encode.rs +++ b/crates/video/src/prores/encode.rs @@ -3,7 +3,7 @@ use crate::prores::bits::{Counter, Sink, Writer}; use crate::prores::dct::fdct8x8; use crate::prores::header::{FRAME_ID, FrameHeader, PictureHeader, row_slice_widths}; -use crate::prores::tables::*; +use crate::prores::tables::{DC_CB, DC_CTX_INIT, FIRST_DC_CB, LEVEL_CB, LEVEL_CTX_INIT, RUN_CB, RUN_CTX_INIT, SCAN_PROGRESSIVE, qscale}; use crate::prores::{AlphaType, ChromaFormat, ColorInfo, Error, Frame, Interlace, Profile, Result}; /// Encoder configuration. diff --git a/crates/vst3-host/src/lib.rs b/crates/vst3-host/src/lib.rs index b78e932d..9c76446c 100644 --- a/crates/vst3-host/src/lib.rs +++ b/crates/vst3-host/src/lib.rs @@ -114,7 +114,7 @@ pub enum Vst3Error { #[cfg(not(target_arch = "wasm32"))] mod api { - use super::*; + use super::{Path, PathBuf, Plugin, PluginInfo, Vst3Descriptor, Vst3Error, ffi, registry}; pub fn cached() -> Option> { registry::cached() @@ -147,7 +147,7 @@ mod api { #[cfg(target_arch = "wasm32")] mod api { - use super::*; + use super::{Path, PathBuf, Plugin, PluginInfo, Vst3Descriptor, Vst3Error}; pub fn cached() -> Option> { Some(Vec::new())