use alloc::vec::Vec;
use serde::{Deserialize, Serialize};
use crate::core::sample::SampleDataType;
use super::FormatError;
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum PcmKind {
Mono8,
Mono16,
Stereo8,
Stereo16,
StereoFloat,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub struct PcmRef {
pub kind: PcmKind,
pub offset: u32,
pub len: u32,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub struct InstSampleRef {
pub instrument: u32,
pub sample: u32,
pub pcm: PcmRef,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub struct TrackSampleRef {
pub track: u32,
pub pcm: PcmRef,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub struct AssetSampleRef {
pub asset: u32,
pub pcm: PcmRef,
}
impl PcmKind {
fn of(d: &SampleDataType) -> Self {
match d {
SampleDataType::Mono8(_) => PcmKind::Mono8,
SampleDataType::Mono16(_) => PcmKind::Mono16,
SampleDataType::Stereo8(_) => PcmKind::Stereo8,
SampleDataType::Stereo16(_) => PcmKind::Stereo16,
SampleDataType::StereoFloat(_) => PcmKind::StereoFloat,
}
}
}
fn identity(d: &SampleDataType) -> usize {
match d {
SampleDataType::Mono8(v) | SampleDataType::Stereo8(v) => v.as_ptr() as usize,
SampleDataType::Mono16(v) | SampleDataType::Stereo16(v) => v.as_ptr() as usize,
SampleDataType::StereoFloat(v) => v.as_ptr() as usize,
}
}
fn to_le_bytes(d: &SampleDataType) -> Vec<u8> {
let mut out = Vec::new();
match d {
SampleDataType::Mono8(v) | SampleDataType::Stereo8(v) => {
out.extend(v.iter().map(|&x| x as u8));
}
SampleDataType::Mono16(v) | SampleDataType::Stereo16(v) => {
for &x in v.iter() {
out.extend_from_slice(&x.to_le_bytes());
}
}
SampleDataType::StereoFloat(v) => {
for &x in v.iter() {
out.extend_from_slice(&x.to_le_bytes());
}
}
}
out
}
fn from_le_bytes(kind: PcmKind, b: &[u8]) -> Result<SampleDataType, FormatError> {
Ok(match kind {
PcmKind::Mono8 => SampleDataType::Mono8(b.iter().map(|&x| x as i8).collect()),
PcmKind::Stereo8 => SampleDataType::Stereo8(b.iter().map(|&x| x as i8).collect()),
PcmKind::Mono16 | PcmKind::Stereo16 => {
if !b.len().is_multiple_of(2) {
return Err(FormatError::Pcm("16-bit blob length not a multiple of 2"));
}
let it = b.chunks_exact(2).map(|c| i16::from_le_bytes([c[0], c[1]]));
match kind {
PcmKind::Mono16 => SampleDataType::Mono16(it.collect()),
_ => SampleDataType::Stereo16(it.collect()),
}
}
PcmKind::StereoFloat => {
if !b.len().is_multiple_of(4) {
return Err(FormatError::Pcm("f32 blob length not a multiple of 4"));
}
SampleDataType::StereoFloat(
b.chunks_exact(4)
.map(|c| f32::from_le_bytes([c[0], c[1], c[2], c[3]]))
.collect(),
)
}
})
}
#[derive(Default)]
pub struct PcmRegion {
bytes: Vec<u8>,
seen: Vec<(usize, PcmKind, PcmRef)>,
}
impl PcmRegion {
pub fn intern(&mut self, d: &SampleDataType) -> PcmRef {
let id = identity(d);
let kind = PcmKind::of(d);
if let Some((_, _, r)) = self.seen.iter().find(|(i, k, _)| *i == id && *k == kind) {
return *r;
}
if let Some((_, _, r)) = self.seen.iter().find(|(i, _, _)| *i == id) {
let r = PcmRef { kind, ..*r };
self.seen.push((id, kind, r));
return r;
}
let raw = to_le_bytes(d);
let r = PcmRef {
kind,
offset: self.bytes.len() as u32,
len: raw.len() as u32,
};
self.bytes.extend_from_slice(&raw);
let pad = (8 - (self.bytes.len() % 8)) % 8;
self.bytes.resize(self.bytes.len() + pad, 0);
self.seen.push((id, kind, r));
r
}
pub fn is_empty(&self) -> bool {
self.bytes.is_empty()
}
pub fn into_bytes(self) -> Vec<u8> {
self.bytes
}
}
pub struct PcmResolver<'a> {
region: &'a [u8],
cache: Vec<((u32, PcmKind), SampleDataType)>,
}
impl<'a> PcmResolver<'a> {
pub fn new(region: &'a [u8]) -> Self {
Self {
region,
cache: Vec::new(),
}
}
pub fn resolve(&mut self, r: PcmRef) -> Result<SampleDataType, FormatError> {
if let Some((_, d)) = self.cache.iter().find(|(k, _)| *k == (r.offset, r.kind)) {
return Ok(d.clone());
}
let start = r.offset as usize;
let end = start
.checked_add(r.len as usize)
.ok_or(FormatError::Pcm("blob span overflows"))?;
if end > self.region.len() {
return Err(FormatError::Pcm("blob span past end of PCM region"));
}
let d = from_le_bytes(r.kind, &self.region[start..end])?;
self.cache.push(((r.offset, r.kind), d.clone()));
Ok(d)
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::sync::Arc;
#[test]
fn round_trips_each_kind_bit_exact() {
let m8 = SampleDataType::Mono8(alloc::vec![-128i8, 0, 127].into());
let m16 = SampleDataType::Mono16(alloc::vec![-32768i16, 0, 32767].into());
let sf = SampleDataType::StereoFloat(alloc::vec![-1.0f32, 0.0, 0.5, f32::NAN].into());
for d in [m8, m16, sf] {
let mut region = PcmRegion::default();
let r = region.intern(&d);
let bytes = region.into_bytes();
let mut res = PcmResolver::new(&bytes);
let back = res.resolve(r).unwrap();
assert_eq!(to_le_bytes(&d), to_le_bytes(&back));
}
}
#[test]
fn shared_arc_interns_once() {
let arc: Arc<[i16]> = alloc::vec![1i16, 2, 3, 4].into();
let a = SampleDataType::Mono16(arc.clone());
let b = SampleDataType::Mono16(arc); let mut region = PcmRegion::default();
let ra = region.intern(&a);
let rb = region.intern(&b);
assert_eq!(ra, rb, "shared Arc must intern to one blob");
assert_eq!(
region.into_bytes().len(),
8,
"one 8-byte blob, no duplicate"
);
}
#[test]
fn one_buffer_read_two_ways_keeps_both_kinds() {
let arc: Arc<[i16]> = alloc::vec![1i16, 2, 3, 4].into();
let mono = SampleDataType::Mono16(arc.clone());
let stereo = SampleDataType::Stereo16(arc);
let mut region = PcmRegion::default();
let rm = region.intern(&mono);
let rs = region.intern(&stereo);
assert_eq!(rm.offset, rs.offset, "one shared buffer, one stored blob");
assert_eq!((rm.kind, rs.kind), (PcmKind::Mono16, PcmKind::Stereo16));
assert_eq!(region.bytes.len(), 8, "no duplicate copy of the payload");
let bytes = region.into_bytes();
let mut res = PcmResolver::new(&bytes);
assert!(matches!(
res.resolve(rm).unwrap(),
SampleDataType::Mono16(_)
));
assert!(matches!(
res.resolve(rs).unwrap(),
SampleDataType::Stereo16(_)
));
}
#[test]
fn distinct_blobs_are_eight_aligned() {
let a = SampleDataType::Mono8(alloc::vec![1i8, 2, 3].into()); let b = SampleDataType::Mono8(alloc::vec![4i8, 5].into());
let mut region = PcmRegion::default();
let _ = region.intern(&a);
let rb = region.intern(&b);
assert_eq!(rb.offset, 8, "second blob starts 8-aligned after padding");
}
}