Skip to content

Add Swift SDK support via UniFFI bridge - #1

Merged
setoelkahfi merged 4 commits into
mainfrom
feature/pensive-fermat-li942i
Sep 26, 2026
Merged

setoelkahfi merged 4 commits into
mainfrom
feature/pensive-fermat-li942i

Conversation

@setoelkahfi

@setoelkahfi setoelkahfi commented Sep 25, 2026 •

Copy link
Copy Markdown
Collaborator

This PR adds the UniFFI-based demucs-ffi crate that the demucs-swift XCFramework is built from.

It is the second PR in a stack: #2 (Clippy fix) → #1 → #3 (XCFramework build script) → #4 (release workflow).

Summary

Introduces the demucs-ffi crate as a thin translation layer over demucs-core, exposing audio separation functionality to Swift through UniFFI.

Key Changes

  • New demucs-ffi crate (demucs-ffi/src/lib.rs):

    • Wraps demucs-core types in FFI-safe records and enums (prefixed Ffi)
    • Implements FfiDemucsEngine for model loading, warmup, and audio separation
    • Provides model cache management (download, delete, query)
    • Implements progress callbacks for downloads and separation via UniFFI traits
    • Handles GPU initialization with wgpu and Metal backends
    • Runs inference on dedicated worker threads to avoid blocking foreign executors
    • Comprehensive error handling and input validation
    • Full test coverage for model selection, progress reporting, and edge cases
  • Build infrastructure:

    • demucs-ffi/build.rs: Compiles tvOS/visionOS stack probe stub (workaround for tier-3 targets)
    • demucs-ffi/scripts/tvos_chkstk.s: No-op stack probe helper for embedded Apple platforms
    • uniffi-bindgen/: Thin wrapper around UniFFI's CLI for generating Swift bindings
  • Configuration:

    • Updated Cargo.toml workspace to include new crates
    • burn's metal feature (MSL codegen) on macOS only. cubecl-wgpu 0.9 implements it only for macOS, so enabling it for every Apple target broke the iOS, tvOS and visionOS builds. Those platforms still run on Metal through wgpu's WGSL path.

Notable Implementation Details

  • Model state is protected by Mutex (not Sync) since burn modules aren't thread-safe; status queries use a separate Arc<Mutex<Option<FfiDemucsModel>>> to avoid blocking on inference
  • Progress reporting bridges demucs-core's ForwardListener events into monotonic fractions for UI consumption
  • Downloads use atomic file operations (write to .part, rename on completion) to prevent truncated files from being trusted as cached
  • Resampling is accounted for in chunk calculations to provide accurate progress UI sizing
  • Fine-tuned model validation deduplicates stems and rejects unsupported selections at load time
  • XCFramework build uses reproducible zip creation with fixed timestamps for consistent checksums

https://claude.ai/code/session_017hgnUBegVyTQzrjT5zjBB7

@setoelkahfi setoelkahfi self-assigned this Sep 25, 2026
@sigit-code-review

Copy link
Copy Markdown

siGit Code Review is paused because the linked siGit account has reached its monthly cloud allowance.

@setoelkahfi
setoelkahfi force-pushed the feature/pensive-fermat-li942i branch from 94f7c16 to 699fc60 Compare September 26, 2026 08:48
@setoelkahfi
setoelkahfi changed the base branch from main to swift-sdk/clippy-as-chunks September 26, 2026 08:48
@setoelkahfi
setoelkahfi added this pull request to stack #5 September 26, 2026 08:48

