use crate::{Error, Result, SampleFormat};
mod private {
use crate::{Result, SampleFormat};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Codec {
pub format: SampleFormat,
pub bits: u16,
pub container: u16,
pub shift: u32,
}
impl Codec {
pub fn new(format: SampleFormat, bits: u16, container: u16) -> Codec {
let shift = 32u32.saturating_sub(u32::from(bits));
Codec {
format,
bits,
container,
shift,
}
}
pub fn width(&self) -> usize {
usize::from(self.container)
}
}
pub trait SampleOps: Copy {
fn accepts_read(codec: &Codec) -> bool;
fn accepts_write(codec: &Codec) -> bool;
fn decode(bytes: &[u8], codec: &Codec) -> Self;
fn encode(self, codec: &Codec, out: &mut Vec<u8>) -> Result<()>;
}
}
pub(crate) use private::{Codec, SampleOps};
pub trait Sample: Copy + private::SampleOps {}
#[inline(always)]
fn decode_int(bytes: &[u8], codec: &Codec) -> i32 {
match *bytes {
[a] => i32::from((a ^ 0x80) as i8),
[a, b] => ((u32::from(u16::from_le_bytes([a, b])) << codec.shift) as i32) >> codec.shift,
[a, b, c] => ((u32::from_le_bytes([a, b, c, 0]) << codec.shift) as i32) >> codec.shift,
[a, b, c, d] => ((u32::from_le_bytes([a, b, c, d]) << codec.shift) as i32) >> codec.shift,
_ => unreachable!("PCM container width is 1, 2, 3, or 4 after validate"),
}
}
#[inline(always)]
fn encode_int(value: i32, source_bits: u16, codec: &Codec, out: &mut Vec<u8>) -> Result<()> {
if source_bits > codec.bits {
let half = 1i64 << (codec.bits - 1);
let v = i64::from(value);
if v < -half || v >= half {
return Err(Error::TooWide);
}
}
match codec.container {
1 => out.push((value as u8) ^ 0x80),
w => out.extend_from_slice(&value.to_le_bytes()[..usize::from(w)]),
}
Ok(())
}
macro_rules! int_sample {
($t:ty, $bits:expr) => {
impl SampleOps for $t {
#[inline]
fn accepts_read(codec: &Codec) -> bool {
codec.format == SampleFormat::Int && codec.bits <= $bits
}
#[inline]
fn accepts_write(codec: &Codec) -> bool {
codec.format == SampleFormat::Int
}
#[inline(always)]
fn decode(bytes: &[u8], codec: &Codec) -> Self {
decode_int(bytes, codec) as $t
}
#[inline(always)]
fn encode(self, codec: &Codec, out: &mut Vec<u8>) -> Result<()> {
encode_int(i32::from(self), $bits, codec, out)
}
}
impl Sample for $t {}
};
}
int_sample!(i8, 8);
int_sample!(i16, 16);
int_sample!(i32, 32);
impl SampleOps for f32 {
#[inline]
fn accepts_read(codec: &Codec) -> bool {
codec.format == SampleFormat::Float && codec.bits == 32
}
#[inline]
fn accepts_write(codec: &Codec) -> bool {
codec.format == SampleFormat::Float
}
#[inline(always)]
fn decode(bytes: &[u8], _codec: &Codec) -> Self {
match *bytes {
[a, b, c, d] => f32::from_le_bytes([a, b, c, d]),
_ => unreachable!("f32 files use 4-byte containers"),
}
}
#[inline(always)]
fn encode(self, codec: &Codec, out: &mut Vec<u8>) -> Result<()> {
match codec.container {
4 => out.extend_from_slice(&self.to_le_bytes()),
_ => out.extend_from_slice(&f64::from(self).to_le_bytes()),
}
Ok(())
}
}
impl Sample for f32 {}
impl SampleOps for f64 {
#[inline]
fn accepts_read(codec: &Codec) -> bool {
codec.format == SampleFormat::Float && (codec.bits == 32 || codec.bits == 64)
}
#[inline]
fn accepts_write(codec: &Codec) -> bool {
codec.format == SampleFormat::Float && codec.bits == 64
}
#[inline(always)]
fn decode(bytes: &[u8], _codec: &Codec) -> Self {
match *bytes {
[a, b, c, d] => f64::from(f32::from_le_bytes([a, b, c, d])),
[a, b, c, d, e, f, g, h] => f64::from_le_bytes([a, b, c, d, e, f, g, h]),
_ => unreachable!("float files use 4-byte or 8-byte containers"),
}
}
#[inline(always)]
fn encode(self, _codec: &Codec, out: &mut Vec<u8>) -> Result<()> {
out.extend_from_slice(&self.to_le_bytes());
Ok(())
}
}
impl Sample for f64 {}
#[inline]
pub(crate) fn decode_block<S: SampleOps>(codec: &Codec, src: &[u8], dst: &mut [S]) {
match codec.container {
1 => decode_fixed::<S, 1>(codec, src, dst),
2 => decode_fixed::<S, 2>(codec, src, dst),
3 => decode_fixed::<S, 3>(codec, src, dst),
4 => decode_fixed::<S, 4>(codec, src, dst),
8 => decode_fixed::<S, 8>(codec, src, dst),
_ => unreachable!("container width is 1, 2, 3, 4, or 8 after validate"),
}
}
#[inline(always)]
fn decode_fixed<S: SampleOps, const W: usize>(codec: &Codec, src: &[u8], dst: &mut [S]) {
for (d, c) in dst.iter_mut().zip(src.chunks_exact(W)) {
*d = S::decode(c, codec);
}
}