#[cfg(feature = "alloc")]
use crate::Vec;
#[cfg(feature = "std")]
use crate::{Fs, Path};
use crate::{PcmRawBuf, PcmRawError, PcmSample, PcmSpec, is, read_at, whilst, write_at};
#[doc = crate::_tags!(audio codec)]
#[doc = crate::_doc_meta!{location("media/audio")}]
#[derive(Debug)]
pub struct PcmRaw;
impl PcmRaw {
pub const fn from_bytes(bytes: &[u8], spec: PcmSpec) -> Result<PcmRawBuf<&[u8]>, PcmRawError> {
is! { !spec.is_valid(), return Err(PcmRawError::InvalidSpec) }
if spec.frames_for_data_len(bytes.len()).is_none() {
return Err(PcmRawError::InvalidDataLength);
}
Ok(PcmRawBuf::_new(bytes, spec))
}
#[cfg(feature = "alloc")]
pub fn from_vec(bytes: Vec<u8>, spec: PcmSpec) -> Result<PcmRawBuf<Vec<u8>>, PcmRawError> {
let _ = Self::from_bytes(bytes.as_slice(), spec)?; Ok(PcmRawBuf::_new(bytes, spec))
}
#[cfg(feature = "std")]
pub fn from_file<P: AsRef<Path>>(
path: P,
spec: PcmSpec,
) -> Result<PcmRawBuf<Vec<u8>>, PcmRawError> {
Self::from_vec(Fs::read(path)?, spec)
}
const fn check_decode(
bytes: &[u8],
spec: PcmSpec,
expected: PcmSample,
sample_bytes: usize,
dst_samples: usize,
) -> Result<usize, PcmRawError> {
use PcmRawError as E;
is! { !spec.is_valid(), return Err(E::InvalidSpec) }
is! { !spec.sample.eq(expected), return Err(E::MismatchedSampleFormat) }
is! { !bytes.len().is_multiple_of(sample_bytes), return Err(E::InvalidDataLength) }
is! { spec.frames_for_data_len(bytes.len()).is_none(), return Err(E::InvalidDataLength) }
let samples = bytes.len() / sample_bytes;
is! { dst_samples < samples, return Err(E::NotEnoughSpace) }
Ok(samples)
}
pub const fn decode_u8_into(
bytes: &[u8],
spec: PcmSpec,
dst: &mut [u8],
) -> Result<usize, PcmRawError> {
match Self::check_decode(bytes, spec, PcmSample::U8, 1, dst.len()) {
Ok(samples) => {
whilst! { i in 0..samples; { dst[i] = bytes[i]; }}
Ok(samples)
}
Err(err) => Err(err),
}
}
pub const fn decode_i8_into(
bytes: &[u8],
spec: PcmSpec,
dst: &mut [i8],
) -> Result<usize, PcmRawError> {
match Self::check_decode(bytes, spec, PcmSample::I8, 1, dst.len()) {
Ok(samples) => {
whilst! { i in 0..samples; { dst[i] = bytes[i] as i8; }}
Ok(samples)
}
Err(err) => Err(err),
}
}
pub const fn decode_i16_le_into(
bytes: &[u8],
spec: PcmSpec,
dst: &mut [i16],
) -> Result<usize, PcmRawError> {
match Self::check_decode(bytes, spec, PcmSample::I16, 2, dst.len()) {
Ok(samples) => {
let mut offset = 0;
whilst! { i in 0..samples; {
dst[i] = i16::from_le_bytes(read_at![bytes, +=offset, @2]);
}}
Ok(samples)
}
Err(err) => Err(err),
}
}
pub const fn decode_i24_le_into(
bytes: &[u8],
spec: PcmSpec,
dst: &mut [i32],
) -> Result<usize, PcmRawError> {
match Self::check_decode(bytes, spec, PcmSample::I24, 3, dst.len()) {
Ok(samples) => {
let mut offset = 0;
whilst! { i in 0..samples; {
let b = read_at![bytes, +=offset, @3];
let unsigned = (b[0] as i32) | ((b[1] as i32) << 8) | ((b[2] as i32) << 16);
dst[i] = is![unsigned & 0x0080_0000 != 0, unsigned | !0x00FF_FFFF, unsigned];
}}
Ok(samples)
}
Err(err) => Err(err),
}
}
pub const fn decode_i32_le_into(
bytes: &[u8],
spec: PcmSpec,
dst: &mut [i32],
) -> Result<usize, PcmRawError> {
match Self::check_decode(bytes, spec, PcmSample::I32, 4, dst.len()) {
Ok(samples) => {
let mut offset = 0;
whilst! { i in 0..samples; {
dst[i] = i32::from_le_bytes(read_at![bytes, +=offset, @4]);
}}
Ok(samples)
}
Err(err) => Err(err),
}
}
pub const fn decode_f32_le_into(
bytes: &[u8],
spec: PcmSpec,
dst: &mut [f32],
) -> Result<usize, PcmRawError> {
match Self::check_decode(bytes, spec, PcmSample::F32, 4, dst.len()) {
Ok(samples) => {
let mut offset = 0;
whilst! { i in 0..samples; {
dst[i] = f32::from_bits(u32::from_le_bytes(read_at![bytes, +=offset, @4]));
}}
Ok(samples)
}
Err(err) => Err(err),
}
}
pub const fn decode_f64_le_into(
bytes: &[u8],
spec: PcmSpec,
dst: &mut [f64],
) -> Result<usize, PcmRawError> {
match Self::check_decode(bytes, spec, PcmSample::F64, 8, dst.len()) {
Ok(samples) => {
let mut offset = 0;
whilst! { i in 0..samples; {
dst[i] = f64::from_bits(u64::from_le_bytes(read_at![bytes, +=offset, @8]));
}}
Ok(samples)
}
Err(err) => Err(err),
}
}
}
impl PcmRaw {
pub const fn written_len(spec: PcmSpec, data_len: usize) -> Result<usize, PcmRawError> {
is! { !spec.is_valid(), return Err(PcmRawError::InvalidSpec) }
if spec.frames_for_data_len(data_len).is_none() {
return Err(PcmRawError::InvalidDataLength);
}
Ok(data_len)
}
pub const fn write_into(
dst: &mut [u8],
spec: PcmSpec,
data: &[u8],
) -> Result<usize, PcmRawError> {
let len = match Self::written_len(spec, data.len()) {
Ok(len) => len,
Err(err) => return Err(err),
};
is! { dst.len() < len, return Err(PcmRawError::NotEnoughSpace) }
whilst! { i in 0..data.len(); { dst[i] = data[i]; }}
Ok(len)
}
#[cfg(feature = "alloc")]
pub fn to_vec(spec: PcmSpec, data: &[u8]) -> Result<Vec<u8>, PcmRawError> {
Self::written_len(spec, data.len())?;
Ok(data.to_vec())
}
#[cfg(feature = "std")]
pub fn to_file<P: AsRef<Path>>(path: P, spec: PcmSpec, data: &[u8]) -> Result<(), PcmRawError> {
Self::written_len(spec, data.len())?;
Fs::write(path, data)?;
Ok(())
}
const fn check_encode(
dst_len: usize,
spec: PcmSpec,
expected: PcmSample,
sample_bytes: usize,
sample_len: usize,
) -> Result<usize, PcmRawError> {
use PcmRawError as E;
is! { !spec.is_valid(), return Err(E::InvalidSpec) }
is! { !spec.sample.eq(expected), return Err(E::MismatchedSampleFormat) }
let channels = spec.channel_count();
if channels == 0 || !sample_len.is_multiple_of(channels) {
return Err(E::InvalidDataLength);
}
let Some(bytes_len) = sample_len.checked_mul(sample_bytes) else {
return Err(E::InvalidDataLength);
};
is! { spec.frames_for_data_len(bytes_len).is_none(), return Err(E::InvalidDataLength) }
is! { dst_len < bytes_len,return Err(E::NotEnoughSpace) }
Ok(bytes_len)
}
pub const fn encode_u8_into(
dst: &mut [u8],
spec: PcmSpec,
samples: &[u8],
) -> Result<usize, PcmRawError> {
match Self::check_encode(dst.len(), spec, PcmSample::U8, 1, samples.len()) {
Ok(bytes_len) => {
whilst! { i in 0..samples.len(); { dst[i] = samples[i]; }}
Ok(bytes_len)
}
Err(err) => Err(err),
}
}
pub const fn encode_i8_into(
dst: &mut [u8],
spec: PcmSpec,
samples: &[i8],
) -> Result<usize, PcmRawError> {
match Self::check_encode(dst.len(), spec, PcmSample::I8, 1, samples.len()) {
Ok(bytes_len) => {
whilst! { i in 0..samples.len(); { dst[i] = samples[i] as u8; }}
Ok(bytes_len)
}
Err(err) => Err(err),
}
}
pub const fn encode_i16_le_into(
dst: &mut [u8],
spec: PcmSpec,
samples: &[i16],
) -> Result<usize, PcmRawError> {
match Self::check_encode(dst.len(), spec, PcmSample::I16, 2, samples.len()) {
Ok(bytes_len) => {
let mut offset = 0;
whilst! { i in 0..samples.len(); {
write_at![dst, +=offset, @2 samples[i].to_le_bytes()];
}}
Ok(bytes_len)
}
Err(err) => Err(err),
}
}
pub const fn encode_i24_le_into(
dst: &mut [u8],
spec: PcmSpec,
samples: &[i32],
) -> Result<usize, PcmRawError> {
match Self::check_encode(dst.len(), spec, PcmSample::I24, 3, samples.len()) {
Ok(bytes_len) => {
let mut offset = 0;
whilst! { i in 0..samples.len(); {
let sample = samples[i];
if sample < -8_388_608 || sample > 8_388_607 {
return Err(PcmRawError::SampleOutOfRange);
}
write_at![dst, +=offset, @3 sample.to_le_bytes()];
}}
Ok(bytes_len)
}
Err(err) => Err(err),
}
}
pub const fn encode_i32_le_into(
dst: &mut [u8],
spec: PcmSpec,
samples: &[i32],
) -> Result<usize, PcmRawError> {
match Self::check_encode(dst.len(), spec, PcmSample::I32, 4, samples.len()) {
Ok(bytes_len) => {
let mut offset = 0;
whilst! { i in 0..samples.len(); {
write_at![dst, +=offset, @4 samples[i].to_le_bytes()];
}}
Ok(bytes_len)
}
Err(err) => Err(err),
}
}
pub const fn encode_f32_le_into(
dst: &mut [u8],
spec: PcmSpec,
samples: &[f32],
) -> Result<usize, PcmRawError> {
match Self::check_encode(dst.len(), spec, PcmSample::F32, 4, samples.len()) {
Ok(bytes_len) => {
let mut offset = 0;
whilst! { i in 0..samples.len(); {
write_at![dst, +=offset, @4 samples[i].to_bits().to_le_bytes()];
}}
Ok(bytes_len)
}
Err(err) => Err(err),
}
}
pub const fn encode_f64_le_into(
dst: &mut [u8],
spec: PcmSpec,
samples: &[f64],
) -> Result<usize, PcmRawError> {
match Self::check_encode(dst.len(), spec, PcmSample::F64, 8, samples.len()) {
Ok(bytes_len) => {
let mut offset = 0;
whilst! { i in 0..samples.len(); {
write_at![dst, +=offset, @8 samples[i].to_bits().to_le_bytes()];
}}
Ok(bytes_len)
}
Err(err) => Err(err),
}
}
}