mod common;
use core::sync::atomic::AtomicBool;
use coremlit::audio::align::{
ANALYSIS_TIMEBASE, AlignError, Aligner, AlignerKey, AlignmentBinding, AlignmentFallback,
AlignmentSetBuilder, EnglishNormalizer, Lang, OovDecision, OutputClock, TimeRange,
};
#[test]
fn resolve_exposes_binding_as_data_never_an_aligner() {
let set = AlignmentSetBuilder::new()
.with_fallback(AlignmentFallback::Error)
.build();
let handle = set.resolve(&Lang::Zh);
assert_eq!(handle.language(), &Lang::Zh);
assert_eq!(
handle.binding(),
AlignmentBinding::Miss(AlignmentFallback::Error)
);
}
fn en_aligner() -> Aligner {
Aligner::from_paths(
Lang::En,
&common::model_path(),
Box::new(EnglishNormalizer::new()),
)
.expect("load base960h_aligner.mlmodelc as an En aligner (set ALIGNKIT_TEST_MODELS)")
}
fn whole_chunk_is_speech(samples: &[f32]) -> [TimeRange; 1] {
[TimeRange::new(0, samples.len() as i64, ANALYSIS_TIMEBASE)]
}
#[test]
#[ignore = "requires local alignkit models (ALIGNKIT_TEST_MODELS)"]
fn any_fallback_handle_keys_policy_on_the_requested_language() {
let set = AlignmentSetBuilder::new()
.register(AlignerKey::Any, en_aligner())
.build();
let handle = set.resolve(&Lang::Zh);
assert_eq!(handle.language(), &Lang::Zh);
assert_eq!(handle.binding(), AlignmentBinding::AnyFallback(Lang::En));
let samples = common::load_wav_mono_f32(&common::jfk_wav_path());
let text = common::JFK_TRANSCRIPT.replacen("Americans", "Américans", 1);
let detection = handle.detect_oov(&text).expect("handle detect_oov");
assert_eq!(detection.language(), &Lang::Zh);
let events = detection.events().expect("the Any aligner read the text");
assert!(
events.iter().any(|event| event.char() == Some('é')),
"the `é` of `Américans` is an OOV event: {events:?}"
);
assert!(events.iter().all(|event| event.language() == &Lang::Zh));
let resolution = detection.decide(|event| {
if event.language() == &Lang::Zh {
OovDecision::Wildcard
} else {
OovDecision::FailClosed
}
});
let clock = OutputClock::new(0, ANALYSIS_TIMEBASE, 0).expect("clock");
let abort = AtomicBool::new(false);
let alignment = handle
.align_chunk(
&samples,
&whole_chunk_is_speech(&samples),
&text,
clock,
&abort,
resolution,
)
.expect("Any-fallback alignment through the handle must not fail on the decisions");
assert!(
!alignment.words().is_empty(),
"the English Any aligner must align English speech to English words"
);
}
#[test]
#[ignore = "requires local alignkit models (ALIGNKIT_TEST_MODELS)"]
fn any_fallback_refusal_names_the_events_the_caller_decided() {
let set = AlignmentSetBuilder::new()
.register(AlignerKey::Any, en_aligner())
.build();
let handle = set.resolve(&Lang::Zh);
let samples = common::load_wav_mono_f32(&common::jfk_wav_path());
let text = "ask not what your country can do for you, AT&T b4d";
let detection = handle.detect_oov(text).expect("handle detect_oov");
let decided: Vec<_> = detection
.events()
.expect("the Any aligner read the text")
.iter()
.map(|event| (event.kind().clone(), event.char_index(), event.word_index()))
.collect();
assert!(
decided.len() >= 2,
"the `&` and the `4` are both events: {decided:?}"
);
let resolution = detection.decide(|_| OovDecision::FailClosed);
let clock = OutputClock::new(0, ANALYSIS_TIMEBASE, 0).expect("clock");
let abort = AtomicBool::new(false);
let err = handle
.align_chunk(&samples, &[], text, clock, &abort, resolution)
.expect_err("a policy that fails closed on every event refuses the chunk");
let AlignError::Refused(refusal) = err else {
panic!("the refusal must be named, got {err:?}");
};
let refused: Vec<_> = refusal
.events()
.iter()
.map(|event| (event.kind().clone(), event.char_index(), event.word_index()))
.collect();
assert_eq!(
refused, decided,
"the refusal names exactly the positions the caller decided"
);
assert!(
refusal
.events()
.iter()
.all(|event| event.language() == &Lang::Zh),
"the refusal names every position under the requested language"
);
}