1use crate::SampleFormat;
4
5use super::AudioFrame;
6
7impl AudioFrame {
8 #[must_use]
23 pub fn total_size(&self) -> usize {
24 self.planes.iter().map(Vec::len).sum()
25 }
26
27 #[must_use]
41 #[inline]
42 pub fn bytes_per_sample(&self) -> usize {
43 self.format.bytes_per_sample()
44 }
45
46 #[must_use]
57 #[inline]
58 pub fn sample_count(&self) -> usize {
59 self.samples * self.channels as usize
60 }
61
62 #[must_use]
92 #[allow(
93 clippy::cast_possible_truncation,
94 clippy::cast_precision_loss,
95 clippy::too_many_lines
96 )]
97 pub fn to_f32_interleaved(&self) -> Vec<f32> {
98 let total = self.sample_count();
99 if total == 0 {
100 return Vec::new();
101 }
102
103 match self.format {
104 SampleFormat::F32 => self.as_f32().map(<[f32]>::to_vec).unwrap_or_default(),
105 SampleFormat::F32p => {
106 let mut out = vec![0f32; total];
107 let ch_count = self.channels as usize;
108 for ch in 0..ch_count {
109 if let Some(plane) = self.channel_as_f32(ch) {
110 for (i, &s) in plane.iter().enumerate() {
111 out[i * ch_count + ch] = s;
112 }
113 }
114 }
115 out
116 }
117 SampleFormat::F64 => {
118 let bytes = self.data();
119 bytes
120 .as_chunks::<8>()
121 .0
122 .iter()
123 .map(|b| {
124 f64::from_le_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]) as f32
125 })
126 .collect()
127 }
128 SampleFormat::F64p => {
129 let mut out = vec![0f32; total];
130 let ch_count = self.channels as usize;
131 for ch in 0..ch_count {
132 if let Some(bytes) = self.channel(ch) {
133 for (i, b) in bytes.as_chunks::<8>().0.iter().enumerate() {
134 out[i * ch_count + ch] = f64::from_le_bytes([
135 b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
136 ]) as f32;
137 }
138 }
139 }
140 out
141 }
142 SampleFormat::I16 => {
143 let bytes = self.data();
144 bytes
145 .as_chunks::<2>()
146 .0
147 .iter()
148 .map(|b| f32::from(i16::from_le_bytes([b[0], b[1]])) / f32::from(i16::MAX))
149 .collect()
150 }
151 SampleFormat::I16p => {
152 let mut out = vec![0f32; total];
153 let ch_count = self.channels as usize;
154 for ch in 0..ch_count {
155 if let Some(bytes) = self.channel(ch) {
156 for (i, b) in bytes.as_chunks::<2>().0.iter().enumerate() {
157 out[i * ch_count + ch] =
158 f32::from(i16::from_le_bytes([b[0], b[1]])) / f32::from(i16::MAX);
159 }
160 }
161 }
162 out
163 }
164 SampleFormat::I32 => {
165 let bytes = self.data();
166 bytes
167 .as_chunks::<4>()
168 .0
169 .iter()
170 .map(|b| i32::from_le_bytes([b[0], b[1], b[2], b[3]]) as f32 / i32::MAX as f32)
171 .collect()
172 }
173 SampleFormat::I32p => {
174 let mut out = vec![0f32; total];
175 let ch_count = self.channels as usize;
176 for ch in 0..ch_count {
177 if let Some(bytes) = self.channel(ch) {
178 for (i, b) in bytes.as_chunks::<4>().0.iter().enumerate() {
179 out[i * ch_count + ch] = i32::from_le_bytes([b[0], b[1], b[2], b[3]])
180 as f32
181 / i32::MAX as f32;
182 }
183 }
184 }
185 out
186 }
187 SampleFormat::U8 => {
188 let bytes = self.data();
189 bytes
190 .iter()
191 .map(|&b| (f32::from(b) - 128.0) / 128.0)
192 .collect()
193 }
194 SampleFormat::U8p => {
195 let mut out = vec![0f32; total];
196 let ch_count = self.channels as usize;
197 for ch in 0..ch_count {
198 if let Some(bytes) = self.channel(ch) {
199 for (i, &b) in bytes.iter().enumerate() {
200 out[i * ch_count + ch] = (f32::from(b) - 128.0) / 128.0;
201 }
202 }
203 }
204 out
205 }
206 SampleFormat::Other(_) => Vec::new(),
207 }
208 }
209
210 #[must_use]
235 #[allow(clippy::cast_possible_truncation, clippy::too_many_lines)] pub fn to_i16_interleaved(&self) -> Vec<i16> {
237 let total = self.sample_count();
238 if total == 0 {
239 return Vec::new();
240 }
241
242 match self.format {
243 SampleFormat::I16 => self.as_i16().map(<[i16]>::to_vec).unwrap_or_default(),
244 SampleFormat::I16p => {
245 let mut out = vec![0i16; total];
246 let ch_count = self.channels as usize;
247 for ch in 0..ch_count {
248 if let Some(plane) = self.channel_as_i16(ch) {
249 for (i, &s) in plane.iter().enumerate() {
250 out[i * ch_count + ch] = s;
251 }
252 }
253 }
254 out
255 }
256 SampleFormat::F32 => {
257 let bytes = self.data();
258 bytes
259 .as_chunks::<4>()
260 .0
261 .iter()
262 .map(|b| {
263 let s = f32::from_le_bytes([b[0], b[1], b[2], b[3]]);
264 (s.clamp(-1.0, 1.0) * f32::from(i16::MAX)) as i16
265 })
266 .collect()
267 }
268 SampleFormat::F32p => {
269 let mut out = vec![0i16; total];
270 let ch_count = self.channels as usize;
271 for ch in 0..ch_count {
272 if let Some(bytes) = self.channel(ch) {
273 for (i, b) in bytes.as_chunks::<4>().0.iter().enumerate() {
274 let s = f32::from_le_bytes([b[0], b[1], b[2], b[3]]);
275 out[i * ch_count + ch] =
276 (s.clamp(-1.0, 1.0) * f32::from(i16::MAX)) as i16;
277 }
278 }
279 }
280 out
281 }
282 SampleFormat::F64 => {
283 let bytes = self.data();
284 bytes
285 .as_chunks::<8>()
286 .0
287 .iter()
288 .map(|b| {
289 let s =
290 f64::from_le_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]);
291 (s.clamp(-1.0, 1.0) * f64::from(i16::MAX)) as i16
292 })
293 .collect()
294 }
295 SampleFormat::F64p => {
296 let mut out = vec![0i16; total];
297 let ch_count = self.channels as usize;
298 for ch in 0..ch_count {
299 if let Some(bytes) = self.channel(ch) {
300 for (i, b) in bytes.as_chunks::<8>().0.iter().enumerate() {
301 let s = f64::from_le_bytes([
302 b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
303 ]);
304 out[i * ch_count + ch] =
305 (s.clamp(-1.0, 1.0) * f64::from(i16::MAX)) as i16;
306 }
307 }
308 }
309 out
310 }
311 SampleFormat::I32 => {
312 let bytes = self.data();
313 bytes
314 .as_chunks::<4>()
315 .0
316 .iter()
317 .map(|b| (i32::from_le_bytes([b[0], b[1], b[2], b[3]]) >> 16) as i16)
318 .collect()
319 }
320 SampleFormat::I32p => {
321 let mut out = vec![0i16; total];
322 let ch_count = self.channels as usize;
323 for ch in 0..ch_count {
324 if let Some(bytes) = self.channel(ch) {
325 for (i, b) in bytes.as_chunks::<4>().0.iter().enumerate() {
326 out[i * ch_count + ch] =
327 (i32::from_le_bytes([b[0], b[1], b[2], b[3]]) >> 16) as i16;
328 }
329 }
330 }
331 out
332 }
333 SampleFormat::U8 => {
334 let bytes = self.data();
335 bytes.iter().map(|&b| (i16::from(b) - 128) << 8).collect()
336 }
337 SampleFormat::U8p => {
338 let mut out = vec![0i16; total];
339 let ch_count = self.channels as usize;
340 for ch in 0..ch_count {
341 if let Some(bytes) = self.channel(ch) {
342 for (i, &b) in bytes.iter().enumerate() {
343 out[i * ch_count + ch] = (i16::from(b) - 128) << 8;
344 }
345 }
346 }
347 out
348 }
349 SampleFormat::Other(_) => Vec::new(),
350 }
351 }
352}
353
354#[cfg(test)]
355#[allow(clippy::unwrap_used)]
356mod tests {
357 use super::*;
358 use crate::{Rational, Timestamp};
359
360 #[test]
365 fn total_size_packed_should_equal_samples_times_channels_times_bytes() {
366 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
368 assert_eq!(frame.total_size(), 1024 * 2 * 4);
369
370 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
372 assert_eq!(frame.total_size(), 1024 * 4 * 2);
373 }
374
375 #[test]
376 fn bytes_per_sample_should_match_format_width() {
377 assert_eq!(
378 AudioFrame::empty(1024, 2, 48000, SampleFormat::U8)
379 .unwrap()
380 .bytes_per_sample(),
381 1
382 );
383 assert_eq!(
384 AudioFrame::empty(1024, 2, 48000, SampleFormat::I16)
385 .unwrap()
386 .bytes_per_sample(),
387 2
388 );
389 assert_eq!(
390 AudioFrame::empty(1024, 2, 48000, SampleFormat::F32)
391 .unwrap()
392 .bytes_per_sample(),
393 4
394 );
395 assert_eq!(
396 AudioFrame::empty(1024, 2, 48000, SampleFormat::F64)
397 .unwrap()
398 .bytes_per_sample(),
399 8
400 );
401 }
402
403 #[test]
404 fn sample_count_should_return_samples_times_channels() {
405 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
406 assert_eq!(frame.sample_count(), 2048);
407
408 let mono = AudioFrame::empty(512, 1, 44100, SampleFormat::I16).unwrap();
409 assert_eq!(mono.sample_count(), 512);
410 }
411
412 #[test]
417 fn to_f32_interleaved_f32p_should_transpose_to_interleaved() {
418 let left: Vec<u8> = [1.0f32, 2.0f32]
421 .iter()
422 .flat_map(|f| f.to_le_bytes())
423 .collect();
424 let right: Vec<u8> = [3.0f32, 4.0f32]
425 .iter()
426 .flat_map(|f| f.to_le_bytes())
427 .collect();
428
429 let frame = AudioFrame::new(
430 vec![left, right],
431 2,
432 2,
433 48000,
434 SampleFormat::F32p,
435 Timestamp::default(),
436 )
437 .unwrap();
438
439 let pcm = frame.to_f32_interleaved();
440 assert_eq!(pcm.len(), 4);
441 assert!((pcm[0] - 1.0).abs() < f32::EPSILON); assert!((pcm[1] - 3.0).abs() < f32::EPSILON); assert!((pcm[2] - 2.0).abs() < f32::EPSILON); assert!((pcm[3] - 4.0).abs() < f32::EPSILON); }
446
447 #[test]
448 fn to_f32_interleaved_i16p_should_scale_to_minus_one_to_one() {
449 let make_i16_bytes = |v: i16| v.to_le_bytes().to_vec();
451
452 let left: Vec<u8> = [i16::MAX, 0i16]
453 .iter()
454 .flat_map(|&v| make_i16_bytes(v))
455 .collect();
456 let right: Vec<u8> = [i16::MIN, 0i16]
457 .iter()
458 .flat_map(|&v| make_i16_bytes(v))
459 .collect();
460
461 let frame = AudioFrame::new(
462 vec![left, right],
463 2,
464 2,
465 48000,
466 SampleFormat::I16p,
467 Timestamp::default(),
468 )
469 .unwrap();
470
471 let pcm = frame.to_f32_interleaved();
472 for &s in &pcm {
475 assert!(s >= -1.001 && s <= 1.001, "out of range: {s}");
476 }
477 assert!((pcm[0] - 1.0).abs() < 0.0001); assert!((pcm[1] - (-1.0)).abs() < 0.001); }
480
481 #[test]
482 fn to_f32_interleaved_unknown_should_return_empty() {
483 let frame = AudioFrame::new(
486 vec![vec![0u8; 16]],
487 4,
488 1,
489 48000,
490 SampleFormat::Other(999),
491 Timestamp::default(),
492 )
493 .unwrap();
494 assert_eq!(frame.to_f32_interleaved(), Vec::<f32>::new());
495 }
496
497 #[test]
498 fn to_i16_interleaved_i16p_should_transpose_to_interleaved() {
499 let left: Vec<u8> = [100i16, 200i16]
500 .iter()
501 .flat_map(|v| v.to_le_bytes())
502 .collect();
503 let right: Vec<u8> = [300i16, 400i16]
504 .iter()
505 .flat_map(|v| v.to_le_bytes())
506 .collect();
507
508 let frame = AudioFrame::new(
509 vec![left, right],
510 2,
511 2,
512 48000,
513 SampleFormat::I16p,
514 Timestamp::default(),
515 )
516 .unwrap();
517
518 let pcm = frame.to_i16_interleaved();
519 assert_eq!(pcm, vec![100, 300, 200, 400]);
520 }
521
522 #[test]
523 fn to_i16_interleaved_f32_should_scale_and_clamp() {
524 let samples: &[f32] = &[1.0, -1.0, 2.0, -2.0];
526 let bytes: Vec<u8> = samples.iter().flat_map(|f| f.to_le_bytes()).collect();
527
528 let frame = AudioFrame::new(
529 vec![bytes],
530 4,
531 1,
532 48000,
533 SampleFormat::F32,
534 Timestamp::default(),
535 )
536 .unwrap();
537
538 let pcm = frame.to_i16_interleaved();
539 assert_eq!(pcm.len(), 4);
540 assert_eq!(pcm[0], i16::MAX);
541 assert_eq!(pcm[1], -i16::MAX);
542 assert_eq!(pcm[2], i16::MAX);
544 assert_eq!(pcm[3], -i16::MAX);
545 }
546
547 #[test]
548 fn audio_frame_clone_should_have_identical_data() {
549 let samples = 512;
550 let channels = 2u32;
551 let bytes_per_sample = 4; let plane_data = vec![7u8; samples * bytes_per_sample];
553 let ts = Timestamp::new(500, Rational::new(1, 1000));
554
555 let original = AudioFrame::new(
556 vec![plane_data.clone()],
557 samples,
558 channels,
559 44100,
560 SampleFormat::F32,
561 ts,
562 )
563 .unwrap();
564
565 let clone = original.clone();
566
567 assert_eq!(clone.samples(), original.samples());
568 assert_eq!(clone.channels(), original.channels());
569 assert_eq!(clone.sample_rate(), original.sample_rate());
570 assert_eq!(clone.format(), original.format());
571 assert_eq!(clone.timestamp(), original.timestamp());
572 assert_eq!(clone.num_planes(), original.num_planes());
573 assert_eq!(clone.plane(0), original.plane(0));
574 }
575}