transcribe_cpp/
diarize.rs1use std::os::raw::c_void;
5use std::sync::atomic::AtomicBool;
6use std::sync::Arc;
7
8use transcribe_cpp_sys as sys;
9
10use crate::cancel::{abort_trampoline, CancelToken};
11use crate::error::{check, Result};
12use crate::family::DiarizeExtension;
13use crate::model::{Model, ModelInner};
14use crate::result::{SpeakerSegment, Timings};
15use crate::session::clamp_len;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19#[non_exhaustive]
20#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
21pub struct DiarizeInfo {
22 pub sample_rate: i32,
24 pub max_speakers: i32,
26}
27
28#[derive(Debug, Clone, Default, PartialEq, Eq)]
30#[cfg_attr(
31 feature = "serde",
32 derive(serde::Serialize, serde::Deserialize),
33 serde(default)
34)]
35pub struct DiarizeSessionOptions {
36 pub n_threads: i32,
38}
39
40#[derive(Debug, Clone, Default, PartialEq, Eq)]
42#[cfg_attr(
43 feature = "serde",
44 derive(serde::Serialize, serde::Deserialize),
45 serde(default)
46)]
47pub struct DiarizeOptions {
48 pub family: Option<DiarizeExtension>,
50}
51
52pub struct DiarizeSession {
54 ptr: *mut sys::transcribe_diarize_session,
55 model: Arc<ModelInner>,
57 cancel: Option<Arc<AtomicBool>>,
59}
60
61impl std::fmt::Debug for DiarizeSession {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 f.debug_struct("DiarizeSession").finish_non_exhaustive()
64 }
65}
66
67unsafe impl Send for DiarizeSession {}
70
71impl Drop for DiarizeSession {
72 fn drop(&mut self) {
73 unsafe { sys::transcribe_diarize_session_free(self.ptr) };
74 }
75}
76
77impl DiarizeSession {
78 pub(crate) fn new(model: &Model, options: &DiarizeSessionOptions) -> Result<DiarizeSession> {
79 let mut params: sys::transcribe_diarize_session_params = unsafe { std::mem::zeroed() };
80 unsafe { sys::transcribe_diarize_session_params_init(&mut params) };
81 params.n_threads = options.n_threads;
82
83 let mut out: *mut sys::transcribe_diarize_session = std::ptr::null_mut();
84 let status =
85 unsafe { sys::transcribe_diarize_session_init(model.inner.ptr, ¶ms, &mut out) };
86 check(status, "diarize session init")?;
87 debug_assert!(!out.is_null());
88
89 Ok(DiarizeSession {
90 ptr: out,
91 model: Arc::clone(&model.inner),
92 cancel: None,
93 })
94 }
95
96 pub fn set_cancel_token(&mut self, token: &CancelToken) {
100 let flag = Arc::clone(&token.flag);
101 let userdata = Arc::as_ptr(&flag) as *mut c_void;
102 unsafe {
103 sys::transcribe_diarize_set_abort_callback(self.ptr, Some(abort_trampoline), userdata)
104 };
105 self.cancel = Some(flag);
106 }
107
108 pub fn clear_cancel_token(&mut self) {
110 unsafe { sys::transcribe_diarize_set_abort_callback(self.ptr, None, std::ptr::null_mut()) };
111 self.cancel = None;
112 }
113
114 pub fn run(&mut self, pcm: &[f32], options: &DiarizeOptions) -> Result<Vec<SpeakerSegment>> {
118 let n = clamp_len(pcm.len())?;
119 let family = options.family.as_ref().map(DiarizeExtension::materialize);
120 let mut params: sys::transcribe_diarize_params = unsafe { std::mem::zeroed() };
121 unsafe { sys::transcribe_diarize_params_init(&mut params) };
122 params.family = family.as_ref().map_or(std::ptr::null(), |f| f.ext_ptr());
123
124 let status = self.model.with_compute(
125 Some("a stream is active on this model; finish or drop it before diarize run()"),
126 |_| unsafe { sys::transcribe_diarize_run(self.ptr, pcm.as_ptr(), n, ¶ms) },
127 )?;
128 check(status, "diarize run")?;
129
130 let count = unsafe { sys::transcribe_diarize_n_segments(self.ptr) };
131 Ok((0..count)
132 .map(|i| {
133 let mut raw: sys::transcribe_speaker_segment = unsafe { std::mem::zeroed() };
134 unsafe { sys::transcribe_speaker_segment_init(&mut raw) };
135 let _ = unsafe { sys::transcribe_diarize_get_segment(self.ptr, i, &mut raw) };
136 SpeakerSegment::from_raw(&raw)
137 })
138 .collect())
139 }
140
141 pub fn timings(&self) -> Timings {
143 let mut raw: sys::transcribe_timings = unsafe { std::mem::zeroed() };
144 unsafe { sys::transcribe_timings_init(&mut raw) };
145 let _ = unsafe { sys::transcribe_diarize_get_timings(self.ptr, &mut raw) };
146 Timings::from_raw(&raw)
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153 use crate::Error;
154 use std::sync::Mutex;
155
156 #[test]
157 fn run_is_busy_while_a_stream_holds_the_lease() {
158 let mut session = DiarizeSession {
161 ptr: std::ptr::null_mut(),
162 model: Arc::new(ModelInner {
163 ptr: std::ptr::null_mut(),
164 compute_lock: Mutex::new(true),
165 }),
166 cancel: None,
167 };
168 let err = session
169 .run(&[0.0; 160], &DiarizeOptions::default())
170 .unwrap_err();
171 let Error::Busy(msg) = err else {
172 panic!("expected Busy, got {err:?}");
173 };
174 assert_eq!(
175 msg,
176 "a stream is active on this model; finish or drop it before diarize run()"
177 );
178 }
179}