Skip to main content

midas_audio/
dsp.rs

1// Intentional truncation for audio DSP
2#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
3#[must_use]
4pub fn resample_and_mix(
5    input: &[i16],
6    in_channels: usize,
7    in_rate: u32,
8    out_channels: usize,
9    out_rate: u32,
10) -> Vec<i16> {
11    if input.is_empty() {
12        return Vec::new();
13    }
14
15    let num_input_frames = input.len() / in_channels;
16    let ratio = out_rate as f32 / in_rate as f32;
17    // Calculate expected output frames.
18    let num_output_frames = (num_input_frames as f32 * ratio).ceil() as usize;
19
20    let mut output = Vec::with_capacity(num_output_frames * out_channels);
21
22    for i in 0..num_output_frames {
23        let src_idx_float = i as f32 / ratio;
24        let src_idx_floor = src_idx_float.floor() as usize;
25        let t = src_idx_float - src_idx_floor as f32;
26
27        // Get interpolated source frame
28        let mut src_frame = vec![0.0; in_channels];
29
30        if src_idx_floor < num_input_frames {
31            let idx0 = src_idx_floor * in_channels;
32            let idx1 = if src_idx_floor + 1 < num_input_frames {
33                (src_idx_floor + 1) * in_channels
34            } else {
35                idx0 // Clamp
36            };
37
38            for c in 0..in_channels {
39                let s0 = f32::from(input[idx0 + c]);
40                let s1 = f32::from(input[idx1 + c]);
41                src_frame[c] = s0 + (s1 - s0) * t;
42            }
43        }
44
45        // Map to output channels
46        if in_channels == 1 && out_channels == 2 {
47            // Mono -> Stereo (Duplicate)
48            let val = src_frame[0] as i16;
49            output.push(val);
50            output.push(val);
51        } else if in_channels == 2 && out_channels == 1 {
52            // Stereo -> Mono (Average)
53            let val = ((src_frame[0] + src_frame[1]) * 0.5) as i16;
54            output.push(val);
55        } else if in_channels == out_channels {
56            // Pass through
57            output.extend(src_frame.iter().map(|&s| s as i16));
58        } else {
59            // Fallback: fill available, pad rest
60            for &s in src_frame.iter().take(out_channels) {
61                output.push(s as i16);
62            }
63            if out_channels > in_channels {
64                output.extend(std::iter::repeat_n(0, out_channels - in_channels));
65            }
66        }
67    }
68
69    output
70}