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 log::warn!(
345 "duration unavailable, sample_rate is 0, returning zero \
346 samples={} fallback=Duration::ZERO",
347 self.samples
348 );
349 return Duration::ZERO;
350 }
351 let secs = self.samples as f64 / f64::from(self.sample_rate);
352 Duration::from_secs_f64(secs)
353 }
354
355 // ==========================================================================
356 // Plane Data Access
357 // ==========================================================================
358
359 /// Returns the number of planes in this frame.
360 ///
361 /// - Packed formats: 1 plane (interleaved channels)
362 /// - Planar formats: 1 plane per channel
363 ///
364 /// # Examples
365 ///
366 /// ```
367 /// use ff_format::{AudioFrame, SampleFormat};
368 ///
369 /// // Packed format - 1 plane
370 /// let packed = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
371 /// assert_eq!(packed.num_planes(), 1);
372 ///
373 /// // Planar format - 1 plane per channel
374 /// let planar = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
375 /// assert_eq!(planar.num_planes(), 2);
376 /// ```
377 #[must_use]
378 #[inline]
379 pub fn num_planes(&self) -> usize {
380 self.planes.len()
381 }
382
383 /// Returns a slice of all plane data.
384 ///
385 /// # Examples
386 ///
387 /// ```
388 /// use ff_format::{AudioFrame, SampleFormat};
389 ///
390 /// let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
391 /// let planes = frame.planes();
392 /// assert_eq!(planes.len(), 2);
393 /// ```
394 #[must_use]
395 #[inline]
396 pub fn planes(&self) -> &[Vec<u8>] {
397 &self.planes
398 }
399
400 /// Returns the data for a specific plane, or `None` if the index is out of bounds.
401 ///
402 /// For packed formats, use `plane(0)`. For planar formats, use `plane(channel_index)`.
403 ///
404 /// # Arguments
405 ///
406 /// * `index` - The plane index (0 for packed, channel index for planar)
407 ///
408 /// # Examples
409 ///
410 /// ```
411 /// use ff_format::{AudioFrame, SampleFormat};
412 ///
413 /// let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
414 ///
415 /// // Access left channel (plane 0)
416 /// assert!(frame.plane(0).is_some());
417 ///
418 /// // Access right channel (plane 1)
419 /// assert!(frame.plane(1).is_some());
420 ///
421 /// // No third channel
422 /// assert!(frame.plane(2).is_none());
423 /// ```
424 #[must_use]
425 #[inline]
426 pub fn plane(&self, index: usize) -> Option<&[u8]> {
427 self.planes.get(index).map(Vec::as_slice)
428 }
429
430 /// Returns mutable access to a specific plane's data.
431 ///
432 /// # Arguments
433 ///
434 /// * `index` - The plane index
435 ///
436 /// # Examples
437 ///
438 /// ```
439 /// use ff_format::{AudioFrame, SampleFormat};
440 ///
441 /// let mut frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
442 /// if let Some(data) = frame.plane_mut(0) {
443 /// // Modify left channel
444 /// data[0] = 128;
445 /// }
446 /// ```
447 #[must_use]
448 #[inline]
449 pub fn plane_mut(&mut self, index: usize) -> Option<&mut [u8]> {
450 self.planes.get_mut(index).map(Vec::as_mut_slice)
451 }
452
453 /// Returns the channel data for planar formats.
454 ///
455 /// This is an alias for [`plane()`](Self::plane) that's more semantically
456 /// meaningful for audio data.
457 ///
458 /// # Arguments
459 ///
460 /// * `channel` - The channel index (0 = left, 1 = right, etc.)
461 ///
462 /// # Examples
463 ///
464 /// ```
465 /// use ff_format::{AudioFrame, SampleFormat};
466 ///
467 /// let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
468 ///
469 /// // Get left channel data
470 /// let left = frame.channel(0).unwrap();
471 /// assert_eq!(left.len(), 1024 * 4); // 1024 samples * 4 bytes
472 /// ```
473 #[must_use]
474 #[inline]
475 pub fn channel(&self, channel: usize) -> Option<&[u8]> {
476 self.plane(channel)
477 }
478
479 /// Returns mutable access to channel data for planar formats.
480 ///
481 /// # Arguments
482 ///
483 /// * `channel` - The channel index
484 #[must_use]
485 #[inline]
486 pub fn channel_mut(&mut self, channel: usize) -> Option<&mut [u8]> {
487 self.plane_mut(channel)
488 }
489
490 // ==========================================================================
491 // Contiguous Data Access
492 // ==========================================================================
493
494 /// Returns the raw sample data as a contiguous byte slice.
495 ///
496 /// For packed formats, this returns a reference to the single plane.
497 /// For planar formats, this returns `None` (use [`channel()`](Self::channel) instead).
498 ///
499 /// # Examples
500 ///
501 /// ```
502 /// use ff_format::{AudioFrame, SampleFormat};
503 ///
504 /// // Packed format - returns data
505 /// let packed = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
506 /// assert!(packed.data().is_some());
507 /// assert_eq!(packed.data().unwrap().len(), 1024 * 2 * 4);
508 ///
509 /// // Planar format - returns None
510 /// let planar = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
511 /// assert!(planar.data().is_none());
512 /// ```
513 #[must_use]
514 #[inline]
515 pub fn data(&self) -> Option<&[u8]> {
516 if self.format.is_packed() && self.planes.len() == 1 {
517 Some(&self.planes[0])
518 } else {
519 None
520 }
521 }
522
523 /// Returns mutable access to the raw sample data.
524 ///
525 /// Only available for packed formats.
526 ///
527 /// # Examples
528 ///
529 /// ```
530 /// use ff_format::{AudioFrame, SampleFormat};
531 ///
532 /// let mut frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
533 /// if let Some(data) = frame.data_mut() {
534 /// data[0] = 128;
535 /// }
536 /// ```
537 #[must_use]
538 #[inline]
539 pub fn data_mut(&mut self) -> Option<&mut [u8]> {
540 if self.format.is_packed() && self.planes.len() == 1 {
541 Some(&mut self.planes[0])
542 } else {
543 None
544 }
545 }
546
547 // ==========================================================================
548 // Typed Sample Access
549 // ==========================================================================
550 //
551 // These methods provide zero-copy typed access to audio sample data.
552 // They use unsafe code to reinterpret byte buffers as typed slices.
553 //
554 // SAFETY: The data buffers are allocated with proper size and the
555 // underlying Vec<u8> is guaranteed to be properly aligned for the
556 // platform's requirements. We verify format matches before casting.
557
558 /// Returns the sample data as an f32 slice.
559 ///
560 /// This only works if the format is [`SampleFormat::F32`] (packed).
561 /// For planar F32p format, use [`channel_as_f32()`](Self::channel_as_f32).
562 ///
563 /// # Safety Note
564 ///
565 /// This method reinterprets the raw bytes as f32 values. It requires
566 /// proper alignment and format matching.
567 ///
568 /// # Examples
569 ///
570 /// ```
571 /// use ff_format::{AudioFrame, SampleFormat};
572 ///
573 /// let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
574 /// if let Some(samples) = frame.as_f32() {
575 /// assert_eq!(samples.len(), 1024 * 2); // samples * channels
576 /// }
577 /// ```
578 #[must_use]
579 #[allow(unsafe_code, clippy::cast_ptr_alignment)]
580 pub fn as_f32(&self) -> Option<&[f32]> {
581 if self.format != SampleFormat::F32 {
582 return None;
583 }
584
585 self.data().map(|bytes| {
586 // SAFETY: We verified the format is F32, and the data was allocated
587 // for F32 samples. Vec<u8> is aligned to at least 1 byte, but in practice
588 // most allocators align to at least 8/16 bytes which is sufficient for f32.
589 let ptr = bytes.as_ptr().cast::<f32>();
590 let len = bytes.len() / std::mem::size_of::<f32>();
591 unsafe { std::slice::from_raw_parts(ptr, len) }
592 })
593 }
594
595 /// Returns mutable access to sample data as an f32 slice.
596 ///
597 /// Only works for [`SampleFormat::F32`] (packed).
598 #[must_use]
599 #[allow(unsafe_code, clippy::cast_ptr_alignment)]
600 pub fn as_f32_mut(&mut self) -> Option<&mut [f32]> {
601 if self.format != SampleFormat::F32 {
602 return None;
603 }
604
605 self.data_mut().map(|bytes| {
606 let ptr = bytes.as_mut_ptr().cast::<f32>();
607 let len = bytes.len() / std::mem::size_of::<f32>();
608 unsafe { std::slice::from_raw_parts_mut(ptr, len) }
609 })
610 }
611
612 /// Returns the sample data as an i16 slice.
613 ///
614 /// This only works if the format is [`SampleFormat::I16`] (packed).
615 /// For planar I16p format, use [`channel_as_i16()`](Self::channel_as_i16).
616 ///
617 /// # Examples
618 ///
619 /// ```
620 /// use ff_format::{AudioFrame, SampleFormat};
621 ///
622 /// let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::I16).unwrap();
623 /// if let Some(samples) = frame.as_i16() {
624 /// assert_eq!(samples.len(), 1024 * 2);
625 /// }
626 /// ```
627 #[must_use]
628 #[allow(unsafe_code, clippy::cast_ptr_alignment)]
629 pub fn as_i16(&self) -> Option<&[i16]> {
630 if self.format != SampleFormat::I16 {
631 return None;
632 }
633
634 self.data().map(|bytes| {
635 let ptr = bytes.as_ptr().cast::<i16>();
636 let len = bytes.len() / std::mem::size_of::<i16>();
637 unsafe { std::slice::from_raw_parts(ptr, len) }
638 })
639 }
640
641 /// Returns mutable access to sample data as an i16 slice.
642 ///
643 /// Only works for [`SampleFormat::I16`] (packed).
644 #[must_use]
645 #[allow(unsafe_code, clippy::cast_ptr_alignment)]
646 pub fn as_i16_mut(&mut self) -> Option<&mut [i16]> {
647 if self.format != SampleFormat::I16 {
648 return None;
649 }
650
651 self.data_mut().map(|bytes| {
652 let ptr = bytes.as_mut_ptr().cast::<i16>();
653 let len = bytes.len() / std::mem::size_of::<i16>();
654 unsafe { std::slice::from_raw_parts_mut(ptr, len) }
655 })
656 }
657
658 /// Returns a specific channel's data as an f32 slice.
659 ///
660 /// Works for planar F32p format.
661 ///
662 /// # Arguments
663 ///
664 /// * `channel` - The channel index
665 ///
666 /// # Examples
667 ///
668 /// ```
669 /// use ff_format::{AudioFrame, SampleFormat};
670 ///
671 /// let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
672 /// if let Some(left) = frame.channel_as_f32(0) {
673 /// assert_eq!(left.len(), 1024);
674 /// }
675 /// ```
676 #[must_use]
677 #[allow(unsafe_code, clippy::cast_ptr_alignment)]
678 pub fn channel_as_f32(&self, channel: usize) -> Option<&[f32]> {
679 if self.format != SampleFormat::F32p {
680 return None;
681 }
682
683 self.channel(channel).map(|bytes| {
684 let ptr = bytes.as_ptr().cast::<f32>();
685 let len = bytes.len() / std::mem::size_of::<f32>();
686 unsafe { std::slice::from_raw_parts(ptr, len) }
687 })
688 }
689
690 /// Returns mutable access to a channel's data as an f32 slice.
691 ///
692 /// Works for planar F32p format.
693 #[must_use]
694 #[allow(unsafe_code, clippy::cast_ptr_alignment)]
695 pub fn channel_as_f32_mut(&mut self, channel: usize) -> Option<&mut [f32]> {
696 if self.format != SampleFormat::F32p {
697 return None;
698 }
699
700 self.channel_mut(channel).map(|bytes| {
701 let ptr = bytes.as_mut_ptr().cast::<f32>();
702 let len = bytes.len() / std::mem::size_of::<f32>();
703 unsafe { std::slice::from_raw_parts_mut(ptr, len) }
704 })
705 }
706
707 /// Returns a specific channel's data as an i16 slice.
708 ///
709 /// Works for planar I16p format.
710 ///
711 /// # Arguments
712 ///
713 /// * `channel` - The channel index
714 ///
715 /// # Examples
716 ///
717 /// ```
718 /// use ff_format::{AudioFrame, SampleFormat};
719 ///
720 /// let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::I16p).unwrap();
721 /// if let Some(left) = frame.channel_as_i16(0) {
722 /// assert_eq!(left.len(), 1024);
723 /// }
724 /// ```
725 #[must_use]
726 #[allow(unsafe_code, clippy::cast_ptr_alignment)]
727 pub fn channel_as_i16(&self, channel: usize) -> Option<&[i16]> {
728 if self.format != SampleFormat::I16p {
729 return None;
730 }
731
732 self.channel(channel).map(|bytes| {
733 let ptr = bytes.as_ptr().cast::<i16>();
734 let len = bytes.len() / std::mem::size_of::<i16>();
735 unsafe { std::slice::from_raw_parts(ptr, len) }
736 })
737 }
738
739 /// Returns mutable access to a channel's data as an i16 slice.
740 ///
741 /// Works for planar I16p format.
742 #[must_use]
743 #[allow(unsafe_code, clippy::cast_ptr_alignment)]
744 pub fn channel_as_i16_mut(&mut self, channel: usize) -> Option<&mut [i16]> {
745 if self.format != SampleFormat::I16p {
746 return None;
747 }
748
749 self.channel_mut(channel).map(|bytes| {
750 let ptr = bytes.as_mut_ptr().cast::<i16>();
751 let len = bytes.len() / std::mem::size_of::<i16>();
752 unsafe { std::slice::from_raw_parts_mut(ptr, len) }
753 })
754 }
755
756 // ==========================================================================
757 // Utility Methods
758 // ==========================================================================
759
760 /// Returns the total size in bytes of all sample data.
761 ///
762 /// # Examples
763 ///
764 /// ```
765 /// use ff_format::{AudioFrame, SampleFormat};
766 ///
767 /// let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
768 /// assert_eq!(frame.total_size(), 1024 * 2 * 4);
769 /// ```
770 #[must_use]
771 pub fn total_size(&self) -> usize {
772 self.planes.iter().map(Vec::len).sum()
773 }
774
775 /// Returns the size in bytes of a single sample (one channel).
776 ///
777 /// # Examples
778 ///
779 /// ```
780 /// use ff_format::{AudioFrame, SampleFormat};
781 ///
782 /// let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
783 /// assert_eq!(frame.bytes_per_sample(), 4);
784 ///
785 /// let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::I16).unwrap();
786 /// assert_eq!(frame.bytes_per_sample(), 2);
787 /// ```
788 #[must_use]
789 #[inline]
790 pub fn bytes_per_sample(&self) -> usize {
791 self.format.bytes_per_sample()
792 }
793}
794
795impl fmt::Debug for AudioFrame {
796 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
797 f.debug_struct("AudioFrame")
798 .field("samples", &self.samples)
799 .field("channels", &self.channels)
800 .field("sample_rate", &self.sample_rate)
801 .field("format", &self.format)
802 .field("timestamp", &self.timestamp)
803 .field("num_planes", &self.planes.len())
804 .field(
805 "plane_sizes",
806 &self.planes.iter().map(Vec::len).collect::<Vec<_>>(),
807 )
808 .finish()
809 }
810}
811
812impl fmt::Display for AudioFrame {
813 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
814 let duration_ms = self.duration().as_secs_f64() * 1000.0;
815 write!(
816 f,
817 "AudioFrame({} samples, {}ch, {}Hz, {} @ {}, {:.2}ms)",
818 self.samples, self.channels, self.sample_rate, self.format, self.timestamp, duration_ms
819 )
820 }
821}
822
823impl Default for AudioFrame {
824 /// Returns a default empty mono F32 frame with 0 samples.
825 fn default() -> Self {
826 Self {
827 planes: vec![vec![]],
828 samples: 0,
829 channels: 1,
830 sample_rate: 48000,
831 format: SampleFormat::F32,
832 timestamp: Timestamp::default(),
833 }
834 }
835}
836
837#[cfg(test)]
838#[allow(clippy::unwrap_used, clippy::redundant_closure_for_method_calls)]
839mod tests {
840 use super::*;
841 use crate::Rational;
842
843 // ==========================================================================
844 // Construction Tests
845 // ==========================================================================
846
847 #[test]
848 fn test_new_packed_f32() {
849 let samples = 1024;
850 let channels = 2u32;
851 let bytes_per_sample = 4;
852 let data = vec![0u8; samples * channels as usize * bytes_per_sample];
853
854 let frame = AudioFrame::new(
855 vec![data],
856 samples,
857 channels,
858 48000,
859 SampleFormat::F32,
860 Timestamp::default(),
861 )
862 .unwrap();
863
864 assert_eq!(frame.samples(), 1024);
865 assert_eq!(frame.channels(), 2);
866 assert_eq!(frame.sample_rate(), 48000);
867 assert_eq!(frame.format(), SampleFormat::F32);
868 assert_eq!(frame.num_planes(), 1);
869 }
870
871 #[test]
872 fn test_new_planar_f32p() {
873 let samples = 1024;
874 let channels = 2u32;
875 let bytes_per_sample = 4;
876 let plane_size = samples * bytes_per_sample;
877
878 let planes = vec![vec![0u8; plane_size], vec![0u8; plane_size]];
879
880 let frame = AudioFrame::new(
881 planes,
882 samples,
883 channels,
884 48000,
885 SampleFormat::F32p,
886 Timestamp::default(),
887 )
888 .unwrap();
889
890 assert_eq!(frame.samples(), 1024);
891 assert_eq!(frame.channels(), 2);
892 assert_eq!(frame.format(), SampleFormat::F32p);
893 assert_eq!(frame.num_planes(), 2);
894 }
895
896 #[test]
897 fn test_new_invalid_plane_count_packed() {
898 // Packed format should have 1 plane, but we provide 2
899 let result = AudioFrame::new(
900 vec![vec![0u8; 100], vec![0u8; 100]],
901 100,
902 2,
903 48000,
904 SampleFormat::F32,
905 Timestamp::default(),
906 );
907
908 assert!(result.is_err());
909 assert_eq!(
910 result.unwrap_err(),
911 FrameError::InvalidPlaneCount {
912 expected: 1,
913 actual: 2
914 }
915 );
916 }
917
918 #[test]
919 fn test_new_invalid_plane_count_planar() {
920 // Planar format with 2 channels should have 2 planes, but we provide 1
921 let result = AudioFrame::new(
922 vec![vec![0u8; 100]],
923 100,
924 2,
925 48000,
926 SampleFormat::F32p,
927 Timestamp::default(),
928 );
929
930 assert!(result.is_err());
931 assert_eq!(
932 result.unwrap_err(),
933 FrameError::InvalidPlaneCount {
934 expected: 2,
935 actual: 1
936 }
937 );
938 }
939
940 #[test]
941 fn test_empty_packed() {
942 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
943
944 assert_eq!(frame.samples(), 1024);
945 assert_eq!(frame.channels(), 2);
946 assert_eq!(frame.sample_rate(), 48000);
947 assert_eq!(frame.format(), SampleFormat::F32);
948 assert_eq!(frame.num_planes(), 1);
949 assert_eq!(frame.total_size(), 1024 * 2 * 4);
950 }
951
952 #[test]
953 fn test_empty_planar() {
954 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
955
956 assert_eq!(frame.num_planes(), 2);
957 assert_eq!(frame.plane(0).map(|p| p.len()), Some(1024 * 4));
958 assert_eq!(frame.plane(1).map(|p| p.len()), Some(1024 * 4));
959 assert_eq!(frame.total_size(), 1024 * 2 * 4);
960 }
961
962 #[test]
963 fn test_empty_i16() {
964 let frame = AudioFrame::empty(1024, 2, 44100, SampleFormat::I16).unwrap();
965
966 assert_eq!(frame.bytes_per_sample(), 2);
967 assert_eq!(frame.total_size(), 1024 * 2 * 2);
968 }
969
970 #[test]
971 fn test_empty_other_format_error() {
972 let result = AudioFrame::empty(1024, 2, 48000, SampleFormat::Other(999));
973
974 assert!(result.is_err());
975 assert_eq!(
976 result.unwrap_err(),
977 FrameError::UnsupportedSampleFormat(SampleFormat::Other(999))
978 );
979 }
980
981 #[test]
982 fn test_default() {
983 let frame = AudioFrame::default();
984
985 assert_eq!(frame.samples(), 0);
986 assert_eq!(frame.channels(), 1);
987 assert_eq!(frame.sample_rate(), 48000);
988 assert_eq!(frame.format(), SampleFormat::F32);
989 }
990
991 // ==========================================================================
992 // Metadata Tests
993 // ==========================================================================
994
995 #[test]
996 fn test_duration() {
997 // 1024 samples at 48kHz = 0.02133... seconds
998 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
999 let duration = frame.duration();
1000 assert!((duration.as_secs_f64() - 0.021_333_333).abs() < 0.000_001);
1001
1002 // 48000 samples at 48kHz = 1 second
1003 let frame = AudioFrame::empty(48000, 2, 48000, SampleFormat::F32).unwrap();
1004 assert_eq!(frame.duration().as_secs(), 1);
1005 }
1006
1007 #[test]
1008 fn test_duration_zero_sample_rate() {
1009 let frame = AudioFrame::new(
1010 vec![vec![]],
1011 0,
1012 1,
1013 0,
1014 SampleFormat::F32,
1015 Timestamp::default(),
1016 )
1017 .unwrap();
1018
1019 assert_eq!(frame.duration(), Duration::ZERO);
1020 }
1021
1022 #[test]
1023 fn test_set_timestamp() {
1024 let mut frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
1025 let ts = Timestamp::new(48000, Rational::new(1, 48000));
1026
1027 frame.set_timestamp(ts);
1028 assert_eq!(frame.timestamp(), ts);
1029 }
1030
1031 // ==========================================================================
1032 // Plane Access Tests
1033 // ==========================================================================
1034
1035 #[test]
1036 fn test_plane_access_packed() {
1037 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
1038
1039 assert!(frame.plane(0).is_some());
1040 assert!(frame.plane(1).is_none());
1041 }
1042
1043 #[test]
1044 fn test_plane_access_planar() {
1045 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
1046
1047 assert!(frame.plane(0).is_some());
1048 assert!(frame.plane(1).is_some());
1049 assert!(frame.plane(2).is_none());
1050 }
1051
1052 #[test]
1053 fn test_plane_mut_access() {
1054 let mut frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
1055
1056 if let Some(data) = frame.plane_mut(0) {
1057 data[0] = 255;
1058 }
1059
1060 assert_eq!(frame.plane(0).unwrap()[0], 255);
1061 }
1062
1063 #[test]
1064 fn test_channel_access() {
1065 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
1066
1067 let left = frame.channel(0).unwrap();
1068 let right = frame.channel(1).unwrap();
1069
1070 assert_eq!(left.len(), 1024 * 4);
1071 assert_eq!(right.len(), 1024 * 4);
1072 }
1073
1074 // ==========================================================================
1075 // Data Access Tests
1076 // ==========================================================================
1077
1078 #[test]
1079 fn test_data_packed() {
1080 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
1081 assert!(frame.data().is_some());
1082 assert_eq!(frame.data().unwrap().len(), 1024 * 2 * 4);
1083 }
1084
1085 #[test]
1086 fn test_data_planar_returns_none() {
1087 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
1088 assert!(frame.data().is_none());
1089 }
1090
1091 #[test]
1092 fn test_data_mut() {
1093 let mut frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
1094
1095 if let Some(data) = frame.data_mut() {
1096 data[0] = 123;
1097 }
1098
1099 assert_eq!(frame.data().unwrap()[0], 123);
1100 }
1101
1102 // ==========================================================================
1103 // Typed Access Tests
1104 // ==========================================================================
1105
1106 #[test]
1107 fn test_as_f32() {
1108 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
1109 let samples = frame.as_f32().unwrap();
1110 assert_eq!(samples.len(), 1024 * 2);
1111 }
1112
1113 #[test]
1114 fn test_as_f32_wrong_format() {
1115 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::I16).unwrap();
1116 assert!(frame.as_f32().is_none());
1117 }
1118
1119 #[test]
1120 fn test_as_f32_mut() {
1121 let mut frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
1122
1123 if let Some(samples) = frame.as_f32_mut() {
1124 samples[0] = 1.0;
1125 samples[1] = -1.0;
1126 }
1127
1128 let samples = frame.as_f32().unwrap();
1129 assert!((samples[0] - 1.0).abs() < f32::EPSILON);
1130 assert!((samples[1] - (-1.0)).abs() < f32::EPSILON);
1131 }
1132
1133 #[test]
1134 fn test_as_i16() {
1135 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::I16).unwrap();
1136 let samples = frame.as_i16().unwrap();
1137 assert_eq!(samples.len(), 1024 * 2);
1138 }
1139
1140 #[test]
1141 fn test_as_i16_wrong_format() {
1142 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
1143 assert!(frame.as_i16().is_none());
1144 }
1145
1146 #[test]
1147 fn test_channel_as_f32() {
1148 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
1149
1150 let left = frame.channel_as_f32(0).unwrap();
1151 let right = frame.channel_as_f32(1).unwrap();
1152
1153 assert_eq!(left.len(), 1024);
1154 assert_eq!(right.len(), 1024);
1155 }
1156
1157 #[test]
1158 fn test_channel_as_f32_wrong_format() {
1159 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
1160 assert!(frame.channel_as_f32(0).is_none()); // F32 is packed, not F32p
1161 }
1162
1163 #[test]
1164 fn test_channel_as_f32_mut() {
1165 let mut frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
1166
1167 if let Some(left) = frame.channel_as_f32_mut(0) {
1168 left[0] = 0.5;
1169 }
1170
1171 assert!((frame.channel_as_f32(0).unwrap()[0] - 0.5).abs() < f32::EPSILON);
1172 }
1173
1174 #[test]
1175 fn test_channel_as_i16() {
1176 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::I16p).unwrap();
1177
1178 let left = frame.channel_as_i16(0).unwrap();
1179 assert_eq!(left.len(), 1024);
1180 }
1181
1182 #[test]
1183 fn test_channel_as_i16_mut() {
1184 let mut frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::I16p).unwrap();
1185
1186 if let Some(left) = frame.channel_as_i16_mut(0) {
1187 left[0] = 32767;
1188 }
1189
1190 assert_eq!(frame.channel_as_i16(0).unwrap()[0], 32767);
1191 }
1192
1193 // ==========================================================================
1194 // Utility Tests
1195 // ==========================================================================
1196
1197 #[test]
1198 fn test_total_size() {
1199 // Packed stereo F32: 1024 samples * 2 channels * 4 bytes
1200 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
1201 assert_eq!(frame.total_size(), 1024 * 2 * 4);
1202
1203 // Planar stereo F32p: 2 planes * 1024 samples * 4 bytes
1204 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32p).unwrap();
1205 assert_eq!(frame.total_size(), 1024 * 4 * 2);
1206 }
1207
1208 #[test]
1209 fn test_bytes_per_sample() {
1210 assert_eq!(
1211 AudioFrame::empty(1024, 2, 48000, SampleFormat::U8)
1212 .unwrap()
1213 .bytes_per_sample(),
1214 1
1215 );
1216 assert_eq!(
1217 AudioFrame::empty(1024, 2, 48000, SampleFormat::I16)
1218 .unwrap()
1219 .bytes_per_sample(),
1220 2
1221 );
1222 assert_eq!(
1223 AudioFrame::empty(1024, 2, 48000, SampleFormat::F32)
1224 .unwrap()
1225 .bytes_per_sample(),
1226 4
1227 );
1228 assert_eq!(
1229 AudioFrame::empty(1024, 2, 48000, SampleFormat::F64)
1230 .unwrap()
1231 .bytes_per_sample(),
1232 8
1233 );
1234 }
1235
1236 // ==========================================================================
1237 // Clone Tests
1238 // ==========================================================================
1239
1240 #[test]
1241 fn test_clone() {
1242 let mut original = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
1243 original.set_timestamp(Timestamp::new(1000, Rational::new(1, 1000)));
1244
1245 // Modify some data
1246 if let Some(data) = original.plane_mut(0) {
1247 data[0] = 42;
1248 }
1249
1250 let cloned = original.clone();
1251
1252 // Verify metadata matches
1253 assert_eq!(cloned.samples(), original.samples());
1254 assert_eq!(cloned.channels(), original.channels());
1255 assert_eq!(cloned.sample_rate(), original.sample_rate());
1256 assert_eq!(cloned.format(), original.format());
1257 assert_eq!(cloned.timestamp(), original.timestamp());
1258
1259 // Verify data was cloned
1260 assert_eq!(cloned.plane(0).unwrap()[0], 42);
1261
1262 // Verify it's a deep clone
1263 let mut cloned = cloned;
1264 if let Some(data) = cloned.plane_mut(0) {
1265 data[0] = 99;
1266 }
1267 assert_eq!(original.plane(0).unwrap()[0], 42);
1268 assert_eq!(cloned.plane(0).unwrap()[0], 99);
1269 }
1270
1271 // ==========================================================================
1272 // Display/Debug Tests
1273 // ==========================================================================
1274
1275 #[test]
1276 fn test_debug() {
1277 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
1278 let debug = format!("{frame:?}");
1279 assert!(debug.contains("AudioFrame"));
1280 assert!(debug.contains("1024"));
1281 assert!(debug.contains("48000"));
1282 assert!(debug.contains("F32"));
1283 }
1284
1285 #[test]
1286 fn test_display() {
1287 let frame = AudioFrame::empty(1024, 2, 48000, SampleFormat::F32).unwrap();
1288 let display = format!("{frame}");
1289 assert!(display.contains("1024 samples"));
1290 assert!(display.contains("2ch"));
1291 assert!(display.contains("48000Hz"));
1292 assert!(display.contains("flt")); // F32 displays as "flt"
1293 }
1294
1295 // ==========================================================================
1296 // All Sample Formats Tests
1297 // ==========================================================================
1298
1299 #[test]
1300 fn test_all_packed_formats() {
1301 let formats = [
1302 SampleFormat::U8,
1303 SampleFormat::I16,
1304 SampleFormat::I32,
1305 SampleFormat::F32,
1306 SampleFormat::F64,
1307 ];
1308
1309 for format in formats {
1310 let frame = AudioFrame::empty(1024, 2, 48000, format).unwrap();
1311 assert_eq!(frame.num_planes(), 1);
1312 assert!(frame.data().is_some());
1313 }
1314 }
1315
1316 #[test]
1317 fn test_all_planar_formats() {
1318 let formats = [
1319 SampleFormat::U8p,
1320 SampleFormat::I16p,
1321 SampleFormat::I32p,
1322 SampleFormat::F32p,
1323 SampleFormat::F64p,
1324 ];
1325
1326 for format in formats {
1327 let frame = AudioFrame::empty(1024, 2, 48000, format).unwrap();
1328 assert_eq!(frame.num_planes(), 2);
1329 assert!(frame.data().is_none());
1330 assert!(frame.channel(0).is_some());
1331 assert!(frame.channel(1).is_some());
1332 }
1333 }
1334
1335 #[test]
1336 fn test_mono_planar() {
1337 let frame = AudioFrame::empty(1024, 1, 48000, SampleFormat::F32p).unwrap();
1338 assert_eq!(frame.num_planes(), 1);
1339 assert_eq!(frame.plane(0).map(|p| p.len()), Some(1024 * 4));
1340 }
1341
1342 #[test]
1343 fn test_surround_planar() {
1344 // 5.1 surround sound
1345 let frame = AudioFrame::empty(1024, 6, 48000, SampleFormat::F32p).unwrap();
1346 assert_eq!(frame.num_planes(), 6);
1347 for i in 0..6 {
1348 assert!(frame.plane(i).is_some());
1349 }
1350 assert!(frame.plane(6).is_none());
1351 }
1352}