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