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    pub(crate) num_buffers: u32,
134    pub(crate) buffers_ptr: *mut AudioBuffer,
135    pub(crate) buffers_len: usize,
136}
137
138/// List of audio buffers from an audio sample
139///
140/// Contains one or more [`AudioBuffer`]s, typically one per audio channel.
141/// Use [`iter()`](Self::iter) to iterate over the buffers.
142pub struct AudioBufferList {
143    pub(crate) inner: AudioBufferListRaw,
144    /// Block buffer that owns the audio data - must be kept alive
145    pub(crate) block_buffer_ptr: *mut std::ffi::c_void,
146}
147
148impl AudioBufferList {
149    /// Get the number of buffers in the list
150    #[must_use]
151    pub const fn num_buffers(&self) -> usize {
152        self.inner.num_buffers as usize
153    }
154
155    /// Get a buffer by index
156    #[must_use]
157    pub fn get(&self, index: usize) -> Option<&AudioBuffer> {
158        if index >= self.num_buffers() {
159            None
160        } else {
161            unsafe { Some(&*self.inner.buffers_ptr.add(index)) }
162        }
163    }
164
165    /// Get a buffer reference by index
166    #[must_use]
167    pub fn buffer(&self, index: usize) -> Option<AudioBufferRef<'_>> {
168        self.get(index).map(|buffer| AudioBufferRef { buffer })
169    }
170
171    /// Get a mutable buffer by index
172    pub fn get_mut(&mut self, index: usize) -> Option<&mut AudioBuffer> {
173        if index >= self.num_buffers() {
174            None
175        } else {
176            unsafe { Some(&mut *self.inner.buffers_ptr.add(index)) }
177        }
178    }
179
180    /// Iterate over the audio buffers
181    #[must_use]
182    pub const fn iter(&self) -> AudioBufferListIter<'_> {
183        AudioBufferListIter {
184            list: self,
185            index: 0,
186        }
187    }
188}
189
190impl Drop for AudioBufferList {
191    fn drop(&mut self) {
192        // Free the buffers array allocated in Swift via UnsafeMutablePointer.allocate().
193        // Must use the system allocator (not Rust's global allocator) because Swift
194        // allocates with the system malloc. Using Vec::from_raw_parts here would route
195        // through the global allocator, which crashes when a custom allocator like
196        // mimalloc is active.
197        if !self.inner.buffers_ptr.is_null() {
198            unsafe {
199                use std::alloc::{GlobalAlloc, Layout, System};
200                let layout = Layout::array::<AudioBuffer>(self.inner.buffers_len)
201                    .expect("AudioBufferList layout overflow");
202                System.dealloc(self.inner.buffers_ptr.cast::<u8>(), layout);
203            }
204        }
205        // Release the block buffer that owns the audio data
206        if !self.block_buffer_ptr.is_null() {
207            unsafe {
208                ffi::cm_block_buffer_release(self.block_buffer_ptr);
209            }
210        }
211    }
212}
213
214impl<'a> IntoIterator for &'a AudioBufferList {
215    type Item = &'a AudioBuffer;
216    type IntoIter = AudioBufferListIter<'a>;
217
218    fn into_iter(self) -> Self::IntoIter {
219        self.iter()
220    }
221}
222
223impl fmt::Display for AudioBufferList {
224    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225        write!(f, "AudioBufferList({} buffers)", self.num_buffers())
226    }
227}
228
229impl fmt::Debug for AudioBufferList {
230    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231        f.debug_struct("AudioBufferList")
232            .field("num_buffers", &self.num_buffers())
233            .finish()
234    }
235}
236
237/// Iterator over audio buffers in an [`AudioBufferList`]
238pub struct AudioBufferListIter<'a> {
239    list: &'a AudioBufferList,
240    index: usize,
241}
242
243impl std::fmt::Debug for AudioBufferListIter<'_> {
244    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
245        f.debug_struct("AudioBufferListIter")
246            .field("total", &self.list.num_buffers())
247            .field(
248                "remaining",
249                &(self.list.num_buffers().saturating_sub(self.index)),
250            )
251            .finish()
252    }
253}
254
255impl<'a> Iterator for AudioBufferListIter<'a> {
256    type Item = &'a AudioBuffer;
257
258    fn next(&mut self) -> Option<Self::Item> {
259        if self.index < self.list.num_buffers() {
260            let buffer = self.list.get(self.index);
261            self.index += 1;
262            buffer
263        } else {
264            None
265        }
266    }
267}