1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
//! This module is an attempt to provide a friendly, rust-esque interface to Apple's Audio Unit API. //! //! 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) //! and [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/TheAudioUnit/TheAudioUnit.html). //! //! TODO: The following are `kAudioUnitSubType`s (along with their const u32) generated by //! rust-bindgen that we could not find any documentation on: //! //! - MIDISynth = 1836284270, //! - RoundTripAAC = 1918984547, //! - SpatialMixer = 862217581, //! - SphericalHeadPanner = 1936746610, //! - VectorPanner = 1986158963, //! - SoundFieldPanner = 1634558569, //! - HRTFPanner = 1752331366, //! - NetReceive = 1852990326, //! //! If you can find documentation on these, please feel free to submit an issue or PR with the //! fixes! use bindings::audio_unit as au; use error::Error; use libc; use std::mem; use std::ptr; pub use self::audio_format::AudioFormat; pub use self::sample_format::{SampleFormat, Sample}; pub use self::stream_format::StreamFormat; pub use self::types::{ Type, EffectType, FormatConverterType, GeneratorType, IOType, MixerType, MusicDeviceType, }; pub mod audio_format; pub mod render_callback; pub mod sample_format; pub mod stream_format; pub mod types; /// The input and output **Scope**s. /// /// More info [here](https://developer.apple.com/library/ios/documentation/AudioUnit/Reference/AudioUnitPropertiesReference/index.html#//apple_ref/doc/constant_group/Audio_Unit_Scopes) /// and [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/TheAudioUnit/TheAudioUnit.html). #[derive(Copy, Clone, Debug)] pub enum Scope { Output = 0, Input = 1, } /// Represents the **Input** and **Output** **Element**s. /// /// These are used when specifying which **Element** we're setting the properties of. #[derive(Copy, Clone, Debug)] pub enum Element { Output = 0, Input = 1, } /// A rust representation of the au::AudioUnit, including a pointer to the current rendering callback. /// /// Find the original Audio Unit Programming Guide [here](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/TheAudioUnit/TheAudioUnit.html). pub struct AudioUnit { instance: au::AudioUnit, maybe_callback: Option<*mut render_callback::InputProcFnWrapper> } macro_rules! try_os_status { ($expr:expr) => (try!(Error::from_os_status($expr))) } impl AudioUnit { /// Construct a new AudioUnit with any type that may be automatically converted into /// [**Type**](./enum.Type). /// /// Here is a list of compatible types: /// /// - [**Type**](./types/enum.Type) /// - [**IOType**](./types/enum.IOType) /// - [**MusicDeviceType**](./types/enum.MusicDeviceType) /// - [**GeneratorType**](./types/enum.GeneratorType) /// - [**FormatConverterType**](./types/enum.FormatConverterType) /// - [**EffectType**](./types/enum.EffectType) /// - [**MixerType**](./types/enum.MixerType) /// /// To construct the **AudioUnit** with some component flags, see /// [**AudioUnit::new_with_flags**](./struct.AudioUnit#method.new_with_flags). /// /// Note: the `AudioUnit` is constructed with the `kAudioUnitManufacturer_Apple` Manufacturer /// Identifier, as this is the only Audio Unit Manufacturer Identifier documented by Apple in /// 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)). pub fn new<T>(ty: T) -> Result<AudioUnit, Error> where T: Into<Type>, { AudioUnit::new_with_flags(ty, 0, 0) } /// The same as [**AudioUnit::new**](./struct.AudioUnit#method.new) but with the given /// component flags and mask. pub fn new_with_flags<T>(ty: T, flags: u32, mask: u32) -> Result<AudioUnit, Error> where T: Into<Type>, { const MANUFACTURER_IDENTIFIER: u32 = au::kAudioUnitManufacturer_Apple; let au_type: Type = ty.into(); let sub_type_u32 = match au_type.to_subtype_u32() { Some(u) => u, None => return Err(Error::NoKnownSubtype), }; // A description of the audio unit we desire. let desc = au::AudioComponentDescription { componentType: au_type.to_u32() as libc::c_uint, componentSubType: sub_type_u32 as libc::c_uint, componentManufacturer: MANUFACTURER_IDENTIFIER, componentFlags: flags, componentFlagsMask: mask, }; unsafe { // Find the default audio unit for the description. // // From the "Audio Unit Hosting Guide for iOS": // // Passing NULL to the first parameter of AudioComponentFindNext tells this function to // find the first system audio unit matching the description, using a system-defined // ordering. If you instead pass a previously found audio unit reference in this // parameter, the function locates the next audio unit matching the description. let component = au::AudioComponentFindNext(ptr::null_mut(), &desc as *const _); if component.is_null() { return Err(Error::NoMatchingDefaultAudioUnitFound); } // Create an instance of the default audio unit using the component. let mut instance: au::AudioUnit = mem::uninitialized(); try_os_status!( au::AudioComponentInstanceNew(component, &mut instance as *mut au::AudioUnit) ); // Initialise the audio unit! try_os_status!(au::AudioUnitInitialize(instance)); Ok(AudioUnit { instance: instance, maybe_callback: None }) } } /// Sets the value for some property of the **AudioUnit**. /// /// To clear an audio unit property value, set the data paramater with `None::<()>`. /// /// Clearing properties only works for those properties that do not have a default value. /// /// For more on "properties" see [the reference](https://developer.apple.com/library/ios/documentation/AudioUnit/Reference/AudioUnitPropertiesReference/index.html#//apple_ref/doc/uid/TP40007288). /// /// **Available** in iOS 2.0 and later. /// /// Parameters /// ---------- /// /// - **id**: The identifier of the property. /// - **scope**: The audio unit scope for the property. /// - **elem**: The audio unit element for the property. /// - **maybe_data**: The value that you want to apply to the property. pub fn set_property<T>(&mut self, 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 libc::c_void; let size = ::std::mem::size_of::<T>() as u32; (ptr, size) }).unwrap_or_else(|| (::std::ptr::null(), 0)); let scope = scope as libc::c_uint; let elem = elem as libc::c_uint; unsafe { try_os_status!(au::AudioUnitSetProperty(self.instance, id, scope, elem, data_ptr, size)) } Ok(()) } /// Gets the value of an **AudioUnit** property. /// /// **Available** in iOS 2.0 and later. /// /// Parameters /// ---------- /// /// - **id**: The identifier of the property. /// - **scope**: The audio unit scope for the property. /// - **elem**: The audio unit element for the property. pub fn get_property<T>(&self, id: u32, scope: Scope, elem: Element) -> Result<T, Error> { let scope = scope as libc::c_uint; let elem = elem as libc::c_uint; let mut size = ::std::mem::size_of::<T>() as u32; unsafe { let mut data: T = ::std::mem::uninitialized(); let data_ptr = &mut data as *mut _ as *mut libc::c_void; let size_ptr = &mut size as *mut _; try_os_status!( au::AudioUnitGetProperty(self.instance, id, scope, elem, data_ptr, size_ptr) ); Ok(data) } } /// Starts an I/O **AudioUnit**, which in turn starts the audio unit processing graph that it is /// connected to. /// /// **Available** in OS X v10.0 and later. pub fn start(&mut self) -> Result<(), Error> { unsafe { try_os_status!(au::AudioOutputUnitStart(self.instance)); } Ok(()) } /// Stops an I/O **AudioUnit**, which in turn stops the audio unit processing graph that it is /// connected to. /// /// **Available** in OS X v10.0 and later. pub fn stop(&mut self) -> Result<(), Error> { unsafe { try_os_status!(au::AudioOutputUnitStop(self.instance)); } Ok(()) } /// Set the **AudioUnit**'s sample rate. /// /// **Available** in iOS 2.0 and later. pub fn set_sample_rate(&mut self, sample_rate: f64) -> Result<(), Error> { let id = au::kAudioUnitProperty_SampleRate; self.set_property(id, Scope::Input, Element::Output, Some(&sample_rate)) } /// Get the **AudioUnit**'s sample rate. pub fn sample_rate(&self) -> Result<f64, Error> { let id = au::kAudioUnitProperty_SampleRate; self.get_property(id, Scope::Input, Element::Output) } /// Sets the current **StreamFormat** for the AudioUnit. /// /// Core Audio uses slightly different defaults depending on the platform. /// /// From the Core Audio Overview: /// /// > The canonical formats in Core Audio are as follows: /// > /// > - iOS input and output: Linear PCM with 16-bit integer samples. /// > - iOS audio units and other audio processing: Noninterleaved linear PCM with 8.24-bit /// fixed-point samples /// > - Mac input and output: Linear PCM with 32-bit floating point samples. /// > - Mac audio units and other audio processing: Noninterleaved linear PCM with 32-bit /// floating-point pub fn set_stream_format(&mut self, stream_format: StreamFormat) -> Result<(), Error> { let id = au::kAudioUnitProperty_StreamFormat; let asbd = stream_format.to_asbd(); self.set_property(id, Scope::Input, Element::Output, Some(&asbd)) } /// Return the current Stream Format for the AudioUnit. pub fn stream_format(&self) -> Result<StreamFormat, Error> { let id = au::kAudioUnitProperty_StreamFormat; let asbd = try!(self.get_property(id, Scope::Output, Element::Output)); StreamFormat::from_asbd(asbd) } } unsafe impl Send for AudioUnit {} impl Drop for AudioUnit { fn drop(&mut self) { unsafe { use error; // We don't want to panic in `drop`, so we'll ignore returned errors. // // A user should explicitly terminate the `AudioUnit` if they want to handle errors (we // still need to provide a way to actually do that). self.stop().ok(); error::Error::from_os_status(au::AudioUnitUninitialize(self.instance)).ok(); self.free_render_callback(); } } }