ff_format/frame/audio.rs
1//! Audio frame type.
2//!
3//! This module provides [`AudioFrame`] for working with decoded audio frames.
4//!
5//! # Examples
6//!
7//! ```
8//! use ff_format::{AudioFrame, SampleFormat, Rational, Timestamp};
9//!
10//! // Create a stereo F32 audio frame with 1024 samples
11//! let channels = 2u32;
12//! let samples = 1024usize;
13//! let sample_rate = 48000u32;
14//!
15//! let frame = AudioFrame::empty(
16//! samples,
17//! channels,
18//! sample_rate,
19//! SampleFormat::F32,
20//! ).unwrap();
21//!
22//! assert_eq!(frame.samples(), 1024);
23//! assert_eq!(frame.channels(), 2);
24//! assert_eq!(frame.sample_rate(), 48000);
25//! assert!(!frame.format().is_planar());
26//! ```
27
28use std::fmt;
29use std::time::Duration;
30
31use crate::error::FrameError;
32use crate::{SampleFormat, Timestamp};
33
34/// A decoded audio frame.
35///
36/// This structure holds audio sample data and metadata for a segment of audio.
37/// It supports both packed (interleaved) formats where all channels are
38/// interleaved in a single buffer, and planar formats where each channel
39/// is stored in a separate buffer.
40///
41/// # Memory Layout
42///
43/// For packed (interleaved) formats (I16, F32, etc.):
44/// - Single plane containing interleaved samples: L R L R L R ...
45/// - Total size: `samples * channels * bytes_per_sample`
46///
47/// For planar formats (I16p, F32p, etc.):
48/// - One plane per channel
49/// - Each plane size: `samples * bytes_per_sample`
50///
51/// # Examples
52///
53/// ```
54/// use ff_format::{AudioFrame, SampleFormat, Timestamp, Rational};
55///
56/// // Create a stereo F32 frame with 1024 samples at 48kHz
57/// let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
58///
59/// assert_eq!(frame.samples(), 1024);
60/// assert_eq!(frame.channels(), 2);
61/// assert_eq!(frame.sample_rate(), 48000);
62/// assert_eq!(frame.format(), SampleFormat::F32);
63///
64/// // Duration of this frame: 1024 / 48000 ≈ 21.33ms
65/// let duration = frame.duration();
66/// assert!((duration.as_secs_f64() - 0.02133).abs() < 0.001);
67/// ```
68#[derive(Clone)]
69pub struct AudioFrame {
70 /// Sample data for each plane (1 for packed, channels for planar)
71 planes: Vec<Vec<u8>>,
72 /// Number of samples per channel
73 samples: usize,
74 /// Number of audio channels
75 channels: u32,
76 /// Sample rate in Hz
77 sample_rate: u32,
78 /// Sample format
79 format: SampleFormat,
80 /// Presentation timestamp
81 timestamp: Timestamp,
82}
83
84impl AudioFrame {
85 /// Creates a new audio frame with the specified parameters.
86 ///
87 /// # Arguments
88 ///
89 /// * `planes` - Audio sample data (1 plane for packed, channels for planar)
90 /// * `samples` - Number of samples per channel
91 /// * `channels` - Number of audio channels
92 /// * `sample_rate` - Sample rate in Hz
93 /// * `format` - Sample format
94 /// * `timestamp` - Presentation timestamp
95 ///
96 /// # Errors
97 ///
98 /// Returns [`FrameError::InvalidPlaneCount`] if the number of planes doesn't
99 /// match the format (1 for packed, channels for planar).
100 ///
101 /// # Examples
102 ///
103 /// ```
104 /// use ff_format::{AudioFrame, SampleFormat, Timestamp};
105 ///
106 /// // Create a mono F32 frame with 1024 samples
107 /// let samples = 1024;
108 /// let bytes_per_sample = 4; // F32
109 /// let data = vec![0u8; samples * bytes_per_sample];
110 ///
111 /// let frame = AudioFrame::new(
112 /// vec![data],
113 /// samples,
114 /// 1,
115 /// 48000,
116 /// SampleFormat::F32,
117 /// Timestamp::default(),
118 /// ).unwrap();
119 ///
120 /// assert_eq!(frame.samples(), 1024);
121 /// assert_eq!(frame.channels(), 1);
122 /// ```
123 pub fn new(
124 planes: Vec<Vec<u8>>,
125 samples: usize,
126 channels: u32,
127 sample_rate: u32,
128 format: SampleFormat,
129 timestamp: Timestamp,
130 ) -> Result<Self, FrameError> {
131 let expected_planes = if format.is_planar() {
132 channels as usize
133 } else {
134 1
135 };
136
137 if planes.len() != expected_planes {
138 return Err(FrameError::InvalidPlaneCount {
139 expected: expected_planes,
140 actual: planes.len(),
141 });
142 }
143
144 Ok(Self {
145 planes,
146 samples,
147 channels,
148 sample_rate,
149 format,
150 timestamp,
151 })
152 }
153
154 /// Creates an empty audio frame with the specified parameters.
155 ///
156 /// The frame will have properly sized planes filled with zeros.
157 ///
158 /// # Arguments
159 ///
160 /// * `samples` - Number of samples per channel
161 /// * `channels` - Number of audio channels
162 /// * `sample_rate` - Sample rate in Hz
163 /// * `format` - Sample format
164 ///
165 /// # Errors
166 ///
167 /// Returns [`FrameError::UnsupportedSampleFormat`] if the format is
168 /// [`SampleFormat::Other`], as the memory layout cannot be determined.
169 ///
170 /// # Examples
171 ///
172 /// ```
173 /// use ff_format::{AudioFrame, SampleFormat};
174 ///
175 /// // Create a stereo I16 frame
176 /// let frame = AudioFrame::empty(1024, 2, 44100, SampleFormat::I16).unwrap();
177 /// assert_eq!(frame.samples(), 1024);
178 /// assert_eq!(frame.channels(), 2);
179 /// assert_eq!(frame.num_planes(), 1); // Packed format
180 /// ```
181 pub fn empty(
182 samples: usize,
183 channels: u32,
184 sample_rate: u32,
185 format: SampleFormat,
186 ) -> Result<Self, FrameError> {
187 if matches!(format, SampleFormat::Other(_)) {
188 return Err(FrameError::UnsupportedSampleFormat(format));
189 }
190
191 let planes = Self::allocate_planes(samples, channels, format);
192
193 Ok(Self {
194 planes,
195 samples,
196 channels,
197 sample_rate,
198 format,
199 timestamp: Timestamp::default(),
200 })
201 }
202
203 /// Allocates planes for the given parameters.
204 fn allocate_planes(samples: usize, channels: u32, format: SampleFormat) -> Vec<Vec<u8>> {
205 let bytes_per_sample = format.bytes_per_sample();
206
207 if format.is_planar() {
208 // Planar: one plane per channel
209 let plane_size = samples * bytes_per_sample;
210 (0..channels).map(|_| vec![0u8; plane_size]).collect()
211 } else {
212 // Packed: single plane with interleaved samples
213 let total_size = samples * channels as usize * bytes_per_sample;
214 vec![vec![0u8; total_size]]
215 }
216 }
217
218 // ==========================================================================
219 // Metadata Accessors
220 // ==========================================================================
221
222 /// Returns the number of samples per channel.
223 ///
224 /// # Examples
225 ///
226 /// ```
227 /// use ff_format::{AudioFrame, SampleFormat};
228 ///
229 /// let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
230 /// assert_eq!(frame.samples(), 1024);
231 /// ```
232 #[must_use]
233 #[inline]
234 pub const fn samples(&self) -> usize {
235 self.samples
236 }
237
238 /// Returns the number of audio channels.
239 ///
240 /// # Examples
241 ///
242 /// ```
243 /// use ff_format::{AudioFrame, SampleFormat};
244 ///
245 /// let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
246 /// assert_eq!(frame.channels(), 2);
247 /// ```
248 #[must_use]
249 #[inline]
250 pub const fn channels(&self) -> u32 {
251 self.channels
252 }
253
254 /// Returns the sample rate in Hz.
255 ///
256 /// # Examples
257 ///
258 /// ```
259 /// use ff_format::{AudioFrame, SampleFormat};
260 ///
261 /// let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
262 /// assert_eq!(frame.sample_rate(), 48000);
263 /// ```
264 #[must_use]
265 #[inline]
266 pub const fn sample_rate(&self) -> u32 {
267 self.sample_rate
268 }
269
270 /// Returns the sample format.
271 ///
272 /// # Examples
273 ///
274 /// ```
275 /// use ff_format::{AudioFrame, SampleFormat};
276 ///
277 /// let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
278 /// assert_eq!(frame.format(), SampleFormat::F32);
279 /// assert!(frame.format().is_float());
280 /// ```
281 #[must_use]
282 #[inline]
283 pub const fn format(&self) -> SampleFormat {
284 self.format
285 }
286
287 /// Returns the presentation timestamp.
288 ///
289 /// # Examples
290 ///
291 /// ```
292 /// use ff_format::{AudioFrame, SampleFormat, Timestamp, Rational};
293 ///
294 /// let ts = Timestamp::new(48000, Rational::new(1, 48000));
295 /// let mut frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
296 /// frame.set_timestamp(ts);
297 /// assert_eq!(frame.timestamp(), ts);
298 /// ```
299 #[must_use]
300 #[inline]
301 pub const fn timestamp(&self) -> Timestamp {
302 self.timestamp
303 }
304
305 /// Sets the presentation timestamp.
306 ///
307 /// # Examples
308 ///
309 /// ```
310 /// use ff_format::{AudioFrame, SampleFormat, Timestamp, Rational};
311 ///
312 /// let mut frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
313 /// let ts = Timestamp::new(48000, Rational::new(1, 48000));
314 /// frame.set_timestamp(ts);
315 /// assert_eq!(frame.timestamp(), ts);
316 /// ```
317 #[inline]
318 pub fn set_timestamp(&mut self, timestamp: Timestamp) {
319 self.timestamp = timestamp;
320 }
321
322 /// Returns the duration of this audio frame.
323 ///
324 /// The duration is calculated as `samples / sample_rate`.
325 ///
326 /// # Examples
327 ///
328 /// ```
329 /// use ff_format::{AudioFrame, SampleFormat};
330 ///
331 /// // 1024 samples at 48kHz = ~21.33ms
332 /// let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
333 /// let duration = frame.duration();
334 /// assert!((duration.as_secs_f64() - 0.02133).abs() < 0.001);
335 ///
336 /// // 48000 samples at 48kHz = 1 second
337 /// let frame = AudioFrame::empty(48000, 2, 48000, SampleFormat::F32).unwrap();
338 /// assert_eq!(frame.duration().as_secs(), 1);
339 /// ```
340 #[must_use]
341 #[allow(clippy::cast_precision_loss)] // Audio frame sample counts are well within f64's precision
342 pub fn duration(&self) -> Duration {
343 if self.sample_rate == 0 {
344 return Duration::ZERO;
345 }
346 let secs = self.samples as f64 / f64::from(self.sample_rate);
347 Duration::from_secs_f64(secs)
348 }
349
350 // ==========================================================================
351 // Plane Data Access
352 // ==========================================================================
353
354 /// Returns the number of planes in this frame.
355 ///
356 /// - Packed formats: 1 plane (interleaved channels)
357 /// - Planar formats: 1 plane per channel
358 ///
359 /// # Examples
360 ///
361 /// ```
362 /// use ff_format::{AudioFrame, SampleFormat};
363 ///
364 /// // Packed format - 1 plane
365 /// let packed = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
366 /// assert_eq!(packed.num_planes(), 1);
367 ///
368 /// // Planar format - 1 plane per channel
369 /// let planar = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
370 /// assert_eq!(planar.num_planes(), 2);
371 /// ```
372 #[must_use]
373 #[inline]
374 pub fn num_planes(&self) -> usize {
375 self.planes.len()
376 }
377
378 /// Returns a slice of all plane data.
379 ///
380 /// # Examples
381 ///
382 /// ```
383 /// use ff_format::{AudioFrame, SampleFormat};
384 ///
385 /// let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
386 /// let planes = frame.planes();
387 /// assert_eq!(planes.len(), 2);
388 /// ```
389 #[must_use]
390 #[inline]
391 pub fn planes(&self) -> &[Vec<u8>] {
392 &self.planes
393 }
394
395 /// Returns the data for a specific plane, or `None` if the index is out of bounds.
396 ///
397 /// For packed formats, use `plane(0)`. For planar formats, use `plane(channel_index)`.
398 ///
399 /// # Arguments
400 ///
401 /// * `index` - The plane index (0 for packed, channel index for planar)
402 ///
403 /// # Examples
404 ///
405 /// ```
406 /// use ff_format::{AudioFrame, SampleFormat};
407 ///
408 /// let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
409 ///
410 /// // Access left channel (plane 0)
411 /// assert!(frame.plane(0).is_some());
412 ///
413 /// // Access right channel (plane 1)
414 /// assert!(frame.plane(1).is_some());
415 ///
416 /// // No third channel
417 /// assert!(frame.plane(2).is_none());
418 /// ```
419 #[must_use]
420 #[inline]
421 pub fn plane(&self, index: usize) -> Option<&[u8]> {
422 self.planes.get(index).map(Vec::as_slice)
423 }
424
425 /// Returns mutable access to a specific plane's data.
426 ///
427 /// # Arguments
428 ///
429 /// * `index` - The plane index
430 ///
431 /// # Examples
432 ///
433 /// ```
434 /// use ff_format::{AudioFrame, SampleFormat};
435 ///
436 /// let mut frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
437 /// if let Some(data) = frame.plane_mut(0) {
438 /// // Modify left channel
439 /// data[0] = 128;
440 /// }
441 /// ```
442 #[must_use]
443 #[inline]
444 pub fn plane_mut(&mut self, index: usize) -> Option<&mut [u8]> {
445 self.planes.get_mut(index).map(Vec::as_mut_slice)
446 }
447
448 /// Returns the channel data for planar formats.
449 ///
450 /// This is an alias for [`plane()`](Self::plane) that's more semantically
451 /// meaningful for audio data.
452 ///
453 /// # Arguments
454 ///
455 /// * `channel` - The channel index (0 = left, 1 = right, etc.)
456 ///
457 /// # Examples
458 ///
459 /// ```
460 /// use ff_format::{AudioFrame, SampleFormat};
461 ///
462 /// let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
463 ///
464 /// // Get left channel data
465 /// let left = frame.channel(0).unwrap();
466 /// assert_eq!(left.len(), 1024 * 4); // 1024 samples * 4 bytes
467 /// ```
468 #[must_use]
469 #[inline]
470 pub fn channel(&self, channel: usize) -> Option<&[u8]> {
471 self.plane(channel)
472 }
473
474 /// Returns mutable access to channel data for planar formats.
475 ///
476 /// # Arguments
477 ///
478 /// * `channel` - The channel index
479 #[must_use]
480 #[inline]
481 pub fn channel_mut(&mut self, channel: usize) -> Option<&mut [u8]> {
482 self.plane_mut(channel)
483 }
484
485 // ==========================================================================
486 // Contiguous Data Access
487 // ==========================================================================
488
489 /// Returns the raw sample data as a contiguous byte slice.
490 ///
491 /// For packed formats, this returns a reference to the single plane.
492 /// For planar formats, this returns `None` (use [`channel()`](Self::channel) instead).
493 ///
494 /// # Examples
495 ///
496 /// ```
497 /// use ff_format::{AudioFrame, SampleFormat};
498 ///
499 /// // Packed format - returns data
500 /// let packed = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
501 /// assert!(packed.data().is_some());
502 /// assert_eq!(packed.data().unwrap().len(), 1024 * 2 * 4);
503 ///
504 /// // Planar format - returns None
505 /// let planar = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
506 /// assert!(planar.data().is_none());
507 /// ```
508 #[must_use]
509 #[inline]
510 pub fn data(&self) -> Option<&[u8]> {
511 if self.format.is_packed() && self.planes.len() == 1 {
512 Some(&self.planes[0])
513 } else {
514 None
515 }
516 }
517
518 /// Returns mutable access to the raw sample data.
519 ///
520 /// Only available for packed formats.
521 ///
522 /// # Examples
523 ///
524 /// ```
525 /// use ff_format::{AudioFrame, SampleFormat};
526 ///
527 /// let mut frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
528 /// if let Some(data) = frame.data_mut() {
529 /// data[0] = 128;
530 /// }
531 /// ```
532 #[must_use]
533 #[inline]
534 pub fn data_mut(&mut self) -> Option<&mut [u8]> {
535 if self.format.is_packed() && self.planes.len() == 1 {
536 Some(&mut self.planes[0])
537 } else {
538 None
539 }
540 }
541
542 // ==========================================================================
543 // Typed Sample Access
544 // ==========================================================================
545 //
546 // These methods provide zero-copy typed access to audio sample data.
547 // They use unsafe code to reinterpret byte buffers as typed slices.
548 //
549 // SAFETY: The data buffers are allocated with proper size and the
550 // underlying Vec<u8> is guaranteed to be properly aligned for the
551 // platform's requirements. We verify format matches before casting.
552
553 /// Returns the sample data as an f32 slice.
554 ///
555 /// This only works if the format is [`SampleFormat::F32`] (packed).
556 /// For planar F32p format, use [`channel_as_f32()`](Self::channel_as_f32).
557 ///
558 /// # Safety Note
559 ///
560 /// This method reinterprets the raw bytes as f32 values. It requires
561 /// proper alignment and format matching.
562 ///
563 /// # Examples
564 ///
565 /// ```
566 /// use ff_format::{AudioFrame, SampleFormat};
567 ///
568 /// let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
569 /// if let Some(samples) = frame.as_f32() {
570 /// assert_eq!(samples.len(), 1024 * 2); // samples * channels
571 /// }
572 /// ```
573 #[must_use]
574 #[allow(unsafe_code, clippy::cast_ptr_alignment)]
575 pub fn as_f32(&self) -> Option<&[f32]> {
576 if self.format != SampleFormat::F32 {
577 return None;
578 }
579
580 self.data().map(|bytes| {
581 // SAFETY: We verified the format is F32, and the data was allocated
582 // for F32 samples. Vec<u8> is aligned to at least 1 byte, but in practice
583 // most allocators align to at least 8/16 bytes which is sufficient for f32.
584 let ptr = bytes.as_ptr().cast::<f32>();
585 let len = bytes.len() / std::mem::size_of::<f32>();
586 unsafe { std::slice::from_raw_parts(ptr, len) }
587 })
588 }
589
590 /// Returns mutable access to sample data as an f32 slice.
591 ///
592 /// Only works for [`SampleFormat::F32`] (packed).
593 #[must_use]
594 #[allow(unsafe_code, clippy::cast_ptr_alignment)]
595 pub fn as_f32_mut(&mut self) -> Option<&mut [f32]> {
596 if self.format != SampleFormat::F32 {
597 return None;
598 }
599
600 self.data_mut().map(|bytes| {
601 let ptr = bytes.as_mut_ptr().cast::<f32>();
602 let len = bytes.len() / std::mem::size_of::<f32>();
603 unsafe { std::slice::from_raw_parts_mut(ptr, len) }
604 })
605 }
606
607 /// Returns the sample data as an i16 slice.
608 ///
609 /// This only works if the format is [`SampleFormat::I16`] (packed).
610 /// For planar I16p format, use [`channel_as_i16()`](Self::channel_as_i16).
611 ///
612 /// # Examples
613 ///
614 /// ```
615 /// use ff_format::{AudioFrame, SampleFormat};
616 ///
617 /// let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::I16).unwrap();
618 /// if let Some(samples) = frame.as_i16() {
619 /// assert_eq!(samples.len(), 1024 * 2);
620 /// }
621 /// ```
622 #[must_use]
623 #[allow(unsafe_code, clippy::cast_ptr_alignment)]
624 pub fn as_i16(&self) -> Option<&[i16]> {
625 if self.format != SampleFormat::I16 {
626 return None;
627 }
628
629 self.data().map(|bytes| {
630 let ptr = bytes.as_ptr().cast::<i16>();
631 let len = bytes.len() / std::mem::size_of::<i16>();
632 unsafe { std::slice::from_raw_parts(ptr, len) }
633 })
634 }
635
636 /// Returns mutable access to sample data as an i16 slice.
637 ///
638 /// Only works for [`SampleFormat::I16`] (packed).
639 #[must_use]
640 #[allow(unsafe_code, clippy::cast_ptr_alignment)]
641 pub fn as_i16_mut(&mut self) -> Option<&mut [i16]> {
642 if self.format != SampleFormat::I16 {
643 return None;
644 }
645
646 self.data_mut().map(|bytes| {
647 let ptr = bytes.as_mut_ptr().cast::<i16>();
648 let len = bytes.len() / std::mem::size_of::<i16>();
649 unsafe { std::slice::from_raw_parts_mut(ptr, len) }
650 })
651 }
652
653 /// Returns a specific channel's data as an f32 slice.
654 ///
655 /// Works for planar F32p format.
656 ///
657 /// # Arguments
658 ///
659 /// * `channel` - The channel index
660 ///
661 /// # Examples
662 ///
663 /// ```
664 /// use ff_format::{AudioFrame, SampleFormat};
665 ///
666 /// let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
667 /// if let Some(left) = frame.channel_as_f32(0) {
668 /// assert_eq!(left.len(), 1024);
669 /// }
670 /// ```
671 #[must_use]
672 #[allow(unsafe_code, clippy::cast_ptr_alignment)]
673 pub fn channel_as_f32(&self, channel: usize) -> Option<&[f32]> {
674 if self.format != SampleFormat::F32p {
675 return None;
676 }
677
678 self.channel(channel).map(|bytes| {
679 let ptr = bytes.as_ptr().cast::<f32>();
680 let len = bytes.len() / std::mem::size_of::<f32>();
681 unsafe { std::slice::from_raw_parts(ptr, len) }
682 })
683 }
684
685 /// Returns mutable access to a channel's data as an f32 slice.
686 ///
687 /// Works for planar F32p format.
688 #[must_use]
689 #[allow(unsafe_code, clippy::cast_ptr_alignment)]
690 pub fn channel_as_f32_mut(&mut self, channel: usize) -> Option<&mut [f32]> {
691 if self.format != SampleFormat::F32p {
692 return None;
693 }
694
695 self.channel_mut(channel).map(|bytes| {
696 let ptr = bytes.as_mut_ptr().cast::<f32>();
697 let len = bytes.len() / std::mem::size_of::<f32>();
698 unsafe { std::slice::from_raw_parts_mut(ptr, len) }
699 })
700 }
701
702 /// Returns a specific channel's data as an i16 slice.
703 ///
704 /// Works for planar I16p format.
705 ///
706 /// # Arguments
707 ///
708 /// * `channel` - The channel index
709 ///
710 /// # Examples
711 ///
712 /// ```
713 /// use ff_format::{AudioFrame, SampleFormat};
714 ///
715 /// let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::I16p).unwrap();
716 /// if let Some(left) = frame.channel_as_i16(0) {
717 /// assert_eq!(left.len(), 1024);
718 /// }
719 /// ```
720 #[must_use]
721 #[allow(unsafe_code, clippy::cast_ptr_alignment)]
722 pub fn channel_as_i16(&self, channel: usize) -> Option<&[i16]> {
723 if self.format != SampleFormat::I16p {
724 return None;
725 }
726
727 self.channel(channel).map(|bytes| {
728 let ptr = bytes.as_ptr().cast::<i16>();
729 let len = bytes.len() / std::mem::size_of::<i16>();
730 unsafe { std::slice::from_raw_parts(ptr, len) }
731 })
732 }
733
734 /// Returns mutable access to a channel's data as an i16 slice.
735 ///
736 /// Works for planar I16p format.
737 #[must_use]
738 #[allow(unsafe_code, clippy::cast_ptr_alignment)]
739 pub fn channel_as_i16_mut(&mut self, channel: usize) -> Option<&mut [i16]> {
740 if self.format != SampleFormat::I16p {
741 return None;
742 }
743
744 self.channel_mut(channel).map(|bytes| {
745 let ptr = bytes.as_mut_ptr().cast::<i16>();
746 let len = bytes.len() / std::mem::size_of::<i16>();
747 unsafe { std::slice::from_raw_parts_mut(ptr, len) }
748 })
749 }
750
751 // ==========================================================================
752 // Utility Methods
753 // ==========================================================================
754
755 /// Returns the total size in bytes of all sample data.
756 ///
757 /// # Examples
758 ///
759 /// ```
760 /// use ff_format::{AudioFrame, SampleFormat};
761 ///
762 /// let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
763 /// assert_eq!(frame.total_size(), 1024 * 2 * 4);
764 /// ```
765 #[must_use]
766 pub fn total_size(&self) -> usize {
767 self.planes.iter().map(Vec::len).sum()
768 }
769
770 /// Returns the size in bytes of a single sample (one channel).
771 ///
772 /// # Examples
773 ///
774 /// ```
775 /// use ff_format::{AudioFrame, SampleFormat};
776 ///
777 /// let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
778 /// assert_eq!(frame.bytes_per_sample(), 4);
779 ///
780 /// let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::I16).unwrap();
781 /// assert_eq!(frame.bytes_per_sample(), 2);
782 /// ```
783 #[must_use]
784 #[inline]
785 pub fn bytes_per_sample(&self) -> usize {
786 self.format.bytes_per_sample()
787 }
788}
789
790impl fmt::Debug for AudioFrame {
791 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
792 f.debug_struct("AudioFrame")
793 .field("samples", &self.samples)
794 .field("channels", &self.channels)
795 .field("sample_rate", &self.sample_rate)
796 .field("format", &self.format)
797 .field("timestamp", &self.timestamp)
798 .field("num_planes", &self.planes.len())
799 .field(
800 "plane_sizes",
801 &self.planes.iter().map(Vec::len).collect::<Vec<_>>(),
802 )
803 .finish()
804 }
805}
806
807impl fmt::Display for AudioFrame {
808 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
809 let duration_ms = self.duration().as_secs_f64() * 1000.0;
810 write!(
811 f,
812 "AudioFrame({} samples, {}ch, {}Hz, {} @ {}, {:.2}ms)",
813 self.samples, self.channels, self.sample_rate, self.format, self.timestamp, duration_ms
814 )
815 }
816}
817
818impl Default for AudioFrame {
819 /// Returns a default empty mono F32 frame with 0 samples.
820 fn default() -> Self {
821 Self {
822 planes: vec![vec![]],
823 samples: 0,
824 channels: 1,
825 sample_rate: 48000,
826 format: SampleFormat::F32,
827 timestamp: Timestamp::default(),
828 }
829 }
830}
831
832#[cfg(test)]
833#[allow(clippy::unwrap_used, clippy::redundant_closure_for_method_calls)]
834mod tests {
835 use super::*;
836 use crate::Rational;
837
838 // ==========================================================================
839 // Construction Tests
840 // ==========================================================================
841
842 #[test]
843 fn test_new_packed_f32() {
844 let samples = 1024;
845 let channels = 2u32;
846 let bytes_per_sample = 4;
847 let data = vec![0u8; samples * channels as usize * bytes_per_sample];
848
849 let frame = AudioFrame::new(
850 vec![data],
851 samples,
852 channels,
853 48000,
854 SampleFormat::F32,
855 Timestamp::default(),
856 )
857 .unwrap();
858
859 assert_eq!(frame.samples(), 1024);
860 assert_eq!(frame.channels(), 2);
861 assert_eq!(frame.sample_rate(), 48000);
862 assert_eq!(frame.format(), SampleFormat::F32);
863 assert_eq!(frame.num_planes(), 1);
864 }
865
866 #[test]
867 fn test_new_planar_f32p() {
868 let samples = 1024;
869 let channels = 2u32;
870 let bytes_per_sample = 4;
871 let plane_size = samples * bytes_per_sample;
872
873 let planes = vec![vec![0u8; plane_size], vec![0u8; plane_size]];
874
875 let frame = AudioFrame::new(
876 planes,
877 samples,
878 channels,
879 48000,
880 SampleFormat::F32p,
881 Timestamp::default(),
882 )
883 .unwrap();
884
885 assert_eq!(frame.samples(), 1024);
886 assert_eq!(frame.channels(), 2);
887 assert_eq!(frame.format(), SampleFormat::F32p);
888 assert_eq!(frame.num_planes(), 2);
889 }
890
891 #[test]
892 fn test_new_invalid_plane_count_packed() {
893 // Packed format should have 1 plane, but we provide 2
894 let result = AudioFrame::new(
895 vec![vec![0u8; 100], vec![0u8; 100]],
896 100,
897 2,
898 48000,
899 SampleFormat::F32,
900 Timestamp::default(),
901 );
902
903 assert!(result.is_err());
904 assert_eq!(
905 result.unwrap_err(),
906 FrameError::InvalidPlaneCount {
907 expected: 1,
908 actual: 2
909 }
910 );
911 }
912
913 #[test]
914 fn test_new_invalid_plane_count_planar() {
915 // Planar format with 2 channels should have 2 planes, but we provide 1
916 let result = AudioFrame::new(
917 vec![vec![0u8; 100]],
918 100,
919 2,
920 48000,
921 SampleFormat::F32p,
922 Timestamp::default(),
923 );
924
925 assert!(result.is_err());
926 assert_eq!(
927 result.unwrap_err(),
928 FrameError::InvalidPlaneCount {
929 expected: 2,
930 actual: 1
931 }
932 );
933 }
934
935 #[test]
936 fn test_empty_packed() {
937 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
938
939 assert_eq!(frame.samples(), 1024);
940 assert_eq!(frame.channels(), 2);
941 assert_eq!(frame.sample_rate(), 48000);
942 assert_eq!(frame.format(), SampleFormat::F32);
943 assert_eq!(frame.num_planes(), 1);
944 assert_eq!(frame.total_size(), 1024 * 2 * 4);
945 }
946
947 #[test]
948 fn test_empty_planar() {
949 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
950
951 assert_eq!(frame.num_planes(), 2);
952 assert_eq!(frame.plane(0).map(|p| p.len()), Some(1024 * 4));
953 assert_eq!(frame.plane(1).map(|p| p.len()), Some(1024 * 4));
954 assert_eq!(frame.total_size(), 1024 * 2 * 4);
955 }
956
957 #[test]
958 fn test_empty_i16() {
959 let frame = AudioFrame::empty(1024, 2, 44100, SampleFormat::I16).unwrap();
960
961 assert_eq!(frame.bytes_per_sample(), 2);
962 assert_eq!(frame.total_size(), 1024 * 2 * 2);
963 }
964
965 #[test]
966 fn test_empty_other_format_error() {
967 let result = AudioFrame::empty(1024, 2, 48000, SampleFormat::Other(999));
968
969 assert!(result.is_err());
970 assert_eq!(
971 result.unwrap_err(),
972 FrameError::UnsupportedSampleFormat(SampleFormat::Other(999))
973 );
974 }
975
976 #[test]
977 fn test_default() {
978 let frame = AudioFrame::default();
979
980 assert_eq!(frame.samples(), 0);
981 assert_eq!(frame.channels(), 1);
982 assert_eq!(frame.sample_rate(), 48000);
983 assert_eq!(frame.format(), SampleFormat::F32);
984 }
985
986 // ==========================================================================
987 // Metadata Tests
988 // ==========================================================================
989
990 #[test]
991 fn test_duration() {
992 // 1024 samples at 48kHz = 0.02133... seconds
993 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
994 let duration = frame.duration();
995 assert!((duration.as_secs_f64() - 0.021_333_333).abs() < 0.000_001);
996
997 // 48000 samples at 48kHz = 1 second
998 let frame = AudioFrame::empty(48000, 2, 48000, SampleFormat::F32).unwrap();
999 assert_eq!(frame.duration().as_secs(), 1);
1000 }
1001
1002 #[test]
1003 fn test_duration_zero_sample_rate() {
1004 let frame = AudioFrame::new(
1005 vec![vec![]],
1006 0,
1007 1,
1008 0,
1009 SampleFormat::F32,
1010 Timestamp::default(),
1011 )
1012 .unwrap();
1013
1014 assert_eq!(frame.duration(), Duration::ZERO);
1015 }
1016
1017 #[test]
1018 fn test_set_timestamp() {
1019 let mut frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
1020 let ts = Timestamp::new(48000, Rational::new(1, 48000));
1021
1022 frame.set_timestamp(ts);
1023 assert_eq!(frame.timestamp(), ts);
1024 }
1025
1026 // ==========================================================================
1027 // Plane Access Tests
1028 // ==========================================================================
1029
1030 #[test]
1031 fn test_plane_access_packed() {
1032 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
1033
1034 assert!(frame.plane(0).is_some());
1035 assert!(frame.plane(1).is_none());
1036 }
1037
1038 #[test]
1039 fn test_plane_access_planar() {
1040 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
1041
1042 assert!(frame.plane(0).is_some());
1043 assert!(frame.plane(1).is_some());
1044 assert!(frame.plane(2).is_none());
1045 }
1046
1047 #[test]
1048 fn test_plane_mut_access() {
1049 let mut frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
1050
1051 if let Some(data) = frame.plane_mut(0) {
1052 data[0] = 255;
1053 }
1054
1055 assert_eq!(frame.plane(0).unwrap()[0], 255);
1056 }
1057
1058 #[test]
1059 fn test_channel_access() {
1060 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
1061
1062 let left = frame.channel(0).unwrap();
1063 let right = frame.channel(1).unwrap();
1064
1065 assert_eq!(left.len(), 1024 * 4);
1066 assert_eq!(right.len(), 1024 * 4);
1067 }
1068
1069 // ==========================================================================
1070 // Data Access Tests
1071 // ==========================================================================
1072
1073 #[test]
1074 fn test_data_packed() {
1075 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
1076 assert!(frame.data().is_some());
1077 assert_eq!(frame.data().unwrap().len(), 1024 * 2 * 4);
1078 }
1079
1080 #[test]
1081 fn test_data_planar_returns_none() {
1082 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
1083 assert!(frame.data().is_none());
1084 }
1085
1086 #[test]
1087 fn test_data_mut() {
1088 let mut frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
1089
1090 if let Some(data) = frame.data_mut() {
1091 data[0] = 123;
1092 }
1093
1094 assert_eq!(frame.data().unwrap()[0], 123);
1095 }
1096
1097 // ==========================================================================
1098 // Typed Access Tests
1099 // ==========================================================================
1100
1101 #[test]
1102 fn test_as_f32() {
1103 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
1104 let samples = frame.as_f32().unwrap();
1105 assert_eq!(samples.len(), 1024 * 2);
1106 }
1107
1108 #[test]
1109 fn test_as_f32_wrong_format() {
1110 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::I16).unwrap();
1111 assert!(frame.as_f32().is_none());
1112 }
1113
1114 #[test]
1115 fn test_as_f32_mut() {
1116 let mut frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
1117
1118 if let Some(samples) = frame.as_f32_mut() {
1119 samples[0] = 1.0;
1120 samples[1] = -1.0;
1121 }
1122
1123 let samples = frame.as_f32().unwrap();
1124 assert!((samples[0] - 1.0).abs() < f32::EPSILON);
1125 assert!((samples[1] - (-1.0)).abs() < f32::EPSILON);
1126 }
1127
1128 #[test]
1129 fn test_as_i16() {
1130 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::I16).unwrap();
1131 let samples = frame.as_i16().unwrap();
1132 assert_eq!(samples.len(), 1024 * 2);
1133 }
1134
1135 #[test]
1136 fn test_as_i16_wrong_format() {
1137 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
1138 assert!(frame.as_i16().is_none());
1139 }
1140
1141 #[test]
1142 fn test_channel_as_f32() {
1143 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
1144
1145 let left = frame.channel_as_f32(0).unwrap();
1146 let right = frame.channel_as_f32(1).unwrap();
1147
1148 assert_eq!(left.len(), 1024);
1149 assert_eq!(right.len(), 1024);
1150 }
1151
1152 #[test]
1153 fn test_channel_as_f32_wrong_format() {
1154 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
1155 assert!(frame.channel_as_f32(0).is_none()); // F32 is packed, not F32p
1156 }
1157
1158 #[test]
1159 fn test_channel_as_f32_mut() {
1160 let mut frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
1161
1162 if let Some(left) = frame.channel_as_f32_mut(0) {
1163 left[0] = 0.5;
1164 }
1165
1166 assert!((frame.channel_as_f32(0).unwrap()[0] - 0.5).abs() < f32::EPSILON);
1167 }
1168
1169 #[test]
1170 fn test_channel_as_i16() {
1171 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::I16p).unwrap();
1172
1173 let left = frame.channel_as_i16(0).unwrap();
1174 assert_eq!(left.len(), 1024);
1175 }
1176
1177 #[test]
1178 fn test_channel_as_i16_mut() {
1179 let mut frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::I16p).unwrap();
1180
1181 if let Some(left) = frame.channel_as_i16_mut(0) {
1182 left[0] = 32767;
1183 }
1184
1185 assert_eq!(frame.channel_as_i16(0).unwrap()[0], 32767);
1186 }
1187
1188 // ==========================================================================
1189 // Utility Tests
1190 // ==========================================================================
1191
1192 #[test]
1193 fn test_total_size() {
1194 // Packed stereo F32: 1024 samples * 2 channels * 4 bytes
1195 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
1196 assert_eq!(frame.total_size(), 1024 * 2 * 4);
1197
1198 // Planar stereo F32p: 2 planes * 1024 samples * 4 bytes
1199 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
1200 assert_eq!(frame.total_size(), 1024 * 4 * 2);
1201 }
1202
1203 #[test]
1204 fn test_bytes_per_sample() {
1205 assert_eq!(
1206 AudioFrame::empty(1024, 2, 48000, SampleFormat::U8)
1207 .unwrap()
1208 .bytes_per_sample(),
1209 1
1210 );
1211 assert_eq!(
1212 AudioFrame::empty(1024, 2, 48000, SampleFormat::I16)
1213 .unwrap()
1214 .bytes_per_sample(),
1215 2
1216 );
1217 assert_eq!(
1218 AudioFrame::empty(1024, 2, 48000, SampleFormat::F32)
1219 .unwrap()
1220 .bytes_per_sample(),
1221 4
1222 );
1223 assert_eq!(
1224 AudioFrame::empty(1024, 2, 48000, SampleFormat::F64)
1225 .unwrap()
1226 .bytes_per_sample(),
1227 8
1228 );
1229 }
1230
1231 // ==========================================================================
1232 // Clone Tests
1233 // ==========================================================================
1234
1235 #[test]
1236 fn test_clone() {
1237 let mut original = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
1238 original.set_timestamp(Timestamp::new(1000, Rational::new(1, 1000)));
1239
1240 // Modify some data
1241 if let Some(data) = original.plane_mut(0) {
1242 data[0] = 42;
1243 }
1244
1245 let cloned = original.clone();
1246
1247 // Verify metadata matches
1248 assert_eq!(cloned.samples(), original.samples());
1249 assert_eq!(cloned.channels(), original.channels());
1250 assert_eq!(cloned.sample_rate(), original.sample_rate());
1251 assert_eq!(cloned.format(), original.format());
1252 assert_eq!(cloned.timestamp(), original.timestamp());
1253
1254 // Verify data was cloned
1255 assert_eq!(cloned.plane(0).unwrap()[0], 42);
1256
1257 // Verify it's a deep clone
1258 let mut cloned = cloned;
1259 if let Some(data) = cloned.plane_mut(0) {
1260 data[0] = 99;
1261 }
1262 assert_eq!(original.plane(0).unwrap()[0], 42);
1263 assert_eq!(cloned.plane(0).unwrap()[0], 99);
1264 }
1265
1266 // ==========================================================================
1267 // Display/Debug Tests
1268 // ==========================================================================
1269
1270 #[test]
1271 fn test_debug() {
1272 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
1273 let debug = format!("{frame:?}");
1274 assert!(debug.contains("AudioFrame"));
1275 assert!(debug.contains("1024"));
1276 assert!(debug.contains("48000"));
1277 assert!(debug.contains("F32"));
1278 }
1279
1280 #[test]
1281 fn test_display() {
1282 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
1283 let display = format!("{frame}");
1284 assert!(display.contains("1024 samples"));
1285 assert!(display.contains("2ch"));
1286 assert!(display.contains("48000Hz"));
1287 assert!(display.contains("flt")); // F32 displays as "flt"
1288 }
1289
1290 // ==========================================================================
1291 // All Sample Formats Tests
1292 // ==========================================================================
1293
1294 #[test]
1295 fn test_all_packed_formats() {
1296 let formats = [
1297 SampleFormat::U8,
1298 SampleFormat::I16,
1299 SampleFormat::I32,
1300 SampleFormat::F32,
1301 SampleFormat::F64,
1302 ];
1303
1304 for format in formats {
1305 let frame = AudioFrame::empty(1024, 2, 48000, format).unwrap();
1306 assert_eq!(frame.num_planes(), 1);
1307 assert!(frame.data().is_some());
1308 }
1309 }
1310
1311 #[test]
1312 fn test_all_planar_formats() {
1313 let formats = [
1314 SampleFormat::U8p,
1315 SampleFormat::I16p,
1316 SampleFormat::I32p,
1317 SampleFormat::F32p,
1318 SampleFormat::F64p,
1319 ];
1320
1321 for format in formats {
1322 let frame = AudioFrame::empty(1024, 2, 48000, format).unwrap();
1323 assert_eq!(frame.num_planes(), 2);
1324 assert!(frame.data().is_none());
1325 assert!(frame.channel(0).is_some());
1326 assert!(frame.channel(1).is_some());
1327 }
1328 }
1329
1330 #[test]
1331 fn test_mono_planar() {
1332 let frame = AudioFrame::empty(1024, 1, 48000, SampleFormat::F32p).unwrap();
1333 assert_eq!(frame.num_planes(), 1);
1334 assert_eq!(frame.plane(0).map(|p| p.len()), Some(1024 * 4));
1335 }
1336
1337 #[test]
1338 fn test_surround_planar() {
1339 // 5.1 surround sound
1340 let frame = AudioFrame::empty(1024, 6, 48000, SampleFormat::F32p).unwrap();
1341 assert_eq!(frame.num_planes(), 6);
1342 for i in 0..6 {
1343 assert!(frame.plane(i).is_some());
1344 }
1345 assert!(frame.plane(6).is_none());
1346 }
1347}