use std::io::Cursor;
use std::marker::PhantomData;
use std::mem;
use byteorder::{BigEndian, ByteOrder, LittleEndian, WriteBytesExt};
use claxon::frame::FrameReader;
use crate::cdrom::{CD_FRAME_SIZE, CD_MAX_SECTOR_DATA, CD_MAX_SUBCODE_DATA};
use crate::compression::zlib::ZlibCodec;
use crate::compression::{
CodecImplementation, CompressionCodec, CompressionCodecType, DecompressResult,
};
use crate::error::{Error, Result};
use crate::header::CodecType;
struct FlacCodec<T: ByteOrder, const CHANNELS: usize = 2> {
buffer: Vec<i32>,
_byteorder: PhantomData<T>,
}
impl<T: ByteOrder, const CHANNELS: usize> CodecImplementation for FlacCodec<T, CHANNELS> {
fn new(hunk_bytes: u32) -> Result<Self>
where
Self: Sized,
{
if hunk_bytes % (CHANNELS * mem::size_of::<i16>()) as u32 != 0 {
return Err(Error::CodecError);
}
Ok(FlacCodec {
buffer: Vec::new(),
_byteorder: PhantomData::default(),
})
}
fn decompress(&mut self, input: &[u8], output: &mut [u8]) -> Result<DecompressResult> {
let comp_buf = Cursor::new(input);
let sample_len = output.len() / (CHANNELS * mem::size_of::<i16>());
let mut frame_read = FrameReader::new(comp_buf);
let mut cursor = Cursor::new(output);
let mut block_buf = mem::take(&mut self.buffer);
let mut samples_written = 0;
while samples_written < sample_len {
match frame_read.read_next_or_eof(block_buf) {
Ok(Some(block)) => {
#[cfg(not(feature = "nonstandard_channel_count"))]
for (l, r) in block.stereo_samples() {
cursor.write_i16::<T>(l as i16)?;
cursor.write_i16::<T>(r as i16)?;
samples_written += 1;
}
#[cfg(feature = "nonstandard_channel_count")]
for sample in 0..block.len() / block.channels() {
for channel in 0..block.channels() {
let sample_data = block.sample(channel, sample) as u16;
cursor.write_i16::<T>(sample_data as i16)?;
}
samples_written += 1;
}
block_buf = block.into_buffer();
}
_ => {
return Err(Error::DecompressionError);
}
}
}
self.buffer = block_buf;
let bytes_in = frame_read.into_inner().position();
Ok(DecompressResult::new(
samples_written * 4,
bytes_in as usize,
))
}
}
pub struct RawFlacCodec {
be: FlacCodec<BigEndian>,
le: FlacCodec<LittleEndian>,
}
impl CompressionCodec for RawFlacCodec {}
impl CompressionCodecType for RawFlacCodec {
fn codec_type(&self) -> CodecType
where
Self: Sized,
{
CodecType::FlacV5
}
}
impl CodecImplementation for RawFlacCodec {
fn new(hunk_bytes: u32) -> Result<Self> {
Ok(RawFlacCodec {
be: FlacCodec::new(hunk_bytes)?,
le: FlacCodec::new(hunk_bytes)?,
})
}
fn decompress(&mut self, input: &[u8], output: &mut [u8]) -> Result<DecompressResult> {
match input[0] {
b'L' => self.le.decompress(&input[1..], output),
b'B' => self.be.decompress(&input[1..], output),
_ => Err(Error::DecompressionError),
}
}
}
pub struct CdFlacCodec {
engine: FlacCodec<BigEndian>,
sub_engine: ZlibCodec,
buffer: Vec<u8>,
}
impl CompressionCodec for CdFlacCodec {}
impl CompressionCodecType for CdFlacCodec {
fn codec_type(&self) -> CodecType {
CodecType::FlacCdV5
}
}
impl CodecImplementation for CdFlacCodec {
fn new(hunk_size: u32) -> Result<Self>
where
Self: Sized,
{
if hunk_size % CD_FRAME_SIZE != 0 {
return Err(Error::CodecError);
}
let max_frames = hunk_size / CD_FRAME_SIZE;
let flac_data_size = max_frames * CD_MAX_SECTOR_DATA;
Ok(CdFlacCodec {
engine: FlacCodec::new(flac_data_size)?,
sub_engine: ZlibCodec::new(hunk_size)?,
buffer: vec![0u8; hunk_size as usize],
})
}
fn decompress(&mut self, input: &[u8], output: &mut [u8]) -> Result<DecompressResult> {
let total_frames = output.len() / CD_FRAME_SIZE as usize;
let frame_res = self.engine.decompress(
input,
&mut self.buffer[..total_frames * CD_MAX_SECTOR_DATA as usize],
)?;
#[cfg(feature = "want_subcode")]
let sub_res = self.sub_engine.decompress(
&input[frame_res.total_in()..],
&mut self.buffer[total_frames * CD_MAX_SECTOR_DATA as usize..]
[..total_frames * CD_MAX_SUBCODE_DATA as usize],
)?;
#[cfg(not(feature = "want_subcode"))]
let sub_res = DecompressResult::default();
for (frame_num, chunk) in self.buffer[..total_frames * CD_MAX_SECTOR_DATA as usize]
.chunks_exact(CD_MAX_SECTOR_DATA as usize)
.enumerate()
{
output[frame_num * CD_FRAME_SIZE as usize..][..CD_MAX_SECTOR_DATA as usize]
.copy_from_slice(chunk);
}
#[cfg(feature = "want_subcode")]
for (frame_num, chunk) in self.buffer[total_frames * CD_MAX_SECTOR_DATA as usize..]
.chunks_exact(CD_MAX_SUBCODE_DATA as usize)
.enumerate()
{
output[frame_num * CD_FRAME_SIZE as usize + CD_MAX_SECTOR_DATA as usize..]
[..CD_MAX_SUBCODE_DATA as usize]
.copy_from_slice(chunk);
}
Ok(frame_res + sub_res)
}
}