sound_to_patch/audio/
resample.rs1use std::error::Error;
4use std::fmt;
5
6use rubato::audioadapter_buffers::direct::SequentialSlice;
7use rubato::{Fft, FixedSync, Resampler};
8
9use super::SampleRate;
10
11const CHUNK: usize = 1024;
13
14pub fn resample(
38 samples: &[f64],
39 from: SampleRate,
40 to: SampleRate,
41) -> Result<Vec<f64>, ResampleError> {
42 if from == to || samples.is_empty() {
43 return Ok(samples.to_vec());
44 }
45 let fail = |message: String| ResampleError { from, to, message };
46 if let Some((index, value)) = samples.iter().enumerate().find(|(_, x)| !x.is_finite()) {
47 return Err(fail(format!("sample {index} is not finite ({value})")));
48 }
49 let rate = |rate: SampleRate| usize::try_from(rate.get()).map_err(|e| fail(e.to_string()));
50 let mut resampler = Fft::<f64>::new(rate(from)?, rate(to)?, CHUNK, 1, FixedSync::Input)
51 .map_err(|e| fail(e.to_string()))?;
52 let input = SequentialSlice::new(samples, 1, samples.len()).map_err(|e| fail(e.to_string()))?;
53 let output = resampler
54 .process_all(&input, samples.len(), None)
55 .map_err(|e| fail(e.to_string()))?;
56 let mut output = output.take_data();
57 let len = (u64::try_from(samples.len()).map_err(|e| fail(e.to_string()))?
59 * u64::from(to.get()))
60 .div_ceil(u64::from(from.get()));
61 output.truncate(usize::try_from(len).map_err(|e| fail(e.to_string()))?);
62 Ok(output)
63}
64
65#[derive(Clone, Debug, PartialEq, Eq)]
67pub struct ResampleError {
68 from: SampleRate,
69 to: SampleRate,
70 message: String,
71}
72
73impl fmt::Display for ResampleError {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 write!(
76 f,
77 "cannot resample from {} to {}: {}",
78 self.from, self.to, self.message
79 )
80 }
81}
82
83impl Error for ResampleError {}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 fn rate(hz: u32) -> SampleRate {
90 SampleRate::new(hz).unwrap()
91 }
92
93 #[test]
94 fn output_length_follows_the_ratio() {
95 let input = vec![0.0; 48_000];
96 let output = resample(&input, rate(48_000), rate(22_050)).unwrap();
97 assert_eq!(output.len(), 22_050);
98 let output = resample(&input[..1000], rate(48_000), rate(22_050)).unwrap();
99 assert_eq!(output.len(), 460);
101 }
102
103 #[test]
104 fn upsampling_keeps_the_ceiling_length() {
105 let input = vec![0.0; 44_100];
106 let output = resample(&input, rate(22_050), rate(48_000)).unwrap();
107 assert_eq!(output.len(), 96_000);
108 }
109
110 fn lag(from: u32, to: u32) -> f64 {
113 let tone = |n: usize, fs: u32| {
114 std::f64::consts::TAU * 100.0 * f64::from(u32::try_from(n).unwrap()) / f64::from(fs)
115 };
116 let x: Vec<f64> = (0..2 * usize::try_from(from).unwrap())
117 .map(|n| tone(n, from).sin())
118 .collect();
119 let y = resample(&x, rate(from), rate(to)).unwrap();
120 let (mut s, mut c) = (0.0, 0.0);
121 for (n, value) in y.iter().enumerate().take(y.len() * 3 / 4).skip(y.len() / 4) {
122 s += value * tone(n, to).sin();
123 c += value * tone(n, to).cos();
124 }
125 -c.atan2(s) / std::f64::consts::TAU / 100.0 * f64::from(to)
126 }
127
128 #[test]
129 fn some_rate_pairs_lag_by_half_an_output_sample() {
130 for from in [16_000, 32_000, 48_000, 96_000, 192_000] {
131 assert!((lag(from, 22_050) - 0.5).abs() < 1e-3, "{from}");
132 }
133 for (from, to) in [(44_100, 22_050), (48_000, 44_100), (22_050, 48_000)] {
134 assert!(lag(from, to).abs() < 1e-3, "{from} {to}");
135 }
136 }
137
138 #[test]
139 fn a_non_finite_sample_is_an_error_at_any_rate_pair() {
140 for bad in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
141 let mut input = vec![0.1; 48_000];
142 input[5] = bad;
143 for (from, to) in [(48_000, 22_050), (8_000, 22_050), (22_050, 48_000)] {
144 let error = resample(&input, rate(from), rate(to)).unwrap_err();
145 assert_eq!(
146 error.to_string(),
147 format!(
148 "cannot resample from {from} Hz to {to} Hz: sample 5 is not finite ({bad})"
149 )
150 );
151 }
152 }
153 }
154
155 #[test]
156 fn equal_rates_and_empty_input_return_a_copy() {
157 let input = vec![0.25, -0.5];
158 assert_eq!(resample(&input, rate(48_000), rate(48_000)).unwrap(), input);
159 assert_eq!(
160 resample(&[], rate(48_000), rate(22_050)).unwrap(),
161 Vec::<f64>::new()
162 );
163 }
164}