Merge pull request #27 from mitchmindtree/master

OSX support via the Apple Core Audio, Audio Unit C API. Only supports f32 samples so far. closes #15
This commit is contained in:
tomaka 2015-02-27 23:58:13 +01:00
commit 502b5f647d
10 changed files with 11043 additions and 4 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
/target
/Cargo.lock
.cargo/
.DS_Store

View File

@ -32,5 +32,9 @@ path = "alsa-sys"
version = "0"
path = "alsa-sys"
[target.x86_64-apple-darwin.dependencies.core_audio-sys]
version = "0"
path = "core_audio-sys"
[dev-dependencies]
vorbis = "0"

3
core_audio-sys/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/target
/Cargo.lock
.cargo/

10
core_audio-sys/Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "core_audio-sys"
version = "0.0.1"
authors = ["Mitchell Nordine <mitchell.nordine@gmail.com>"]
description = "Bindings for Apple's CoreAudio, AudioUnit and AudioToolbox APIs generated via rust-bindgen"
license = "MIT"
[dependencies]
libc = "*"

File diff suppressed because it is too large Load Diff

10
core_audio-sys/src/lib.rs Normal file
View File

@ -0,0 +1,10 @@
#![allow(missing_copy_implementations)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(raw_pointer_derive)]
extern crate libc;
pub mod audio_unit;

View File

