spectrusty_core/audio/
sample.rs

1/*
2    Copyright (C) 2020-2022  Rafal Michalski
3
4    This file is part of SPECTRUSTY, a Rust library for building emulators.
5
6    For the full copyright notice, see the lib.rs file.
7*/
8//! Various traits for types being used as audio samples.
9
10/// Provides various methods to primitive types being used as audio samples.
11pub trait AudioSample: Copy + Send + Default + 'static {
12    /// Creates a silent sample value (with zero amplitude). Useful for filling buffers.
13    #[inline(always)]
14    fn silence() -> Self {
15        Self::default()
16    }
17    fn max_pos_amplitude() -> Self;
18    fn max_neg_amplitude() -> Self;
19}
20
21/// For converting samples between types.
22pub trait FromSample<S> {
23    /// Converts to Self a sample from the `other`.
24    fn from_sample(other: S) -> Self;
25}
26
27/// For converting samples between types.
28pub trait IntoSample<S> {
29    /// Convert to `S` a sample type from `self`.
30    fn into_sample(self) -> S;
31}
32
33/// This trait is being used to calculate sample amplitude differences (∆).
34pub trait SampleDelta: Copy + Default {
35    /// Returns the difference (if any) between `after` and `self` (before).
36    fn sample_delta(self, after: Self) -> Option<Self>;
37}
38
39/// This trait is being used for scaling sample amplitudes.
40pub trait MulNorm {
41    /// Saturating addition. Computes self + other, saturating at the normalized bounds instead of overflowing.
42    ///
43    /// Float samples operates in range: [-1.0, 1.0], integer: [min, max]
44    fn saturating_add(self, other: Self) -> Self;
45    /// Multiplies `self` with the `other` in the normalized sample amplitude range.
46    ///
47    /// Float samples operates in range: [-1.0, 1.0], integer: [min, max]
48    fn mul_norm(self, other: Self) -> Self;
49}
50
51impl SampleDelta for f32 {
52    #[inline]
53    fn sample_delta(self, after: f32) -> Option<f32> {
54        let delta = after - self;
55        if delta.abs() > core::f32::EPSILON {
56            Some(delta)
57        }
58        else {
59            None
60        }
61    }
62}
63
64macro_rules! impl_sample_delta_int {
65    ($ty:ty) => {
66        impl SampleDelta for $ty {
67            #[inline]
68            fn sample_delta(self, after: $ty) -> Option<$ty> {
69                let delta = after - self;
70                if delta != 0 {
71                    Some(delta)
72                }
73                else {
74                    None
75                }
76            }
77        }
78    };
79}
80
81impl_sample_delta_int!(i16);
82impl_sample_delta_int!(i32);
83
84impl AudioSample for f32 {
85    #[inline(always)] fn max_pos_amplitude() -> Self {  1.0 }
86    #[inline(always)] fn max_neg_amplitude() -> Self { -1.0 }
87}
88impl AudioSample for i16 {
89    #[inline(always)] fn max_pos_amplitude() -> Self { i16::max_value() }
90    #[inline(always)] fn max_neg_amplitude() -> Self { i16::min_value() }
91}
92impl AudioSample for i8 {
93    #[inline(always)] fn max_pos_amplitude() -> Self { i8::max_value() }
94    #[inline(always)] fn max_neg_amplitude() -> Self { i8::min_value() }
95}
96impl AudioSample for u16 {
97    #[inline(always)]
98    fn silence() -> Self {
99        0x8000
100    }    
101    #[inline(always)] fn max_pos_amplitude() -> Self { u16::max_value() }
102    #[inline(always)] fn max_neg_amplitude() -> Self { 0 }
103}
104impl AudioSample for u8 {
105    #[inline(always)]
106    fn silence() -> Self {
107        0x80
108    }
109    #[inline(always)] fn max_pos_amplitude() -> Self { u8::max_value() }
110    #[inline(always)] fn max_neg_amplitude() -> Self { 0 }
111}
112
113impl<S: FromSample<T>, T> IntoSample<S> for T {
114    #[inline]
115    fn into_sample(self) -> S {
116        S::from_sample(self)
117    }    
118}
119
120impl<T: AudioSample> FromSample<T> for T {
121    #[inline(always)]
122    fn from_sample(other: T) -> T {
123        other
124    }
125}
126
127macro_rules! impl_from_sample {
128    ($int:ty, $uint:ty, $ft:ty) => {
129        impl FromSample<$int> for $ft {
130            #[inline]
131            fn from_sample(other: $int) -> $ft {
132                if other < 0 {
133                    other as $ft / -(<$int>::min_value() as $ft)
134                } else {
135                    other as $ft / <$int>::max_value() as $ft
136                }
137            }
138        }
139
140        impl FromSample<$ft> for $int {
141            #[inline]
142            fn from_sample(other: $ft) -> $int {
143                if other >= 0.0 {
144                    (other * <$int>::max_value() as $ft) as $int
145                } else {
146                    (-other * <$int>::min_value() as $ft) as $int
147                }
148            }
149        }
150
151        impl FromSample<$uint> for $ft {
152            #[inline]
153            fn from_sample(other: $uint) -> $ft {
154                <$ft>::from_sample(<$int>::from_sample(other))
155            }
156        }
157
158        impl FromSample<$uint> for $int {
159            #[inline]
160            fn from_sample(other: $uint) -> $int {
161                other.wrapping_sub(<$int>::min_value() as $uint) as $int
162            }
163        }
164
165        impl FromSample<$int> for $uint {
166            #[inline]
167            fn from_sample(other: $int) -> $uint {
168                other.wrapping_sub(<$int>::min_value()) as $uint
169            }
170        }
171
172        impl FromSample<$ft> for $uint {
173            #[inline]
174            fn from_sample(other: $ft) -> $uint {
175                <$uint>::from_sample(<$int>::from_sample(other))
176            }
177        }
178
179    };
180}
181
182impl_from_sample!(i8, u8, f32);
183impl_from_sample!(i16, u16, f32);
184
185impl MulNorm for f32 {
186    fn saturating_add(self, other: f32) -> f32 {
187        (self + other).clamp(-1.0, 1.0)
188    }
189
190    fn mul_norm(self, other: f32) -> f32 {
191        self * other
192    }
193}
194
195impl MulNorm for i16 {
196    fn saturating_add(self, other: i16) -> i16 {
197        self.saturating_add(other)
198    }
199
200    fn mul_norm(self, other: i16) -> i16 {
201        ((self as i32 * other as i32) >> 15) as i16
202    }
203}
204
205impl MulNorm for i32 {
206    fn saturating_add(self, other: i32) -> i32 {
207        self.saturating_add(other)
208    }
209
210    fn mul_norm(self, other: i32) -> i32 {
211        ((self as i64 * other as i64) >> 31) as i32
212    }
213}
214
215#[cfg(test)]
216mod test {
217    use super::*;
218
219    #[test]
220    fn i16_from_i16() {
221        for (f, t) in vec![(0i16, 0i16), (123, 123), (-456, -456), (32767, 32767), (-32768, -32768)] {
222            assert_eq!(i16::from_sample(f), t);
223            assert_eq!(IntoSample::<i16>::into_sample(f), t);
224        }
225    }
226
227    #[test]
228    fn i16_from_u16() {
229        for (f, t) in vec![(32768u16, 0i16), (32769, 1), (16384, -16384), (65535, 32767), (0, -32768)] {
230            assert_eq!(i16::from_sample(f), t);
231            assert_eq!(IntoSample::<i16>::into_sample(f), t);
232        }
233    }
234
235    #[test]
236    fn i16_from_f32() {
237        for (f, t) in vec![(0.0f32, 0i16), (1.0/32767.0, 1), (-0.5, -16384), (1.0, 32767), (-1.0, -32768)] {
238            assert_eq!(i16::from_sample(f), t);
239            assert_eq!(IntoSample::<i16>::into_sample(f), t);
240        }
241    }
242
243    #[test]
244    fn u16_from_i16() {
245        for (f, t) in vec![(0i16, 32768u16), (1, 32769), (-16384, 16384), (32767, 65535), (-32768, 0)] {
246            assert_eq!(u16::from_sample(f), t);
247            assert_eq!(IntoSample::<u16>::into_sample(f), t);
248        }
249    }
250
251    #[test]
252    fn u16_from_u16() {
253        for (f, t) in vec![(32768u16, 32768u16), (32769, 32769), (16384, 16384), (65535, 65535), (0, 0)] {
254            assert_eq!(u16::from_sample(f), t);
255            assert_eq!(IntoSample::<u16>::into_sample(f), t);
256        }
257    }
258
259    #[test]
260    fn u16_from_f32() {
261        for (f, t) in vec![(0.0f32, 32768u16), (1.0/32767.0, 32769), (-0.5, 16384), (1.0, 65535), (-1.0, 0)] {
262            assert_eq!(u16::from_sample(f), t);
263            assert_eq!(IntoSample::<u16>::into_sample(f), t);
264        }
265    }
266
267    #[test]
268    fn f32_from_i16() {
269        for (f, t) in vec![(0i16, 0.0f32), (1, 1.0/32767.0), (-16384, -0.5), (32767, 1.0), (-32768, -1.0)] {
270            assert_eq!(f32::from_sample(f), t);
271            assert_eq!(IntoSample::<f32>::into_sample(f), t);
272        }
273    }
274
275    #[test]
276    fn f32_from_u16() {
277        for (f, t) in vec![(32768u16, 0.0f32), (32769, 1.0/32767.0), (16384, -0.5), (65535, 1.0), (0, -1.0)] {
278            assert_eq!(f32::from_sample(f), t);
279            assert_eq!(IntoSample::<f32>::into_sample(f), t);
280        }
281    }
282
283    #[test]
284    fn f32_from_f32() {
285        for (f, t) in vec![(0.0f32, 0.0f32), (-0.5, -0.5), (0.5, 0.5), (1.0, 1.0), (-1.0, -1.0)] {
286            assert_eq!(f32::from_sample(f), t);
287            assert_eq!(IntoSample::<f32>::into_sample(f), t);
288        }
289    }
290}