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