asio sys lib comments and rust fmt

This commit is contained in:
Tom Gowan 2018-11-05 11:28:59 +11:00 committed by mitchmindtree
parent ffcbc02e52
commit f6d1a74be2
3 changed files with 493 additions and 317 deletions

Binary file not shown.

Binary file not shown.

View File

@ -11,57 +11,85 @@ mod asio_import;
#[macro_use] #[macro_use]
pub mod errors; pub mod errors;
use errors::{AsioError, AsioDriverError, AsioErrorWrapper}; use errors::{AsioDriverError, AsioError, AsioErrorWrapper};
use std::ffi::CStr; use std::ffi::CStr;
use std::ffi::CString; use std::ffi::CString;
use std::mem; use std::mem;
use std::os::raw::{c_char, c_double, c_long, c_void}; use std::os::raw::{c_char, c_double, c_long, c_void};
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::{Mutex, MutexGuard}; use std::sync::{Mutex, MutexGuard};
// Bindings import
use asio_import as ai; use asio_import as ai;
// TODO I dont think this is needed anymore
pub struct CbArgs<S, D> { pub struct CbArgs<S, D> {
pub stream_id: S, pub stream_id: S,
pub data: D, pub data: D,
} }
/// Holds the pointer to the callbacks that come from cpal
struct BufferCallback(Box<FnMut(i32) + Send>); struct BufferCallback(Box<FnMut(i32) + Send>);
/// A global way to access all the callbacks.
/// This is required because of how ASIO
/// calls the buffer_switch function.
/// Options are used so that when a callback is
/// removed we don't change the Vec indicies.
/// The indicies are how we match a callback
/// with a stream.
lazy_static! { lazy_static! {
static ref buffer_callback: Mutex<Vec<Option<BufferCallback>>> = Mutex::new(Vec::new()); static ref buffer_callback: Mutex<Vec<Option<BufferCallback>>> = Mutex::new(Vec::new());
} }
/// Globally available state of the ASIO driver.
/// This allows all calls the the driver to ensure
/// they are calling in the correct state.
/// It also prevents multiple calls happening at once.
lazy_static! { lazy_static! {
static ref ASIO_DRIVERS: Mutex<AsioWrapper> = Mutex::new(AsioWrapper { static ref ASIO_DRIVERS: Mutex<AsioWrapper> = Mutex::new(AsioWrapper {
state: AsioState::Offline, state: AsioState::Offline,
}); });
} }
/// Count of active device and streams.
/// Used to clean up the driver connection
/// when there are no active connections.
static STREAM_DRIVER_COUNT: AtomicUsize = AtomicUsize::new(0); static STREAM_DRIVER_COUNT: AtomicUsize = AtomicUsize::new(0);
/// Tracks which buffer needs to be silenced.
pub static SILENCE_FIRST: AtomicBool = AtomicBool::new(false); pub static SILENCE_FIRST: AtomicBool = AtomicBool::new(false);
pub static SILENCE_SECOND: AtomicBool = AtomicBool::new(false); pub static SILENCE_SECOND: AtomicBool = AtomicBool::new(false);
/// Amount of input and output
/// channels available.
#[derive(Debug)] #[derive(Debug)]
pub struct Channel { pub struct Channel {
pub ins: i64, pub ins: i64,
pub outs: i64, pub outs: i64,
} }
/// Sample rate of the ASIO device.
#[derive(Debug)] #[derive(Debug)]
pub struct SampleRate { pub struct SampleRate {
pub rate: u32, pub rate: u32,
} }
/// A marker type to make sure
/// all calls to the driver have an
/// active connection.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Drivers; pub struct Drivers;
/// Tracks the current state of the
/// ASIO drivers.
#[derive(Debug)] #[derive(Debug)]
struct AsioWrapper { struct AsioWrapper {
state: AsioState, state: AsioState,
} }
/// All possible states of the
/// ASIO driver. Mapped to the
/// FSM in the ASIO SDK docs.
#[derive(Debug)] #[derive(Debug)]
enum AsioState { enum AsioState {
Offline, Offline,
@ -71,18 +99,27 @@ enum AsioState {
Running, Running,
} }
/// Input and Output streams.
/// There is only ever max one
/// input and one output. Only one
/// is required.
pub struct AsioStreams { pub struct AsioStreams {
pub input: Option<AsioStream>, pub input: Option<AsioStream>,
pub output: Option<AsioStream>, pub output: Option<AsioStream>,
} }
/// A stream to ASIO.
/// Contains the buffers.
pub struct AsioStream { pub struct AsioStream {
/// A Double buffer per channel
pub buffer_infos: Vec<AsioBufferInfo>, pub buffer_infos: Vec<AsioBufferInfo>,
/// Size of each buffer
pub buffer_size: i32, pub buffer_size: i32,
} }
// This is a direct copy of the ASIOSampleType /// All the possible types from ASIO.
// inside ASIO SDK. /// This is a direct copy of the ASIOSampleType
/// inside ASIO SDK.
#[derive(Debug, FromPrimitive)] #[derive(Debug, FromPrimitive)]
#[repr(C)] #[repr(C)]
pub enum AsioSampleType { pub enum AsioSampleType {
@ -120,14 +157,20 @@ pub enum AsioSampleType {
ASIOSTLastEntry, ASIOSTLastEntry,
} }
/// Gives information about buffers
/// Receives pointers to buffers
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
#[repr(C)] #[repr(C)]
pub struct AsioBufferInfo { pub struct AsioBufferInfo {
/// 0 for output 1 for input
pub is_input: c_long, pub is_input: c_long,
/// Which channel. Starts at 0
pub channel_num: c_long, pub channel_num: c_long,
/// Pointer to each half of the double buffer.
pub buffers: [*mut c_void; 2], pub buffers: [*mut c_void; 2],
} }
/// Callbacks that ASIO calls
#[repr(C)] #[repr(C)]
struct AsioCallbacks { struct AsioCallbacks {
buffer_switch: extern "C" fn(double_buffer_index: c_long, direct_process: c_long) -> (), buffer_switch: extern "C" fn(double_buffer_index: c_long, direct_process: c_long) -> (),
@ -141,7 +184,14 @@ struct AsioCallbacks {
direct_process: c_long, direct_process: c_long,
) -> *mut ai::ASIOTime, ) -> *mut ai::ASIOTime,
} }
/// This is called by ASIO.
/// Here we run the callback for each stream.
/// double_buffer_index is either 0 or 1
/// indicating which buffer to fill
extern "C" fn buffer_switch(double_buffer_index: c_long, _direct_process: c_long) -> () { extern "C" fn buffer_switch(double_buffer_index: c_long, _direct_process: c_long) -> () {
// This lock is probably unavoidable
// but locks in the audio stream is not great
let mut bcs = buffer_callback.lock().unwrap(); let mut bcs = buffer_callback.lock().unwrap();
for mut bc in bcs.iter_mut() { for mut bc in bcs.iter_mut() {
@ -151,48 +201,61 @@ extern "C" fn buffer_switch(double_buffer_index: c_long, _direct_process: c_long
} }
} }
/// Idicates the sample rate has changed
/// TODO Change the sample rate when this
/// is called.
extern "C" fn sample_rate_did_change(_s_rate: c_double) -> () {} extern "C" fn sample_rate_did_change(_s_rate: c_double) -> () {}
/// Messages for ASIO
/// This is not currently used
extern "C" fn asio_message( extern "C" fn asio_message(
_selector: c_long, _value: c_long, _message: *mut (), _opt: *mut c_double, _selector: c_long, _value: c_long, _message: *mut (), _opt: *mut c_double,
) -> c_long { ) -> c_long {
// TODO Impliment this to give proper responses
4 as c_long 4 as c_long
} }
/// Similar to buffer switch but with time info
/// Not currently used
extern "C" fn buffer_switch_time_info( extern "C" fn buffer_switch_time_info(
params: *mut ai::ASIOTime, _double_buffer_index: c_long, _direct_process: c_long, params: *mut ai::ASIOTime, _double_buffer_index: c_long, _direct_process: c_long,
) -> *mut ai::ASIOTime { ) -> *mut ai::ASIOTime {
params params
} }
/// Helper function for getting the drivers.
/// Note this is a lock.
fn get_drivers() -> MutexGuard<'static, AsioWrapper> { fn get_drivers() -> MutexGuard<'static, AsioWrapper> {
ASIO_DRIVERS.lock().unwrap() ASIO_DRIVERS.lock().unwrap()
} }
impl Drivers { impl Drivers {
/// Load the drivers from a driver name.
/// This will destroy the old drivers.
#[allow(unused_assignments)] #[allow(unused_assignments)]
pub fn load(driver_name: &str) -> Result<Self, AsioDriverError> { pub fn load(driver_name: &str) -> Result<Self, AsioDriverError> {
let mut drivers = get_drivers(); let mut drivers = get_drivers();
// Make owned CString to send to load driver // Make owned CString to send to load driver
let mut my_driver_name = let mut my_driver_name = CString::new(driver_name).expect("Can't go from str to CString");
CString::new(driver_name).expect("Can't go from str to CString");
let raw = my_driver_name.into_raw(); let raw = my_driver_name.into_raw();
let mut driver_info = ai::ASIODriverInfo { let mut driver_info = ai::ASIODriverInfo {
_bindgen_opaque_blob: [0u32; 43], _bindgen_opaque_blob: [0u32; 43],
}; };
unsafe { unsafe {
// Destroy old drivers and load new drivers.
let load_result = drivers.load(raw); let load_result = drivers.load(raw);
// Take back ownership // Take back ownership
my_driver_name = CString::from_raw(raw); my_driver_name = CString::from_raw(raw);
if load_result { if load_result {
// Initialize ASIO
match drivers.asio_init(&mut driver_info) { match drivers.asio_init(&mut driver_info) {
Ok(_) => { Ok(_) => {
// If it worked then add a active connection to the drivers
// TODO Make sure this is decremented when old drivers are dropped
STREAM_DRIVER_COUNT.fetch_add(1, Ordering::SeqCst); STREAM_DRIVER_COUNT.fetch_add(1, Ordering::SeqCst);
Ok(Drivers) Ok(Drivers)
}, },
Err(_) => { Err(_) => Err(AsioDriverError::DriverLoadError),
Err(AsioDriverError::DriverLoadError)
},
} }
} else { } else {
Err(AsioDriverError::DriverLoadError) Err(AsioDriverError::DriverLoadError)
@ -200,13 +263,8 @@ impl Drivers {
} }
} }
/// Returns the channels for the driver it's passed /// Returns the number of input and output
/// /// channels for the active drivers
/// # Arguments
/// * `driver name` - Name of the driver
/// # Usage
/// Use the get_driver_list() to get the list of names
/// Then pass the one you want to get_channels
pub fn get_channels(&self) -> Channel { pub fn get_channels(&self) -> Channel {
let channel: Channel; let channel: Channel;
@ -214,7 +272,11 @@ impl Drivers {
let mut ins: c_long = 0; let mut ins: c_long = 0;
let mut outs: c_long = 0; let mut outs: c_long = 0;
unsafe { unsafe {
get_drivers().asio_get_channels(&mut ins, &mut outs).expect("failed to get channels"); get_drivers()
.asio_get_channels(&mut ins, &mut outs)
// TODO pass this result along
// and handle it without panic
.expect("failed to get channels");
channel = Channel { channel = Channel {
ins: ins as i64, ins: ins as i64,
outs: outs as i64, outs: outs as i64,
@ -224,6 +286,7 @@ impl Drivers {
channel channel
} }
/// Get current sample rate of the active drivers
pub fn get_sample_rate(&self) -> SampleRate { pub fn get_sample_rate(&self) -> SampleRate {
let sample_rate: SampleRate; let sample_rate: SampleRate;
@ -231,47 +294,59 @@ impl Drivers {
let mut rate: c_double = 0.0f64; let mut rate: c_double = 0.0f64;
unsafe { unsafe {
get_drivers().asio_get_sample_rate(&mut rate).expect("failed to get sample rate"); get_drivers()
.asio_get_sample_rate(&mut rate)
// TODO pass this result along
// and handle it without panic
.expect("failed to get sample rate");
sample_rate = SampleRate { rate: rate as u32 }; sample_rate = SampleRate { rate: rate as u32 };
} }
sample_rate sample_rate
} }
/// Set the sample rate for the active drivers
pub fn set_sample_rate(&self, sample_rate: u32) -> Result<(), AsioError> { pub fn set_sample_rate(&self, sample_rate: u32) -> Result<(), AsioError> {
// Initialize memory for calls // Initialize memory for calls
let rate: c_double = c_double::from(sample_rate); let rate: c_double = c_double::from(sample_rate);
unsafe { unsafe { get_drivers().asio_set_sample_rate(rate) }
get_drivers().asio_set_sample_rate(rate)
}
} }
/// Can the drivers accept the given sample rate
pub fn can_sample_rate(&self, sample_rate: u32) -> bool { pub fn can_sample_rate(&self, sample_rate: u32) -> bool {
// Initialize memory for calls // Initialize memory for calls
let rate: c_double = c_double::from(sample_rate); let rate: c_double = c_double::from(sample_rate);
unsafe { // TODO this gives an error is it can't handle the sample
get_drivers().asio_can_sample_rate(rate).is_ok() // rate but it can also give error for no divers
} // Both should be handled.
unsafe { get_drivers().asio_can_sample_rate(rate).is_ok() }
} }
/// Get the current data type of the active drivers.
/// Just queries a single channels type as all channels
/// have the same sample type.
pub fn get_data_type(&self) -> Result<AsioSampleType, AsioDriverError> { pub fn get_data_type(&self) -> Result<AsioSampleType, AsioDriverError> {
// TODO make this a seperate call for input and output as
// it is possible that input and output have different sample types
// Initialize memory for calls // Initialize memory for calls
let mut channel_info = ai::ASIOChannelInfo { let mut channel_info = ai::ASIOChannelInfo {
// Which channel we are querying
channel: 0, channel: 0,
// Was it input or output
isInput: 0, isInput: 0,
// Was it active
isActive: 0, isActive: 0,
channelGroup: 0, channelGroup: 0,
// The sample type
type_: 0, type_: 0,
name: [0 as c_char; 32], name: [0 as c_char; 32],
}; };
unsafe { unsafe {
match get_drivers().asio_get_channel_info(&mut channel_info) { match get_drivers().asio_get_channel_info(&mut channel_info) {
Ok(_) => { Ok(_) => num::FromPrimitive::from_i32(channel_info.type_)
num::FromPrimitive::from_i32(channel_info.type_) .map_or(Err(AsioDriverError::TypeError), |t| Ok(t)),
.map_or(Err(AsioDriverError::TypeError), |t| Ok(t))
},
Err(e) => { Err(e) => {
println!("Error getting data type {}", e); println!("Error getting data type {}", e);
Err(AsioDriverError::DriverLoadError) Err(AsioDriverError::DriverLoadError)
@ -280,50 +355,92 @@ impl Drivers {
} }
} }
pub fn prepare_input_stream(&self, output: Option<AsioStream>, num_channels: usize) -> Result<AsioStreams, AsioDriverError> { /// Prepare the input stream.
/// Because only the latest call
/// to ASIOCreateBuffers is relevant this
/// call will destroy all past active buffers
/// and recreate them. For this reason we take
/// the output stream if it exists.
/// num_channels is the number of input channels.
/// This returns a full AsioStreams with both input
/// and output if output was active.
pub fn prepare_input_stream(
&self, output: Option<AsioStream>, num_channels: usize,
) -> Result<AsioStreams, AsioDriverError> {
let buffer_infos = (0 .. num_channels) let buffer_infos = (0 .. num_channels)
.map(|i| { .map(|i| AsioBufferInfo {
AsioBufferInfo { // These are output channels
is_input: 1, is_input: 1,
// Channel index
channel_num: i as c_long, channel_num: i as c_long,
// Double buffer. We don't know the type
// at this point
buffers: [std::ptr::null_mut(); 2], buffers: [std::ptr::null_mut(); 2],
} }).collect();
})
.collect();
let streams = AsioStreams{input: Some(AsioStream{buffer_infos, buffer_size: 0}), output}; // Create the streams
let streams = AsioStreams {
input: Some(AsioStream {
buffer_infos,
buffer_size: 0,
}),
output,
};
self.create_streams(streams) self.create_streams(streams)
} }
/// Creates the output stream /// Prepare the output stream.
pub fn prepare_output_stream(&self, input: Option<AsioStream>, num_channels: usize) -> Result<AsioStreams, AsioDriverError> { /// Because only the latest call
/// to ASIOCreateBuffers is relevant this
/// call will destroy all past active buffers
/// and recreate them. For this reason we take
/// the input stream if it exists.
/// num_channels is the number of output channels.
/// This returns a full AsioStreams with both input
/// and output if input was active.
pub fn prepare_output_stream(
&self, input: Option<AsioStream>, num_channels: usize,
) -> Result<AsioStreams, AsioDriverError> {
// Initialize data for FFI // Initialize data for FFI
let buffer_infos = (0 .. num_channels) let buffer_infos = (0 .. num_channels)
.map(|i| { .map(|i| AsioBufferInfo {
AsioBufferInfo { // These are outputs
is_input: 0, is_input: 0,
// Channel index
channel_num: i as c_long, channel_num: i as c_long,
// Pointer to each buffer. We don't know
// the type yet.
buffers: [std::ptr::null_mut(); 2], buffers: [std::ptr::null_mut(); 2],
} }).collect();
})
.collect(); // Create streams
let streams = AsioStreams{output: Some(AsioStream{buffer_infos, buffer_size: 0}), input}; let streams = AsioStreams {
output: Some(AsioStream {
buffer_infos,
buffer_size: 0,
}),
input,
};
self.create_streams(streams) self.create_streams(streams)
} }
/// Creates the output stream /// Creates the streams.
/// Both input and output streams
/// need to be created together as
/// a single slice of ASIOBufferInfo
fn create_streams(&self, streams: AsioStreams) -> Result<AsioStreams, AsioDriverError> { fn create_streams(&self, streams: AsioStreams) -> Result<AsioStreams, AsioDriverError> {
let AsioStreams { let AsioStreams { input, output } = streams;
input,
output,
} = streams;
match (input, output) { match (input, output) {
// Both stream exist.
(Some(input), Some(mut output)) => { (Some(input), Some(mut output)) => {
let split_point = input.buffer_infos.len(); let split_point = input.buffer_infos.len();
let mut bi = input.buffer_infos; let mut bi = input.buffer_infos;
// Append the output to the input channels
bi.append(&mut output.buffer_infos); bi.append(&mut output.buffer_infos);
self.create_buffers(bi) // Create the buffers.
.map(|(mut bi, buffer_size)|{ // if successful then split the output
// and input again
self.create_buffers(bi).map(|(mut bi, buffer_size)| {
let out_bi = bi.split_off(split_point); let out_bi = bi.split_off(split_point);
let in_bi = bi; let in_bi = bi;
let output = Some(AsioStream { let output = Some(AsioStream {
@ -337,6 +454,7 @@ impl Drivers {
AsioStreams { output, input } AsioStreams { output, input }
}) })
}, },
// Just input
(Some(input), None) => { (Some(input), None) => {
self.create_buffers(input.buffer_infos) self.create_buffers(input.buffer_infos)
.map(|(buffer_infos, buffer_size)| { .map(|(buffer_infos, buffer_size)| {
@ -350,6 +468,7 @@ impl Drivers {
} }
}) })
}, },
// Just output
(None, Some(output)) => { (None, Some(output)) => {
self.create_buffers(output.buffer_infos) self.create_buffers(output.buffer_infos)
.map(|(buffer_infos, buffer_size)| { .map(|(buffer_infos, buffer_size)| {
@ -363,13 +482,19 @@ impl Drivers {
} }
}) })
}, },
// Impossible
(None, None) => panic!("Trying to create streams without preparing"), (None, None) => panic!("Trying to create streams without preparing"),
} }
} }
fn create_buffers(&self, buffer_infos: Vec<AsioBufferInfo>) /// Ask ASIO to allocate the buffers
-> Result<(Vec<AsioBufferInfo>, c_long), AsioDriverError>{ /// and give the callback pointers.
/// This will destroy any already allocated
/// buffers.
/// The prefered buffer size from ASIO is used.
fn create_buffers(
&self, buffer_infos: Vec<AsioBufferInfo>,
) -> Result<(Vec<AsioBufferInfo>, c_long), AsioDriverError> {
let num_channels = buffer_infos.len(); let num_channels = buffer_infos.len();
let callbacks = AsioCallbacks { let callbacks = AsioCallbacks {
buffer_switch: buffer_switch, buffer_switch: buffer_switch,
@ -391,48 +516,46 @@ impl Drivers {
// max possible size // max possible size
// preferred size // preferred size
// granularity // granularity
drivers.asio_get_buffer_size( drivers
.asio_get_buffer_size(
&mut min_b_size, &mut min_b_size,
&mut max_b_size, &mut max_b_size,
&mut pref_b_size, &mut pref_b_size,
&mut grans, &mut grans,
).expect("Failed getting buffers"); ).expect("Failed getting buffers");
if pref_b_size > 0 { if pref_b_size > 0 {
/* // Convert Rust structs to opaque ASIO structs
let mut buffer_info_convert: Vec<ai::ASIOBufferInfo> = buffer_infos let mut buffer_info_convert =
.into_iter() mem::transmute::<Vec<AsioBufferInfo>, Vec<ai::ASIOBufferInfo>>(buffer_infos);
.map(|bi| mem::transmute::<AsioBufferInfo, ai::ASIOBufferInfo>(bi))
.collect();
*/
let mut buffer_info_convert = mem::transmute::<Vec<AsioBufferInfo>, Vec<ai::ASIOBufferInfo>>(buffer_infos);
let mut callbacks_convert = let mut callbacks_convert =
mem::transmute::<AsioCallbacks, ai::ASIOCallbacks>(callbacks); mem::transmute::<AsioCallbacks, ai::ASIOCallbacks>(callbacks);
drivers.asio_create_buffers( drivers
.asio_create_buffers(
buffer_info_convert.as_mut_ptr(), buffer_info_convert.as_mut_ptr(),
num_channels as i32, num_channels as i32,
pref_b_size, pref_b_size,
&mut callbacks_convert, &mut callbacks_convert,
).map(|_| { ).map(|_| {
let buffer_infos = mem::transmute::<Vec<ai::ASIOBufferInfo>, Vec<AsioBufferInfo>>(buffer_info_convert); let buffer_infos = mem::transmute::<
for d in &buffer_infos { Vec<ai::ASIOBufferInfo>,
println!("after {:?}", d); Vec<AsioBufferInfo>,
} >(buffer_info_convert);
println!("channels: {:?}", num_channels);
(buffer_infos, pref_b_size) (buffer_infos, pref_b_size)
}).map_err(|e| { }).map_err(|e| {
AsioDriverError::BufferError(format!( AsioDriverError::BufferError(format!(
"failed to create buffers, error code: {:?}", e)) "failed to create buffers, error code: {:?}",
e
))
}) })
} else { } else {
Err(AsioDriverError::BufferError( Err(AsioDriverError::BufferError("bad buffer size".to_owned()))
"bad buffer size".to_owned(),
))
} }
} }
} }
} }
/// If all drivers and streams are gone
/// clean up drivers
impl Drop for Drivers { impl Drop for Drivers {
fn drop(&mut self) { fn drop(&mut self) {
let count = STREAM_DRIVER_COUNT.fetch_sub(1, Ordering::SeqCst); let count = STREAM_DRIVER_COUNT.fetch_sub(1, Ordering::SeqCst);
@ -442,17 +565,21 @@ impl Drop for Drivers {
} }
} }
/// Required for Mutex
unsafe impl Send for AsioWrapper {} unsafe impl Send for AsioWrapper {}
/// Required for Mutex
unsafe impl Send for AsioStream {}
impl BufferCallback { impl BufferCallback {
/// Calls the inner callback
fn run(&mut self, index: i32) { fn run(&mut self, index: i32) {
let cb = &mut self.0; let cb = &mut self.0;
cb(index); cb(index);
} }
} }
unsafe impl Send for AsioStream {}
/// Adds a callback to the list of active callbacks
pub fn set_callback<F: 'static>(callback: F) -> () pub fn set_callback<F: 'static>(callback: F) -> ()
where where
F: FnMut(i32) + Send, F: FnMut(i32) + Send,
@ -461,20 +588,25 @@ where
bc.push(Some(BufferCallback(Box::new(callback)))); bc.push(Some(BufferCallback(Box::new(callback))));
} }
/// Returns a list of all the ASIO drivers /// Returns a list of all the ASIO devices.
/// This is used at the start to allow the
/// user to choose which device they want.
#[allow(unused_assignments)] #[allow(unused_assignments)]
pub fn get_driver_list() -> Vec<String> { pub fn get_driver_list() -> Vec<String> {
// The most devices we can take
const MAX_DRIVERS: usize = 100; const MAX_DRIVERS: usize = 100;
// Max length for divers name
const CHAR_LEN: usize = 32; const CHAR_LEN: usize = 32;
// 2D array of driver names set to 0
let mut driver_names: [[c_char; CHAR_LEN]; MAX_DRIVERS] = [[0; CHAR_LEN]; MAX_DRIVERS]; let mut driver_names: [[c_char; CHAR_LEN]; MAX_DRIVERS] = [[0; CHAR_LEN]; MAX_DRIVERS];
// Pointer to each driver name
let mut p_driver_name: [*mut i8; MAX_DRIVERS] = [0 as *mut i8; MAX_DRIVERS]; let mut p_driver_name: [*mut i8; MAX_DRIVERS] = [0 as *mut i8; MAX_DRIVERS];
for i in 0 .. MAX_DRIVERS { for i in 0 .. MAX_DRIVERS {
p_driver_name[i] = driver_names[i].as_mut_ptr(); p_driver_name[i] = driver_names[i].as_mut_ptr();
} }
unsafe { unsafe {
let num_drivers = ai::get_driver_names(p_driver_name.as_mut_ptr(), MAX_DRIVERS as i32); let num_drivers = ai::get_driver_names(p_driver_name.as_mut_ptr(), MAX_DRIVERS as i32);
@ -483,33 +615,50 @@ pub fn get_driver_list() -> Vec<String> {
let mut my_driver_name = CString::new("").unwrap(); let mut my_driver_name = CString::new("").unwrap();
let name = CStr::from_ptr(p_driver_name[i as usize]); let name = CStr::from_ptr(p_driver_name[i as usize]);
my_driver_name = name.to_owned(); my_driver_name = name.to_owned();
my_driver_name.into_string().expect("Failed to convert driver name") my_driver_name
}) .into_string()
.collect() .expect("Failed to convert driver name")
}).collect()
} }
} }
/// Cleans up the drivers and
/// any allocations
pub fn clean_up() { pub fn clean_up() {
let mut drivers = get_drivers(); let mut drivers = get_drivers();
drivers.clean_up(); drivers.clean_up();
} }
/// Starts input and output streams playing
pub fn play() { pub fn play() {
unsafe { unsafe {
// TODO handle result instead of printing
let result = get_drivers().asio_start(); let result = get_drivers().asio_start();
println!("start result: {:?}", result); println!("start result: {:?}", result);
} }
} }
/// Stops input and output streams playing
pub fn stop() { pub fn stop() {
unsafe { unsafe {
// TODO handle result instead of printing
let result = get_drivers().asio_stop(); let result = get_drivers().asio_stop();
println!("stop result: {:?}", result); println!("stop result: {:?}", result);
} }
} }
/// All the actual calls to ASIO.
/// This is where we handle the state
/// and assert that all calls
/// happen in the correct state.
/// TODO it is possible to enforce most of this
/// at compile time using type parameters.
/// All calls have results that are converted
/// to Rust style results.
impl AsioWrapper { impl AsioWrapper {
/// Load the driver.
/// Unloads the previous driver.
/// Sets state to Loaded on success.
unsafe fn load(&mut self, raw: *mut i8) -> bool { unsafe fn load(&mut self, raw: *mut i8) -> bool {
use AsioState::*; use AsioState::*;
self.clean_up(); self.clean_up();
@ -521,25 +670,31 @@ unsafe fn load(&mut self, raw: *mut i8) -> bool {
} }
} }
/// Unloads the current driver from ASIO
unsafe fn unload(&mut self) { unsafe fn unload(&mut self) {
ai::remove_current_driver(); ai::remove_current_driver();
} }
/// Initializes ASIO.
/// Needs to be already Loaded.
/// Initialized on success.
/// No-op if already Initialized or higher.
/// TODO should fail if Offline
unsafe fn asio_init(&mut self, di: &mut ai::ASIODriverInfo) -> Result<(), AsioError> { unsafe fn asio_init(&mut self, di: &mut ai::ASIODriverInfo) -> Result<(), AsioError> {
if let AsioState::Loaded = self.state { if let AsioState::Loaded = self.state {
let result = ai::ASIOInit(di); let result = ai::ASIOInit(di);
asio_result!(result) asio_result!(result)
.map(|_| self.state = AsioState::Initialized) .map(|_| self.state = AsioState::Initialized)
.map_err(|e| {
self.state = AsioState::Offline;
e
})
} else { } else {
Ok(()) Ok(())
} }
} }
unsafe fn asio_get_channels(&mut self, ins: &mut c_long, outs: &mut c_long) -> Result<(), AsioError> { /// Gets the number of channels.
/// Needs to be atleast Loaded.
unsafe fn asio_get_channels(
&mut self, ins: &mut c_long, outs: &mut c_long,
) -> Result<(), AsioError> {
if let AsioState::Offline = self.state { if let AsioState::Offline = self.state {
Err(AsioError::NoDrivers) Err(AsioError::NoDrivers)
} else { } else {
@ -548,6 +703,8 @@ unsafe fn asio_get_channels(&mut self, ins: &mut c_long, outs: &mut c_long) -> R
} }
} }
/// Gets the sample rate.
/// Needs to be atleast Loaded.
unsafe fn asio_get_sample_rate(&mut self, rate: &mut c_double) -> Result<(), AsioError> { unsafe fn asio_get_sample_rate(&mut self, rate: &mut c_double) -> Result<(), AsioError> {
if let AsioState::Offline = self.state { if let AsioState::Offline = self.state {
Err(AsioError::NoDrivers) Err(AsioError::NoDrivers)
@ -557,6 +714,8 @@ unsafe fn asio_get_sample_rate(&mut self, rate: &mut c_double) -> Result<(), Asi
} }
} }
/// Sets the sample rate.
/// Needs to be atleast Loaded.
unsafe fn asio_set_sample_rate(&mut self, rate: c_double) -> Result<(), AsioError> { unsafe fn asio_set_sample_rate(&mut self, rate: c_double) -> Result<(), AsioError> {
if let AsioState::Offline = self.state { if let AsioState::Offline = self.state {
Err(AsioError::NoDrivers) Err(AsioError::NoDrivers)
@ -566,6 +725,8 @@ unsafe fn asio_set_sample_rate(&mut self, rate: c_double) -> Result<(), AsioErro
} }
} }
/// Queries if the sample rate is possible.
/// Needs to be atleast Loaded.
unsafe fn asio_can_sample_rate(&mut self, rate: c_double) -> Result<(), AsioError> { unsafe fn asio_can_sample_rate(&mut self, rate: c_double) -> Result<(), AsioError> {
if let AsioState::Offline = self.state { if let AsioState::Offline = self.state {
Err(AsioError::NoDrivers) Err(AsioError::NoDrivers)
@ -575,7 +736,11 @@ unsafe fn asio_can_sample_rate(&mut self, rate: c_double) -> Result<(), AsioErro
} }
} }
unsafe fn asio_get_channel_info(&mut self, ci: &mut ai::ASIOChannelInfo) -> Result<(), AsioError> { /// Get information about a channel.
/// Needs to be atleast Loaded.
unsafe fn asio_get_channel_info(
&mut self, ci: &mut ai::ASIOChannelInfo,
) -> Result<(), AsioError> {
if let AsioState::Offline = self.state { if let AsioState::Offline = self.state {
Err(AsioError::NoDrivers) Err(AsioError::NoDrivers)
} else { } else {
@ -584,38 +749,40 @@ unsafe fn asio_get_channel_info(&mut self, ci: &mut ai::ASIOChannelInfo) -> Resu
} }
} }
/// Gets the buffer sizes.
/// Needs to be atleast Loaded.
unsafe fn asio_get_buffer_size( unsafe fn asio_get_buffer_size(
&mut self, &mut self, min_b_size: &mut c_long, max_b_size: &mut c_long, pref_b_size: &mut c_long,
min_b_size: &mut c_long, max_b_size: &mut c_long, pref_b_size: &mut c_long, grans: &mut c_long, grans: &mut c_long,
) -> Result<(), AsioError> { ) -> Result<(), AsioError> {
if let AsioState::Offline = self.state { if let AsioState::Offline = self.state {
Err(AsioError::NoDrivers) Err(AsioError::NoDrivers)
} else { } else {
let result = ai::ASIOGetBufferSize( let result = ai::ASIOGetBufferSize(min_b_size, max_b_size, pref_b_size, grans);
min_b_size,
max_b_size,
pref_b_size,
grans,
);
asio_result!(result) asio_result!(result)
} }
} }
/// Creates the buffers.
/// Needs to be atleast Loaded.
/// If Running or Prepared then old buffers
/// will be destoryed.
unsafe fn asio_create_buffers( unsafe fn asio_create_buffers(
&mut self, &mut self, buffer_info_convert: *mut ai::ASIOBufferInfo, num_channels: i32,
buffer_info_convert: *mut ai::ASIOBufferInfo, num_channels: i32, pref_b_size: c_long, pref_b_size: c_long, callbacks_convert: &mut ai::ASIOCallbacks,
callbacks_convert: &mut ai::ASIOCallbacks,
) -> Result<(), AsioError> { ) -> Result<(), AsioError> {
use AsioState::*; use AsioState::*;
match self.state { match self.state {
Offline | Loaded => return Err(AsioError::NoDrivers), Offline | Loaded => return Err(AsioError::NoDrivers),
Running => { Running => {
self.asio_stop().expect("Asio failed to stop"); self.asio_stop().expect("Asio failed to stop");
self.asio_dispose_buffers().expect("Failed to dispose buffers"); self.asio_dispose_buffers()
.expect("Failed to dispose buffers");
self.state = Initialized; self.state = Initialized;
}, },
Prepared => { Prepared => {
self.asio_dispose_buffers().expect("Failed to dispose buffers"); self.asio_dispose_buffers()
.expect("Failed to dispose buffers");
self.state = Initialized; self.state = Initialized;
}, },
_ => (), _ => (),
@ -626,10 +793,12 @@ unsafe fn asio_create_buffers(
pref_b_size, pref_b_size,
callbacks_convert, callbacks_convert,
); );
asio_result!(result) asio_result!(result).map(|_| self.state = AsioState::Prepared)
.map(|_| self.state = AsioState::Prepared)
} }
/// Releases buffers allocations.
/// Needs to be atleast Loaded.
/// No op if already released.
unsafe fn asio_dispose_buffers(&mut self) -> Result<(), AsioError> { unsafe fn asio_dispose_buffers(&mut self) -> Result<(), AsioError> {
use AsioState::*; use AsioState::*;
match self.state { match self.state {
@ -638,10 +807,11 @@ unsafe fn asio_dispose_buffers(&mut self) -> Result<(), AsioError> {
Initialized => return Ok(()), Initialized => return Ok(()),
} }
let result = ai::ASIODisposeBuffers(); let result = ai::ASIODisposeBuffers();
asio_result!(result) asio_result!(result).map(|_| self.state = AsioState::Initialized)
.map(|_| self.state = AsioState::Initialized)
} }
/// Closes down ASIO.
/// Needs to be atleast Loaded.
unsafe fn asio_exit(&mut self) -> Result<(), AsioError> { unsafe fn asio_exit(&mut self) -> Result<(), AsioError> {
use AsioState::*; use AsioState::*;
match self.state { match self.state {
@ -649,10 +819,11 @@ unsafe fn asio_exit(&mut self) -> Result<(), AsioError> {
_ => (), _ => (),
} }
let result = ai::ASIOExit(); let result = ai::ASIOExit();
asio_result!(result) asio_result!(result).map(|_| self.state = AsioState::Offline)
.map(|_| self.state = AsioState::Offline)
} }
/// Starts ASIO streams playing.
/// Needs to be atleast Initialized.
unsafe fn asio_start(&mut self) -> Result<(), AsioError> { unsafe fn asio_start(&mut self) -> Result<(), AsioError> {
use AsioState::*; use AsioState::*;
match self.state { match self.state {
@ -661,10 +832,12 @@ unsafe fn asio_start(&mut self) -> Result<(), AsioError> {
Prepared => (), Prepared => (),
} }
let result = ai::ASIOStart(); let result = ai::ASIOStart();
asio_result!(result) asio_result!(result).map(|_| self.state = AsioState::Running)
.map(|_| self.state = AsioState::Running)
} }
/// Stops ASIO streams playing.
/// Needs to be Running.
/// No-op if already stopped.
unsafe fn asio_stop(&mut self) -> Result<(), AsioError> { unsafe fn asio_stop(&mut self) -> Result<(), AsioError> {
use AsioState::*; use AsioState::*;
match self.state { match self.state {
@ -673,10 +846,11 @@ unsafe fn asio_stop(&mut self) -> Result<(), AsioError> {
Initialized | Prepared => return Ok(()), Initialized | Prepared => return Ok(()),
} }
let result = ai::ASIOStop(); let result = ai::ASIOStop();
asio_result!(result) asio_result!(result).map(|_| self.state = AsioState::Prepared)
.map(|_| self.state = AsioState::Prepared)
} }
/// Cleans up the drivers based
/// on the current state of the driver.
fn clean_up(&mut self) { fn clean_up(&mut self) {
match self.state { match self.state {
AsioState::Offline => (), AsioState::Offline => (),
@ -696,7 +870,8 @@ fn clean_up(&mut self) {
}, },
AsioState::Prepared => { AsioState::Prepared => {
unsafe { unsafe {
self.asio_dispose_buffers().expect("Failed to dispose buffers"); self.asio_dispose_buffers()
.expect("Failed to dispose buffers");
self.asio_exit().expect("Failed to exit asio"); self.asio_exit().expect("Failed to exit asio");
self.unload(); self.unload();
} }
@ -705,7 +880,8 @@ fn clean_up(&mut self) {
AsioState::Running => { AsioState::Running => {
unsafe { unsafe {
self.asio_stop().expect("Asio failed to stop"); self.asio_stop().expect("Asio failed to stop");
self.asio_dispose_buffers().expect("Failed to dispose buffers"); self.asio_dispose_buffers()
.expect("Failed to dispose buffers");
self.asio_exit().expect("Failed to exit asio"); self.asio_exit().expect("Failed to exit asio");
self.unload(); self.unload();
} }