Real-time AEC, noise suppression, and VAD for voice agents — wraps SpeexDSP, RNNoise, and Silero VAD.
Project description
voiceclean
Real-time acoustic echo cancellation (AEC), noise suppression, and voice activity detection (VAD) for voice agents. Built for telephony (8 kHz PSTN audio), works at any sample rate.
Wraps three proven open-source libraries into a single Python package:
| Feature | Library | What it does |
|---|---|---|
| AEC | SpeexDSP | Removes the bot's own speech from the caller's mic signal. Essential when the telephony provider doesn't do carrier-side echo cancellation. |
| Noise suppression | RNNoise via pyrnnoise | Neural noise reduction. Best at 48 kHz; for 8 kHz telephony, AEC alone is usually sufficient. |
| VAD | Silero VAD | Detects speech vs silence. <1 ms per chunk on CPU, 8 kHz native support, ~2 MB ONNX model. |
Why this exists
Cloud telephony providers like Twilio perform echo cancellation on their infrastructure before delivering audio to your WebSocket. Others (like Exotel) don't — the bot's TTS output bleeds through the caller's mic and gets transcribed as user input, creating a self-conversation loop.
Commercial solutions like ai-coustics ($149/mo) provide noise suppression and VAD but not AEC. voiceclean fills that gap with open-source components.
Install
pip install voiceclean # core (numpy + soxr only)
pip install voiceclean[all] # AEC + noise suppression + VAD + Pipecat integration
pip install voiceclean[silero] # VAD only
pip install voiceclean[rnnoise] # noise suppression only
System dependency for AEC — libspeexdsp must be installed on the host:
# Debian / Ubuntu
sudo apt install libspeexdsp-dev
# macOS
brew install speexdsp
Quick start
Standalone
from voiceclean import VoiceClean
vc = VoiceClean(sample_rate=8000)
# Feed the bot's outgoing audio as AEC reference
vc.feed_reference(bot_pcm_bytes)
# Process the caller's mic audio
result = vc.process(mic_pcm_bytes)
result.audio # cleaned PCM bytes (echo removed)
result.is_speech # bool — is the caller speaking?
result.speech_prob # float 0.0–1.0
Individual components
from voiceclean.aec import AEC
from voiceclean.denoise import Denoiser
from voiceclean.vad import VAD
# Echo cancellation
aec = AEC(sample_rate=8000, filter_length=4000)
aec.feed_reference(bot_audio)
clean = aec.process(mic_audio)
# Noise suppression (best at 48 kHz; resamples internally)
denoiser = Denoiser(sample_rate=8000)
clean = denoiser.process(noisy_audio)
# Voice activity detection
vad = VAD(sample_rate=8000, threshold=0.5)
result = vad.process(audio) # result.is_speech, result.speech_prob
Pipecat integration
voiceclean plugs into Pipecat as a BaseAudioFilter + VADAnalyzer. The key design: a reference_collector FrameProcessor captures outgoing audio for the AEC reference signal.
from voiceclean.pipecat import VoiceCleanFilter
vc_filter = VoiceCleanFilter(sample_rate=8000)
# Wire as audio input filter on the transport
transport = FastAPIWebsocketTransport(
websocket=websocket,
params=FastAPIWebsocketParams(
audio_in_filter=vc_filter,
serializer=serializer,
),
)
# VAD for turn detection
vad_analyzer = vc_filter.create_vad_analyzer()
# Build pipeline — reference_collector MUST go before transport.output()
pipeline = Pipeline([
transport.input(),
stt,
user_aggregator,
llm,
tts,
# ... other processors ...
vc_filter.reference_collector, # captures outgoing audio for AEC
transport.output(),
assistant_aggregator,
])
How AEC works
Echo cancellation needs two audio streams:
- Reference signal — what the bot is sending to the speaker (outgoing TTS audio)
- Mic signal — what the caller's mic picks up (speech + echo of the bot)
SpeexDSP's adaptive filter learns the echo path (including network delay, acoustic coupling, codec artifacts) and subtracts the predicted echo from the mic signal. The async playback/capture API handles delay alignment internally — you don't need to time-synchronize the two streams.
Bot TTS audio ──► feed_reference() ──► SpeexDSP learns echo path
│
Caller mic audio ──► process() ─────► SpeexDSP subtracts echo ──► clean audio
Filter length: Default 4000 samples = 500 ms at 8 kHz. This must cover the full echo round-trip delay (typically 100–500 ms on PSTN). Increase if you hear residual echo on high-latency paths.
Audio format
All audio is PCM int16 mono (signed 16-bit little-endian, single channel). This is the standard format for telephony audio and what Pipecat's frame processors use internally.
Notes on RNNoise
RNNoise operates at 48 kHz internally. For 8 kHz telephony audio, it resamples up and back (8k → 48k → 8k), which can degrade narrowband speech quality. In telephony applications, AEC alone is usually sufficient — the PSTN noise floor is low enough for STT accuracy. The Pipecat filter disables RNNoise by default for this reason.
RNNoise works well for wideband (16 kHz+) applications where the full frequency range benefits from neural denoising.
Credits
This package wraps open-source libraries by their respective authors:
- SpeexDSP — Jean-Marc Valin / Xiph.Org Foundation (BSD License)
- RNNoise — Jean-Marc Valin / Xiph.Org Foundation (BSD-3-Clause License)
- Silero VAD — Silero Team (MIT License)
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file voiceclean-0.3.0.tar.gz.
File metadata
- Download URL: voiceclean-0.3.0.tar.gz
- Upload date:
- Size: 14.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6cc274ff5bd231bbe75594e9787df49d093c659ad951d6dce266c0823fc52f59
|
|
| MD5 |
af41903311f7baad5995fa447325ede4
|
|
| BLAKE2b-256 |
8ab3587a07011cc98c9c90162f2956cf6fa4651f5626c38ae01f9129527a43c2
|
File details
Details for the file voiceclean-0.3.0-py3-none-any.whl.
File metadata
- Download URL: voiceclean-0.3.0-py3-none-any.whl
- Upload date:
- Size: 14.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
edc0542624eef153d22f79077894d115d05fc79f20ac9596d483ef0c3cf99c3e
|
|
| MD5 |
7ad3ceecfbc3cae73e3d862d040c66b3
|
|
| BLAKE2b-256 |
23aaabccd11d33c3d824b84a4a32a069c29ab8c8925996ecdd8c670e4e521569
|