Skip to main content

sva_samples/measure/
envelope.rs

1// Concern: traces level over time as successive frames of RMS and peak | Non-concern: frequency content (spectrum.rs), choosing the frame length | IO: (&[f64], sample rate, frame) -> frames
2
3#[derive(Clone, Copy, Debug, PartialEq)]
4pub struct EnvelopeFrame {
5    pub t_secs: f64,
6    pub rms: f64,
7    pub peak: f64,
8}
9
10pub fn trace(
11    samples: &[f64],
12    sample_rate: f64,
13    start_secs: f64,
14    frame_secs: f64,
15) -> Vec<EnvelopeFrame> {
16    let stride = ((frame_secs * sample_rate).round() as usize).max(1);
17    samples
18        .chunks(stride)
19        .enumerate()
20        .map(|(n, chunk)| EnvelopeFrame {
21            t_secs: start_secs + (n * stride) as f64 / sample_rate,
22            rms: rms(chunk),
23            peak: chunk.iter().fold(0f64, |a, &x| a.max(x.abs())),
24        })
25        .collect()
26}
27
28pub fn rms(samples: &[f64]) -> f64 {
29    if samples.is_empty() {
30        return 0.0;
31    }
32    let sum_sq: f64 = samples.iter().map(|&x| x * x).sum();
33    (sum_sq / samples.len() as f64).sqrt()
34}