oximedia_codec/flac/
encoder.rs1#![forbid(unsafe_code)]
14#![allow(clippy::cast_possible_truncation)]
15#![allow(clippy::cast_lossless)]
16#![allow(clippy::cast_precision_loss)]
17#![allow(clippy::cast_sign_loss)]
18
19use super::lpc::{autocorrelate, compute_residuals, levinson_durbin, quantise_coeffs};
20use super::rice::{optimal_rice_param, rice_encode};
21use crate::error::{CodecError, CodecResult};
22
23fn crc16(data: &[u8]) -> u16 {
28 const POLY: u16 = 0x8005;
29 let mut crc = 0u16;
30 for &byte in data {
31 crc ^= u16::from(byte) << 8;
32 for _ in 0..8 {
33 if crc & 0x8000 != 0 {
34 crc = (crc << 1) ^ POLY;
35 } else {
36 crc <<= 1;
37 }
38 }
39 }
40 crc
41}
42
43#[derive(Clone, Debug)]
49pub struct FlacConfig {
50 pub sample_rate: u32,
52 pub channels: u8,
54 pub bits_per_sample: u8,
56}
57
58impl FlacConfig {
59 pub const BLOCK_SIZE: usize = 4096;
61 pub const LPC_ORDER: usize = 8;
63}
64
65#[derive(Clone, Debug)]
71pub struct FlacFrame {
72 pub data: Vec<u8>,
74 pub sample_number: u64,
76 pub block_size: u32,
78}
79
80pub struct FlacEncoder {
86 config: FlacConfig,
87 samples_encoded: u64,
89}
90
91impl FlacEncoder {
92 #[must_use]
94 pub fn new(config: FlacConfig) -> Self {
95 Self {
96 config,
97 samples_encoded: 0,
98 }
99 }
100
101 pub fn stream_header(&self) -> Vec<u8> {
105 let mut out = Vec::new();
106 out.extend_from_slice(b"fLaC");
107
108 out.push(0x80); out.push(0x00);
111 out.push(0x00);
112 out.push(34); let bs = FlacConfig::BLOCK_SIZE as u16;
116 out.extend_from_slice(&bs.to_be_bytes()); out.extend_from_slice(&bs.to_be_bytes()); out.extend_from_slice(&[0, 0, 0]); out.extend_from_slice(&[0, 0, 0]); let sr = self.config.sample_rate;
123 let ch = (self.config.channels - 1) as u32;
124 let bps = (self.config.bits_per_sample - 1) as u32;
125
126 let packed = (sr << 12) | (ch << 9) | (bps << 4);
128 out.extend_from_slice(&packed.to_be_bytes()); out.extend_from_slice(&[0, 0, 0, 0]); out.extend_from_slice(&[0u8; 16]);
133
134 out
135 }
136
137 pub fn encode(&mut self, samples: &[i32]) -> CodecResult<(Vec<u8>, Vec<FlacFrame>)> {
149 let ch = self.config.channels as usize;
150 if samples.len() % ch != 0 {
151 return Err(CodecError::InvalidParameter(
152 "Sample count must be a multiple of channel count".to_string(),
153 ));
154 }
155
156 let frame_samples = samples.len() / ch;
157 let block = FlacConfig::BLOCK_SIZE;
158 let header = self.stream_header();
159 let mut frames = Vec::new();
160
161 let mut offset = 0usize;
162 while offset < frame_samples {
163 let end = (offset + block).min(frame_samples);
164 let block_len = end - offset;
165
166 let channels: Vec<Vec<i32>> = (0..ch)
168 .map(|c| (offset..end).map(|s| samples[s * ch + c]).collect())
169 .collect();
170
171 let frame = self.encode_frame(&channels, block_len, self.samples_encoded)?;
172 self.samples_encoded += block_len as u64;
173 frames.push(frame);
174
175 offset = end;
176 }
177
178 Ok((header, frames))
179 }
180
181 fn encode_frame(
183 &self,
184 channels: &[Vec<i32>],
185 block_len: usize,
186 sample_number: u64,
187 ) -> CodecResult<FlacFrame> {
188 let ch = channels.len();
189 let mut data: Vec<u8> = Vec::new();
190
191 data.push(0xFF);
193 data.push(0xF8);
194
195 data.push(0x70 | 0x09); data.push(((ch as u8 - 1) << 4) | (self.config.bits_per_sample / 4 - 1));
199
200 let sn = sample_number;
202 data.push(0xF0 | ((sn >> 18) as u8 & 0x07));
203 data.push(0x80 | ((sn >> 12) as u8 & 0x3F));
204 data.push(0x80 | ((sn >> 6) as u8 & 0x3F));
205 data.push(0x80 | (sn as u8 & 0x3F));
206
207 let bs = block_len as u16;
209 data.push((bs >> 8) as u8);
210 data.push(bs as u8);
211
212 data.push(0);
214
215 for chan in channels {
217 let subframe = self.encode_subframe(chan)?;
218 data.extend_from_slice(&subframe);
219 }
220
221 while data.len() % 2 != 0 {
223 data.push(0);
224 }
225
226 let crc = crc16(&data);
228 data.extend_from_slice(&crc.to_be_bytes());
229
230 Ok(FlacFrame {
231 data,
232 sample_number,
233 block_size: block_len as u32,
234 })
235 }
236
237 fn encode_subframe(&self, samples: &[i32]) -> CodecResult<Vec<u8>> {
239 let order = FlacConfig::LPC_ORDER.min(samples.len() / 2);
240
241 let signal_f64: Vec<f64> = samples.iter().map(|&s| s as f64).collect();
243 let ac = autocorrelate(&signal_f64, order);
244 let (float_coeffs, _) = levinson_durbin(&ac, order);
245
246 let effective_order = float_coeffs.len();
247
248 if effective_order == 0 {
250 return self.encode_verbatim(samples);
251 }
252
253 let (int_coeffs, shift) = quantise_coeffs(&float_coeffs, 15);
255
256 let residuals = compute_residuals(samples, &float_coeffs);
258
259 let k = optimal_rice_param(&residuals);
261 let rice_bytes = rice_encode(&residuals, k);
262
263 let mut sf: Vec<u8> = Vec::new();
265 let subframe_type = 0x40 | ((effective_order as u8 - 1) & 0x3F);
266 sf.push(subframe_type);
267
268 for &s in &samples[..effective_order] {
270 sf.push((s >> 8) as u8);
271 sf.push(s as u8);
272 }
273
274 sf.push(14); sf.push(shift);
277
278 for &c in &int_coeffs {
280 sf.push((c >> 8) as u8);
281 sf.push(c as u8);
282 }
283
284 sf.push(0x00);
286 sf.push(k);
287 sf.extend_from_slice(&rice_bytes);
288
289 Ok(sf)
290 }
291
292 fn encode_verbatim(&self, samples: &[i32]) -> CodecResult<Vec<u8>> {
294 let mut sf = Vec::new();
295 sf.push(0x02); for &s in samples {
297 sf.push((s >> 8) as u8);
298 sf.push(s as u8);
299 }
300 Ok(sf)
301 }
302}
303
304#[cfg(test)]
309mod tests {
310 use super::*;
311
312 fn make_encoder() -> FlacEncoder {
313 FlacEncoder::new(FlacConfig {
314 sample_rate: 44100,
315 channels: 2,
316 bits_per_sample: 16,
317 })
318 }
319
320 #[test]
321 fn test_flac_stream_header_magic() {
322 let enc = make_encoder();
323 let header = enc.stream_header();
324 assert!(header.starts_with(b"fLaC"), "Header must start with fLaC");
325 }
326
327 #[test]
328 fn test_flac_stream_header_length() {
329 let enc = make_encoder();
330 let header = enc.stream_header();
331 assert_eq!(header.len(), 42, "Stream header must be 42 bytes");
333 }
334
335 #[test]
336 fn test_flac_encode_silence() {
337 let mut enc = make_encoder();
338 let silence = vec![0i32; 4096 * 2]; let (header, frames) = enc.encode(&silence).expect("encode");
340 assert!(header.starts_with(b"fLaC"));
341 assert!(!frames.is_empty(), "Should produce at least one frame");
342 }
343
344 #[test]
345 fn test_flac_encode_ramp() {
346 let mut enc = make_encoder();
347 let ramp: Vec<i32> = (0..4096 * 2).map(|i| (i % 1000 - 500) as i32).collect();
348 let (_, frames) = enc.encode(&ramp).expect("encode ramp");
349 assert!(!frames.is_empty());
350 for frame in &frames {
351 assert!(frame.data.len() > 10, "Frame data must be non-trivial");
353 }
354 }
355
356 #[test]
357 fn test_flac_encode_wrong_channel_count_errors() {
358 let mut enc = make_encoder();
359 let result = enc.encode(&[0i32; 3]);
361 assert!(result.is_err());
362 }
363
364 #[test]
365 fn test_flac_frame_sample_number_increases() {
366 let mut enc = make_encoder();
367 let samples: Vec<i32> = vec![0i32; FlacConfig::BLOCK_SIZE * 4]; let (_, frames) = enc.encode(&samples).expect("encode");
369 if frames.len() >= 2 {
370 assert!(
371 frames[1].sample_number > frames[0].sample_number,
372 "Sample number must increase between frames"
373 );
374 }
375 }
376
377 #[test]
378 fn test_flac_encode_mono() {
379 let mut enc = FlacEncoder::new(FlacConfig {
380 sample_rate: 48000,
381 channels: 1,
382 bits_per_sample: 16,
383 });
384 let samples = vec![0i32; 2048];
385 let (header, frames) = enc.encode(&samples).expect("mono encode");
386 assert!(header.starts_with(b"fLaC"));
387 assert!(!frames.is_empty());
388 }
389
390 #[test]
391 fn test_flac_frame_has_crc() {
392 let mut enc = make_encoder();
393 let samples = vec![100i32; 512 * 2];
394 let (_, frames) = enc.encode(&samples).expect("encode");
395 assert!(!frames.is_empty());
396 let f = &frames[0].data;
398 let n = f.len();
399 assert!(n >= 2, "Frame too short");
400 let _ = (f[n - 2], f[n - 1]); }
403
404 #[test]
405 fn test_crc16_deterministic() {
406 let data = b"hello flac";
407 let c1 = crc16(data);
408 let c2 = crc16(data);
409 assert_eq!(c1, c2);
410 }
411
412 #[test]
413 fn test_crc16_sensitivity() {
414 let data1 = b"hello";
415 let data2 = b"world";
416 assert_ne!(crc16(data1), crc16(data2));
417 }
418}