Last active
August 12, 2016 14:10
-
-
Save tomaka/5b0b0c77d826ec9ec9ac7f257c9b738c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extern crate coreaudio; | |
extern crate libc; | |
use std::sync::mpsc::{channel, Sender, Receiver}; | |
use std::sync::{Arc, Mutex}; | |
use std::sync::atomic::{AtomicUsize, Ordering}; | |
use std::cell::RefCell; | |
use std::mem; | |
use std::cmp; | |
use std::marker::PhantomData; | |
use futures::Poll; | |
use futures::Task; | |
use futures::TaskHandle; | |
use futures::stream::Stream; | |
use CreationError; | |
use Format; | |
use FormatsEnumerationError; | |
use SampleFormat; | |
use SamplesRate; | |
use ChannelPosition; | |
use UnknownTypeBuffer; | |
mod enumerate; | |
pub use self::enumerate::{EndpointsIterator, | |
SupportedFormatsIterator, | |
get_default_endpoint}; | |
use self::coreaudio::audio_unit::{AudioUnit, IOType}; | |
use self::coreaudio::audio_unit::render_callback::{self, data}; | |
#[derive(Clone, PartialEq, Eq)] | |
pub struct Endpoint; | |
impl Endpoint { | |
pub fn get_supported_formats_list(&self) | |
-> Result<SupportedFormatsIterator, FormatsEnumerationError> | |
{ | |
Ok(vec!(Format { | |
channels: vec![ChannelPosition::FrontLeft, ChannelPosition::FrontRight], | |
samples_rate: SamplesRate(44100), | |
data_type: SampleFormat::F32 | |
}).into_iter()) | |
} | |
pub fn get_name(&self) -> String { | |
"Default AudioUnit Endpoint".to_string() | |
} | |
} | |
pub struct EventLoop; | |
impl EventLoop { | |
#[inline] | |
pub fn new() -> EventLoop { EventLoop } | |
#[inline] | |
pub fn run(&self) { loop {} } | |
} | |
pub struct Buffer<T> { | |
args: render_callback::Args<data::NonInterleaved<f32>>, | |
buffer: Vec<f32>, | |
} | |
impl<T> Buffer<T> { | |
#[inline] | |
pub fn get_buffer(&mut self) -> &mut [T] { | |
&mut self.samples[..] | |
} | |
#[inline] | |
pub fn len(&self) -> usize { | |
self.samples.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 = 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; | |
} | |
} | |
} | |
} | |
type NumChannels = usize; | |
type NumFrames = usize; | |
pub struct Voice; | |
#[allow(dead_code)] // the audio_unit will be dropped if we don't hold it. | |
pub struct SamplesStream { | |
audio_unit: AudioUnit, | |
inner: Arc<Mutex<SamplesStreamInner>>, | |
} | |
// TODO: shouldn't be necessary | |
unsafe impl Sync for SamplesStream {} | |
unsafe impl Send for SamplesStream {} | |
struct SamplesStreamInner { | |
scheduled_task: Option<TaskHandle>, | |
current_callback: Option<render_callback::Args<data::NonInterleaved<f32>>>, | |
} | |
impl Stream for SamplesStream { | |
type Item = UnknownTypeBuffer; | |
type Error = (); | |
fn poll(&mut self, _: &mut Task) -> Poll<Option<Self::Item>, Self::Error> { | |
let mut inner = self.inner.lock().unwrap(); | |
// There are two possibilites: either we're answering a callback of coreaudio and we return | |
// a buffer, or we're not answering a callback and we return that we're not ready. | |
let current_callback = match inner.current_callback.take() { | |
Some(c) => c, | |
None => return Poll::NotReady | |
}; | |
let buffer_len = args.num_frames * args.data.channels().count(); | |
let buffer = Buffer { | |
args: current_callback, | |
buffer: vec![0; buffer_len], | |
}; | |
Poll::Ok(UnknownTypeBuffer::F32(::Buffer { target: Some(buffer) })) | |
} | |
fn schedule(&mut self, task: &mut Task) { | |
*self.inner.lock().unwrap().scheduled_task = Some(task.handle().clone()); | |
} | |
} | |
impl Voice { | |
pub fn new(_: &Endpoint, _: &Format, _: &EventLoop) | |
-> Result<(Voice, SamplesStream), CreationError> | |
{ | |
let inner = Arc::new(SamplesStreamInner { | |
scheduled_task: Mutex::new(None), | |
current_callback: Mutex::new(None), | |
}); | |
let audio_unit = match AudioUnit::new(IOType::HalOutput) { | |
Ok(au) => au, | |
Err(_) => return Err(CreationError::DeviceNotAvailable) // TODO: correct error | |
}; | |
// TODO: iOS uses integer and fixed-point data | |
{ | |
let inner = inner.clone(); | |
let ret = audio_unit.set_render_callback(move |args| { | |
// This callback is entered whenever the coreaudio engine needs to be fed data. | |
// Store the callback argument in the `SamplesStreamInner` and return the task | |
// that we're supposed to notify. | |
let scheduled = { | |
let mut inner = inner.lock().unwrap(); | |
assert!(inner.current_callback.is_none()); | |
*inner.current_callback = Some(args); | |
inner.scheduled_task.take() | |
}; | |
// It is important that `inner` is unlocked here. | |
if let Some(scheduled) = scheduled { | |
// Calling `notify()` should immediately call `poll()` on the `SamplesStream`, | |
// which will use the data we stored in `current_callback`. | |
scheduled.notify(); | |
} | |
// TODO: what should happen if the callback wasn't processed? in other word, what | |
// if the user didn't register any handler or did a stupid thing in the | |
// handler (like mem::forgetting the buffer)? | |
Ok(()) | |
}); | |
match ret { | |
Ok(_) => (), | |
Err(_) => return Err(CreationError::DeviceNotAvailable) // TODO: correct error | |
}; | |
} | |
match audio_unit.start() { | |
Ok(_) => (), | |
Err(_) => return Err(CreationError::DeviceNotAvailable) // TODO: correct error | |
}; | |
let samples_stream = SamplesStream { | |
audio_unit: audio_unit, | |
inner: inner, | |
}; | |
Ok((Voice, samples_stream)) | |
} | |
#[inline] | |
pub fn play(&mut self) { | |
// implicitly playing | |
} | |
#[inline] | |
pub fn pause(&mut self) { | |
unimplemented!() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment