Skip to main content

ahx_rs/
packet.rs

1use crate::{
2    bits::BitReader,
3    error::{Error, InvalidData, Result},
4    input::{Input, SliceInput},
5    quantization::{self, SubbandSamples},
6    synthesis::Synthesis,
7    SAMPLES_PER_FRAME,
8};
9
10pub(crate) const FRAME_HEADER: [u8; 4] = [0xff, 0xf5, 0xe0, 0xc0];
11
12/// One complete decoded packet, borrowing the caller's PCM storage.
13#[derive(Debug)]
14pub struct DecodedPacket<'a> {
15    pcm: &'a [i16; SAMPLES_PER_FRAME],
16    consumed: usize,
17}
18
19impl<'a> DecodedPacket<'a> {
20    /// The complete frame's 1,152 mono PCM16 elements, with its size in the type.
21    pub fn pcm(&self) -> &'a [i16; SAMPLES_PER_FRAME] {
22        self.pcm
23    }
24
25    /// Compressed bytes consumed, including the MPEG header and final padded byte.
26    /// Any following frame or container footer is left untouched.
27    pub const fn consumed_bytes(&self) -> usize {
28        self.consumed
29    }
30}
31
32/// Allocation-free decoder for AHX's fixed-profile MPEG frame packets.
33///
34/// Supply consecutive frames from a single stream to preserve synthesis history.
35/// This API does not parse an AHX header/footer or trim the final frame. Use
36/// [`crate::SliceDecoder`] or the standard I/O decoder for complete AHX files.
37/// All packet validation precedes synthesis. Errors preserve both output and
38/// synthesis state, allowing a truncated input or small output buffer to be retried.
39pub struct PacketDecoder {
40    synthesis: Synthesis,
41}
42
43impl core::fmt::Debug for PacketDecoder {
44    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
45        f.debug_struct("PacketDecoder").finish_non_exhaustive()
46    }
47}
48
49impl Default for PacketDecoder {
50    fn default() -> Self {
51        Self::new()
52    }
53}
54
55impl PacketDecoder {
56    /// Initialize zeroed synthesis history without allocating.
57    pub fn new() -> Self {
58        Self {
59            synthesis: Synthesis::new(),
60        }
61    }
62
63    /// Decode the first complete frame in `input` into caller-owned PCM16.
64    ///
65    /// Requires [`SAMPLES_PER_FRAME`] output elements. Extra output is unchanged.
66    /// Extra input is not consumed. `Truncated` means the first frame needs more
67    /// input; retain this prefix and retry with more bytes, up to [`crate::MAX_FRAME_BYTES`].
68    pub fn decode_into<'a>(
69        &mut self,
70        input: &[u8],
71        pcm: &'a mut [i16],
72    ) -> Result<DecodedPacket<'a>> {
73        let pcm = frame_buffer(pcm)?;
74        let consumed = self.decode_from(&mut SliceInput(input), pcm)?;
75        Ok(DecodedPacket { pcm, consumed })
76    }
77
78    pub(crate) fn decode_from(
79        &mut self,
80        input: &mut impl Input,
81        pcm: &mut [i16; SAMPLES_PER_FRAME],
82    ) -> Result<usize> {
83        let frame = ParsedFrame::read(input)?;
84        // Synthesis is infallible and can only see fully validated parameters
85        // and a complete output array. Parsing never receives mutable history.
86        frame.synthesize(&mut self.synthesis, pcm);
87        Ok(frame.consumed)
88    }
89}
90
91pub(crate) fn frame_buffer(pcm: &mut [i16]) -> Result<&mut [i16; SAMPLES_PER_FRAME]> {
92    let provided = pcm.len();
93    pcm.first_chunk_mut().ok_or(Error::BufferTooSmall {
94        required: SAMPLES_PER_FRAME,
95        provided,
96    })
97}
98
99struct ParsedFrame {
100    bands: SubbandSamples,
101    consumed: usize,
102}
103
104impl ParsedFrame {
105    fn read(input: &mut impl Input) -> Result<Self> {
106        let mut header = [0; FRAME_HEADER.len()];
107        input.read_exact(&mut header)?;
108        if header != FRAME_HEADER {
109            return Err(Error::Invalid(InvalidData::FrameHeader));
110        }
111        let mut bits = BitReader::new(input);
112        let bands = quantization::read_subbands(&mut bits)?;
113        Ok(Self {
114            bands,
115            consumed: bits.consumed_bytes(),
116        })
117    }
118
119    fn synthesize(&self, synthesis: &mut Synthesis, pcm: &mut [i16; SAMPLES_PER_FRAME]) {
120        let (blocks, _) = pcm.as_chunks_mut::<32>();
121        for (band, output) in self.bands.iter().zip(blocks) {
122            synthesis.decode(band, output);
123        }
124    }
125}