Skip to main content

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}