From de32ec2504fad5e291938f00a73ba541a7854eed Mon Sep 17 00:00:00 2001 From: thewh1teagle <61390950+thewh1teagle@users.noreply.github.com> Date: Sun, 8 Dec 2024 20:07:59 +0200 Subject: [PATCH] Feat/wasm (#13) feat: add wasm --- .github/workflows/publish-libaec.yml | 24 +++++++---- BUILDING.md | 11 +++++ Cargo.lock | 61 ++++++++++++++++++++++++++++ Cargo.toml | 7 ++++ README.md | 2 +- crates/aec-rs-sys/build.rs | 11 ++++- src/lib.rs | 8 ++++ 7 files changed, 114 insertions(+), 10 deletions(-) diff --git a/.github/workflows/publish-libaec.yml b/.github/workflows/publish-libaec.yml index cd12ab5..9c4b933 100644 --- a/.github/workflows/publish-libaec.yml +++ b/.github/workflows/publish-libaec.yml @@ -10,31 +10,37 @@ jobs: fail-fast: false matrix: include: - - platform: "macos-latest" # for Arm-based Macs (M1 and above) + - platform: "macos-latest" # Wasm32-emscripten + args: "--target wasm32-unknown-emscripten" + build-dir: "libaec-wasm32-unknown-emscripten" + archive: "tar" + target: "wasm32-unknown-emscripten" + + - platform: "macos-latest" # macOS arm64 args: "--target aarch64-apple-darwin" build-dir: "libaec-osx-aarch64" archive: "tar" target: "aarch64-apple-darwin" - - platform: "macos-latest" # for Apple IOS + - platform: "macos-latest" # IOS args: "--target aarch64-apple-ios" build-dir: "libaec-ios-aarch64" archive: "tar" target: "aarch64-apple-ios" - - platform: "macos-latest" # for Intel-based Macs + - platform: "macos-latest" # macOS x86-64 args: "--target x86_64-apple-darwin" build-dir: "libaec-osx-x86-64" archive: "tar" target: "x86_64-apple-darwin" - - platform: "ubuntu-22.04" # Linux 22.04 x86_64 + - platform: "ubuntu-22.04" # Linux x86_64 args: "" build-dir: "libaec-linux-x86-64" archive: "tar" target: "x86_64-unknown-linux-gnu" - - platform: "macos-latest" # Linux Android arm64 + - platform: "macos-latest" # Linux arm64 args: "" build-dir: "libaec-android-aarch64" archive: "tar" @@ -47,13 +53,13 @@ jobs: archive: "zip" target: "x86_64-pc-windows-msvc" - - platform: "windows-latest" # Windows ARM (aarch64) + - platform: "windows-latest" # Windows arm64 args: "--target aarch64-pc-windows-msvc" build-dir: "libaec-win-aarch64" archive: "zip" target: "aarch64-pc-windows-msvc" - - platform: "ubuntu-22.04" # Raspberry Pi 4 (64-bit ARM) + - platform: "ubuntu-22.04" # Linux arm64 args: "--target aarch64-unknown-linux-gnu" build-dir: "libaec-linux-aarch64" archive: "tar" @@ -83,6 +89,10 @@ jobs: local-cache: true if: matrix.target == 'aarch64-linux-android' + - name: Setup emsdk + uses: mymindstorm/setup-emsdk@v14 + if: contains(matrix.target, 'wasm') + - name: Install Rust stable uses: dtolnay/rust-toolchain@stable with: diff --git a/BUILDING.md b/BUILDING.md index 8465fd1..9d758e5 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -56,6 +56,17 @@ Build cargo build --release --target aarch64-apple-ios ``` +## Build for wasm + +Use [wasm-pack](https://rustwasm.github.io/docs/wasm-pack) with [emscripten.org](https://emscripten.org) + +```console +brew install emscripten +rustup target add wasm32-unknown-emscripten +cargo build --release --target wasm32-unknown-emscripten +CC=emcc AR=emar wasm-pack build +``` + ## Build pyaec (Python) Use [uv](https://astral.sh/blog/uv) diff --git a/Cargo.lock b/Cargo.lock index 48ba09f..cb59ed5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,7 @@ version = "1.0.0" dependencies = [ "aec-rs-sys", "hound", + "wasm-bindgen", ] [[package]] @@ -79,6 +80,12 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + [[package]] name = "cbindgen" version = "0.26.0" @@ -528,6 +535,60 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +[[package]] +name = "wasm-bindgen" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.90", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" + [[package]] name = "which" version = "4.4.2" diff --git a/Cargo.toml b/Cargo.toml index b79e5e8..e5a5d83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,5 +14,12 @@ homepage = "https://github.com/thewh1teagle/aec-rs" [dependencies] aec-rs-sys = { path = "crates/aec-rs-sys", version = "1.0.0" } +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen = { version = "0.2.99" } + +[lib] +# For wasm +crate-type = ["cdylib", "rlib"] + [dev-dependencies] hound = { version = "3.5.1" } diff --git a/README.md b/README.md index f58caae..8516375 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Acoustic echo cancellation in Rust based on [speexdsp](https://github.com/xiph/s - 🦀 Rust and 🐍 Python support - 🔗 Easy integration with C/C++ (or any other language) via C API - 📦 Precompiled library and C header files available in the [releases](https://github.com/thewh1teagle/aec-rs/releases/latest) -- 🖥️ Support for Windows (x86/arm64), Linux (x86/arm64), macOS (x86/arm64), Android (arm64), IOS (arm64) +- 🖥️ Support for Windows (x86/arm64), Linux (x86/arm64), macOS (x86/arm64), Android (arm64), IOS (arm64), and WASM! - 🖥️ Run on Raspberry PI as well # Install diff --git a/crates/aec-rs-sys/build.rs b/crates/aec-rs-sys/build.rs index 5adabb3..54ca3f6 100644 --- a/crates/aec-rs-sys/build.rs +++ b/crates/aec-rs-sys/build.rs @@ -41,8 +41,15 @@ fn main() { if target.contains("android") { clang_target = "armv8-linux-androideabi".to_string(); } - let bindings = bindgen::Builder::default() - .header("wrapper.h") + + let mut bindings = bindgen::Builder::default().header("wrapper.h"); + + if target.contains("wasm") { + // See https://github.com/rust-lang/rust-bindgen/issues/2624#issuecomment-1708117271 + bindings = bindings.clang_arg("-fvisibility=default"); + } + + let bindings = bindings .clang_arg(format!("-I{}", lib_dst.display())) // Explicitly set target in case we are cross-compiling. // See https://github.com/rust-lang/rust-bindgen/issues/1780 for context. diff --git a/src/lib.rs b/src/lib.rs index 5629d6e..26929f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,11 @@ +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::prelude::*; + use std::os::raw::c_void; /// See https://www.speex.org/docs/api/speex-api-reference/speex__echo_8h.html #[derive(Debug, Clone)] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] pub struct AecConfig { pub frame_size: usize, pub filter_length: i32, @@ -21,12 +25,15 @@ impl Default for AecConfig { } } +#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] pub struct Aec { echo_state: *mut aec_rs_sys::SpeexEchoState, preprocess_state: Option<*mut aec_rs_sys::SpeexPreprocessState>, } +#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] impl Aec { + #[cfg_attr(target_arch = "wasm32", wasm_bindgen)] pub fn new(config: &AecConfig) -> Self { let echo_state = unsafe { aec_rs_sys::speex_echo_state_init(config.frame_size as i32, config.filter_length) @@ -54,6 +61,7 @@ impl Aec { } } + #[cfg_attr(target_arch = "wasm32", wasm_bindgen)] pub fn cancel_echo(&self, rec_buffer: &[i16], echo_buffer: &[i16], out_buffer: &mut [i16]) { unsafe { aec_rs_sys::speex_echo_cancellation(