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
@@ -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"@@ -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>@@ -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 = true@@ -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;new file mode 100644@@ -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(())+}@@ -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()); vnew file mode 100644@@ -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"+ }))+}@@ -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;@@ -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);@@ -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(), }@@ -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")),new file mode 100644@@ -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();+ }+}