OSX support via the Apple Core Audio, Audio Unit C API. Only supports f32 so far.
This commit is contained in:
parent
2b86445c9a
commit
f212d85889
|
@ -1,3 +1,4 @@
|
||||||
/target
|
/target
|
||||||
/Cargo.lock
|
/Cargo.lock
|
||||||
.cargo/
|
.cargo/
|
||||||
|
.DS_Store
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
/target
|
||||||
|
/Cargo.lock
|
||||||
|
.cargo/
|
|
@ -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
|
@ -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;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![feature(core)]
|
||||||
|
|
||||||
extern crate cpal;
|
extern crate cpal;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -0,0 +1,233 @@
|
||||||
|
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<'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",
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ a conversion on your data.
|
||||||
If you have the possibility, you should try to match the format of the voice.
|
If you have the possibility, you should try to match the format of the voice.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
#![feature(core, unsafe_destructor)]
|
#![feature(box_syntax, core, unsafe_destructor)]
|
||||||
#![unstable]
|
#![unstable]
|
||||||
|
|
||||||
pub use samples_formats::{SampleFormat, Sample};
|
pub use samples_formats::{SampleFormat, Sample};
|
||||||
|
|
|
@ -18,7 +18,7 @@ impl Voice {
|
||||||
::SampleFormat::U16
|
::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
|
Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue