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) or [`data_mut()`](Self::data_mut).
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 raw audio data as a mutable byte slice
72    pub fn data_mut(&mut self) -> &mut [u8] {
73        if self.data_ptr.is_null() || self.data_bytes_size == 0 {
74            &mut []
75        } else {
76            unsafe {
77                std::slice::from_raw_parts_mut(
78                    self.data_ptr.cast::<u8>(),
79                    self.data_bytes_size as usize,
80                )
81            }
82        }
83    }
84
85    /// Get the size of the data in bytes
86    #[must_use]
87    pub const fn data_byte_size(&self) -> usize {
88        self.data_bytes_size as usize
89    }
90}
91
92/// Reference to an audio buffer with convenience methods
93pub struct AudioBufferRef<'a> {
94    buffer: &'a AudioBuffer,
95}
96
97impl AudioBufferRef<'_> {
98    /// Get the size of the data in bytes
99    #[must_use]
100    pub const fn data_byte_size(&self) -> usize {
101        self.buffer.data_byte_size()
102    }
103
104    /// Get the raw audio data as a byte slice
105    #[must_use]
106    pub fn data(&self) -> &[u8] {
107        self.buffer.data()
108    }
109}
110
111impl std::fmt::Debug for AudioBufferRef<'_> {
112    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113        f.debug_struct("AudioBufferRef")
114            .field("channels", &self.buffer.number_channels)
115            .field("data_bytes", &self.buffer.data_bytes_size)
116            .finish()
117    }
118}
119
120impl std::fmt::Debug for AudioBuffer {
121    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122        f.debug_struct("AudioBuffer")
123            .field("number_channels", &self.number_channels)
124            .field("data_bytes_size", &self.data_bytes_size)
125            .finish_non_exhaustive()
126    }
127}
128
129/// List of audio buffers from an audio sample
130#[repr(C)]
131#[derive(Debug)]
132pub struct AudioBufferListRaw {
133    /// Number of `AudioBuffer` entries referenced by `buffers_ptr`.
134    pub(crate) num_buffers: u32,
135    /// Pointer to the contiguous `AudioBuffer` entries.
136    pub(crate) buffers_ptr: *mut AudioBuffer,
137    /// Cached Rust length for `buffers_ptr`.
138    pub(crate) buffers_len: usize,
139}
140
141/// List of audio buffers from an audio sample
142///
143/// Contains one or more [`AudioBuffer`]s, typically one per audio channel.
144/// Use [`iter()`](Self::iter) to iterate over the buffers.
145pub struct AudioBufferList {
146    /// Borrowed raw audio-buffer-list storage.
147    pub(crate) inner: AudioBufferListRaw,
148    /// Block buffer that owns the audio data - must be kept alive
149    pub(crate) block_buffer_ptr: *mut std::ffi::c_void,
150}
151
152impl AudioBufferList {
153    /// Get the number of buffers in the list
154    #[must_use]
155    pub const fn num_buffers(&self) -> usize {
156        self.inner.num_buffers as usize
157    }
158
159    /// Get a buffer by index
160    #[must_use]
161    pub fn get(&self, index: usize) -> Option<&AudioBuffer> {
162        if index >= self.num_buffers() {
163            None
164        } else {
165            unsafe { Some(&*self.inner.buffers_ptr.add(index)) }
166        }
167    }
168
169    /// Get a buffer reference by index
170    #[must_use]
171    pub fn buffer(&self, index: usize) -> Option<AudioBufferRef<'_>> {
172        self.get(index).map(|buffer| AudioBufferRef { buffer })
173    }
174
175    /// Get a mutable buffer by index
176    pub fn get_mut(&mut self, index: usize) -> Option<&mut AudioBuffer> {
177        if index >= self.num_buffers() {
178            None
179        } else {
180            unsafe { Some(&mut *self.inner.buffers_ptr.add(index)) }
181        }
182    }
183
184    /// Iterate over the audio buffers
185    #[must_use]
186    pub const fn iter(&self) -> AudioBufferListIter<'_> {
187        AudioBufferListIter {
188            list: self,
189            index: 0,
190        }
191    }
192}
193
194impl Drop for AudioBufferList {
195    fn drop(&mut self) {
196        // Free the buffers array allocated in Swift via UnsafeMutablePointer.allocate().
197        // Must use the system allocator (not Rust's global allocator) because Swift
198        // allocates with the system malloc. Using Vec::from_raw_parts here would route
199        // through the global allocator, which crashes when a custom allocator like
200        // mimalloc is active.
201        if !self.inner.buffers_ptr.is_null() {
202            unsafe {
203                use std::alloc::{GlobalAlloc, Layout, System};
204                let layout = Layout::array::<AudioBuffer>(self.inner.buffers_len)
205                    .expect("AudioBufferList layout overflow");
206                System.dealloc(self.inner.buffers_ptr.cast::<u8>(), layout);
207            }
208        }
209        // Release the block buffer that owns the audio data
210        if !self.block_buffer_ptr.is_null() {
211            unsafe {
212                ffi::cm_block_buffer_release(self.block_buffer_ptr);
213            }
214        }
215    }
216}
217
218impl<'a> IntoIterator for &'a AudioBufferList {
219    type Item = &'a AudioBuffer;
220    type IntoIter = AudioBufferListIter<'a>;
221
222    fn into_iter(self) -> Self::IntoIter {
223        self.iter()
224    }
225}
226
227impl fmt::Display for AudioBufferList {
228    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229        write!(f, "AudioBufferList({} buffers)", self.num_buffers())
230    }
231}
232
233impl fmt::Debug for AudioBufferList {
234    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
235        f.debug_struct("AudioBufferList")
236            .field("num_buffers", &self.num_buffers())
237            .finish()
238    }
239}
240
241/// Iterator over audio buffers in an [`AudioBufferList`]
242pub struct AudioBufferListIter<'a> {
243    list: &'a AudioBufferList,
244    index: usize,
245}
246
247impl std::fmt::Debug for AudioBufferListIter<'_> {
248    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
249        f.debug_struct("AudioBufferListIter")
250            .field("total", &self.list.num_buffers())
251            .field(
252                "remaining",
253                &(self.list.num_buffers().saturating_sub(self.index)),
254            )
255            .finish()
256    }
257}
258
259impl<'a> Iterator for AudioBufferListIter<'a> {
260    type Item = &'a AudioBuffer;
261
262    fn next(&mut self) -> Option<Self::Item> {
263        if self.index < self.list.num_buffers() {
264            let buffer = self.list.get(self.index);
265            self.index += 1;
266            buffer
267        } else {
268            None
269        }
270    }
271}