2016-07-05 20:54:03 +00:00
|
|
|
extern crate coreaudio;
|
2015-02-28 18:50:29 +00:00
|
|
|
|
2017-10-11 11:24:49 +00:00
|
|
|
use ChannelPosition;
|
2015-09-24 03:02:28 +00:00
|
|
|
use CreationError;
|
|
|
|
use Format;
|
|
|
|
use FormatsEnumerationError;
|
2016-08-12 07:49:13 +00:00
|
|
|
use Sample;
|
2015-09-24 03:02:28 +00:00
|
|
|
use SampleFormat;
|
|
|
|
use SamplesRate;
|
2017-10-20 19:18:40 +00:00
|
|
|
use SupportedFormat;
|
2016-08-12 07:49:13 +00:00
|
|
|
use UnknownTypeBuffer;
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
use std::mem;
|
2016-08-12 07:49:13 +00:00
|
|
|
use std::sync::{Arc, Mutex};
|
2017-04-07 10:06:00 +00:00
|
|
|
use std::thread;
|
|
|
|
use std::time::Duration;
|
2017-11-03 09:51:02 +00:00
|
|
|
use std::slice;
|
2016-08-12 07:49:13 +00:00
|
|
|
|
2017-11-03 09:51:02 +00:00
|
|
|
use self::coreaudio::audio_unit::{AudioUnit, Scope, Element};
|
2016-08-12 07:49:13 +00:00
|
|
|
use self::coreaudio::audio_unit::render_callback::{self, data};
|
2017-11-03 09:51:02 +00:00
|
|
|
use self::coreaudio::bindings::audio_unit::{
|
|
|
|
AudioStreamBasicDescription,
|
|
|
|
AudioBuffer,
|
|
|
|
kAudioFormatLinearPCM,
|
|
|
|
kAudioFormatFlagIsFloat,
|
|
|
|
kAudioFormatFlagIsPacked,
|
|
|
|
kAudioUnitProperty_StreamFormat
|
|
|
|
};
|
2015-02-28 18:50:29 +00:00
|
|
|
|
2015-09-24 03:02:28 +00:00
|
|
|
mod enumerate;
|
|
|
|
|
2017-10-12 09:54:09 +00:00
|
|
|
pub use self::enumerate::{EndpointsIterator, SupportedFormatsIterator, default_endpoint};
|
2015-09-24 03:02:28 +00:00
|
|
|
|
|
|
|
#[derive(Clone, PartialEq, Eq)]
|
|
|
|
pub struct Endpoint;
|
|
|
|
|
|
|
|
impl Endpoint {
|
2017-10-23 14:41:38 +00:00
|
|
|
pub fn supported_formats(&self) -> Result<SupportedFormatsIterator, FormatsEnumerationError> {
|
2017-10-11 11:24:49 +00:00
|
|
|
Ok(
|
|
|
|
vec![
|
2017-10-20 19:18:40 +00:00
|
|
|
SupportedFormat {
|
2017-10-11 11:24:49 +00:00
|
|
|
channels: vec![ChannelPosition::FrontLeft, ChannelPosition::FrontRight],
|
2017-10-20 19:18:40 +00:00
|
|
|
min_samples_rate: SamplesRate(44100),
|
|
|
|
max_samples_rate: SamplesRate(44100),
|
2017-10-11 11:24:49 +00:00
|
|
|
data_type: SampleFormat::F32,
|
|
|
|
},
|
|
|
|
].into_iter(),
|
|
|
|
)
|
2015-09-24 03:02:28 +00:00
|
|
|
}
|
|
|
|
|
2017-10-12 09:54:09 +00:00
|
|
|
pub fn name(&self) -> String {
|
2015-09-24 03:02:28 +00:00
|
|
|
"Default AudioUnit Endpoint".to_string()
|
|
|
|
}
|
2015-02-28 18:50:29 +00:00
|
|
|
}
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
// The ID of a voice is its index within the `voices` array of the events loop.
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
|
|
pub struct VoiceId(usize);
|
|
|
|
|
|
|
|
pub struct EventLoop {
|
|
|
|
// This `Arc` is shared with all the callbacks of coreaudio.
|
|
|
|
active_callbacks: Arc<ActiveCallbacks>,
|
|
|
|
voices: Mutex<Vec<Option<VoiceInner>>>,
|
2016-08-12 07:49:13 +00:00
|
|
|
}
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
struct ActiveCallbacks {
|
|
|
|
// Whenever the `run()` method is called with a callback, this callback is put in this list.
|
|
|
|
callbacks: Mutex<Vec<&'static mut FnMut(VoiceId, UnknownTypeBuffer)>>,
|
2015-02-28 18:50:29 +00:00
|
|
|
}
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
struct VoiceInner {
|
|
|
|
playing: bool,
|
|
|
|
audio_unit: AudioUnit,
|
|
|
|
}
|
2015-02-28 18:50:29 +00:00
|
|
|
|
2017-11-03 09:50:02 +00:00
|
|
|
// TODO need stronger error identification
|
|
|
|
impl From<coreaudio::Error> 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,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
impl EventLoop {
|
2015-09-11 08:55:29 +00:00
|
|
|
#[inline]
|
2017-10-18 18:24:05 +00:00
|
|
|
pub fn new() -> EventLoop {
|
|
|
|
EventLoop {
|
2017-10-23 14:41:38 +00:00
|
|
|
active_callbacks: Arc::new(ActiveCallbacks { callbacks: Mutex::new(Vec::new()) }),
|
2017-10-18 18:24:05 +00:00
|
|
|
voices: Mutex::new(Vec::new()),
|
|
|
|
}
|
2015-09-24 03:02:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2017-10-18 18:24:05 +00:00
|
|
|
pub fn run<F>(&self, mut callback: F) -> !
|
|
|
|
where F: FnMut(VoiceId, UnknownTypeBuffer)
|
|
|
|
{
|
|
|
|
let callback: &mut FnMut(VoiceId, UnknownTypeBuffer) = &mut callback;
|
2017-10-23 14:41:38 +00:00
|
|
|
self.active_callbacks
|
|
|
|
.callbacks
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
|
|
|
.push(unsafe { mem::transmute(callback) });
|
2016-08-12 07:49:13 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
loop {
|
|
|
|
// So the loop does not get optimised out in --release
|
|
|
|
thread::sleep(Duration::new(1u64, 0u32));
|
2015-09-24 03:02:28 +00:00
|
|
|
}
|
2016-08-12 07:49:13 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
// Note: if we ever change this API so that `run` can return, then it is critical that
|
|
|
|
// we remove the callback from `active_callbacks`.
|
2016-08-12 07:49:13 +00:00
|
|
|
}
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
#[inline]
|
2017-11-03 09:51:02 +00:00
|
|
|
pub fn build_voice(&self, _endpoint: &Endpoint, _format: &Format)
|
2017-10-23 14:41:38 +00:00
|
|
|
-> Result<VoiceId, CreationError> {
|
2017-10-18 18:24:05 +00:00
|
|
|
let mut audio_unit = {
|
|
|
|
let au_type = if cfg!(target_os = "ios") {
|
2017-10-23 14:41:38 +00:00
|
|
|
// The DefaultOutput unit isn't available in iOS unfortunately.
|
|
|
|
// RemoteIO is a sensible replacement.
|
|
|
|
// See https://goo.gl/CWwRTx
|
2017-10-18 18:24:05 +00:00
|
|
|
coreaudio::audio_unit::IOType::RemoteIO
|
|
|
|
} else {
|
|
|
|
coreaudio::audio_unit::IOType::DefaultOutput
|
|
|
|
};
|
|
|
|
|
2017-11-03 09:50:02 +00:00
|
|
|
AudioUnit::new(au_type)?
|
2017-10-10 16:24:53 +00:00
|
|
|
};
|
2017-10-18 18:24:05 +00:00
|
|
|
|
2017-11-03 09:51:02 +00:00
|
|
|
// 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)?;
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
// Determine the future ID of the voice.
|
|
|
|
let mut voices_lock = self.voices.lock().unwrap();
|
2017-10-23 14:41:38 +00:00
|
|
|
let voice_id = voices_lock
|
|
|
|
.iter()
|
|
|
|
.position(|n| n.is_none())
|
|
|
|
.unwrap_or(voices_lock.len());
|
2015-09-24 21:24:12 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
// 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();
|
2017-11-03 09:51:02 +00:00
|
|
|
audio_unit.set_render_callback(move |args: render_callback::Args<data::Raw>| unsafe {
|
2017-10-18 18:24:05 +00:00
|
|
|
// If `run()` is currently running, then a callback will be available from this list.
|
|
|
|
// Otherwise, we just fill the buffer with zeroes and return.
|
2017-11-03 09:51:02 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
let mut callbacks = active_callbacks.callbacks.lock().unwrap();
|
|
|
|
let callback = if let Some(cb) = callbacks.get_mut(0) {
|
|
|
|
cb
|
|
|
|
} else {
|
2017-11-03 09:51:02 +00:00
|
|
|
for sample in data_slice.iter_mut() {
|
|
|
|
*sample = 0.0;
|
2017-10-18 18:24:05 +00:00
|
|
|
}
|
2017-11-03 09:51:02 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
return Ok(());
|
|
|
|
};
|
|
|
|
|
|
|
|
let buffer = {
|
|
|
|
Buffer {
|
2017-11-03 09:51:02 +00:00
|
|
|
buffer: data_slice
|
2015-09-24 03:02:28 +00:00
|
|
|
}
|
2017-10-18 18:24:05 +00:00
|
|
|
};
|
2016-08-12 07:49:13 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
callback(VoiceId(voice_id), UnknownTypeBuffer::F32(::Buffer { target: Some(buffer) }));
|
|
|
|
Ok(())
|
2016-08-12 07:49:13 +00:00
|
|
|
|
2017-11-03 09:50:02 +00:00
|
|
|
})?;
|
2015-09-24 03:02:28 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
// TODO: start playing now? is that consistent with the other backends?
|
2017-11-03 09:50:02 +00:00
|
|
|
audio_unit.start()?;
|
2017-10-18 18:24:05 +00:00
|
|
|
|
|
|
|
// Add the voice to the list of voices within `self`.
|
|
|
|
{
|
|
|
|
let inner = VoiceInner {
|
|
|
|
playing: true,
|
|
|
|
audio_unit: audio_unit,
|
|
|
|
};
|
|
|
|
|
|
|
|
if voice_id == voices_lock.len() {
|
|
|
|
voices_lock.push(Some(inner));
|
|
|
|
} else {
|
|
|
|
voices_lock[voice_id] = Some(inner);
|
|
|
|
}
|
2015-09-24 03:02:28 +00:00
|
|
|
}
|
2016-01-12 16:06:14 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
Ok(VoiceId(voice_id))
|
|
|
|
}
|
2015-02-28 18:50:29 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
pub fn destroy_voice(&self, voice_id: VoiceId) {
|
|
|
|
let mut voices = self.voices.lock().unwrap();
|
|
|
|
voices[voice_id.0] = None;
|
|
|
|
}
|
2017-04-19 10:44:42 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
pub fn play(&self, voice: VoiceId) {
|
|
|
|
let mut voices = self.voices.lock().unwrap();
|
|
|
|
let voice = voices[voice.0].as_mut().unwrap();
|
2016-08-12 07:49:13 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
if !voice.playing {
|
|
|
|
voice.audio_unit.start().unwrap();
|
|
|
|
voice.playing = true;
|
|
|
|
}
|
2015-02-28 18:50:29 +00:00
|
|
|
}
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
pub fn pause(&self, voice: VoiceId) {
|
|
|
|
let mut voices = self.voices.lock().unwrap();
|
|
|
|
let voice = voices[voice.0].as_mut().unwrap();
|
|
|
|
|
|
|
|
if voice.playing {
|
|
|
|
voice.audio_unit.stop().unwrap();
|
|
|
|
voice.playing = false;
|
2016-10-18 06:20:40 +00:00
|
|
|
}
|
2015-02-28 18:50:29 +00:00
|
|
|
}
|
2017-10-18 18:24:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Buffer<'a, T: 'a> {
|
2017-11-03 09:51:02 +00:00
|
|
|
buffer: &'a mut [T],
|
2017-10-18 18:24:05 +00:00
|
|
|
}
|
2015-02-28 18:50:29 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
impl<'a, T> Buffer<'a, T>
|
|
|
|
where T: Sample
|
|
|
|
{
|
2015-09-11 08:55:29 +00:00
|
|
|
#[inline]
|
2017-10-18 18:24:05 +00:00
|
|
|
pub fn buffer(&mut self) -> &mut [T] {
|
2017-11-03 09:51:02 +00:00
|
|
|
&mut self.buffer
|
2017-10-18 18:24:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
self.buffer.len()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn finish(self) {
|
2017-11-03 09:51:02 +00:00
|
|
|
// Do nothing. We wrote directly to the buffer.
|
2015-02-28 18:50:29 +00:00
|
|
|
}
|
|
|
|
}
|