1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#[cfg(feature = "simd")]
use std::simd::{f32x16, i16x16};

/// Convert an array of 16 bit mono audio samples to a vector of 32 bit floats.
///
/// This variant does not use SIMD instructions.
///
/// # Arguments
/// * `samples` - The array of 16 bit mono audio samples.
///
/// # Returns
/// A vector of 32 bit floats.
pub fn convert_integer_to_float_audio(samples: &[i16]) -> Vec<f32> {
    let mut floats = Vec::with_capacity(samples.len());
    for sample in samples {
        floats.push(*sample as f32 / 32768.0);
    }
    floats
}

/// Convert an array of 16 bit mono audio samples to a vector of 32 bit floats.
///
/// This variant uses SIMD instructions, and as such is only available on
/// nightly Rust.
///
/// # Arguments
/// * `samples` - The array of 16 bit mono audio samples.
///
/// # Returns
/// A vector of 32 bit floats.
#[cfg(feature = "simd")]
pub fn convert_integer_to_float_audio_simd(samples: &[i16]) -> Vec<f32> {
    let mut floats = Vec::with_capacity(samples.len());

    let div_arr = f32x16::splat(32768.0);

    let chunks = samples.chunks_exact(16);
    let remainder = chunks.remainder();
    for chunk in chunks {
        let simd = i16x16::from_slice(chunk).cast::<f32>();
        let simd = simd / div_arr;
        floats.extend(&simd.to_array()[..]);
    }

    // Handle the remainder.
    // do this normally because it's only a few samples and the overhead of
    // converting to SIMD is not worth it.
    for sample in remainder {
        floats.push(*sample as f32 / 32768.0);
    }

    floats
}

/// Convert 32 bit floating point stereo PCM audio to 32 bit floating point mono PCM audio.
///
/// This variant does not use SIMD instructions.
///
/// # Arguments
/// * `samples` - The array of 32 bit floating point stereo PCM audio samples.
///
/// # Returns
/// A vector of 32 bit floating point mono PCM audio samples.
pub fn convert_stereo_to_mono_audio(samples: &[f32]) -> Result<Vec<f32>, &'static str> {
    if samples.len() & 1 != 0 {
        return Err("The stereo audio vector has an odd number of samples. \
            This means a half-sample is missing somewhere");
    }

    Ok(samples
        .chunks_exact(2)
        .map(|x| (x[0] + x[1]) / 2.0)
        .collect())
}

/// Convert 32 bit floating point stereo PCM audio to 32 bit floating point mono PCM audio.
///
/// This variant uses SIMD instructions, and as such is only available on
/// nightly Rust.
///
/// # Arguments
/// * `samples` - The array of 32 bit floating point stereo PCM audio samples.
///
/// # Returns
/// A vector of 32 bit floating point mono PCM audio samples.
#[cfg(feature = "simd")]
pub fn convert_stereo_to_mono_audio_simd(samples: &[f32]) -> Result<Vec<f32>, &'static str> {
    let mut mono = Vec::with_capacity(samples.len() / 2);

    let div_array = f32x16::splat(2.0);

    let chunks = samples.chunks_exact(32);
    let remainder = chunks.remainder();
    for chunk in chunks {
        let [c1, c2] = [0, 1].map(|offset| {
            let mut arr = [0.0; 16];
            std::iter::zip(&mut arr, chunk.iter().skip(offset).step_by(2).copied())
                .for_each(|(a, c)| *a = c);
            arr
        });

        let c1 = f32x16::from(c1);
        let c2 = f32x16::from(c2);
        let mono_simd = (c1 + c2) / div_array;
        mono.extend(&mono_simd.to_array()[..]);
    }

    // Handle the remainder.
    // do this normally because it's only a few samples and the overhead of
    // converting to SIMD is not worth it.
    mono.extend(convert_stereo_to_mono_audio(remainder)?);

    Ok(mono)
}

#[cfg(feature = "simd")]
#[cfg(test)]
mod test {
    use super::*;

    #[test]
    pub fn assert_stereo_to_mono_err() {
        // fake some sample data
        let samples = (0u16..1029).map(f32::from).collect::<Vec<f32>>();
        let mono = convert_stereo_to_mono_audio(&samples);
        assert!(mono.is_err());
    }
}

#[cfg(feature = "simd")]
#[cfg(test)]
mod test_simd {
    use super::*;

    #[test]
    pub fn assert_stereo_to_mono_simd() {
        // fake some sample data
        let samples = (0u16..1028).map(f32::from).collect::<Vec<f32>>();
        let mono_simd = convert_stereo_to_mono_audio_simd(&samples);
        let mono = convert_stereo_to_mono_audio(&samples);
        assert_eq!(mono_simd, mono);
    }

    #[test]
    pub fn assert_stereo_to_mono_simd_err() {
        // fake some sample data
        let samples = (0u16..1029).map(f32::from).collect::<Vec<f32>>();
        let mono_simd = convert_stereo_to_mono_audio_simd(&samples);
        let mono = convert_stereo_to_mono_audio(&samples);
        assert_eq!(mono_simd, mono);
    }
}