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