Skip to main content

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:

  1. Reference signal — what the bot is sending to the speaker (outgoing TTS audio)
  2. 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:

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

voiceclean-0.2.0.tar.gz (14.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

voiceclean-0.2.0-py3-none-any.whl (15.0 kB view details)

Uploaded Python 3

File details

Details for the file voiceclean-0.2.0.tar.gz.

File metadata

  • Download URL: voiceclean-0.2.0.tar.gz
  • Upload date:
  • Size: 14.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for voiceclean-0.2.0.tar.gz
Algorithm Hash digest
SHA256 07abef8bf6987479db9d38dc700d07d062ec1406b2df3eb15e6ac3d0d385ec7b
MD5 4e2eb6b02ebc21c1fc726f2eaa9d3b78
BLAKE2b-256 87a8498a56f6dbaa7c8d8e871193fd086732807b110fadd7f72f6d92f3f9a87c

See more details on using hashes here.

File details

Details for the file voiceclean-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: voiceclean-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 15.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for voiceclean-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 43d99020144ef439b41900ba37fb7db126c7378e85a23ec00b5b79ef8af6d94d
MD5 9b424444501cfbef2772dd809a5d1aa5
BLAKE2b-256 073f11b1939af1eec6695d96c53d2c370de582107422ca7e3ade408bc15f98e8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page