use sim_kernel::{Error, Result};
use sim_lib_stream_core::PcmPacket;
use crate::{PcmSampleFormat, PcmSpec};
#[derive(Clone, Debug)]
pub struct PcmBuffer {
spec: PcmSpec,
frames: usize,
samples: PcmBufferSamples,
}
impl PartialEq for PcmBuffer {
fn eq(&self, other: &Self) -> bool {
self.spec == other.spec && self.frames == other.frames && self.samples == other.samples
}
}
impl Eq for PcmBuffer {}
#[derive(Clone, Debug)]
enum PcmBufferSamples {
I16(Vec<i16>),
F32(Vec<f32>),
}
impl PartialEq for PcmBufferSamples {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::I16(left), Self::I16(right)) => left == right,
(Self::F32(left), Self::F32(right)) => {
left.len() == right.len()
&& left
.iter()
.zip(right)
.all(|(left, right)| left.to_bits() == right.to_bits())
}
_ => false,
}
}
}
impl Eq for PcmBufferSamples {}
impl PcmBuffer {
pub fn i16(spec: PcmSpec, frames: usize, samples_i16: Vec<i16>) -> Result<Self> {
if spec.sample_format() != PcmSampleFormat::I16 {
return Err(Error::Eval(
"PCM buffer requires an i16 PCM spec".to_owned(),
));
}
let expected = expected_samples(spec.channels(), frames)?;
if samples_i16.len() != expected {
return Err(Error::Eval(format!(
"PCM buffer sample length {} does not match channels {} * frames {}",
samples_i16.len(),
spec.channels(),
frames
)));
}
Ok(Self {
spec,
frames,
samples: PcmBufferSamples::I16(samples_i16),
})
}
pub fn f32(spec: PcmSpec, frames: usize, samples_f32: Vec<f32>) -> Result<Self> {
if spec.sample_format() != PcmSampleFormat::F32 {
return Err(Error::Eval(
"PCM buffer requires an f32 PCM spec".to_owned(),
));
}
let expected = expected_samples(spec.channels(), frames)?;
if samples_f32.len() != expected {
return Err(Error::Eval(format!(
"PCM buffer sample length {} does not match channels {} * frames {}",
samples_f32.len(),
spec.channels(),
frames
)));
}
validate_f32_samples(&samples_f32)?;
Ok(Self {
spec,
frames,
samples: PcmBufferSamples::F32(samples_f32),
})
}
pub fn from_packet(spec: PcmSpec, packet: &PcmPacket) -> Result<Self> {
if packet.channels() != spec.channels() {
return Err(Error::Eval(format!(
"PCM packet channel count {} does not match sink spec {}",
packet.channels(),
spec.channels()
)));
}
if packet.sample_format() != spec.sample_format() {
return Err(Error::Eval(format!(
"PCM packet sample format {:?} does not match sink spec {:?}",
packet.sample_format(),
spec.sample_format()
)));
}
match spec.sample_format() {
PcmSampleFormat::I16 => Self::i16(spec, packet.frames(), packet.samples_i16().to_vec()),
PcmSampleFormat::F32 => Self::f32(spec, packet.frames(), packet.samples_f32().to_vec()),
}
}
pub fn to_packet(&self) -> Result<PcmPacket> {
match &self.samples {
PcmBufferSamples::I16(samples) => {
PcmPacket::i16(self.spec.channels(), self.frames, samples.clone())
}
PcmBufferSamples::F32(samples) => {
PcmPacket::f32(self.spec.channels(), self.frames, samples.clone())
}
}
}
pub fn spec(&self) -> PcmSpec {
self.spec
}
pub fn frames(&self) -> usize {
self.frames
}
pub fn samples_i16(&self) -> &[i16] {
match &self.samples {
PcmBufferSamples::I16(samples) => samples,
PcmBufferSamples::F32(_) => panic!("PCM buffer does not contain i16 samples"),
}
}
pub fn samples_f32(&self) -> &[f32] {
match &self.samples {
PcmBufferSamples::F32(samples) => samples,
PcmBufferSamples::I16(_) => panic!("PCM buffer does not contain f32 samples"),
}
}
}
pub fn i16_sample_to_f32(sample: i16) -> f32 {
if sample < 0 {
f32::from(sample) / 32768.0
} else {
f32::from(sample) / f32::from(i16::MAX)
}
}
pub fn f32_sample_to_i16(sample: f32) -> Result<i16> {
if !sample.is_finite() {
return Err(Error::Eval("PCM f32 sample must be finite".to_owned()));
}
let clamped = sample.clamp(-1.0, 1.0);
if clamped < 0.0 {
Ok((clamped * 32768.0).round().max(f32::from(i16::MIN)) as i16)
} else {
Ok((clamped * f32::from(i16::MAX))
.round()
.min(f32::from(i16::MAX)) as i16)
}
}
pub fn i16_samples_to_f32(samples: &[i16]) -> Vec<f32> {
samples
.iter()
.map(|sample| i16_sample_to_f32(*sample))
.collect()
}
pub fn f32_samples_to_i16(samples: &[f32]) -> Result<Vec<i16>> {
samples
.iter()
.map(|sample| f32_sample_to_i16(*sample))
.collect()
}
pub fn i16_interleaved_to_planar(samples: &[i16], channels: usize) -> Result<Vec<Vec<i16>>> {
interleaved_to_planar(samples, channels)
}
pub fn i16_planar_to_interleaved(channels: &[Vec<i16>]) -> Result<Vec<i16>> {
planar_to_interleaved(channels)
}
pub fn f32_interleaved_to_planar(samples: &[f32], channels: usize) -> Result<Vec<Vec<f32>>> {
validate_f32_samples(samples)?;
interleaved_to_planar(samples, channels)
}
pub fn f32_planar_to_interleaved(channels: &[Vec<f32>]) -> Result<Vec<f32>> {
let samples = planar_to_interleaved(channels)?;
validate_f32_samples(&samples)?;
Ok(samples)
}
fn expected_samples(channels: usize, frames: usize) -> Result<usize> {
channels
.checked_mul(frames)
.ok_or_else(|| Error::Eval("PCM buffer sample count overflow".to_owned()))
}
fn validate_f32_samples(samples: &[f32]) -> Result<()> {
if let Some(index) = samples.iter().position(|sample| !sample.is_finite()) {
return Err(Error::Eval(format!(
"PCM f32 sample at {index} must be finite"
)));
}
Ok(())
}
fn interleaved_to_planar<T>(samples: &[T], channels: usize) -> Result<Vec<Vec<T>>>
where
T: Copy,
{
if channels == 0 {
return Err(Error::Eval(
"PCM channel count must be greater than zero".to_owned(),
));
}
if !samples.len().is_multiple_of(channels) {
return Err(Error::Eval(format!(
"PCM interleaved sample length {} is not divisible by channels {}",
samples.len(),
channels
)));
}
let frames = samples.len() / channels;
let mut planar = vec![Vec::with_capacity(frames); channels];
for frame in samples.chunks(channels) {
for (channel, sample) in frame.iter().enumerate() {
planar[channel].push(*sample);
}
}
Ok(planar)
}
fn planar_to_interleaved<T>(channels: &[Vec<T>]) -> Result<Vec<T>>
where
T: Copy,
{
let Some(first) = channels.first() else {
return Err(Error::Eval(
"PCM planar data must contain at least one channel".to_owned(),
));
};
let frames = first.len();
if let Some((index, channel)) = channels
.iter()
.enumerate()
.find(|(_, channel)| channel.len() != frames)
{
return Err(Error::Eval(format!(
"PCM planar channel {index} length {} does not match frame count {frames}",
channel.len()
)));
}
let mut interleaved = Vec::with_capacity(frames * channels.len());
for frame in 0..frames {
for channel in channels {
interleaved.push(channel[frame]);
}
}
Ok(interleaved)
}