use std::time::Duration;
use crate::audio::{CANONICAL_SAMPLE_RATE, canonical_samples};
pub(super) const LEAST_READ_SAMPLES: usize = canonical_samples(Duration::from_millis(500)) as usize;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum Reading {
Owner,
Other,
Pending,
}
struct Bars {
seconds: f32,
accept: f32,
doubt: f32,
}
const FITTED: [Bars; 6] = [
Bars {
seconds: 0.5,
accept: 0.313,
doubt: 0.156,
},
Bars {
seconds: 1.0,
accept: 0.358,
doubt: 0.232,
},
Bars {
seconds: 1.5,
accept: 0.379,
doubt: 0.346,
},
Bars {
seconds: 2.0,
accept: 0.390,
doubt: 0.390,
},
Bars {
seconds: 2.5,
accept: 0.398,
doubt: 0.398,
},
Bars {
seconds: 3.0,
accept: 0.403,
doubt: 0.403,
},
];
pub(super) fn read(similarity: f32, scored: usize) -> Reading {
if scored < LEAST_READ_SAMPLES {
return Reading::Pending;
}
let (accept, doubt) = bars(scored as f32 / CANONICAL_SAMPLE_RATE as f32);
if similarity >= accept {
Reading::Owner
} else if similarity < doubt {
Reading::Other
} else {
Reading::Pending
}
}
fn bars(seconds: f32) -> (f32, f32) {
for pair in FITTED.windows(2) {
let (before, after) = (&pair[0], &pair[1]);
if seconds < after.seconds {
let along = (seconds - before.seconds) / (after.seconds - before.seconds);
let between = |from: f32, to: f32| from + (to - from) * along;
return (
between(before.accept, after.accept),
between(before.doubt, after.doubt),
);
}
}
let last = &FITTED[FITTED.len() - 1];
(last.accept, last.doubt)
}
#[cfg(test)]
mod tests {
use super::*;
fn samples(seconds: f32) -> usize {
(seconds * CANONICAL_SAMPLE_RATE as f32) as usize
}
#[test]
fn under_half_a_second_no_score_decides() {
for similarity in [-1.0, 0.0, 0.99] {
assert_eq!(read(similarity, samples(0.4)), Reading::Pending);
}
}
#[test]
fn each_fitted_length_reads_by_its_own_bars() {
for row in &FITTED {
let scored = samples(row.seconds);
assert_eq!(read(row.accept, scored), Reading::Owner);
assert_eq!(read(row.doubt.next_down(), scored), Reading::Other);
}
assert_eq!(read(0.25, samples(1.5)), Reading::Other);
assert_eq!(read(0.36, samples(1.5)), Reading::Pending);
}
#[test]
fn a_length_between_rows_reads_by_bars_between_theirs() {
assert_eq!(read(0.367, samples(1.25)), Reading::Pending);
assert_eq!(read(0.369, samples(1.25)), Reading::Owner);
}
}