sim_lib_stream_core/packet/
pcm.rs1use sim_kernel::{Error, Expr, Result, Symbol};
11
12use crate::buffer::symbol_field;
13
14use super::{list_field, parse_string_expr, parse_string_field};
15
16#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub enum PcmSampleFormat {
19 I16,
21 F32,
23}
24
25impl PcmSampleFormat {
26 fn symbol(self) -> Symbol {
27 Symbol::qualified("pcm", self.name())
28 }
29
30 fn name(self) -> &'static str {
31 match self {
32 Self::I16 => "i16",
33 Self::F32 => "f32",
34 }
35 }
36}
37
38#[derive(Clone, Debug)]
46pub struct PcmPacket {
47 channels: usize,
48 frames: usize,
49 samples: PcmPacketSamples,
50}
51
52impl PartialEq for PcmPacket {
53 fn eq(&self, other: &Self) -> bool {
54 self.channels == other.channels
55 && self.frames == other.frames
56 && self.samples == other.samples
57 }
58}
59
60impl Eq for PcmPacket {}
61
62#[derive(Clone, Debug)]
63enum PcmPacketSamples {
64 I16(Vec<i16>),
65 F32(Vec<f32>),
66}
67
68impl PartialEq for PcmPacketSamples {
69 fn eq(&self, other: &Self) -> bool {
70 match (self, other) {
71 (Self::I16(left), Self::I16(right)) => left == right,
72 (Self::F32(left), Self::F32(right)) => {
73 left.len() == right.len()
74 && left
75 .iter()
76 .zip(right)
77 .all(|(left, right)| left.to_bits() == right.to_bits())
78 }
79 _ => false,
80 }
81 }
82}
83
84impl Eq for PcmPacketSamples {}
85
86impl PcmPacket {
87 pub fn i16(channels: usize, frames: usize, samples_i16: Vec<i16>) -> Result<Self> {
105 validate_pcm_shape(channels, frames, samples_i16.len())?;
106 Ok(Self {
107 channels,
108 frames,
109 samples: PcmPacketSamples::I16(samples_i16),
110 })
111 }
112
113 pub fn f32(channels: usize, frames: usize, samples_f32: Vec<f32>) -> Result<Self> {
118 validate_pcm_shape(channels, frames, samples_f32.len())?;
119 validate_f32_samples(&samples_f32)?;
120 Ok(Self {
121 channels,
122 frames,
123 samples: PcmPacketSamples::F32(samples_f32),
124 })
125 }
126
127 pub fn channels(&self) -> usize {
129 self.channels
130 }
131
132 pub fn frames(&self) -> usize {
134 self.frames
135 }
136
137 pub fn sample_format(&self) -> PcmSampleFormat {
139 match self.samples {
140 PcmPacketSamples::I16(_) => PcmSampleFormat::I16,
141 PcmPacketSamples::F32(_) => PcmSampleFormat::F32,
142 }
143 }
144
145 pub fn samples_i16(&self) -> &[i16] {
152 match &self.samples {
153 PcmPacketSamples::I16(samples) => samples,
154 PcmPacketSamples::F32(_) => panic!("PCM packet does not contain i16 samples"),
155 }
156 }
157
158 pub fn samples_f32(&self) -> &[f32] {
165 match &self.samples {
166 PcmPacketSamples::F32(samples) => samples,
167 PcmPacketSamples::I16(_) => panic!("PCM packet does not contain f32 samples"),
168 }
169 }
170
171 pub fn to_expr(&self) -> Expr {
173 Expr::Map(vec![
174 (
175 Expr::Symbol(Symbol::new("packet")),
176 Expr::Symbol(Symbol::qualified("stream/packet", "pcm")),
177 ),
178 (
179 Expr::Symbol(Symbol::new("channels")),
180 Expr::String(self.channels.to_string()),
181 ),
182 (
183 Expr::Symbol(Symbol::new("frames")),
184 Expr::String(self.frames.to_string()),
185 ),
186 (
187 Expr::Symbol(Symbol::new("sample-format")),
188 Expr::Symbol(self.sample_format().symbol()),
189 ),
190 (
191 Expr::Symbol(Symbol::new("samples")),
192 Expr::List(self.sample_exprs()),
193 ),
194 ])
195 }
196
197 pub(super) fn from_entries(entries: &[(Expr, Expr)]) -> Result<Self> {
198 let sample_format = symbol_field(entries, "sample-format")?;
199 let channels = parse_string_field::<usize>(entries, "channels")?;
200 let frames = parse_string_field::<usize>(entries, "frames")?;
201 match sample_format.as_qualified_str().as_str() {
202 "pcm/i16" => {
203 let samples = list_field(entries, "samples")?
204 .iter()
205 .enumerate()
206 .map(|(index, sample)| {
207 parse_string_expr::<i16>(sample, "PCM i16 sample").map_err(|err| {
208 Error::Eval(format!("invalid PCM i16 sample at {index}: {err}"))
209 })
210 })
211 .collect::<Result<Vec<_>>>()?;
212 Self::i16(channels, frames, samples)
213 }
214 "pcm/f32" => {
215 let samples = list_field(entries, "samples")?
216 .iter()
217 .enumerate()
218 .map(|(index, sample)| {
219 parse_string_expr::<f32>(sample, "PCM f32 sample").map_err(|err| {
220 Error::Eval(format!("invalid PCM f32 sample at {index}: {err}"))
221 })
222 })
223 .collect::<Result<Vec<_>>>()?;
224 Self::f32(channels, frames, samples)
225 }
226 _ => Err(Error::Eval(format!(
227 "unsupported PCM sample format {}",
228 sample_format.as_qualified_str()
229 ))),
230 }
231 }
232
233 fn sample_exprs(&self) -> Vec<Expr> {
234 match &self.samples {
235 PcmPacketSamples::I16(samples) => samples
236 .iter()
237 .map(|sample| Expr::String(sample.to_string()))
238 .collect(),
239 PcmPacketSamples::F32(samples) => samples
240 .iter()
241 .map(|sample| Expr::String(sample.to_string()))
242 .collect(),
243 }
244 }
245}
246
247fn validate_pcm_shape(channels: usize, frames: usize, samples: usize) -> Result<()> {
248 if channels == 0 {
249 return Err(Error::Eval(
250 "PCM packet channel count must be greater than zero".to_owned(),
251 ));
252 }
253 let expected = channels
254 .checked_mul(frames)
255 .ok_or_else(|| Error::Eval("PCM packet sample count overflow".to_owned()))?;
256 if samples != expected {
257 return Err(Error::Eval(format!(
258 "PCM packet sample length {samples} does not match channels {channels} * frames {frames}"
259 )));
260 }
261 Ok(())
262}
263
264fn validate_f32_samples(samples: &[f32]) -> Result<()> {
265 if let Some(index) = samples.iter().position(|sample| !sample.is_finite()) {
266 return Err(Error::Eval(format!(
267 "PCM f32 sample at {index} must be finite"
268 )));
269 }
270 Ok(())
271}