Decibri
Cross-platform audio capture, conditioning, and playback for Python, with voice activity detection on live and recorded audio.
Decibri is a native Python package that delivers microphone capture, speaker output, local voice activity detection, device enumeration, and sample format conversion. It is written in Rust (via PyO3 / abi3) and ships pre-built wheels for Linux, macOS Apple Silicon, and Windows.
Install
Recommended with uv:
uv pip install decibri
Or with pip:
pip install decibri
Quickstart
Capture audio
import decibri
with decibri.Microphone(sample_rate=16000, channels=1) as mic:
for chunk in mic:
print(f"Got {len(chunk)} bytes")
break # exit after first chunk for demo
Record one second to a WAV file
import decibri
decibri.record_to_file("output.wav", duration_seconds=1.0, sample_rate=16000)
Condition and analyze a recording
import decibri
# The same conditioning chain as the live microphone, over a WAV file.
with decibri.File("clip.wav", denoise="fastenhancer-t", highpass=80) as file:
for chunk in file:
handle(chunk) # conditioned int16 PCM bytes
# Whole-file speech analysis (a live stream cannot do this).
report = decibri.File("clip.wav", vad="silero").analyze()
for segment in report.segments:
print(segment.start, segment.end) # seconds of file time
Capture with Silero VAD
import decibri
with decibri.Microphone(sample_rate=16000, vad="silero") as mic:
for chunk in mic:
print(f"Got {len(chunk)} bytes; VAD score {mic.vad_score}; speaking={mic.is_speaking}")
break # exit after first chunk for demo
Async capture
import asyncio
import decibri
async def main():
async with await decibri.AsyncMicrophone.open(sample_rate=16000, vad="silero") as mic:
async for chunk in mic:
print(f"Got {len(chunk)} bytes; VAD score {mic.vad_score}")
break # exit after first chunk for demo
asyncio.run(main())
Speaker output
import decibri
with decibri.Speaker(sample_rate=24000, channels=1) as spk:
audio_bytes = b"\x00\x00" * 24000 # 1 second of silence at 24kHz int16
spk.write(audio_bytes) # int16 PCM
spk.drain()
Public API
Core classes
Microphone: synchronous audio captureSpeaker: synchronous audio outputFile: offline source; conditions a recording or in-memory samples, analyzes it for speech, or writes it back outAsyncMicrophone: async-await audio captureAsyncSpeaker: async-await audio outputAsyncFile: async-await offline source
Module-level functions
decibri.input_devices(): enumerate available input devicesdecibri.output_devices(): enumerate available output devicesdecibri.version(): version + audio backend infodecibri.record_to_file(path, duration_seconds, ...): record N seconds to a WAV filedecibri.async_record_to_file(path, duration_seconds, ...): async equivalent
Value types
MicrophoneInfo, SpeakerInfo, VersionInfo, Chunk, VadReport, VadWindow, Segment, AecMetrics, AecChannelMetrics, SaveReport, the config objects Vad and Aec, and the device selector Device.
Exceptions
The full hierarchy lives at decibri.exceptions. Top-level catch-targets surfaced at the package root:
DecibriError: base of the hierarchyDeviceError: input / output device problemsOrtError: ONNX Runtime issuesOrtPathError: ORT dylib path resolution issuesForkAfterOrtInit: Linux fork-after-ORT-init detection
Device selection
import decibri
# System default
mic = decibri.Microphone()
# By name (case-insensitive substring match)
mic = decibri.Microphone(device="USB")
# By index
devices = decibri.input_devices()
mic = decibri.Microphone(device=devices[1].index)
# By stable per-host identifier (exact match; survives across enumerations)
mic = decibri.Microphone(device=decibri.Device(id=devices[1].id))
Speaker, AsyncMicrophone, AsyncSpeaker, record_to_file and async_record_to_file take the same device forms. The device is resolved when the stream starts: a name that matches more than one device raises MultipleDevicesMatch, and a selector that matches none raises MicrophoneNotFound or SpeakerNotFound.
Voice Activity Detection
Decibri ships with two VAD modes: a lightweight RMS energy threshold (opt-in via vad="energy") and a Silero ONNX model (~2.3 MB, bundled in the wheel; no API keys required). Pass the mode as the vad="silero" / vad="energy" shorthand (which uses the default threshold and a 300 ms holdoff) or as a decibri.Vad(model=, threshold=, holdoff_ms=) config object to tune them.
# Energy mode (lightweight, no model)
mic = decibri.Microphone(vad=decibri.Vad(model="energy", threshold=0.01))
# Silero mode (ML-based, more accurate in noisy environments)
mic = decibri.Microphone(vad=decibri.Vad(model="silero", threshold=0.5))
Use mic.vad_score (a value in [0, 1]) to gate downstream processing. mic.is_speaking returns the boolean above-threshold view.
Decibri ACE (audio conditioning)
Decibri ACE (Audio Conditioning Engine) is decibri's opt-in audio front-end for speech: a conditioning chain applied to the captured audio before it is delivered. Every stage is off by default, runs on-device, and needs no API key. Leave the options unset and the capture path is unchanged.
The stages run in this order. Pass any subset on the Microphone constructor:
| Stage | Keyword | Value |
|---|---|---|
| DC removal | dc_removal=True |
bool, default False |
| Denoise | denoise="fastenhancer-t" |
the one bundled model |
| High-pass | highpass=80 or 100 |
Hz, second-order Butterworth |
| AGC | agc=-18 |
int dBFS, -40 to -3 |
| Limiter | limiter=-1.0 |
float dBFS, -3.0 to 0.0 |
import decibri
with decibri.Microphone(
sample_rate=16000,
denoise="fastenhancer-t", # bundled speech-enhancement model
highpass=80, # remove low-frequency rumble
agc=-18, # target level in dBFS
limiter=-1.0, # peak ceiling in dBFS
vad=decibri.Vad(model="silero", threshold=0.5),
) as mic:
for chunk in mic:
if mic.is_speaking:
handle(chunk) # conditioned int16 PCM bytes
The denoise model is bundled in the wheel (the same way the Silero VAD model is), so denoise="fastenhancer-t" needs no download. VAD reads the signal before the chain, so mic.vad_score and mic.is_speaking are unaffected by which conditioning stages you enable. The same options are available on AsyncMicrophone.
Echo cancellation
Acoustic echo cancellation removes the echo of far-end audio (what your application is playing out) from the captured audio. It is opt-in: pass aec="tau" on the Microphone constructor and push the far-end audio through push_aec_reference() as it is played, in played order. Leave aec unset and the capture path is unchanged; with no reference pushed the captured audio passes through unchanged. The canceller runs before voice activity detection, so mic.vad_score and mic.is_speaking read the echo-removed signal and playback stops triggering detection. It requires sample_rate in 8000 to 48000; a rate outside that raises AecSampleRateUnsupported at construction.
aec="tau" selects the model with its defaults; an Aec config object tunes it:
| Field | Default | Value |
|---|---|---|
model |
"tau" |
"tau", the one model |
tail_ms |
None (the canceller's 200) |
int ms, 16 to 500 |
suppression |
None (the canceller's "conservative") |
"conservative" or "off" |
reference_sample_rate |
None (the capture sample_rate) |
int Hz, 1000 to 384000 |
reference_channels |
1 |
int, at least 1 |
import decibri
with decibri.Microphone(sample_rate=16000, aec="tau") as mic:
# Push the far-end audio as it is played, in played order: the same
# input shapes Speaker.write accepts, in the microphone's dtype.
mic.push_aec_reference(far_end_pcm)
for chunk in mic:
handle(chunk) # echo-cancelled int16 PCM bytes
metrics = mic.aec_metrics() # AecMetrics while capture runs
push_aec_reference() never blocks and never raises on a full queue: samples that do not fit are discarded and counted by aec_metrics().reference_dropped. Silence between played audio need not be pushed. The canceller reads one mono reference: a reference at a reference_sample_rate other than the capture rate is converted before the canceller sees it, and with reference_channels above 1 each frame is averaged to one mono sample. With channels above 1, one canceller runs per delivered channel, each fed the same pushed reference, so one push serves every channel.
aec_metrics() returns an AecMetrics dataclass, the canceller's report merged with the reference queue's counters (delay_samples, erle_db, double_talk, reference_starved, acquisition_parked, reference_reanchors, reference_dropped, reference_silence), or None when aec is unset or capture is not running. Its channels tuple holds one AecChannelMetrics per delivered channel, in delivered order; the top-level fields report the first delivered channel's. erle_db is not a quality ranking across channels: it rises with echo distance, so a far microphone reports a higher figure than a near one while removing less echo in absolute terms.
The same aec parameter is available on AsyncMicrophone. push_aec_reference() is a plain method there too, so a renderer callback calls it without awaiting; aec_metrics() is awaited.
Files
File runs the same conditioning chain as Microphone on audio you already have: the same conditioning options (dc_removal, denoise, highpass, agc, limiter), the same iteration, the same conditioned chunks out, and the same opt-in vad=. Echo cancellation is a live-capture option and is not offered on File. A File reads WAV, AIFF, AIFF-C and FLAC (File("clip.wav"), or the identical File.open("clip.wav"); the container is identified from the file's own bytes rather than its extension) or wraps in-memory samples (File.buffer(samples, input_rate=48000); raw samples carry no header, so their native rate is explicit). sample_rate stays the target output rate, the same meaning it has on Microphone, so a 44.1 kHz recording comes out at 16 kHz unless you set it.
Because a File is a complete recording, it can analyze the whole recording for speech:
report = decibri.File("clip.wav", vad="silero").analyze() # or .analyse()
for w in report.scores:
print(w.start, w.end, w.vad_score, w.is_speech) # per 32 ms window
for segment in report.segments:
print(segment.start, segment.end) # merged speech regions
analyze() requires VAD: a File built without vad= raises VadNotConfigured rather than constructing a detector silently. With vad= set, metadata iteration (iter_with_metadata()) carries per-chunk vad_score and is_speaking exactly as the microphone does, with one deliberate difference: on a File, the speaking holdoff and the Chunk.timestamp are measured in FILE time (sample positions in seconds), never wall-clock time, so a file processed faster than real time still reports correct speech timing.
A File can also be written back out. save(path, format=None, compression=None) runs the recording once through the same conditioning pass iteration delivers and writes it as 16-bit PCM at sample_rate, at the delivered channel count, in WAV, AIFF or FLAC:
report = decibri.File("noisy.wav", denoise="fastenhancer-t").save("clean.flac")
print(report.clipped_samples, report.non_finite_samples)
The container comes from the path's extension (.wav, .aiff, .aif, .aifc or .flac) or from format; an extension it does not recognise raises AudioFormatUnsupported rather than defaulting. compression sets the FLAC compression level, 0 to 8 with 5 the default. The returned SaveReport says what the write did to the samples: clipped_samples counts finite samples outside full scale clamped to [-1.0, 1.0], and non_finite_samples counts the non-finite samples the save replaces before they reach the file (NaN as silence, an infinity as full scale). When the conditioning chain runs it has already replaced every non-finite sample with silence at its entry, so the count covers the direct path only, a mono source already at sample_rate with no conditioning enabled. Saving consumes the source and raises FileEngaged once iteration has begun, exactly as analyze() does.
Iteration, analysis and saving are separate single passes; construct one File per operation. AsyncFile mirrors the whole surface with async semantics, await file.save(...) included.
Compatibility
| Python | Platforms |
|---|---|
| 3.10, 3.11, 3.12, 3.13, 3.14 | Linux x64, Linux ARM64, macOS Apple Silicon, Windows x64, Windows ARM64 |
On Windows ARM64 the oldest supported Python is 3.11, the first CPython release published for that platform.
Bundled assets
The wheel includes:
- Silero VAD ONNX model (~2.3 MB): no downloads or API keys required for
vad="silero". - ONNX Runtime dylib (~15-20 MB platform-specific): no system dependency on
pip install onnxruntime.
First ORT load on vad="silero" initialization is ~100 to 500 ms (amortized across subsequent calls).
ONNX Runtime telemetry
vad="silero" and the ACE denoise stage run on ONNX Runtime, which carries its own telemetry, separate from anything decibri does. Decibri disables it on the environment it commits when it initializes the runtime. Set DECIBRI_ORT_TELEMETRY=1 in the environment before first use to leave it enabled; every other value, an empty value, and an absent variable leave it disabled.
Two limits apply on Windows and decibri can close neither, so decibri does not claim that no telemetry is emitted. ONNX Runtime logs one process-information event while the environment is being created, before the setting is applied, and logs it once per process, so that event is emitted whichever way the setting is left. The runtime also assigns its telemetry state from the Windows tracing session through an ETW callback, so the platform can re-enable telemetry after decibri has disabled it. On other platforms ONNX Runtime's telemetry provider does nothing.
Async usage
For Silero VAD in async code, use the open() factory to dispatch the synchronous ORT init off the event loop:
import asyncio
import decibri
async def main():
async with await decibri.AsyncMicrophone.open(vad="silero") as mic:
async for chunk in mic:
...
asyncio.run(main())
Synchronous constructors (AsyncMicrophone(...)) remain supported and unchanged; open() is the recommended pattern when ORT load cost matters.
Multiprocessing
Linux multiprocessing with Silero VAD requires set_start_method('spawn'). Calling fork() after ORT initialization raises ForkAfterOrtInit:
import multiprocessing as mp
mp.set_start_method('spawn') # required on Linux for Silero
See the ecosystem guides under bindings/python/docs/ecosystem/ (jupyter, docker, multiprocessing) for environment-specific details.
Documentation
- Source code & issues: github.com/decibri/decibri
- CHANGELOG: CHANGELOG.md
- Ecosystem guides (Jupyter, Docker, multiprocessing): under
bindings/python/docs/ecosystem/in the repo
License
Apache-2.0. See LICENSE for details.
Copyright (c) 2026 Decibri.
Metadata
Release files for decibri 0.13.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| decibri-0.13.0.tar.gz | 2.7 MB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| decibri-0.13.0-cp310-abi3-win_arm64.whl | CPython 3.10 | abi3 | Windows ARM64 | Details |
| decibri-0.13.0-cp310-abi3-win_amd64.whl | CPython 3.10 | abi3 | Windows x86-64 | Details |
| decibri-0.13.0-cp310-abi3-manylinux_2_28_x86_64.whl | CPython 3.10 | abi3 | Linux glibc 2.28+ x86-64 | Details |
| decibri-0.13.0-cp310-abi3-manylinux_2_28_aarch64.whl | CPython 3.10 | abi3 | Linux glibc 2.28+ ARM64 | Details |
| decibri-0.13.0-cp310-abi3-macosx_14_0_arm64.whl | CPython 3.10 | abi3 | macOS 14.0+ ARM64 | Details |
Total release size: 56.7 MB
Release files / decibri-0.13.0.tar.gz
| Download URL | decibri-0.13.0.tar.gz |
|---|---|
| Size | 2.7 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c8e00bce5e3fd55f73f6099010d65239e6d3ef83c27f8937a4d230658c079217
|
|
BLAKE2b-256 checksum How to use checksums |
1cfb370411baf7aaff71552952b9649ad686133aa548de03690697158170d87d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.
Transparency logRelease files / decibri-0.13.0-cp310-abi3-win_arm64.whl
| Download URL | decibri-0.13.0-cp310-abi3-win_arm64.whl |
|---|---|
| Size | 8.6 MB |
| Tags | CPython 3.10 Windows ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
68a7ed687ac0fac64752d86c06851df30597d280faa7eb6273086e51b958dd7d
|
|
BLAKE2b-256 checksum How to use checksums |
b3fe48f496c37afef3299d299cc67feb3affc7a809cf8f15765fce639cb90b80
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.
Transparency logRelease files / decibri-0.13.0-cp310-abi3-win_amd64.whl
| Download URL | decibri-0.13.0-cp310-abi3-win_amd64.whl |
|---|---|
| Size | 8.7 MB |
| Tags | CPython 3.10 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
158a27688058da280bb29f6c9c30b6d81852b40258909ddb372c365e5b8542ff
|
|
BLAKE2b-256 checksum How to use checksums |
c6e5b57ef3b38e183642c4f1e07dc86e453d38d0781992c6c214cdd3e0c9b925
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.
Transparency logRelease files / decibri-0.13.0-cp310-abi3-manylinux_2_28_x86_64.whl
| Download URL | decibri-0.13.0-cp310-abi3-manylinux_2_28_x86_64.whl |
|---|---|
| Size | 12.1 MB |
| Tags | CPython 3.10 Linux glibc 2.28+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
556b186ce17cb19a48217e1bcd7f85988086f3febd95f37fa6baa236f24323a2
|
|
BLAKE2b-256 checksum How to use checksums |
87cb34eb32b2df101d374ee0b63da10e16a0d93096d9c2b4e15d4df8698dcd55
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.
Transparency logRelease files / decibri-0.13.0-cp310-abi3-manylinux_2_28_aarch64.whl
| Download URL | decibri-0.13.0-cp310-abi3-manylinux_2_28_aarch64.whl |
|---|---|
| Size | 11.0 MB |
| Tags | CPython 3.10 Linux glibc 2.28+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
03faddc6346bb0182c09a0a0f193db2f2acb433f8ebc1d72daf5e7d23409ab95
|
|
BLAKE2b-256 checksum How to use checksums |
05341ed73e4abf298ab25efc15d13a7a0d2e298278656be4b34fbe855646223f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.
Transparency logRelease files / decibri-0.13.0-cp310-abi3-macosx_14_0_arm64.whl
| Download URL | decibri-0.13.0-cp310-abi3-macosx_14_0_arm64.whl |
|---|---|
| Size | 13.6 MB |
| Tags | CPython 3.10 abi3 macOS 14.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
9ee3d392e43f810b5a7537487f4b5b4235bf53d0a409e7fc33e97d65e07a854c
|
|
BLAKE2b-256 checksum How to use checksums |
8a0755f716ff3829060e56f667dd13c6e56efb18e216de524bcff4054dcfa45c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.
Transparency log