@sigit-code-review sigit-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds the demucs-ffi crate, a UniFFI translation layer over demucs-core that exposes model loading, warmup, separation, and cache management to Swift. The engine wraps a Demucs<Wgpu> behind a Mutex (burn modules are Send but not Sync), runs all blocking work on dedicated 8 MB worker threads via run_blocking, and bridges ForwardListener events into monotonic progress fractions for UI consumption. Downloads write to a .part file and rename atomically, with listener-based cancellation. A build.rs stubs out ___chkstk_darwin for tier-3 Apple targets, and a thin uniffi-bindgen wrapper crate is added for Swift codegen. The workspace Cargo.toml registers the two new crates. Overall the design is clean and the concurrency reasoning is sound; the findings below are about robustness of the download path and a maintenance hazard in model mapping.


Automated review by siGit Code Review · commit 699fc60 · see the review dashboard

Comment thread demucs-ffi/src/lib.rs
Comment thread demucs-ffi/src/lib.rs Outdated
Base automatically changed from swift-sdk/clippy-as-chunks to main September 26, 2026 10:49
setoelkahfi and others added 3 commits September 26, 2026 12:49
demucs-ffi exposes demucs-core over UniFFI for the demucs-swift package:
model catalog and cache management, weight download with progress, and a
DemucsEngine object that loads, warms up and separates on the wgpu/Metal
backend. Blocking work runs on dedicated 8 MB-stack threads behind async
exports, so no async runtime is needed; progress callbacks can cancel.

uniffi-bindgen pins the generator to the same UniFFI version so the Swift
bindings always match the scaffolding.

Co-Authored-By: siGit Code <noreply@sigit.si>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
burn's metal feature turns on cubecl-wgpu's MSL codegen, which
cubecl-wgpu 0.9 implements only for macOS. On iOS, tvOS and visionOS it
leaves backend::request_device and register_features undefined, so the
XCFramework build failed at the first iOS target. Those platforms still
run on Metal through wgpu's WGSL path.

Co-Authored-By: siGit Code <noreply@sigit.si>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A stalled connection blocked reader.read() on the download thread
forever, and the cancel callback only runs between reads, so nothing
could stop it. Add 30 s connect and 60 s per-read timeouts. There is no
overall timeout, since the fine-tuned weights are 333 MB.

all_models() mapped any unknown model id to FourStem. It now lists the
three variants directly, so a model added to demucs-core without an FFI
variant fails all_models_matches_core_metadata instead of being
mislabelled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017hgnUBegVyTQzrjT5zjBB7
@setoelkahfi
setoelkahfi force-pushed the feature/pensive-fermat-li942i branch from 8454a82 to 857d669 Compare September 26, 2026 10:49

@sigit-code-review sigit-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR adds the demucs-ffi crate, a UniFFI translation layer over demucs-core exposing model download, cache management, loading, warmup, and audio separation to Swift. The engine wraps burn's Wgpu backend behind a Mutex, runs all blocking work on dedicated 8 MB worker threads via run_blocking, and bridges ForwardListener events into monotonic progress fractions. Downloads write to a .part file and rename atomically. A build.rs stub provides ___chkstk_darwin for tier-3 Apple targets, and a thin uniffi-bindgen wrapper crate is included for Swift code generation. Earlier review items (download timeouts and explicit all_models listing) were addressed in a follow-up commit.


Automated review by siGit Code Review · commit 857d669 · see the review dashboard

Comment thread demucs-ffi/Cargo.toml Outdated
Comment thread demucs-ffi/src/lib.rs
Comment thread demucs-ffi/src/lib.rs
Two download_model calls for the same file both passed the exists check
and wrote into the same .part path, so the renamed file could be a mix of
both. The fine-tuned stem selections share one file, which makes this
easy to hit from Swift. A per-file lock now makes the second caller wait
and then return early once the file is cached.

Also drop ureq's gzip feature. Content-Length is the compressed size when
the server gzips, which threw off the progress fraction, and safetensors
barely compress anyway.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: siGit Code <noreply@sigit.si>
@setoelkahfi
setoelkahfi merged commit f145316 into main Sep 26, 2026
7 checks passed
@setoelkahfi
setoelkahfi deleted the feature/pensive-fermat-li942i branch September 26, 2026 11:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants