pacmog/
lib.rs

1//! pacmog is a decoding library for PCM files for embedded environments.  
2//!
3//! Rust has an include_bytes! macro to embed the byte sequence in the program.   
4//! Using it, PCM files can be embedded in firmware and used for playback.  
5//!
6//! # Examples
7//!
8//! Read a sample WAV file.
9//! ```
10//! use pacmog::PcmReader;
11//! # use pacmog::PcmReaderError;
12//!
13//! # fn main() -> Result<(), pacmog::PcmReaderError> {
14//! let wav = include_bytes!("../tests/resources/Sine440Hz_1ch_48000Hz_16.wav");
15//! let mut input = &wav[..];
16//! let reader = PcmReader::new(&mut input)?;
17//! let specs = reader.get_pcm_specs();
18//! let num_samples = specs.num_samples;
19//! let num_channels = specs.num_channels;
20//!
21//! println!("PCM info: {:?}", specs);
22//!
23//! for sample in 0..num_samples {
24//!     for channel in 0..num_channels {
25//!         let sample_value: f32 = reader.read_sample(channel, sample)?;
26//!         println!("{}", sample_value);
27//!     }
28//! }
29//! # Ok(())
30//! # }
31//! ```
32
33#![cfg_attr(not(test), no_std)]
34
35use heapless::Vec;
36use num_traits::float::Float;
37use winnow::binary::{
38    be_f32, be_f64, be_i16, be_i24, be_i32, le_f32, le_f64, le_i16, le_i24, le_i32,
39};
40use winnow::{ModalResult, Parser};
41
42mod aiff;
43pub mod imaadpcm;
44mod wav;
45
46const MAX_NUM_CHUNKS: usize = 16;
47
48/// Error type for LinearPCM
49#[derive(Debug, thiserror::Error)]
50pub enum PcmReaderError {
51    #[error("Unsupported bit-depth")]
52    UnsupportedBitDepth,
53    #[error("Unsupported audio format")]
54    UnsupportedAudioFormat,
55    #[error("Invalid channel")]
56    InvalidChannel,
57    #[error("Invalid sample")]
58    InvalidSample,
59    #[error("RIFF or AIFF header size mismatch")]
60    HeaderSizeMismatch,
61    #[error("fmt parse error")]
62    FmtParseError,
63    #[error("Header parse error")]
64    HeaderParseError,
65}
66
67/// Audio format
68#[derive(Debug, Default, PartialEq, Eq, Clone)]
69pub enum AudioFormat {
70    /// Unknown format
71    #[default]
72    Unknown,
73    /// Linear PCM little endian    
74    LinearPcmLe,
75    /// Linear PCM big endian
76    LinearPcmBe,
77    /// IEEE float big endian
78    IeeeFloatLe,
79    /// IEEE float little endian
80    IeeeFloatBe,
81    /// IMA-ADPCM little endian
82    ImaAdpcmLe,
83}
84
85/// Basic information on the PCM file.
86#[derive(Default, Debug, Clone)]
87pub struct PcmSpecs {
88    /// Audio format.
89    pub audio_format: AudioFormat,
90    /// Number of channels.
91    pub num_channels: u16,
92    /// Sample rate in Hz.
93    pub sample_rate: u32,
94    /// Bit depth.
95    pub bit_depth: u16,
96    /// Number of samples per channel.
97    pub num_samples: u32,
98    /// IMA-ADPCM only. Number of bytes per block of IMA-ADPCM.
99    pub(crate) ima_adpcm_num_block_align: Option<u16>,
100    /// IMA-ADPCM only. Number of samples per block of IMA-ADPCM.
101    pub(crate) ima_adpcm_num_samples_per_block: Option<u16>,
102}
103
104/// Reads low level information and Data chunks from the PCM file.
105///
106/// # Examples
107///
108/// Read a sample WAV file.
109/// ```
110/// use pacmog::PcmReader;
111/// # use pacmog::PcmReaderError;
112///
113/// # fn main() -> Result<(), pacmog::PcmReaderError> {
114/// let wav = include_bytes!("../tests/resources/Sine440Hz_1ch_48000Hz_16.wav");
115/// let mut input = &wav[..];
116/// let reader = PcmReader::new(&mut input)?;
117/// let specs = reader.get_pcm_specs();
118/// let num_samples = specs.num_samples;
119/// let num_channels = specs.num_channels;
120///
121/// println!("PCM info: {:?}", specs);
122///
123/// for sample in 0..num_samples {
124///     for channel in 0..num_channels {
125///         let sample_value: f32 = reader.read_sample(channel, sample)?;
126///         println!("{}", sample_value);
127///     }
128/// }
129/// # Ok(())
130/// # }
131/// ```
132#[derive(Default)]
133pub struct PcmReader<'a> {
134    pub(crate) specs: PcmSpecs,
135    pub(crate) data: &'a [u8],
136}
137
138impl<'a> PcmReader<'a> {
139    /// Create a new PcmReader instance.
140    ///
141    /// # Arguments
142    ///
143    /// * 'input' - PCM data byte array
144    ///
145    /// # Errors
146    ///
147    /// If the input data is invalid or the PCM specs are not supported, an error will be returned.
148    pub fn new(input: &mut &'a [u8]) -> Result<Self, PcmReaderError> {
149        let mut reader = PcmReader {
150            data: &[],
151            specs: PcmSpecs::default(),
152        };
153        reader.reload(input)?;
154        Ok(reader)
155    }
156
157    /// Reload a new PCM byte array.
158    ///
159    /// # Arguments
160    ///
161    /// * 'input' - PCM data byte array
162    ///
163    /// # Errors
164    ///
165    /// If the input data is invalid or the PCM specs are not supported, an error will be returned.
166    pub fn reload(&mut self, input: &mut &'a [u8]) -> Result<(), PcmReaderError> {
167        let file_length = input.len();
168        self.data = &[];
169        self.specs = PcmSpecs::default();
170
171        // Parse WAVE format
172        // inputを消費しないようにparse_nextではなくparse_peekを使用している
173        if let Ok((_, riff)) = wav::parse_riff_header.parse_peek(input) {
174            if file_length - 8 != riff.size as usize {
175                return Err(PcmReaderError::HeaderSizeMismatch);
176            }
177
178            return self.parse_wav(input);
179        };
180
181        // Parse AIFF format
182        // inputを消費しないようにparse_nextではなくparse_peekを使用している
183        if let Ok((_, aiff)) = aiff::parse_aiff_header.parse_peek(input) {
184            if (file_length - 8) != aiff.size as usize {
185                return Err(PcmReaderError::HeaderSizeMismatch);
186            }
187
188            return self.parse_aiff(input);
189        };
190
191        Err(PcmReaderError::UnsupportedAudioFormat)
192    }
193
194    /// Parse AIFF format.
195    ///
196    /// # Arguments
197    ///
198    /// * `input` - PCM data byte array
199    fn parse_aiff(&mut self, input: &mut &'a [u8]) -> Result<(), PcmReaderError> {
200        // Parse AIFF header
201        let Ok(_) = aiff::parse_aiff_header.parse_next(input) else {
202            return Err(PcmReaderError::HeaderParseError);
203        };
204
205        let mut chunk_vec = Vec::<aiff::Chunk, MAX_NUM_CHUNKS>::new();
206
207        // Parse chunks
208        while let Ok(chunk) = aiff::parse_chunk.parse_next(input) {
209            chunk_vec.push(chunk).unwrap();
210        }
211
212        for mut chunk in chunk_vec {
213            match chunk.id {
214                aiff::ChunkId::Common => {
215                    let Ok(spec) = aiff::parse_comm.parse_next(&mut chunk.data) else {
216                        return Err(PcmReaderError::UnsupportedAudioFormat);
217                    };
218                    self.specs = spec;
219                }
220                aiff::ChunkId::SoundData => {
221                    let Ok(_ssnd_block_info) = aiff::parse_ssnd.parse_next(&mut chunk.data) else {
222                        return Err(PcmReaderError::UnsupportedAudioFormat);
223                    };
224                    self.data = chunk.data;
225                }
226                aiff::ChunkId::FormatVersion => {}
227                aiff::ChunkId::Marker => {}
228                aiff::ChunkId::Instrument => {}
229                aiff::ChunkId::Midi => {}
230                aiff::ChunkId::AudioRecording => {}
231                aiff::ChunkId::ApplicationSpecific => {}
232                aiff::ChunkId::Comment => {}
233                aiff::ChunkId::Name => {}
234                aiff::ChunkId::Author => {}
235                aiff::ChunkId::Copyright => {}
236                aiff::ChunkId::Annotation => {}
237                aiff::ChunkId::Unknown => {}
238            }
239        }
240        Ok(())
241    }
242
243    /// Parse WAVE format.
244    ///
245    /// # Arguments
246    ///
247    /// * `input` - PCM data byte array
248    fn parse_wav(&mut self, input: &mut &'a [u8]) -> Result<(), PcmReaderError> {
249        // Parse RIFF header
250        let Ok(_) = wav::parse_riff_header.parse_next(input) else {
251            return Err(PcmReaderError::HeaderParseError);
252        };
253
254        let mut chunk_vec = Vec::<wav::Chunk, MAX_NUM_CHUNKS>::new();
255
256        // Parse chunks
257        while let Ok(chunk) = wav::parse_chunk.parse_next(input) {
258            chunk_vec.push(chunk).unwrap();
259        }
260
261        for mut chunk in chunk_vec {
262            match chunk.id {
263                wav::ChunkId::Fmt => {
264                    let Ok(spec) = wav::parse_fmt.parse_next(&mut chunk.data) else {
265                        return Err(PcmReaderError::FmtParseError);
266                    };
267                    self.specs.num_channels = spec.num_channels;
268                    self.specs.sample_rate = spec.sample_rate;
269                    self.specs.audio_format = spec.audio_format;
270                    self.specs.bit_depth = spec.bit_depth;
271                    if self.specs.audio_format == AudioFormat::ImaAdpcmLe {
272                        self.specs.ima_adpcm_num_block_align = spec.ima_adpcm_num_block_align;
273                        self.specs.ima_adpcm_num_samples_per_block =
274                            spec.ima_adpcm_num_samples_per_block;
275                    }
276                }
277                wav::ChunkId::Data => {
278                    self.data = chunk.data;
279                }
280                wav::ChunkId::Fact => {}
281                wav::ChunkId::IDv3 => {}
282                wav::ChunkId::Junk => {}
283                wav::ChunkId::List => {}
284                wav::ChunkId::Peak => {}
285                wav::ChunkId::Unknown => {}
286            }
287        }
288
289        match self.specs.audio_format {
290            AudioFormat::ImaAdpcmLe => {
291                self.specs.num_samples =
292                    imaadpcm::calc_num_samples_per_channel(self.data.len() as u32, &self.specs)
293                        .unwrap();
294            }
295            AudioFormat::LinearPcmLe | AudioFormat::IeeeFloatLe => {
296                self.specs.num_samples =
297                    wav::calc_num_samples_per_channel(self.data.len() as u32, &self.specs).unwrap();
298            }
299            _ => {
300                unreachable!();
301            }
302        }
303        Ok(())
304    }
305
306    /// Returns basic information about the PCM file.
307    #[must_use]
308    pub fn get_pcm_specs(&self) -> PcmSpecs {
309        self.specs.clone()
310    }
311
312    /// Read the sample at an arbitrary position.
313    ///
314    /// # Arguments
315    ///
316    /// * 'channel' - Channel number (0-indexed)
317    /// * 'sample' - Sample number (0-indexed)
318    ///
319    /// # Returns
320    ///
321    /// Returns a normalized value in the range +/-1.0 regardless of AudioFormat.
322    pub fn read_sample<T: Float>(&self, channel: u16, sample: u32) -> Result<T, PcmReaderError> {
323        let num_channels = self.specs.num_channels;
324        if channel >= num_channels {
325            return Err(PcmReaderError::InvalidChannel);
326        }
327
328        if sample >= self.specs.num_samples {
329            return Err(PcmReaderError::InvalidSample);
330        }
331
332        let byte_depth = self.specs.bit_depth / 8u16;
333        let byte_offset = ((byte_depth as u32 * sample * num_channels as u32)
334            + (byte_depth * channel) as u32) as usize;
335        let mut data = &self.data[byte_offset..];
336        decode_sample(&self.specs, &mut data)
337    }
338}
339
340/// Decode a sample from a byte array.
341///
342/// # Arguments
343///
344/// * 'specs' - PCM file specifications
345/// * 'data' - Byte array containing the sample data
346///
347/// # Returns
348///
349/// Returns a normalized value in the range +/-1.0 regardless of AudioFormat.
350fn decode_sample<T: Float>(specs: &PcmSpecs, data: &mut &[u8]) -> Result<T, PcmReaderError> {
351    match specs.audio_format {
352        AudioFormat::Unknown => Err(PcmReaderError::UnsupportedAudioFormat),
353        AudioFormat::LinearPcmLe => match specs.bit_depth {
354            16 => {
355                const MAX: u32 = 2u32.pow(15);
356                let res: ModalResult<i16> = le_i16.parse_next(data);
357                let Ok(sample) = res else {
358                    return Err(PcmReaderError::InvalidSample);
359                };
360                Ok(T::from(sample).unwrap() / T::from(MAX).unwrap())
361            }
362            24 => {
363                const MAX: u32 = 2u32.pow(23);
364                let res: ModalResult<i32> = le_i24.parse_next(data);
365                let Ok(sample) = res else {
366                    return Err(PcmReaderError::InvalidSample);
367                };
368                Ok(T::from(sample).unwrap() / T::from(MAX).unwrap())
369            }
370            32 => {
371                const MAX: u32 = 2u32.pow(31);
372                let res: ModalResult<i32> = le_i32.parse_next(data);
373                let Ok(sample) = res else {
374                    return Err(PcmReaderError::InvalidSample);
375                };
376                Ok(T::from(sample).unwrap() / T::from(MAX).unwrap())
377            }
378            _ => Err(PcmReaderError::UnsupportedBitDepth),
379        },
380        AudioFormat::LinearPcmBe => match specs.bit_depth {
381            16 => {
382                const MAX: u32 = 2u32.pow(15);
383                let res: ModalResult<i16> = be_i16.parse_next(data);
384                let Ok(sample) = res else {
385                    return Err(PcmReaderError::InvalidSample);
386                };
387                Ok(T::from(sample).unwrap() / T::from(MAX).unwrap())
388            }
389            24 => {
390                const MAX: u32 = 2u32.pow(23);
391                let res: ModalResult<i32> = be_i24.parse_next(data);
392                let Ok(sample) = res else {
393                    return Err(PcmReaderError::InvalidSample);
394                };
395                Ok(T::from(sample).unwrap() / T::from(MAX).unwrap())
396            }
397            32 => {
398                const MAX: u32 = 2u32.pow(31);
399                let res: ModalResult<i32> = be_i32.parse_next(data);
400                let Ok(sample) = res else {
401                    return Err(PcmReaderError::InvalidSample);
402                };
403                Ok(T::from(sample).unwrap() / T::from(MAX).unwrap())
404            }
405            _ => Err(PcmReaderError::UnsupportedBitDepth),
406        },
407        AudioFormat::IeeeFloatLe => match specs.bit_depth {
408            32 => {
409                let res: ModalResult<f32> = le_f32.parse_next(data);
410                let Ok(sample) = res else {
411                    return Err(PcmReaderError::InvalidSample);
412                };
413                Ok(T::from(sample).unwrap())
414            }
415            64 => {
416                let res: ModalResult<f64> = le_f64.parse_next(data);
417                let Ok(sample) = res else {
418                    return Err(PcmReaderError::InvalidSample);
419                };
420                Ok(T::from(sample).unwrap())
421            }
422            _ => Err(PcmReaderError::UnsupportedBitDepth),
423        },
424        AudioFormat::IeeeFloatBe => match specs.bit_depth {
425            32 => {
426                let res: ModalResult<f32> = be_f32.parse_next(data);
427                let Ok(sample) = res else {
428                    return Err(PcmReaderError::InvalidSample);
429                };
430                Ok(T::from(sample).unwrap())
431            }
432            64 => {
433                let res: ModalResult<f64> = be_f64.parse_next(data);
434                let Ok(sample) = res else {
435                    return Err(PcmReaderError::InvalidSample);
436                };
437                Ok(T::from(sample).unwrap())
438            }
439            _ => Err(PcmReaderError::UnsupportedBitDepth),
440        },
441        AudioFormat::ImaAdpcmLe => Err(PcmReaderError::UnsupportedAudioFormat),
442    }
443}
444
445/// Error type for PcmPlayer
446#[derive(Debug, thiserror::Error)]
447pub enum PcmPlayerError {
448    #[error("Output buffer too short")]
449    OutputBufferTooShort,
450    #[error("Invalid position")]
451    InvalidPosition,
452    #[error("Finish playing")]
453    FinishPlaying,
454}
455
456/// High level of organized players for LinearPCM (WAVE or AIFF) file.
457///
458/// # Examples
459///
460/// Play a sample WAV file.
461/// ```
462/// use pacmog::{PcmPlayer, PcmReader};
463///
464/// # fn main() -> anyhow::Result<()> {
465/// let wav = include_bytes!("../tests/resources/Sine440Hz_1ch_48000Hz_16.wav");
466/// let mut input = &wav[..];
467/// let reader = PcmReader::new(&mut input)?;
468/// let mut player = PcmPlayer::new(reader);
469/// let specs = player.reader.get_pcm_specs();
470/// let num_samples = specs.num_samples;
471///
472/// player.set_loop_playing(true);
473/// let mut buffer: [f32; 2] = [0.0f32; 2];
474/// let buf = buffer.as_mut_slice();
475///
476/// for sample in 0..num_samples {
477///    player.get_next_frame(buf)?;
478/// }
479/// # Ok(())
480/// # }
481/// ```
482#[derive(Default)]
483pub struct PcmPlayer<'a> {
484    /// A reader to access basic information about the PCM file.
485    pub reader: PcmReader<'a>,
486    playback_position: u32,
487    loop_playing: bool,
488}
489
490impl<'a> PcmPlayer<'a> {
491    /// * 'input' - PCM data byte array
492    pub fn new(reader: PcmReader<'a>) -> Self {
493        PcmPlayer {
494            reader,
495            playback_position: 0,
496            loop_playing: false,
497        }
498    }
499
500    /// Move the playback position to the desired position.
501    /// * 'sample' - Playback position in samples.
502    pub fn set_position(&mut self, sample: u32) -> Result<(), PcmPlayerError> {
503        if self.reader.specs.num_samples <= sample {
504            return Err(PcmPlayerError::InvalidPosition);
505        }
506        self.playback_position = sample;
507        Ok(())
508    }
509
510    /// Enable loop playback.
511    ///
512    /// true: Enable loop playback
513    /// false: Disable loop playback
514    pub fn set_loop_playing(&mut self, en: bool) {
515        self.loop_playing = en;
516    }
517
518    /// Return samples value of the next frame.
519    ///
520    /// # Arguments
521    ///
522    /// * ‘out’ - Output buffer which the sample values are written. Number of elements must be equal to or greater than the number of channels in the PCM file.
523    ///
524    /// # Errors
525    ///
526    /// If the output buffer is too short, an error will be returned.
527    /// If the loop playback is disabled and the end of the file is reached, an error will be returned.
528    pub fn get_next_frame<T: Float>(&mut self, out: &mut [T]) -> Result<(), PcmPlayerError> {
529        if out.len() < self.reader.specs.num_channels as usize {
530            return Err(PcmPlayerError::OutputBufferTooShort);
531        }
532
533        let num_samples = self.reader.specs.num_samples;
534        if self.playback_position >= num_samples {
535            if self.loop_playing {
536                self.set_position(0)?;
537            } else {
538                return Err(PcmPlayerError::FinishPlaying);
539            }
540        }
541
542        let num_chennels = self.reader.specs.num_channels;
543        for ch in 0..num_chennels {
544            let Ok(sample) = self.reader.read_sample(ch, self.playback_position) else {
545                return Err(PcmPlayerError::InvalidPosition);
546            };
547            out[ch as usize] = sample;
548        }
549
550        // Update the playback position.
551        self.playback_position += 1;
552
553        Ok(())
554    }
555}