diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ab3b84..d98fa18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ - Changed the emscripten backend to consume less CPU. - Added improvements to the crate documentation. - Implement `pause` and `play` for ALSA backend. +- Reduced the number of allocations in the CoreAudio backend. # Version 0.5.1 (2017-10-21) diff --git a/src/coreaudio/enumerate.rs b/src/coreaudio/enumerate.rs index 93fc527..52af70c 100644 --- a/src/coreaudio/enumerate.rs +++ b/src/coreaudio/enumerate.rs @@ -1,6 +1,5 @@ use super::Endpoint; -use Format; use SupportedFormat; use std::vec::IntoIter as VecIntoIter; diff --git a/src/coreaudio/mod.rs b/src/coreaudio/mod.rs index f800154..f79f76f 100644 --- a/src/coreaudio/mod.rs +++ b/src/coreaudio/mod.rs @@ -14,9 +14,18 @@ use std::mem; use std::sync::{Arc, Mutex}; use std::thread; use std::time::Duration; +use std::slice; -use self::coreaudio::audio_unit::AudioUnit; +use self::coreaudio::audio_unit::{AudioUnit, Scope, Element}; use self::coreaudio::audio_unit::render_callback::{self, data}; +use self::coreaudio::bindings::audio_unit::{ + AudioStreamBasicDescription, + AudioBuffer, + kAudioFormatLinearPCM, + kAudioFormatFlagIsFloat, + kAudioFormatFlagIsPacked, + kAudioUnitProperty_StreamFormat +}; mod enumerate; @@ -108,7 +117,7 @@ impl EventLoop { } #[inline] - pub fn build_voice(&self, endpoint: &Endpoint, format: &Format) + pub fn build_voice(&self, _endpoint: &Endpoint, _format: &Format) -> Result { let mut audio_unit = { let au_type = if cfg!(target_os = "ios") { @@ -123,6 +132,27 @@ impl EventLoop { AudioUnit::new(au_type)? }; + // TODO: iOS uses integer and fixed-point data + + // Set the stream in interleaved mode. + let asbd = AudioStreamBasicDescription { + mBitsPerChannel: 32, + mBytesPerFrame: 8, + mChannelsPerFrame: 2, + mBytesPerPacket: 8, + mFramesPerPacket: 1, + mFormatFlags: (kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked) as u32, + mFormatID: kAudioFormatLinearPCM, + mSampleRate: 44100.0, + ..Default::default() + }; + audio_unit.set_property( + kAudioUnitProperty_StreamFormat, + Scope::Input, + Element::Output, + Some(&asbd) + ).map_err(convert_error)?; + // Determine the future ID of the voice. let mut voices_lock = self.voices.lock().unwrap(); let voice_id = voices_lock @@ -130,31 +160,34 @@ impl EventLoop { .position(|n| n.is_none()) .unwrap_or(voices_lock.len()); - // TODO: iOS uses integer and fixed-point data - // 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(); - audio_unit.set_render_callback(move |mut args: render_callback::Args>| { + audio_unit.set_render_callback(move |args: render_callback::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 data_slice = slice::from_raw_parts_mut(data as *mut f32, (data_byte_size / 4) as usize); + let mut callbacks = active_callbacks.callbacks.lock().unwrap(); let callback = if let Some(cb) = callbacks.get_mut(0) { cb } else { - for channel in args.data.channels_mut() { - for elem in channel.iter_mut() { - *elem = 0.0; - } + for sample in data_slice.iter_mut() { + *sample = 0.0; } + return Ok(()); }; let buffer = { - let buffer_len = args.num_frames * args.data.channels().count(); Buffer { - args: &mut args, - buffer: vec![0.0; buffer_len], + buffer: data_slice } }; @@ -210,8 +243,7 @@ impl EventLoop { } pub struct Buffer<'a, T: 'a> { - args: &'a mut render_callback::Args>, - buffer: Vec, + buffer: &'a mut [T], } impl<'a, T> Buffer<'a, T> @@ -219,7 +251,7 @@ impl<'a, T> Buffer<'a, T> { #[inline] pub fn buffer(&mut self) -> &mut [T] { - &mut self.buffer[..] + &mut self.buffer } #[inline] @@ -229,13 +261,6 @@ impl<'a, T> Buffer<'a, T> #[inline] pub fn finish(self) { - // TODO: At the moment this assumes the Vec is a Vec. - // Need to add T: Sample and use Sample::to_vec_f32. - let num_channels = self.args.data.channels().count(); - for (i, frame) in self.buffer.chunks(num_channels).enumerate() { - for (channel, sample) in self.args.data.channels_mut().zip(frame.iter()) { - channel[i] = *sample; - } - } + // Do nothing. We wrote directly to the buffer. } } diff --git a/src/lib.rs b/src/lib.rs index f5452b6..4b72fc4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -111,6 +111,7 @@ #![recursion_limit = "512"] +#[cfg(target_os = "windows")] #[macro_use] extern crate lazy_static;