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            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}