1#[derive(Clone, Debug)]
2pub struct Clip {
3 pub rate: u32,
4 pub channels: u16,
5 pub samples: Vec<f32>,
6}
7
8impl Clip {
9 pub fn from_wav(bytes: &[u8]) -> Option<Clip> {
10 crate::wav::parse(bytes)
11 }
12
13 pub fn to_wav(&self) -> Vec<u8> {
14 crate::wav::write_f32(self)
15 }
16
17 pub fn to_wav_i16(&self) -> Vec<u8> {
18 crate::wav::write_i16(self)
19 }
20
21 pub fn reverse(&mut self) {
22 let channels = self.channels as usize;
23 if channels == 0 {
24 return;
25 }
26 let frames: Vec<&[f32]> = self.samples.chunks_exact(channels).collect();
27 let mut reversed = Vec::with_capacity(frames.len().saturating_mul(channels));
28 for frame in frames.into_iter().rev() {
29 reversed.extend_from_slice(frame);
30 }
31 self.samples = reversed;
32 }
33
34 pub fn seconds(&self) -> f32 {
35 if self.rate == 0 || self.channels == 0 {
36 return 0.0;
37 }
38 self.samples.len() as f32 / (self.rate as f32 * self.channels as f32)
39 }
40
41 pub fn frames(&self) -> u64 {
42 if self.channels == 0 {
43 return 0;
44 }
45 (self.samples.len() / self.channels as usize) as u64
46 }
47
48 pub fn for_rate(&self, rate: u32) -> Clip {
49 if self.rate == rate {
50 return self.clone();
51 }
52 Clip {
53 rate,
54 channels: self.channels,
55 samples: self.at_rate(rate, self.channels),
56 }
57 }
58
59 pub fn at_rate(&self, rate: u32, channels: u16) -> Vec<f32> {
60 if self.rate == rate && self.channels == channels {
61 return self.samples.clone();
62 }
63 if self.rate == 0 || self.channels == 0 || rate == 0 || channels == 0 {
64 return Vec::new();
65 }
66 if self.rate == rate {
67 return remap(&self.samples, self.channels, channels);
68 }
69 resample(self, rate, channels)
70 }
71}
72
73fn remap(samples: &[f32], src_channels: u16, dst_channels: u16) -> Vec<f32> {
74 let src_channels = src_channels as usize;
75 let dst_channels = dst_channels as usize;
76 if src_channels == 0 || dst_channels == 0 {
77 return Vec::new();
78 }
79 let frames = samples.len() / src_channels;
80 let mut out = Vec::with_capacity(frames.saturating_mul(dst_channels));
81 for frame in 0..frames {
82 for channel in 0..dst_channels {
83 let src = channel.min(src_channels - 1);
84 out.push(samples[frame * src_channels + src]);
85 }
86 }
87 out
88}
89
90fn resample(clip: &Clip, rate: u32, channels: u16) -> Vec<f32> {
91 let src_channels = clip.channels as usize;
92 let frames_in = clip.samples.len() / src_channels;
93 let frames_out = scaled_frames(frames_in, clip.rate, rate);
94 if frames_out == 0 || clip.samples.is_empty() {
95 return Vec::new();
96 }
97 let dst_channels = channels as usize;
98 let last = clip.samples.len() - 1;
99 let mut out = Vec::with_capacity(frames_out.saturating_mul(dst_channels));
100 for i in 0..frames_out {
101 let pos = i as f64 * f64::from(clip.rate) / f64::from(rate);
102 let base = pos.floor().max(0.0) as usize;
103 let frac = (pos - base as f64) as f32;
104 for channel in 0..dst_channels {
105 let src = channel.min(src_channels - 1);
106 let a = clip.samples[(base.saturating_mul(src_channels).saturating_add(src)).min(last)];
107 let b = clip.samples[(base
108 .saturating_add(1)
109 .saturating_mul(src_channels)
110 .saturating_add(src))
111 .min(last)];
112 out.push(a + (b - a) * frac);
113 }
114 }
115 out
116}
117
118fn scaled_frames(frames_in: usize, from: u32, to: u32) -> usize {
119 if from == 0 {
120 return 0;
121 }
122 let frames = (frames_in as u128).saturating_mul(u128::from(to)) / u128::from(from);
123 usize::try_from(frames).unwrap_or(usize::MAX)
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 fn bits(samples: &[f32]) -> Vec<u32> {
131 samples.iter().map(|sample| sample.to_bits()).collect()
132 }
133
134 fn pcm16(rate: u32, channels: u16, frames: &[i16]) -> Vec<u8> {
135 let data: Vec<u8> = frames
136 .iter()
137 .flat_map(|sample| sample.to_le_bytes())
138 .collect();
139 let mut out = Vec::new();
140 out.extend_from_slice(b"RIFF");
141 out.extend_from_slice(&(36 + data.len() as u32).to_le_bytes());
142 out.extend_from_slice(b"WAVE");
143 out.extend_from_slice(b"fmt ");
144 out.extend_from_slice(&16u32.to_le_bytes());
145 out.extend_from_slice(&1u16.to_le_bytes());
146 out.extend_from_slice(&channels.to_le_bytes());
147 out.extend_from_slice(&rate.to_le_bytes());
148 out.extend_from_slice(&(rate * channels as u32 * 2).to_le_bytes());
149 out.extend_from_slice(&(channels * 2).to_le_bytes());
150 out.extend_from_slice(&16u16.to_le_bytes());
151 out.extend_from_slice(b"fact");
152 out.extend_from_slice(&4u32.to_le_bytes());
153 out.extend_from_slice(&(frames.len() as u32 / u32::from(channels.max(1))).to_le_bytes());
154 out.extend_from_slice(b"data");
155 out.extend_from_slice(&(data.len() as u32).to_le_bytes());
156 out.extend_from_slice(&data);
157 out
158 }
159
160 #[test]
161 fn a_pcm16_wav_parses_to_unit_samples() {
162 let clip = Clip::from_wav(&pcm16(48_000, 1, &[0, 16384, -32768])).unwrap();
163 assert_eq!(clip.rate, 48_000);
164 assert_eq!(clip.channels, 1);
165 assert_eq!(bits(&clip.samples), bits(&[0.0, 0.5, -1.0]));
166 }
167
168 #[test]
169 fn garbage_is_not_a_clip() {
170 assert!(Clip::from_wav(b"not a wav").is_none());
171 assert!(Clip::from_wav(&[]).is_none());
172 assert!(Clip::from_wav(&pcm16(0, 1, &[0])).is_none());
173 }
174
175 #[test]
176 fn eight_bit_samples_are_centred_on_128() {
177 let data = [0u8, 128, 255];
178 let mut bytes = Vec::new();
179 bytes.extend_from_slice(b"RIFF");
180 bytes.extend_from_slice(&(36 + data.len() as u32).to_le_bytes());
181 bytes.extend_from_slice(b"WAVE");
182 bytes.extend_from_slice(b"fmt ");
183 bytes.extend_from_slice(&16u32.to_le_bytes());
184 bytes.extend_from_slice(&1u16.to_le_bytes());
185 bytes.extend_from_slice(&1u16.to_le_bytes());
186 bytes.extend_from_slice(&8_000u32.to_le_bytes());
187 bytes.extend_from_slice(&8_000u32.to_le_bytes());
188 bytes.extend_from_slice(&1u16.to_le_bytes());
189 bytes.extend_from_slice(&8u16.to_le_bytes());
190 bytes.extend_from_slice(b"data");
191 bytes.extend_from_slice(&(data.len() as u32).to_le_bytes());
192 bytes.extend_from_slice(&data);
193 let clip = Clip::from_wav(&bytes).unwrap();
194 assert_eq!(bits(&clip.samples), bits(&[-1.0, 0.0, 127.0 / 128.0]));
195 }
196
197 #[test]
198 fn wav_f32_and_pcm16_round_trip() {
199 let clip = Clip {
200 rate: 48_000,
201 channels: 2,
202 samples: vec![0.0, 1.0, -1.0, 0.1, -0.3],
203 };
204 let back = Clip::from_wav(&clip.to_wav()).unwrap();
205 assert_eq!(back.rate, 48_000);
206 assert_eq!(back.channels, 2);
207 assert_eq!(bits(&back.samples), bits(&clip.samples));
208 assert_eq!(&clip.to_wav()[20..22], &3u16.to_le_bytes());
209 assert_eq!(&clip.to_wav()[34..36], &32u16.to_le_bytes());
210
211 let pcm = Clip {
212 rate: 44_100,
213 channels: 1,
214 samples: vec![0.0, 0.5, -1.0],
215 };
216 let back = Clip::from_wav(&pcm.to_wav_i16()).unwrap();
217 assert_eq!(bits(&back.samples), bits(&[0.0, 0.5, -1.0]));
218 assert_eq!(&pcm.to_wav_i16()[20..22], &1u16.to_le_bytes());
219 assert_eq!(&pcm.to_wav_i16()[34..36], &16u16.to_le_bytes());
220 }
221
222 #[test]
223 fn reversing_keeps_stereo_frames_paired() {
224 let mut clip = Clip::from_wav(&pcm16(48_000, 2, &[1, 2, 3, 4, 5, 6])).unwrap();
225 clip.reverse();
226 let back: Vec<i32> = clip
227 .samples
228 .iter()
229 .map(|sample| (sample * 32768.0).round() as i32)
230 .collect();
231 assert_eq!(back, vec![5, 6, 3, 4, 1, 2]);
232 }
233
234 #[test]
235 fn matching_rates_copy_samples_and_a_rate_change_interpolates() {
236 let clip = Clip {
237 rate: 24_000,
238 channels: 1,
239 samples: vec![0.0, 1.0, 0.0, -1.0],
240 };
241 assert_eq!(bits(&clip.at_rate(24_000, 1)), bits(&clip.samples));
242 let spread = clip.at_rate(24_000, 2);
243 assert_eq!(
244 bits(&spread),
245 bits(&[0.0, 0.0, 1.0, 1.0, 0.0, 0.0, -1.0, -1.0])
246 );
247 let up = clip.at_rate(48_000, 1);
248 assert_eq!(
249 bits(&up),
250 bits(&[0.0, 0.5, 1.0, 0.5, 0.0, -0.5, -1.0, -1.0])
251 );
252 let tone = Clip::from_wav(&pcm16(24_000, 1, &[16384; 240])).unwrap();
253 let stereo = tone.at_rate(48_000, 2);
254 assert_eq!(stereo.len(), 960);
255 assert_eq!(bits(&stereo[..2]), bits(&[0.5, 0.5]));
256 assert!((tone.seconds() - 0.01).abs() < 0.0001);
257 }
258}