Skip to main content

oximedia_codec/
audio.rs

1//! Audio frame types and sample format definitions.
2//!
3//! This module provides common types for audio codec implementations,
4//! including frame representation and sample format handling.
5
6use crate::{CodecError, CodecResult};
7
8/// Audio sample format.
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum SampleFormat {
11    /// 32-bit floating point samples (normalized -1.0 to 1.0)
12    F32,
13    /// 16-bit signed integer samples
14    I16,
15    /// 32-bit signed integer samples
16    I32,
17    /// 8-bit unsigned integer samples
18    U8,
19}
20
21impl SampleFormat {
22    /// Returns the size in bytes of a single sample in this format.
23    #[must_use]
24    pub const fn sample_size(&self) -> usize {
25        match self {
26            Self::F32 => 4,
27            Self::I16 => 2,
28            Self::I32 => 4,
29            Self::U8 => 1,
30        }
31    }
32
33    /// Returns whether this format uses floating point representation.
34    #[must_use]
35    pub const fn is_float(&self) -> bool {
36        matches!(self, Self::F32)
37    }
38
39    /// Returns whether this format uses signed integer representation.
40    #[must_use]
41    pub const fn is_signed(&self) -> bool {
42        !matches!(self, Self::U8)
43    }
44}
45
46/// Audio channel layout.
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum ChannelLayout {
49    /// Single channel (mono)
50    Mono,
51    /// Two channels (stereo: left, right)
52    Stereo,
53    /// 5.1 surround sound
54    Surround51,
55    /// 7.1 surround sound
56    Surround71,
57    /// Custom channel count
58    Custom(u8),
59}
60
61impl ChannelLayout {
62    /// Returns the number of channels in this layout.
63    #[must_use]
64    pub const fn channel_count(&self) -> usize {
65        match self {
66            Self::Mono => 1,
67            Self::Stereo => 2,
68            Self::Surround51 => 6,
69            Self::Surround71 => 8,
70            Self::Custom(n) => *n as usize,
71        }
72    }
73}
74
75/// An audio frame containing decoded samples.
76///
77/// Audio frames store PCM samples in interleaved format for multi-channel audio.
78/// For example, stereo audio is stored as [L0, R0, L1, R1, L2, R2, ...].
79#[derive(Debug, Clone)]
80pub struct AudioFrame {
81    /// Raw sample data (interleaved if multi-channel)
82    pub samples: Vec<u8>,
83    /// Number of samples per channel
84    pub sample_count: usize,
85    /// Sample rate in Hz (e.g., 48000 for 48kHz)
86    pub sample_rate: u32,
87    /// Number of channels (1 = mono, 2 = stereo, etc.)
88    pub channels: usize,
89    /// Sample format
90    pub format: SampleFormat,
91    /// Presentation timestamp in sample units
92    pub pts: Option<i64>,
93    /// Duration in sample units
94    pub duration: Option<u64>,
95}
96
97impl AudioFrame {
98    /// Creates a new audio frame.
99    ///
100    /// # Arguments
101    ///
102    /// * `samples` - Raw sample data (interleaved if multi-channel)
103    /// * `sample_count` - Number of samples per channel
104    /// * `sample_rate` - Sample rate in Hz
105    /// * `channels` - Number of channels
106    /// * `format` - Sample format
107    pub fn new(
108        samples: Vec<u8>,
109        sample_count: usize,
110        sample_rate: u32,
111        channels: usize,
112        format: SampleFormat,
113    ) -> Self {
114        Self {
115            samples,
116            sample_count,
117            sample_rate,
118            channels,
119            format,
120            pts: None,
121            duration: None,
122        }
123    }
124
125    /// Creates a new audio frame with timing information.
126    pub fn with_timing(
127        samples: Vec<u8>,
128        sample_count: usize,
129        sample_rate: u32,
130        channels: usize,
131        format: SampleFormat,
132        pts: i64,
133        duration: u64,
134    ) -> Self {
135        Self {
136            samples,
137            sample_count,
138            sample_rate,
139            channels,
140            format,
141            pts: Some(pts),
142            duration: Some(duration),
143        }
144    }
145
146    /// Returns the total number of samples (all channels combined).
147    #[must_use]
148    pub const fn total_samples(&self) -> usize {
149        self.sample_count * self.channels
150    }
151
152    /// Returns the size in bytes of the audio data.
153    #[must_use]
154    pub fn byte_size(&self) -> usize {
155        self.total_samples() * self.format.sample_size()
156    }
157
158    /// Returns the duration in seconds.
159    #[must_use]
160    pub fn duration_seconds(&self) -> f64 {
161        f64::from(self.sample_count as u32) / f64::from(self.sample_rate)
162    }
163
164    /// Converts samples to f32 slice if format is F32.
165    ///
166    /// Note: This method is not available as it requires unsafe code.
167    /// Use `samples` field directly with proper byte-to-f32 conversion.
168    #[allow(dead_code)]
169    fn as_f32_internal(&self) -> CodecResult<Vec<f32>> {
170        if self.format != SampleFormat::F32 {
171            return Err(CodecError::InvalidData(
172                "Sample format is not F32".to_string(),
173            ));
174        }
175        if self.samples.len() % 4 != 0 {
176            return Err(CodecError::InvalidData(
177                "Sample data length is not a multiple of 4".to_string(),
178            ));
179        }
180        let mut result = Vec::with_capacity(self.samples.len() / 4);
181        for chunk in self.samples.chunks_exact(4) {
182            let bytes: [u8; 4] = [chunk[0], chunk[1], chunk[2], chunk[3]];
183            result.push(f32::from_le_bytes(bytes));
184        }
185        Ok(result)
186    }
187
188    /// Converts samples to i16 slice if format is I16.
189    ///
190    /// Note: This method is not available as it requires unsafe code.
191    /// Use `samples` field directly with proper byte-to-i16 conversion.
192    #[allow(dead_code)]
193    fn as_i16_internal(&self) -> CodecResult<Vec<i16>> {
194        if self.format != SampleFormat::I16 {
195            return Err(CodecError::InvalidData(
196                "Sample format is not I16".to_string(),
197            ));
198        }
199        if self.samples.len() % 2 != 0 {
200            return Err(CodecError::InvalidData(
201                "Sample data length is not a multiple of 2".to_string(),
202            ));
203        }
204        let mut result = Vec::with_capacity(self.samples.len() / 2);
205        for chunk in self.samples.chunks_exact(2) {
206            let bytes: [u8; 2] = [chunk[0], chunk[1]];
207            result.push(i16::from_le_bytes(bytes));
208        }
209        Ok(result)
210    }
211
212    /// Converts bytes to f32 and returns a new Vec.
213    pub fn to_f32(&self) -> CodecResult<Vec<f32>> {
214        match self.format {
215            SampleFormat::F32 => {
216                if self.samples.len() % 4 != 0 {
217                    return Err(CodecError::InvalidData(
218                        "Sample data length is not a multiple of 4".to_string(),
219                    ));
220                }
221                let mut result = Vec::with_capacity(self.samples.len() / 4);
222                for chunk in self.samples.chunks_exact(4) {
223                    let bytes: [u8; 4] = [chunk[0], chunk[1], chunk[2], chunk[3]];
224                    result.push(f32::from_le_bytes(bytes));
225                }
226                Ok(result)
227            }
228            SampleFormat::I16 => {
229                if self.samples.len() % 2 != 0 {
230                    return Err(CodecError::InvalidData(
231                        "Sample data length is not a multiple of 2".to_string(),
232                    ));
233                }
234                let mut result = Vec::with_capacity(self.samples.len() / 2);
235                for chunk in self.samples.chunks_exact(2) {
236                    let bytes: [u8; 2] = [chunk[0], chunk[1]];
237                    let i16_val = i16::from_le_bytes(bytes);
238                    result.push(f32::from(i16_val) / 32768.0);
239                }
240                Ok(result)
241            }
242            _ => Err(CodecError::InvalidData(
243                "Unsupported format conversion".to_string(),
244            )),
245        }
246    }
247
248    /// Converts bytes to i16 and returns a new Vec.
249    pub fn to_i16(&self) -> CodecResult<Vec<i16>> {
250        match self.format {
251            SampleFormat::I16 => {
252                if self.samples.len() % 2 != 0 {
253                    return Err(CodecError::InvalidData(
254                        "Sample data length is not a multiple of 2".to_string(),
255                    ));
256                }
257                let mut result = Vec::with_capacity(self.samples.len() / 2);
258                for chunk in self.samples.chunks_exact(2) {
259                    let bytes: [u8; 2] = [chunk[0], chunk[1]];
260                    result.push(i16::from_le_bytes(bytes));
261                }
262                Ok(result)
263            }
264            SampleFormat::F32 => {
265                if self.samples.len() % 4 != 0 {
266                    return Err(CodecError::InvalidData(
267                        "Sample data length is not a multiple of 4".to_string(),
268                    ));
269                }
270                let mut result = Vec::with_capacity(self.samples.len() / 4);
271                for chunk in self.samples.chunks_exact(4) {
272                    let bytes: [u8; 4] = [chunk[0], chunk[1], chunk[2], chunk[3]];
273                    let f32_val = f32::from_le_bytes(bytes);
274                    result.push((f32_val.clamp(-1.0, 1.0) * 32767.0) as i16);
275                }
276                Ok(result)
277            }
278            _ => Err(CodecError::InvalidData(
279                "Unsupported format conversion".to_string(),
280            )),
281        }
282    }
283}
284
285#[cfg(test)]
286mod tests {
287    use super::*;
288
289    #[test]
290    fn test_sample_format_size() {
291        assert_eq!(SampleFormat::F32.sample_size(), 4);
292        assert_eq!(SampleFormat::I16.sample_size(), 2);
293        assert_eq!(SampleFormat::I32.sample_size(), 4);
294        assert_eq!(SampleFormat::U8.sample_size(), 1);
295    }
296
297    #[test]
298    fn test_channel_layout_count() {
299        assert_eq!(ChannelLayout::Mono.channel_count(), 1);
300        assert_eq!(ChannelLayout::Stereo.channel_count(), 2);
301        assert_eq!(ChannelLayout::Surround51.channel_count(), 6);
302        assert_eq!(ChannelLayout::Surround71.channel_count(), 8);
303        assert_eq!(ChannelLayout::Custom(4).channel_count(), 4);
304    }
305
306    #[test]
307    fn test_audio_frame_creation() {
308        let samples = vec![0u8; 1920 * 2 * 4]; // 1920 samples, stereo, f32
309        let frame = AudioFrame::new(samples, 1920, 48000, 2, SampleFormat::F32);
310        assert_eq!(frame.sample_count, 1920);
311        assert_eq!(frame.sample_rate, 48000);
312        assert_eq!(frame.channels, 2);
313        assert_eq!(frame.total_samples(), 3840);
314        assert_eq!(frame.byte_size(), 15360);
315    }
316
317    #[test]
318    fn test_audio_frame_duration() {
319        let samples = vec![0u8; 480 * 2 * 4];
320        let frame = AudioFrame::new(samples, 480, 48000, 2, SampleFormat::F32);
321        assert!((frame.duration_seconds() - 0.01).abs() < 0.0001);
322    }
323}