sva_samples/measure/
sliding.rs1use super::harmony::dissonance::Extreme;
4
5#[derive(Clone, Copy, Debug, PartialEq)]
7pub enum Unread {
8 WindowUnderTwoPeriods {
9 window_secs: f64,
10 least_secs: f64,
11 },
12 TargetShorterThanWindow {
13 held_secs: f64,
14 window_secs: f64,
15 least_secs: f64,
16 },
17}
18
19pub fn fits(
20 held: usize,
21 sample_rate: f64,
22 window_secs: f64,
23 longest_period_secs: f64,
24) -> Result<(), Unread> {
25 let least_secs = 2.0 * longest_period_secs;
26 if window_secs < least_secs {
27 return Err(Unread::WindowUnderTwoPeriods {
28 window_secs,
29 least_secs,
30 });
31 }
32 let held_secs = held as f64 / sample_rate;
33 if held_secs < window_secs {
34 return Err(Unread::TargetShorterThanWindow {
35 held_secs,
36 window_secs,
37 least_secs,
38 });
39 }
40 Ok(())
41}
42
43#[derive(Clone, Copy, Debug, PartialEq)]
44pub struct Span {
45 pub t_secs: f64,
46 pub from_secs: f64,
47 pub to_secs: f64,
48 pub from: usize,
49 pub to: usize,
50}
51
52#[derive(Clone, Debug, PartialEq)]
53pub struct Spans {
54 pub hop_secs: f64,
55 pub window_secs: f64,
56 pub spans: Vec<Span>,
57}
58
59pub fn spans(
62 held: usize,
63 sample_rate: f64,
64 start_secs: f64,
65 hop_secs: f64,
66 rate: f64,
67 window_secs: f64,
68 len: usize,
69) -> Spans {
70 let hop = ((hop_secs * sample_rate).round() as usize).max(1);
71 let size = (window_secs * rate).round() as usize;
72 Spans {
73 hop_secs: hop as f64 / sample_rate,
74 window_secs: size as f64 / rate,
75 spans: (0..held)
76 .step_by(hop)
77 .map(|c| {
78 let middle = (c as f64 * rate / sample_rate).round() as usize;
79 let from = middle.saturating_sub(size / 2);
80 let to = (middle + size - size / 2).min(len);
81 Span {
82 t_secs: start_secs + c as f64 / sample_rate,
83 from_secs: start_secs + from as f64 / rate,
84 to_secs: start_secs + to as f64 / rate,
85 from,
86 to,
87 }
88 })
89 .collect(),
90 }
91}
92
93#[derive(Clone, Copy, Debug, PartialEq)]
94pub struct Stats {
95 pub mean: f64,
96 pub min: Extreme,
97 pub max: Extreme,
98}
99
100pub fn stats(values: impl Iterator<Item = (f64, f64)>) -> Option<Stats> {
101 let held: Vec<(f64, f64)> = values.collect();
102 let first = *held.first()?;
103 let pick = |better: fn(f64, f64) -> bool| {
104 let (t, value) = held
105 .iter()
106 .copied()
107 .fold(first, |a, b| if better(b.1, a.1) { b } else { a });
108 Extreme { value, t_secs: t }
109 };
110 Some(Stats {
111 mean: held.iter().map(|(_, v)| v).sum::<f64>() / held.len() as f64,
112 min: pick(|a, b| a < b),
113 max: pick(|a, b| a > b),
114 })
115}