Skip to main content

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}