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}