extern crate coreaudio; extern crate core_foundation_sys; use ChannelCount; use CreationError; use DefaultFormatError; use Format; use FormatsEnumerationError; use Sample; use SampleFormat; use SampleRate; use StreamData; use SupportedFormat; use UnknownTypeInputBuffer; use UnknownTypeOutputBuffer; use std::ffi::CStr; use std::mem; use std::os::raw::c_char; use std::ptr::null; use std::sync::{Arc, Mutex}; use std::thread; use std::time::Duration; use std::slice; use self::coreaudio::audio_unit::{AudioUnit, Scope, Element}; use self::coreaudio::audio_unit::render_callback::{self, data}; use self::coreaudio::sys::{ AudioBuffer, AudioBufferList, AudioDeviceID, AudioObjectGetPropertyData, AudioObjectGetPropertyDataSize, AudioObjectPropertyAddress, AudioObjectPropertyScope, AudioStreamBasicDescription, AudioValueRange, kAudioDevicePropertyAvailableNominalSampleRates, kAudioDevicePropertyDeviceNameCFString, kAudioObjectPropertyScopeInput, kAudioDevicePropertyScopeOutput, kAudioDevicePropertyStreamConfiguration, kAudioDevicePropertyStreamFormat, kAudioFormatFlagIsFloat, kAudioFormatFlagIsPacked, kAudioFormatLinearPCM, kAudioHardwareNoError, kAudioObjectPropertyElementMaster, kAudioObjectPropertyScopeOutput, kAudioOutputUnitProperty_CurrentDevice, kAudioOutputUnitProperty_EnableIO, kAudioUnitProperty_StreamFormat, kCFStringEncodingUTF8, OSStatus, }; use self::core_foundation_sys::string::{ CFStringRef, CFStringGetCStringPtr, }; mod enumerate; pub use self::enumerate::{Devices, SupportedInputFormats, SupportedOutputFormats, default_input_device, default_output_device}; #[derive(Clone, PartialEq, Eq)] pub struct Device { audio_device_id: AudioDeviceID, } impl Device { pub fn name(&self) -> String { let property_address = AudioObjectPropertyAddress { mSelector: kAudioDevicePropertyDeviceNameCFString, mScope: kAudioDevicePropertyScopeOutput, mElement: kAudioObjectPropertyElementMaster, }; let device_name: CFStringRef = null(); let data_size = mem::size_of::(); let c_str = unsafe { let status = AudioObjectGetPropertyData( self.audio_device_id, &property_address as *const _, 0, null(), &data_size as *const _ as *mut _, &device_name as *const _ as *mut _, ); if status != kAudioHardwareNoError as i32 { return format!("", status); } let c_string: *const c_char = CFStringGetCStringPtr(device_name, kCFStringEncodingUTF8); if c_string == null() { return "".into(); } CStr::from_ptr(c_string as *mut _) }; c_str.to_string_lossy().into_owned() } // Logic re-used between `supported_input_formats` and `supported_output_formats`. fn supported_formats( &self, scope: AudioObjectPropertyScope, ) -> Result { let mut property_address = AudioObjectPropertyAddress { mSelector: kAudioDevicePropertyStreamConfiguration, mScope: scope, mElement: kAudioObjectPropertyElementMaster, }; unsafe { // Retrieve the devices audio buffer list. let data_size = 0u32; let status = AudioObjectGetPropertyDataSize( self.audio_device_id, &property_address as *const _, 0, null(), &data_size as *const _ as *mut _, ); if status != kAudioHardwareNoError as i32 { unimplemented!(); } let mut audio_buffer_list: Vec = vec![]; audio_buffer_list.reserve_exact(data_size as usize); let status = AudioObjectGetPropertyData( self.audio_device_id, &property_address as *const _, 0, null(), &data_size as *const _ as *mut _, audio_buffer_list.as_mut_ptr() as *mut _, ); if status != kAudioHardwareNoError as i32 { unimplemented!(); } let audio_buffer_list = audio_buffer_list.as_mut_ptr() as *mut AudioBufferList; // If there's no buffers, skip. if (*audio_buffer_list).mNumberBuffers == 0 { return Ok(vec![].into_iter()); } // Count the number of channels as the sum of all channels in all output buffers. let n_buffers = (*audio_buffer_list).mNumberBuffers as usize; let first: *const AudioBuffer = (*audio_buffer_list).mBuffers.as_ptr(); let buffers: &'static [AudioBuffer] = slice::from_raw_parts(first, n_buffers); let mut n_channels = 0; for buffer in buffers { n_channels += buffer.mNumberChannels as usize; } // AFAIK the sample format should always be f32 on macos and i16 on iOS? Feel free to // fix this if more pcm formats are supported. let sample_format = if cfg!(target_os = "ios") { SampleFormat::I16 } else { SampleFormat::F32 }; // Get available sample rate ranges. property_address.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; let data_size = 0u32; let status = AudioObjectGetPropertyDataSize( self.audio_device_id, &property_address as *const _, 0, null(), &data_size as *const _ as *mut _, ); if status != kAudioHardwareNoError as i32 { unimplemented!(); } let n_ranges = data_size as usize / mem::size_of::(); let mut ranges: Vec = vec![]; ranges.reserve_exact(data_size as usize); let status = AudioObjectGetPropertyData( self.audio_device_id, &property_address as *const _, 0, null(), &data_size as *const _ as *mut _, ranges.as_mut_ptr() as *mut _, ); if status != kAudioHardwareNoError as i32 { unimplemented!(); } let ranges: *mut AudioValueRange = ranges.as_mut_ptr() as *mut _; let ranges: &'static [AudioValueRange] = slice::from_raw_parts(ranges, n_ranges); // Collect the supported formats for the device. let mut fmts = vec![]; for range in ranges { let fmt = SupportedFormat { channels: n_channels as ChannelCount, min_sample_rate: SampleRate(range.mMinimum as _), max_sample_rate: SampleRate(range.mMaximum as _), data_type: sample_format, }; fmts.push(fmt); } Ok(fmts.into_iter()) } } pub fn supported_input_formats(&self) -> Result { self.supported_formats(kAudioObjectPropertyScopeInput) } pub fn supported_output_formats(&self) -> Result { self.supported_formats(kAudioObjectPropertyScopeOutput) } fn default_format( &self, scope: AudioObjectPropertyScope, ) -> Result { fn default_format_error_from_os_status(status: OSStatus) -> Option { let err = match coreaudio::Error::from_os_status(status) { Err(err) => err, Ok(_) => return None, }; match err { coreaudio::Error::RenderCallbackBufferFormatDoesNotMatchAudioUnitStreamFormat | coreaudio::Error::NoKnownSubtype | coreaudio::Error::AudioUnit(coreaudio::error::AudioUnitError::FormatNotSupported) | coreaudio::Error::AudioCodec(_) | coreaudio::Error::AudioFormat(_) => Some(DefaultFormatError::StreamTypeNotSupported), _ => Some(DefaultFormatError::DeviceNotAvailable), } } let property_address = AudioObjectPropertyAddress { mSelector: kAudioDevicePropertyStreamFormat, mScope: scope, mElement: kAudioObjectPropertyElementMaster, }; unsafe { let asbd: AudioStreamBasicDescription = mem::uninitialized(); let data_size = mem::size_of::() as u32; let status = AudioObjectGetPropertyData( self.audio_device_id, &property_address as *const _, 0, null(), &data_size as *const _ as *mut _, &asbd as *const _ as *mut _, ); if status != kAudioHardwareNoError as i32 { let err = default_format_error_from_os_status(status) .expect("no known error for OsStatus"); return Err(err); } let sample_format = { let audio_format = coreaudio::audio_unit::AudioFormat::from_format_and_flag( asbd.mFormatID, Some(asbd.mFormatFlags), ); let flags = match audio_format { Some(coreaudio::audio_unit::AudioFormat::LinearPCM(flags)) => flags, _ => return Err(DefaultFormatError::StreamTypeNotSupported), }; let maybe_sample_format = coreaudio::audio_unit::SampleFormat::from_flags_and_bytes_per_frame( flags, asbd.mBytesPerFrame, ); match maybe_sample_format { Some(coreaudio::audio_unit::SampleFormat::F32) => SampleFormat::F32, Some(coreaudio::audio_unit::SampleFormat::I16) => SampleFormat::I16, _ => return Err(DefaultFormatError::StreamTypeNotSupported), } }; let format = Format { sample_rate: SampleRate(asbd.mSampleRate as _), channels: asbd.mChannelsPerFrame as _, data_type: sample_format, }; Ok(format) } } pub fn default_input_format(&self) -> Result { self.default_format(kAudioObjectPropertyScopeInput) } pub fn default_output_format(&self) -> Result { self.default_format(kAudioObjectPropertyScopeOutput) } } // The ID of a stream is its index within the `streams` array of the events loop. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct StreamId(usize); pub struct EventLoop { // This `Arc` is shared with all the callbacks of coreaudio. active_callbacks: Arc, streams: Mutex>>, } struct ActiveCallbacks { // Whenever the `run()` method is called with a callback, this callback is put in this list. callbacks: Mutex>, } struct StreamInner { playing: bool, audio_unit: AudioUnit, } // TODO need stronger error identification impl From for CreationError { fn from(err: coreaudio::Error) -> CreationError { match err { coreaudio::Error::RenderCallbackBufferFormatDoesNotMatchAudioUnitStreamFormat | coreaudio::Error::NoKnownSubtype | coreaudio::Error::AudioUnit(coreaudio::error::AudioUnitError::FormatNotSupported) | coreaudio::Error::AudioCodec(_) | coreaudio::Error::AudioFormat(_) => CreationError::FormatNotSupported, _ => CreationError::DeviceNotAvailable, } } } // Create a coreaudio AudioStreamBasicDescription from a CPAL Format. fn asbd_from_format(format: &Format) -> AudioStreamBasicDescription { let n_channels = format.channels as usize; let sample_rate = format.sample_rate.0; let bytes_per_channel = format.data_type.sample_size(); let bits_per_channel = bytes_per_channel * 8; let bytes_per_frame = n_channels * bytes_per_channel; let frames_per_packet = 1; let bytes_per_packet = frames_per_packet * bytes_per_frame; let sample_format = format.data_type; let format_flags = match sample_format { SampleFormat::F32 => (kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked) as u32, _ => kAudioFormatFlagIsPacked as u32, }; let asbd = AudioStreamBasicDescription { mBitsPerChannel: bits_per_channel as _, mBytesPerFrame: bytes_per_frame as _, mChannelsPerFrame: n_channels as _, mBytesPerPacket: bytes_per_packet as _, mFramesPerPacket: frames_per_packet as _, mFormatFlags: format_flags, mFormatID: kAudioFormatLinearPCM, mSampleRate: sample_rate as _, ..Default::default() }; asbd } fn audio_unit_from_device(device: &Device, input: bool) -> Result { let mut audio_unit = { let au_type = if cfg!(target_os = "ios") { // The HalOutput unit isn't available in iOS unfortunately. // RemoteIO is a sensible replacement. // See https://goo.gl/CWwRTx coreaudio::audio_unit::IOType::RemoteIO } else { coreaudio::audio_unit::IOType::HalOutput }; AudioUnit::new(au_type)? }; if input { // Enable input processing. let enable_input = 1u32; audio_unit.set_property( kAudioOutputUnitProperty_EnableIO, Scope::Input, Element::Input, Some(&enable_input), )?; // Disable output processing. let disable_output = 0u32; audio_unit.set_property( kAudioOutputUnitProperty_EnableIO, Scope::Output, Element::Output, Some(&disable_output), )?; } audio_unit.set_property( kAudioOutputUnitProperty_CurrentDevice, Scope::Global, Element::Output, Some(&device.audio_device_id), )?; Ok(audio_unit) } impl EventLoop { #[inline] pub fn new() -> EventLoop { EventLoop { active_callbacks: Arc::new(ActiveCallbacks { callbacks: Mutex::new(Vec::new()) }), streams: Mutex::new(Vec::new()), } } #[inline] pub fn run(&self, mut callback: F) -> ! where F: FnMut(StreamId, StreamData) + Send { { let callback: &mut (FnMut(StreamId, StreamData) + Send) = &mut callback; self.active_callbacks .callbacks .lock() .unwrap() .push(unsafe { mem::transmute(callback) }); } loop { // So the loop does not get optimised out in --release thread::sleep(Duration::new(1u64, 0u32)); } // Note: if we ever change this API so that `run` can return, then it is critical that // we remove the callback from `active_callbacks`. } fn next_stream_id(&self) -> usize { let streams_lock = self.streams.lock().unwrap(); let stream_id = streams_lock .iter() .position(|n| n.is_none()) .unwrap_or(streams_lock.len()); stream_id } // Add the stream to the list of streams within `self`. fn add_stream(&self, stream_id: usize, au: AudioUnit) { let inner = StreamInner { playing: true, audio_unit: au, }; let mut streams_lock = self.streams.lock().unwrap(); if stream_id == streams_lock.len() { streams_lock.push(Some(inner)); } else { streams_lock[stream_id] = Some(inner); } } #[inline] pub fn build_input_stream( &self, device: &Device, format: &Format, ) -> Result { let mut audio_unit = audio_unit_from_device(device, true)?; // The scope and element for working with a device's output stream. let scope = Scope::Output; let element = Element::Input; // Set the stream in interleaved mode. let asbd = asbd_from_format(format); audio_unit.set_property(kAudioUnitProperty_StreamFormat, scope, element, Some(&asbd))?; // Determine the future ID of the stream. let stream_id = self.next_stream_id(); // Register the callback that is being called by coreaudio whenever it needs data to be // fed to the audio buffer. let active_callbacks = self.active_callbacks.clone(); let sample_format = format.data_type; let bytes_per_channel = format.data_type.sample_size(); type Args = render_callback::Args; audio_unit.set_input_callback(move |args: Args| unsafe { let ptr = (*args.data.data).mBuffers.as_ptr() as *const AudioBuffer; let len = (*args.data.data).mNumberBuffers as usize; let buffers: &[AudioBuffer] = slice::from_raw_parts(ptr, len); // TODO: Perhaps loop over all buffers instead? let AudioBuffer { mNumberChannels: _num_channels, mDataByteSize: data_byte_size, mData: data } = buffers[0]; let mut callbacks = active_callbacks.callbacks.lock().unwrap(); // A small macro to simplify handling the callback for different sample types. macro_rules! try_callback { ($SampleFormat:ident, $SampleType:ty) => {{ let data_len = (data_byte_size as usize / bytes_per_channel) as usize; let data_slice = slice::from_raw_parts(data as *const $SampleType, data_len); let callback = match callbacks.get_mut(0) { Some(cb) => cb, None => return Ok(()), }; let buffer = InputBuffer { buffer: data_slice }; let unknown_type_buffer = UnknownTypeInputBuffer::$SampleFormat(::InputBuffer { buffer: Some(buffer) }); let stream_data = StreamData::Input { buffer: unknown_type_buffer }; callback(StreamId(stream_id), stream_data); }}; } match sample_format { SampleFormat::F32 => try_callback!(F32, f32), SampleFormat::I16 => try_callback!(I16, i16), SampleFormat::U16 => try_callback!(U16, u16), } Ok(()) })?; // TODO: start playing now? is that consistent with the other backends? audio_unit.start()?; // Add the stream to the list of streams within `self`. self.add_stream(stream_id, audio_unit); Ok(StreamId(stream_id)) } #[inline] pub fn build_output_stream( &self, device: &Device, format: &Format, ) -> Result { let mut audio_unit = audio_unit_from_device(device, false)?; // The scope and element for working with a device's output stream. let scope = Scope::Input; let element = Element::Output; // Set the stream in interleaved mode. let asbd = asbd_from_format(format); audio_unit.set_property(kAudioUnitProperty_StreamFormat, scope, element, Some(&asbd))?; // Determine the future ID of the stream. let stream_id = self.next_stream_id(); // Register the callback that is being called by coreaudio whenever it needs data to be // fed to the audio buffer. let active_callbacks = self.active_callbacks.clone(); let sample_format = format.data_type; let bytes_per_channel = format.data_type.sample_size(); type Args = render_callback::Args; audio_unit.set_render_callback(move |args: Args| unsafe { // If `run()` is currently running, then a callback will be available from this list. // Otherwise, we just fill the buffer with zeroes and return. let AudioBuffer { mNumberChannels: _num_channels, mDataByteSize: data_byte_size, mData: data } = (*args.data.data).mBuffers[0]; let mut callbacks = active_callbacks.callbacks.lock().unwrap(); // A small macro to simplify handling the callback for different sample types. macro_rules! try_callback { ($SampleFormat:ident, $SampleType:ty, $equilibrium:expr) => {{ let data_len = (data_byte_size as usize / bytes_per_channel) as usize; let data_slice = slice::from_raw_parts_mut(data as *mut $SampleType, data_len); let callback = match callbacks.get_mut(0) { Some(cb) => cb, None => { for sample in data_slice.iter_mut() { *sample = $equilibrium; } return Ok(()); } }; let buffer = OutputBuffer { buffer: data_slice }; let unknown_type_buffer = UnknownTypeOutputBuffer::$SampleFormat(::OutputBuffer { target: Some(buffer) }); let stream_data = StreamData::Output { buffer: unknown_type_buffer }; callback(StreamId(stream_id), stream_data); }}; } match sample_format { SampleFormat::F32 => try_callback!(F32, f32, 0.0), SampleFormat::I16 => try_callback!(I16, i16, 0), SampleFormat::U16 => try_callback!(U16, u16, ::std::u16::MAX / 2), } Ok(()) })?; // TODO: start playing now? is that consistent with the other backends? audio_unit.start()?; // Add the stream to the list of streams within `self`. self.add_stream(stream_id, audio_unit); Ok(StreamId(stream_id)) } pub fn destroy_stream(&self, stream_id: StreamId) { let mut streams = self.streams.lock().unwrap(); streams[stream_id.0] = None; } pub fn play_stream(&self, stream: StreamId) { let mut streams = self.streams.lock().unwrap(); let stream = streams[stream.0].as_mut().unwrap(); if !stream.playing { stream.audio_unit.start().unwrap(); stream.playing = true; } } pub fn pause_stream(&self, stream: StreamId) { let mut streams = self.streams.lock().unwrap(); let stream = streams[stream.0].as_mut().unwrap(); if stream.playing { stream.audio_unit.stop().unwrap(); stream.playing = false; } } } pub struct InputBuffer<'a, T: 'a> { buffer: &'a [T], } pub struct OutputBuffer<'a, T: 'a> { buffer: &'a mut [T], } impl<'a, T> InputBuffer<'a, T> { #[inline] pub fn buffer(&self) -> &[T] { &self.buffer } #[inline] pub fn finish(self) { // Nothing to be done. } } impl<'a, T> OutputBuffer<'a, T> where T: Sample { #[inline] pub fn buffer(&mut self) -> &mut [T] { &mut self.buffer } #[inline] pub fn len(&self) -> usize { self.buffer.len() } #[inline] pub fn finish(self) { // Do nothing. We wrote directly to the buffer. } }