Skip to main content

Crate decibri

Crate decibri 

Source
Expand description

Cross-platform audio capture, playback, and voice-activity detection.

decibri records from a microphone, plays audio to a speaker, and runs Silero voice-activity detection through one API on Windows, macOS, and Linux. It is the Rust core that also powers decibri’s Python and Node.js packages.

§Orientation

There are two primary types: Microphone for input and Speaker for output. Both follow the same shape: build a configuration, construct the object, then call start() to open the device and obtain a stream.

Voice-activity detection runs through SileroVad: construct it once, then call process on each chunk’s samples.

List devices with input_devices / output_devices (or Microphone::devices / Speaker::devices) and pick one with a DeviceSelector. Every fallible operation returns DecibriError.

§Feature flags

FlagDefaultPurpose
captureonMicrophone input stream support
playbackonSpeaker output stream support
vadonSilero voice-activity detection (pulls ort)
ort-load-dynamiconONNX Runtime loaded at runtime from a path
ort-download-binariesoffONNX Runtime downloaded at build time

ort-load-dynamic and ort-download-binaries are mutually exclusive; selecting both is a compile error. Disabling vad drops the ONNX Runtime dependency entirely.

Execution-provider features (coreml, cuda, directml, rocm) are off by default and opt in to GPU acceleration on specific platforms. See docs/features.md for the full reference.

§Example: capture and detect speech

MicrophoneStream::next_chunk returns a three-state Result: Ok(Some(chunk)) with data, Ok(None) on a timeout while the stream is still open, and Err(DecibriError::MicrophoneStreamClosed) once the stream ends.

use std::time::Duration;
use decibri::{Microphone, MicrophoneConfig, SileroVad, VadConfig, DecibriError};

let microphone = Microphone::new(MicrophoneConfig::default())?;
let stream = microphone.start()?;
let mut vad = SileroVad::new(VadConfig::default())?;

// 1600 interleaved samples = one 100 ms block of mono 16 kHz audio
// (frames_per_buffer * channels for the default config).
loop {
    match stream.next_chunk(1600, Some(Duration::from_millis(100))) {
        Ok(Some(chunk)) => {
            let result = vad.process(&chunk.data)?;
            if result.is_speech {
                println!("speech @ p={:.2}", result.probability);
            }
        }
        Ok(None) => continue,
        Err(DecibriError::MicrophoneStreamClosed) => break,
        Err(e) => return Err(e.into()),
    }
}

§Process-global ORT initialization

ONNX Runtime initializes exactly once per process. The first successful SileroVad::new call wins; later constructions with a different vad::VadConfig::ort_library_path silently reuse the first path. See vad::VadConfig for details.

§ORT error construction has FFI side effects

Under ort-load-dynamic, constructing any ort::Error calls into the ORT C API (ortsys![CreateStatus]), which in turn triggers ORT dylib loading. If the dylib path is invalid (missing file, a directory, or a wrong-arch binary), this load can hang indefinitely on some hosts (reproduced on Windows with pyke/ort 2.0.0-rc.12 and onnxruntime 1.24.4).

For authors writing FFI bindings on top of decibri: when you need to surface a “this ORT path is wrong” failure from a pre-load check, use DecibriError::OrtPathInvalid (carries path: PathBuf and reason: &'static str, never touches ORT symbols) rather than reconstructing an ort::Error. decibri’s internal init_ort_once follows this pattern: it performs a filesystem-level Path::is_file() check first and returns OrtPathInvalid on rejection, only handing the path to ort::init_from once the check passes.

This constraint is why DecibriError splits “ORT path failure” across two variants: OrtLoadFailed carries an ort::Error source (reached after ort::init_from genuinely failed, by which point ORT is loaded and constructing errors is safe), and OrtPathInvalid does not (reached before any ORT symbol is touched). The is_ort_path_error helper groups both so consumers don’t need to match the variant distinction.

§Fork safety

Once ORT has been initialized in a process (via the first SileroVad::new call), that process’s ORT state survives across fork(). ORT is not designed to have its runtime state cloned into a forked child: internal threads, memory pools, and device handles belonging to the parent may misbehave in the child.

Python consumers using multiprocessing should call multiprocessing.set_start_method('spawn', force=True) before importing decibri. This gives each child its own fresh ORT state rather than inheriting the parent’s.

Node.js consumers using child_process or worker_threads are unaffected; those mechanisms do not share ORT state across workers.

§Thread safety

All public types are Send and suitable for cross-thread handoff. microphone::MicrophoneStream and speaker::SpeakerStream are also Sync (they hold their live platform audio stream behind an internal mutex), so they can be shared across threads, for example via an Arc, without external synchronization.

§Stability

The following stream surface is stable and changes signature only with a breaking version bump:

Re-exports§

pub use microphone::AudioChunk;
pub use microphone::DenoiseModel;
pub use microphone::HighpassFilter;
pub use microphone::Microphone;
pub use microphone::MicrophoneConfig;
pub use microphone::MicrophoneStream;
pub use file::File;
pub use file::FileConfig;
pub use file::Segment;
pub use file::VadReport;
pub use file::VadWindow;
pub use speaker::Speaker;
pub use speaker::SpeakerConfig;
pub use speaker::SpeakerSink;
pub use speaker::SpeakerStream;
pub use device::input_devices;
pub use device::output_devices;
pub use device::DeviceSelector;
pub use device::MicrophoneInfo;
pub use device::SpeakerInfo;
pub use vad::SileroVad;
pub use vad::VadConfig;
pub use vad::VadResult;
pub use error::DecibriError;

Modules§

denoise
Single-channel speech enhancement (denoise) for the capture path.
device
Audio device discovery and selection.
error
file
Offline audio source.
gain
Adaptive level control for the capture chain.
microphone
Microphone capture: open an input device and pull AudioChunks from it.
sample
speaker
Speaker playback: open an output device and push f32 samples to it.
vad
Voice Activity Detection using the Silero VAD v5 ONNX model.

Constants§

CPAL_VERSION
Resolved cpal version (major.minor) extracted at build time from the crate’s Cargo.toml.