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