| Crates.io | aec3 |
| lib.rs | aec3 |
| version | 0.1.3 |
| created_at | 2025-11-18 14:56:45.597112+00 |
| updated_at | 2026-01-12 14:02:27.353453+00 |
| description | An acoustic echo canceller written in rust based on the WebRTC aec3 project |
| homepage | |
| repository | https://github.com/RubyBit/aec3-rs |
| max_upload_size | |
| id | 1938537 |
| size | 793,725 |
A small, pragmatic Rust port of WebRTC's AEC3 acoustic-echo-canceller. This
crate exposes the full audio-processing implementation (in
crate::audio_processing::aec3) and provides a small, ergonomic VOIP-style
wrapper in crate::voip::VoipAec3 for easy integration into real-time
pipelines.
src/audio_processing — low-level audio primitives (buffers, filters, FFT,
and the AEC3 pipeline implementation).src/voip/mod.rs — a small wrapper that hides the plumbing and provides a
convenient builder-based API for VOIP use-cases.examples/karaoke_loopback.rs — example that captures system loopback + mic
and runs AEC in a processing thread.examples/karaoke_loopback_delayed.rs — delayed-loopback example that
simulates playback latency to exercise AEC delay estimation.tests/voip.rs — unit tests that exercise the wrapper API.EchoCanceller3Config.cargo run --example karaoke_loopback
cargo test
The VoipAec3 wrapper is the recommended way to integrate AEC3 into a
real-time pipeline. It handles conversion between interleaved frame buffers
and the internal multi-band audio buffers, applies an optional high-pass
filter, and exposes a small set of methods mirroring the reference demo.
Example (synchronous caller):
use aec3::voip::VoipAec3;
let mut pipeline = VoipAec3::builder(48_000, 2, 2)
.initial_delay_ms(116)
.enable_high_pass(true)
.build()
.expect("failed to create pipeline");
// Per 10 ms captured frame (interleaved f32 samples):
let capture_frame: Vec<f32> = /* filled by your capture callback */;
let render_frame: Vec<f32> = /* optional far-end data */;
let mut out = vec![0.0f32; capture_frame.len()];
let metrics = pipeline.process(&capture_frame, Some(&render_frame), false, &mut out)?;
println!("AEC metrics: {:?}", metrics);
The examples/karaoke_loopback_delayed.rs example demonstrates how to
simulate a playback / speaker-path delay and exercise the AEC's delay
estimation and alignment logic. Important note: to simulate the real-world
behavior of a delayed speaker path you should delay the capture path (the
microphone frames) relative to the far-end reference, or provide an explicit
delay hint with set_audio_buffer_delay. Delaying the render (far-end)
reference itself will make the canceller attempt to remove audio that has
not yet occurred and prevents the filter from converging.
The example uses the wrapper's lower-level methods directly for clarity:
handle_render_frame(&mut, render_frame) — feed far-end frames as they are
captured from system loopback. This keeps the reference aligned with what
was actually played.process_capture_frame(&mut, capture_frame, level_change, out) — process
microphone frames after they have been delayed to simulate playback latency.High-level pattern used by the example:
handle_render_frame.target_delay (e.g.
20 ms). Before processing a delayed capture frame, drain any pending
render frames into the AEC so the canceller sees the freshest far-end
audio for that capture slot.This preserves the proper timing relationship between far-end and near-end signals and allows the AEC to estimate and remove echo correctly.
VoipAec3::builder(sample_rate_hz, render_channels, capture_channels)
VoipAec3 methods
frame_samples() — samples per channel per 10 ms framesample_rate_hz() — configured sample ratehandle_render_frame(&mut self, render_frame: &[f32]) — feed far-endprocess_capture_frame(&mut self, capture_frame: &[f32], level_change: bool, out: &mut [f32]) -> Result<Metrics, Error>process(&mut, capture_frame, Option<render_frame>, level_change, out) — convenienceset_audio_buffer_delay(&mut self, delay_ms: i32) — update delay hintmetrics(&self) — get current metricsframe_samples * channels. frame_samples() returns the per-channel
length for 10 ms frames (e.g. 480 for 48 kHz, 160 for 16 kHz).process(capture, Some(render), ...) so the pipeline sees
render first (consistent with the reference usage order).PRs welcome. Follow standard Rust contribution practices: ensure cargo test
passes and run cargo fmt before submitting.
There are a few community-maintained projects that integrate with or wrap this crate. For example:
If you maintain a project that uses or wraps aec3, please open a PR to add it
here so others can find it easily.
This repository is a port of code aligned with WebRTC reference algorithms. Adopt and/or license in accordance with your needs and the original project policy.