Refactor of asio-sys while reviewing for code correctness

Most of this is an attempt at improving readability and modularity of
the asio-sys crate while attempting to review it for correctness. Still
unsure why glitching is occasionally occuring on output, but recorded
input sounds perfectly clean.
This commit is contained in:
mitchmindtree 2019-07-03 06:12:07 +10:00
parent 91c5aa86b3
commit cc5b0555c2
3 changed files with 215 additions and 193 deletions

View File

@ -1,4 +1,4 @@
mod asio_import; pub mod asio_import;
#[macro_use] #[macro_use]
pub mod errors; pub mod errors;
@ -175,6 +175,15 @@ struct AsioCallbacks {
) -> *mut ai::ASIOTime, ) -> *mut ai::ASIOTime,
} }
// A helper type to simplify retrieval of available buffer sizes.
#[derive(Default)]
struct BufferSizes {
min: c_long,
max: c_long,
pref: c_long,
grans: c_long,
}
lazy_static! { lazy_static! {
/// A global way to access all the callbacks. /// A global way to access all the callbacks.
/// This is required because of how ASIO /// This is required because of how ASIO
@ -200,28 +209,22 @@ impl Asio {
// The most drivers we can take // The most drivers we can take
const MAX_DRIVERS: usize = 100; const MAX_DRIVERS: usize = 100;
// Max length for divers name // Max length for divers name
const CHAR_LEN: usize = 32; const MAX_DRIVER_NAME_LEN: usize = 32;
// 2D array of driver names set to 0 // 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; MAX_DRIVER_NAME_LEN]; MAX_DRIVERS] =
// Pointer to each driver name [[0; MAX_DRIVER_NAME_LEN]; MAX_DRIVERS];
let mut p_driver_name: [*mut i8; MAX_DRIVERS] = [0 as *mut i8; MAX_DRIVERS]; // Pointer to each driver name.
let mut driver_name_ptrs: [*mut i8; MAX_DRIVERS] = [0 as *mut i8; MAX_DRIVERS];
for i in 0 .. MAX_DRIVERS { for (ptr, name) in driver_name_ptrs.iter_mut().zip(&mut driver_names[..]) {
p_driver_name[i] = driver_names[i].as_mut_ptr(); *ptr = (*name).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(driver_name_ptrs.as_mut_ptr(), MAX_DRIVERS as i32);
(0 .. num_drivers) (0 .. num_drivers)
.map(|i| { .map(|i| driver_name_to_utf8(&driver_names[i as usize]).to_string())
let name = CStr::from_ptr(p_driver_name[i as usize]); .collect()
let my_driver_name = name.to_owned();
my_driver_name
.into_string()
.expect("Failed to convert driver name")
}).collect()
} }
} }
@ -339,29 +342,18 @@ impl Driver {
Ok(()) Ok(())
} }
/// Get the current data type of the driver. /// Get the current data type of the driver's input stream.
/// ///
/// This queries a single channel's type assuming all channels have the same sample type. /// This queries a single channel's type assuming all channels have the same sample type.
pub fn input_data_type(&self) -> Result<AsioSampleType, AsioError> {
stream_data_type(true)
}
/// Get the current data type of the driver's output stream.
/// ///
/// TODO: Make this a seperate call for input and output as it is possible that input and /// This queries a single channel's type assuming all channels have the same sample type.
/// output have different sample types Initialize memory for calls. pub fn output_data_type(&self) -> Result<AsioSampleType, AsioError> {
pub fn data_type(&self) -> Result<AsioSampleType, AsioError> { stream_data_type(false)
let mut channel_info = ai::ASIOChannelInfo {
// Which channel we are querying
channel: 0,
// Was it input or output
isInput: 0,
// Was it active
isActive: 0,
channelGroup: 0,
// The sample type
type_: 0,
name: [0 as c_char; 32],
};
unsafe {
asio_result!(ai::ASIOGetChannelInfo(&mut channel_info))?;
Ok(FromPrimitive::from_i32(channel_info.type_).expect("failed to cast sample type"))
}
} }
/// Ask ASIO to allocate the buffers and give the callback pointers. /// Ask ASIO to allocate the buffers and give the callback pointers.
@ -371,116 +363,94 @@ impl Driver {
/// The prefered buffer size from ASIO is used. /// The prefered buffer size from ASIO is used.
fn create_buffers(&self, buffer_infos: &mut [AsioBufferInfo]) -> Result<c_long, AsioError> { fn create_buffers(&self, buffer_infos: &mut [AsioBufferInfo]) -> Result<c_long, AsioError> {
let num_channels = buffer_infos.len(); let num_channels = buffer_infos.len();
let mut callbacks = AsioCallbacks {
buffer_switch: buffer_switch,
sample_rate_did_change: sample_rate_did_change,
asio_message: asio_message,
buffer_switch_time_info: buffer_switch_time_info,
};
// To pass as ai::ASIOCallbacks // To pass as ai::ASIOCallbacks
let callbacks: *mut _ = &mut callbacks; let mut callbacks = create_asio_callbacks();
let mut min_b_size: c_long = 0;
let mut max_b_size: c_long = 0; // Retrieve the available buffer sizes.
let mut pref_b_size: c_long = 0; let buffer_sizes = asio_get_buffer_sizes()?;
let mut grans: c_long = 0; if buffer_sizes.pref <= 0 {
panic!(
"`ASIOGetBufferSize` produced unusable preferred buffer size of {}",
buffer_sizes.pref,
);
}
// Ensure the driver is in the `Initialized` state.
if let DriverState::Running = self.inner.state() {
self.stop()?;
}
if let DriverState::Prepared = self.inner.state() {
self.dispose_buffers()?;
}
unsafe { unsafe {
// Get the buffer sizes
// min possilbe size
// max possible size
// preferred size
// granularity
asio_result!(ai::ASIOGetBufferSize(
&mut min_b_size,
&mut max_b_size,
&mut pref_b_size,
&mut grans,
))?;
if pref_b_size <= 0 {
panic!(
"`ASIOGetBufferSize` produced unusable preferred buffer size of {}",
pref_b_size,
);
}
if let DriverState::Running = self.inner.state() {
self.stop()?;
}
if let DriverState::Prepared = self.inner.state() {
self.dispose_buffers()?;
}
asio_result!(ai::ASIOCreateBuffers( asio_result!(ai::ASIOCreateBuffers(
buffer_infos.as_mut_ptr() as *mut _, buffer_infos.as_mut_ptr() as *mut _,
num_channels as i32, num_channels as i32,
pref_b_size, buffer_sizes.pref,
callbacks as *mut _, &mut callbacks as *mut _ as *mut _,
))?; ))?;
} }
self.inner.set_state(DriverState::Prepared); self.inner.set_state(DriverState::Prepared);
Ok(pref_b_size) Ok(buffer_sizes.pref)
} }
/// Creates the streams. /// Creates the streams.
/// ///
/// Both input and output streams need to be created together as a single slice of /// Both input and output streams need to be created together as a single slice of
/// `ASIOBufferInfo`. /// `ASIOBufferInfo`.
fn create_streams(&self, streams: AsioStreams) -> Result<AsioStreams, AsioError> { fn create_streams(
let AsioStreams { input, output } = streams; &self,
match (input, output) { mut input_buffer_infos: Vec<AsioBufferInfo>,
mut output_buffer_infos: Vec<AsioBufferInfo>,
) -> Result<AsioStreams, AsioError> {
let (input, output) = match (input_buffer_infos.is_empty(), output_buffer_infos.is_empty()) {
// Both stream exist. // Both stream exist.
(Some(input), Some(mut output)) => { (false, false) => {
let split_point = input.buffer_infos.len(); // Create one continuous slice of buffers.
let mut bi = input.buffer_infos; let split_point = input_buffer_infos.len();
// Append the output to the input channels let mut all_buffer_infos = input_buffer_infos;
bi.append(&mut output.buffer_infos); all_buffer_infos.append(&mut output_buffer_infos);
// Create the buffers. // Create the buffers. On success, split the output and input again.
// if successful then split the output let buffer_size = self.create_buffers(&mut all_buffer_infos)?;
// and input again let output_buffer_infos = all_buffer_infos.split_off(split_point);
self.create_buffers(&mut bi).map(|buffer_size| { let input_buffer_infos = all_buffer_infos;
let out_bi = bi.split_off(split_point); let input = Some(AsioStream {
let in_bi = bi; buffer_infos: input_buffer_infos,
let output = Some(AsioStream { buffer_size,
buffer_infos: out_bi, });
buffer_size, let output = Some(AsioStream {
}); buffer_infos: output_buffer_infos,
let input = Some(AsioStream { buffer_size,
buffer_infos: in_bi, });
buffer_size, (input, output)
});
AsioStreams { output, input }
})
}, },
// Just input // Just input
(Some(mut input), None) => { (false, true) => {
self.create_buffers(&mut input.buffer_infos) let buffer_size = self.create_buffers(&mut input_buffer_infos)?;
.map(|buffer_size| { let input = Some(AsioStream {
AsioStreams { buffer_infos: input_buffer_infos,
input: Some(AsioStream { buffer_size,
buffer_infos: input.buffer_infos, });
buffer_size, let output = None;
}), (input, output)
output: None,
}
})
}, },
// Just output // Just output
(None, Some(mut output)) => { (true, false) => {
self.create_buffers(&mut output.buffer_infos) let buffer_size = self.create_buffers(&mut output_buffer_infos)?;
.map(|buffer_size| { let input = None;
AsioStreams { let output = Some(AsioStream {
output: Some(AsioStream { buffer_infos: output_buffer_infos,
buffer_infos: output.buffer_infos, buffer_size,
buffer_size, });
}), (input, output)
input: None,
}
})
}, },
// Impossible // Impossible
(None, None) => unreachable!("Trying to create streams without preparing"), (true, true) => unreachable!("Trying to create streams without preparing"),
} };
Ok(AsioStreams { input, output })
} }
/// Prepare the input stream. /// Prepare the input stream.
@ -492,70 +462,39 @@ impl Driver {
/// ///
/// `num_channels` is the desired number of input channels. /// `num_channels` is the desired number of input channels.
/// ///
/// This returns a full AsioStreams with both input /// This returns a full AsioStreams with both input and output if output was active.
/// and output if output was active.
pub fn prepare_input_stream( pub fn prepare_input_stream(
&self, &self,
output: Option<AsioStream>, output: Option<AsioStream>,
num_channels: usize, num_channels: usize,
) -> Result<AsioStreams, AsioError> { ) -> Result<AsioStreams, AsioError> {
let buffer_infos = (0 .. num_channels) let input_buffer_infos = prepare_buffer_infos(true, num_channels);
.map(|i| AsioBufferInfo { let output_buffer_infos = output
// These are output channels .map(|output| output.buffer_infos)
is_input: 1, .unwrap_or_else(Vec::new);
// Channel index self.create_streams(input_buffer_infos, output_buffer_infos)
channel_num: i as c_long,
// Double buffer. We don't know the type
// at this point
buffers: [std::ptr::null_mut(); 2],
}).collect();
// Create the streams
let streams = AsioStreams {
input: Some(AsioStream {
buffer_infos,
buffer_size: 0,
}),
output,
};
self.create_streams(streams)
} }
/// Prepare the output stream. /// Prepare the output stream.
/// Because only the latest call ///
/// to ASIOCreateBuffers is relevant this /// Because only the latest call to ASIOCreateBuffers is relevant this call will destroy all
/// call will destroy all past active buffers /// past active buffers and recreate them.
/// and recreate them. For this reason we take ///
/// the input stream if it exists. /// 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 /// `num_channels` is the desired number of output channels.
/// and output if input was active. ///
/// This returns a full AsioStreams with both input and output if input was active.
pub fn prepare_output_stream( pub fn prepare_output_stream(
&self, &self,
input: Option<AsioStream>, input: Option<AsioStream>,
num_channels: usize, num_channels: usize,
) -> Result<AsioStreams, AsioError> { ) -> Result<AsioStreams, AsioError> {
// Initialize data for FFI let input_buffer_infos = input
let buffer_infos = (0 .. num_channels) .map(|input| input.buffer_infos)
.map(|i| AsioBufferInfo { .unwrap_or_else(Vec::new);
// These are outputs let output_buffer_infos = prepare_buffer_infos(false, num_channels);
is_input: 0, self.create_streams(input_buffer_infos, output_buffer_infos)
// Channel index
channel_num: i as c_long,
// Pointer to each buffer. We don't know
// the type yet.
buffers: [std::ptr::null_mut(); 2],
}).collect();
// Create streams
let streams = AsioStreams {
output: Some(AsioStream {
buffer_infos,
buffer_size: 0,
}),
input,
};
self.create_streams(streams)
} }
/// Releases buffers allocations. /// Releases buffers allocations.
@ -697,16 +636,100 @@ impl Drop for DriverInner {
unsafe impl Send for AsioStream {} unsafe impl Send for AsioStream {}
/// Used by the input and output stream creation process.
fn prepare_buffer_infos(is_input: bool, n_channels: usize) -> Vec<AsioBufferInfo> {
let is_input = if is_input { 1 } else { 0 };
(0..n_channels)
.map(|ch| {
let channel_num = ch as c_long;
// To be filled by ASIOCreateBuffers.
let buffers = [std::ptr::null_mut(); 2];
AsioBufferInfo { is_input, channel_num, buffers }
})
.collect()
}
/// The set of callbacks passed to `ASIOCreateBuffers`.
fn create_asio_callbacks() -> AsioCallbacks {
AsioCallbacks {
buffer_switch: buffer_switch,
sample_rate_did_change: sample_rate_did_change,
asio_message: asio_message,
buffer_switch_time_info: buffer_switch_time_info,
}
}
/// Retrieve the minimum, maximum and preferred buffer sizes along with the available
/// buffer size granularity.
fn asio_get_buffer_sizes() -> Result<BufferSizes, AsioError> {
let mut b = BufferSizes::default();
unsafe {
let res = ai::ASIOGetBufferSize(&mut b.min, &mut b.max, &mut b.pref, &mut b.grans);
asio_result!(res)?;
}
Ok(b)
}
/// Retrieve the `ASIOChannelInfo` associated with the channel at the given index on either the
/// input or output stream (`true` for input).
fn asio_channel_info(channel: c_long, is_input: bool) -> Result<ai::ASIOChannelInfo, AsioError> {
let mut channel_info = ai::ASIOChannelInfo {
// Which channel we are querying
channel,
// Was it input or output
isInput: if is_input { 1 } else { 0 },
// Was it active
isActive: 0,
channelGroup: 0,
// The sample type
type_: 0,
name: [0 as c_char; 32],
};
unsafe {
asio_result!(ai::ASIOGetChannelInfo(&mut channel_info))?;
Ok(channel_info)
}
}
/// Retrieve the data type of either the input or output stream.
///
/// If `is_input` is true, this will be queried on the input stream.
fn stream_data_type(is_input: bool) -> Result<AsioSampleType, AsioError> {
let channel_info = asio_channel_info(0, is_input)?;
Ok(FromPrimitive::from_i32(channel_info.type_).expect("unkown `ASIOSampletype` value"))
}
/// ASIO uses null terminated c strings for driver names.
///
/// This converts to utf8.
fn driver_name_to_utf8(bytes: &[c_char]) -> std::borrow::Cow<str> {
unsafe {
CStr::from_ptr(bytes.as_ptr()).to_string_lossy()
}
}
/// ASIO uses null terminated c strings for channel names.
///
/// This converts to utf8.
fn _channel_name_to_utf8(bytes: &[c_char]) -> std::borrow::Cow<str> {
unsafe {
CStr::from_ptr(bytes.as_ptr()).to_string_lossy()
}
}
/// Idicates the sample rate has changed /// Idicates the sample rate has changed
/// TODO Change the sample rate when this /// TODO Change the sample rate when this
/// is called. /// is called.
extern "C" fn sample_rate_did_change(_s_rate: c_double) -> () {} extern "C" fn sample_rate_did_change(_s_rate: c_double) -> () {
println!("sample rate changed");
}
/// Messages for ASIO /// Messages for ASIO
/// This is not currently used /// 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 {
println!("selector: {}, vaule: {}", _selector, _value);
// TODO Impliment this to give proper responses // TODO Impliment this to give proper responses
4 as c_long 4 as c_long
} }
@ -727,7 +750,6 @@ extern "C" fn buffer_switch(double_buffer_index: c_long, _direct_process: c_long
// This lock is probably unavoidable // This lock is probably unavoidable
// but locks in the audio stream is not great // 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() {
if let Some(ref mut bc) = bc { if let Some(ref mut bc) = bc {
bc.run(double_buffer_index); bc.run(double_buffer_index);

View File

@ -108,7 +108,7 @@ impl Device {
let channels = self.driver.channels().map_err(default_format_err)?.ins as u16; let channels = self.driver.channels().map_err(default_format_err)?.ins as u16;
let sample_rate = SampleRate(self.driver.sample_rate().map_err(default_format_err)? as _); let sample_rate = SampleRate(self.driver.sample_rate().map_err(default_format_err)? as _);
// Map th ASIO sample type to a CPAL sample type // Map th ASIO sample type to a CPAL sample type
let data_type = self.driver.data_type().map_err(default_format_err)?; let data_type = self.driver.input_data_type().map_err(default_format_err)?;
let data_type = convert_data_type(&data_type) let data_type = convert_data_type(&data_type)
.ok_or(DefaultFormatError::StreamTypeNotSupported)?; .ok_or(DefaultFormatError::StreamTypeNotSupported)?;
Ok(Format { Ok(Format {
@ -122,7 +122,7 @@ impl Device {
pub fn default_output_format(&self) -> Result<Format, DefaultFormatError> { pub fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
let channels = self.driver.channels().map_err(default_format_err)?.outs as u16; let channels = self.driver.channels().map_err(default_format_err)?.outs as u16;
let sample_rate = SampleRate(self.driver.sample_rate().map_err(default_format_err)? as _); let sample_rate = SampleRate(self.driver.sample_rate().map_err(default_format_err)? as _);
let data_type = self.driver.data_type().map_err(default_format_err)?; let data_type = self.driver.output_data_type().map_err(default_format_err)?;
let data_type = convert_data_type(&data_type) let data_type = convert_data_type(&data_type)
.ok_or(DefaultFormatError::StreamTypeNotSupported)?; .ok_or(DefaultFormatError::StreamTypeNotSupported)?;
Ok(Format { Ok(Format {

View File

@ -178,7 +178,7 @@ impl EventLoop {
format: &Format, format: &Format,
) -> Result<StreamId, BuildStreamError> { ) -> Result<StreamId, BuildStreamError> {
let Device { driver, .. } = device; let Device { driver, .. } = device;
let stream_type = driver.data_type().map_err(build_stream_err)?; let stream_type = driver.input_data_type().map_err(build_stream_err)?;
// Ensure that the desired sample type is supported. // Ensure that the desired sample type is supported.
let data_type = super::device::convert_data_type(&stream_type) let data_type = super::device::convert_data_type(&stream_type)
@ -361,7 +361,7 @@ impl EventLoop {
format: &Format, format: &Format,
) -> Result<StreamId, BuildStreamError> { ) -> Result<StreamId, BuildStreamError> {
let Device { driver, .. } = device; let Device { driver, .. } = device;
let stream_type = driver.data_type().map_err(build_stream_err)?; let stream_type = driver.output_data_type().map_err(build_stream_err)?;
// Ensure that the desired sample type is supported. // Ensure that the desired sample type is supported.
let data_type = super::device::convert_data_type(&stream_type) let data_type = super::device::convert_data_type(&stream_type)
@ -399,13 +399,10 @@ impl EventLoop {
let stream_lock = asio_streams.lock().unwrap(); let stream_lock = asio_streams.lock().unwrap();
let ref asio_stream = match stream_lock.output { let ref asio_stream = match stream_lock.output {
Some(ref asio_stream) => asio_stream, Some(ref asio_stream) => asio_stream,
None => return (), None => return,
}; };
let mut callbacks = callbacks.lock().unwrap(); let mut callbacks = callbacks.lock().unwrap();
let callback = match callbacks.as_mut() { let callback = callbacks.as_mut();
Some(callback) => callback,
None => return (),
};
// Silence the ASIO buffer that is about to be used. // Silence the ASIO buffer that is about to be used.
// //
@ -435,7 +432,7 @@ impl EventLoop {
/// performing endianness conversions as necessary. /// performing endianness conversions as necessary.
unsafe fn process_output_callback<A, B, F, G>( unsafe fn process_output_callback<A, B, F, G>(
stream_id: StreamId, stream_id: StreamId,
callback: &mut (dyn FnMut(StreamId, StreamDataResult) + Send), callback: Option<&mut &mut (dyn FnMut(StreamId, StreamDataResult) + Send)>,
interleaved: &mut [u8], interleaved: &mut [u8],
silence_asio_buffer: bool, silence_asio_buffer: bool,
asio_stream: &sys::AsioStream, asio_stream: &sys::AsioStream,
@ -451,10 +448,13 @@ impl EventLoop {
{ {
// 1. Render interleaved buffer from callback. // 1. Render interleaved buffer from callback.
let interleaved: &mut [A] = cast_slice_mut(interleaved); let interleaved: &mut [A] = cast_slice_mut(interleaved);
callback( match callback {
stream_id, None => interleaved.iter_mut().for_each(|s| *s = A::SILENCE),
Ok(StreamData::Output { buffer: A::unknown_type_output_buffer(interleaved) }), Some(callback) => {
); let buffer = A::unknown_type_output_buffer(interleaved);
callback(stream_id, Ok(StreamData::Output { buffer }));
}
}
// 2. Silence ASIO channels if necessary. // 2. Silence ASIO channels if necessary.
let n_channels = interleaved.len() / asio_stream.buffer_size as usize; let n_channels = interleaved.len() / asio_stream.buffer_size as usize;