use std::path::PathBuf;
use thiserror::Error;
#[cfg(target_os = "macos")]
const PERMISSION_HINT: &str = "Enable in System Preferences > Security & Privacy > Microphone.";
#[cfg(target_os = "windows")]
const PERMISSION_HINT: &str = "Enable in Settings > Privacy & Security > Microphone.";
#[cfg(target_os = "linux")]
const PERMISSION_HINT: &str =
"Check your distribution's audio permissions (PulseAudio / PipeWire).";
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
const PERMISSION_HINT: &str = "Check your system audio permissions.";
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum DecibriError {
#[error("sample rate must be between 1000 and 384000")]
SampleRateOutOfRange,
#[error("channels must be between 1 and 32")]
ChannelsOutOfRange,
#[error("frames per buffer must be between 64 and 65536")]
FramesPerBufferOutOfRange,
#[error("format must be 'int16' or 'float32'")]
InvalidFormat,
#[error("No audio input device found matching \"{0}\"")]
DeviceNotFound(String),
#[error("No audio output device found matching \"{0}\"")]
OutputDeviceNotFound(String),
#[error("Multiple devices match \"{name}\":\n{matches}")]
MultipleDevicesMatch { name: String, matches: String },
#[error("device index out of range. Call Decibri.devices() to list available devices")]
DeviceIndexOutOfRange,
#[error("No microphone found. Check system audio input settings.")]
NoMicrophoneFound,
#[error("No audio output device found. Check system audio settings.")]
NoOutputDeviceFound,
#[error("Selected device is not a valid input device.")]
NotAnInputDevice,
#[error("Failed to enumerate devices: {0}")]
DeviceEnumerationFailed(String),
#[error("audio stream is already running. Call stop() first.")]
AlreadyRunning,
#[error("Failed to open audio stream: {0}")]
StreamOpenFailed(String),
#[error("Failed to start audio stream: {0}")]
StreamStartFailed(String),
#[error("Microphone permission denied. {}", PERMISSION_HINT)]
PermissionDenied,
#[error("Capture stream is closed")]
CaptureStreamClosed,
#[error("Output stream closed")]
OutputStreamClosed,
#[error("Silero VAD only supports sample rates 8000 and 16000")]
VadSampleRateUnsupported(u32),
#[error("VAD threshold must be between 0.0 and 1.0")]
VadThresholdOutOfRange(f32),
#[error(
"decibri: failed to initialize ONNX Runtime: {source}. \
Either pass ort_library_path when constructing the VAD, set \
ORT_DYLIB_PATH to point to a valid ONNX Runtime library, or \
enable the `ort-download-binaries` feature for zero-config builds."
)]
OrtInitFailed {
#[source]
source: ort::Error,
},
#[error(
"decibri: failed to load ONNX Runtime from {}: {source}. \
If ORT_DYLIB_PATH is set, verify it points to a valid ONNX Runtime \
library for your platform. Otherwise the bundled ONNX Runtime may \
be missing from your installation. Try reinstalling decibri.",
path.display()
)]
OrtLoadFailed {
path: PathBuf,
#[source]
source: ort::Error,
},
#[error(
"decibri: failed to load ONNX Runtime from {}: {reason}. \
If ORT_DYLIB_PATH is set, verify it points to a valid ONNX Runtime \
library for your platform. Otherwise the bundled ONNX Runtime may \
be missing from your installation. Try reinstalling decibri.",
path.display()
)]
OrtPathInvalid { path: PathBuf, reason: &'static str },
#[error("Failed to create ort session builder: {0}")]
OrtSessionBuildFailed(#[source] ort::Error),
#[error("Failed to set ort threads: {0}")]
OrtThreadsConfigFailed(#[source] ort::Error),
#[error("Failed to load Silero VAD model from {}: {source}", path.display())]
VadModelLoadFailed {
path: PathBuf,
#[source]
source: ort::Error,
},
#[error("Silero VAD inference failed: {0}")]
OrtInferenceFailed(#[source] ort::Error),
#[error("Failed to create {kind} tensor: {source}")]
OrtTensorCreateFailed {
kind: &'static str,
#[source]
source: ort::Error,
},
#[error("Failed to extract {kind} tensor: {source}")]
OrtTensorExtractFailed {
kind: &'static str,
#[source]
source: ort::Error,
},
}
impl DecibriError {
pub fn is_ort_path_error(&self) -> bool {
matches!(
self,
Self::OrtLoadFailed { .. } | Self::OrtPathInvalid { .. }
)
}
}