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;
|
2016-08-12 07:49:13 +00:00
|
|
|
|
|
|
|
use self::coreaudio::audio_unit::AudioUnit;
|
|
|
|
use self::coreaudio::audio_unit::render_callback::{self, data};
|
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-12 09:54:09 +00:00
|
|
|
pub fn supported_formats(
|
2017-10-11 11:24:49 +00:00
|
|
|
&self)
|
|
|
|
-> Result<SupportedFormatsIterator, FormatsEnumerationError> {
|
|
|
|
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-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 {
|
|
|
|
active_callbacks: Arc::new(ActiveCallbacks {
|
|
|
|
callbacks: Mutex::new(Vec::new()),
|
|
|
|
}),
|
|
|
|
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;
|
|
|
|
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]
|
|
|
|
pub fn build_voice(&self, endpoint: &Endpoint, format: &Format)
|
|
|
|
-> Result<VoiceId, CreationError>
|
|
|
|
{
|
2016-08-12 07:49:13 +00:00
|
|
|
fn convert_error(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,
|
|
|
|
}
|
|
|
|
}
|
2015-09-24 03:02:28 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
let mut audio_unit = {
|
|
|
|
let au_type = if cfg!(target_os = "ios") {
|
|
|
|
// The DefaultOutput unit isn't available in iOS unfortunately. RemoteIO is a sensible replacement.
|
|
|
|
// See
|
|
|
|
// https://developer.apple.com/library/content/documentation/MusicAudio/Conceptual/AudioUnitHostingGuide_iOS/UsingSpecificAudioUnits/UsingSpecificAudioUnits.html
|
|
|
|
coreaudio::audio_unit::IOType::RemoteIO
|
|
|
|
} else {
|
|
|
|
coreaudio::audio_unit::IOType::DefaultOutput
|
|
|
|
};
|
|
|
|
|
|
|
|
AudioUnit::new(au_type).map_err(convert_error)?
|
2017-10-10 16:24:53 +00:00
|
|
|
};
|
2017-10-18 18:24:05 +00:00
|
|
|
|
|
|
|
// Determine the future ID of the voice.
|
|
|
|
let mut voices_lock = self.voices.lock().unwrap();
|
|
|
|
let voice_id = voices_lock.iter().position(|n| n.is_none()).unwrap_or(voices_lock.len());
|
2015-09-24 21:24:12 +00:00
|
|
|
|
2016-08-12 07:49:13 +00:00
|
|
|
// TODO: iOS uses integer and fixed-point data
|
2016-06-28 23:09:54 +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();
|
|
|
|
audio_unit.set_render_callback(move |mut args: render_callback::Args<data::NonInterleaved<f32>>| {
|
|
|
|
// 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 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Ok(());
|
|
|
|
};
|
|
|
|
|
|
|
|
let buffer = {
|
|
|
|
let buffer_len = args.num_frames * args.data.channels().count();
|
|
|
|
Buffer {
|
|
|
|
args: &mut args,
|
|
|
|
buffer: vec![0.0; buffer_len],
|
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-10-18 18:24:05 +00:00
|
|
|
}).map_err(convert_error)?;
|
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?
|
|
|
|
audio_unit.start().map_err(convert_error)?;
|
|
|
|
|
|
|
|
// 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> {
|
|
|
|
args: &'a mut render_callback::Args<data::NonInterleaved<T>>,
|
|
|
|
buffer: Vec<T>,
|
|
|
|
}
|
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] {
|
|
|
|
&mut self.buffer[..]
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
self.buffer.len()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn finish(self) {
|
|
|
|
// TODO: At the moment this assumes the Vec<T> is a Vec<f32>.
|
|
|
|
// 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;
|
|
|
|
}
|
2016-10-18 06:20:40 +00:00
|
|
|
}
|
2015-02-28 18:50:29 +00:00
|
|
|
}
|
|
|
|
}
|