From aed8a69a23c4935e8921032507b299871213c01e Mon Sep 17 00:00:00 2001 From: thewh1teagle <61390950+thewh1teagle@users.noreply.github.com> Date: Fri, 6 Dec 2024 13:18:24 +0200 Subject: [PATCH] init --- .gitignore | 2 + .gitmodules | 3 + .vscode/settings.json | 3 + BUILDING.md | 11 + Cargo.lock | 408 ++++++++++++++++++++++++++++++++ Cargo.toml | 15 ++ README.md | 17 ++ crates/aec-rs-sys/Cargo.toml | 8 + crates/aec-rs-sys/build.rs | 56 +++++ crates/aec-rs-sys/src/lib.rs | 5 + crates/aec-rs-sys/wrapper.h | 1 + crates/speexdsp-rs-sys/speexdsp | 1 + examples/usage.rs | 24 ++ examples/wav.rs | 58 +++++ src/lib.rs | 76 ++++++ 15 files changed, 688 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 .vscode/settings.json create mode 100644 BUILDING.md create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 crates/aec-rs-sys/Cargo.toml create mode 100644 crates/aec-rs-sys/build.rs create mode 100644 crates/aec-rs-sys/src/lib.rs create mode 100644 crates/aec-rs-sys/wrapper.h create mode 160000 crates/speexdsp-rs-sys/speexdsp create mode 100644 examples/usage.rs create mode 100644 examples/wav.rs create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..109b25a --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target/ +*.wav diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..25a95b0 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "crates/speexdsp-rs-sys/speexdsp"] + path = crates/speexdsp-rs-sys/speexdsp + url = https://github.com/thewh1teagle/speexdsp diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..23fd35f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "editor.formatOnSave": true +} \ No newline at end of file diff --git a/BUILDING.md b/BUILDING.md new file mode 100644 index 0000000..9eb8902 --- /dev/null +++ b/BUILDING.md @@ -0,0 +1,11 @@ +# Building + +```console +git clone https://github.com/thewh1teagle/aec-rs --recursive +cd aec-rs +cargo build +``` + +Useful demo website: + +https://fjiang9.github.io/NKF-AEC/ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..29bd28e --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,408 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aec-rs" +version = "0.1.0" +dependencies = [ + "aec-rs-sys", + "hound", +] + +[[package]] +name = "aec-rs-sys" +version = "0.1.0" +dependencies = [ + "bindgen", + "cmake", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "cc" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f34d93e62b03caf570cccc334cbc6c2fceca82f39211051345108adcba3eebdc" +dependencies = [ + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "cmake" +version = "0.1.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c682c223677e0e5b6b7f63a64b9351844c3f1b1678a68b7ee617e30fb082620e" +dependencies = [ + "cc", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "hound" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f" + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.167" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" + +[[package]] +name = "libloading" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "prettyplease" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..3365cd0 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[workspace] +members = ["crates/aec-rs-sys"] + +[package] +name = "aec-rs" +version = "0.1.0" +edition = "2021" +authors = ["thewh1teagle"] +homepage = "https://github.com/thewh1teagle/aec-rs" + +[dependencies] +aec-rs-sys = { path = "crates/aec-rs-sys", version = "0.1.0" } + +[dev-dependencies] +hound = { version = "3.5.1" } diff --git a/README.md b/README.md new file mode 100644 index 0000000..2e8f41b --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# aec-rs + +Acoustic echo cancellation in Rust based on [speexdsp](https://github.com/xiph/speexdsp) + +## Install + +```console +cargo add aec-rs +``` + +## Example + +See [examples](examples) + +## Build + +See [building](BUILDING.md) diff --git a/crates/aec-rs-sys/Cargo.toml b/crates/aec-rs-sys/Cargo.toml new file mode 100644 index 0000000..fc70ea8 --- /dev/null +++ b/crates/aec-rs-sys/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "aec-rs-sys" +version = "0.1.0" +edition = "2021" + +[build-dependencies] +bindgen = "0.69.4" +cmake = "0.1" diff --git a/crates/aec-rs-sys/build.rs b/crates/aec-rs-sys/build.rs new file mode 100644 index 0000000..bfd6749 --- /dev/null +++ b/crates/aec-rs-sys/build.rs @@ -0,0 +1,56 @@ +use std::env; +use std::path::{Path, PathBuf}; + +use cmake::Config; + +fn copy_folder(src: &Path, dst: &Path) { + std::fs::create_dir_all(dst).expect("Failed to create dst directory"); + if cfg!(unix) { + std::process::Command::new("cp") + .arg("-rf") + .arg(src) + .arg(dst.parent().unwrap()) + .status() + .expect("Failed to execute cp command"); + } + + if cfg!(windows) { + std::process::Command::new("robocopy.exe") + .arg("/e") + .arg(src) + .arg(dst) + .status() + .expect("Failed to execute robocopy command"); + } +} + +fn main() { + let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("Failed to get CARGO_MANIFEST_DIR"); + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + let lib_src = Path::new(&manifest_dir).join("speexdsp"); + let lib_dst = out_dir.join("speexdsp"); + let profile = env::var("SPEEXDSP_LIB_PROFILE").unwrap_or("Release".to_string()); + if !lib_dst.exists() { + copy_folder(&lib_src, &lib_dst); + } + + let bindings = bindgen::Builder::default() + .header("wrapper.h") + .clang_arg(format!("-I{}", lib_dst.display())) + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + .generate() + .expect("Failed to generate bindings"); + let bindings_path = out_dir.join("bindings.rs"); + bindings + .write_to_file(bindings_path) + .expect("Failed to write bindings"); + + let mut config = Config::new(&lib_dst); + + let build_dir = config.profile(&profile).build(); + println!( + "cargo:rustc-link-search={}", + build_dir.join("lib").display() + ); + println!("cargo:rustc-link-lib=speexdsp"); +} diff --git a/crates/aec-rs-sys/src/lib.rs b/crates/aec-rs-sys/src/lib.rs new file mode 100644 index 0000000..a38a13a --- /dev/null +++ b/crates/aec-rs-sys/src/lib.rs @@ -0,0 +1,5 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/crates/aec-rs-sys/wrapper.h b/crates/aec-rs-sys/wrapper.h new file mode 100644 index 0000000..fb0a20b --- /dev/null +++ b/crates/aec-rs-sys/wrapper.h @@ -0,0 +1 @@ +#include "speexdsp/include/speex/speex_echo.h" \ No newline at end of file diff --git a/crates/speexdsp-rs-sys/speexdsp b/crates/speexdsp-rs-sys/speexdsp new file mode 160000 index 0000000..dbd421d --- /dev/null +++ b/crates/speexdsp-rs-sys/speexdsp @@ -0,0 +1 @@ +Subproject commit dbd421d149a9c362ea16150694b75b63d757a521 diff --git a/examples/usage.rs b/examples/usage.rs new file mode 100644 index 0000000..f66bdf5 --- /dev/null +++ b/examples/usage.rs @@ -0,0 +1,24 @@ +/* +cargo run --example usage +*/ + +fn main() { + let sample_rate = 16000; // 16kHz + let filter_length = (sample_rate as f32 * 0.1).round() as i32; // 0.1s + let mut aec = aec_rs::Aec::new(sample_rate, filter_length); + aec.set_sample_rate(sample_rate); + + let frame_size = aec.get_frame_size(); + + let mut input_frame: Vec = vec![0; frame_size as usize]; + let mut reference_frame: Vec = vec![0; frame_size as usize]; + + for i in 0..frame_size as usize { + input_frame[i] = (i % 10) as i16; // Sample data + reference_frame[i] = (i % 5) as i16; // Simulated echo + } + + // Process the frames with the AEC system + let cancelled = aec.cancel(&mut input_frame, &mut reference_frame); + println!("{}", cancelled.len()); +} diff --git a/examples/wav.rs b/examples/wav.rs new file mode 100644 index 0000000..9034fd2 --- /dev/null +++ b/examples/wav.rs @@ -0,0 +1,58 @@ +/* +cargo run --example usage echo.wav rec.wav cancelled.wav +*/ + +fn main() { + // 16kHz mono int16 same length + let echo_path = std::env::args().nth(1).expect("Please specify echo path"); + let rec_path = std::env::args().nth(2).expect("Please specify rec path"); + let out_path = std::env::args().nth(3).expect("Please specify out path"); + + // Read echo samples + let mut reader = hound::WavReader::open(echo_path).unwrap(); + let echo_samples: Vec = reader.samples::().map(|s| s.unwrap()).collect(); + + // Read recorded samples + let mut reader = hound::WavReader::open(rec_path).unwrap(); + let rec_samples: Vec = reader.samples::().map(|s| s.unwrap()).collect(); + + let sample_rate = 16000; // 16kHz + let filter_length = (sample_rate as f32 * 0.1).round() as i32; // 0.1s + let mut aec = aec_rs::Aec::new(sample_rate, filter_length); + aec.set_sample_rate(sample_rate); + + let frame_size = aec.get_frame_size(); + + // Prepare output WAV writer + let spec = hound::WavSpec { + channels: 1, + sample_rate: sample_rate as u32, + bits_per_sample: 16, + sample_format: hound::SampleFormat::Int, + }; + let mut writer = hound::WavWriter::create(&out_path, spec).unwrap(); + + // Process samples frame by frame + let mut output_samples = Vec::new(); + let mut input_frame = vec![0; frame_size as usize]; + let mut reference_frame = vec![0; frame_size as usize]; + + for i in (0..rec_samples.len()).step_by(frame_size as usize) { + let end = usize::min(i + frame_size as usize, rec_samples.len()); + + // Fill frames + input_frame[..end - i].copy_from_slice(&rec_samples[i..end]); + reference_frame[..end - i].copy_from_slice(&echo_samples[i..end]); + + // Apply AEC cancellation + let cancelled = aec.cancel(&mut input_frame, &mut reference_frame); + output_samples.extend_from_slice(&cancelled); + + // Write to output WAV file + for &sample in &cancelled { + writer.write_sample(sample).unwrap(); + } + } + + println!("Output written to {}", out_path); +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..445a6fe --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,76 @@ +use std::ffi::c_void; + +/// See https://www.speex.org/docs/api/speex-api-reference/speex__echo_8h.html +pub struct Aec { + state: *mut aec_rs_sys::SpeexEchoState, +} + +impl Aec { + pub fn new(frame_size: i32, filter_length: i32) -> Self { + unsafe { + let state = aec_rs_sys::speex_echo_state_init(frame_size, filter_length); + return Self { state }; + }; + } + + pub fn cancel(&mut self, rec: &mut [i16], play: &mut [i16]) -> Vec { + let mut out: Vec = vec![0; rec.len()]; + unsafe { + aec_rs_sys::speex_echo_cancellation( + self.state, + rec.as_mut_ptr(), + play.as_mut_ptr(), + out.as_mut_ptr(), + ); + }; + out + } + + pub fn reset(&mut self) { + unsafe { + aec_rs_sys::speex_echo_state_reset(self.state); + } + } + + pub fn get_frame_size(&mut self) -> i32 { + let mut frame_size: i32 = 0; + unsafe { + aec_rs_sys::speex_echo_ctl( + self.state, + aec_rs_sys::SPEEX_ECHO_GET_FRAME_SIZE as _, + &mut frame_size as *mut i32 as *mut c_void, + ); + }; + frame_size + } + + pub fn get_sample_rate(&mut self) -> i32 { + let mut sample_rate: i32 = 0; + unsafe { + aec_rs_sys::speex_echo_ctl( + self.state, + aec_rs_sys::SPEEX_ECHO_GET_SAMPLING_RATE as _, + &mut sample_rate as *mut i32 as *mut c_void, + ); + } + sample_rate + } + + pub fn set_sample_rate(&mut self, mut sample_rate: i32) { + unsafe { + aec_rs_sys::speex_echo_ctl( + self.state, + aec_rs_sys::SPEEX_ECHO_SET_SAMPLING_RATE as _, + &mut sample_rate as *mut i32 as *mut c_void, + ); + }; + } +} + +impl Drop for Aec { + fn drop(&mut self) { + unsafe { + aec_rs_sys::speex_echo_state_destroy(self.state); + } + } +}