use std::vec::Vec;
use mediatime::TimeRange;
use smol_str::SmolStr;
use crate::types::{ChunkId, Lang};
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Transcript {
range: TimeRange,
language: Lang,
text: SmolStr,
words: Vec<Word>,
avg_logprob: f32,
no_speech_prob: f32,
temperature: f32,
vad_segments: Vec<TimeRange>,
chunk_id: ChunkId,
}
impl Transcript {
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
range: TimeRange,
language: Lang,
text: SmolStr,
words: Vec<Word>,
avg_logprob: f32,
no_speech_prob: f32,
temperature: f32,
vad_segments: Vec<TimeRange>,
chunk_id: ChunkId,
) -> Self {
Self {
range,
language,
text,
words,
avg_logprob,
no_speech_prob,
temperature,
vad_segments,
chunk_id,
}
}
pub fn range(&self) -> TimeRange {
self.range
}
pub fn language(&self) -> &Lang {
&self.language
}
pub fn text(&self) -> &str {
self.text.as_str()
}
pub fn words(&self) -> &[Word] {
&self.words
}
pub fn avg_logprob(&self) -> f32 {
self.avg_logprob
}
pub fn no_speech_prob(&self) -> f32 {
self.no_speech_prob
}
pub fn temperature(&self) -> f32 {
self.temperature
}
pub fn vad_segments(&self) -> &[TimeRange] {
&self.vad_segments
}
pub fn chunk_id(&self) -> ChunkId {
self.chunk_id
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Word {
text: SmolStr,
range: TimeRange,
score: f32,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
language: Option<Lang>,
}
impl Word {
pub(crate) fn new(text: SmolStr, range: TimeRange, score: f32) -> Self {
Self {
text,
range,
score,
language: None,
}
}
#[must_use]
pub fn with_language(mut self, language: Option<Lang>) -> Self {
self.language = language;
self
}
pub fn set_language(&mut self, language: Option<Lang>) {
self.language = language;
}
pub fn text(&self) -> &str {
self.text.as_str()
}
pub fn range(&self) -> TimeRange {
self.range
}
pub fn score(&self) -> f32 {
self.score
}
pub fn language(&self) -> Option<&Lang> {
self.language.as_ref()
}
}
#[cfg(test)]
pub(crate) mod for_test {
use super::*;
use core::num::NonZeroU32;
pub(crate) fn ms_timebase() -> mediatime::Timebase {
mediatime::Timebase::new(1, NonZeroU32::new(1000).unwrap())
}
pub(crate) fn transcript(chunk_id: u64, text: &str, words: Vec<Word>) -> Transcript {
let tb = ms_timebase();
let range = TimeRange::new(0, 1000, tb);
Transcript::new(
range,
Lang::En,
SmolStr::new(text),
words,
-0.5,
0.05,
0.0,
vec![range],
ChunkId::from_raw(chunk_id),
)
}
pub(crate) fn word(text: &str, start_ms: i64, end_ms: i64, score: f32) -> Word {
Word::new(
SmolStr::new(text),
TimeRange::new(start_ms, end_ms, ms_timebase()),
score,
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn transcript_round_trip() {
let t = for_test::transcript(
7,
"hello world",
vec![
for_test::word("hello", 0, 500, 0.95),
for_test::word("world", 500, 1000, 0.92),
],
);
assert_eq!(t.text(), "hello world");
assert_eq!(t.chunk_id().as_u64(), 7);
assert_eq!(t.words().len(), 2);
assert_eq!(t.words()[0].text(), "hello");
assert_eq!(t.words()[1].score(), 0.92);
}
}