use objc2_audio_toolbox::{
kAudioUnitManufacturer_Apple, kAudioUnitProperty_SampleRate, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Global, kAudioUnitScope_Group, kAudioUnitScope_Input, kAudioUnitScope_Layer,
kAudioUnitScope_LayerItem, kAudioUnitScope_Note, kAudioUnitScope_Output, kAudioUnitScope_Part,
AudioComponentDescription, AudioComponentFindNext, AudioComponentInstanceDispose,
AudioComponentInstanceNew, AudioOutputUnitStart, AudioOutputUnitStop,
AudioUnit as InnerAudioUnit, AudioUnitGetProperty, AudioUnitInitialize, AudioUnitSetProperty,
AudioUnitUninitialize,
};
use objc2_core_audio_types::AudioBufferList;
use crate::error::Error;
use std::mem;
use std::os::raw::{c_uint, c_void};
use std::ptr::{self, NonNull};
pub use self::audio_format::AudioFormat;
pub use self::sample_format::{Sample, SampleFormat};
pub use self::stream_format::StreamFormat;
pub use self::types::{
EffectType, FormatConverterType, GeneratorType, IOType, MixerType, MusicDeviceType, Type,
};
#[cfg(target_os = "macos")]
pub mod macos_helpers;
pub mod audio_format;
pub mod render_callback;
pub mod sample_format;
pub mod stream_format;
pub mod types;
#[derive(Copy, Clone, Debug)]
pub enum Scope {
Global = kAudioUnitScope_Global as isize,
Input = kAudioUnitScope_Input as isize,
Output = kAudioUnitScope_Output as isize,
Group = kAudioUnitScope_Group as isize,
Part = kAudioUnitScope_Part as isize,
Note = kAudioUnitScope_Note as isize,
Layer = kAudioUnitScope_Layer as isize,
LayerItem = kAudioUnitScope_LayerItem as isize,
}
#[derive(Copy, Clone, Debug)]
pub enum Element {
Output = 0,
Input = 1,
}
pub struct AudioUnit {
instance: InnerAudioUnit,
maybe_render_callback: Option<*mut render_callback::InputProcFnWrapper>,
maybe_input_callback: Option<InputCallback>,
}
struct InputCallback {
buffer_list: *mut AudioBufferList,
callback: *mut render_callback::InputProcFnWrapper,
}
macro_rules! try_os_status {
($expr:expr) => {
Error::from_os_status($expr)?
};
}
impl AudioUnit {
pub fn new<T>(ty: T) -> Result<AudioUnit, Error>
where
T: Into<Type>,
{
AudioUnit::new_with_flags(ty, 0, 0)
}
pub fn new_with_flags<T>(ty: T, flags: u32, mask: u32) -> Result<AudioUnit, Error>
where
T: Into<Type>,
{
const MANUFACTURER_IDENTIFIER: u32 = kAudioUnitManufacturer_Apple;
let au_type: Type = ty.into();
let sub_type_u32 = match au_type.as_subtype_u32() {
Some(u) => u,
None => return Err(Error::NoKnownSubtype),
};
let desc = AudioComponentDescription {
componentType: au_type.as_u32() as c_uint,
componentSubType: sub_type_u32 as c_uint,
componentManufacturer: MANUFACTURER_IDENTIFIER,
componentFlags: flags,
componentFlagsMask: mask,
};
unsafe {
let component = AudioComponentFindNext(ptr::null_mut(), NonNull::from(&desc));
if component.is_null() {
return Err(Error::NoMatchingDefaultAudioUnitFound);
}
let mut instance_uninit = mem::MaybeUninit::<InnerAudioUnit>::uninit();
try_os_status!(AudioComponentInstanceNew(
component,
NonNull::from(&mut instance_uninit).cast()
));
let instance: InnerAudioUnit = instance_uninit.assume_init();
try_os_status!(AudioUnitInitialize(instance));
Ok(AudioUnit {
instance,
maybe_render_callback: None,
maybe_input_callback: None,
})
}
}
pub fn initialize(&mut self) -> Result<(), Error> {
unsafe {
try_os_status!(AudioUnitInitialize(self.instance));
}
Ok(())
}
pub fn uninitialize(&mut self) -> Result<(), Error> {
unsafe {
try_os_status!(AudioUnitUninitialize(self.instance));
}
Ok(())
}
pub fn set_property<T>(
&mut self,
id: u32,
scope: Scope,
elem: Element,
maybe_data: Option<&T>,
) -> Result<(), Error> {
unsafe { set_property(self.instance, id, scope, elem, maybe_data) }
}
pub fn get_property<T>(&self, id: u32, scope: Scope, elem: Element) -> Result<T, Error> {
unsafe { get_property(self.instance, id, scope, elem) }
}
pub fn start(&mut self) -> Result<(), Error> {
unsafe {
try_os_status!(AudioOutputUnitStart(self.instance));
}
Ok(())
}
pub fn stop(&mut self) -> Result<(), Error> {
unsafe {
try_os_status!(AudioOutputUnitStop(self.instance));
}
Ok(())
}
pub fn set_sample_rate(&mut self, sample_rate: f64) -> Result<(), Error> {
let id = kAudioUnitProperty_SampleRate;
self.set_property(id, Scope::Input, Element::Output, Some(&sample_rate))
}
pub fn sample_rate(&self) -> Result<f64, Error> {
let id = kAudioUnitProperty_SampleRate;
self.get_property(id, Scope::Input, Element::Output)
}
pub fn set_stream_format(
&mut self,
stream_format: StreamFormat,
scope: Scope,
element: Element,
) -> Result<(), Error> {
let id = kAudioUnitProperty_StreamFormat;
let asbd = stream_format.to_asbd();
self.set_property(id, scope, element, Some(&asbd))
}
pub fn stream_format(&self, scope: Scope, element: Element) -> Result<StreamFormat, Error> {
let id = kAudioUnitProperty_StreamFormat;
let asbd = self.get_property(id, scope, element)?;
StreamFormat::from_asbd(asbd)
}
pub fn output_stream_format(&self) -> Result<StreamFormat, Error> {
self.stream_format(Scope::Input, Element::Output)
}
pub fn input_stream_format(&self) -> Result<StreamFormat, Error> {
self.stream_format(Scope::Output, Element::Input)
}
}
impl AsRef<InnerAudioUnit> for AudioUnit {
fn as_ref(&self) -> &InnerAudioUnit {
&self.instance
}
}
impl AsMut<InnerAudioUnit> for AudioUnit {
fn as_mut(&mut self) -> &mut InnerAudioUnit {
&mut self.instance
}
}
unsafe impl Send for AudioUnit {}
impl Drop for AudioUnit {
fn drop(&mut self) {
unsafe {
use crate::error;
self.stop().ok();
error::Error::from_os_status(AudioUnitUninitialize(self.instance)).ok();
self.free_render_callback();
self.free_input_callback();
error::Error::from_os_status(AudioComponentInstanceDispose(self.instance)).ok();
}
}
}
pub unsafe fn set_property<T>(
au: InnerAudioUnit,
id: u32,
scope: Scope,
elem: Element,
maybe_data: Option<&T>,
) -> Result<(), Error> {
let (data_ptr, size) = maybe_data
.map(|data| {
let ptr = data as *const _ as *const c_void;
let size = ::std::mem::size_of::<T>() as u32;
(ptr, size)
})
.unwrap_or_else(|| (::std::ptr::null(), 0));
let scope = scope as c_uint;
let elem = elem as c_uint;
try_os_status!(AudioUnitSetProperty(au, id, scope, elem, data_ptr, size));
Ok(())
}
pub unsafe fn get_property<T>(
au: InnerAudioUnit,
id: u32,
scope: Scope,
elem: Element,
) -> Result<T, Error> {
let scope = scope as c_uint;
let elem = elem as c_uint;
let mut size = ::std::mem::size_of::<T>() as u32;
let mut data_uninit = ::std::mem::MaybeUninit::<T>::uninit();
let data_ptr = NonNull::from(&mut data_uninit).cast::<c_void>();
let size_ptr = NonNull::from(&mut size);
try_os_status!(AudioUnitGetProperty(
au, id, scope, elem, data_ptr, size_ptr
));
let data: T = data_uninit.assume_init();
Ok(data)
}
#[cfg(any(target_os = "ios", target_os = "tvos", target_os = "visionos"))]
pub fn audio_session_get_property<T>(id: u32) -> Result<T, Error> {
let mut size = ::std::mem::size_of::<T>() as u32;
#[allow(deprecated)]
unsafe {
let mut data_uninit = ::std::mem::MaybeUninit::<T>::uninit();
let data_ptr = data_uninit.as_mut_ptr() as *mut _ as *mut c_void;
let size_ptr = &mut size as *mut _;
try_os_status!(objc2_audio_toolbox::AudioSessionGetProperty(
id, size_ptr, data_ptr
));
let data: T = data_uninit.assume_init();
Ok(data)
}
}