audio_overlay/
lib.rs

1//! Overlay audio samples from one array onto another. You can optionally expand the destination array.
2//!
3//! The overlay function can be used for i8, i16, i32, i64, and f32.
4//!
5//! # Example
6//!
7//! ```rust
8//!use audio_overlay::overlay;
9//!use hound::WavReader;
10//!use rodio::buffer::SamplesBuffer;
11//!use rodio::{OutputStream, Sink};
12//!
13//!// Set the framerate.
14//!let framerate: u32 = 44100;
15//!// Load the audio clips.
16//!// Source: https://archive.org/download/NasaApollo11OnboardRecordings/11_highlight_2.ogg
17//!let src: Vec<i16> = WavReader::open("src.wav")
18//!.unwrap()
19//!.samples::<i16>()
20//!.map(|s| s.unwrap())
21//!.collect::<Vec<i16>>();
22//!// Source: https://archive.org/download/airship1904/airship1904.ogg
23//!let mut dst: Vec<i16> = WavReader::open("dst.wav")
24//!.unwrap()
25//!.samples::<i16>()
26//!.map(|s| s.unwrap())
27//!.collect::<Vec<i16>>();
28//!
29//!// // Overlay the audio clips. The src clip will start 1.0 seconds after dst begins.
30//!overlay(src.as_slice(), &mut dst, 1.0, framerate, true);
31//!
32//!// Play the audio clips. Source: https://docs.rs/rodio/latest/rodio/
33//!let (_stream, stream_handle) = OutputStream::try_default().unwrap();
34//!let source = SamplesBuffer::new(1, framerate, dst);
35//!let sink = Sink::try_new(&stream_handle).unwrap();
36//!sink.append(source);
37//!sink.sleep_until_end();
38//! ```
39
40use std::cmp::PartialOrd;
41
42const I8_MAX: i16 = i8::MAX as i16;
43const I8_MIN: i16 = i8::MIN as i16;
44const I16_MAX: i32 = i16::MAX as i32;
45const I16_MIN: i32 = i16::MIN as i32;
46const I32_MAX: i64 = i32::MAX as i64;
47const I32_MIN: i64 = i32::MIN as i64;
48const I64_MAX: i128 = i64::MAX as i128;
49const I64_MIN: i128 = i64::MIN as i128;
50
51/// Overlay audio samples from one array onto another. You can optionally expand the destination array.
52///
53/// This function can be used for i8, i16, i32, i64, f32, and f64.
54///
55/// This function assumes that both the source and destination arrays are a single channel of audio and have the same framerate and sample width.
56///
57/// For multi-channel audio, run `overlay()` for each channel.
58///
59/// Audio mixing algorithm source: <https://github.com/python/cpython/blob/main/Modules/audioop.c#L1083>
60///
61/// # Arguments
62///
63/// * `src` - A slice of type T. This array will be overlaid into `dst`.
64/// * `dst` - A mutable vec of type T. This will be modified, with `src` being overlaid into `dst`.
65/// * `time` - The start time in seconds at which `src` should be overlaid into `dst`.
66/// * `framerate` - The framerate of `src` and `dst`, e.g. 44100. This will be used to convert `time` into an index value.
67/// * `add` - Often, the end time of `src` will exceed the end time of `dst`. If `add == true`, samples from `src` past the original end time of `dst` will be pushed to `dst`, lengthening the waveform. If `add == false`, this function will end at the current length of `dst` and won't modify its length.
68///
69/// # Panics
70///
71/// It is technically possible for this function to panic if the source arrays are of type f32 or f64 because an overlaid value could exceed f32::MIN or f32::MAX, or f64::MIN or f64::MAX, respectively. But this would be a very unusual audio array in the first place. We're assuming that all values in `src` and `dst` are between -1.0 and 1.0.
72///
73/// For integer types such as i16, the function *won't* panic due to overflow errors because summed values will be cast to a type with a higher bit count, added, and recast as the original type (see [Overlayable]).
74pub fn overlay<T, U>(src: &[T], dst: &mut Vec<T>, time: f64, framerate: u32, add: bool)
75where
76    T: Copy + PartialOrd + Overlayable<T, U> + From<u8>,
77    U: Copy + PartialOrd + ValueBounds<U>,
78{
79    // Get the start index.
80    let mut index: usize = (time * framerate as f64) as usize;
81    // The current length of dst.
82    let len: usize = dst.len();
83    // This will be used to fill dst with zeros if needed.
84    let zero: T = T::from(0);
85    // The start time is after the end of dst.
86    if index >= len {
87        if add {
88            // Add zeros up to the start time.
89            dst.extend(vec![zero; index - len]);
90            // Add src.
91            dst.extend(src.iter().cloned());
92        }
93        return;
94    }
95    // Get the minimum and maximum values.
96    let min: U = U::min();
97    let max: U = U::max();
98    for (i, &v) in src.iter().enumerate() {
99        // If the index is greater than the length of dst, then we need to either stop here or append.
100        if index >= len {
101            if add {
102                dst.extend(src[i..].iter().cloned());
103            }
104            return;
105        }
106        // If there is no data at this index, set it to v rather than doing a lot of casting.
107        if dst[index] == zero {
108            dst[index] = v;
109        }
110        // Overlay the sample.
111        else {
112            dst[index] = T::overlay(dst[index], v, min, max);
113        }
114        // Increment the index.
115        index += 1;
116    }
117}
118
119// Clamp the value between a min and max.
120fn clamp<T>(value: T, min: T, max: T) -> T
121where
122    T: PartialOrd,
123{
124    if value > max {
125        max
126    } else if value < min {
127        min
128    } else {
129        value
130    }
131}
132
133/// Overlay values onto each other. The values are are added together and clamped to min/max values.
134pub trait Overlayable<T, U>
135where
136    T: Copy + PartialOrd,
137    U: Copy + PartialOrd,
138{
139    /// Add two values together, clamped between min/max values.
140    ///
141    /// For integer types, we need to cast the values to a higher bit count, e.g. i16 to i32, before adding them, to prevent an overflow error. Values are clamped to the min/max values of the original type, e.g. i16::MAX.
142    ///  
143    /// For float types, it's assumed that the values are between -1.0 and 1.0. They are added and the sum is clamped to be between -1.0 and 1.0.
144    fn overlay(a: T, b: T, min: U, max: U) -> T;
145}
146
147impl Overlayable<i8, i16> for i8 {
148    fn overlay(a: i8, b: i8, min: i16, max: i16) -> i8 {
149        clamp((a + b) as i16, min, max) as i8
150    }
151}
152
153impl Overlayable<i16, i32> for i16 {
154    fn overlay(a: i16, b: i16, min: i32, max: i32) -> i16 {
155        clamp((a + b) as i32, min, max) as i16
156    }
157}
158
159impl Overlayable<i32, i64> for i32 {
160    fn overlay(a: i32, b: i32, min: i64, max: i64) -> i32 {
161        clamp((a + b) as i64, min, max) as i32
162    }
163}
164
165impl Overlayable<i64, i128> for i64 {
166    fn overlay(a: i64, b: i64, min: i128, max: i128) -> i64 {
167        clamp((a + b) as i128, min, max) as i64
168    }
169}
170
171impl Overlayable<f32, f32> for f32 {
172    fn overlay(a: f32, b: f32, min: f32, max: f32) -> f32 {
173        clamp(a + b, min, max)
174    }
175}
176
177impl Overlayable<f64, f64> for f64 {
178    fn overlay(a: f64, b: f64, min: f64, max: f64) -> f64 {
179        clamp(a + b, min, max)
180    }
181}
182
183/// This is used by `overlay()` to get the minimum and maximum values of a given type for the purposes of overlaying data.
184///
185/// For integer types, this is the min/max value of the type one bit count less than this one. For example, `i16::min()` returns `i8::MIN as i16`.
186///
187/// For float types, the min/max value is -1.0 and 1.0.
188pub trait ValueBounds<T>
189where
190    T: Copy + PartialOrd,
191{
192    /// Returns the minimum value of type T.
193    fn min() -> T;
194    /// Returns the maximum value of type T.
195    fn max() -> T;
196}
197
198impl ValueBounds<i16> for i16 {
199    fn min() -> i16 {
200        I8_MIN
201    }
202
203    fn max() -> i16 {
204        I8_MAX
205    }
206}
207
208impl ValueBounds<i32> for i32 {
209    fn min() -> i32 {
210        I16_MIN
211    }
212
213    fn max() -> i32 {
214        I16_MAX
215    }
216}
217
218impl ValueBounds<i64> for i64 {
219    fn min() -> i64 {
220        I32_MIN
221    }
222
223    fn max() -> i64 {
224        I32_MAX
225    }
226}
227
228impl ValueBounds<i128> for i128 {
229    fn min() -> i128 {
230        I64_MIN
231    }
232
233    fn max() -> i128 {
234        I64_MAX
235    }
236}
237
238impl ValueBounds<f32> for f32 {
239    fn min() -> f32 {
240        -1.0
241    }
242
243    fn max() -> f32 {
244        1.0
245    }
246}
247
248impl ValueBounds<f64> for f64 {
249    fn min() -> f64 {
250        -1.0
251    }
252
253    fn max() -> f64 {
254        1.0
255    }
256}