Skip to main content

adk_audio/
frame.rs

1//! Canonical audio buffer type used throughout the crate.
2
3use bytes::Bytes;
4
5/// The canonical audio buffer — raw PCM-16 LE samples with metadata.
6///
7/// All `adk-audio` components produce and consume `AudioFrame` values,
8/// eliminating format negotiation between pipeline stages.
9///
10/// # Example
11///
12/// ```
13/// use adk_audio::AudioFrame;
14///
15/// let silence = AudioFrame::silence(16000, 1, 100);
16/// assert_eq!(silence.sample_rate, 16000);
17/// assert_eq!(silence.channels, 1);
18/// assert_eq!(silence.duration_ms, 100);
19/// ```
20#[derive(Clone, Debug, PartialEq)]
21pub struct AudioFrame {
22    /// Raw PCM-16 LE sample data.
23    pub data: Bytes,
24    /// Sample rate in Hz (e.g. 16000, 24000, 44100, 48000).
25    pub sample_rate: u32,
26    /// Number of channels (1 = mono, 2 = stereo).
27    pub channels: u8,
28    /// Duration in milliseconds, computed from data length.
29    pub duration_ms: u32,
30}
31
32impl AudioFrame {
33    /// Create a new `AudioFrame` from raw PCM-16 LE data.
34    ///
35    /// Duration is computed automatically from the data length, sample rate,
36    /// and channel count.
37    pub fn new(data: impl Into<Bytes>, sample_rate: u32, channels: u8) -> Self {
38        let data = data.into();
39        let samples_per_channel =
40            if channels > 0 && sample_rate > 0 { data.len() / 2 / channels as usize } else { 0 };
41        let duration_ms = if sample_rate > 0 {
42            (samples_per_channel as u64 * 1000 / sample_rate as u64) as u32
43        } else {
44            0
45        };
46        Self { data, sample_rate, channels, duration_ms }
47    }
48
49    /// View the raw data as a slice of i16 samples.
50    pub fn samples(&self) -> &[i16] {
51        if self.data.len() < 2 {
52            return &[];
53        }
54        // SAFETY: PCM-16 LE data is naturally aligned to i16 when the byte
55        // count is even. We ensure even length by truncating the last byte
56        // if needed (should never happen with well-formed data).
57        let even_len = self.data.len() & !1;
58        bytemuck::cast_slice(&self.data[..even_len])
59    }
60
61    /// Create a silent `AudioFrame` of the given duration.
62    pub fn silence(sample_rate: u32, channels: u8, duration_ms: u32) -> Self {
63        let n_samples = (sample_rate as usize * channels as usize * duration_ms as usize) / 1000;
64        Self { data: Bytes::from(vec![0u8; n_samples * 2]), sample_rate, channels, duration_ms }
65    }
66}
67
68/// Merge multiple `AudioFrame` values into a single contiguous frame.
69///
70/// All frames must share the same sample rate and channel count.
71/// Returns an empty frame if the input is empty.
72pub fn merge_frames(frames: &[AudioFrame]) -> AudioFrame {
73    if frames.is_empty() {
74        return AudioFrame::new(Bytes::new(), 16000, 1);
75    }
76    let sample_rate = frames[0].sample_rate;
77    let channels = frames[0].channels;
78    let total_len: usize = frames.iter().map(|f| f.data.len()).sum();
79    let mut buf = Vec::with_capacity(total_len);
80    for f in frames {
81        buf.extend_from_slice(&f.data);
82    }
83    AudioFrame::new(Bytes::from(buf), sample_rate, channels)
84}