1use crate::{Audio, Error, Limits, Result};
3use std::ops::Range;
4
5pub const MAX_MEASUREMENT_BOUNDARIES: usize = 4096;
7
8#[derive(Clone, Debug, Eq, PartialEq)]
10pub struct ThresholdSilence {
11 pub frames: usize,
12 pub leading_frames: usize,
14 pub trailing_frames: usize,
16 pub active_range: Option<Range<usize>>,
19}
20
21#[derive(Clone, Copy, Debug, PartialEq)]
24pub struct StereoMeasurements {
25 pub frames: usize,
26 pub correlation: Option<f64>,
30 pub left_mean_square: Option<f64>,
31 pub right_mean_square: Option<f64>,
32 pub mid_mean_square: Option<f64>,
34 pub side_mean_square: Option<f64>,
36}
37
38#[derive(Clone, Copy, Debug, Eq, PartialEq)]
40pub enum MeasurementBoundary {
41 Frame(usize),
43 LoopSeam,
45}
46#[derive(Clone, Copy, Debug, PartialEq)]
48pub struct ChannelStep {
49 pub before: f64,
50 pub after: f64,
51 pub difference: f64,
53}
54#[derive(Clone, Debug, PartialEq)]
55pub struct BoundarySamples {
56 pub before_frame: usize,
57 pub after_frame: usize,
58 pub channels: Vec<ChannelStep>,
60 pub max_abs_step: f64,
61}
62#[derive(Clone, Debug, PartialEq)]
63pub struct BoundaryMeasurement {
64 pub boundary: MeasurementBoundary,
65 pub samples: Option<BoundarySamples>,
67}
68
69#[derive(Default)]
73struct Sum {
74 sum: f64,
75 correction: f64,
76}
77impl Sum {
78 fn add(&mut self, value: f64) {
79 let next = self.sum + value;
80 self.correction += if self.sum.abs() >= value.abs() {
81 (self.sum - next) + value
82 } else {
83 (value - next) + self.sum
84 };
85 self.sum = next;
86 }
87 fn value(&self) -> f64 {
88 self.sum + self.correction
89 }
90}
91fn finite(sample: f32) -> Result<f64> {
92 if sample.is_finite() {
93 Ok(sample as f64)
94 } else {
95 Err(Error::invalid("Measurement samples must be finite"))
96 }
97}
98fn reserve<T>(count: usize) -> Result<Vec<T>> {
99 let mut values = Vec::new();
100 values
101 .try_reserve_exact(count)
102 .map_err(|_| Error::limit("Cannot allocate measurement output"))?;
103 Ok(values)
104}
105pub fn dc_mean(samples: &[f32]) -> Result<Option<f64>> {
110 let mut sum = Sum::default();
111 for &sample in samples {
112 sum.add(finite(sample)?);
113 }
114 Ok((!samples.is_empty()).then(|| sum.value() / samples.len() as f64))
115}
116pub fn stereo_measurements(left: &[f32], right: &[f32]) -> Result<StereoMeasurements> {
123 if left.len() != right.len() {
124 return Err(Error::invalid("Stereo channels must have equal lengths"));
125 }
126 let mut out = StereoMeasurements {
127 frames: left.len(),
128 correlation: None,
129 left_mean_square: None,
130 right_mean_square: None,
131 mid_mean_square: None,
132 side_mean_square: None,
133 };
134 let l_mean = dc_mean(left)?;
135 let r_mean = dc_mean(right)?;
136 let (Some(l_mean), Some(r_mean)) = (l_mean, r_mean) else {
137 return Ok(out);
138 };
139 let [mut lp, mut rp, mut mp, mut sp, mut lv, mut rv, mut cov] =
140 std::array::from_fn::<_, 7, _>(|_| Sum::default());
141 let (mut l_varies, mut r_varies) = (false, false);
142 for (&l, &r) in left.iter().zip(right) {
143 l_varies |= l != left[0];
144 r_varies |= r != right[0];
145 let (l, r) = (l as f64, r as f64);
146 let (m, s) = ((l + r) * 0.5, (l - r) * 0.5);
147 let (ld, rd) = (l - l_mean, r - r_mean);
148 lp.add(l * l);
149 rp.add(r * r);
150 mp.add(m * m);
151 sp.add(s * s);
152 lv.add(ld * ld);
153 rv.add(rd * rd);
154 cov.add(ld * rd);
155 }
156 let n = left.len() as f64;
157 out.left_mean_square = Some(lp.value() / n);
158 out.right_mean_square = Some(rp.value() / n);
159 out.mid_mean_square = Some(mp.value() / n);
160 out.side_mean_square = Some(sp.value() / n);
161 if left.len() >= 2 && l_varies && r_varies {
162 let denominator = lv.value().sqrt() * rv.value().sqrt();
163 if denominator > 0.0 {
164 out.correlation = Some((cov.value() / denominator).clamp(-1.0, 1.0));
165 }
166 }
167 Ok(out)
168}
169
170impl Audio {
171 pub fn dc_means(&self) -> Result<Vec<Option<f64>>> {
175 Limits::default().check(self.frames(), self.channels(), self.rate)?;
176 let mut values = reserve(self.channels())?;
177 for channel in &self.data {
178 values.push(dc_mean(channel)?);
179 }
180 Ok(values)
181 }
182 pub fn threshold_silence(&self, threshold: f64) -> Result<ThresholdSilence> {
189 if !threshold.is_finite() || threshold < 0.0 {
190 return Err(Error::invalid(
191 "Silence threshold must be finite and nonnegative",
192 ));
193 }
194 Limits::default().check(self.frames(), self.channels(), self.rate)?;
195 let (mut first, mut end) = (self.frames(), 0);
196 for channel in &self.data {
197 for (i, &sample) in channel.iter().enumerate() {
198 if finite(sample)?.abs() > threshold {
199 first = first.min(i);
200 end = end.max(i + 1);
201 }
202 }
203 }
204 let active_range = (end > 0).then_some(first..end);
205 Ok(ThresholdSilence {
206 frames: self.frames(),
207 leading_frames: first,
208 trailing_frames: if end > 0 {
209 self.frames() - end
210 } else {
211 self.frames()
212 },
213 active_range,
214 })
215 }
216 pub fn stereo_measurements(&self) -> Result<StereoMeasurements> {
219 if self.channels() != 2 {
220 return Err(Error::invalid("Stereo measurements require two channels"));
221 }
222 Limits::default().check(self.frames(), self.channels(), self.rate)?;
223 stereo_measurements(&self.data[0], &self.data[1])
224 }
225 pub fn measure_boundaries(
236 &self,
237 boundaries: &[MeasurementBoundary],
238 ) -> Result<Vec<BoundaryMeasurement>> {
239 if boundaries.len() > MAX_MEASUREMENT_BOUNDARIES {
240 return Err(Error::limit("Too many measurement boundaries"));
241 }
242 Limits::default().check(self.frames(), self.channels(), self.rate)?;
243 for boundary in boundaries {
244 if let MeasurementBoundary::Frame(frame) = *boundary {
245 if frame == 0 || frame >= self.frames() {
246 return Err(Error::invalid("Boundary must lie between existing frames"));
247 }
248 }
249 }
250 for &sample in self.data.iter().flatten() {
251 finite(sample)?;
252 }
253 let mut out = reserve(boundaries.len())?;
254 for &boundary in boundaries {
255 let frames = match boundary {
256 MeasurementBoundary::Frame(frame) => Some((frame - 1, frame)),
257 MeasurementBoundary::LoopSeam if self.frames() >= 2 => Some((self.frames() - 1, 0)),
258 MeasurementBoundary::LoopSeam => None,
259 };
260 let samples = if let Some((before_frame, after_frame)) = frames {
261 let mut channels = reserve(self.channels())?;
262 let mut max_abs_step = 0.0f64;
263 for channel in &self.data {
264 let before = channel[before_frame] as f64;
265 let after = channel[after_frame] as f64;
266 let difference = after - before;
267 max_abs_step = max_abs_step.max(difference.abs());
268 channels.push(ChannelStep {
269 before,
270 after,
271 difference,
272 });
273 }
274 Some(BoundarySamples {
275 before_frame,
276 after_frame,
277 channels,
278 max_abs_step,
279 })
280 } else {
281 None
282 };
283 out.push(BoundaryMeasurement { boundary, samples });
284 }
285 Ok(out)
286 }
287}
288
289#[cfg(test)]
290mod tests {
291 use super::*;
292 #[test]
293 fn defensive_audio_scans_reject_nonfinite_samples_even_without_boundaries() {
294 for value in [f32::NAN, f32::INFINITY, f32::NEG_INFINITY] {
295 let a = Audio::new(vec![vec![0.0; 3]; 2], 48000)
296 .unwrap()
297 .with_data(vec![vec![0.0, 0.0, 0.0], vec![0.0, value, 0.0]]);
298 assert_eq!(
299 a.dc_means().unwrap_err().code,
300 crate::ErrorCode::InvalidArgument
301 );
302 assert_eq!(
303 a.threshold_silence(0.0).unwrap_err().code,
304 crate::ErrorCode::InvalidArgument
305 );
306 assert_eq!(
307 a.stereo_measurements().unwrap_err().code,
308 crate::ErrorCode::InvalidArgument
309 );
310 assert_eq!(
311 a.measure_boundaries(&[]).unwrap_err().code,
312 crate::ErrorCode::InvalidArgument
313 );
314 assert_eq!(
315 a.measure_boundaries(&[MeasurementBoundary::LoopSeam])
316 .unwrap_err()
317 .code,
318 crate::ErrorCode::InvalidArgument
319 );
320 }
321 }
322}