use crate::flac::bitstream::{BitReader, BitWriter};
use crate::flac::error::FlacError;
use crate::flac::lpc::{
fixed_predictor_residual, fixed_predictor_restore, lpc_predictor_residual,
lpc_predictor_restore,
};
use crate::flac::rice::{RiceMethod, decode_residual, encode_residual};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SubframeType {
Constant,
Verbatim,
Fixed(u8),
Lpc(u8),
}
impl SubframeType {
pub fn from_header(value: u8) -> Result<Self, FlacError> {
match value {
0b000000 => Ok(SubframeType::Constant),
0b000001 => Ok(SubframeType::Verbatim),
0b001000..=0b001100 => Ok(SubframeType::Fixed((value & 0x07) as u8)),
0b100000..=0b111111 => Ok(SubframeType::Lpc(((value & 0x1F) + 1) as u8)),
_ => Err(FlacError::InvalidSubframeType(value)),
}
}
pub fn to_header(self) -> u8 {
match self {
SubframeType::Constant => 0b000000,
SubframeType::Verbatim => 0b000001,
SubframeType::Fixed(order) => 0b001000 | (order & 0x07),
SubframeType::Lpc(order) => 0b100000 | ((order - 1) & 0x1F),
}
}
pub const fn order(self) -> usize {
match self {
SubframeType::Constant | SubframeType::Verbatim => 0,
SubframeType::Fixed(o) | SubframeType::Lpc(o) => o as usize,
}
}
}
#[derive(Debug, Clone)]
pub struct Subframe {
pub subframe_type: SubframeType,
pub wasted_bits: u8,
pub samples: Vec<i32>,
}
#[derive(Debug, Clone, Copy)]
pub struct SubframeHeader {
pub subframe_type: SubframeType,
pub wasted_bits: u8,
}
impl SubframeHeader {
pub fn decode(reader: &mut BitReader) -> Result<Self, FlacError> {
let zero = reader.read_bit()?;
if zero {
return Err(FlacError::InvalidSubframeType(0xFF));
}
let type_code = reader.read_bits(6)? as u8;
let subframe_type = SubframeType::from_header(type_code)?;
let has_wasted = reader.read_bit()?;
let wasted_bits = if has_wasted {
let k = reader.read_unary()? as u8;
k + 1
} else {
0
};
Ok(SubframeHeader {
subframe_type,
wasted_bits,
})
}
pub fn encode(&self, writer: &mut BitWriter) {
writer.write_bit(false);
writer.write_bits(self.subframe_type.to_header() as u32, 6);
if self.wasted_bits > 0 {
writer.write_bit(true);
writer.write_unary((self.wasted_bits - 1) as u32);
} else {
writer.write_bit(false);
}
}
}
pub fn decode_subframe(
reader: &mut BitReader,
block_size: usize,
bits_per_sample: u8,
) -> Result<Subframe, FlacError> {
let header = SubframeHeader::decode(reader)?;
let effective_bits = bits_per_sample - header.wasted_bits;
let samples = match header.subframe_type {
SubframeType::Constant => decode_constant(reader, block_size, effective_bits)?,
SubframeType::Verbatim => decode_verbatim(reader, block_size, effective_bits)?,
SubframeType::Fixed(order) => {
decode_fixed(reader, block_size, effective_bits, order as usize)?
}
SubframeType::Lpc(order) => decode_lpc(reader, block_size, effective_bits, order as usize)?,
};
let samples = if header.wasted_bits > 0 {
samples
.into_iter()
.map(|s| s << header.wasted_bits)
.collect()
} else {
samples
};
Ok(Subframe {
subframe_type: header.subframe_type,
wasted_bits: header.wasted_bits,
samples,
})
}
fn decode_constant(
reader: &mut BitReader,
block_size: usize,
bits_per_sample: u8,
) -> Result<Vec<i32>, FlacError> {
let value = reader.read_bits_signed(bits_per_sample)?;
Ok(vec![value; block_size])
}
fn decode_verbatim(
reader: &mut BitReader,
block_size: usize,
bits_per_sample: u8,
) -> Result<Vec<i32>, FlacError> {
let mut samples = Vec::with_capacity(block_size);
for _ in 0..block_size {
samples.push(reader.read_bits_signed(bits_per_sample)?);
}
Ok(samples)
}
fn decode_fixed(
reader: &mut BitReader,
block_size: usize,
bits_per_sample: u8,
order: usize,
) -> Result<Vec<i32>, FlacError> {
let mut warmup = Vec::with_capacity(order);
for _ in 0..order {
warmup.push(reader.read_bits_signed(bits_per_sample)?);
}
let method_code = reader.read_bits(2)? as u8;
let method = match method_code {
0b00 => RiceMethod::Rice,
0b01 => RiceMethod::Rice2,
_ => return Err(FlacError::InvalidSubframeType(method_code)),
};
let partition_order = reader.read_bits(4)? as u8;
let residuals = decode_residual(reader, method, partition_order, block_size, order)?;
fixed_predictor_restore(&warmup, &residuals, order)
}
fn decode_lpc(
reader: &mut BitReader,
block_size: usize,
bits_per_sample: u8,
order: usize,
) -> Result<Vec<i32>, FlacError> {
let mut warmup = Vec::with_capacity(order);
for _ in 0..order {
warmup.push(reader.read_bits_signed(bits_per_sample)?);
}
let qlp_precision_code = reader.read_bits(4)? as u8;
if qlp_precision_code == 0b1111 {
return Err(FlacError::InvalidQlpPrecision {
precision: qlp_precision_code,
});
}
let qlp_precision = qlp_precision_code + 1;
let qlp_shift = reader.read_bits_signed(5)? as i8;
if qlp_shift < 0 {
return Err(FlacError::InvalidLpcShift { shift: qlp_shift });
}
let mut qlp_coeffs = Vec::with_capacity(order);
for _ in 0..order {
qlp_coeffs.push(reader.read_bits_signed(qlp_precision)?);
}
let method_code = reader.read_bits(2)? as u8;
let method = match method_code {
0b00 => RiceMethod::Rice,
0b01 => RiceMethod::Rice2,
_ => return Err(FlacError::InvalidSubframeType(method_code)),
};
let partition_order = reader.read_bits(4)? as u8;
let residuals = decode_residual(reader, method, partition_order, block_size, order)?;
lpc_predictor_restore(&warmup, &residuals, &qlp_coeffs, qlp_shift)
}
#[derive(Debug)]
pub struct EncodedSubframe {
pub subframe_type: SubframeType,
pub bits: usize,
pub data: Vec<u8>,
}
pub fn encode_subframe(
samples: &[i32],
bits_per_sample: u8,
max_lpc_order: usize,
qlp_precision: u8,
min_partition_order: u8,
max_partition_order: u8,
exhaustive_rice: bool,
) -> Result<EncodedSubframe, FlacError> {
let block_size = samples.len();
let wasted_bits = compute_wasted_bits(samples);
let effective_samples: Vec<i32> = if wasted_bits > 0 {
samples.iter().map(|&s| s >> wasted_bits).collect()
} else {
samples.to_vec()
};
let effective_bits = bits_per_sample - wasted_bits;
if let Some(encoded) = try_encode_constant(&effective_samples, effective_bits, wasted_bits) {
return Ok(encoded);
}
let verbatim_bits = 8 + block_size * effective_bits as usize;
let mut best = encode_verbatim(&effective_samples, effective_bits, wasted_bits);
for order in 0..=4.min(block_size - 1) {
if let Ok(encoded) = try_encode_fixed(
&effective_samples,
effective_bits,
wasted_bits,
order,
min_partition_order,
max_partition_order,
exhaustive_rice,
) {
if encoded.bits < best.bits {
best = encoded;
}
}
}
if max_lpc_order > 0 && block_size > max_lpc_order {
if let Ok(encoded) = try_encode_lpc(
&effective_samples,
effective_bits,
wasted_bits,
max_lpc_order,
qlp_precision,
min_partition_order,
max_partition_order,
exhaustive_rice,
) {
if encoded.bits < best.bits {
best = encoded;
}
}
}
if best.bits > verbatim_bits {
best = encode_verbatim(&effective_samples, effective_bits, wasted_bits);
}
Ok(best)
}
fn compute_wasted_bits(samples: &[i32]) -> u8 {
if samples.is_empty() {
return 0;
}
let combined = samples.iter().fold(0i32, |acc, &s| acc | s);
if combined == 0 {
return 0; }
combined.trailing_zeros() as u8
}
fn try_encode_constant(
samples: &[i32],
bits_per_sample: u8,
wasted_bits: u8,
) -> Option<EncodedSubframe> {
if samples.is_empty() {
return None;
}
let first = samples[0];
if samples.iter().all(|&s| s == first) {
let mut writer = BitWriter::new();
let header = SubframeHeader {
subframe_type: SubframeType::Constant,
wasted_bits,
};
header.encode(&mut writer);
writer.write_bits_signed(first, bits_per_sample);
let data = writer.finish();
let bits = data.len() * 8;
Some(EncodedSubframe {
subframe_type: SubframeType::Constant,
bits,
data,
})
} else {
None
}
}
fn encode_verbatim(samples: &[i32], bits_per_sample: u8, wasted_bits: u8) -> EncodedSubframe {
let mut writer = BitWriter::new();
let header = SubframeHeader {
subframe_type: SubframeType::Verbatim,
wasted_bits,
};
header.encode(&mut writer);
for &sample in samples {
writer.write_bits_signed(sample, bits_per_sample);
}
let data = writer.finish();
let bits = data.len() * 8;
EncodedSubframe {
subframe_type: SubframeType::Verbatim,
bits,
data,
}
}
fn try_encode_fixed(
samples: &[i32],
bits_per_sample: u8,
wasted_bits: u8,
order: usize,
min_partition_order: u8,
max_partition_order: u8,
exhaustive_rice: bool,
) -> Result<EncodedSubframe, FlacError> {
let block_size = samples.len();
let residuals = fixed_predictor_residual(samples, order)?;
let partition_order = crate::flac::rice::find_optimal_partition_order(
&residuals,
block_size,
order,
RiceMethod::Rice,
min_partition_order,
max_partition_order,
exhaustive_rice,
);
let mut writer = BitWriter::new();
let header = SubframeHeader {
subframe_type: SubframeType::Fixed(order as u8),
wasted_bits,
};
header.encode(&mut writer);
for &sample in &samples[..order] {
writer.write_bits_signed(sample, bits_per_sample);
}
writer.write_bits(0b00, 2);
writer.write_bits(partition_order as u32, 4);
encode_residual(
&mut writer,
&residuals,
RiceMethod::Rice,
partition_order,
block_size,
order,
)?;
let data = writer.finish();
let bits = data.len() * 8;
Ok(EncodedSubframe {
subframe_type: SubframeType::Fixed(order as u8),
bits,
data,
})
}
fn try_encode_lpc(
samples: &[i32],
bits_per_sample: u8,
wasted_bits: u8,
max_order: usize,
qlp_precision: u8,
min_partition_order: u8,
max_partition_order: u8,
exhaustive_rice: bool,
) -> Result<EncodedSubframe, FlacError> {
let block_size = samples.len();
let (order, qlp_coeffs, qlp_shift) =
crate::flac::lpc::find_best_lpc_order(samples, max_order, qlp_precision, false)?;
if order == 0 {
return Err(FlacError::InvalidLpcOrder { order: 0 });
}
let residuals = lpc_predictor_residual(samples, &qlp_coeffs, qlp_shift)?;
let partition_order = crate::flac::rice::find_optimal_partition_order(
&residuals,
block_size,
order,
RiceMethod::Rice,
min_partition_order,
max_partition_order,
exhaustive_rice,
);
let mut writer = BitWriter::new();
let header = SubframeHeader {
subframe_type: SubframeType::Lpc(order as u8),
wasted_bits,
};
header.encode(&mut writer);
for &sample in &samples[..order] {
writer.write_bits_signed(sample, bits_per_sample);
}
writer.write_bits((qlp_precision - 1) as u32, 4);
writer.write_bits_signed(qlp_shift as i32, 5);
for &coeff in &qlp_coeffs {
writer.write_bits_signed(coeff, qlp_precision);
}
writer.write_bits(0b00, 2);
writer.write_bits(partition_order as u32, 4);
encode_residual(
&mut writer,
&residuals,
RiceMethod::Rice,
partition_order,
block_size,
order,
)?;
let data = writer.finish();
let bits = data.len() * 8;
Ok(EncodedSubframe {
subframe_type: SubframeType::Lpc(order as u8),
bits,
data,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_subframe_type_roundtrip() {
let types = [
SubframeType::Constant,
SubframeType::Verbatim,
SubframeType::Fixed(0),
SubframeType::Fixed(4),
SubframeType::Lpc(1),
SubframeType::Lpc(32),
];
for stype in types {
let header = stype.to_header();
let parsed = SubframeType::from_header(header).unwrap();
assert_eq!(parsed, stype, "Roundtrip failed for {:?}", stype);
}
}
#[test]
fn test_constant_subframe() {
let samples = vec![42i32; 100];
let encoded = try_encode_constant(&samples, 16, 0);
assert!(encoded.is_some(), "Should encode as constant");
let encoded = encoded.unwrap();
assert_eq!(encoded.subframe_type, SubframeType::Constant);
}
#[test]
fn test_verbatim_subframe() {
let samples: Vec<i32> = (0..100).collect();
let encoded = encode_verbatim(&samples, 16, 0);
assert_eq!(encoded.subframe_type, SubframeType::Verbatim);
}
#[test]
fn test_fixed_subframe_roundtrip() {
let samples: Vec<i32> = (0..256).map(|i| i * 10).collect();
let bits_per_sample = 16;
let encoded =
try_encode_fixed(&samples, bits_per_sample, 0, 2, 0, 4, false).expect("Encode failed");
assert_eq!(encoded.subframe_type, SubframeType::Fixed(2));
let mut reader = BitReader::new(&encoded.data);
let decoded =
decode_subframe(&mut reader, samples.len(), bits_per_sample).expect("Decode failed");
assert_eq!(decoded.samples, samples);
}
#[test]
fn test_wasted_bits() {
let samples: Vec<i32> = vec![4, 8, 12, 16, 20];
let wasted = compute_wasted_bits(&samples);
assert_eq!(wasted, 2);
let samples: Vec<i32> = vec![1, 3, 5, 7];
let wasted = compute_wasted_bits(&samples);
assert_eq!(wasted, 0);
}
#[test]
fn test_encode_subframe_auto() {
let samples: Vec<i32> = (0..256).map(|i| i * 100).collect();
let encoded = encode_subframe(&samples, 24, 0, 12, 0, 4, false).expect("Encode failed");
assert_ne!(encoded.subframe_type, SubframeType::Verbatim);
}
}