1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
5#[serde(rename_all = "lowercase")]
6pub enum SampleFormat {
7 I8,
8 I16,
9 U16,
10 I32,
11 F32,
12 F64,
13}
14
15impl SampleFormat {
16 pub const ALL: [SampleFormat; 6] =
17 [SampleFormat::I8, SampleFormat::I16, SampleFormat::U16, SampleFormat::I32, SampleFormat::F32, SampleFormat::F64];
18
19 pub const fn bytes(self) -> usize {
20 match self {
21 SampleFormat::I8 => 1,
22 SampleFormat::I16 | SampleFormat::U16 => 2,
23 SampleFormat::I32 | SampleFormat::F32 => 4,
24 SampleFormat::F64 => 8,
25 }
26 }
27
28 pub const fn name(self) -> &'static str {
30 match self {
31 SampleFormat::I8 => "int8",
32 SampleFormat::I16 => "int16",
33 SampleFormat::U16 => "uint16",
34 SampleFormat::I32 => "int32",
35 SampleFormat::F32 => "float32",
36 SampleFormat::F64 => "float64",
37 }
38 }
39
40 pub fn decode(self, bytes: &[u8], out: &mut [f32], gain: f32, offset: f32) {
46 fn run<const B: usize>(bytes: &[u8], out: &mut [f32], gain: f32, offset: f32, value: impl Fn([u8; B]) -> f32) {
47 assert!(bytes.len() >= out.len() * B, "{} bytes hold fewer than {} samples", bytes.len(), out.len());
48 for (o, b) in out.iter_mut().zip(bytes.as_chunks::<B>().0) {
49 *o = value(*b) * gain + offset;
50 }
51 }
52 match self {
53 SampleFormat::I8 => run::<1>(bytes, out, gain, offset, |b| i8::from_le_bytes(b) as f32),
54 SampleFormat::I16 => run::<2>(bytes, out, gain, offset, |b| i16::from_le_bytes(b) as f32),
55 SampleFormat::U16 => run::<2>(bytes, out, gain, offset, |b| u16::from_le_bytes(b) as f32),
56 SampleFormat::I32 => run::<4>(bytes, out, gain, offset, |b| i32::from_le_bytes(b) as f32),
57 SampleFormat::F32 => run::<4>(bytes, out, gain, offset, f32::from_le_bytes),
58 SampleFormat::F64 => run::<8>(bytes, out, gain, offset, |b| f64::from_le_bytes(b) as f32),
59 }
60 }
61
62 pub fn parse(s: &str) -> Option<Self> {
64 match s.trim().to_ascii_lowercase().trim_end_matches("-le") {
65 "i8" | "int8" | "<i1" | "|i1" => Some(SampleFormat::I8),
66 "i16" | "int16" | "<i2" => Some(SampleFormat::I16),
67 "u16" | "uint16" | "<u2" => Some(SampleFormat::U16),
68 "i32" | "int32" | "<i4" => Some(SampleFormat::I32),
69 "f32" | "float32" | "float" | "<f4" => Some(SampleFormat::F32),
70 "f64" | "float64" | "double" | "<f8" => Some(SampleFormat::F64),
71 _ => None,
72 }
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn decode_scales_every_format() {
82 let mut out = [0.0f32; 2];
83 SampleFormat::I16.decode(&[0x10, 0x00, 0xF0, 0xFF], &mut out, 0.5, 1.0);
84 assert_eq!(out, [9.0, -7.0]);
85 SampleFormat::U16.decode(&[0xFF, 0xFF, 0x00, 0x00], &mut out, 1.0, 0.0);
86 assert_eq!(out, [65535.0, 0.0]);
87 SampleFormat::F64.decode(&[1.5f64.to_le_bytes(), (-2.0f64).to_le_bytes()].concat(), &mut out, 2.0, 0.0);
88 assert_eq!(out, [3.0, -4.0]);
89 }
90
91 #[test]
92 fn test_parse_and_size() {
93 assert_eq!(SampleFormat::parse("int16"), Some(SampleFormat::I16));
94 assert_eq!(SampleFormat::parse("float32-le"), Some(SampleFormat::F32));
95 assert_eq!(SampleFormat::parse("<u2"), Some(SampleFormat::U16));
96 assert_eq!(SampleFormat::parse("<f8"), Some(SampleFormat::F64));
97 assert_eq!(SampleFormat::parse("complex64"), None);
98 assert_eq!(SampleFormat::I16.bytes(), 2);
99 assert_eq!(SampleFormat::F64.bytes(), 8);
100 for f in SampleFormat::ALL {
101 assert_eq!(SampleFormat::parse(f.name()), Some(f));
102 }
103 }
104}