collective

Browse / github.com/storytold/soundcraft / patch

Stem separation

Stem separation <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **New Features** * Separate selected audio clips into drums, bass, vocals, and other, with each stem added as a track for independent mixing. * Start separation from a clip’s context menu, monitor processing progress, and cancel from the progress window. * Stem separation uses HTDemucs and downloads its model if it isn’t already available. * **Documentation** * Added guidance on using stem separation, supported audio formats, and an estimated proce

Status
open
Score
0.00
Adopters
0 other operators
Author
jjjjjjjjjjjjjjjjjjmjgb (imported from pull request #1080, open)
Base
e85a4801e9029c9a1f5ce536b11be65aea832ed8
Size
+1424 −52 in 12 files
Review
pending
Shared
2026-10-28
Id
f396e31d0599ee1391ac249e

How it went

No other operator has reported using it yet.

Diff

raw · Cargo.lock README.md crates/dsp/Cargo.toml crates/dsp/src/lib.rs crates/dsp/src/stem_separation.rs crates/engine/src/cmd/mod.rs crates/engine/src/cmd/stem_separation.rs crates/ui-egui/src/dialogs.rs crates/ui-egui/src/edit_window.rs crates/ui-egui/src/lib.rs crates/ui-egui/src/menus.rs crates/ui-egui/src/stem_separation.rs

diff --git a/Cargo.lock b/Cargo.lock--- a/Cargo.lock+++ b/Cargo.lock@@ -183,9 +183,9 @@ dependencies = [  "jni 0.22.4",  "libc",  "log",- "ndk",+ "ndk 0.9.0",  "ndk-context",- "ndk-sys",+ "ndk-sys 0.6.0+11769913",  "num_enum",  "thiserror 2.0.21", ]@@ -446,6 +446,24 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +[[package]]+name = "base64"+version = "0.22.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"++[[package]]+name = "base64"+version = "0.23.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5"++[[package]]+name = "base64ct"+version = "1.8.3"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"+ [[package]] name = "bindgen" version = "0.72.1"@@ -511,6 +529,15 @@ dependencies = [  "serde_core", ] +[[package]]+name = "block-buffer"+version = "0.10.4"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"+dependencies = [+ "generic-array",+]+ [[package]] name = "block2" version = "0.5.1"@@ -542,6 +569,12 @@ dependencies = [  "piper", ] +[[package]]+name = "built"+version = "0.7.7"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b"+ [[package]] name = "bumpalo" version = "3.20.3"@@ -568,6 +601,12 @@ dependencies = [  "syn 3.0.6", ] +[[package]]+name = "byteorder"+version = "1.5.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"+ [[package]] name = "byteorder-lite" version = "0.1.0"@@ -678,6 +717,29 @@ dependencies = [  "libc", ] +[[package]]+name = "charon-audio"+version = "0.1.2"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "fa79b94fc3cdc9afc87de975fc266a2d948d0c539e692f447b9eccf315bf8b32"+dependencies = [+ "cpal 0.15.3",+ "flacenc",+ "hound",+ "indicatif",+ "log",+ "ndarray 0.15.6",+ "ort",+ "rayon",+ "realfft",+ "rubato",+ "serde",+ "serde_json",+ "symphonia 0.6.1",+ "thiserror 1.0.69",+ "walkdir",+]+ [[package]] name = "clang-sys" version = "1.9.1"@@ -759,6 +821,19 @@ dependencies = [  "crossbeam-utils", ] +[[package]]+name = "console"+version = "0.15.11"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8"+dependencies = [+ "encode_unicode",+ "libc",+ "once_cell",+ "unicode-width",+ "windows-sys 0.59.0",+]+ [[package]] name = "core-foundation" version = "0.9.4"@@ -769,6 +844,16 @@ dependencies = [  "libc", ] +[[package]]+name = "core-foundation"+version = "0.10.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6"+dependencies = [+ "core-foundation-sys",+ "libc",+]+ [[package]] name = "core-foundation-sys" version = "0.8.7"@@ -782,9 +867,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [  "bitflags 1.3.2",- "core-foundation",+ "core-foundation 0.9.4",  "core-graphics-types",- "foreign-types",+ "foreign-types 0.5.0",  "libc", ] @@ -795,7 +880,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [  "bitflags 1.3.2",- "core-foundation",+ "core-foundation 0.9.4",  "libc", ] @@ -805,6 +890,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f8f80099a98041a3d1622845c271458a2d73e688351bf3cb999266764b81d48" +[[package]]+name = "coreaudio-rs"+version = "0.11.3"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "321077172d79c662f64f5071a03120748d5bb652f5231570141be24cfcd2bace"+dependencies = [+ "bitflags 1.3.2",+ "core-foundation-sys",+ "coreaudio-sys",+]+ [[package]] name = "coreaudio-rs" version = "0.13.0"@@ -819,6 +915,38 @@ dependencies = [  "objc2-core-foundation", ] +[[package]]+name = "coreaudio-sys"+version = "0.2.18"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "b9b4739a805a62757a83e5654fa3faabec0442666b263bb2287d5a8185bfd953"+dependencies = [+ "bindgen",+]++[[package]]+name = "cpal"+version = "0.15.3"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "873dab07c8f743075e57f524c583985fbaf745602acbe916a01539364369a779"+dependencies = [+ "alsa",+ "core-foundation-sys",+ "coreaudio-rs 0.11.3",+ "dasp_sample",+ "jni 0.21.1",+ "js-sys",+ "libc",+ "mach2",+ "ndk 0.8.0",+ "ndk-context",+ "oboe",+ "wasm-bindgen",+ "wasm-bindgen-futures",+ "web-sys",+ "windows 0.54.0",+]+ [[package]] name = "cpal" version = "0.16.0"@@ -827,13 +955,13 @@ checksum = "cbd307f43cc2a697e2d1f8bc7a1d824b5269e052209e28883e5bc04d095aaa3f" dependencies = [  "alsa",  "asio-sys",- "coreaudio-rs",+ "coreaudio-rs 0.13.0",  "dasp_sample",  "jni 0.21.1",  "js-sys",  "libc",  "mach2",- "ndk",+ "ndk 0.9.0",  "ndk-context",  "num-derive",  "num-traits",@@ -860,6 +988,21 @@ dependencies = [  "serde", ] +[[package]]+name = "crc"+version = "3.4.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d"+dependencies = [+ "crc-catalog",+]++[[package]]+name = "crc-catalog"+version = "2.5.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853"+ [[package]] name = "crc32fast" version = "1.5.2"@@ -909,6 +1052,16 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" +[[package]]+name = "crypto-common"+version = "0.1.7"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"+dependencies = [+ "generic-array",+ "typenum",+]+ [[package]] name = "cursor-icon" version = "1.2.0"@@ -921,6 +1074,16 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" +[[package]]+name = "der"+version = "0.8.2"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "a878c850e9e421b20262e9b41f9c860e4785fa07541c266b62ff9d1ef998a80a"+dependencies = [+ "pem-rfc7468",+ "zeroize",+]+ [[package]] name = "dify" version = "0.8.0"@@ -934,6 +1097,16 @@ dependencies = [  "rayon", ] +[[package]]+name = "digest"+version = "0.10.7"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"+dependencies = [+ "block-buffer",+ "crypto-common",+]+ [[package]] name = "dispatch" version = "0.2.0"@@ -1150,6 +1323,12 @@ dependencies = [  "bytemuck", ] +[[package]]+name = "encode_unicode"+version = "1.0.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"+ [[package]] name = "encoding_rs" version = "0.8.42"@@ -1312,6 +1491,22 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484" +[[package]]+name = "flacenc"+version = "0.5.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "74c892c2b5fa08f967e8b5ad29121570b4762202f2402033ce08479ec65eccd0"+dependencies = [+ "built",+ "crc",+ "crossbeam-channel",+ "heapless",+ "md-5",+ "num-traits",+ "rustversion",+ "seq-macro",+]+ [[package]] name = "flate2" version = "1.1.10"@@ -1344,6 +1539,15 @@ dependencies = [  "bytemuck", ] +[[package]]+name = "foreign-types"+version = "0.3.2"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"+dependencies = [+ "foreign-types-shared 0.1.1",+]+ [[package]] name = "foreign-types" version = "0.5.0"@@ -1351,7 +1555,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [  "foreign-types-macros",- "foreign-types-shared",+ "foreign-types-shared 0.3.1", ]  [[package]]@@ -1365,6 +1569,12 @@ dependencies = [  "syn 3.0.6", ] +[[package]]+name = "foreign-types-shared"+version = "0.1.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"+ [[package]] name = "foreign-types-shared" version = "0.3.1"@@ -1435,6 +1645,16 @@ dependencies = [  "slab", ] +[[package]]+name = "generic-array"+version = "0.14.7"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"+dependencies = [+ "typenum",+ "version_check",+]+ [[package]] name = "gethostname" version = "1.1.0"@@ -1454,6 +1674,17 @@ dependencies = [  "unicode-width", ] +[[package]]+name = "getrandom"+version = "0.2.17"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"+dependencies = [+ "cfg-if",+ "libc",+ "wasi",+]+ [[package]] name = "getrandom" version = "0.3.4"@@ -1635,6 +1866,15 @@ dependencies = [  "smallvec", ] +[[package]]+name = "hash32"+version = "0.3.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"+dependencies = [+ "byteorder",+]+ [[package]] name = "hashbrown" version = "0.16.1"@@ -1655,6 +1895,16 @@ dependencies = [  "foldhash", ] +[[package]]+name = "heapless"+version = "0.8.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"+dependencies = [+ "hash32",+ "stable_deref_trait",+]+ [[package]] name = "hermit-abi" version = "0.5.3"@@ -1667,6 +1917,34 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]]+name = "hmac-sha256"+version = "1.1.15"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "1ad320b3b96fb2a455a0726d16efe0a5afdbd34b71dea5bc53b05ea057714d4e"++[[package]]+name = "hound"+version = "3.5.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f"++[[package]]+name = "http"+version = "1.5.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0"+dependencies = [+ "bytes",+ "itoa",+]++[[package]]+name = "httparse"+version = "1.10.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"+ [[package]] name = "icu_collections" version = "2.3.0"@@ -1797,6 +2075,19 @@ dependencies = [  "hashbrown 0.17.1", ] +[[package]]+name = "indicatif"+version = "0.17.11"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235"+dependencies = [+ "console",+ "number_prefix",+ "portable-atomic",+ "unicode-width",+ "web-time",+]+ [[package]] name = "is-docker" version = "0.2.0"@@ -2070,6 +2361,12 @@ version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" +[[package]]+name = "lzma-rust2"+version = "0.15.8"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "e20f57f9918e5bd7bc58c22cdd70a6afc7375d4dd9683af5f2b34bd3d2bba619"+ [[package]] name = "mach2" version = "0.4.3"@@ -2079,6 +2376,26 @@ dependencies = [  "libc", ] +[[package]]+name = "matrixmultiply"+version = "0.3.11"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "3f607c237553f086e7043417a51df26b2eb899d3caff94e6a67592ff992fedc7"+dependencies = [+ "autocfg",+ "rawpointer",+]++[[package]]+name = "md-5"+version = "0.10.6"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"+dependencies = [+ "cfg-if",+ "digest",+]+ [[package]] name = "memchr" version = "2.8.3"@@ -2202,6 +2519,66 @@ dependencies = [  "thiserror 2.0.21", ] +[[package]]+name = "native-tls"+version = "0.2.18"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2"+dependencies = [+ "libc",+ "log",+ "openssl",+ "openssl-probe",+ "openssl-sys",+ "schannel",+ "security-framework",+ "security-framework-sys",+ "tempfile",+]++[[package]]+name = "ndarray"+version = "0.15.6"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32"+dependencies = [+ "matrixmultiply",+ "num-complex",+ "num-integer",+ "num-traits",+ "rawpointer",+ "rayon",+]++[[package]]+name = "ndarray"+version = "0.17.2"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "520080814a7a6b4a6e9070823bb24b4531daac8c4627e08ba5de8c5ef2f2752d"+dependencies = [+ "matrixmultiply",+ "num-complex",+ "num-integer",+ "num-traits",+ "portable-atomic",+ "portable-atomic-util",+ "rawpointer",+]++[[package]]+name = "ndk"+version = "0.8.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7"+dependencies = [+ "bitflags 2.13.2",+ "jni-sys 0.3.1",+ "log",+ "ndk-sys 0.5.0+25.2.9519653",+ "num_enum",+ "thiserror 1.0.69",+]+ [[package]] name = "ndk" version = "0.9.0"@@ -2211,7 +2588,7 @@ dependencies = [  "bitflags 2.13.2",  "jni-sys 0.3.1",  "log",- "ndk-sys",+ "ndk-sys 0.6.0+11769913",  "num_enum",  "raw-window-handle",  "thiserror 1.0.69",@@ -2223,6 +2600,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" +[[package]]+name = "ndk-sys"+version = "0.5.0+25.2.9519653"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691"+dependencies = [+ "jni-sys 0.3.1",+]+ [[package]] name = "ndk-sys" version = "0.6.0+11769913"@@ -2321,6 +2707,12 @@ dependencies = [  "syn 2.0.119", ] +[[package]]+name = "number_prefix"+version = "0.4.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"+ [[package]] name = "objc-sys" version = "0.3.5"@@ -2669,6 +3061,29 @@ dependencies = [  "objc2-foundation 0.2.2", ] +[[package]]+name = "oboe"+version = "0.6.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "e8b61bebd49e5d43f5f8cc7ee2891c16e0f41ec7954d36bcb6c14c5e0de867fb"+dependencies = [+ "jni 0.21.1",+ "ndk 0.8.0",+ "ndk-context",+ "num-derive",+ "num-traits",+ "oboe-sys",+]++[[package]]+name = "oboe-sys"+version = "0.6.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "6c8bb09a4a2b1d668170cfe0a7d5bc103f8999fb316c98099b6a9939c9f2e79d"+dependencies = [+ "cc",+]+ [[package]] name = "once_cell" version = "1.21.4"@@ -2685,6 +3100,49 @@ dependencies = [  "libc", ] +[[package]]+name = "openssl"+version = "0.10.81"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45"+dependencies = [+ "bitflags 2.13.2",+ "cfg-if",+ "foreign-types 0.3.2",+ "libc",+ "openssl-macros",+ "openssl-sys",+]++[[package]]+name = "openssl-macros"+version = "0.1.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"+dependencies = [+ "proc-macro2",+ "quote",+ "syn 2.0.119",+]++[[package]]+name = "openssl-probe"+version = "0.2.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"++[[package]]+name = "openssl-sys"+version = "0.9.117"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695"+dependencies = [+ "cc",+ "libc",+ "pkg-config",+ "vcpkg",+]+ [[package]] name = "orbclient" version = "0.3.55"@@ -2714,6 +3172,30 @@ dependencies = [  "pin-project-lite", ] +[[package]]+name = "ort"+version = "2.0.0-rc.13"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "4336a1e2b38848325241c72889086886004e589b7c74f335e60a8e8db5138a0b"+dependencies = [+ "ndarray 0.17.2",+ "ort-sys",+ "smallvec",+ "tracing",+ "ureq 3.5.0",+]++[[package]]+name = "ort-sys"+version = "2.0.0-rc.13"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "cf211e3776eea6aec988552fa118dd746d70e1b1e5e244058d1c98015f3e5872"+dependencies = [+ "hmac-sha256",+ "lzma-rust2",+ "ureq 3.5.0",+]+ [[package]] name = "parking" version = "2.2.1"@@ -2752,6 +3234,15 @@ dependencies = [  "nom", ] +[[package]]+name = "pem-rfc7468"+version = "1.0.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "a6305423e0e7738146434843d1694d621cce767262b2a86910beab705e4493d9"+dependencies = [+ "base64ct",+]+ [[package]] name = "peniko" version = "0.6.1"@@ -3122,6 +3613,12 @@ dependencies = [  "objc2-quartz-core 0.3.2", ] +[[package]]+name = "rawpointer"+version = "0.2.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"+ [[package]] name = "rayon" version = "1.12.0"@@ -3153,6 +3650,15 @@ dependencies = [  "once_cell", ] +[[package]]+name = "realfft"+version = "3.5.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "f821338fddb99d089116342c46e9f1fbf3828dba077674613e734e01d6ea8677"+dependencies = [+ "rustfft",+]+ [[package]] name = "redox_syscall" version = "0.4.1"@@ -3203,6 +3709,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"@@ -3242,6 +3754,32 @@ dependencies = [  "windows-sys 0.61.2", ] +[[package]]+name = "ring"+version = "0.17.14"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"+dependencies = [+ "cc",+ "cfg-if",+ "getrandom 0.2.17",+ "libc",+ "untrusted",+ "windows-sys 0.52.0",+]++[[package]]+name = "rubato"+version = "0.15.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "b5d18b486e7d29a408ef3f825bc1327d8f87af091c987ca2f5b734625940e234"+dependencies = [+ "num-complex",+ "num-integer",+ "num-traits",+ "realfft",+]+ [[package]] name = "rustc-hash" version = "1.1.0"@@ -3303,6 +3841,41 @@ dependencies = [  "windows-sys 0.61.2", ] +[[package]]+name = "rustls"+version = "0.23.46"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "48e13bd8c0e9365c43cfa5c9e8f9ad49d3c8444926c9aac819e0e4dc503c8fdf"+dependencies = [+ "log",+ "once_cell",+ "ring",+ "rustls-pki-types",+ "rustls-webpki",+ "subtle",+ "zeroize",+]++[[package]]+name = "rustls-pki-types"+version = "1.15.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96"+dependencies = [+ "zeroize",+]++[[package]]+name = "rustls-webpki"+version = "0.103.16"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "4443b1b6d39d7c663139942cc4558262562d2c84123bb6282ef177009ca8b281"+dependencies = [+ "ring",+ "rustls-pki-types",+ "untrusted",+]+ [[package]] name = "rustversion" version = "1.0.23"@@ -3330,6 +3903,15 @@ dependencies = [  "winapi-util", ] +[[package]]+name = "schannel"+version = "0.1.30"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "9cb23c424d8ee841bb4d813677ca237e2a610067d594fec130f87d6093495ab5"+dependencies = [+ "windows-sys 0.61.2",+]+ [[package]] name = "scoped-tls" version = "1.0.1"@@ -3342,6 +3924,29 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]]+name = "security-framework"+version = "3.7.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"+dependencies = [+ "bitflags 2.13.2",+ "core-foundation 0.10.1",+ "core-foundation-sys",+ "libc",+ "security-framework-sys",+]++[[package]]+name = "security-framework-sys"+version = "2.17.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3"+dependencies = [+ "core-foundation-sys",+ "libc",+]+ [[package]] name = "self_cell" version = "1.3.0"@@ -3354,6 +3959,12 @@ version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +[[package]]+name = "seq-macro"+version = "0.3.6"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc"+ [[package]] name = "serde" version = "1.0.229"@@ -3606,7 +4217,7 @@ dependencies = [  "proptest",  "serde",  "serde_json",- "symphonia",+ "symphonia 0.5.5",  "thiserror 2.0.21", ] @@ -3648,11 +4259,14 @@ dependencies = [ name = "soundcraft-dsp" version = "0.12.1" dependencies = [+ "anyhow",+ "charon-audio",  "log",  "proptest",  "rustfft",  "serde",  "serde_json",+ "ureq 2.12.1", ]  [[package]]@@ -3721,7 +4335,7 @@ dependencies = [ name = "soundcraft-playback" version = "0.12.1" dependencies = [- "cpal",+ "cpal 0.16.0",  "log",  "serde",  "serde_json",@@ -3841,6 +4455,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" +[[package]]+name = "subtle"+version = "2.6.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"+ [[package]] name = "symphonia" version = "0.5.5"@@ -3848,20 +4468,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5773a4c030a19d9bfaa090f49746ff35c75dfddfa700df7a5939d5e076a57039" dependencies = [  "lazy_static",- "symphonia-bundle-flac",- "symphonia-bundle-mp3",- "symphonia-codec-aac",- "symphonia-codec-adpcm",- "symphonia-codec-alac",- "symphonia-codec-pcm",- "symphonia-codec-vorbis",- "symphonia-core",- "symphonia-format-caf",- "symphonia-format-isomp4",- "symphonia-format-mkv",- "symphonia-format-ogg",- "symphonia-format-riff",- "symphonia-metadata",+ "symphonia-bundle-flac 0.5.5",+ "symphonia-bundle-mp3 0.5.5",+ "symphonia-codec-aac 0.5.5",+ "symphonia-codec-adpcm 0.5.5",+ "symphonia-codec-alac 0.5.5",+ "symphonia-codec-pcm 0.5.5",+ "symphonia-codec-vorbis 0.5.5",+ "symphonia-core 0.5.5",+ "symphonia-format-caf 0.5.5",+ "symphonia-format-isomp4 0.5.5",+ "symphonia-format-mkv 0.5.5",+ "symphonia-format-ogg 0.5.5",+ "symphonia-format-riff 0.5.5",+ "symphonia-metadata 0.5.5",+]++[[package]]+name = "symphonia"+version = "0.6.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "a7edef6a96b696d4e0cab5ee9ebb7ca155ed95f30a6b45bbb8b97d2727f02424"+dependencies = [+ "lazy_static",+ "symphonia-bundle-flac 0.6.1",+ "symphonia-bundle-mp3 0.6.1",+ "symphonia-codec-aac 0.6.1",+ "symphonia-codec-adpcm 0.6.1",+ "symphonia-codec-alac 0.6.1",+ "symphonia-codec-pcm 0.6.1",+ "symphonia-codec-vorbis 0.6.1",+ "symphonia-core 0.6.1",+ "symphonia-format-caf 0.6.1",+ "symphonia-format-isomp4 0.6.1",+ "symphonia-format-mkv 0.6.1",+ "symphonia-format-ogg 0.6.1",+ "symphonia-format-riff 0.6.1",+ "symphonia-metadata 0.6.1", ]  [[package]]@@ -3871,11 +4514,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c91565e180aea25d9b80a910c546802526ffd0072d0b8974e3ebe59b686c9976" dependencies = [  "log",- "symphonia-core",- "symphonia-metadata",+ "symphonia-core 0.5.5",+ "symphonia-metadata 0.5.5",  "symphonia-utils-xiph", ] +[[package]]+name = "symphonia-bundle-flac"+version = "0.6.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "6405d5c34ff6f8f7ca08a4101efe66d21e180f7322ef9359900a0e55698a7892"+dependencies = [+ "log",+ "symphonia-common",+ "symphonia-core 0.6.1",+ "symphonia-metadata 0.6.1",+]+ [[package]] name = "symphonia-bundle-mp3" version = "0.5.5"@@ -3884,8 +4539,19 @@ checksum = "4872dd6bb56bf5eac799e3e957aa1981086c3e613b27e0ac23b176054f7c57ed" dependencies = [  "lazy_static",  "log",- "symphonia-core",- "symphonia-metadata",+ "symphonia-core 0.5.5",+ "symphonia-metadata 0.5.5",+]++[[package]]+name = "symphonia-bundle-mp3"+version = "0.6.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "98ea5ffc8716bff677dfb3b01b420c7b758de901a72b8c330bf2040ab74b4add"+dependencies = [+ "lazy_static",+ "log",+ "symphonia-core 0.6.1", ]  [[package]]@@ -3896,7 +4562,19 @@ checksum = "4c263845aa86881416849c1729a54c7f55164f8b96111dba59de46849e73a790" dependencies = [  "lazy_static",  "log",- "symphonia-core",+ "symphonia-core 0.5.5",+]++[[package]]+name = "symphonia-codec-aac"+version = "0.6.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "f5bf8e39552d34a3c4c98333370e62f48c92456d2e814f273b5c3ad7c4a5f45c"+dependencies = [+ "lazy_static",+ "log",+ "symphonia-common",+ "symphonia-core 0.6.1", ]  [[package]]@@ -3906,7 +4584,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dddc50e2bbea4cfe027441eece77c46b9f319748605ab8f3443350129ddd07f" dependencies = [  "log",- "symphonia-core",+ "symphonia-core 0.5.5",+]++[[package]]+name = "symphonia-codec-adpcm"+version = "0.6.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "445932ecb0c59362fde9c082dd63a75380650c5077fdb8b80b8cac850442a74c"+dependencies = [+ "log",+ "symphonia-core 0.6.1", ]  [[package]]@@ -3916,7 +4604,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8413fa754942ac16a73634c9dfd1500ed5c61430956b33728567f667fdd393ab" dependencies = [  "log",- "symphonia-core",+ "symphonia-core 0.5.5",+]++[[package]]+name = "symphonia-codec-alac"+version = "0.6.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "920a78f96f3cf62932d0c497959c0cc2100ef36b03c7ca1417b260f432a482d8"+dependencies = [+ "log",+ "symphonia-common",+ "symphonia-core 0.6.1", ]  [[package]]@@ -3926,7 +4625,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e89d716c01541ad3ebe7c91ce4c8d38a7cf266a3f7b2f090b108fb0cb031d95" dependencies = [  "log",- "symphonia-core",+ "symphonia-core 0.5.5",+]++[[package]]+name = "symphonia-codec-pcm"+version = "0.6.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "e04ba75686acbe43542fdd374571195f0530c0b7785ca25cc6840e9c6c4b6eea"+dependencies = [+ "log",+ "symphonia-core 0.6.1", ]  [[package]]@@ -3936,10 +4645,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f025837c309cd69ffef572750b4a2257b59552c5399a5e49707cc5b1b85d1c73" dependencies = [  "log",- "symphonia-core",+ "symphonia-core 0.5.5",  "symphonia-utils-xiph", ] +[[package]]+name = "symphonia-codec-vorbis"+version = "0.6.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "73d90b4fcf796137cc683c538282804ff9629f8ad9dbfd881fcbba331ac4e986"+dependencies = [+ "log",+ "symphonia-common",+ "symphonia-core 0.6.1",+]++[[package]]+name = "symphonia-common"+version = "0.6.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "2acc3fcc18ec9b8cdd48614e259c4cf0d27b71d41e5d9b120b42c5adab12d7c4"+dependencies = [+ "log",+ "symphonia-core 0.6.1",+ "symphonia-metadata 0.6.1",+]+ [[package]] name = "symphonia-core" version = "0.5.5"@@ -3953,6 +4684,21 @@ dependencies = [  "log", ] +[[package]]+name = "symphonia-core"+version = "0.6.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "01c412864d599d4750d0c3d684d7e093ec05e5309681ef5252cc1096a437f6e0"+dependencies = [+ "bitflags 2.13.2",+ "bytemuck",+ "lazy_static",+ "log",+ "num-complex",+ "rustfft",+ "smallvec",+]+ [[package]] name = "symphonia-format-caf" version = "0.5.5"@@ -3960,8 +4706,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8faf379316b6b6e6bbc274d00e7a592e0d63ff1a7e182ce8ba25e24edd3d096" dependencies = [  "log",- "symphonia-core",- "symphonia-metadata",+ "symphonia-core 0.5.5",+ "symphonia-metadata 0.5.5",+]++[[package]]+name = "symphonia-format-caf"+version = "0.6.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "ab64327ee1920531c5bf86cf7e9cd1a599d57013ddc30691da62683cefc65191"+dependencies = [+ "log",+ "symphonia-common",+ "symphonia-core 0.6.1", ]  [[package]]@@ -3972,11 +4729,23 @@ checksum = "243739585d11f81daf8dac8d9f3d18cc7898f6c09a259675fc364b382c30e0a5" dependencies = [  "encoding_rs",  "log",- "symphonia-core",- "symphonia-metadata",+ "symphonia-core 0.5.5",+ "symphonia-metadata 0.5.5",  "symphonia-utils-xiph", ] +[[package]]+name = "symphonia-format-isomp4"+version = "0.6.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "0e681a70e1870d34e02abf1dbc51e4267c3f1827801474e8870be8c689fc4dc3"+dependencies = [+ "log",+ "symphonia-common",+ "symphonia-core 0.6.1",+ "symphonia-metadata 0.6.1",+]+ [[package]] name = "symphonia-format-mkv" version = "0.5.5"@@ -3985,11 +4754,23 @@ checksum = "122d786d2c43a49beb6f397551b4a050d8229eaa54c7ddf9ee4b98899b8742d0" dependencies = [  "lazy_static",  "log",- "symphonia-core",- "symphonia-metadata",+ "symphonia-core 0.5.5",+ "symphonia-metadata 0.5.5",  "symphonia-utils-xiph", ] +[[package]]+name = "symphonia-format-mkv"+version = "0.6.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "d015c5c0558864665894b3f4cbd95e10abb01b9c868e751c72670f326a56360e"+dependencies = [+ "lazy_static",+ "log",+ "symphonia-common",+ "symphonia-core 0.6.1",+]+ [[package]] name = "symphonia-format-ogg" version = "0.5.5"@@ -3997,11 +4778,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b4955c67c1ed3aa8ae8428d04ca8397fbef6a19b2b051e73b5da8b1435639cb" dependencies = [  "log",- "symphonia-core",- "symphonia-metadata",+ "symphonia-core 0.5.5",+ "symphonia-metadata 0.5.5",  "symphonia-utils-xiph", ] +[[package]]+name = "symphonia-format-ogg"+version = "0.6.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "0b5495e7f7e3c7035328d82b6d6e377eef289bb0c4105bdeb557fc93a833f994"+dependencies = [+ "log",+ "symphonia-common",+ "symphonia-core 0.6.1",+ "symphonia-metadata 0.6.1",+]+ [[package]] name = "symphonia-format-riff" version = "0.5.5"@@ -4010,8 +4803,20 @@ checksum = "c2d7c3df0e7d94efb68401d81906eae73c02b40d5ec1a141962c592d0f11a96f" dependencies = [  "extended",  "log",- "symphonia-core",- "symphonia-metadata",+ "symphonia-core 0.5.5",+ "symphonia-metadata 0.5.5",+]++[[package]]+name = "symphonia-format-riff"+version = "0.6.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "1ff70929083a8c1a5f6cd7c904b6071c7914ad04739b510c2f7239dfc9b7dabe"+dependencies = [+ "extended",+ "log",+ "symphonia-core 0.6.1",+ "symphonia-metadata 0.6.1", ]  [[package]]@@ -4023,7 +4828,20 @@ dependencies = [  "encoding_rs",  "lazy_static",  "log",- "symphonia-core",+ "symphonia-core 0.5.5",+]++[[package]]+name = "symphonia-metadata"+version = "0.6.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "83713a97705d77bdef7cdbc0768fd6e5a54e4cd7e48d60a806ae85639e2c87c6"+dependencies = [+ "lazy_static",+ "log",+ "regex-lite",+ "smallvec",+ "symphonia-core 0.6.1", ]  [[package]]@@ -4032,8 +4850,8 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27c85ab799a338446b68eec77abf42e1a6f1bb490656e121c6e27bfbab9f16" dependencies = [- "symphonia-core",- "symphonia-metadata",+ "symphonia-core 0.5.5",+ "symphonia-metadata 0.5.5", ]  [[package]]@@ -4272,6 +5090,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e" +[[package]]+name = "typenum"+version = "1.20.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"+ [[package]] name = "uds_windows" version = "1.2.1"@@ -4313,6 +5137,57 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" +[[package]]+name = "untrusted"+version = "0.9.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"++[[package]]+name = "ureq"+version = "2.12.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d"+dependencies = [+ "base64 0.22.1",+ "flate2",+ "log",+ "once_cell",+ "rustls",+ "rustls-pki-types",+ "url",+ "webpki-roots 0.26.11",+]++[[package]]+name = "ureq"+version = "3.5.0"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "cde93ff05758a36a851a398a772b0b79a82fbe3c186a46f2696baaeaf84c966b"+dependencies = [+ "base64 0.23.1",+ "der",+ "log",+ "native-tls",+ "percent-encoding",+ "rustls-pki-types",+ "ureq-proto",+ "utf8-zero",+ "webpki-root-certs",+]++[[package]]+name = "ureq-proto"+version = "0.6.5"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "841583fc36089d7d59419568ae157f43a5a03a2e5a81ba87c77d1596ef46e922"+dependencies = [+ "base64 0.23.1",+ "http",+ "httparse",+ "log",+]+ [[package]] name = "url" version = "2.5.8"@@ -4325,6 +5200,12 @@ dependencies = [  "serde", ] +[[package]]+name = "utf8-zero"+version = "0.8.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e"+ [[package]] name = "utf8_iter" version = "1.0.4"@@ -4342,6 +5223,12 @@ dependencies = [  "wasm-bindgen", ] +[[package]]+name = "vcpkg"+version = "0.2.15"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"+ [[package]] name = "vello_common" version = "0.1.0"@@ -4403,6 +5290,12 @@ dependencies = [  "winapi-util", ] +[[package]]+name = "wasi"+version = "0.11.1+wasi-snapshot-preview1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"+ [[package]] name = "wasip2" version = "1.0.4+wasi-0.2.12"@@ -4639,6 +5532,33 @@ dependencies = [  "web-sys", ] +[[package]]+name = "webpki-root-certs"+version = "1.0.11"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "e49d0e65abc00c142e8f9428da991da9f1a250c39be97459901eb12b9b49f62a"+dependencies = [+ "rustls-pki-types",+]++[[package]]+name = "webpki-roots"+version = "0.26.11"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"+dependencies = [+ "webpki-roots 1.0.11",+]++[[package]]+name = "webpki-roots"+version = "1.0.11"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "f5f280bbb2b287bc8e7de7e3852945a0ff50d1bbcb5ed42b6c7a568c546df171"+dependencies = [+ "rustls-pki-types",+]+ [[package]] name = "weezl" version = "0.1.12"@@ -4772,7 +5692,7 @@ dependencies = [  "log",  "naga",  "naga-types",- "ndk-sys",+ "ndk-sys 0.6.0+11769913",  "objc2 0.6.5",  "objc2-core-foundation",  "objc2-core-graphics",@@ -5222,14 +6142,14 @@ dependencies = [  "calloop 0.13.0",  "cfg_aliases",  "concurrent-queue",- "core-foundation",+ "core-foundation 0.9.4",  "core-graphics",  "cursor-icon",  "dpi",  "js-sys",  "libc",  "memmap2",- "ndk",+ "ndk 0.9.0",  "objc2 0.5.3",  "objc2-app-kit 0.2.2",  "objc2-foundation 0.2.2",@@ -5529,6 +6449,12 @@ dependencies = [  "synstructure", ] +[[package]]+name = "zeroize"+version = "1.9.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "e13084392c5e4bc371903e2935a5eaeed24905a7511356b883835e18a78f6879"+ [[package]] name = "zerotrie" version = "0.2.5"diff --git a/README.md b/README.md--- a/README.md+++ b/README.md@@ -13,6 +13,10 @@  <h1 align="center">SoundCraft</h1> +<p align="center">+  <i><b>This is a fork of SoundCraft with AI stem separation support using Charon Audio (HTDemucs).</b></i>+</p>+ <p align="center">   <b>Recording, editing and mixing audio; an open-source, modern digital audio workstation (DAW), built in pure Rust.</b> </p>@@ -73,6 +77,25 @@ editing, a **Mix window** for balancing, edit modes, tools, playlists, memory lo processing and the rest. If you've spent years in that workflow, your hands should already know where things are. +### ✨ New in This Fork: AI Stem Separation++This fork adds **AI-powered stem separation** using Charon Audio (HTDemucs), allowing you to:++- Separate any audio clip into 4 independent stems: **drums, bass, other, vocals**+- Create separate tracks for each stem automatically+- Mix, mute, and solo stems independently+- Perfect for remixing, acapella extraction, and creative audio manipulation++**How to use:**+1. Right-click on any audio clip in the timeline+2. Select "Stem Separation" from the context menu+3. The AI will automatically separate the audio (takes 1-2 minutes)+4. Four new tracks will be created with the separated stems++**Supported formats:** MP3, WAV, FLAC, OGG, AAC, and more++**Based on:** Charon Audio (https://github.com/Valkyra-Labs/charon-audio) - A pure Rust implementation of HTDemucs 4.1.0 using ONNX Runtime+ <table>   <tr>     <td width="50%"><img alt="The Mix window with channel strips, inserts, sends, I/O, pan knobs, faders and meters, and an EQ plugin window" src="docs/images/mix.png"></td>diff --git a/crates/dsp/Cargo.toml b/crates/dsp/Cargo.toml--- a/crates/dsp/Cargo.toml+++ b/crates/dsp/Cargo.toml@@ -11,6 +11,9 @@ repository.workspace = true serde.workspace = true log.workspace = true rustfft.workspace = true+charon-audio = { version = "0.1", features = ["decode", "ort-backend", "realtime"] }+anyhow = "1.0"+ureq = "2"  [dev-dependencies] proptest.workspace = truediff --git a/crates/dsp/src/lib.rs b/crates/dsp/src/lib.rs--- a/crates/dsp/src/lib.rs+++ b/crates/dsp/src/lib.rs@@ -22,6 +22,7 @@ mod params; pub mod pitch_detect; pub mod plugins; pub mod spectrum;+pub mod stem_separation; mod util;  pub use offline::FadeShape;diff --git a/crates/dsp/src/stem_separation.rs b/crates/dsp/src/stem_separation.rsnew file mode 100644--- /dev/null+++ b/crates/dsp/src/stem_separation.rs@@ -0,0 +1,182 @@+// Stem separation module for SoundCraft+// Uses Charon Audio for Rust-based stem separation++use charon_audio::{Separator, SeparatorConfig};+use std::collections::HashMap;+use std::fs;+use std::path::Path;+use std::sync::{Arc, Mutex};+use std::thread;++const MODEL_URL: &str = "https://huggingface.co/StemSplitio/htdemucs-onnx/resolve/main/htdemucs.onnx";++pub struct SeparationResult {+    pub stem_files: HashMap<String, String>,+    pub model: String,+}++/// Progress callback for stem separation+pub type ProgressCallback = Arc<Mutex<dyn Fn(f32, &str) + Send + 'static>>;++/// Configure GPU acceleration for ONNX Runtime+fn configure_gpu() {+    #[cfg(windows)]+    {+        // Check if DirectML is enabled via environment variable+        if std::env::var("ORT_DML_EP_ENABLE").is_ok() {+            log::info!("DirectML GPU acceleration enabled via environment variable");+        } else {+            log::info!("To enable GPU acceleration, set ORT_DML_EP_ENABLE=1 before launching");+        }+    }+    +    #[cfg(not(windows))]+    {+        log::info!("GPU acceleration only available on Windows with DirectML");+    }+}++/// Download the HTDemucs model if it doesn't exist+fn download_model(model_path: &str, progress: Option<ProgressCallback>) -> Result<(), String> {+    log::info!("Downloading HTDemucs model from Hugging Face...");+    +    if let Some(ref cb) = progress {+        if let Ok(cb) = cb.lock() {+            cb(0.0, "Downloading model...");+        }+    }+    +    let response = ureq::get(MODEL_URL)+        .call()+        .map_err(|e| format!("Failed to download model: {}", e))?;+    +    let total_size = response+        .header("Content-Length")+        .and_then(|h| h.parse::<usize>().ok())+        .unwrap_or(0);+    +    let mut reader = response.into_reader();+    let mut data = Vec::new();+    if total_size > 0 {+        data.reserve(total_size);+    }+    +    std::io::copy(&mut reader, &mut data)+        .map_err(|e| format!("Failed to read model data: {}", e))?;+    +    // Ensure the directory exists+    if let Some(parent) = Path::new(model_path).parent() {+        fs::create_dir_all(parent)+            .map_err(|e| format!("Failed to create model directory: {}", e))?;+    }+    +    fs::write(model_path, data)+        .map_err(|e| format!("Failed to write model file: {}", e))?;+    +    log::info!("Model downloaded successfully to {}", model_path);+    +    if let Some(ref cb) = progress {+        if let Ok(cb) = cb.lock() {+            cb(1.0, "Model downloaded");+        }+    }+    +    Ok(())+}++pub fn separate_audio(+    input_file: &str,+    output_dir: &str,+    model_path: &str,+    progress: Option<ProgressCallback>,+) -> Result<SeparationResult, String> {+    // Configure GPU acceleration before creating separator+    configure_gpu();+    +    // Download model if it doesn't exist+    if !Path::new(model_path).exists() {+        download_model(model_path, progress.clone())?;+    }+    +    if let Some(ref cb) = progress {+        if let Ok(cb) = cb.lock() {+            cb(0.1, "Initializing separator...");+        }+    }+    +    // Configure separator with progress+    let config = SeparatorConfig::htdemucs(model_path)+        .with_progress(true)+        .with_overlap(0.5)+        .with_shifts(1);+    +    let separator = Separator::new(config)+        .map_err(|e| format!("Failed to initialize separator: {}", e))?;+    +    log::info!("Starting stem separation for: {}", input_file);+    +    if let Some(ref cb) = progress {+        if let Ok(cb) = cb.lock() {+            cb(0.2, "Separating audio...");+        }+    }+    +    let stems = separator.separate_file(input_file)+        .map_err(|e| format!("Failed to separate audio: {}", e))?;+    +    if let Some(ref cb) = progress {+        if let Ok(cb) = cb.lock() {+            cb(0.9, "Saving stems...");+        }+    }+    +    log::info!("Saving stems to: {}", output_dir);+    stems.save_all(output_dir)+        .map_err(|e| format!("Failed to save stems: {}", e))?;+    +    let mut stem_files = HashMap::new();+    for stem_name in ["drums", "bass", "other", "vocals"] {+        let stem_path = format!("{}/{}.wav", output_dir, stem_name);+        stem_files.insert(stem_name.to_string(), stem_path);+    }+    +    log::info!("Stem separation complete");+    +    if let Some(ref cb) = progress {+        if let Ok(cb) = cb.lock() {+            cb(1.0, "Complete");+        }+    }+    +    Ok(SeparationResult {+        stem_files,+        model: "HTDemucs 4-stem".to_string(),+    })+}++/// Run stem separation in a background thread+pub fn separate_audio_async(+    input_file: String,+    output_dir: String,+    model_path: String,+    progress: ProgressCallback,+) -> thread::JoinHandle<Result<SeparationResult, String>> {+    thread::spawn(move || {+        separate_audio(&input_file, &output_dir, &model_path, Some(progress))+    })+}++pub fn init_stem_separation(model_path: &str) -> Result<(), String> {+    // Configure GPU acceleration+    configure_gpu();+    +    // Download model if it doesn't exist+    if !Path::new(model_path).exists() {+        download_model(model_path, None)?;+    }+    +    let config = SeparatorConfig::htdemucs(model_path);+    let _separator = Separator::new(config)+        .map_err(|e| format!("Failed to initialize separator: {}", e))?;+    Ok(())+}diff --git a/crates/engine/src/cmd/mod.rs b/crates/engine/src/cmd/mod.rs--- a/crates/engine/src/cmd/mod.rs+++ b/crates/engine/src/cmd/mod.rs@@ -27,6 +27,7 @@ mod plugin_state; mod query; pub(crate) mod sampler; mod setup_more;+mod stem_separation; mod surround; mod tab; mod track;@@ -134,6 +135,7 @@ pub fn command_specs() -> &'static [CommandSpec] {         v.extend(plugin_state::specs());         v.extend(instrument::specs());         v.extend(sampler::specs());+        v.extend(stem_separation::specs());         v.extend(video::specs());         v.extend(zoom_toggle::specs());         vdiff --git a/crates/engine/src/cmd/stem_separation.rs b/crates/engine/src/cmd/stem_separation.rsnew file mode 100644--- /dev/null+++ b/crates/engine/src/cmd/stem_separation.rs@@ -0,0 +1,188 @@+//! Stem separation command for SoundCraft++use super::*;+use crate::cmd;+use serde_json::json;+use soundcraft_model::ClipContent;+use std::sync::mpsc;+use std::thread;+use std::time::Duration;++pub fn specs() -> Vec<CommandSpec> {+    vec![cmd!(+        "audiosuite.stem_separation",+        "Stem Separation",+        [],+        None,+        "{model_path?: <path to HTDemucs ONNX model>, output_dir?: <output directory for stems>} — separates selected audio clips into 4 stems (drums, bass, other, vocals)",+        has_selection,+        stem_separation+    )]+}++fn stem_separation(e: &mut Engine, p: &Value) -> Result<Value> {+    let ids = clip_ids_param(e, p);+    if ids.is_empty() {+        return Err(bad("audiosuite.stem_separation", "select audio clips first"));+    }++    let model_path = str_param(p, "model_path")+        .unwrap_or("models/htdemucs.onnx")+        .to_string();+    let output_dir = str_param(p, "output_dir")+        .unwrap_or("stems_output")+        .to_string();++    // Model will be auto-downloaded if it doesn't exist+    let mut results = Vec::new();+    let mut model_name = String::new();+    let mut created_tracks = Vec::new();++    for id in ids {+        let Some((track_id, clip)) = e.session().find_clip(id).map(|(t, c)| (t, c.clone())) else {+            continue;+        };++        if !clip.is_audio() {+            continue;+        }++        // Get the track name for stem track naming+        let track_name = e.session().track(track_id).map(|t| t.name.clone()).unwrap_or_else(|| "Audio".to_string());++        // Create output directory+        std::fs::create_dir_all(&output_dir).map_err(|e| bad("audiosuite.stem_separation", format!("failed to create output directory: {}", e)))?;++        // Get the actual audio file path from the clip+        let audio_path = match &clip.content {+            ClipContent::Audio { source, .. } => {+                log::info!("Clip source ID: {:?}", source);+                log::info!("Session folder: {:?}", e.session().folder);+                +                // Get the source from the session+                let source = e.session().source(*source);+                log::info!("Source from session: {:?}", source.as_ref().map(|s| &s.path));+                +                match (source, e.session().folder.as_ref()) {+                    (Some(src), Some(folder)) => {+                        let path = folder.join(&src.path);+                        log::info!("Full audio path: {:?}", path);+                        path.to_str()+                            .map(|s| s.to_string())+                            .ok_or_else(|| bad("audiosuite.stem_separation", "could not convert path to string"))+                    }+                    (Some(src), None) => {+                        // Fallback: try to use the source path directly if it's absolute+                        log::info!("No session folder, trying source path directly: {:?}", src.path);+                        if src.path.starts_with("/") || src.path.contains(":") {+                            Ok(src.path.clone())+                        } else {+                            Err(bad("audiosuite.stem_separation", "session folder not set and source path is relative"))+                        }+                    }+                    (None, _) => Err(bad("audiosuite.stem_separation", "source not found in session")),+                }+            }+            _ => return Err(bad("audiosuite.stem_separation", "clip is not an audio clip")),+        }?;++        log::info!("Starting stem separation for clip: {}", clip.name);+        +        // Run stem separation in background thread with progress updates+        let (progress_tx, progress_rx) = mpsc::channel::<(f32, String)>();+        let audio_path_clone = audio_path.clone();+        let output_dir_clone = output_dir.clone();+        let model_path_clone = model_path.clone();+        +        let handle = thread::spawn(move || {+            let progress_cb = std::sync::Arc::new(std::sync::Mutex::new(move |pct: f32, msg: &str| {+                let _ = progress_tx.send((pct, msg.to_string()));+            }));+            +            soundcraft_dsp::stem_separation::separate_audio(+                &audio_path_clone,+                &output_dir_clone,+                &model_path_clone,+                Some(progress_cb),+            )+        });+        +        // Poll for progress (non-blocking)+        loop {+            // Check for progress updates+            match progress_rx.try_recv() {+                Ok((pct, msg)) => {+                    log::info!("Stem separation progress: {:.0}% - {}", pct * 100.0, msg);+                }+                Err(mpsc::TryRecvError::Empty) => {+                    // No update yet+                }+                Err(mpsc::TryRecvError::Disconnected) => {+                    // Thread finished+                    break;+                }+            }+            +            // Check if thread is done+            if handle.is_finished() {+                break;+            }+            +            // Small sleep to avoid busy-waiting+            thread::sleep(Duration::from_millis(100));+        }+        +        // Get the result+        let result = handle.join()+            .map_err(|e| bad("audiosuite.stem_separation", format!("stem separation thread panicked: {:?}", e)))?;+        +        let result = result.map_err(|e| bad("audiosuite.stem_separation", format!("separation failed: {}", e)))?;++        model_name = result.model.clone();++        // Create a new track for each stem+        for (stem_name, stem_path) in &result.stem_files {+            let new_track_name = format!("{} - {}", track_name, stem_name);+            +            // Create new track+            let track_result = e.execute("track.new", &json!({+                "count": 1,+                "format": "stereo",+                "name": new_track_name+            })).map_err(|e| bad("audiosuite.stem_separation", format!("failed to create track: {}", e)))?;+            +            log::info!("Track creation result: {:?}", track_result);++            // Import the stem file into the new track using the track name (not ID)+            log::info!("Importing stem file {} into track {}", stem_path, new_track_name);+            let import_result = e.execute("file.import_audio", &json!({+                "path": stem_path,+                "track": new_track_name,+                "at": clip.start+            }));+            +            log::info!("Import result: {:?}", import_result);+            +            if let Err(e) = import_result {+                log::warn!("Failed to import stem file {}: {}", stem_path, e);+            } else {+                log::info!("Successfully imported stem file {}", stem_path);+                created_tracks.push(new_track_name.clone());+            }+            +            results.push(json!({+                "stem": stem_name,+                "track": new_track_name,+                "file": stem_path+            }));+        }+    }++    log::info!("Stem separation complete: {} tracks created", created_tracks.len());+    Ok(json!({+        "stems": results,+        "model": model_name,+        "tracks_created": created_tracks.len(),+        "note": "Created separate tracks for each stem"+    }))+}diff --git a/crates/ui-egui/src/dialogs.rs b/crates/ui-egui/src/dialogs.rs--- a/crates/ui-egui/src/dialogs.rs+++ b/crates/ui-egui/src/dialogs.rs@@ -307,6 +307,7 @@ fn default_dir() -> String { pub fn show(app: &mut SoundApp, ctx: &egui::Context) {     audiosuite_window(app, ctx);     crate::bounce::show(app, ctx);+    crate::stem_separation::show(app, ctx);     let Some(mut d) = app.dialogs.open.take() else { return };     let mut keep = true;     let mut action: Option<(String, Value)> = None;diff --git a/crates/ui-egui/src/edit_window.rs b/crates/ui-egui/src/edit_window.rs--- a/crates/ui-egui/src/edit_window.rs+++ b/crates/ui-egui/src/edit_window.rs@@ -2616,6 +2616,22 @@ fn clip_context_menu(app: &mut SoundApp, ui: &mut Ui, clip: Option<ClipId>) {         }     }     ui.separator();+    if ui.button("Stem Separation").clicked() {+        // Show progress modal+        app.stem_progress = Some(("Processing audio...".to_string(), 0.0));+        ui.ctx().request_repaint();+        +        // Run stem separation+        let result = app.run("audiosuite.stem_separation", json!({}));+        +        // Hide progress modal+        app.stem_progress = None;+        +        if let Err(e) = result {+            app.ui.status = format!("Stem separation failed: {}", e);+        }+    }+    ui.separator();     if ui.button("Rename...").clicked() {         let name = app.engine.session().find_clip(c).map(|(_, x)| x.name.clone()).unwrap_or_default();         app.dialogs.open_rename_clip(c, &name);diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs--- a/crates/ui-egui/src/lib.rs+++ b/crates/ui-egui/src/lib.rs@@ -25,7 +25,8 @@ pub mod sampler_window; pub mod score_editor; pub mod shortcuts; pub mod spot;-pub mod theme;+pub mod stem_separation;+mod theme; pub mod toolbar; pub mod video_track; pub mod video_window;@@ -329,6 +330,8 @@ pub struct SoundApp {     pub video: video_track::VideoPool,     /// The Bounce Mix in progress, rendered a slice per frame.     pub bounce: Option<soundcraft_engine::io::Bounce>,+    /// Stem separation progress.+    pub stem_progress: Option<(String, f32)>,     /// Test-only: owns the process-wide palette lock while this app exists, so frames painted     /// and colour assertions in one test can never straddle another test's palette change.     #[cfg(test)]@@ -336,7 +339,6 @@ pub struct SoundApp { }  /// Whether another automation-write pass is due: the transport moved at least-/// `step` since `last` in either direction. `last == i64::MIN` marks "never /// written", so the first pass after (re)start is always due. Saturating /// arithmetic keeps restarts and loop wraps overflow-free: the previous /// `(pos - last).abs()` panicked in debug builds on the first playing frame,@@ -404,6 +406,7 @@ impl SoundApp {             last_meter_poll: f64::NEG_INFINITY,             video: video_track::VideoPool::default(),             bounce: None,+            stem_progress: None,             #[cfg(test)]             _palette_lock: theme::lock_test_palette(),         }diff --git a/crates/ui-egui/src/menus.rs b/crates/ui-egui/src/menus.rs--- a/crates/ui-egui/src/menus.rs+++ b/crates/ui-egui/src/menus.rs@@ -12,6 +12,7 @@ pub const UI_COMMANDS: &[(&str, &str, &str, Option<&str>)] = &[     ("window.mix", "Mix", "Window > Mix", Some("Cmd+=")),     ("window.edit", "Edit", "Window > Edit", Some("Cmd+=")),     ("window.toggle_mix_edit", "Toggle Mix/Edit", "", Some("Cmd+=")),+    ("audiosuite.stem_separation", "Stem Separation", "AudioSuite > Other > Stem Separation", None),     ("window.mix_separate", "Mix in Separate Window", "", None),     ("window.transport", "Transport", "Window > Transport", Some("Cmd+1")),     ("window.big_counter", "Big Counter", "Window > Big Counter", Some("Cmd+3")),diff --git a/crates/ui-egui/src/stem_separation.rs b/crates/ui-egui/src/stem_separation.rsnew file mode 100644--- /dev/null+++ b/crates/ui-egui/src/stem_separation.rs@@ -0,0 +1,26 @@+//! Stem separation progress window++use crate::{SoundApp, theme::{Tokens, bold}};++/// Shows stem separation progress with a loading bar.+pub fn show(app: &mut SoundApp, ctx: &egui::Context) {+    let Some((status, progress)) = &app.stem_progress else { return };+    let mut cancel = ctx.input(|i| i.key_pressed(egui::Key::Escape));+    +    egui::Modal::new(egui::Id::new("stem_separation_progress")).show(ctx, |ui| {+        ui.spacing_mut().item_spacing = egui::vec2(8.0, 8.0);+        ui.set_width(320.0);+        ui.label(egui::RichText::new("Stem Separation").font(bold(13.0)));+        ui.label(egui::RichText::new(status).color(Tokens::current().text_dim));+        ui.add(egui::ProgressBar::new(*progress));+        ui.horizontal(|ui| {+            cancel |= ui.button("Cancel").clicked();+            ui.label(egui::RichText::new(format!("{:.0}%", progress * 100.0)).color(Tokens::current().text_dim));+        });+    });+    +    if cancel {+        app.stem_progress = None;+        app.ui.status = "Stem separation cancelled".into();+    }+}