@ -1,3 +1,5 @@
#![feature(core)]
extern crate cpal;
fn main() {

242
src/core_audio/mod.rs Normal file
View File

@ -0,0 +1,242 @@
extern crate "core_audio-sys" as core_audio;
extern crate libc;
use self::core_audio::audio_unit as au;
use std::mem;
use std::ptr::null_mut;
use std::sync::mpsc::{channel, Sender, Receiver};
type NumChannels = usize;
#[allow(dead_code)]
pub struct Voice {
audio_unit: *mut au::AudioUnit,
ready_receiver: Receiver<()>,
samples_sender: Sender<(Vec<f32>, NumChannels)>,
}
pub struct Buffer<'a, T: 'a> {
samples_sender: Sender<(Vec<f32>, NumChannels)>,
samples: Vec<T>,
num_channels: NumChannels,
marker: ::std::marker::PhantomData<&'a T>,
}
struct CallbackConnection {
ready_sender: Sender<()>,
samples_receiver: Receiver<(Vec<f32>, NumChannels)>,
}
const INPUT_SCOPE : au::AudioUnitScope = au::kAudioUnitScope_Input;
const OUTPUT_ELEMENT : au::AudioUnitElement = 0;
impl Voice {
pub fn new() -> Voice {
new_voice().unwrap()
}
pub fn get_channels(&self) -> ::ChannelsCount {
// TODO: use AudioUnitGetProperty...
2
}
pub fn get_samples_rate(&self) -> ::SamplesRate {
// TODO: use AudioUnitGetProperty...
::SamplesRate(44100)
}
pub fn get_samples_format(&self) -> ::SampleFormat {
// TODO: use AudioUnitGetProperty...
::SampleFormat::F32
}
pub fn append_data<'a, T>(&'a mut self, buffer_size: usize) -> Buffer<'a, T> where T: Clone {
while let None = self.ready_receiver.try_recv().ok() {}
Buffer {
samples_sender: self.samples_sender.clone(),
samples: vec![unsafe{ mem::uninitialized() }; buffer_size],
num_channels: 2,
marker: ::std::marker::PhantomData,
}
}
pub fn play(&mut self) {
// TODO
}
pub fn pause(&mut self) {
// TODO
}
}
impl Drop for Voice {
fn drop(&mut self) {
unsafe {
check_errors(au::AudioOutputUnitStop(*self.audio_unit)).unwrap();
check_errors(au::AudioUnitUninitialize(*self.audio_unit)).unwrap();
}
}
}
impl<'a, T> Buffer<'a, T> {
pub fn get_buffer<'b>(&'b mut self) -> &'b mut [T] {
&mut self.samples[..]
}
pub fn finish(self) {
let Buffer { samples_sender, samples, num_channels, .. } = 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 samples = unsafe { mem::transmute(samples) };
match samples_sender.send((samples, num_channels)) {
Err(_) => panic!("Failed to send samples to audio unit callback."),
Ok(()) => (),
}
}
}
/// Construct a new Voice.
fn new_voice() -> Result<Voice, String> {
let mut audio_unit = try!(default_audio_unit());
// A channel for signalling that the audio unit is ready for data.
let (ready_sender, ready_receiver) = channel();
// A channel for sending the audio callback a pointer to the sample data.
let (samples_sender, samples_receiver) = channel();
let callback_connection = box CallbackConnection {
ready_sender: ready_sender,
samples_receiver: samples_receiver,
};
let size_of_render_callback_struct = mem::size_of::<au::AURenderCallbackStruct>() as u32;
unsafe {
// Setup render callback.
let render_callback = au::AURenderCallbackStruct {
inputProc: Some(input_proc), // TODO
inputProcRefCon: mem::transmute(callback_connection),
};
try!(check_errors(au::AudioUnitSetProperty(audio_unit,
au::kAudioUnitProperty_SetRenderCallback,
INPUT_SCOPE,
OUTPUT_ELEMENT,
&render_callback as *const _ as *const libc::c_void,
size_of_render_callback_struct)));
// Initialise the audio unit!
try!(check_errors(au::AudioUnitInitialize(audio_unit)));
try!(check_errors(au::AudioOutputUnitStart(audio_unit)));
Ok(Voice {
audio_unit: &mut audio_unit as *mut au::AudioUnit,
ready_receiver: ready_receiver,
samples_sender: samples_sender,
})
}
}
/// Construct and initiate a new, default AudioUnit.
fn default_audio_unit() -> Result<au::AudioUnit, String> {
// A description of the audio unit we desire.
let desc = au::AudioComponentDescription {
componentType : au::kAudioUnitType_Output,
componentSubType : au::kAudioUnitSubType_HALOutput,
componentManufacturer : au::kAudioUnitManufacturer_Apple,
componentFlags : 0,
componentFlagsMask : 0,
};
unsafe {
// Find the default audio unit for the description.
let component = match au::AudioComponentFindNext(null_mut(), &desc as *const _) {
component if component == null_mut() => panic!("Could not find a default audio device."),
component => component,
};
// Get an instance of the default audio unit using the component.
let mut audio_unit: au::AudioUnit = mem::uninitialized();
au::AudioComponentInstanceNew(component, &mut audio_unit as *mut au::AudioUnit);
Ok(audio_unit as au::AudioUnit)
}
}
/// Callback procedure that will be called each time our audio_unit requests audio.
extern "C" fn input_proc(in_ref_con: *mut libc::c_void,
_io_action_flags: *mut au::AudioUnitRenderActionFlags,
_in_time_stamp: *const au::AudioTimeStamp,
_in_bus_number: au::UInt32,
in_number_frames: au::UInt32,
io_data: *mut au::AudioBufferList) -> au::OSStatus {
let callback_connection = in_ref_con as *mut CallbackConnection;
let (samples, num_channels) = match unsafe { (*callback_connection).samples_receiver.try_recv() } {
Ok((samples, num_channels)) => (samples, num_channels),
_ => (vec![0.0; 1024], 2),
};
if let Err(_) = unsafe { (*callback_connection).ready_sender.send(()) } {
return -1500
}
assert!(in_number_frames == (samples.len() / num_channels) as u32,
"The number of input frames given differs from the number requested by the AudioUnit");
let mut channels: Vec<&mut [f32]> = unsafe {
(0..num_channels)
.map(|i| {
let slice_ptr = (*io_data).mBuffers[i].mData as *mut libc::c_float;
::std::slice::from_raw_parts_mut(slice_ptr, in_number_frames as usize)
})
.collect()
};
for (i, frame) in samples.chunks(num_channels).enumerate() {
for (channel, sample) in channels.iter_mut().zip(frame.iter()) {
channel[i] = *sample;
}
}
0
}
/// Convert the AudioUnit OSStatus result into a Rust Result.
fn check_errors(result: au::OSStatus) -> Result<(), String> {
if result == 0 { return Ok(()); }
Err(format!("OSStatus {:?}: {:?}", result, match result {
-1500 => "An unspecified error has occurred",
-1501 => "System sound client message timed out",
-10847 => "Unauthorized",
-10848 => "Invalid offline render",
-10849 => "Initialized",
-10850 => "Property not in use",
-10851 => "Invalid property type",
-10863 => "Cannot do in current context",
-10865 => "Property not writeable",
-10866 => "Invalid scope",
-10867 => "Uninitialized",
-10868 => "Format not supported",
-10869 => "File not specified",
-10870 => "Unknown file type",
-10871 => "Invalid file",
-10872 => "Instrument type not found",
-10873 => "Illegal instrument",
-10874 => "Too many frames to process",
-10875 => "Failed initialization",
-10876 => "No connection",
-10877 => "Invalid element",
-10878 => "Invalid parameter",
-10879 => "Invalid property",
_ => "Unknown error",
}))
}

View File

@ -47,8 +47,7 @@ a conversion on your data.
If you have the possibility, you should try to match the format of the voice.
*/
#![allow(unstable)]
#![feature(unsafe_destructor)]
#![feature(box_syntax, core, unsafe_destructor)]
#![unstable]
pub use samples_formats::{SampleFormat, Sample};
@ -58,13 +57,18 @@ use std::ops::{Deref, DerefMut};
mod conversions;
mod samples_formats;
#[cfg(unix)]
#[cfg(target_os = "linux")]
#[path="alsa/mod.rs"]
mod cpal_impl;
#[cfg(windows)]
#[path="wasapi/mod.rs"]
mod cpal_impl;
#[cfg(target_os = "macos")]
#[path="core_audio/mod.rs"]
mod cpal_impl;
#[cfg(all(not(windows), not(unix)))]
#[path="null/mod.rs"]
mod cpal_impl;

View File

@ -18,7 +18,7 @@ impl Voice {
::SampleFormat::U16
}
pub fn append_data<'a, T>(&'a mut self, _: uint) -> Buffer<'a, T> {
pub fn append_data<'a, T>(&'a mut self, _: usize) -> Buffer<'a, T> {
Buffer
}