Skip to main content

Decibri

Cross-platform audio capture, conditioning, and playback for Python, with voice activity detection on live and recorded audio.

PyPI version Python versions License

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 capture
  • Speaker: synchronous audio output
  • File: offline source; conditions a recording or in-memory samples, analyzes it for speech, or writes it back out
  • AsyncMicrophone: async-await audio capture
  • AsyncSpeaker: async-await audio output
  • AsyncFile: async-await offline source

Module-level functions

  • decibri.input_devices(): enumerate available input devices
  • decibri.output_devices(): enumerate available output devices
  • decibri.version(): version + audio backend info
  • decibri.record_to_file(path, duration_seconds, ...): record N seconds to a WAV file
  • decibri.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 hierarchy
  • DeviceError: input / output device problems
  • OrtError: ONNX Runtime issues
  • OrtPathError: ORT dylib path resolution issues
  • ForkAfterOrtInit: 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

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)

Source distribution for decibri 0.13.0
File Size Uploaded
decibri-0.13.0.tar.gz 2.7 MB Details

Built distributions (wheels)

Table of built distributions (wheels) for decibri 0.13.0
File
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 log

Release 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 log

Release 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 log

Release 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 log

Release 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 log

Release 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

Release history Release notifications | RSS feed

This release

0.13.0 This release

6 release files

0.12.0

6 release files

0.11.0

5 release files

0.9.0

5 release files

0.8.0

5 release files

0.7.5

5 release files

0.7.4

5 release files

0.7.3

5 release files

0.7.2

5 release files

0.7.1

5 release files

0.7.0

5 release files

0.6.0

5 release files

0.5.0

5 release files

0.4.3

5 release files

0.4.2

5 release files

0.4.1

5 release files

0.4.0

5 release files

0.3.0

5 release files

0.2.1

5 release files

0.2.0

5 release files

0.1.3

5 release files

0.1.2

4 release files

0.1.1

4 release files

0.1.0

4 release files

0.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page