use crate::codec::Codec;
use crate::codec::CODEC_FLAC;
use crate::error::CoreError;
pub struct FlacCodec;
impl Codec for FlacCodec {
fn id(&self) -> u8 {
CODEC_FLAC
}
fn name(&self) -> &'static str {
"flac"
}
fn min_compress_size(&self) -> usize {
1024
}
fn compress(&self, plaintext: &[u8]) -> Result<Vec<u8>, CoreError> {
let params = omnizip_flac::pcm_header::parse_wav(plaintext)
.or_else(|| omnizip_flac::pcm_header::parse_aiff(plaintext))
.ok_or_else(|| CoreError::Corrupt {
reason: "flac: input is not a recognised WAV/AIFF container".into(),
})?;
let pcm_offset = pcm_payload_offset(plaintext);
let pcm = &plaintext[pcm_offset..];
let expected = params.total_bytes();
if pcm.len() < expected {
return Err(CoreError::Corrupt {
reason: format!(
"flac: PCM payload {} bytes < declared {} bytes",
pcm.len(),
expected
),
});
}
omnizip_flac::compress(&pcm[..expected], ¶ms).map_err(flac_err)
}
fn decompress(&self, compressed: &[u8], _expected_len: u32) -> Result<Vec<u8>, CoreError> {
omnizip_flac::decompress(compressed).map_err(flac_err)
}
}
fn flac_err(e: omnizip_codecs::OmnizipError) -> CoreError {
CoreError::Corrupt {
reason: format!("flac: {e}"),
}
}
fn pcm_payload_offset(input: &[u8]) -> usize {
if input.len() < 12 {
return 0;
}
if &input[0..4] == b"RIFF" && &input[8..12] == b"WAVE" {
let mut off = 12;
while off + 8 <= input.len() {
let chunk_id = &input[off..off + 4];
let size = u32::from_le_bytes([
input[off + 4],
input[off + 5],
input[off + 6],
input[off + 7],
]) as usize;
let body = off + 8;
if chunk_id == b"data" {
return body;
}
off = body + size + (size & 1);
}
}
if &input[0..4] == b"FORM" && &input[8..12] == b"AIFF" {
let mut off = 12;
while off + 8 <= input.len() {
let chunk_id = &input[off..off + 4];
let size = u32::from_be_bytes([
input[off + 4],
input[off + 5],
input[off + 6],
input[off + 7],
]) as usize;
let body = off + 8;
if chunk_id == b"SSND" {
return body + 8;
}
off = body + size + (size & 1);
}
}
0
}
#[cfg(test)]
mod tests {
use super::*;
fn make_test_wav(sample_rate: u32, channels: u8, bits: u8, frames: u32) -> Vec<u8> {
let data_size = frames as usize * channels as usize * bits as usize / 8;
let mut wav = Vec::new();
wav.extend_from_slice(b"RIFF");
wav.extend_from_slice(&(36 + data_size as u32).to_le_bytes());
wav.extend_from_slice(b"WAVE");
wav.extend_from_slice(b"fmt ");
wav.extend_from_slice(&16u32.to_le_bytes());
wav.extend_from_slice(&1u16.to_le_bytes()); wav.extend_from_slice(&[channels, 0]);
wav.extend_from_slice(&sample_rate.to_le_bytes());
wav.extend_from_slice(
&(sample_rate * u32::from(channels) * u32::from(bits) / 8).to_le_bytes(),
);
wav.extend_from_slice(&[(channels * bits / 8), 0]);
wav.extend_from_slice(&[bits, 0]);
wav.extend_from_slice(b"data");
wav.extend_from_slice(&(data_size as u32).to_le_bytes());
for i in 0..(frames as usize * channels as usize) {
let s = ((i as u32).wrapping_mul(7) & ((1u32 << bits) - 1)) as u16;
if bits == 16 {
wav.extend_from_slice(&s.to_le_bytes());
} else {
wav.push(s as u8);
}
}
wav
}
#[test]
fn round_trips_small_wav() {
let wav = make_test_wav(8000, 1, 16, 4096);
let c = FlacCodec;
let compressed = c.compress(&wav).expect("compress");
let _recovered_pcm = c.decompress(&compressed, 0).expect("decompress");
}
#[test]
fn rejects_non_wav_input() {
let c = FlacCodec;
let result = c.compress(b"not a wav file at all");
assert!(result.is_err());
}
use crate::codec::CodecTunables;
#[test]
fn flac_ignores_tunables_by_default() {
let wav = make_test_wav(44_100, 1, 16, 4096);
let plain = FlacCodec.compress(&wav).expect("plain");
let tuned = FlacCodec
.compress_with_tunables(&wav, &CodecTunables::from_quality(11))
.expect("tuned");
assert_eq!(
plain, tuned,
"default routing: tunables must not change FLAC output"
);
}
}