Skip to main content

libflo_audio/streaming/
encoder.rs

1use crate::core::{ChannelData, FloResult, FrameType, ResidualEncoding};
2use crate::lossless::Encoder;
3use crate::{compute_crc32, Reader, MAGIC};
4use alloc::vec;
5use alloc::vec::Vec;
6
7pub struct StreamingEncoder {
8    sample_rate: u32,
9    channels: u8,
10    bit_depth: u8,
11    compression_level: u8,
12    sample_buffer: Vec<f32>,
13    samples_per_frame: usize,
14    pending_frames: Vec<EncodedFrame>,
15    encoder: Encoder,
16    total_samples: u64,
17    frame_index: u32,
18}
19
20/// An encoded frame ready for transmission
21#[derive(Debug, Clone)]
22pub struct EncodedFrame {
23    /// Frame index
24    pub index: u32,
25    /// Timestamp in milliseconds
26    pub timestamp_ms: u32,
27    /// Encoded frame data
28    pub data: Vec<u8>,
29    /// Number of samples in this frame
30    pub samples: u32,
31}
32
33impl StreamingEncoder {
34    /// Create a new streaming encoder
35    pub fn new(sample_rate: u32, channels: u8, bit_depth: u8) -> Self {
36        let samples_per_frame = sample_rate as usize;
37
38        Self {
39            sample_rate,
40            channels,
41            bit_depth,
42            compression_level: 5,
43            sample_buffer: Vec::with_capacity(samples_per_frame * channels as usize * 2),
44            samples_per_frame,
45            pending_frames: Vec::new(),
46            encoder: Encoder::new(sample_rate, channels, bit_depth),
47            total_samples: 0,
48            frame_index: 0,
49        }
50    }
51
52    /// Set compression level (0-9)
53    pub fn with_compression(mut self, level: u8) -> Self {
54        self.compression_level = level.min(9);
55        self.encoder =
56            Encoder::new(self.sample_rate, self.channels, self.bit_depth).with_compression(level);
57        self
58    }
59
60    /// Get number of pending samples in buffer
61    pub fn pending_samples(&self) -> usize {
62        self.sample_buffer.len() / self.channels as usize
63    }
64
65    /// Get number of encoded frames ready
66    pub fn pending_frames(&self) -> usize {
67        self.pending_frames.len()
68    }
69
70    /// Push samples to the encoder
71    ///
72    /// Samples should be interleaved if multi-channel
73    pub fn push_samples(&mut self, samples: &[f32]) -> FloResult<()> {
74        self.sample_buffer.extend_from_slice(samples);
75        self.try_encode_frames()?;
76        Ok(())
77    }
78
79    /// Get next encoded frame if available
80    pub fn next_frame(&mut self) -> Option<EncodedFrame> {
81        if self.pending_frames.is_empty() {
82            None
83        } else {
84            Some(self.pending_frames.remove(0))
85        }
86    }
87
88    /// Flush remaining samples (may produce a partial frame)
89    pub fn flush(&mut self) -> FloResult<Option<EncodedFrame>> {
90        if self.sample_buffer.is_empty() {
91            return Ok(None);
92        }
93
94        let samples_per_channel = self.sample_buffer.len() / self.channels as usize;
95        let timestamp_ms = (self.total_samples as f64 / self.sample_rate as f64 * 1000.0) as u32;
96
97        let frame_data = self.encode_frame_data(&self.sample_buffer)?;
98
99        let encoded = EncodedFrame {
100            index: self.frame_index,
101            timestamp_ms,
102            data: frame_data,
103            samples: samples_per_channel as u32,
104        };
105
106        self.total_samples += samples_per_channel as u64;
107        self.frame_index += 1;
108        self.sample_buffer.clear();
109
110        Ok(Some(encoded))
111    }
112
113    /// Flush any remaining partial frame into the pending frame buffer.
114    ///
115    /// Returns the number of frames now ready via [`Self::next_frame`].
116    pub fn flush_into_frames(&mut self) -> FloResult<usize> {
117        if let Some(frame) = self.flush()? {
118            self.pending_frames.push(frame);
119        }
120        Ok(self.pending_frames.len())
121    }
122
123    /// Build a complete flo file from accumulated frames
124    pub fn finalize(&mut self, metadata: &[u8]) -> FloResult<Vec<u8>> {
125        if let Some(frame) = self.flush()? {
126            self.pending_frames.push(frame);
127        }
128
129        // Build TOC
130        let mut toc_data = Vec::new();
131        let num_frames = self.pending_frames.len() as u32;
132        toc_data.extend_from_slice(&num_frames.to_le_bytes());
133
134        let mut byte_offset: u64 = 0;
135        for frame in &self.pending_frames {
136            toc_data.extend_from_slice(&frame.index.to_le_bytes());
137            toc_data.extend_from_slice(&byte_offset.to_le_bytes());
138            toc_data.extend_from_slice(&(frame.data.len() as u32).to_le_bytes());
139            toc_data.extend_from_slice(&frame.timestamp_ms.to_le_bytes());
140            byte_offset += frame.data.len() as u64;
141        }
142
143        // Build DATA
144        let mut data_chunk = Vec::new();
145        for frame in &self.pending_frames {
146            data_chunk.extend_from_slice(&frame.data);
147        }
148
149        let data_crc32 = compute_crc32(&data_chunk);
150
151        let header_size: u64 = 66;
152        let toc_size = toc_data.len() as u64;
153        let data_size = data_chunk.len() as u64;
154        let extra_size: u64 = 0;
155        let meta_size = metadata.len() as u64;
156
157        let mut output = Vec::new();
158
159        // Magic
160        output.extend_from_slice(&MAGIC);
161
162        // Header
163        output.push(1); // version_major
164        output.push(2); // version_minor
165        output.extend_from_slice(&0u16.to_le_bytes()); // flags
166        output.extend_from_slice(&self.sample_rate.to_le_bytes());
167        output.push(self.channels);
168        output.push(self.bit_depth);
169        // Calculate total samples across all frames
170        let total_samples: u64 = self
171            .pending_frames
172            .iter()
173            .map(|frame| frame.samples as u64)
174            .sum();
175        output.extend_from_slice(&total_samples.to_le_bytes());
176        output.push(self.compression_level);
177        output.extend_from_slice(&[0u8; 3]); // reserved
178        output.extend_from_slice(&data_crc32.to_le_bytes());
179        output.extend_from_slice(&header_size.to_le_bytes());
180        output.extend_from_slice(&toc_size.to_le_bytes());
181        output.extend_from_slice(&data_size.to_le_bytes());
182        output.extend_from_slice(&extra_size.to_le_bytes());
183        output.extend_from_slice(&meta_size.to_le_bytes());
184
185        // TOC
186        output.extend_from_slice(&toc_data);
187
188        // DATA
189        output.extend_from_slice(&data_chunk);
190
191        // META
192        output.extend_from_slice(metadata);
193
194        self.pending_frames.clear();
195
196        Ok(output)
197    }
198
199    // ========================================================================
200    // Internal methods
201    // ========================================================================
202
203    fn try_encode_frames(&mut self) -> FloResult<()> {
204        let frame_samples = self.samples_per_frame * self.channels as usize;
205
206        while self.sample_buffer.len() >= frame_samples {
207            let frame_data: Vec<f32> = self.sample_buffer.drain(..frame_samples).collect();
208            let timestamp_ms =
209                (self.total_samples as f64 / self.sample_rate as f64 * 1000.0) as u32;
210
211            let encoded_data = self.encode_frame_data(&frame_data)?;
212
213            self.pending_frames.push(EncodedFrame {
214                index: self.frame_index,
215                timestamp_ms,
216                data: encoded_data,
217                samples: self.samples_per_frame as u32,
218            });
219
220            self.total_samples += self.samples_per_frame as u64;
221            self.frame_index += 1;
222        }
223
224        Ok(())
225    }
226
227    fn encode_frame_data(&self, samples: &[f32]) -> FloResult<Vec<u8>> {
228        let temp_flo = self.encoder.encode(samples, &[])?;
229
230        let reader = Reader::new();
231        let file = reader.read(&temp_flo)?;
232
233        if file.frames.is_empty() {
234            return Err("No frames encoded".into());
235        }
236
237        let frame = &file.frames[0];
238        let mut data = Vec::new();
239
240        // Frame header
241        data.push(frame.frame_type);
242        data.extend_from_slice(&frame.frame_samples.to_le_bytes());
243        data.push(frame.flags);
244
245        // Channel data
246        for ch in &frame.channels {
247            let ch_data = self.serialize_channel(ch, FrameType::from(frame.frame_type));
248            data.extend_from_slice(&(ch_data.len() as u32).to_le_bytes());
249            data.extend_from_slice(&ch_data);
250        }
251
252        Ok(data)
253    }
254
255    fn serialize_channel(&self, ch: &ChannelData, frame_type: FrameType) -> Vec<u8> {
256        match frame_type {
257            FrameType::Silence => vec![],
258            FrameType::Raw | FrameType::Transform => ch.residuals.clone(),
259            _ => {
260                // ALPC: order, predictor coeffs, shift bits, residual encoding, rice param, residuals
261                let mut data = Vec::new();
262                data.push(ch.predictor_coeffs.len() as u8);
263                for &coeff in &ch.predictor_coeffs {
264                    data.extend_from_slice(&coeff.to_le_bytes());
265                }
266                data.push(ch.shift_bits);
267                data.push(ch.residual_encoding as u8);
268                if ch.residual_encoding == ResidualEncoding::Rice {
269                    data.push(ch.rice_parameter);
270                }
271                data.extend_from_slice(&ch.residuals);
272                data
273            }
274        }
275    }
276}