use std::{ffi::c_void, marker::PhantomData};
use crate::{
arc,
at::{
self,
audio::{
self, StreamBasicDesc,
component::{InitializedState, State, UninitializedState},
unit::ScheduledSlice,
},
},
cf, define_opts, os,
};
use super::{ParamInfo, RenderCbStruct};
#[derive(Debug)]
#[repr(transparent)]
pub struct Unit(audio::component::Instance);
impl State<Unit> for UninitializedState {}
impl State<Unit> for InitializedState {
fn release_resources(unit: &mut Unit) -> os::Result {
unsafe { AudioUnitUninitialize(unit).result() }
}
}
#[derive(Debug)]
pub struct UnitRef<S>(&'static mut Unit, PhantomData<S>)
where
S: State<Unit>;
impl<S: State<Unit>> Drop for UnitRef<S> {
fn drop(&mut self) {
let res = S::release_resources(self.0);
debug_assert!(res.is_ok());
let res = unsafe { self.0.0.dispose() };
debug_assert!(res.is_ok());
}
}
#[doc(alias = "AudioUnitType")]
#[repr(transparent)]
pub struct Type(pub u32);
impl Type {
#[doc(alias = "kAudioUnitType_Output")]
pub const OUTPUT: Self = Self(u32::from_be_bytes(*b"auou"));
#[doc(alias = "kAudioUnitType_MusicDevice")]
pub const MUSIC_DEVICE: Self = Self(u32::from_be_bytes(*b"aumu"));
#[doc(alias = "kAudioUnitType_MusicEffect")]
pub const MUSIC_EFFECT: Self = Self(u32::from_be_bytes(*b"aumf"));
#[doc(alias = "kAudioUnitType_FormatConverter")]
pub const FORMAT_CONVERTER: Self = Self(u32::from_be_bytes(*b"aufc"));
#[doc(alias = "kAudioUnitType_Effect")]
pub const EFFECT: Self = Self(u32::from_be_bytes(*b"aufx"));
#[doc(alias = "kAudioUnitType_Mixer")]
pub const MIXER: Self = Self(u32::from_be_bytes(*b"aumx"));
#[doc(alias = "kAudioUnitType_Panner")]
pub const PANNER: Self = Self(u32::from_be_bytes(*b"aupn"));
#[doc(alias = "kAudioUnitType_Generator")]
pub const GENERATOR: Self = Self(u32::from_be_bytes(*b"augn"));
#[doc(alias = "kAudioUnitType_OfflineEffect")]
pub const OFFLINE_EFFECT: Self = Self(u32::from_be_bytes(*b"auol"));
#[doc(alias = "kAudioUnitType_MIDIProcessor")]
pub const MIDI_PROCESSOR: Self = Self(u32::from_be_bytes(*b"aumi"));
#[doc(alias = "kAudioUnitType_SpeechSynthesizer")]
pub const SPEECH_SYNTHESIZER: Self = Self(u32::from_be_bytes(*b"ausp"));
}
#[doc(alias = "AudioUnitManufacturer")]
#[repr(transparent)]
pub struct Manufacturer(pub u32);
impl Manufacturer {
#[doc(alias = "kAudioUnitManufacturer_Apple")]
pub const APPLE: Self = Self(u32::from_be_bytes(*b"appl"));
}
#[doc(alias = "AudioUnitSubType")]
#[repr(transparent)]
pub struct SubType(pub u32);
impl SubType {
#[doc(alias = "kAudioUnitSubType_GenericOutput")]
pub const GENERIC_OUTPUT: Self = Self(u32::from_be_bytes(*b"genr"));
#[doc(alias = "kAudioUnitSubType_VoiceProcessingIO")]
pub const VOICE_PROCESSING_IO: Self = Self(u32::from_be_bytes(*b"vpio"));
#[cfg(target_os = "macos")]
#[doc(alias = "kAudioUnitSubType_HALOutput")]
pub const HAL_OUTPUT: Self = Self(u32::from_be_bytes(*b"ahal"));
#[cfg(target_os = "macos")]
#[doc(alias = "kAudioUnitSubType_DefaultOutput")]
pub const DEFAULT_OUTPUT: Self = Self(u32::from_be_bytes(*b"def "));
#[cfg(target_os = "macos")]
#[doc(alias = "kAudioUnitSubType_SystemOutput")]
pub const SYSTEM_OUTPUT: Self = Self(u32::from_be_bytes(*b"sys "));
#[cfg(not(target_os = "macos"))]
#[doc(alias = "kAudioUnitSubType_RemoteIO")]
pub const REMOTE_IO: Self = Self(u32::from_be_bytes(*b"rioc"));
}
impl SubType {
#[cfg(target_os = "macos")]
#[doc(alias = "kAudioUnitSubType_DLSSynth")]
pub const DLS_SYNTH: Self = Self(u32::from_be_bytes(*b"dls "));
#[doc(alias = "kAudioUnitSubType_Sampler")]
pub const SAMPLER: Self = Self(u32::from_be_bytes(*b"samp"));
#[doc(alias = "kAudioUnitSubType_MIDISynth")]
pub const MIDI_SYNTH: Self = Self(u32::from_be_bytes(*b"msyn"));
}
impl SubType {
#[doc(alias = "kAudioUnitSubType_AUConverter")]
pub const CONVERTER: Self = Self(u32::from_be_bytes(*b"conv"));
#[doc(alias = "kAudioUnitSubType_Varispeed")]
pub const VARISPEED: Self = Self(u32::from_be_bytes(*b"vari"));
#[doc(alias = "kAudioUnitSubType_DeferredRenderer")]
pub const DEFERRED_RENDERER: Self = Self(u32::from_be_bytes(*b"defr"));
#[doc(alias = "kAudioUnitSubType_Splitter")]
pub const SPLITTER: Self = Self(u32::from_be_bytes(*b"splt"));
#[doc(alias = "kAudioUnitSubType_MultiSplitter")]
pub const MUTLI_SPLITTER: Self = Self(u32::from_be_bytes(*b"mspl"));
#[doc(alias = "kAudioUnitSubType_Merger")]
pub const MERGER: Self = Self(u32::from_be_bytes(*b"merg"));
#[doc(alias = "kAudioUnitSubType_NewTimePitch")]
pub const NEW_TIME_PITCH: Self = Self(u32::from_be_bytes(*b"nutp"));
#[doc(alias = "kAudioUnitSubType_RoundTripAAC")]
pub const ROUND_TRIP_AAC: Self = Self(u32::from_be_bytes(*b"raac"));
}
impl SubType {
#[doc(alias = "kAudioUnitSubType_PeakLimiter")]
pub const PEAK_LIMITER: Self = Self(u32::from_be_bytes(*b"lmtr"));
#[doc(alias = "kAudioUnitSubType_DynamicsProcessor")]
pub const DYNAMICS_PROCESSOR: Self = Self(u32::from_be_bytes(*b"dcmp"));
#[doc(alias = "kAudioUnitSubType_LowPassFilter")]
pub const LOW_PASS_FILTER: Self = Self(u32::from_be_bytes(*b"lpas"));
#[doc(alias = "kAudioUnitSubType_HighPassFilter")]
pub const HIGH_PASS_FILTER: Self = Self(u32::from_be_bytes(*b"hpas"));
#[doc(alias = "kAudioUnitSubType_BandPassFilter")]
pub const BAND_PASS_FILTER: Self = Self(u32::from_be_bytes(*b"bpas"));
#[doc(alias = "kAudioUnitSubType_HighShelfFilter")]
pub const HIGH_SHELF_FILTER: Self = Self(u32::from_be_bytes(*b"hshf"));
#[doc(alias = "kAudioUnitSubType_LowShelfFilter")]
pub const LOW_SHELF_FILTER: Self = Self(u32::from_be_bytes(*b"lshf"));
#[doc(alias = "kAudioUnitSubType_ParametricEQ")]
pub const PARAMETRIC_EQ: Self = Self(u32::from_be_bytes(*b"pmeq"));
#[doc(alias = "kAudioUnitSubType_Distortion")]
pub const DISTORTION: Self = Self(u32::from_be_bytes(*b"dist"));
#[doc(alias = "kAudioUnitSubType_Delay")]
pub const DELAY: Self = Self(u32::from_be_bytes(*b"dely"));
#[doc(alias = "kAudioUnitSubType_SampleDelay")]
pub const SAMPLE_DELAY: Self = Self(u32::from_be_bytes(*b"sdly"));
#[doc(alias = "kAudioUnitSubType_NBandEQ")]
pub const N_BAND_EQ: Self = Self(u32::from_be_bytes(*b"nbeq"));
#[doc(alias = "kAudioUnitSubType_Reverb2")]
pub const REVERB2: Self = Self(u32::from_be_bytes(*b"rvb2"));
#[doc(alias = "kAudioUnitSubType_AUSoundIsolation")]
pub const SOUND_ISOLATION: Self = Self(u32::from_be_bytes(*b"vois"));
}
#[cfg(target_os = "macos")]
impl SubType {
#[doc(alias = "kAudioUnitSubType_GraphicEQ")]
pub const GRAPHIC_EQ: Self = Self(u32::from_be_bytes(*b"greq"));
#[doc(alias = "kAudioUnitSubType_MultiBandCompressor")]
pub const MULTI_BAND_COMPRESSOR: Self = Self(u32::from_be_bytes(*b"mcmp"));
#[doc(alias = "kAudioUnitSubType_MatrixReverb")]
pub const MATRIX_REVERB: Self = Self(u32::from_be_bytes(*b"mrev"));
#[doc(alias = "kAudioUnitSubType_Pitch")]
pub const PITCH: Self = Self(u32::from_be_bytes(*b"tmpt"));
#[doc(alias = "kAudioUnitSubType_AUFilter")]
pub const FILTER: Self = Self(u32::from_be_bytes(*b"filt"));
#[doc(alias = "kAudioUnitSubType_NetSend")]
pub const NET_SEND: Self = Self(u32::from_be_bytes(*b"nsnd"));
#[doc(alias = "kAudioUnitSubType_RogerBeep")]
pub const ROGER_BEEP: Self = Self(u32::from_be_bytes(*b"rogr"));
}
impl SubType {
#[doc(alias = "kAudioUnitSubType_MultiChannelMixer")]
pub const MULTI_CHANNEL_MIXER: Self = Self(u32::from_be_bytes(*b"mcmx"));
#[doc(alias = "kAudioUnitSubType_MatrixMixer")]
pub const MATRIX_MIXER: Self = Self(u32::from_be_bytes(*b"mxmx"));
#[doc(alias = "kAudioUnitSubType_SpatialMixer")]
pub const SPATIAL_MIXER: Self = Self(u32::from_be_bytes(*b"3dem"));
}
#[cfg(target_os = "macos")]
impl SubType {
#[doc(alias = "kAudioUnitSubType_StereoMixer")]
pub const STEREO_MIXER: Self = Self(u32::from_be_bytes(*b"smxr"));
}
impl SubType {
#[cfg(target_os = "macos")]
#[doc(alias = "kAudioUnitSubType_NetReceive")]
pub const NET_RECEIVE: Self = Self(u32::from_be_bytes(*b"nrcv"));
#[doc(alias = "kAudioUnitSubType_ScheduledSoundPlayer")]
pub const SCHEDULED_SOUND_PLAYER: Self = Self(u32::from_be_bytes(*b"sspl"));
#[doc(alias = "kAudioUnitSubType_AudioFilePlayer")]
pub const AUDIO_FILE_PLAYER: Self = Self(u32::from_be_bytes(*b"afpl"));
}
define_opts!(
#[doc(alias = "AudioUnitRenderActionFlags")]
pub RenderActionFlags(u32)
);
impl RenderActionFlags {
#[doc(alias = "kAudioUnitRenderAction_PreRender")]
pub const PRE_RENDER: Self = Self(1u32 << 2);
#[doc(alias = "kAudioUnitRenderAction_PostRender")]
pub const POST_RENDER: Self = Self(1u32 << 3);
#[doc(alias = "kAudioUnitRenderAction_OutputIsSilence")]
pub const OUTPUT_IS_SILENCE: Self = Self(1u32 << 4);
#[doc(alias = "kAudioOfflineUnitRenderAction_Preflight")]
pub const PREFLIGHT: Self = Self(1u32 << 5);
#[doc(alias = "kAudioOfflineUnitRenderAction_Render")]
pub const RENDER: Self = Self(1u32 << 6);
#[doc(alias = "kAudioOfflineUnitRenderAction_Complete")]
pub const COMPLETE: Self = Self(1u32 << 7);
#[doc(alias = "kAudioUnitRenderAction_PostRenderError")]
pub const POST_RENDER_ERROR: Self = Self(1u32 << 8);
#[doc(alias = "kAudioUnitRenderAction_DoNotCheckRenderArgs")]
pub const DO_NOT_CHECK_RENDER_ARGS: Self = Self(1u32 << 9);
}
pub mod err {
use crate::os::Error;
#[doc(alias = "kAudioUnitErr_InvalidProperty")]
pub const INVALID_PROPERTY: Error = Error::new_unchecked(-10879);
#[doc(alias = "kAudioUnitErr_InvalidParameter")]
pub const INVALID_PARAMETER: Error = Error::new_unchecked(-10878);
#[doc(alias = "kAudioUnitErr_InvalidElement")]
pub const INVALID_ELEMENT: Error = Error::new_unchecked(-10877);
#[doc(alias = "kAudioUnitErr_NoConnection")]
pub const NO_CONNECTION: Error = Error::new_unchecked(-10876);
#[doc(alias = "kAudioUnitErr_FailedInitialization")]
pub const FAILED_INITIALIZATION: Error = Error::new_unchecked(-10875);
#[doc(alias = "kAudioUnitErr_TooManyFramesToProcess")]
pub const TOO_MANY_FRAMES_TO_PROCESS: Error = Error::new_unchecked(-10874);
#[doc(alias = "kAudioUnitErr_InvalidFile")]
pub const INVALID_FILE: Error = Error::new_unchecked(-10871);
#[doc(alias = "kAudioUnitErr_UnknownFileType")]
pub const UNKNOWN_FILE_TYPE: Error = Error::new_unchecked(-10870);
#[doc(alias = "kAudioUnitErr_FileNotSpecified")]
pub const FILE_NOT_SPECIFIED: Error = Error::new_unchecked(-10869);
#[doc(alias = "kAudioUnitErr_FormatNotSupported")]
pub const FORMAT_NOT_SUPPORTED: Error = Error::new_unchecked(-10868);
#[doc(alias = "kAudioUnitErr_Uninitialized")]
pub const UNINITIALIZED: Error = Error::new_unchecked(-10867);
#[doc(alias = "kAudioUnitErr_InvalidScope")]
pub const INVALID_SCOPE: Error = Error::new_unchecked(-10866);
#[doc(alias = "kAudioUnitErr_PropertyNotWritable")]
pub const PROPERTY_NOT_WRITABLE: Error = Error::new_unchecked(-10865);
#[doc(alias = "kAudioUnitErr_CannotDoInCurrentContext")]
pub const CANNOT_DO_IN_CURRENT_CONTEXT: Error = Error::new_unchecked(-10863);
#[doc(alias = "kAudioUnitErr_InvalidPropertyValue")]
pub const INVALID_PROPERTY_VALUE: Error = Error::new_unchecked(-10851);
#[doc(alias = "kAudioUnitErr_PropertyNotInUse")]
pub const PROPERTY_NOT_IN_USE: Error = Error::new_unchecked(-10850);
#[doc(alias = "kAudioUnitErr_Initialized")]
pub const INITIALIZED: Error = Error::new_unchecked(-10849);
#[doc(alias = "kAudioUnitErr_InvalidOfflineRender")]
pub const INVALID_OFFLINE_RENDER: Error = Error::new_unchecked(-10848);
#[doc(alias = "kAudioUnitErr_Unauthorized")]
pub const UNAUTHORIZED: Error = Error::new_unchecked(-10847);
#[doc(alias = "kAudioUnitErr_MIDIOutputBufferFull")]
pub const MIDI_OUTPUT_BUFFER_FULL: Error = Error::new_unchecked(-66753);
#[doc(alias = "kAudioUnitErr_RenderTimeout")]
pub const RENDER_TIMEOUT: Error = Error::new_unchecked(-66745);
#[doc(alias = "kAudioUnitErr_ExtensionNotFound")]
pub const EXTENSION_NOT_FOUND: Error = Error::new_unchecked(-66744);
#[doc(alias = "kAudioUnitErr_InvalidParameterValue")]
pub const INVALID_PARAMETER_VALUE: Error = Error::new_unchecked(-66743);
#[doc(alias = "kAudioUnitErr_InvalidFilePath")]
pub const INVALID_FILE_PATH: Error = Error::new_unchecked(-66742);
#[doc(alias = "kAudioUnitErr_MissingKey")]
pub const MISSING_KEY: Error = Error::new_unchecked(-66741);
#[doc(alias = "kAudioUnitErr_ComponentManagerNotSupported")]
pub const COMPONENT_MANAGER_NOT_SUPPORTED: Error = Error::new_unchecked(-66749);
#[doc(alias = "kAudioUnitErr_MultipleVoiceProcessors")]
pub const MULTIPLE_VOICE_PROCESSORS: Error = Error::new_unchecked(-66635);
}
pub mod component_err {
use crate::os::Error;
#[doc(alias = "kAudioComponentErr_InstanceTimedOut")]
pub const INSTANCE_TIMED_OUT: Error = Error::new_unchecked(-66754);
#[doc(alias = "kAudioComponentErr_InstanceInvalidated")]
pub const INSTANCE_INVALIDATED: Error = Error::new_unchecked(-66749);
#[doc(alias = "kAudioComponentErr_DuplicateDescription")]
pub const DUPLICATE_DESCRIPTION: Error = Error::new_unchecked(-66752);
#[doc(alias = "kAudioComponentErr_UnsupportedType")]
pub const UNSUPPORTED_TYPE: Error = Error::new_unchecked(-66751);
#[doc(alias = "kAudioComponentErr_TooManyInstances")]
pub const TOO_MANY_INSTANCES: Error = Error::new_unchecked(-66750);
#[doc(alias = "kAudioComponentErr_NotPermitted")]
pub const NOT_PERMITTED: Error = Error::new_unchecked(-66748);
#[doc(alias = "kAudioComponentErr_InitializationTimedOut")]
pub const INITIALIZATION_TIMED_OUT: Error = Error::new_unchecked(-66747);
#[doc(alias = "kAudioComponentErr_InvalidFormat")]
pub const INVALID_FORMAT: Error = Error::new_unchecked(-66746);
}
#[doc(alias = "AudioUnitPropertyID")]
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
#[repr(transparent)]
pub struct PropId(pub u32);
#[doc(alias = "AudioUnitScope")]
#[derive(Debug, Default, Eq, PartialEq, Copy, Clone)]
#[repr(transparent)]
pub struct Scope(pub u32);
#[doc(alias = "AudioUnitElement")]
#[derive(Debug, Default, Eq, PartialEq, Copy, Clone)]
#[repr(transparent)]
pub struct Element(pub u32);
#[doc(alias = "AudioUnitParameterID")]
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
#[repr(transparent)]
pub struct ParamId(pub u32);
#[doc(alias = "AudioUnitParameterValue")]
pub type ParamValue = f32;
#[doc(alias = "AUParameterEventType")]
#[repr(u32)]
pub enum ParamEventType {
Immediate = 1,
Ramped = 2,
}
#[repr(C, u32)]
pub enum ParamEventValue {
Immediate {
buf_offset: i32,
value: ParamValue,
} = ParamEventType::Immediate as u32,
Ramped {
start_buf_offset: i32,
duration_in_frames: u32,
start_value: ParamValue,
end_value: ParamValue,
} = ParamEventType::Ramped as u32,
}
impl ParamEventValue {
pub fn type_(&self) -> ParamEventType {
match self {
Self::Ramped { .. } => ParamEventType::Ramped,
Self::Immediate { .. } => ParamEventType::Immediate,
}
}
}
#[doc(alias = "AudioUnitParameterEvent")]
#[repr(C)]
pub struct ParamEvent {
pub scope: Scope,
pub element: Element,
pub param_id: ParamId,
pub value: ParamEventValue,
}
#[derive(Debug, Clone)]
#[doc(alias = "AudioUnitParameter")]
pub struct Param {
pub unit: *mut Unit,
pub param_id: ParamId,
pub scope: Scope,
pub element: Element,
}
impl Default for Param {
fn default() -> Self {
Self {
unit: std::ptr::null_mut(),
param_id: Default::default(),
scope: Default::default(),
element: Default::default(),
}
}
}
#[doc(alias = "AudioUnitProperty")]
pub struct Prop {
pub unit: *const Unit,
pub prop_id: PropId,
pub scope: Scope,
pub element: Element,
}
#[doc(alias = "AURenderCallback")]
pub type RenderCb<const N: usize = 1, T = c_void> = extern "C-unwind" fn(
in_ref_con: *mut T,
io_action_flags: &mut RenderActionFlags,
in_timestamp: &at::AudioTimeStamp,
in_bus_num: u32,
in_number_frames: u32,
io_data: *mut at::AudioBufList<N>,
) -> os::Status;
#[doc(alias = "AudioUnitPropertyListenerProc")]
pub type PropListenerProc<T = c_void> = extern "C" fn(
in_ref_con: *mut T,
in_unit: *const Unit,
in_id: PropId,
in_scope: Scope,
in_element: Element,
);
#[doc(alias = "AUInputSamplesInOutputCallback")]
pub type InputSamplesInOutputCb<T = c_void> = extern "C" fn(
in_ref_con: *mut T,
in_output_ts: &at::AudioTimeStamp,
in_input_sample: f64,
in_number_input_samples: f64,
);
impl cf::NotificationName {
#[doc(alias = "kAudioComponentRegistrationsChangedNotification")]
#[inline]
pub fn audio_component_registrations_changed() -> &'static Self {
unsafe { kAudioComponentRegistrationsChangedNotification }
}
#[doc(alias = "kAudioComponentInstanceInvalidationNotification")]
#[inline]
pub fn audio_component_instance_invalidation() -> &'static Self {
unsafe { kAudioComponentInstanceInvalidationNotification }
}
}
impl Unit {
pub fn render<const N: usize>(
&mut self,
timestamp: &audio::TimeStamp,
output_bus_num: u32,
frames_num: u32,
buf_list: &mut audio::BufList<N>,
) -> os::Result {
unsafe {
AudioUnitRender(
self,
std::ptr::null_mut(),
timestamp,
output_bus_num,
frames_num,
buf_list as *mut audio::BufList<N> as *mut audio::BufList<1>,
)
.result()
}
}
pub fn process<const N: usize>(
&mut self,
timestamp: &audio::TimeStamp,
frames_num: u32,
buf_list: &mut audio::BufList<N>,
) -> os::Result {
unsafe {
AudioUnitProcess(
self,
std::ptr::null_mut(),
timestamp,
frames_num,
buf_list as *mut audio::BufList<N> as *mut audio::BufList<1>,
)
.result()
}
}
pub fn prop_info(
&self,
prop_id: PropId,
scope: Scope,
element: Element,
) -> os::Result<(u32, bool)> {
let mut size = 0u32;
let mut writable = false;
unsafe {
AudioUnitGetPropertyInfo(&self, prop_id, scope, element, &mut size, &mut writable)
.result()?;
}
Ok((size, writable))
}
pub fn prop_fill<T>(
&self,
prop_id: PropId,
scope: Scope,
element: Element,
buf: &mut [T],
) -> os::Result {
let ptr = buf.as_mut_ptr();
let mut size = (buf.len() * std::mem::size_of::<T>()) as u32;
unsafe { AudioUnitGetProperty(self, prop_id, scope, element, ptr as _, &mut size).result() }
}
pub fn prop_vec<T: Sized + Default + Clone>(
&self,
prop_id: PropId,
scope: Scope,
element: Element,
) -> os::Result<Vec<T>> {
let (mut size, _) = self.prop_info(prop_id, scope, element)?;
if size == 0 {
return Ok(vec![]);
}
let mut vec = vec![T::default(); size as usize / std::mem::size_of::<T>()];
unsafe {
AudioUnitGetProperty(
self,
prop_id,
scope,
element,
vec.as_mut_ptr().cast(),
&mut size,
)
.result()?;
}
Ok(vec)
}
#[doc(alias = "AudioCodecGetProperty")]
pub fn prop<T: Sized>(&self, prop_id: PropId, scope: Scope, element: Element) -> os::Result<T> {
let mut size = std::mem::size_of::<T>() as u32;
unsafe {
os::result_init(|res: *mut T| {
AudioUnitGetProperty(self, prop_id, scope, element, res.cast(), &mut size)
})
}
}
pub fn set_prop<T: Sized>(
&mut self,
prop_id: PropId,
scope: Scope,
element: Element,
val: &T,
) -> os::Result {
let size = std::mem::size_of::<T>() as u32;
unsafe {
AudioUnitSetProperty(self, prop_id, scope, element, val as *const _ as _, size).result()
}
}
pub fn params_list(&self, scope: Scope) -> os::Result<Vec<ParamId>> {
self.prop_vec(PropId::PARAM_LIST, scope, Element::OUTPUT)
}
pub fn param_info(&self, param_id: ParamId) -> os::Result<ParamInfo> {
self.prop(PropId::PARAM_INFO, Scope::GLOBAL, Element(param_id.0))
}
pub fn param(
&self,
param_id: ParamId,
scope: Scope,
element: Element,
) -> os::Result<ParamValue> {
unsafe { os::result_init(|res| AudioUnitGetParameter(self, param_id, scope, element, res)) }
}
pub fn set_param(
&mut self,
param_id: ParamId,
scope: Scope,
element: Element,
val: ParamValue,
frames_offset: u32,
) -> os::Result {
unsafe {
AudioUnitSetParameter(self, param_id, scope, element, val, frames_offset).result()
}
}
pub fn add_prop_listener<T>(
&mut self,
prop_id: PropId,
proc: PropListenerProc<T>,
proc_user_data: *mut T,
) -> os::Result {
unsafe {
AudioUnitAddPropertyListener(
self,
prop_id,
std::mem::transmute(proc),
proc_user_data as _,
)
.result()
}
}
pub fn remove_prop_listener<T>(
&mut self,
prop_id: PropId,
proc: PropListenerProc<T>,
proc_user_data: *mut T,
) -> os::Result {
unsafe {
AudioUnitRemovePropertyListenerWithUserData(
self,
prop_id,
std::mem::transmute(proc),
proc_user_data as _,
)
.result()
}
}
pub fn offline_render(&self) -> os::Result<bool> {
let res: os::Result<u32> = self.prop(PropId::OFFLINE_RENDER, Scope::GLOBAL, Element::INPUT);
res.map(|v| v == 1)
}
pub fn set_offline_render(&mut self, val: bool) -> os::Result {
let val = val as u32;
self.set_prop(PropId::OFFLINE_RENDER, Scope::GLOBAL, Element::INPUT, &val)
}
pub fn last_render_sample_time(&self) -> os::Result<f64> {
self.prop(
PropId::LAST_RENDER_SAMPLE_TIME,
Scope::GLOBAL,
Element::OUTPUT,
)
}
pub fn last_render_err(&self) -> os::Result<os::Status> {
self.prop(PropId::LAST_RENDER_ERROR, Scope::GLOBAL, Element::OUTPUT)
}
pub fn render_quality(&self) -> os::Result<u32> {
self.prop(PropId::RENDER_QUALITY, Scope::GLOBAL, Element::OUTPUT)
}
pub fn set_render_quality(&mut self, val: u32) -> os::Result {
self.set_prop(
PropId::RENDER_QUALITY,
Scope::GLOBAL,
Default::default(),
&val,
)
}
pub fn should_allocate_input_buf(&self) -> os::Result<bool> {
let res: os::Result<u32> =
self.prop(PropId::SHOULD_ALLOCATE_BUF, Scope::INPUT, Element::INPUT);
res.map(|v| v == 1)
}
pub fn should_allocate_output_buf(&self) -> os::Result<bool> {
let res: os::Result<u32> =
self.prop(PropId::SHOULD_ALLOCATE_BUF, Scope::OUTPUT, Element::OUTPUT);
res.map(|v| v == 1)
}
pub fn set_should_allocate_output_buf(&mut self, val: bool) -> os::Result {
let val = val as u32;
self.set_prop(
PropId::SHOULD_ALLOCATE_BUF,
Scope::OUTPUT,
Element::OUTPUT,
&val,
)
}
pub fn set_should_allocate_input_buf(&mut self, val: bool) -> os::Result {
let val = val as u32;
self.set_prop(
PropId::SHOULD_ALLOCATE_BUF,
Scope::INPUT,
Element::OUTPUT,
&val,
)
}
pub fn element_count(&self, scope: Scope) -> os::Result<u32> {
let element = if scope == Scope::INPUT {
Element::INPUT
} else {
Element::OUTPUT
};
self.prop(PropId::ELEMENT_COUNT, scope, element)
}
pub fn set_element_count(&mut self, scope: Scope, val: u32) -> os::Result {
let element = if scope == Scope::INPUT {
Element::INPUT
} else {
Element::OUTPUT
};
self.set_prop(PropId::ELEMENT_COUNT, scope, element, &val)
}
pub fn sample_rate(&self, scope: Scope) -> os::Result<f64> {
let element = if scope == Scope::INPUT {
Element::INPUT
} else {
Element::OUTPUT
};
self.prop(PropId::SAMPLE_RATE, scope, element)
}
pub fn stream_format(&self, scope: Scope, bus: u32) -> os::Result<audio::StreamBasicDesc> {
self.prop(PropId::STREAM_FORMAT, scope, Element(bus))
}
pub fn set_stream_format(
&mut self,
scope: Scope,
bus: u32,
val: &StreamBasicDesc,
) -> os::Result {
self.set_prop(PropId::STREAM_FORMAT, scope, Element(bus), val)
}
pub fn nick_name(&self) -> os::Result<Option<arc::R<cf::String>>> {
self.prop(PropId::NICK_NAME, Scope::GLOBAL, Default::default())
}
pub fn set_nick_name(&mut self, val: Option<&cf::String>) -> os::Result {
self.set_prop(PropId::NICK_NAME, Scope::GLOBAL, Default::default(), &val)
}
pub fn max_frames_per_slice(&self) -> os::Result<u32> {
self.prop(
PropId::MAX_FRAMES_PER_SLICE,
Scope::GLOBAL,
Default::default(),
)
}
pub fn set_max_frames_per_slice(&mut self, val: u32) -> os::Result {
self.set_prop(
PropId::MAX_FRAMES_PER_SLICE,
Scope::GLOBAL,
Default::default(),
&val,
)
}
pub fn set_input_cb<const N: usize, T>(
&mut self,
scope: Scope,
bus: u32,
cb: RenderCb<N, T>,
ref_con: *const T,
) -> os::Result {
let val: RenderCbStruct<N, T> = RenderCbStruct {
proc: cb as _,
proc_ref_con: ref_con,
};
self.set_prop(PropId::SET_RENDER_CB, scope, Element(bus), &val)
}
pub fn remove_input_cb(&mut self, scope: Scope, bus: u32) -> os::Result {
let val: RenderCbStruct<1, c_void> = RenderCbStruct {
proc: std::ptr::null(),
proc_ref_con: std::ptr::null(),
};
self.set_prop(PropId::SET_RENDER_CB, scope, Element(bus), &val)
}
pub fn output_set_input_cb<const N: usize, T>(
&mut self,
scope: Scope,
bus: u32,
cb: RenderCb<N, T>,
ref_con: *const T,
) -> os::Result {
let val: RenderCbStruct<N, T> = RenderCbStruct {
proc: cb as _,
proc_ref_con: ref_con,
};
self.set_prop(PropId::OUTPUT_SET_INPUT_CB, scope, Element(bus), &val)
}
pub fn output_remove_input_cb(&mut self, scope: Scope, bus: u32) -> os::Result {
let val: RenderCbStruct<1, c_void> = RenderCbStruct {
proc: std::ptr::null(),
proc_ref_con: std::ptr::null(),
};
self.set_prop(PropId::OUTPUT_SET_INPUT_CB, scope, Element(bus), &val)
}
pub fn set_render_cb<const N: usize, T>(
&mut self,
scope: Scope,
bus: u32,
cb: RenderCb<N, T>,
ref_con: *const T,
) -> os::Result {
let val: RenderCbStruct<N, T> = RenderCbStruct {
proc: cb as _,
proc_ref_con: ref_con,
};
self.set_prop(PropId::SET_RENDER_CB, scope, Element(bus), &val)
}
}
impl UnitRef<UninitializedState> {
pub fn initialize(mut self) -> os::Result<UnitRef<InitializedState>> {
unsafe {
AudioUnitInitialize(&mut self.0).result()?;
Ok(std::mem::transmute(self))
}
}
pub fn set_offline_render(&mut self, val: bool) -> os::Result {
self.0.set_offline_render(val)
}
pub fn set_render_quality(&mut self, val: u32) -> os::Result {
self.0.set_render_quality(val)
}
pub fn set_should_allocate_output_buf(&mut self, val: bool) -> os::Result {
self.0.set_should_allocate_output_buf(val)
}
pub fn set_should_allocate_input_buf(&mut self, val: bool) -> os::Result {
self.0.set_should_allocate_input_buf(val)
}
pub fn set_max_frames_per_slice(&mut self, val: u32) -> os::Result {
self.0.set_max_frames_per_slice(val)
}
pub fn set_stream_format(
&mut self,
scope: Scope,
bus: u32,
val: &audio::StreamBasicDesc,
) -> os::Result {
self.0.set_stream_format(scope, bus, val)
}
}
impl<S: State<Unit>> UnitRef<S> {
pub fn unit(&self) -> &Unit {
self.0
}
pub fn unit_mut(&mut self) -> &mut Unit {
self.0
}
pub fn set_input_cb<const N: usize, T>(
&mut self,
bus: u32,
cb: RenderCb<N, T>,
ref_con: *const T,
) -> os::Result {
self.0.set_input_cb(Scope::INPUT, bus, cb, ref_con)
}
pub fn set_global_input_cb<const N: usize, T>(
&mut self,
cb: RenderCb<N, T>,
ref_con: *const T,
) -> os::Result {
self.0.set_input_cb(Scope::GLOBAL, 1, cb, ref_con)
}
pub fn remove_input_cb(&mut self, bus: u32) -> os::Result {
self.0.remove_input_cb(Scope::INPUT, bus)
}
pub fn remove_global_input_cb(&mut self, bus: u32) -> os::Result {
self.0.remove_input_cb(Scope::GLOBAL, bus)
}
pub fn prop_info(
&self,
prop_id: PropId,
scope: Scope,
element: Element,
) -> os::Result<(u32, bool)> {
self.0.prop_info(prop_id, scope, element)
}
pub fn params_list(&self, scope: Scope) -> os::Result<Vec<ParamId>> {
self.0.params_list(scope)
}
pub fn param(
&self,
param_id: ParamId,
scope: Scope,
element: Element,
) -> os::Result<ParamValue> {
self.0.param(param_id, scope, element)
}
#[inline]
pub fn element_count(&self, scope: Scope) -> os::Result<u32> {
self.0.element_count(scope)
}
#[inline]
pub fn offline_render(&self) -> os::Result<bool> {
self.0.offline_render()
}
#[inline]
pub fn last_render_sample_time(&self) -> os::Result<f64> {
self.0.last_render_sample_time()
}
#[inline]
pub fn last_render_err(&self) -> os::Result<os::Status> {
self.0.last_render_err()
}
#[inline]
pub fn render_quality(&self) -> os::Result<u32> {
self.0.render_quality()
}
#[inline]
pub fn should_allocate_input_buf(&self) -> os::Result<bool> {
self.0.should_allocate_input_buf()
}
#[inline]
pub fn should_allocate_output_buf(&self) -> os::Result<bool> {
self.0.should_allocate_output_buf()
}
#[inline]
pub fn sample_rate(&self, scope: Scope) -> os::Result<f64> {
self.0.sample_rate(scope)
}
#[inline]
pub fn stream_format(&self, scope: Scope, bus: u32) -> os::Result<audio::StreamBasicDesc> {
self.0.stream_format(scope, bus)
}
#[inline]
pub fn set_element_count(&mut self, scope: Scope, val: u32) -> os::Result {
self.0.set_element_count(scope, val)
}
#[inline]
pub fn nick_name(&self) -> os::Result<Option<arc::R<cf::String>>> {
self.0.nick_name()
}
#[inline]
pub fn set_nick_name(&mut self, val: Option<&cf::String>) -> os::Result {
self.0.set_nick_name(val)
}
#[inline]
pub fn max_frames_per_slice(&self) -> os::Result<u32> {
self.0.max_frames_per_slice()
}
}
impl UnitRef<InitializedState> {
pub fn unintialize(mut self) -> os::Result<UnitRef<UninitializedState>> {
Ok(unsafe {
AudioUnitUninitialize(&mut self.0).result()?;
std::mem::transmute(self)
})
}
pub fn render<const N: usize>(
&mut self,
timestamp: &audio::TimeStamp,
output_bus_num: u32,
frames_num: u32,
buf_list: &mut audio::BufList<N>,
) -> os::Result {
self.0
.render(timestamp, output_bus_num, frames_num, buf_list)
}
pub fn process<const N: usize>(
&mut self,
timestamp: &audio::TimeStamp,
frames_num: u32,
buf_list: &mut audio::BufList<N>,
) -> os::Result {
self.0.process(timestamp, frames_num, buf_list)
}
pub fn schedule_slice(&mut self, slice: &ScheduledSlice) -> os::Result {
self.0.set_prop(
PropId::SCHEDULE_SLICE,
Scope::GLOBAL,
Default::default(),
slice,
)
}
}
impl audio::Component {
pub fn open_unit(&self) -> os::Result<UnitRef<UninitializedState>> {
Ok(unsafe { std::mem::transmute(self.open()?) })
}
}
#[link(name = "AudioToolbox", kind = "framework")]
unsafe extern "C-unwind" {
static kAudioComponentRegistrationsChangedNotification: &'static cf::NotificationName;
static kAudioComponentInstanceInvalidationNotification: &'static cf::NotificationName;
fn AudioUnitInitialize(in_unit: &mut Unit) -> os::Status;
fn AudioUnitUninitialize(in_unit: &mut Unit) -> os::Status;
fn AudioUnitGetPropertyInfo(
in_unit: &Unit,
in_id: PropId,
in_scope: Scope,
in_element: Element,
out_data_size: *mut u32,
out_writable: *mut bool,
) -> os::Status;
fn AudioUnitGetProperty(
in_unit: &Unit,
in_id: PropId,
in_scope: Scope,
in_element: Element,
out_data: *mut c_void,
io_data_size: *mut u32,
) -> os::Status;
fn AudioUnitSetProperty(
in_unit: &mut Unit,
in_id: PropId,
in_scope: Scope,
in_element: Element,
in_data: *const c_void,
in_data_size: u32,
) -> os::Status;
fn AudioUnitRender(
in_unit: &mut Unit,
io_action_flags: *mut RenderActionFlags,
in_timestamp: *const audio::TimeStamp,
in_output_bus_num: u32,
in_number_frames: u32,
io_data: *mut audio::BufList,
) -> os::Status;
fn AudioUnitProcess(
in_unit: &mut Unit,
io_action_flags: *mut RenderActionFlags,
in_timestamp: *const audio::TimeStamp,
in_number_frames: u32,
io_data: *mut audio::BufList,
) -> os::Status;
fn AudioUnitGetParameter(
in_unit: &Unit,
param_id: ParamId,
in_scope: Scope,
in_element: Element,
out_value: *mut ParamValue,
) -> os::Status;
fn AudioUnitSetParameter(
in_unit: &mut Unit,
param_id: ParamId,
in_scope: Scope,
in_element: Element,
in_value: ParamValue,
in_buf_offset_in_frames: u32,
) -> os::Status;
fn AudioUnitAddPropertyListener(
in_unit: &mut Unit,
id: PropId,
proc: PropListenerProc,
proc_user_data: *mut std::ffi::c_void,
) -> os::Status;
fn AudioUnitRemovePropertyListenerWithUserData(
in_unit: &mut Unit,
id: PropId,
proc: PropListenerProc,
proc_user_data: *mut std::ffi::c_void,
) -> os::Status;
}
impl audio::Component {
pub fn apple_scheduled_sound_player() -> Option<&'static Self> {
audio::ComponentDesc {
type_: Type::GENERATOR.0,
sub_type: SubType::SCHEDULED_SOUND_PLAYER.0,
manufacturer: Manufacturer::APPLE.0,
flags: 0,
flags_mask: 0,
}
.into_iter()
.next()
}
}
#[cfg(test)]
mod tests {
#[cfg(target_os = "macos")]
use std::{f32::consts::PI, ffi::c_void};
use au::Element;
use audio::{BufList, TimeStamp, unit::FrequencyResponseBin};
use crate::{
at::{au, audio},
cf, os,
};
#[test]
fn basics() {
let desc = audio::ComponentDesc {
type_: au::Type::MIXER.0,
sub_type: au::SubType::SPATIAL_MIXER.0,
manufacturer: au::Manufacturer::APPLE.0,
..Default::default()
};
let comp = desc.into_iter().next().unwrap();
let mut mixer = comp.open_unit().unwrap();
let (size, writable) = mixer
.prop_info(
au::PropId::OFFLINE_RENDER,
au::Scope::GLOBAL,
au::Element::OUTPUT,
)
.unwrap();
assert!(writable);
assert_eq!(4, size);
println!("size: {size}, writable: {writable}");
assert_eq!(false, mixer.offline_render().unwrap());
mixer.set_offline_render(true).unwrap();
mixer.set_render_quality(200).unwrap();
let mixer = mixer.initialize().unwrap();
let (size, writable) = mixer
.prop_info(
au::PropId::OFFLINE_RENDER,
au::Scope::GLOBAL,
au::Element::OUTPUT,
)
.unwrap();
assert!(!writable);
assert_eq!(4, size);
assert_eq!(true, mixer.offline_render().unwrap());
assert_eq!(f64::MIN, mixer.last_render_sample_time().unwrap());
assert_eq!(200, mixer.render_quality().unwrap());
assert_eq!(true, mixer.should_allocate_input_buf().unwrap());
assert_eq!(true, mixer.should_allocate_output_buf().unwrap());
assert_eq!(1, mixer.element_count(au::Scope::OUTPUT).unwrap());
assert_eq!(32, mixer.element_count(au::Scope::INPUT).unwrap());
}
#[test]
fn generator() {
let desc = audio::ComponentDesc {
type_: au::Type::GENERATOR.0,
sub_type: au::SubType::SCHEDULED_SOUND_PLAYER.0,
..Default::default()
};
let player = desc.into_iter().next().unwrap().open_unit().unwrap();
println!(
"params {:?}",
player.params_list(au::Scope::OUTPUT).unwrap()
);
let mut player = player.initialize().unwrap();
let mut ts = TimeStamp::with_sample_time(0.0);
let mut buf: BufList<2> = audio::BufList::default();
player.render(&ts, 0, 1024, &mut buf).unwrap();
println!("{:?}", buf.buffers[0].data_bytes_size);
ts.sample_time += 1024.0;
player.render(&ts, 0, 200, &mut buf).unwrap();
println!("{:?}", buf.buffers[0].data_bytes_size);
println!("{:?}", player.last_render_sample_time());
println!("{:?}", player.sample_rate(au::Scope::OUTPUT));
}
#[cfg(target_os = "macos")]
#[test]
fn effect() {
let desc = audio::ComponentDesc {
type_: au::Type::EFFECT.0,
sub_type: au::SubType::FILTER.0,
..Default::default()
};
let comp = desc.into_iter().next().unwrap();
let mut unit = comp.open_unit().unwrap();
let (size, writable) = unit
.prop_info(
au::PropId::FREQUENCY_RESPONSE,
au::Scope::GLOBAL,
au::Element(0),
)
.unwrap();
assert!(!writable);
let expected_size =
std::mem::size_of::<au::FrequencyResponseBin>() * au::FrequencyResponseBin::MAX_LEN;
assert_eq!(expected_size as u32, size);
let mut bins = vec![FrequencyResponseBin::default(); FrequencyResponseBin::MAX_LEN];
bins[0].frequency = 500.0;
bins[1].frequency = 1000.0;
bins[2].frequency = 2000.0;
bins[3].frequency = -1.0;
extern "C-unwind" fn render(
_in_ref_con: *mut c_void,
_io_action_flags: &mut au::RenderActionFlags,
_in_timestamp: &audio::TimeStamp,
_in_bus_num: u32,
in_number_frames: u32,
io_data: *mut audio::BufList<2>,
) -> os::Status {
let buf = unsafe { io_data.as_mut().unwrap() };
let len = in_number_frames as usize;
let a = unsafe { std::slice::from_raw_parts_mut(buf.buffers[0].data as *mut f32, len) };
let b = unsafe { std::slice::from_raw_parts_mut(buf.buffers[1].data as *mut f32, len) };
for i in 0..len {
let t = i as f32 / 44_100.0f32;
let v = f32::sin(2.0 * PI * 1_000.0 * t);
a[i] = v;
b[i] = v;
}
eprintln!("{:?}", a);
os::Status::NO_ERR
}
unit.set_input_cb(0, render, std::ptr::null()).unwrap();
let mut unit = unit.initialize().unwrap();
let ts = audio::TimeStamp::with_sample_time(0.0);
let mut buf: audio::BufList<2> = Default::default();
unit.render(&ts, 0, 1024, &mut buf).unwrap();
unit.0
.prop_fill(
au::PropId::FREQUENCY_RESPONSE,
au::Scope::GLOBAL,
au::Element(0),
&mut bins,
)
.unwrap();
println!("{:?}", &bins[..3]);
}
#[test]
fn mixer() {
let desc = audio::ComponentDesc {
type_: au::Type::MUSIC_DEVICE.0,
..Default::default()
};
for d in desc.into_iter() {
println!("{:?}", d.name());
}
let desc = audio::ComponentDesc {
type_: au::Type::MIXER.0,
sub_type: au::SubType::MULTI_CHANNEL_MIXER.0,
..Default::default()
};
let ts = TimeStamp::with_sample_time(0.0);
let mut buf: BufList<2> = audio::BufList::default();
let mut mixer = desc.into_iter().next().unwrap().open_unit().unwrap();
mixer.set_should_allocate_output_buf(true).unwrap();
mixer.set_element_count(au::Scope::INPUT, 1).unwrap();
println!("input {:?}", mixer.params_list(au::Scope::INPUT).unwrap());
println!("output {:?}", mixer.params_list(au::Scope::OUTPUT).unwrap());
println!("global {:?}", mixer.params_list(au::Scope::GLOBAL).unwrap());
let mut mixer = mixer.initialize().unwrap();
mixer.set_element_count(au::Scope::INPUT, 2).unwrap();
println!("count {:?}", mixer.element_count(au::Scope::INPUT).unwrap());
println!("{:?}", mixer.sample_rate(au::Scope::OUTPUT));
mixer.render(&ts, 0, 1024, &mut buf).unwrap();
let asbd = mixer.stream_format(au::Scope::OUTPUT, 0).unwrap();
println!("mixer {:?}", asbd);
println!("mixer {:?}", mixer.last_render_sample_time());
println!("{:?}", buf.buffers[0].data_bytes_size);
assert_eq!(mixer.last_render_err().unwrap(), os::Status::NO_ERR);
assert!(mixer.nick_name().unwrap().is_none());
mixer.set_nick_name(Some(cf::str!(c"mixer"))).unwrap();
let nick_name = mixer.nick_name().unwrap().unwrap();
assert_eq!(nick_name.to_string(), "mixer");
assert_eq!(1156, mixer.max_frames_per_slice().unwrap());
let level = mixer
.param(
au::ParamId::MULTI_CHANNEL_MIXER_POST_AVERAGE_POWER,
au::Scope::GLOBAL,
Element::OUTPUT,
)
.unwrap();
println!("level {level}");
}
}