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 /// Construct a new AudioUnit with any type that may be automatically converted into
128 /// [**Type**](./enum.Type). This constructor leaves the audio unit uninitialized
129 /// for the caller to decide when to do it manually!
130 ///
131 /// Here is a list of compatible types:
132 ///
133 /// - [**Type**](./types/enum.Type)
134 /// - [**IOType**](./types/enum.IOType)
135 /// - [**MusicDeviceType**](./types/enum.MusicDeviceType)
136 /// - [**GeneratorType**](./types/enum.GeneratorType)
137 /// - [**FormatConverterType**](./types/enum.FormatConverterType)
138 /// - [**EffectType**](./types/enum.EffectType)
139 /// - [**MixerType**](./types/enum.MixerType)
140 ///
141 /// To construct the **AudioUnit** with some component flags, see
142 /// [**AudioUnit::new_with_flags**](./struct.AudioUnit#method.new_with_flags).
143 ///
144 /// Note: the `AudioUnit` is constructed with the `kAudioUnitManufacturer_Apple` Manufacturer
145 /// Identifier, as this is the only Audio Unit Manufacturer Identifier documented by Apple in
146 /// 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)).
147 pub fn new_uninitialized<T>(ty: T) -> Result<AudioUnit, Error>
148 where
149 T: Into<Type>,
150 {
151 AudioUnit::new_with_flags_uninitialized(ty, 0, 0)
152 }
153
154 /// The same as [**AudioUnit::new**](./struct.AudioUnit#method.new) but with the given
155 /// component flags and mask.
156 pub fn new_with_flags_uninitialized<T>(ty: T, flags: u32, mask: u32) -> Result<AudioUnit, Error>
157 where
158 T: Into<Type>,
159 {
160 const MANUFACTURER_IDENTIFIER: u32 = kAudioUnitManufacturer_Apple;
161 let au_type: Type = ty.into();
162 let sub_type_u32 = match au_type.as_subtype_u32() {
163 Some(u) => u,
164 None => return Err(Error::NoKnownSubtype),
165 };
166
167 // A description of the audio unit we desire.
168 let desc = AudioComponentDescription {
169 componentType: au_type.as_u32() as c_uint,
170 componentSubType: sub_type_u32 as c_uint,
171 componentManufacturer: MANUFACTURER_IDENTIFIER,
172 componentFlags: flags,
173 componentFlagsMask: mask,
174 };
175
176 unsafe {
177 // Find the default audio unit for the description.
178 //
179 // From the "Audio Unit Hosting Guide for iOS":
180 //
181 // Passing NULL to the first parameter of AudioComponentFindNext tells this function to
182 // find the first system audio unit matching the description, using a system-defined
183 // ordering. If you instead pass a previously found audio unit reference in this
184 // parameter, the function locates the next audio unit matching the description.
185 let component = AudioComponentFindNext(ptr::null_mut(), NonNull::from(&desc));
186 if component.is_null() {
187 return Err(Error::NoMatchingDefaultAudioUnitFound);
188 }
189
190 // Create an instance of the default audio unit using the component.
191 let mut instance_uninit = mem::MaybeUninit::<InnerAudioUnit>::uninit();
192 try_os_status!(AudioComponentInstanceNew(
193 component,
194 NonNull::from(&mut instance_uninit).cast()
195 ));
196 let instance: InnerAudioUnit = instance_uninit.assume_init();
197
198 Ok(AudioUnit {
199 instance,
200 maybe_render_callback: None,
201 maybe_input_callback: None,
202 })
203 }
204 }
205
206 /// The same as [**AudioUnit::new**](./struct.AudioUnit#method.new) but with the given
207 /// component flags and mask.
208 pub fn new_with_flags<T>(ty: T, flags: u32, mask: u32) -> Result<AudioUnit, Error>
209 where
210 T: Into<Type>,
211 {
212 let mut audio_unit = AudioUnit::new_with_flags_uninitialized(ty, flags, mask)?;
213 audio_unit.initialize()?;
214 Ok(audio_unit)
215 }
216
217 /// On successful initialization, the audio formats for input and output are valid
218 /// and the audio unit is ready to render. During initialization, an audio unit
219 /// allocates memory according to the maximum number of audio frames it can produce
220 /// in response to a single render call.
221 ///
222 /// Usually, the state of an audio unit (such as its I/O formats and memory allocations)
223 /// cannot be changed while an audio unit is initialized.
224 pub fn initialize(&mut self) -> Result<(), Error> {
225 unsafe {
226 try_os_status!(AudioUnitInitialize(self.instance));
227 }
228 Ok(())
229 }
230
231 /// Before you change an initialize audio unit’s processing characteristics,
232 /// such as its input or output audio data format or its sample rate, you must
233 /// first uninitialize it. Calling this function deallocates the audio unit’s resources.
234 ///
235 /// After calling this function, you can reconfigure the audio unit and then call
236 /// AudioUnitInitialize to reinitialize it.
237 pub fn uninitialize(&mut self) -> Result<(), Error> {
238 unsafe {
239 try_os_status!(AudioUnitUninitialize(self.instance));
240 }
241 Ok(())
242 }
243
244 /// Sets the value for some property of the **AudioUnit**.
245 ///
246 /// To clear an audio unit property value, set the data parameter with `None::<()>`.
247 ///
248 /// Clearing properties only works for those properties that do not have a default value.
249 ///
250 /// For more on "properties" see [the reference](https://developer.apple.com/library/ios/documentation/AudioUnit/Reference/AudioUnitPropertiesReference/index.html#//apple_ref/doc/uid/TP40007288).
251 ///
252 /// **Available** in iOS 2.0 and later.
253 ///
254 /// Parameters
255 /// ----------
256 ///
257 /// - **id**: The identifier of the property.
258 /// - **scope**: The audio unit scope for the property.
259 /// - **elem**: The audio unit element for the property.
260 /// - **maybe_data**: The value that you want to apply to the property.
261 pub fn set_property<T>(
262 &mut self,
263 id: u32,
264 scope: Scope,
265 elem: Element,
266 maybe_data: Option<&T>,
267 ) -> Result<(), Error> {
268 unsafe { set_property(self.instance, id, scope, elem, maybe_data) }
269 }
270
271 /// Gets the value of an **AudioUnit** property.
272 ///
273 /// **Available** in iOS 2.0 and later.
274 ///
275 /// Parameters
276 /// ----------
277 ///
278 /// - **id**: The identifier of the property.
279 /// - **scope**: The audio unit scope for the property.
280 /// - **elem**: The audio unit element for the property.
281 pub fn get_property<T>(&self, id: u32, scope: Scope, elem: Element) -> Result<T, Error> {
282 unsafe { get_property(self.instance, id, scope, elem) }
283 }
284
285 /// Starts an I/O **AudioUnit**, which in turn starts the audio unit processing graph that it is
286 /// connected to.
287 ///
288 /// **Available** in OS X v10.0 and later.
289 pub fn start(&mut self) -> Result<(), Error> {
290 unsafe {
291 try_os_status!(AudioOutputUnitStart(self.instance));
292 }
293 Ok(())
294 }
295
296 /// Stops an I/O **AudioUnit**, which in turn stops the audio unit processing graph that it is
297 /// connected to.
298 ///
299 /// **Available** in OS X v10.0 and later.
300 pub fn stop(&mut self) -> Result<(), Error> {
301 unsafe {
302 try_os_status!(AudioOutputUnitStop(self.instance));
303 }
304 Ok(())
305 }
306
307 /// Set the **AudioUnit**'s sample rate.
308 ///
309 /// **Available** in iOS 2.0 and later.
310 pub fn set_sample_rate(&mut self, sample_rate: f64) -> Result<(), Error> {
311 let id = kAudioUnitProperty_SampleRate;
312 self.set_property(id, Scope::Input, Element::Output, Some(&sample_rate))
313 }
314
315 /// Get the **AudioUnit**'s sample rate.
316 pub fn sample_rate(&self) -> Result<f64, Error> {
317 let id = kAudioUnitProperty_SampleRate;
318 self.get_property(id, Scope::Input, Element::Output)
319 }
320
321 /// Sets the current **StreamFormat** for the AudioUnit.
322 ///
323 /// Core Audio uses slightly different defaults depending on the platform.
324 ///
325 /// From the Core Audio Overview:
326 ///
327 /// > The canonical formats in Core Audio are as follows:
328 /// >
329 /// > - iOS input and output: Linear PCM with 16-bit integer samples.
330 /// > - iOS audio units and other audio processing: Noninterleaved linear PCM with 8.24-bit
331 /// > fixed-point samples
332 /// > - Mac input and output: Linear PCM with 32-bit floating point samples.
333 /// > - Mac audio units and other audio processing: Noninterleaved linear PCM with 32-bit
334 /// > floating-point
335 pub fn set_stream_format(
336 &mut self,
337 stream_format: StreamFormat,
338 scope: Scope,
339 element: Element,
340 ) -> Result<(), Error> {
341 let id = kAudioUnitProperty_StreamFormat;
342 let asbd = stream_format.to_asbd();
343 self.set_property(id, scope, element, Some(&asbd))
344 }
345
346 /// Return the current Stream Format for the AudioUnit.
347 pub fn stream_format(&self, scope: Scope, element: Element) -> Result<StreamFormat, Error> {
348 let id = kAudioUnitProperty_StreamFormat;
349 let asbd = self.get_property(id, scope, element)?;
350 StreamFormat::from_asbd(asbd)
351 }
352
353 /// Return the current output Stream Format for the AudioUnit.
354 pub fn output_stream_format(&self) -> Result<StreamFormat, Error> {
355 self.stream_format(Scope::Input, Element::Output)
356 }
357
358 /// Return the current input Stream Format for the AudioUnit.
359 pub fn input_stream_format(&self) -> Result<StreamFormat, Error> {
360 self.stream_format(Scope::Output, Element::Input)
361 }
362}
363
364impl AsRef<InnerAudioUnit> for AudioUnit {
365 fn as_ref(&self) -> &InnerAudioUnit {
366 &self.instance
367 }
368}
369
370impl AsMut<InnerAudioUnit> for AudioUnit {
371 fn as_mut(&mut self) -> &mut InnerAudioUnit {
372 &mut self.instance
373 }
374}
375
376unsafe impl Send for AudioUnit {}
377
378impl Drop for AudioUnit {
379 fn drop(&mut self) {
380 unsafe {
381 use crate::error;
382
383 // We don't want to panic in `drop`, so we'll ignore returned errors.
384 //
385 // A user should explicitly terminate the `AudioUnit` if they want to handle errors (we
386 // still need to provide a way to actually do that).
387 self.stop().ok();
388 error::Error::from_os_status(AudioUnitUninitialize(self.instance)).ok();
389
390 self.free_render_callback();
391 self.free_input_callback();
392
393 error::Error::from_os_status(AudioComponentInstanceDispose(self.instance)).ok();
394 }
395 }
396}
397
398/// Sets the value for some property of the **AudioUnit**.
399///
400/// To clear an audio unit property value, set the data parameter with `None::<()>`.
401///
402/// Clearing properties only works for those properties that do not have a default value.
403///
404/// For more on "properties" see [the reference](https://developer.apple.com/library/ios/documentation/AudioUnit/Reference/AudioUnitPropertiesReference/index.html#//apple_ref/doc/uid/TP40007288).
405///
406/// **Available** in iOS 2.0 and later.
407///
408/// Parameters
409/// ----------
410///
411/// - **au**: The AudioUnit instance.
412/// - **id**: The identifier of the property.
413/// - **scope**: The audio unit scope for the property.
414/// - **elem**: The audio unit element for the property.
415/// - **maybe_data**: The value that you want to apply to the property.
416///
417/// Safety
418/// ------
419/// This function is safe as long as the **au** parameter is a valid pointer to an AudioUnit instance.
420/// The caller is responsible for ensuring this.
421/// For a safer alternative, consider using an [AudioUnit] instance
422/// and calling the associated [AudioUnit::set_property] method.
423pub unsafe fn set_property<T>(
424 au: InnerAudioUnit,
425 id: u32,
426 scope: Scope,
427 elem: Element,
428 maybe_data: Option<&T>,
429) -> Result<(), Error> {
430 let (data_ptr, size) = maybe_data
431 .map(|data| {
432 let ptr = data as *const _ as *const c_void;
433 let size = ::std::mem::size_of::<T>() as u32;
434 (ptr, size)
435 })
436 .unwrap_or_else(|| (::std::ptr::null(), 0));
437 let scope = scope as c_uint;
438 let elem = elem as c_uint;
439 try_os_status!(AudioUnitSetProperty(au, id, scope, elem, data_ptr, size));
440 Ok(())
441}
442
443/// Gets the value of an **AudioUnit** property.
444///
445/// **Available** in iOS 2.0 and later.
446///
447/// Parameters
448/// ----------
449///
450/// - **au**: The AudioUnit instance.
451/// - **id**: The identifier of the property.
452/// - **scope**: The audio unit scope for the property.
453/// - **elem**: The audio unit element for the property.
454///
455/// Safety
456/// ------
457/// This function is safe as long as the **au** parameter is a valid pointer to an AudioUnit instance.
458/// The caller is responsible for ensuring this.
459/// For a safer alternative, consider using an [AudioUnit] instance
460/// and calling the associated [AudioUnit::get_property] method.
461pub unsafe fn get_property<T>(
462 au: InnerAudioUnit,
463 id: u32,
464 scope: Scope,
465 elem: Element,
466) -> Result<T, Error> {
467 let scope = scope as c_uint;
468 let elem = elem as c_uint;
469 let mut size = ::std::mem::size_of::<T>() as u32;
470 let mut data_uninit = ::std::mem::MaybeUninit::<T>::uninit();
471 let data_ptr = NonNull::from(&mut data_uninit).cast::<c_void>();
472 let size_ptr = NonNull::from(&mut size);
473 try_os_status!(AudioUnitGetProperty(
474 au, id, scope, elem, data_ptr, size_ptr
475 ));
476 let data: T = data_uninit.assume_init();
477 Ok(data)
478}
479
480/// Gets the value of a specified audio session property.
481///
482/// **Available** in iOS 2.0 and later, and tvOS 9.0 and later.
483///
484/// Parameters
485/// ----------
486///
487/// - **id**: The identifier of the property.
488#[cfg(any(target_os = "ios", target_os = "tvos", target_os = "visionos"))]
489pub fn audio_session_get_property<T>(id: u32) -> Result<T, Error> {
490 let mut size = ::std::mem::size_of::<T>() as u32;
491 #[allow(deprecated)]
492 unsafe {
493 let mut data_uninit = ::std::mem::MaybeUninit::<T>::uninit();
494 let data_ptr = data_uninit.as_mut_ptr() as *mut _ as *mut c_void;
495 let size_ptr = &mut size as *mut _;
496 try_os_status!(objc2_audio_toolbox::AudioSessionGetProperty(
497 id, size_ptr, data_ptr
498 ));
499 let data: T = data_uninit.assume_init();
500 Ok(data)
501 }
502}