diff --git a/asio-sys/src/bindings/mod.rs b/asio-sys/src/bindings/mod.rs index e4e0376..596b1ca 100644 --- a/asio-sys/src/bindings/mod.rs +++ b/asio-sys/src/bindings/mod.rs @@ -5,7 +5,6 @@ pub mod errors; use self::errors::{AsioDriverError, AsioError, AsioErrorWrapper}; use std::ffi::CStr; use std::ffi::CString; -use std::mem; use std::os::raw::{c_char, c_double, c_long, c_void}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::{Mutex, MutexGuard}; @@ -478,15 +477,17 @@ impl Drivers { /// buffers. /// The prefered buffer size from ASIO is used. fn create_buffers( - &self, buffer_infos: Vec, + &self, mut buffer_infos: Vec, ) -> Result<(Vec, c_long), AsioDriverError> { let num_channels = buffer_infos.len(); - let callbacks = AsioCallbacks { + 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 + let callbacks: *mut _ = &mut callbacks; let mut min_b_size: c_long = 0; let mut max_b_size: c_long = 0; @@ -509,22 +510,14 @@ impl Drivers { &mut grans, ).expect("Failed getting buffers"); if pref_b_size > 0 { - // Convert Rust structs to opaque ASIO structs - let mut buffer_info_convert = - mem::transmute::, Vec>(buffer_infos); - let mut callbacks_convert = - mem::transmute::(callbacks); drivers .asio_create_buffers( - buffer_info_convert.as_mut_ptr(), + //buffer_info_convert.as_mut_ptr(), + buffer_infos.as_mut_ptr() as *mut _, num_channels as i32, pref_b_size, - &mut callbacks_convert, + callbacks as *mut _, ).map(|_| { - let buffer_infos = mem::transmute::< - Vec, - Vec, - >(buffer_info_convert); (buffer_infos, pref_b_size) }).map_err(|e| { AsioDriverError::BufferError(format!( @@ -616,18 +609,14 @@ pub fn clean_up() { /// Starts input and output streams playing pub fn play() { unsafe { - // TODO handle result instead of printing - let result = get_drivers().asio_start(); - println!("start result: {:?}", result); + get_drivers().asio_start().expect("Failed to start asio playing"); } } /// Stops input and output streams playing pub fn stop() { unsafe { - // TODO handle result instead of printing - let result = get_drivers().asio_stop(); - println!("stop result: {:?}", result); + get_drivers().asio_stop().expect("Failed to stop asio"); } } @@ -643,7 +632,7 @@ 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 c_char) -> bool { use self::AsioState::*; self.clean_up(); if ai::load_asio_driver(raw) { @@ -752,7 +741,7 @@ impl AsioWrapper { /// will be destoryed. unsafe fn asio_create_buffers( &mut self, buffer_info_convert: *mut ai::ASIOBufferInfo, num_channels: i32, - pref_b_size: c_long, callbacks_convert: &mut ai::ASIOCallbacks, + pref_b_size: c_long, callbacks_convert: *mut ai::ASIOCallbacks, ) -> Result<(), AsioError> { use self::AsioState::*; match self.state { diff --git a/src/os/mod.rs b/src/os/mod.rs index 581af77..3716caa 100644 --- a/src/os/mod.rs +++ b/src/os/mod.rs @@ -1,2 +1,7 @@ +/// The cpal::os module provides operating-system-specific +/// functionality. If you are using this module within a +/// cross-platform project, you may wish to use +/// cfg(target_os = "") to ensure that you only +/// use the OS-specific items when compiling for that OS. #[cfg(target_os = "windows")] pub mod windows; diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index 8c40f02..e148c81 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -1,3 +1,7 @@ +/// This allows you to choose either Wasapi or ASIO +/// as your back end. Wasapi is the default. +/// The CPAL_ASIO_DIR must be set to the ASIO SDK +/// directory for use_asio_backend to be available. use std::sync::Mutex; #[derive(Clone)] @@ -6,22 +10,23 @@ pub enum BackEnd { Asio, } -//static BACKEND: BackEnd = BackEnd::Asio; - lazy_static! { static ref BACK_END: Mutex = Mutex::new(BackEnd::Wasapi); } +/// See which beackend is currently set. pub fn which_backend() -> BackEnd { (*BACK_END.lock().unwrap()).clone() } #[cfg(asio)] +/// Choose ASIO as the backend pub fn use_asio_backend() -> Result<(), BackEndError> { *BACK_END.lock().unwrap() = BackEnd::Asio; Ok(()) } +/// Choose Wasapi as the backend pub fn use_wasapi_backend() -> Result<(), BackEndError> { *BACK_END.lock().unwrap() = BackEnd::Wasapi; Ok(()) diff --git a/src/platform/windows/asio/asio_utils/mod.rs b/src/platform/windows/asio/asio_utils/mod.rs index c9b221f..017454c 100644 --- a/src/platform/windows/asio/asio_utils/mod.rs +++ b/src/platform/windows/asio/asio_utils/mod.rs @@ -15,6 +15,10 @@ pub fn interleave(channels: &[Vec], target: &mut Vec) where T: Copy, { + assert!( + target.len() % channels.len() == 0, + "the length of the interleaved buffer must be a multiple of the expected number of channels" + ); assert!(!channels.is_empty()); target.clear(); let frames = channels[0].len(); @@ -32,6 +36,10 @@ pub fn deinterleave(cpal_buffer: &[T], asio_channels: &mut [Vec]) where T: Copy, { + assert!( + cpal_buffer.len() % asio_channels.len() == 0, + "the length of the interleaved buffer must be a multiple of the expected number of channels" + ); for ch in asio_channels.iter_mut() { ch.clear(); } diff --git a/src/platform/windows/asio/asio_utils/tests.rs b/src/platform/windows/asio/asio_utils/tests.rs index c0d9ffd..c43fe5f 100644 --- a/src/platform/windows/asio/asio_utils/tests.rs +++ b/src/platform/windows/asio/asio_utils/tests.rs @@ -1,4 +1,4 @@ -use super::{deinterleave, deinterleave_index, interleave}; +use super::{deinterleave, interleave}; #[test] fn interleave_two() { @@ -6,7 +6,7 @@ fn interleave_two() { let goal = vec![1, 2, 1, 2, 1, 2, 1, 2]; let mut result = vec![0; 8]; - interleave(&a[..], &mut result[..]); + interleave(&a[..], &mut result); assert_eq!(goal, result); } @@ -17,7 +17,7 @@ fn interleave_three() { let goal = vec![1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]; let mut result = vec![0; 12]; - interleave(&a[..], &mut result[..]); + interleave(&a[..], &mut result); assert_eq!(goal, result); } @@ -28,7 +28,7 @@ fn interleave_none() { let goal = Vec::::new(); let mut result = Vec::::new(); - interleave(&a[..], &mut result[..]); + interleave(&a[..], &mut result); assert_eq!(goal, result); } @@ -39,7 +39,7 @@ fn interleave_two_diff() { let goal = vec![1, 5, 2, 6, 3, 7, 4, 8]; let mut result = vec![0; 8]; - interleave(&a[..], &mut result[..]); + interleave(&a[..], &mut result); assert_eq!(goal, result); } diff --git a/src/platform/windows/asio/stream.rs b/src/platform/windows/asio/stream.rs index 8420d52..53443d0 100644 --- a/src/platform/windows/asio/stream.rs +++ b/src/platform/windows/asio/stream.rs @@ -8,14 +8,14 @@ use std; use std::mem; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; +use std::thread; +use std::time::Duration; use CreationError; use Format; use SampleFormat; use StreamData; use UnknownTypeInputBuffer; use UnknownTypeOutputBuffer; -use std::thread; -use std::time::Duration; /// Controls all streams pub struct EventLoop { @@ -26,8 +26,7 @@ pub struct EventLoop { /// Total stream count stream_count: AtomicUsize, /// The CPAL callback that the user gives to fill the buffers. - /// TODO This should probably not be in a Vec as there can only be one - callbacks: Arc>>, + callbacks: Arc>>, } /// Id for each stream. @@ -85,7 +84,7 @@ impl EventLoop { // This is why the Id's count from one not zero // because at this point there is no streams stream_count: AtomicUsize::new(0), - callbacks: Arc::new(Mutex::new(Vec::new())), + callbacks: Arc::new(Mutex::new(None)), } } @@ -93,7 +92,9 @@ impl EventLoop { /// If there is no ASIO Input Stream /// it will be created. fn get_input_stream( - &self, drivers: &sys::Drivers, format: &Format, + &self, + drivers: &sys::Drivers, + format: &Format, ) -> Result { let Format { channels, @@ -111,7 +112,7 @@ impl EventLoop { .set_sample_rate(sample_rate) .expect("Unsupported sample rate"); } else { - panic!("This sample rate {:?} is not supported", sample_rate); + return Err(CreationError::FormatNotSupported); } } // Either create a stream if thers none or had back the @@ -133,7 +134,7 @@ impl EventLoop { println!("Error preparing stream: {}", e); CreationError::DeviceNotAvailable }) - }, + } } } @@ -141,7 +142,9 @@ impl EventLoop { /// If there is no ASIO Output Stream /// it will be created. fn get_output_stream( - &self, drivers: &sys::Drivers, format: &Format, + &self, + drivers: &sys::Drivers, + format: &Format, ) -> Result { let Format { channels, @@ -182,13 +185,15 @@ impl EventLoop { println!("Error preparing stream: {}", e); CreationError::DeviceNotAvailable }) - }, + } } } /// Builds a new cpal input stream pub fn build_input_stream( - &self, device: &Device, format: &Format, + &self, + device: &Device, + format: &Format, ) -> Result { let Device { drivers, .. } = device; let num_channels = format.channels.clone(); @@ -212,7 +217,7 @@ impl EventLoop { SampleFormat::I16 => Buffers { i16_buff: I16Buffer { cpal: vec![0 as i16; cpal_num_samples], - channel: (0 .. num_channels) + channel: (0..num_channels) .map(|_| Vec::with_capacity(channel_len)) .collect(), }, @@ -222,7 +227,7 @@ impl EventLoop { i16_buff: I16Buffer::default(), f32_buff: F32Buffer { cpal: vec![0 as f32; cpal_num_samples], - channel: (0 .. num_channels) + channel: (0..num_channels) .map(|_| Vec::with_capacity(channel_len)) .collect(), }, @@ -253,27 +258,35 @@ impl EventLoop { // Theres only a single callback because theres only one event loop // TODO is 64bit necessary. Might be using more memory then needed - match callbacks.first_mut() { + match callbacks.as_mut() { Some(callback) => { // Macro to convert sample from ASIO to CPAL type macro_rules! convert_sample { - // Unsigned types required different conversion + // floats types required different conversion ($AsioTypeIdent:ident, - u16, + f32, $SampleTypeIdent:ident, $Sample:expr - ) => { - ((*$Sample as f64 + $AsioTypeIdent::MAX as f64) - / (::std::u16::MAX as f64 - / ::std::AsioTypeIdent::MAX as f64)) as u16 + ) => { + (*$Sample as f64 + / ::std::$SampleTypeIdent::MAX as f64) as f32 + }; + ($AsioTypeIdent:ident, + f64, + $SampleTypeIdent:ident, + $Sample:expr + ) => { + *$Sample as f64 + / ::std::$SampleTypeIdent::MAX as f64 }; ($AsioTypeIdent:ident, $SampleType:ty, $SampleTypeIdent:ident, $Sample:expr - ) => { + ) => { (*$Sample as i64 * ::std::$SampleTypeIdent::MAX as i64 - / ::std::$AsioTypeIdent::MAX as i64) as $SampleType + / ::std::$AsioTypeIdent::MAX as i64) + as $SampleType }; }; // This creates gets the buffer and interleaves it. @@ -293,7 +306,8 @@ impl EventLoop { // For each channel write the asio buffer to // the cpal buffer - for (i, channel) in $Buffers.channel.iter_mut().enumerate() + for (i, channel) in + $Buffers.channel.iter_mut().enumerate() { let buff_ptr = asio_stream.buffer_infos[i].buffers [index as usize] @@ -340,7 +354,9 @@ impl EventLoop { buffer: UnknownTypeInputBuffer::$SampleFormat( ::InputBuffer { buffer: Some( - super::super::InputBuffer::Asio(buff), + super::super::InputBuffer::Asio( + buff, + ), ), }, ), @@ -363,7 +379,7 @@ impl EventLoop { Endian::Little, convert_endian_to ); - }, + } sys::AsioSampleType::ASIOSTInt16LSB => { try_callback!( I16, @@ -377,7 +393,7 @@ impl EventLoop { Endian::Little, convert_endian_to ); - }, + } sys::AsioSampleType::ASIOSTInt32MSB => { try_callback!( I16, @@ -391,7 +407,7 @@ impl EventLoop { Endian::Big, convert_endian_to ); - }, + } sys::AsioSampleType::ASIOSTInt16MSB => { try_callback!( I16, @@ -405,7 +421,7 @@ impl EventLoop { Endian::Big, convert_endian_to ); - }, + } sys::AsioSampleType::ASIOSTFloat32LSB => { try_callback!( F32, @@ -419,7 +435,7 @@ impl EventLoop { Endian::Little, |a, _| a ); - }, + } sys::AsioSampleType::ASIOSTFloat64LSB => { try_callback!( F32, @@ -433,7 +449,7 @@ impl EventLoop { Endian::Little, |a, _| a ); - }, + } sys::AsioSampleType::ASIOSTFloat32MSB => { try_callback!( F32, @@ -447,7 +463,7 @@ impl EventLoop { Endian::Big, |a, _| a ); - }, + } sys::AsioSampleType::ASIOSTFloat64MSB => { try_callback!( F32, @@ -461,10 +477,10 @@ impl EventLoop { Endian::Big, |a, _| a ); - }, + } _ => println!("unsupported format {:?}", stream_type), } - }, + } None => return (), } } @@ -480,7 +496,9 @@ impl EventLoop { /// Create the an output cpal stream. pub fn build_output_stream( - &self, device: &Device, format: &Format, + &self, + device: &Device, + format: &Format, ) -> Result { let Device { drivers, .. } = device; let num_channels = format.channels.clone(); @@ -499,7 +517,7 @@ impl EventLoop { SampleFormat::I16 => Buffers { i16_buff: I16Buffer { cpal: vec![0 as i16; cpal_num_samples], - channel: (0 .. num_channels) + channel: (0..num_channels) .map(|_| Vec::with_capacity(channel_len)) .collect(), }, @@ -509,7 +527,7 @@ impl EventLoop { i16_buff: I16Buffer::default(), f32_buff: F32Buffer { cpal: vec![0 as f32; cpal_num_samples], - channel: (0 .. num_channels) + channel: (0..num_channels) .map(|_| Vec::with_capacity(channel_len)) .collect(), }, @@ -538,39 +556,46 @@ impl EventLoop { // Convert sample depending on the sample type macro_rules! convert_sample { ($AsioTypeIdent:ident, - $AsioType:ty, - u16, - $Sample:expr - ) => { - ((*$Sample as i64 * ::std::$AsioTypeIdent::MAX as i64 - / ::std::u16::MAX as i64) - - $AsioTypeIdent::MAX as i64) as $AsioType + f32, + $SampleTypeIdent:ident, + $Sample:expr + ) => { + (*$Sample as f64 + / ::std::$SampleTypeIdent::MAX as f64) as f32 }; ($AsioTypeIdent:ident, - $AsioType:ty, - $SampleTypeIdent:ident, - $Sample:expr - ) => { + f64, + $SampleTypeIdent:ident, + $Sample:expr + ) => { + *$Sample as f64 + / ::std::$SampleTypeIdent::MAX as f64 + }; + ($AsioTypeIdent:ident, + $AsioType:ty, + $SampleTypeIdent:ident, + $Sample:expr + ) => { (*$Sample as i64 * ::std::$AsioTypeIdent::MAX as i64 / ::std::$SampleTypeIdent::MAX as i64) as $AsioType }; }; // Theres only a single callback because theres only one event loop - match callbacks.first_mut() { + match callbacks.as_mut() { Some(callback) => { macro_rules! try_callback { ($SampleFormat:ident, - $SampleType:ty, - $SampleTypeIdent:ident, - $AsioType:ty, - $AsioTypeIdent:ident, - $Buffers:expr, - $BuffersType:ty, - $BuffersTypeIdent:ident, - $Endianness:expr, - $ConvertEndian:expr - ) => { + $SampleType:ty, + $SampleTypeIdent:ident, + $AsioType:ty, + $AsioTypeIdent:ident, + $Buffers:expr, + $BuffersType:ty, + $BuffersTypeIdent:ident, + $Endianness:expr, + $ConvertEndian:expr + ) => { let mut my_buffers = $Buffers; { // Wrap the cpal buffer @@ -675,7 +700,7 @@ impl EventLoop { Endian::Little, convert_endian_from ); - }, + } sys::AsioSampleType::ASIOSTInt16LSB => { try_callback!( I16, @@ -689,7 +714,7 @@ impl EventLoop { Endian::Little, convert_endian_from ); - }, + } sys::AsioSampleType::ASIOSTInt32MSB => { try_callback!( I16, @@ -703,7 +728,7 @@ impl EventLoop { Endian::Big, convert_endian_from ); - }, + } sys::AsioSampleType::ASIOSTInt16MSB => { try_callback!( I16, @@ -717,7 +742,7 @@ impl EventLoop { Endian::Big, convert_endian_from ); - }, + } sys::AsioSampleType::ASIOSTFloat32LSB => { try_callback!( F32, @@ -731,7 +756,7 @@ impl EventLoop { Endian::Little, |a, _| a ); - }, + } sys::AsioSampleType::ASIOSTFloat64LSB => { try_callback!( F32, @@ -745,7 +770,7 @@ impl EventLoop { Endian::Little, |a, _| a ); - }, + } sys::AsioSampleType::ASIOSTFloat32MSB => { try_callback!( F32, @@ -759,7 +784,7 @@ impl EventLoop { Endian::Big, |a, _| a ); - }, + } sys::AsioSampleType::ASIOSTFloat64MSB => { try_callback!( F32, @@ -773,10 +798,10 @@ impl EventLoop { Endian::Big, |a, _| a ); - }, + } _ => println!("unsupported format {:?}", stream_type), } - }, + } None => return (), } } @@ -842,10 +867,10 @@ impl EventLoop { F: FnMut(StreamId, StreamData) + Send, { let callback: &mut (FnMut(StreamId, StreamData) + Send) = &mut callback; - self.callbacks + // Transmute needed to convince the compiler that the callback has a static lifetime + *self.callbacks .lock() - .unwrap() - .push(unsafe { mem::transmute(callback) }); + .unwrap() = Some(unsafe { mem::transmute(callback) }); loop { // A sleep here to prevent the loop being // removed in --release