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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
pub type BufferSize = usize;

pub type Point = f32;

/// Error type for different failures
#[derive(Debug)]
pub enum StreamErr {
    /// Tried to access index in stream that doesn not exist
    NonExistentIndex(usize),
}

/// Struct representing a stream of audio data
#[derive(Debug, PartialEq, Clone)]
pub struct Stream {
    /// Vec containing all audio points, multiple channels are interleaved
    pub samples: Vec<Point>,
    /// amount of channels
    pub channels: usize,
}

/// Convert u8 to point value (f32 between -1.0 and 1.0)
pub fn u8_to_point(n: u8) -> Point {
    (n as f32 / u8::MAX as f32) * 2.0 - 1.0
}

/// Convert i16 to point value (f32 between -1.0 and 1.0)
pub fn i16_to_point(n: i16) -> Point {
    n as f32 / i16::MAX as f32
}

/// Convert i32 to point value (f32 between -1.0 and 1.0)
pub fn i32_to_point(n: i32) -> Point {
    n as f32 / i32::MAX as f32
}

impl Stream {
    /// Create zero initialized (silent) stream
    pub fn empty(size: BufferSize, channels: usize) -> Self {
        Stream {
            samples: vec![0.0; size],
            channels,
        }
    }

    /// Create new stream based on provided samples
    pub fn from_samples(samples: Vec<Point>, channels: usize) -> Self {
        Stream { samples, channels }
    }

    /// Returns the length of the stream
    ///
    /// *note* this does not take into account the amount of channels
    pub fn len(&self) -> usize {
        self.samples.len()
    }

    /// Get sample for provided position argument, errors when the index does not exist in the stream
    pub fn get_sample(&self, position: usize) -> Result<f32, StreamErr> {
        match self.samples.get(position) {
            Some(&f) => Ok(f),
            None => Err(StreamErr::NonExistentIndex(position)),
        }
    }

    /// Mix together multiple streams into the given stream
    ///
    /// *note* the size of the stream is unchanged,
    /// if the other streams are shorter it inserts silence (0.0)
    /// if the other streams are longer the remaining points are ignored
    ///
    /// *note* this is a naive mix, it does not take into account the channel size, 
    /// it assumes you are mixing together channels of the same size
    pub fn mix(&mut self, streams: &Vec<&Stream>) -> &mut Self {
        for (i, sample) in self.samples.iter_mut().enumerate() {
            *sample = streams.iter().fold(sample.clone(), |xs, x| {
                xs + x.samples.get(i).unwrap_or(&0.0)
            });
        }

        self
    }

    /// Amplify a stream by decibels
    pub fn amplify(&mut self, db: f32) -> &mut Self {
        let ratio = 10_f32.powf(db / 20.0);

        for sample in self.samples.iter_mut() {
            *sample = (sample.clone() * ratio).clamp(-1.0, 1.0);
        }

        self
    }
}

#[cfg(test)]
mod tests {
    #![allow(overflowing_literals)]
    use super::*;

    #[test]
    fn test_mix() {
        let samples = vec![-1.0, -0.5, 0.0, 0.5, 1.0];
        let streams = vec![];
        let mut stream = Stream::from_samples(samples, 1);
        stream.mix(&streams);
        assert_eq!(stream.samples, vec![-1.0, -0.5, 0.0, 0.5, 1.0]);

        let samples = vec![1.0, 0.2, 1.0, 1.0, 0.2];
        let streams = vec![Stream::from_samples(vec![0.0, 0.0, 0.0, 0.0, 0.0], 1)];
        let mut stream = Stream::from_samples(samples, 1);
        stream.mix(&streams.iter().collect());
        assert_eq!(stream.samples, vec![1.0, 0.2, 1.0, 1.0, 0.2]);

        let samples = vec![0.1, 0.0, -0.1, -0.2, -0.3];
        let streams = vec![
            Stream::from_samples(vec![0.2, 0.1, 0.0, -0.1, -0.2], 1),
            Stream::from_samples(vec![0.3, 0.2, 0.1, 0.0, -0.1], 1),
        ];
        let mut stream = Stream::from_samples(samples, 1);
        stream.mix(&streams.iter().collect());
        assert_eq!(stream.samples, vec![0.6, 0.3, 0.0, -0.3, -0.6]);

        let samples = vec![0.1, 0.0, -0.1, -0.2, -0.3];
        let streams = vec![
            Stream::from_samples(vec![0.2, 0.1, 0.0], 1),
            Stream::from_samples(vec![0.3], 1),
        ];
        let mut stream = Stream::from_samples(samples, 1);
        stream.mix(&streams.iter().collect());
        assert_eq!(stream.samples, vec![0.6, 0.1, -0.1, -0.2, -0.3]);
    }

    #[test]
    fn test_amplify() {
        let mut stream = Stream::empty(1, 1);
        stream.amplify(6.0);
        assert_eq!(stream.samples, vec![0.0]);

        // 6 dBs should roughly double / half
        let mut stream = Stream::from_samples(vec![0.1, 0.25, 0.3, -0.1, -0.4], 1);
        stream.amplify(6.0);
        let rounded_samples: Vec<Point> = stream
            .samples
            .iter()
            .map(|x| (x * 10.0).round() / 10.0)
            .collect::<Vec<Point>>();
        assert_eq!(rounded_samples, vec![0.2, 0.5, 0.6, -0.2, -0.8]);

        let mut stream = Stream::from_samples(vec![0.4, 0.5, 0.8, -0.3, -0.6], 1);
        stream.amplify(-6.0);
        let rounded_samples: Vec<Point> = stream
            .samples
            .iter()
            .map(|x| (x * 100.0).round() / 100.0)
            .collect::<Vec<Point>>();
        assert_eq!(rounded_samples, vec![0.2, 0.25, 0.4, -0.15, -0.3]);

        // clamp the value
        let mut stream = Stream::from_samples(vec![0.1, 0.4, 0.6, -0.2, -0.3, -0.5], 1);
        stream.amplify(12.0);
        let rounded_samples: Vec<Point> = stream
            .samples
            .iter()
            .map(|x| (x * 100.0).round() / 100.0)
            .collect::<Vec<Point>>();
        assert_eq!(rounded_samples, vec![0.4, 1.0, 1.0, -0.8, -1.0, -1.0]);
    }

    #[test]
    fn test_u8_to_point() {
        assert_eq!(u8_to_point(u8::MIN), -1.0);
        assert_eq!(u8_to_point(0x80u8), 0.003921628);
        assert_eq!(u8_to_point(u8::MAX), 1.0);
    }

    #[test]
    fn test_i16_to_point() {
        assert_eq!(i16_to_point(i16::MIN + 1), -1.0);
        assert_eq!(i16_to_point(0i16), 0.0);
        assert_eq!(i16_to_point(i16::MAX), 1.0);
    }

    #[test]
    fn test_i32_to_point() {
        assert_eq!(i32_to_point(i32::MIN + 1), -1.0);
        assert_eq!(i32_to_point(0i32), 0.0);
        assert_eq!(i32_to_point(i32::MAX), 1.0);
    }
}