coreaudio/audio_unit/mod.rs
1//! This module is an attempt to provide a friendly, rust-esque interface to Apple's Audio Unit API.
2//!
3//! Learn more about the Audio Unit API [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40003278-CH1-SW2)
4//! and [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/TheAudioUnit/TheAudioUnit.html).
5//!
6//! TODO: The following are `kAudioUnitSubType`s (along with their const u32) generated by
7//! rust-bindgen that we could not find any documentation on:
8//!
9//! - MIDISynth = 1836284270,
10//! - RoundTripAAC = 1918984547,
11//! - SpatialMixer = 862217581,
12//! - SphericalHeadPanner = 1936746610,
13//! - VectorPanner = 1986158963,
14//! - SoundFieldPanner = 1634558569,
15//! - HRTFPanner = 1752331366,
16//! - NetReceive = 1852990326,
17//!
18//! If you can find documentation on these, please feel free to submit an issue or PR with the
19//! fixes!
20
21use objc2_audio_toolbox::{
22 kAudioUnitManufacturer_Apple, kAudioUnitProperty_SampleRate, kAudioUnitProperty_StreamFormat,
23 kAudioUnitScope_Global, kAudioUnitScope_Group, kAudioUnitScope_Input, kAudioUnitScope_Layer,
24 kAudioUnitScope_LayerItem, kAudioUnitScope_Note, kAudioUnitScope_Output, kAudioUnitScope_Part,
25 AudioComponentDescription, AudioComponentFindNext, AudioComponentInstanceDispose,
26 AudioComponentInstanceNew, AudioOutputUnitStart, AudioOutputUnitStop,
27 AudioUnit as InnerAudioUnit, AudioUnitGetProperty, AudioUnitInitialize, AudioUnitSetProperty,
28 AudioUnitUninitialize,
29};
30use objc2_core_audio_types::AudioBufferList;
31
32use crate::error::Error;
33use std::mem;
34use std::os::raw::{c_uint, c_void};
35use std::ptr::{self, NonNull};
36
37pub use self::audio_format::AudioFormat;
38pub use self::sample_format::{Sample, SampleFormat};
39pub use self::stream_format::StreamFormat;
40pub use self::types::{
41 EffectType, FormatConverterType, GeneratorType, IOType, MixerType, MusicDeviceType, Type,
42};
43
44#[cfg(target_os = "macos")]
45pub mod macos_helpers;
46
47pub mod audio_format;
48pub mod render_callback;
49pub mod sample_format;
50pub mod stream_format;
51pub mod types;
52
53/// The input and output **Scope**s.
54///
55/// More info [here](https://developer.apple.com/library/ios/documentation/AudioUnit/Reference/AudioUnitPropertiesReference/index.html#//apple_ref/doc/constant_group/Audio_Unit_Scopes)
56/// and [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/TheAudioUnit/TheAudioUnit.html).
57#[derive(Copy, Clone, Debug)]
58pub enum Scope {
59 Global = kAudioUnitScope_Global as isize,
60 Input = kAudioUnitScope_Input as isize,
61 Output = kAudioUnitScope_Output as isize,
62 Group = kAudioUnitScope_Group as isize,
63 Part = kAudioUnitScope_Part as isize,
64 Note = kAudioUnitScope_Note as isize,
65 Layer = kAudioUnitScope_Layer as isize,
66 LayerItem = kAudioUnitScope_LayerItem as isize,
67}
68
69/// Represents the **Input** and **Output** **Element**s.
70///
71/// These are used when specifying which **Element** we're setting the properties of.
72#[derive(Copy, Clone, Debug)]
73pub enum Element {
74 Output = 0,
75 Input = 1,
76}
77
78/// A rust representation of the [`objc2_audio_toolbox::AudioUnit`], including
79/// a pointer to the current rendering callback.
80///
81/// Find the original Audio Unit Programming Guide [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/TheAudioUnit/TheAudioUnit.html).
82pub struct AudioUnit {
83 instance: InnerAudioUnit,
84 maybe_render_callback: Option<*mut render_callback::InputProcFnWrapper>,
85 maybe_input_callback: Option<InputCallback>,
86}
87
88struct InputCallback {
89 // The audio buffer list to which input data is rendered.
90 buffer_list: *mut AudioBufferList,
91 callback: *mut render_callback::InputProcFnWrapper,
92}
93
94macro_rules! try_os_status {
95 ($expr:expr) => {
96 Error::from_os_status($expr)?
97 };
98}
99
100impl AudioUnit {
101 /// Construct a new AudioUnit with any type that may be automatically converted into
102 /// [**Type**](./enum.Type).
103 ///
104 /// Here is a list of compatible types:
105 ///
106 /// - [**Type**](./types/enum.Type)
107 /// - [**IOType**](./types/enum.IOType)
108 /// - [**MusicDeviceType**](./types/enum.MusicDeviceType)
109 /// - [**GeneratorType**](./types/enum.GeneratorType)
110 /// - [**FormatConverterType**](./types/enum.FormatConverterType)
111 /// - [**EffectType**](./types/enum.EffectType)
112 /// - [**MixerType**](./types/enum.MixerType)
113 ///
114 /// To construct the **AudioUnit** with some component flags, see
115 /// [**AudioUnit::new_with_flags**](./struct.AudioUnit#method.new_with_flags).
116 ///
117 /// Note: the `AudioUnit` is constructed with the `kAudioUnitManufacturer_Apple` Manufacturer
118 /// Identifier, as this is the only Audio Unit Manufacturer Identifier documented by Apple in
119 /// the AudioUnit reference (see [here](https://developer.apple.com/library/prerelease/mac/documentation/AudioUnit/Reference/AUComponentServicesReference/index.html#//apple_ref/doc/constant_group/Audio_Unit_Manufacturer_Identifier)).
120 pub fn new<T>(ty: T) -> Result<AudioUnit, Error>
121 where
122 T: Into<Type>,
123 {
124 AudioUnit::new_with_flags(ty, 0, 0)
125 }
126
127 /// The same as [**AudioUnit::new**](./struct.AudioUnit#method.new) but with the given
128 /// component flags and mask.
129 pub fn new_with_flags<T>(ty: T, flags: u32, mask: u32) -> Result<AudioUnit, Error>
130 where
131 T: Into<Type>,
132 {
133 const MANUFACTURER_IDENTIFIER: u32 = kAudioUnitManufacturer_Apple;
134 let au_type: Type = ty.into();
135 let sub_type_u32 = match au_type.as_subtype_u32() {
136 Some(u) => u,
137 None => return Err(Error::NoKnownSubtype),
138 };
139
140 // A description of the audio unit we desire.
141 let desc = AudioComponentDescription {
142 componentType: au_type.as_u32() as c_uint,
143 componentSubType: sub_type_u32 as c_uint,
144 componentManufacturer: MANUFACTURER_IDENTIFIER,
145 componentFlags: flags,
146 componentFlagsMask: mask,
147 };
148
149 unsafe {
150 // Find the default audio unit for the description.
151 //
152 // From the "Audio Unit Hosting Guide for iOS":
153 //
154 // Passing NULL to the first parameter of AudioComponentFindNext tells this function to
155 // find the first system audio unit matching the description, using a system-defined
156 // ordering. If you instead pass a previously found audio unit reference in this
157 // parameter, the function locates the next audio unit matching the description.
158 let component = AudioComponentFindNext(ptr::null_mut(), NonNull::from(&desc));
159 if component.is_null() {
160 return Err(Error::NoMatchingDefaultAudioUnitFound);
161 }
162
163 // Create an instance of the default audio unit using the component.
164 let mut instance_uninit = mem::MaybeUninit::<InnerAudioUnit>::uninit();
165 try_os_status!(AudioComponentInstanceNew(
166 component,
167 NonNull::from(&mut instance_uninit).cast()
168 ));
169 let instance: InnerAudioUnit = instance_uninit.assume_init();
170
171 // Initialise the audio unit!
172 try_os_status!(AudioUnitInitialize(instance));
173 Ok(AudioUnit {
174 instance,
175 maybe_render_callback: None,
176 maybe_input_callback: None,
177 })
178 }
179 }
180
181 /// On successful initialization, the audio formats for input and output are valid
182 /// and the audio unit is ready to render. During initialization, an audio unit
183 /// allocates memory according to the maximum number of audio frames it can produce
184 /// in response to a single render call.
185 ///
186 /// Usually, the state of an audio unit (such as its I/O formats and memory allocations)
187 /// cannot be changed while an audio unit is initialized.
188 pub fn initialize(&mut self) -> Result<(), Error> {
189 unsafe {
190 try_os_status!(AudioUnitInitialize(self.instance));
191 }
192 Ok(())
193 }
194
195 /// Before you change an initialize audio unit’s processing characteristics,
196 /// such as its input or output audio data format or its sample rate, you must
197 /// first uninitialize it. Calling this function deallocates the audio unit’s resources.
198 ///
199 /// After calling this function, you can reconfigure the audio unit and then call
200 /// AudioUnitInitialize to reinitialize it.
201 pub fn uninitialize(&mut self) -> Result<(), Error> {
202 unsafe {
203 try_os_status!(AudioUnitUninitialize(self.instance));
204 }
205 Ok(())
206 }
207
208 /// Sets the value for some property of the **AudioUnit**.
209 ///
210 /// To clear an audio unit property value, set the data parameter with `None::<()>`.
211 ///
212 /// Clearing properties only works for those properties that do not have a default value.
213 ///
214 /// For more on "properties" see [the reference](https://developer.apple.com/library/ios/documentation/AudioUnit/Reference/AudioUnitPropertiesReference/index.html#//apple_ref/doc/uid/TP40007288).
215 ///
216 /// **Available** in iOS 2.0 and later.
217 ///
218 /// Parameters
219 /// ----------
220 ///
221 /// - **id**: The identifier of the property.
222 /// - **scope**: The audio unit scope for the property.
223 /// - **elem**: The audio unit element for the property.
224 /// - **maybe_data**: The value that you want to apply to the property.
225 pub fn set_property<T>(
226 &mut self,
227 id: u32,
228 scope: Scope,
229 elem: Element,
230 maybe_data: Option<&T>,
231 ) -> Result<(), Error> {
232 unsafe { set_property(self.instance, id, scope, elem, maybe_data) }
233 }
234
235 /// Gets the value of an **AudioUnit** property.
236 ///
237 /// **Available** in iOS 2.0 and later.
238 ///
239 /// Parameters
240 /// ----------
241 ///
242 /// - **id**: The identifier of the property.
243 /// - **scope**: The audio unit scope for the property.
244 /// - **elem**: The audio unit element for the property.
245 pub fn get_property<T>(&self, id: u32, scope: Scope, elem: Element) -> Result<T, Error> {
246 unsafe { get_property(self.instance, id, scope, elem) }
247 }
248
249 /// Starts an I/O **AudioUnit**, which in turn starts the audio unit processing graph that it is
250 /// connected to.
251 ///
252 /// **Available** in OS X v10.0 and later.
253 pub fn start(&mut self) -> Result<(), Error> {
254 unsafe {
255 try_os_status!(AudioOutputUnitStart(self.instance));
256 }
257 Ok(())
258 }
259
260 /// Stops an I/O **AudioUnit**, which in turn stops the audio unit processing graph that it is
261 /// connected to.
262 ///
263 /// **Available** in OS X v10.0 and later.
264 pub fn stop(&mut self) -> Result<(), Error> {
265 unsafe {
266 try_os_status!(AudioOutputUnitStop(self.instance));
267 }
268 Ok(())
269 }
270
271 /// Set the **AudioUnit**'s sample rate.
272 ///
273 /// **Available** in iOS 2.0 and later.
274 pub fn set_sample_rate(&mut self, sample_rate: f64) -> Result<(), Error> {
275 let id = kAudioUnitProperty_SampleRate;
276 self.set_property(id, Scope::Input, Element::Output, Some(&sample_rate))
277 }
278
279 /// Get the **AudioUnit**'s sample rate.
280 pub fn sample_rate(&self) -> Result<f64, Error> {
281 let id = kAudioUnitProperty_SampleRate;
282 self.get_property(id, Scope::Input, Element::Output)
283 }
284
285 /// Sets the current **StreamFormat** for the AudioUnit.
286 ///
287 /// Core Audio uses slightly different defaults depending on the platform.
288 ///
289 /// From the Core Audio Overview:
290 ///
291 /// > The canonical formats in Core Audio are as follows:
292 /// >
293 /// > - iOS input and output: Linear PCM with 16-bit integer samples.
294 /// > - iOS audio units and other audio processing: Noninterleaved linear PCM with 8.24-bit
295 /// > fixed-point samples
296 /// > - Mac input and output: Linear PCM with 32-bit floating point samples.
297 /// > - Mac audio units and other audio processing: Noninterleaved linear PCM with 32-bit
298 /// > floating-point
299 pub fn set_stream_format(
300 &mut self,
301 stream_format: StreamFormat,
302 scope: Scope,
303 element: Element,
304 ) -> Result<(), Error> {
305 let id = kAudioUnitProperty_StreamFormat;
306 let asbd = stream_format.to_asbd();
307 self.set_property(id, scope, element, Some(&asbd))
308 }
309
310 /// Return the current Stream Format for the AudioUnit.
311 pub fn stream_format(&self, scope: Scope, element: Element) -> Result<StreamFormat, Error> {
312 let id = kAudioUnitProperty_StreamFormat;
313 let asbd = self.get_property(id, scope, element)?;
314 StreamFormat::from_asbd(asbd)
315 }
316
317 /// Return the current output Stream Format for the AudioUnit.
318 pub fn output_stream_format(&self) -> Result<StreamFormat, Error> {
319 self.stream_format(Scope::Input, Element::Output)
320 }
321
322 /// Return the current input Stream Format for the AudioUnit.
323 pub fn input_stream_format(&self) -> Result<StreamFormat, Error> {
324 self.stream_format(Scope::Output, Element::Input)
325 }
326}
327
328impl AsRef<InnerAudioUnit> for AudioUnit {
329 fn as_ref(&self) -> &InnerAudioUnit {
330 &self.instance
331 }
332}
333
334impl AsMut<InnerAudioUnit> for AudioUnit {
335 fn as_mut(&mut self) -> &mut InnerAudioUnit {
336 &mut self.instance
337 }
338}
339
340unsafe impl Send for AudioUnit {}
341
342impl Drop for AudioUnit {
343 fn drop(&mut self) {
344 unsafe {
345 use crate::error;
346
347 // We don't want to panic in `drop`, so we'll ignore returned errors.
348 //
349 // A user should explicitly terminate the `AudioUnit` if they want to handle errors (we
350 // still need to provide a way to actually do that).
351 self.stop().ok();
352 error::Error::from_os_status(AudioUnitUninitialize(self.instance)).ok();
353
354 self.free_render_callback();
355 self.free_input_callback();
356
357 error::Error::from_os_status(AudioComponentInstanceDispose(self.instance)).ok();
358 }
359 }
360}
361
362/// Sets the value for some property of the **AudioUnit**.
363///
364/// To clear an audio unit property value, set the data parameter with `None::<()>`.
365///
366/// Clearing properties only works for those properties that do not have a default value.
367///
368/// For more on "properties" see [the reference](https://developer.apple.com/library/ios/documentation/AudioUnit/Reference/AudioUnitPropertiesReference/index.html#//apple_ref/doc/uid/TP40007288).
369///
370/// **Available** in iOS 2.0 and later.
371///
372/// Parameters
373/// ----------
374///
375/// - **au**: The AudioUnit instance.
376/// - **id**: The identifier of the property.
377/// - **scope**: The audio unit scope for the property.
378/// - **elem**: The audio unit element for the property.
379/// - **maybe_data**: The value that you want to apply to the property.
380///
381/// Safety
382/// ------
383/// This function is safe as long as the **au** parameter is a valid pointer to an AudioUnit instance.
384/// The caller is responsible for ensuring this.
385/// For a safer alternative, consider using an [AudioUnit] instance
386/// and calling the associated [AudioUnit::set_property] method.
387pub unsafe fn set_property<T>(
388 au: InnerAudioUnit,
389 id: u32,
390 scope: Scope,
391 elem: Element,
392 maybe_data: Option<&T>,
393) -> Result<(), Error> {
394 let (data_ptr, size) = maybe_data
395 .map(|data| {
396 let ptr = data as *const _ as *const c_void;
397 let size = ::std::mem::size_of::<T>() as u32;
398 (ptr, size)
399 })
400 .unwrap_or_else(|| (::std::ptr::null(), 0));
401 let scope = scope as c_uint;
402 let elem = elem as c_uint;
403 try_os_status!(AudioUnitSetProperty(au, id, scope, elem, data_ptr, size));
404 Ok(())
405}
406
407/// Gets the value of an **AudioUnit** property.
408///
409/// **Available** in iOS 2.0 and later.
410///
411/// Parameters
412/// ----------
413///
414/// - **au**: The AudioUnit instance.
415/// - **id**: The identifier of the property.
416/// - **scope**: The audio unit scope for the property.
417/// - **elem**: The audio unit element for the property.
418///
419/// Safety
420/// ------
421/// This function is safe as long as the **au** parameter is a valid pointer to an AudioUnit instance.
422/// The caller is responsible for ensuring this.
423/// For a safer alternative, consider using an [AudioUnit] instance
424/// and calling the associated [AudioUnit::get_property] method.
425pub unsafe fn get_property<T>(
426 au: InnerAudioUnit,
427 id: u32,
428 scope: Scope,
429 elem: Element,
430) -> Result<T, Error> {
431 let scope = scope as c_uint;
432 let elem = elem as c_uint;
433 let mut size = ::std::mem::size_of::<T>() as u32;
434 let mut data_uninit = ::std::mem::MaybeUninit::<T>::uninit();
435 let data_ptr = NonNull::from(&mut data_uninit).cast::<c_void>();
436 let size_ptr = NonNull::from(&mut size);
437 try_os_status!(AudioUnitGetProperty(
438 au, id, scope, elem, data_ptr, size_ptr
439 ));
440 let data: T = data_uninit.assume_init();
441 Ok(data)
442}
443
444/// Gets the value of a specified audio session property.
445///
446/// **Available** in iOS 2.0 and later, and tvOS 9.0 and later.
447///
448/// Parameters
449/// ----------
450///
451/// - **id**: The identifier of the property.
452#[cfg(any(target_os = "ios", target_os = "tvos", target_os = "visionos"))]
453pub fn audio_session_get_property<T>(id: u32) -> Result<T, Error> {
454 let mut size = ::std::mem::size_of::<T>() as u32;
455 #[allow(deprecated)]
456 unsafe {
457 let mut data_uninit = ::std::mem::MaybeUninit::<T>::uninit();
458 let data_ptr = data_uninit.as_mut_ptr() as *mut _ as *mut c_void;
459 let size_ptr = &mut size as *mut _;
460 try_os_status!(objc2_audio_toolbox::AudioSessionGetProperty(
461 id, size_ptr, data_ptr
462 ));
463 let data: T = data_uninit.assume_init();
464 Ok(data)
465 }
466}