DTLN noise suppression plugin for LiveKit Agents — self-hosted, in-process, no cloud API
Project description
livekit-plugins-dtln
Python LiveKit plugin for DTLN (Dual-Signal Transformation LSTM Network) noise suppression — a fully self-hosted, open-source alternative to cloud-based noise cancellation services like Krisp or AI-coustics.
Runs entirely in-process using ONNX Runtime. No cloud API, no per-minute fees, no proprietary binaries. Works with self-hosted LiveKit servers.
Based on Westhausen & Meyer, "Noise Reduction with DTLN", Interspeech 2020 Original implementation: github.com/breizhn/DTLN
Why DTLN?
| DTLN (this plugin) | Krisp / AI-coustics | |
|---|---|---|
| Hosting | Self-hosted, in-process | Cloud API required |
| Cost | Free (open weights) | Per-minute billing |
| LiveKit | Works with self-hosted | Requires LiveKit Cloud |
| Latency | ~8 ms (one block shift) | Network round-trip |
| Privacy | Audio never leaves your server | Audio sent to third party |
| Real-time factor | ~0.05× (20× faster than real-time) | Varies |
Installation
pip:
pip install livekit-plugins-dtln
requirements.txt:
livekit-plugins-dtln
From source:
git clone https://github.com/aloware/livekit-plugins-dtln.git
pip install -e ./livekit-plugins-dtln
The pretrained ONNX model weights (~4 MB) are bundled in the PyPI wheel — no separate download step needed.
Usage
Session pipeline (recommended)
from livekit.agents import room_io
from livekit.plugins import dtln
await session.start(
# ...,
room_options=room_io.RoomOptions(
audio_input=room_io.AudioInputOptions(
noise_cancellation=dtln.noise_suppression(),
),
),
)
Custom AudioStream
from livekit import rtc
from livekit.plugins import dtln
stream = rtc.AudioStream.from_track(
track=track,
noise_cancellation=dtln.noise_suppression(),
)
Note: Create one
dtln.noise_suppression()instance per session. Each instance holds stateful LSTM hidden states that must be scoped to a single call.
Note: DTLN is trained on raw microphone audio. Do not chain it with another noise cancellation model — applying two models in series produces unexpected results.
Custom model paths
dtln.noise_suppression(
model_1_path="/path/to/model_1.onnx",
model_2_path="/path/to/model_2.onnx",
)
Requirements
- Python >= 3.10
- livekit >= 1.1.0
- livekit-agents >= 1.4.4
- onnxruntime >= 1.17.0
- numpy >= 1.26.0
How It Works
DTLN uses two sequential LSTM-based models:
-
Model 1 — Spectral masking: Computes the magnitude spectrum of a 32 ms window, runs it through an LSTM to produce a spectral mask, applies the mask in the frequency domain (preserving phase), and reconstructs the time-domain signal via IFFT.
-
Model 2 — Time-domain refinement: Refines the output of Model 1 with a second LSTM that operates directly on the waveform, capturing residual artifacts that spectral processing misses.
The two models are chained: Model 1's output feeds Model 2. Both LSTMs are stateful — their hidden states persist across audio frames, giving the network temporal context across the full duration of a call.
Signal flow:
Input frame (any sample rate, any channels)
→ downsample to 16 kHz mono
→ overlap-add loop (512-sample window, 128-sample shift)
→ FFT → magnitude → Model 1 (spectral mask) → masked IFFT
→ Model 2 (time-domain refinement)
→ upsample back to original sample rate
→ restore original channel count
→ Denoised output frame
The overlap-add synthesis uses 75% overlap (512-sample window, 128-sample shift), identical to the original DTLN paper. This gives ~8 ms of algorithmic latency at 16 kHz.
Performance
Benchmarked on Apple M3 Pro, processing 16 kHz mono audio:
| Metric | Value |
|---|---|
| Steady-state latency per block | ~0.7 ms |
| Real-time factor | ~0.05× |
| Headroom vs real-time | ~20× |
| Cold-start (first inference) | ~500 ms (amortized by warmup in __init__) |
The __init__ method runs a dummy forward pass to trigger ONNX Runtime's JIT compilation before the first real audio frame arrives, eliminating the cold-start stall.
Models
Pretrained weights are the official DTLN models published by the original authors:
| File | Source |
|---|---|
model_1.onnx |
breizhn/DTLN · pretrained_model/ |
model_2.onnx |
breizhn/DTLN · pretrained_model/ |
The models are not bundled in this repository (to keep it lightweight). They are downloaded automatically by python agent.py download-files or by calling download_models() directly.
References
- Original DTLN paper: Westhausen & Meyer, "Noise Reduction with DTLN", Interspeech 2020
- Original DTLN implementation & pretrained models: github.com/breizhn/DTLN
- DataDog engineering article — the inspiration for this plugin: Building a Real-Time Noise Suppression Library
- LiveKit noise cancellation overview: docs.livekit.io — Noise Cancellation
- LiveKit Agents SDK: github.com/livekit/agents
- ONNX Runtime: onnxruntime.ai
License
The plugin code in this repository is released under the MIT License.
The pretrained DTLN model weights are published by the original authors under the MIT License — see breizhn/DTLN.
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 livekit_plugins_dtln-0.1.0.tar.gz.
File metadata
- Download URL: livekit_plugins_dtln-0.1.0.tar.gz
- Upload date:
- Size: 3.7 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2e4f748fd2c2e05bcdd4c4df6fee0382c51f392d72b8b4450481fae13b1cde63
|
|
| MD5 |
755d645e9178fdc6c4bbbccc742ceae4
|
|
| BLAKE2b-256 |
381c4298b9a0e812b50aa021a596f2c3505e37e46413788a406b9072c35b2768
|
Provenance
The following attestation bundles were made for livekit_plugins_dtln-0.1.0.tar.gz:
Publisher:
publish.yml on aloware/livekit-plugins-dtln
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
livekit_plugins_dtln-0.1.0.tar.gz -
Subject digest:
2e4f748fd2c2e05bcdd4c4df6fee0382c51f392d72b8b4450481fae13b1cde63 - Sigstore transparency entry: 1047224407
- Sigstore integration time:
-
Permalink:
aloware/livekit-plugins-dtln@7a39dbbf322fa193896d9e7d5856dc11c74b1dd7 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/aloware
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7a39dbbf322fa193896d9e7d5856dc11c74b1dd7 -
Trigger Event:
push
-
Statement type:
File details
Details for the file livekit_plugins_dtln-0.1.0-py3-none-any.whl.
File metadata
- Download URL: livekit_plugins_dtln-0.1.0-py3-none-any.whl
- Upload date:
- Size: 3.7 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7f3e664d510478247edb51cae73ba7913978d969695696ad603335f68592c0a8
|
|
| MD5 |
efb360b30e0eb5bfb9c136222598642e
|
|
| BLAKE2b-256 |
21c34b8b9d23ff1d47d5454af8fed9b0bcc99267e8348f2f0463b38a9386090e
|
Provenance
The following attestation bundles were made for livekit_plugins_dtln-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on aloware/livekit-plugins-dtln
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
livekit_plugins_dtln-0.1.0-py3-none-any.whl -
Subject digest:
7f3e664d510478247edb51cae73ba7913978d969695696ad603335f68592c0a8 - Sigstore transparency entry: 1047224484
- Sigstore integration time:
-
Permalink:
aloware/livekit-plugins-dtln@7a39dbbf322fa193896d9e7d5856dc11c74b1dd7 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/aloware
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7a39dbbf322fa193896d9e7d5856dc11c74b1dd7 -
Trigger Event:
push
-
Statement type: