1use std::fmt::Debug;
3
4use bytemuck::Pod;
5use i24::i24;
6use num_traits::Num;
7
8use crate::core::alloc_sample_buffer;
9
10pub trait AudioSample:
12 Copy
13 + Pod
14 + Num
15 + ConvertTo<i16>
16 + ConvertTo<i32>
17 + ConvertTo<i24>
18 + ConvertTo<f32>
19 + ConvertTo<f64>
20 + Sync
21 + Send
22 + Debug
23{
24}
25
26impl AudioSample for i16 {}
27impl AudioSample for i24 {}
28impl AudioSample for i32 {}
29impl AudioSample for f32 {}
30impl AudioSample for f64 {}
31
32pub trait ConvertTo<T: AudioSample> {
35 fn convert_to(&self) -> T
36 where
37 Self: Sized + AudioSample;
38}
39
40pub trait ConvertSlice<T: AudioSample> {
43 fn convert_slice(self) -> Box<[T]>;
44}
45
46impl<T: AudioSample, F> ConvertSlice<T> for Box<[F]>
47where
48 F: AudioSample + ConvertTo<T>,
49{
50 fn convert_slice(self) -> Box<[T]> {
51 let mut out: Box<[T]> = alloc_sample_buffer(self.len());
52 for i in 0..self.len() {
53 out[i] = self[i].convert_to();
54 }
55 out
56 }
57}
58
59impl ConvertTo<i16> for i16 {
61 #[inline(always)]
62 fn convert_to(&self) -> i16 {
63 *self
64 }
65}
66
67impl ConvertTo<i24> for i16 {
68 #[inline(always)]
69 fn convert_to(&self) -> i24 {
70 i24::from_i32((*self as i32) << 8)
71 }
72}
73
74impl ConvertTo<i32> for i16 {
75 #[inline(always)]
76 fn convert_to(&self) -> i32 {
77 (*self as i32) << 16
78 }
79}
80
81impl ConvertTo<f32> for i16 {
82 #[inline(always)]
83 fn convert_to(&self) -> f32 {
84 ((*self as f32) / (i16::MAX as f32)).clamp(-1.0, 1.0)
85 }
86}
87
88impl ConvertTo<f64> for i16 {
89 #[inline(always)]
90 fn convert_to(&self) -> f64 {
91 ((*self as f64) / (i16::MAX as f64)).clamp(-1.0, 1.0)
92 }
93}
94
95impl ConvertTo<i16> for i24 {
98 #[inline(always)]
99 fn convert_to(&self) -> i16 {
100 (self.to_i32() >> 8) as i16
101 }
102}
103
104impl ConvertTo<i24> for i24 {
105 #[inline(always)]
106 fn convert_to(&self) -> i24 {
107 *self
108 }
109}
110
111impl ConvertTo<i32> for i24 {
112 #[inline(always)]
113 fn convert_to(&self) -> i32 {
114 self.to_i32() << 8
115 }
116}
117
118impl ConvertTo<f32> for i24 {
119 #[inline(always)]
120 fn convert_to(&self) -> f32 {
121 (self.to_i32() as f32) / (i32::MAX as f32)
122 }
123}
124
125impl ConvertTo<f64> for i24 {
126 #[inline(always)]
127 fn convert_to(&self) -> f64 {
128 (self.to_i32() as f64) / (i32::MAX as f64)
129 }
130}
131
132impl ConvertTo<i16> for i32 {
134 #[inline(always)]
135 fn convert_to(&self) -> i16 {
136 (*self >> 16) as i16
137 }
138}
139
140impl ConvertTo<i24> for i32 {
141 #[inline(always)]
142 fn convert_to(&self) -> i24 {
143 i24::from_i32(*self >> 8)
144 }
145}
146
147impl ConvertTo<i32> for i32 {
148 #[inline(always)]
149 fn convert_to(&self) -> i32 {
150 *self
151 }
152}
153
154impl ConvertTo<f32> for i32 {
155 #[inline(always)]
156 fn convert_to(&self) -> f32 {
157 ((*self as f32) / (i32::MAX as f32)).clamp(-1.0, 1.0)
158 }
159}
160
161impl ConvertTo<f64> for i32 {
162 #[inline(always)]
163 fn convert_to(&self) -> f64 {
164 ((*self as f64) / (i32::MAX as f64)).clamp(-1.0, 1.0)
165 }
166}
167
168impl ConvertTo<i16> for f32 {
170 #[inline(always)]
171 fn convert_to(&self) -> i16 {
172 ((*self * (i16::MAX as f32)).clamp(i16::MIN as f32, i16::MAX as f32)).round() as i16
173 }
174}
175
176impl ConvertTo<i24> for f32 {
177 #[inline(always)]
178 fn convert_to(&self) -> i24 {
179 i24::from_i32(
180 ((*self * (i32::MAX as f32)).clamp(i32::MIN as f32, i32::MAX as f32)).round() as i32,
181 )
182 }
183}
184
185impl ConvertTo<i32> for f32 {
186 #[inline(always)]
187 fn convert_to(&self) -> i32 {
188 ((*self * (i32::MAX as f32)).clamp(i32::MIN as f32, i32::MAX as f32)).round() as i32
189 }
190}
191
192impl ConvertTo<f32> for f32 {
193 #[inline(always)]
194 fn convert_to(&self) -> f32 {
195 *self
196 }
197}
198
199impl ConvertTo<f64> for f32 {
200 #[inline(always)]
201 fn convert_to(&self) -> f64 {
202 *self as f64
203 }
204}
205
206impl ConvertTo<i16> for f64 {
208 #[inline(always)]
209 fn convert_to(&self) -> i16 {
210 ((*self * (i16::MAX as f64)).clamp(i16::MIN as f64, i16::MAX as f64)).round() as i16
211 }
212}
213
214impl ConvertTo<i24> for f64 {
215 #[inline(always)]
216 fn convert_to(&self) -> i24 {
217 i24::from_i32(
218 ((*self * (i32::MAX as f64)).clamp(i32::MIN as f64, i32::MAX as f64)).round() as i32,
219 )
220 }
221}
222
223impl ConvertTo<i32> for f64 {
224 #[inline(always)]
225 fn convert_to(&self) -> i32 {
226 ((*self * (i32::MAX as f64)).clamp(i32::MIN as f64, i32::MAX as f64)).round() as i32
227 }
228}
229
230impl ConvertTo<f32> for f64 {
231 #[inline(always)]
232 fn convert_to(&self) -> f32 {
233 *self as f32
234 }
235}
236
237impl ConvertTo<f64> for f64 {
238 #[inline(always)]
239 fn convert_to(&self) -> f64 {
240 *self
241 }
242}
243
244#[cfg(test)]
245mod conversion_tests {
246
247 use super::*;
248 use std::fs::File;
249 use std::io::BufRead;
250 use std::path::Path;
251 use std::str::FromStr;
252
253 use approx_eq::assert_approx_eq;
254
255 #[test]
256 fn i16_to_f32() {
257 let i16_samples: Vec<i16> =
258 read_text_to_vec(Path::new("./test_resources/one_channel_i16.txt")).unwrap();
259 let i16_samples: &[i16] = &i16_samples;
260
261 let f32_samples: Vec<f32> =
262 read_text_to_vec(Path::new("./test_resources/one_channel_f32.txt")).unwrap();
263 let f32_samples: &[f32] = &f32_samples;
264 for (expected_sample, actual_sample) in f32_samples.iter().zip(i16_samples) {
265 let actual_sample: f32 = actual_sample.convert_to();
266 assert_approx_eq!(*expected_sample as f64, actual_sample as f64, 1e-4);
267 }
268 }
269
270 #[test]
271 fn i16_to_f32_slice() {
272 let i16_samples: Vec<i16> =
273 read_text_to_vec(Path::new("./test_resources/one_channel_i16.txt")).unwrap();
274 let i16_samples: Box<[i16]> = i16_samples.into_boxed_slice();
275 let f32_samples: Vec<f32> =
276 read_text_to_vec(Path::new("./test_resources/one_channel_f32.txt")).unwrap();
277
278 let f32_samples: &[f32] = &f32_samples;
279 let converted_i16_samples: Box<[f32]> = i16_samples.convert_slice();
280
281 for (_, (expected_sample, actual_sample)) in
282 converted_i16_samples.iter().zip(f32_samples).enumerate()
283 {
284 assert_approx_eq!(*expected_sample as f64, *actual_sample as f64, 1e-4);
285 }
286 }
287
288 #[test]
289 fn f32_to_i16() {
290 let i16_samples: Vec<i16> =
291 read_text_to_vec(Path::new("./test_resources/one_channel_i16.txt")).unwrap();
292 let i16_samples: &[i16] = &i16_samples;
293
294 let f32_samples: Vec<f32> =
295 read_text_to_vec(Path::new("./test_resources/one_channel_f32.txt")).unwrap();
296
297 let f32_samples: &[f32] = &f32_samples;
298 for (expected_sample, actual_sample) in i16_samples.iter().zip(f32_samples) {
299 let converted_sample: i16 = actual_sample.convert_to();
300 assert_eq!(
301 *expected_sample, converted_sample,
302 "Failed to convert sample {} to i16",
303 actual_sample
304 );
305 }
306 }
307
308 #[cfg(test)]
309 fn read_lines<P>(filename: P) -> std::io::Result<std::io::Lines<std::io::BufReader<File>>>
310 where
311 P: AsRef<Path>,
312 {
313 let file = File::open(filename)?;
314 Ok(std::io::BufReader::new(file).lines())
315 }
316
317 #[cfg(test)]
318 fn read_text_to_vec<T: FromStr>(fp: &Path) -> Result<Vec<T>, Box<dyn std::error::Error>>
319 where
320 <T as FromStr>::Err: std::error::Error + 'static,
321 {
322 let mut data = Vec::new();
323 let lines = read_lines(fp)?;
324 for line in lines {
325 let line = line?;
326 for sample in line.split(" ") {
327 let parsed_sample: T = match sample.trim().parse::<T>() {
328 Ok(num) => num,
329 Err(err) => {
330 eprintln!("Failed to parse {}", sample);
331 panic!("{}", err)
332 }
333 };
334 data.push(parsed_sample);
335 }
336 }
337 Ok(data)
338 }
339}
340
341#[cfg(feature = "ndarray")]
342pub trait IntoNdarray {
343 type Target: AudioSample;
344 fn into_ndarray(self) -> crate::WaversResult<(ndarray::Array2<Self::Target>, i32)>;
345}
346
347#[cfg(feature = "ndarray")]
348pub trait AsNdarray {
349 type Target: AudioSample;
350 fn as_ndarray(&mut self) -> crate::WaversResult<(ndarray::Array2<Self::Target>, i32)>;
351}