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}