#[cfg(all(feature = "silero", not(target_family = "wasm")))]
mod changes;
mod diarizer;
#[cfg(not(target_family = "wasm"))]
mod enrollment;
mod label;
mod profile;
#[cfg(not(target_family = "wasm"))]
mod store;
#[cfg(all(feature = "silero", not(target_family = "wasm")))]
mod turns;
mod voices;
use std::path::PathBuf;
use crate::backend::{BackendError, ModelDownload, ModelError};
#[cfg(all(
any(test, feature = "test-support"),
feature = "silero",
not(target_family = "wasm")
))]
pub use changes::LevelSegmenter;
pub use diarizer::{CLUSTER_THRESHOLD, Diarizer, ENROLLED_THRESHOLD, OWN_VOICE_THRESHOLD, settle};
#[cfg(not(target_family = "wasm"))]
pub use enrollment::{Enrolled, Enrollment, EnrollmentError, MIN_SAMPLES, centroid, consistency};
pub(crate) use label::AnonymousNumbering;
pub use label::Label;
#[cfg(any(test, feature = "test-support"))]
pub use profile::enrolled_voice;
pub use profile::{Profile, Role, SpeakerName, SpeakerNameError};
#[cfg(not(target_family = "wasm"))]
pub use store::{ProfileError, ProfileStore, Replace, StoredProfiles};
#[cfg(all(feature = "silero", not(target_family = "wasm")))]
pub use turns::{
Admission, Ahead, Decision, Gate, NoSegmenter, OwnerTrackingError, Released, Speaker,
SpeakerTracker, Timeline, TurnFeed, VoiceprintTracker,
};
pub use voices::{
Chunk, Grouping, Merged, PyannoteVoices, Revision, Spoken, Standing, Voiceprint, Voices,
attribute_spans,
};
pub trait Embedder {
fn embed(&mut self, window: &[f32]) -> Result<Option<Vec<f32>>, SpeakerError>;
fn dimension(&self) -> usize;
}
pub trait Engine: Embedder + Sized {
const NAME: &'static str;
const MODEL: &'static [ModelDownload];
type Config;
fn check_from(config: &Self::Config) -> Result<PathBuf, SpeakerError>;
fn load_from(config: Self::Config) -> Result<Self, SpeakerError>;
}
pub fn normalized(mut values: Vec<f32>) -> Option<Vec<f32>> {
let norm = values.iter().map(|value| value * value).sum::<f32>().sqrt();
if !norm.is_normal() {
return None;
}
for value in &mut values {
*value /= norm;
}
Some(values)
}
#[cfg(not(target_family = "wasm"))]
pub fn validate_model_file(path: PathBuf, backend: &'static str) -> Result<PathBuf, SpeakerError> {
crate::backend::validate_model_file(path)
.map_err(|source| SpeakerError::Model { backend, source })
}
#[derive(Debug, thiserror::Error)]
pub enum SpeakerError {
#[error("the {backend} model cannot be used")]
Model {
backend: &'static str,
#[source]
source: ModelError,
},
#[error("failed to load the {backend} model {model}")]
Load {
backend: &'static str,
model: String,
#[source]
source: BackendError,
},
#[error("{backend} inference failed")]
Inference {
backend: &'static str,
#[source]
source: BackendError,
},
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn normalizing_leaves_a_unit_vector_pointing_the_same_way() {
let unit = normalized(vec![3.0, 4.0]).expect("the vector has a direction");
assert!((unit[0] - 0.6).abs() < 1e-6);
assert!((unit[1] - 0.8).abs() < 1e-6);
}
#[test]
fn a_vector_without_direction_cannot_be_normalized() {
assert!(normalized(vec![0.0, 0.0]).is_none());
}
}