Skip to main content

apple_cf/cm/
audio.rs

1//! Audio buffer types for captured audio samples
2//!
3//! This module provides types for accessing audio data from captured samples.
4//!
5//! ## Main Types
6//!
7//! - [`AudioBuffer`] - Single audio buffer containing sample data
8//! - [`AudioBufferList`] - Collection of audio buffers (typically one per channel)
9//! - [`AudioBufferRef`] - Reference to an audio buffer with convenience methods
10
11use crate::ffi;
12use std::fmt;
13
14/// Raw audio buffer containing sample data
15///
16/// An `AudioBuffer` represents a single channel or interleaved audio data.
17/// Access the raw bytes via [`data()`](Self::data).
18#[repr(C)]
19pub struct AudioBuffer {
20    /// Number of audio channels in this buffer
21    pub number_channels: u32,
22    /// Size of the audio data in bytes
23    pub data_bytes_size: u32,
24    data_ptr: *mut std::ffi::c_void,
25}
26
27impl PartialEq for AudioBuffer {
28    fn eq(&self, other: &Self) -> bool {
29        self.number_channels == other.number_channels
30            && self.data_bytes_size == other.data_bytes_size
31            && self.data_ptr == other.data_ptr
32    }
33}
34
35impl Eq for AudioBuffer {}
36
37impl std::hash::Hash for AudioBuffer {
38    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
39        self.number_channels.hash(state);
40        self.data_bytes_size.hash(state);
41        self.data_ptr.hash(state);
42    }
43}
44
45impl fmt::Display for AudioBuffer {
46    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47        write!(
48            f,
49            "AudioBuffer({} channels, {} bytes)",
50            self.number_channels, self.data_bytes_size
51        )
52    }
53}
54
55impl AudioBuffer {
56    /// Get the raw audio data as a byte slice
57    #[must_use]
58    pub fn data(&self) -> &[u8] {
59        if self.data_ptr.is_null() || self.data_bytes_size == 0 {
60            &[]
61        } else {
62            unsafe {
63                std::slice::from_raw_parts(
64                    self.data_ptr as *const u8,
65                    self.data_bytes_size as usize,
66                )
67            }
68        }
69    }
70
71    /// Get the size of the data in bytes
72    #[must_use]
73    pub const fn data_byte_size(&self) -> usize {
74        self.data_bytes_size as usize
75    }
76}
77
78/// Reference to an audio buffer with convenience methods
79pub struct AudioBufferRef<'a> {
80    buffer: &'a AudioBuffer,
81}
82
83impl AudioBufferRef<'_> {
84    /// Get the size of the data in bytes
85    #[must_use]
86    pub const fn data_byte_size(&self) -> usize {
87        self.buffer.data_byte_size()
88    }
89
90    /// Get the raw audio data as a byte slice
91    #[must_use]
92    pub fn data(&self) -> &[u8] {
93        self.buffer.data()
94    }
95}
96
97impl std::fmt::Debug for AudioBufferRef<'_> {
98    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99        f.debug_struct("AudioBufferRef")
100            .field("channels", &self.buffer.number_channels)
101            .field("data_bytes", &self.buffer.data_bytes_size)
102            .finish()
103    }
104}
105
106impl std::fmt::Debug for AudioBuffer {
107    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108        f.debug_struct("AudioBuffer")
109            .field("number_channels", &self.number_channels)
110            .field("data_bytes_size", &self.data_bytes_size)
111            .finish_non_exhaustive()
112    }
113}
114
115/// List of audio buffers from an audio sample
116#[repr(C)]
117#[derive(Debug)]
118pub struct AudioBufferListRaw {
119    /// Number of `AudioBuffer` entries referenced by `buffers_ptr`.
120    pub(crate) num_buffers: u32,
121    /// Pointer to the contiguous `AudioBuffer` entries.
122    pub(crate) buffers_ptr: *mut AudioBuffer,
123    /// Cached Rust length for `buffers_ptr`.
124    pub(crate) buffers_len: usize,
125}
126
127/// List of audio buffers from an audio sample
128///
129/// Contains one or more [`AudioBuffer`]s, typically one per audio channel.
130/// Use [`iter()`](Self::iter) to iterate over the buffers.
131pub struct AudioBufferList {
132    /// Borrowed raw audio-buffer-list storage.
133    pub(crate) inner: AudioBufferListRaw,
134    /// Block buffer that owns the audio data - must be kept alive
135    pub(crate) block_buffer_ptr: *mut std::ffi::c_void,
136}
137
138impl AudioBufferList {
139    pub(crate) unsafe fn from_bridge(
140        num_buffers: u32,
141        buffers_ptr: *mut AudioBuffer,
142        buffers_len: usize,
143        block_buffer_ptr: *mut std::ffi::c_void,
144    ) -> Option<Self> {
145        let list = Self {
146            inner: AudioBufferListRaw {
147                num_buffers,
148                buffers_ptr,
149                buffers_len,
150            },
151            block_buffer_ptr,
152        };
153        let consistent = !buffers_ptr.is_null()
154            && !block_buffer_ptr.is_null()
155            && usize::try_from(num_buffers).is_ok_and(|count| count == buffers_len);
156        consistent.then_some(list)
157    }
158
159    /// Get the number of buffers in the list
160    #[must_use]
161    pub const fn num_buffers(&self) -> usize {
162        self.inner.num_buffers as usize
163    }
164
165    /// Get a buffer by index
166    #[must_use]
167    pub fn get(&self, index: usize) -> Option<&AudioBuffer> {
168        if index >= self.num_buffers() {
169            None
170        } else {
171            unsafe { Some(&*self.inner.buffers_ptr.add(index)) }
172        }
173    }
174
175    /// Get a buffer reference by index
176    #[must_use]
177    pub fn buffer(&self, index: usize) -> Option<AudioBufferRef<'_>> {
178        self.get(index).map(|buffer| AudioBufferRef { buffer })
179    }
180
181    /// Iterate over the audio buffers
182    #[must_use]
183    pub const fn iter(&self) -> AudioBufferListIter<'_> {
184        AudioBufferListIter {
185            list: self,
186            index: 0,
187        }
188    }
189}
190
191impl Drop for AudioBufferList {
192    fn drop(&mut self) {
193        if !self.inner.buffers_ptr.is_null() {
194            unsafe { ffi::acf_cm_audio_buffer_array_free(self.inner.buffers_ptr.cast()) };
195        }
196        // Release the block buffer that owns the audio data
197        if !self.block_buffer_ptr.is_null() {
198            unsafe {
199                ffi::cm_block_buffer_release(self.block_buffer_ptr);
200            }
201        }
202    }
203}
204
205impl<'a> IntoIterator for &'a AudioBufferList {
206    type Item = &'a AudioBuffer;
207    type IntoIter = AudioBufferListIter<'a>;
208
209    fn into_iter(self) -> Self::IntoIter {
210        self.iter()
211    }
212}
213
214impl fmt::Display for AudioBufferList {
215    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216        write!(f, "AudioBufferList({} buffers)", self.num_buffers())
217    }
218}
219
220impl fmt::Debug for AudioBufferList {
221    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222        f.debug_struct("AudioBufferList")
223            .field("num_buffers", &self.num_buffers())
224            .finish()
225    }
226}
227
228/// Iterator over audio buffers in an [`AudioBufferList`]
229pub struct AudioBufferListIter<'a> {
230    list: &'a AudioBufferList,
231    index: usize,
232}
233
234impl std::fmt::Debug for AudioBufferListIter<'_> {
235    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
236        f.debug_struct("AudioBufferListIter")
237            .field("total", &self.list.num_buffers())
238            .field(
239                "remaining",
240                &(self.list.num_buffers().saturating_sub(self.index)),
241            )
242            .finish()
243    }
244}
245
246impl<'a> Iterator for AudioBufferListIter<'a> {
247    type Item = &'a AudioBuffer;
248
249    fn next(&mut self) -> Option<Self::Item> {
250        if self.index < self.list.num_buffers() {
251            let buffer = self.list.get(self.index);
252            self.index += 1;
253            buffer
254        } else {
255            None
256        }
257    }
258}