Skip to main content

core_audio/
audio_hardware.rs

1use std::{mem, ptr::NonNull};
2
3use core_foundation::base::OSStatus;
4use libc::c_void;
5use objc2_core_audio as sys;
6pub use sys::{
7    AudioClassID, AudioObjectID, AudioObjectPropertyAddress, AudioObjectPropertyElement, AudioObjectPropertyScope, AudioObjectPropertySelector,
8};
9
10use crate::base::status_to_result;
11
12#[derive(Clone, Copy, Debug, Eq, PartialEq)]
13pub struct AudioObject {
14    id: AudioObjectID,
15}
16
17impl AudioObject {
18    #[inline]
19    pub const fn new(id: AudioObjectID) -> Self {
20        Self {
21            id,
22        }
23    }
24
25    #[inline]
26    pub const fn system() -> Self {
27        Self::new(sys::kAudioObjectSystemObject as AudioObjectID)
28    }
29
30    #[inline]
31    pub const fn unknown() -> Self {
32        Self::new(sys::kAudioObjectUnknown)
33    }
34
35    #[inline]
36    pub const fn id(self) -> AudioObjectID {
37        self.id
38    }
39
40    #[inline]
41    pub fn show(self) {
42        unsafe { sys::AudioObjectShow(self.id) }
43    }
44
45    #[inline]
46    pub fn has_property(self, address: &AudioObjectPropertyAddress) -> bool {
47        unsafe { sys::AudioObjectHasProperty(self.id, NonNull::from(address)) }
48    }
49
50    #[inline]
51    pub fn is_property_settable(self, address: &AudioObjectPropertyAddress) -> Result<bool, OSStatus> {
52        let mut is_settable = 0;
53        let status = unsafe { sys::AudioObjectIsPropertySettable(self.id, NonNull::from(address), NonNull::from(&mut is_settable)) };
54        status_to_result(status).map(|_| is_settable != 0)
55    }
56
57    #[inline]
58    pub fn property_data_size(self, address: &AudioObjectPropertyAddress) -> Result<u32, OSStatus> {
59        self.property_data_size_with_qualifier(address, &[])
60    }
61
62    pub fn property_data_size_with_qualifier(self, address: &AudioObjectPropertyAddress, qualifier: &[u8]) -> Result<u32, OSStatus> {
63        let mut data_size = 0;
64        let status = unsafe {
65            sys::AudioObjectGetPropertyDataSize(
66                self.id,
67                NonNull::from(address),
68                qualifier.len() as u32,
69                qualifier.as_ptr().cast(),
70                NonNull::from(&mut data_size),
71            )
72        };
73        status_to_result(status).map(|_| data_size)
74    }
75
76    #[inline]
77    pub fn get_property<T: Copy>(self, address: &AudioObjectPropertyAddress) -> Result<T, OSStatus> {
78        self.get_property_with_qualifier(address, &[])
79    }
80
81    pub fn get_property_with_qualifier<T: Copy>(self, address: &AudioObjectPropertyAddress, qualifier: &[u8]) -> Result<T, OSStatus> {
82        let mut value = mem::MaybeUninit::<T>::uninit();
83        let mut data_size = mem::size_of::<T>() as u32;
84        let status = unsafe {
85            sys::AudioObjectGetPropertyData(
86                self.id,
87                NonNull::from(address),
88                qualifier.len() as u32,
89                qualifier.as_ptr().cast(),
90                NonNull::from(&mut data_size),
91                NonNull::new_unchecked(value.as_mut_ptr().cast()),
92            )
93        };
94        status_to_result(status)?;
95        Ok(unsafe { value.assume_init() })
96    }
97
98    #[inline]
99    pub fn get_property_bytes(self, address: &AudioObjectPropertyAddress) -> Result<Vec<u8>, OSStatus> {
100        self.get_property_bytes_with_qualifier(address, &[])
101    }
102
103    pub fn get_property_bytes_with_qualifier(self, address: &AudioObjectPropertyAddress, qualifier: &[u8]) -> Result<Vec<u8>, OSStatus> {
104        let mut bytes = vec![0; self.property_data_size_with_qualifier(address, qualifier)? as usize];
105        let mut data_size = bytes.len() as u32;
106        let out_data = NonNull::new(bytes.as_mut_ptr().cast::<c_void>()).unwrap_or_else(NonNull::dangling);
107        let status = unsafe {
108            sys::AudioObjectGetPropertyData(
109                self.id,
110                NonNull::from(address),
111                qualifier.len() as u32,
112                qualifier.as_ptr().cast(),
113                NonNull::from(&mut data_size),
114                out_data,
115            )
116        };
117        status_to_result(status)?;
118        bytes.truncate(data_size as usize);
119        Ok(bytes)
120    }
121
122    #[inline]
123    pub fn get_property_array<T: Copy>(self, address: &AudioObjectPropertyAddress) -> Result<Vec<T>, OSStatus> {
124        self.get_property_array_with_qualifier(address, &[])
125    }
126
127    pub fn get_property_array_with_qualifier<T: Copy>(self, address: &AudioObjectPropertyAddress, qualifier: &[u8]) -> Result<Vec<T>, OSStatus> {
128        let element_size = mem::size_of::<T>();
129        let data_size = self.property_data_size_with_qualifier(address, qualifier)? as usize;
130        if element_size == 0 || !data_size.is_multiple_of(element_size) {
131            return Err(sys::kAudioHardwareBadPropertySizeError);
132        }
133
134        let mut values = Vec::<T>::with_capacity(data_size / element_size);
135        let mut io_data_size = data_size as u32;
136        let out_data = NonNull::new(values.as_mut_ptr().cast::<c_void>()).unwrap_or_else(NonNull::dangling);
137        let status = unsafe {
138            sys::AudioObjectGetPropertyData(
139                self.id,
140                NonNull::from(address),
141                qualifier.len() as u32,
142                qualifier.as_ptr().cast(),
143                NonNull::from(&mut io_data_size),
144                out_data,
145            )
146        };
147        status_to_result(status)?;
148        if !(io_data_size as usize).is_multiple_of(element_size) {
149            return Err(sys::kAudioHardwareBadPropertySizeError);
150        }
151        unsafe { values.set_len(io_data_size as usize / element_size) };
152        Ok(values)
153    }
154
155    #[inline]
156    pub fn set_property<T: Copy>(self, address: &AudioObjectPropertyAddress, value: &T) -> Result<(), OSStatus> {
157        self.set_property_with_qualifier(address, &[], value)
158    }
159
160    pub fn set_property_with_qualifier<T: Copy>(self, address: &AudioObjectPropertyAddress, qualifier: &[u8], value: &T) -> Result<(), OSStatus> {
161        let status = unsafe {
162            sys::AudioObjectSetPropertyData(
163                self.id,
164                NonNull::from(address),
165                qualifier.len() as u32,
166                qualifier.as_ptr().cast(),
167                mem::size_of::<T>() as u32,
168                NonNull::from(value).cast(),
169            )
170        };
171        status_to_result(status)
172    }
173
174    #[inline]
175    pub fn set_property_bytes(self, address: &AudioObjectPropertyAddress, bytes: &[u8]) -> Result<(), OSStatus> {
176        self.set_property_bytes_with_qualifier(address, &[], bytes)
177    }
178
179    pub fn set_property_bytes_with_qualifier(self, address: &AudioObjectPropertyAddress, qualifier: &[u8], bytes: &[u8]) -> Result<(), OSStatus> {
180        let data = NonNull::new(bytes.as_ptr() as *mut c_void).unwrap_or_else(NonNull::dangling);
181        let status = unsafe {
182            sys::AudioObjectSetPropertyData(
183                self.id,
184                NonNull::from(address),
185                qualifier.len() as u32,
186                qualifier.as_ptr().cast(),
187                bytes.len() as u32,
188                data,
189            )
190        };
191        status_to_result(status)
192    }
193}
194
195impl From<AudioObjectID> for AudioObject {
196    #[inline]
197    fn from(id: AudioObjectID) -> Self {
198        Self::new(id)
199    }
200}
201
202#[inline]
203pub const fn property_address(
204    selector: AudioObjectPropertySelector,
205    scope: AudioObjectPropertyScope,
206    element: AudioObjectPropertyElement,
207) -> AudioObjectPropertyAddress {
208    AudioObjectPropertyAddress {
209        mSelector: selector,
210        mScope: scope,
211        mElement: element,
212    }
213}
214
215#[inline]
216pub const fn global_property_address(selector: AudioObjectPropertySelector) -> AudioObjectPropertyAddress {
217    property_address(selector, sys::kAudioObjectPropertyScopeGlobal, sys::kAudioObjectPropertyElementMain)
218}
219
220#[inline]
221pub const fn input_property_address(selector: AudioObjectPropertySelector) -> AudioObjectPropertyAddress {
222    property_address(selector, sys::kAudioObjectPropertyScopeInput, sys::kAudioObjectPropertyElementMain)
223}
224
225#[inline]
226pub const fn output_property_address(selector: AudioObjectPropertySelector) -> AudioObjectPropertyAddress {
227    property_address(selector, sys::kAudioObjectPropertyScopeOutput, sys::kAudioObjectPropertyElementMain)
228}