firefly_audio/
pcm.rs

1use crate::*;
2use alloc::vec::Vec;
3use core::fmt::Display;
4
5pub enum PcmError {
6    TooShort,
7    BadMagicNumber,
8    BadSampleRate(u16),
9}
10
11impl Display for PcmError {
12    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
13        match self {
14            Self::TooShort => write!(f, "file is too short"),
15            Self::BadMagicNumber => write!(f, "bad magic number"),
16            Self::BadSampleRate(sr) => write!(f, "bad sample rate: expected 44100, got {sr}"),
17        }
18    }
19}
20
21/// Play audio from a pulse-code modulated audio file.
22pub struct Pcm<R: embedded_io::Read> {
23    reader: R,
24    _sample_rate: u16,
25    is16: bool,
26    stereo: bool,
27    _adpcm: bool,
28}
29
30impl<R: embedded_io::Read> Pcm<R> {
31    /// Create the source from a file in the Firefly Zero format.
32    ///
33    /// # Errors
34    ///
35    /// Returns an error if the file header is invalid.
36    pub fn from_file(mut reader: R) -> Result<Self, PcmError> {
37        let mut header = [0u8; 4];
38        let res = reader.read_exact(&mut header);
39        if res.is_err() {
40            return Err(PcmError::TooShort);
41        }
42        if header[0] != 0x31 {
43            return Err(PcmError::BadMagicNumber);
44        }
45        let sample_rate = u16::from_le_bytes([header[2], header[3]]);
46        if sample_rate != 44100 {
47            return Err(PcmError::BadSampleRate(sample_rate));
48        }
49        Ok(Self {
50            reader,
51            _sample_rate: sample_rate,
52            stereo: header[1] & 0b_100 != 0,
53            is16: header[1] & 0b_010 != 0,
54            _adpcm: header[1] & 0b_001 != 0,
55        })
56    }
57}
58
59impl<R: embedded_io::Read> Processor for Pcm<R> {
60    fn process_children(&mut self, _cn: &mut Vec<Node>) -> Option<Frame> {
61        let f = match (self.is16, self.stereo) {
62            // 8 bit mono
63            (false, false) => {
64                let mut buf = [0u8; 8];
65                self.reader.read_exact(&mut buf).ok()?;
66                let s = Sample::new(i8s_to_f32s(buf));
67                Frame::mono(s)
68            }
69            // 8 bit stereo
70            (false, true) => {
71                let mut buf = [0u8; 16];
72                self.reader.read_exact(&mut buf).ok()?;
73                let left = Sample::new(i8s_to_f32s_left(buf));
74                let right = Sample::new(i8s_to_f32s_right(buf));
75                Frame::stereo(left, right)
76            }
77            // 16 bit mono
78            (true, false) => {
79                let mut buf = [0u8; 16];
80                self.reader.read_exact(&mut buf).ok()?;
81                let s = Sample::new(i16s_to_f32s(buf));
82                Frame::mono(s)
83            }
84            // 16 bit stereo
85            (true, true) => {
86                let mut buf = [0u8; 32];
87                self.reader.read_exact(&mut buf).ok()?;
88                let left = Sample::new(i16s_to_f32s_left(buf));
89                let right = Sample::new(i16s_to_f32s_right(buf));
90                Frame::stereo(left, right)
91            }
92        };
93        Some(f)
94    }
95}
96
97fn i8s_to_f32s(us: [u8; 8]) -> [f32; 8] {
98    [
99        i8_to_f32(us[0]),
100        i8_to_f32(us[1]),
101        i8_to_f32(us[2]),
102        i8_to_f32(us[3]),
103        i8_to_f32(us[4]),
104        i8_to_f32(us[5]),
105        i8_to_f32(us[6]),
106        i8_to_f32(us[7]),
107    ]
108}
109
110fn i8s_to_f32s_left(us: [u8; 16]) -> [f32; 8] {
111    [
112        i8_to_f32(us[0]),
113        i8_to_f32(us[2]),
114        i8_to_f32(us[4]),
115        i8_to_f32(us[6]),
116        i8_to_f32(us[8]),
117        i8_to_f32(us[10]),
118        i8_to_f32(us[12]),
119        i8_to_f32(us[14]),
120    ]
121}
122
123fn i8s_to_f32s_right(us: [u8; 16]) -> [f32; 8] {
124    [
125        i8_to_f32(us[1]),
126        i8_to_f32(us[3]),
127        i8_to_f32(us[5]),
128        i8_to_f32(us[7]),
129        i8_to_f32(us[9]),
130        i8_to_f32(us[11]),
131        i8_to_f32(us[13]),
132        i8_to_f32(us[15]),
133    ]
134}
135
136fn i16s_to_f32s(us: [u8; 16]) -> [f32; 8] {
137    [
138        i16_to_f32(us[0], us[1]),
139        i16_to_f32(us[2], us[3]),
140        i16_to_f32(us[4], us[5]),
141        i16_to_f32(us[6], us[7]),
142        i16_to_f32(us[8], us[9]),
143        i16_to_f32(us[10], us[11]),
144        i16_to_f32(us[12], us[13]),
145        i16_to_f32(us[14], us[15]),
146    ]
147}
148
149fn i16s_to_f32s_left(us: [u8; 32]) -> [f32; 8] {
150    [
151        i16_to_f32(us[0], us[1]),
152        i16_to_f32(us[4], us[5]),
153        i16_to_f32(us[8], us[9]),
154        i16_to_f32(us[12], us[13]),
155        i16_to_f32(us[16], us[17]),
156        i16_to_f32(us[20], us[21]),
157        i16_to_f32(us[24], us[25]),
158        i16_to_f32(us[28], us[29]),
159    ]
160}
161
162fn i16s_to_f32s_right(us: [u8; 32]) -> [f32; 8] {
163    [
164        i16_to_f32(us[2], us[3]),
165        i16_to_f32(us[6], us[7]),
166        i16_to_f32(us[10], us[11]),
167        i16_to_f32(us[14], us[15]),
168        i16_to_f32(us[18], us[19]),
169        i16_to_f32(us[22], us[23]),
170        i16_to_f32(us[26], us[27]),
171        i16_to_f32(us[30], us[31]),
172    ]
173}
174
175fn i8_to_f32(u: u8) -> f32 {
176    #[expect(clippy::cast_possible_wrap)]
177    let i = u as i8;
178    f32::from(i) / f32::from(i8::MAX)
179}
180
181fn i16_to_f32(l: u8, r: u8) -> f32 {
182    let i = i16::from_le_bytes([l, r]);
183    f32::from(i) / f32::from(i16::MAX)
184}