1const FRACTION_BITS: u32 = 16;
7const MOST_STEP_FRAMES: u64 = 64;
10
11pub trait DeviceSample: Copy {
13 fn from_mix(sample: i16) -> Self;
15}
16
17impl DeviceSample for f32 {
18 fn from_mix(sample: i16) -> Self {
19 Self::from(sample) / 32_768.0
20 }
21}
22
23impl DeviceSample for f64 {
24 fn from_mix(sample: i16) -> Self {
25 Self::from(sample) / 32_768.0
26 }
27}
28
29impl DeviceSample for i16 {
30 fn from_mix(sample: i16) -> Self {
31 sample
32 }
33}
34
35impl DeviceSample for i32 {
36 fn from_mix(sample: i16) -> Self {
37 Self::from(sample) << 16
38 }
39}
40
41impl DeviceSample for u16 {
42 fn from_mix(sample: i16) -> Self {
43 sample.cast_unsigned() ^ 0x8000
44 }
45}
46
47impl DeviceSample for u8 {
48 fn from_mix(sample: i16) -> Self {
49 (sample.cast_unsigned() >> 8).to_le_bytes()[0] ^ 0x80
50 }
51}
52
53#[derive(Debug)]
57pub struct Boundary {
58 from: usize,
60 to: usize,
61 step: u64,
63 place: u64,
65 previous: [i16; 2],
67 held: Box<[i16]>,
69 at: usize,
70 filled: usize,
71}
72
73impl Boundary {
74 pub fn new(from: usize, from_rate: u32, frames: usize, to: usize, to_rate: u32) -> Self {
77 Self {
78 from: from.clamp(1, 2),
79 to: to.max(1),
80 step: (u64::from(from_rate) << FRACTION_BITS) / u64::from(to_rate.max(1)),
81 place: 0,
82 previous: [0; 2],
83 held: vec![0; frames.max(1) * from.clamp(1, 2)].into_boxed_slice(),
84 at: 0,
85 filled: 0,
86 }
87 }
88
89 fn next(&mut self, mix: &mut dyn FnMut(&mut [i16])) -> [i16; 2] {
92 if self.at >= self.filled {
93 mix(&mut self.held);
94 self.at = 0;
95 self.filled = self.held.len();
96 }
97 let frame = [self.held[self.at], self.held[self.at + self.from - 1]];
98 self.at += self.from;
99 frame
100 }
101
102 fn frame(&mut self, mix: &mut dyn FnMut(&mut [i16])) -> [i16; 2] {
104 let one = 1u64 << FRACTION_BITS;
105 if self.step == one {
106 return self.next(mix);
107 }
108 let mut ahead = self.next_held(mix);
109 for _ in 0..(self.place >> FRACTION_BITS).min(MOST_STEP_FRAMES) {
112 self.previous = ahead;
113 self.at += self.from;
114 ahead = self.next_held(mix);
115 }
116 self.place &= one - 1;
117 let between = |a: i16, b: i16| {
118 let (a, b) = (i64::from(a), i64::from(b));
119 let mixed = a + (b - a) * i64::try_from(self.place).unwrap_or(0) / (1 << FRACTION_BITS);
120 i16::try_from(mixed).unwrap_or(0)
121 };
122 let frame = [
123 between(self.previous[0], ahead[0]),
124 between(self.previous[1], ahead[1]),
125 ];
126 self.place += self.step;
127 frame
128 }
129
130 fn next_held(&mut self, mix: &mut dyn FnMut(&mut [i16])) -> [i16; 2] {
132 if self.at >= self.filled {
133 mix(&mut self.held);
134 self.at = 0;
135 self.filled = self.held.len();
136 }
137 [self.held[self.at], self.held[self.at + self.from - 1]]
138 }
139
140 pub fn fill<T: DeviceSample>(&mut self, out: &mut [T], mix: &mut dyn FnMut(&mut [i16])) {
143 let silence = T::from_mix(0);
144 for device in out.chunks_exact_mut(self.to) {
145 let [left, right] = self.frame(mix);
146 match device {
147 [only] => {
148 *only = T::from_mix(i16::midpoint(left, right));
149 }
150 [first, second, rest @ ..] => {
151 *first = T::from_mix(left);
152 *second = T::from_mix(right);
153 rest.fill(silence);
154 }
155 [] => {}
156 }
157 }
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164
165 const CHUNK_FRAMES: usize = 1024;
166
167 fn counting() -> impl FnMut(&mut [i16]) {
169 let mut next = 1i16;
170 move |out: &mut [i16]| {
171 for frame in out.as_chunks_mut::<2>().0 {
172 frame[0] = next;
173 frame[1] = -next;
174 next = next.wrapping_add(1);
175 }
176 }
177 }
178
179 #[test]
180 fn a_float_device_gets_the_same_fraction_of_full_scale() {
181 assert!((f32::from_mix(i16::MIN) + 1.0).abs() < f32::EPSILON);
182 assert!(f32::from_mix(0).abs() < f32::EPSILON);
183 assert!((f32::from_mix(16_384) - 0.5).abs() < f32::EPSILON);
184 assert!((f32::from_mix(i16::MAX) - 32_767.0 / 32_768.0).abs() < f32::EPSILON);
185 }
186
187 #[test]
188 fn every_sixteen_bit_sample_survives_a_float_device_and_back() {
189 for sample in i16::MIN..=i16::MAX {
190 let back = f32::from_mix(sample) * 32_768.0;
191 assert!((back - f32::from(sample)).abs() < f32::EPSILON, "{sample}");
192 }
193 }
194
195 #[test]
196 fn integer_devices_keep_the_order_and_the_extremes() {
197 assert_eq!(i16::from_mix(-5), -5);
198 assert_eq!(i32::from_mix(i16::MIN), i32::MIN);
199 assert_eq!(i32::from_mix(1), 1 << 16);
200 assert_eq!(u16::from_mix(i16::MIN), 0);
201 assert_eq!(u16::from_mix(0), 0x8000);
202 assert_eq!(u16::from_mix(i16::MAX), u16::MAX);
203 assert_eq!(u8::from_mix(i16::MIN), 0);
204 assert_eq!(u8::from_mix(0), 0x80);
205 assert_eq!(u8::from_mix(i16::MAX), u8::MAX);
206 assert!((f64::from_mix(16_384) - 0.5).abs() < f64::EPSILON);
207 }
208
209 #[test]
210 fn at_the_same_rate_frames_go_through_untouched() {
211 let mut boundary = Boundary::new(2, 48_000, CHUNK_FRAMES, 2, 48_000);
212 let mut mix = counting();
213 let mut seen = Vec::new();
215 for size in [7usize, 1024, 1, 2000] {
216 let mut out = vec![0i16; size * 2];
217 boundary.fill(&mut out, &mut mix);
218 seen.extend(out);
219 }
220 for (index, frame) in seen.as_chunks::<2>().0.iter().enumerate() {
221 let want = i16::try_from(index + 1).unwrap_or(0);
222 assert_eq!(*frame, [want, -want]);
223 }
224 }
225
226 #[test]
227 fn a_mono_device_has_the_mean_and_a_surround_one_silence_past_two() {
228 let mut mix = |out: &mut [i16]| {
229 for frame in out.as_chunks_mut::<2>().0 {
230 *frame = [1000, 3000];
231 }
232 };
233 let mut mono = [0i16; 2];
234 Boundary::new(2, 8000, CHUNK_FRAMES, 1, 8000).fill(&mut mono, &mut mix);
235 assert_eq!(mono, [2000, 2000]);
236 let mut surround = [9i16; 8];
237 Boundary::new(2, 8000, CHUNK_FRAMES, 4, 8000).fill(&mut surround, &mut mix);
238 assert_eq!(surround, [1000, 3000, 0, 0, 1000, 3000, 0, 0]);
239 }
240
241 #[test]
242 fn a_mono_mix_sounds_on_both_channels() {
243 let mut mix = |out: &mut [i16]| out.fill(500);
244 let mut stereo = [0i16; 4];
245 Boundary::new(1, 8000, CHUNK_FRAMES, 2, 8000).fill(&mut stereo, &mut mix);
246 assert_eq!(stereo, [500; 4]);
247 }
248
249 #[test]
250 fn a_partial_frame_at_the_end_of_a_buffer_is_left_alone() {
251 let mut mix = counting();
252 let mut out = [9i16; 3];
253 Boundary::new(2, 8000, CHUNK_FRAMES, 2, 8000).fill(&mut out, &mut mix);
254 assert_eq!(out, [1, -1, 9]);
255 }
256
257 #[test]
258 fn another_rate_comes_out_in_a_straight_line_at_the_device_rate() {
259 let mut boundary = Boundary::new(2, 8000, CHUNK_FRAMES, 2, 16_000);
261 let mut mix = |out: &mut [i16]| {
262 for (index, frame) in out.as_chunks_mut::<2>().0.iter_mut().enumerate() {
263 let value = i16::try_from(index * 100).unwrap_or(0);
264 *frame = [value, value];
265 }
266 };
267 let mut out = [0i16; 16];
268 boundary.fill(&mut out, &mut mix);
269 let left: Vec<i16> = out.iter().step_by(2).copied().collect();
270 assert_eq!(left, [0, 0, 0, 50, 100, 150, 200, 250]);
271 }
272
273 #[test]
274 fn a_higher_mix_rate_uses_the_frames_up_at_its_rate() {
275 let mut asked = 0usize;
278 let mut mix = |out: &mut [i16]| {
279 asked += out.len() / 2;
280 out.fill(7);
281 };
282 let mut boundary = Boundary::new(2, 48_000, CHUNK_FRAMES, 2, 44_100);
283 let mut out = vec![0i16; 44_100 * 2];
284 boundary.fill(&mut out, &mut mix);
285 assert!(
286 (48_000..48_000 + CHUNK_FRAMES * 2).contains(&asked),
287 "{asked}"
288 );
289 assert_eq!(out[100], 7);
290 }
291}