use std::path::PathBuf;
use crate::backend::{BackendError, ModelError};
pub trait Segmenter {
fn shape(&self) -> ChunkShape;
fn segment(&mut self, chunk: &[f32]) -> Result<Vec<Activity>, SegmentationError>;
}
pub trait Engine: Segmenter + Sized {
const NAME: &'static str;
type Config;
fn check_from(config: &Self::Config) -> Result<PathBuf, SegmentationError>;
fn load_from(config: Self::Config) -> Result<Self, SegmentationError>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ChunkShape {
pub samples: usize,
pub frame_shift: usize,
pub frame_size: usize,
pub speakers: usize,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Activity(u8);
impl Activity {
pub const SILENT: Self = Self(0);
pub const MAX_SPEAKERS: usize = 8;
pub fn speaking(mut self, local: usize) -> Self {
if local < Self::MAX_SPEAKERS {
self.0 |= 1 << local;
}
self
}
pub fn speaks(self, local: usize) -> bool {
local < Self::MAX_SPEAKERS && self.0 & (1 << local) != 0
}
pub fn voices(self) -> usize {
self.0.count_ones() as usize
}
}
#[cfg(not(target_family = "wasm"))]
pub fn validate_model_file(
path: PathBuf,
backend: &'static str,
) -> Result<PathBuf, SegmentationError> {
crate::backend::validate_model_file(path)
.map_err(|source| SegmentationError::Model { backend, source })
}
#[derive(Debug, thiserror::Error)]
pub enum SegmentationError {
#[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 silence_names_nobody() {
assert_eq!(Activity::SILENT.voices(), 0);
assert!(!Activity::SILENT.speaks(0));
}
#[test]
fn two_voices_at_once_is_an_ordinary_answer() {
let both = Activity::SILENT.speaking(0).speaking(2);
assert_eq!(both.voices(), 2);
assert!(both.speaks(0) && both.speaks(2));
assert!(!both.speaks(1));
}
#[test]
fn a_voice_the_set_cannot_hold_is_dropped_not_folded_onto_another() {
let overflowed = Activity::SILENT.speaking(Activity::MAX_SPEAKERS);
assert_eq!(overflowed.voices(), 0);
assert!(!overflowed.speaks(0));
}
}