fixes, remove transmute

This commit is contained in:
Tom Gowan 2018-11-07 18:38:33 +11:00 committed by mitchmindtree
parent 7950045240
commit ab3b76ad75
6 changed files with 131 additions and 99 deletions

View File

@ -5,7 +5,6 @@ pub mod errors;
use self::errors::{AsioDriverError, AsioError, AsioErrorWrapper}; use self::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::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::{AtomicBool, AtomicUsize, Ordering}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::{Mutex, MutexGuard}; use std::sync::{Mutex, MutexGuard};
@ -478,15 +477,17 @@ impl Drivers {
/// buffers. /// buffers.
/// The prefered buffer size from ASIO is used. /// The prefered buffer size from ASIO is used.
fn create_buffers( fn create_buffers(
&self, buffer_infos: Vec<AsioBufferInfo>, &self, mut buffer_infos: Vec<AsioBufferInfo>,
) -> Result<(Vec<AsioBufferInfo>, c_long), AsioDriverError> { ) -> Result<(Vec<AsioBufferInfo>, c_long), AsioDriverError> {
let num_channels = buffer_infos.len(); let num_channels = buffer_infos.len();
let callbacks = AsioCallbacks { let mut callbacks = AsioCallbacks {
buffer_switch: buffer_switch, buffer_switch: buffer_switch,
sample_rate_did_change: sample_rate_did_change, sample_rate_did_change: sample_rate_did_change,
asio_message: asio_message, asio_message: asio_message,
buffer_switch_time_info: buffer_switch_time_info, 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 min_b_size: c_long = 0;
let mut max_b_size: c_long = 0; let mut max_b_size: c_long = 0;
@ -509,22 +510,14 @@ impl Drivers {
&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 =
mem::transmute::<Vec<AsioBufferInfo>, Vec<ai::ASIOBufferInfo>>(buffer_infos);
let mut callbacks_convert =
mem::transmute::<AsioCallbacks, ai::ASIOCallbacks>(callbacks);
drivers drivers
.asio_create_buffers( .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, num_channels as i32,
pref_b_size, pref_b_size,
&mut callbacks_convert, callbacks as *mut _,
).map(|_| { ).map(|_| {
let buffer_infos = mem::transmute::<
Vec<ai::ASIOBufferInfo>,
Vec<AsioBufferInfo>,
>(buffer_info_convert);
(buffer_infos, pref_b_size) (buffer_infos, pref_b_size)
}).map_err(|e| { }).map_err(|e| {
AsioDriverError::BufferError(format!( AsioDriverError::BufferError(format!(
@ -616,18 +609,14 @@ pub fn clean_up() {
/// Starts input and output streams playing /// Starts input and output streams playing
pub fn play() { pub fn play() {
unsafe { unsafe {
// TODO handle result instead of printing get_drivers().asio_start().expect("Failed to start asio playing");
let result = get_drivers().asio_start();
println!("start result: {:?}", result);
} }
} }
/// Stops input and output streams playing /// Stops input and output streams playing
pub fn stop() { pub fn stop() {
unsafe { unsafe {
// TODO handle result instead of printing get_drivers().asio_stop().expect("Failed to stop asio");
let result = get_drivers().asio_stop();
println!("stop result: {:?}", result);
} }
} }
@ -643,7 +632,7 @@ impl AsioWrapper {
/// Load the driver. /// Load the driver.
/// Unloads the previous driver. /// Unloads the previous driver.
/// Sets state to Loaded on success. /// 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::*; use self::AsioState::*;
self.clean_up(); self.clean_up();
if ai::load_asio_driver(raw) { if ai::load_asio_driver(raw) {
@ -752,7 +741,7 @@ impl AsioWrapper {
/// will be destoryed. /// will be destoryed.
unsafe fn asio_create_buffers( unsafe fn asio_create_buffers(
&mut self, buffer_info_convert: *mut ai::ASIOBufferInfo, num_channels: i32, &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> { ) -> Result<(), AsioError> {
use self::AsioState::*; use self::AsioState::*;
match self.state { match self.state {

View File

@ -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 = "<os_name>") to ensure that you only
/// use the OS-specific items when compiling for that OS.
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub mod windows; pub mod windows;

View File

@ -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; use std::sync::Mutex;
#[derive(Clone)] #[derive(Clone)]
@ -6,22 +10,23 @@ pub enum BackEnd {
Asio, Asio,
} }
//static BACKEND: BackEnd = BackEnd::Asio;
lazy_static! { lazy_static! {
static ref BACK_END: Mutex<BackEnd> = Mutex::new(BackEnd::Wasapi); static ref BACK_END: Mutex<BackEnd> = Mutex::new(BackEnd::Wasapi);
} }
/// See which beackend is currently set.
pub fn which_backend() -> BackEnd { pub fn which_backend() -> BackEnd {
(*BACK_END.lock().unwrap()).clone() (*BACK_END.lock().unwrap()).clone()
} }
#[cfg(asio)] #[cfg(asio)]
/// Choose ASIO as the backend
pub fn use_asio_backend() -> Result<(), BackEndError> { pub fn use_asio_backend() -> Result<(), BackEndError> {
*BACK_END.lock().unwrap() = BackEnd::Asio; *BACK_END.lock().unwrap() = BackEnd::Asio;
Ok(()) Ok(())
} }
/// Choose Wasapi as the backend
pub fn use_wasapi_backend() -> Result<(), BackEndError> { pub fn use_wasapi_backend() -> Result<(), BackEndError> {
*BACK_END.lock().unwrap() = BackEnd::Wasapi; *BACK_END.lock().unwrap() = BackEnd::Wasapi;
Ok(()) Ok(())

View File

@ -15,6 +15,10 @@ pub fn interleave<T>(channels: &[Vec<T>], target: &mut Vec<T>)
where where
T: Copy, 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()); assert!(!channels.is_empty());
target.clear(); target.clear();
let frames = channels[0].len(); let frames = channels[0].len();
@ -32,6 +36,10 @@ pub fn deinterleave<T>(cpal_buffer: &[T], asio_channels: &mut [Vec<T>])
where where
T: Copy, 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() { for ch in asio_channels.iter_mut() {
ch.clear(); ch.clear();
} }

View File

@ -1,4 +1,4 @@
use super::{deinterleave, deinterleave_index, interleave}; use super::{deinterleave, interleave};
#[test] #[test]
fn interleave_two() { fn interleave_two() {
@ -6,7 +6,7 @@ fn interleave_two() {
let goal = vec![1, 2, 1, 2, 1, 2, 1, 2]; let goal = vec![1, 2, 1, 2, 1, 2, 1, 2];
let mut result = vec![0; 8]; let mut result = vec![0; 8];
interleave(&a[..], &mut result[..]); interleave(&a[..], &mut result);
assert_eq!(goal, 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 goal = vec![1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3];
let mut result = vec![0; 12]; let mut result = vec![0; 12];
interleave(&a[..], &mut result[..]); interleave(&a[..], &mut result);
assert_eq!(goal, result); assert_eq!(goal, result);
} }
@ -28,7 +28,7 @@ fn interleave_none() {
let goal = Vec::<i32>::new(); let goal = Vec::<i32>::new();
let mut result = Vec::<i32>::new(); let mut result = Vec::<i32>::new();
interleave(&a[..], &mut result[..]); interleave(&a[..], &mut result);
assert_eq!(goal, 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 goal = vec![1, 5, 2, 6, 3, 7, 4, 8];
let mut result = vec![0; 8]; let mut result = vec![0; 8];
interleave(&a[..], &mut result[..]); interleave(&a[..], &mut result);
assert_eq!(goal, result); assert_eq!(goal, result);
} }

View File

@ -8,14 +8,14 @@ use std;
use std::mem; use std::mem;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use CreationError; use CreationError;
use Format; use Format;
use SampleFormat; use SampleFormat;
use StreamData; use StreamData;
use UnknownTypeInputBuffer; use UnknownTypeInputBuffer;
use UnknownTypeOutputBuffer; use UnknownTypeOutputBuffer;
use std::thread;
use std::time::Duration;
/// Controls all streams /// Controls all streams
pub struct EventLoop { pub struct EventLoop {
@ -26,8 +26,7 @@ pub struct EventLoop {
/// Total stream count /// Total stream count
stream_count: AtomicUsize, stream_count: AtomicUsize,
/// The CPAL callback that the user gives to fill the buffers. /// 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<Mutex<Option<&'static mut (FnMut(StreamId, StreamData) + Send)>>>,
callbacks: Arc<Mutex<Vec<&'static mut (FnMut(StreamId, StreamData) + Send)>>>,
} }
/// Id for each stream. /// Id for each stream.
@ -85,7 +84,7 @@ impl EventLoop {
// This is why the Id's count from one not zero // This is why the Id's count from one not zero
// because at this point there is no streams // because at this point there is no streams
stream_count: AtomicUsize::new(0), 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 /// If there is no ASIO Input Stream
/// it will be created. /// it will be created.
fn get_input_stream( fn get_input_stream(
&self, drivers: &sys::Drivers, format: &Format, &self,
drivers: &sys::Drivers,
format: &Format,
) -> Result<usize, CreationError> { ) -> Result<usize, CreationError> {
let Format { let Format {
channels, channels,
@ -111,7 +112,7 @@ impl EventLoop {
.set_sample_rate(sample_rate) .set_sample_rate(sample_rate)
.expect("Unsupported sample rate"); .expect("Unsupported sample rate");
} else { } 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 // Either create a stream if thers none or had back the
@ -133,7 +134,7 @@ impl EventLoop {
println!("Error preparing stream: {}", e); println!("Error preparing stream: {}", e);
CreationError::DeviceNotAvailable CreationError::DeviceNotAvailable
}) })
}, }
} }
} }
@ -141,7 +142,9 @@ impl EventLoop {
/// If there is no ASIO Output Stream /// If there is no ASIO Output Stream
/// it will be created. /// it will be created.
fn get_output_stream( fn get_output_stream(
&self, drivers: &sys::Drivers, format: &Format, &self,
drivers: &sys::Drivers,
format: &Format,
) -> Result<usize, CreationError> { ) -> Result<usize, CreationError> {
let Format { let Format {
channels, channels,
@ -182,13 +185,15 @@ impl EventLoop {
println!("Error preparing stream: {}", e); println!("Error preparing stream: {}", e);
CreationError::DeviceNotAvailable CreationError::DeviceNotAvailable
}) })
}, }
} }
} }
/// Builds a new cpal input stream /// Builds a new cpal input stream
pub fn build_input_stream( pub fn build_input_stream(
&self, device: &Device, format: &Format, &self,
device: &Device,
format: &Format,
) -> Result<StreamId, CreationError> { ) -> Result<StreamId, CreationError> {
let Device { drivers, .. } = device; let Device { drivers, .. } = device;
let num_channels = format.channels.clone(); let num_channels = format.channels.clone();
@ -253,19 +258,26 @@ impl EventLoop {
// Theres only a single callback because theres only one event loop // Theres only a single callback because theres only one event loop
// TODO is 64bit necessary. Might be using more memory then needed // TODO is 64bit necessary. Might be using more memory then needed
match callbacks.first_mut() { match callbacks.as_mut() {
Some(callback) => { Some(callback) => {
// Macro to convert sample from ASIO to CPAL type // Macro to convert sample from ASIO to CPAL type
macro_rules! convert_sample { macro_rules! convert_sample {
// Unsigned types required different conversion // floats types required different conversion
($AsioTypeIdent:ident, ($AsioTypeIdent:ident,
u16, f32,
$SampleTypeIdent:ident, $SampleTypeIdent:ident,
$Sample:expr $Sample:expr
) => { ) => {
((*$Sample as f64 + $AsioTypeIdent::MAX as f64) (*$Sample as f64
/ (::std::u16::MAX as f64 / ::std::$SampleTypeIdent::MAX as f64) as f32
/ ::std::AsioTypeIdent::MAX as f64)) as u16 };
($AsioTypeIdent:ident,
f64,
$SampleTypeIdent:ident,
$Sample:expr
) => {
*$Sample as f64
/ ::std::$SampleTypeIdent::MAX as f64
}; };
($AsioTypeIdent:ident, ($AsioTypeIdent:ident,
$SampleType:ty, $SampleType:ty,
@ -273,7 +285,8 @@ impl EventLoop {
$Sample:expr $Sample:expr
) => { ) => {
(*$Sample as i64 * ::std::$SampleTypeIdent::MAX as i64 (*$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. // This creates gets the buffer and interleaves it.
@ -293,7 +306,8 @@ impl EventLoop {
// For each channel write the asio buffer to // For each channel write the asio buffer to
// the cpal buffer // 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 let buff_ptr = asio_stream.buffer_infos[i].buffers
[index as usize] [index as usize]
@ -340,7 +354,9 @@ impl EventLoop {
buffer: UnknownTypeInputBuffer::$SampleFormat( buffer: UnknownTypeInputBuffer::$SampleFormat(
::InputBuffer { ::InputBuffer {
buffer: Some( buffer: Some(
super::super::InputBuffer::Asio(buff), super::super::InputBuffer::Asio(
buff,
),
), ),
}, },
), ),
@ -363,7 +379,7 @@ impl EventLoop {
Endian::Little, Endian::Little,
convert_endian_to convert_endian_to
); );
}, }
sys::AsioSampleType::ASIOSTInt16LSB => { sys::AsioSampleType::ASIOSTInt16LSB => {
try_callback!( try_callback!(
I16, I16,
@ -377,7 +393,7 @@ impl EventLoop {
Endian::Little, Endian::Little,
convert_endian_to convert_endian_to
); );
}, }
sys::AsioSampleType::ASIOSTInt32MSB => { sys::AsioSampleType::ASIOSTInt32MSB => {
try_callback!( try_callback!(
I16, I16,
@ -391,7 +407,7 @@ impl EventLoop {
Endian::Big, Endian::Big,
convert_endian_to convert_endian_to
); );
}, }
sys::AsioSampleType::ASIOSTInt16MSB => { sys::AsioSampleType::ASIOSTInt16MSB => {
try_callback!( try_callback!(
I16, I16,
@ -405,7 +421,7 @@ impl EventLoop {
Endian::Big, Endian::Big,
convert_endian_to convert_endian_to
); );
}, }
sys::AsioSampleType::ASIOSTFloat32LSB => { sys::AsioSampleType::ASIOSTFloat32LSB => {
try_callback!( try_callback!(
F32, F32,
@ -419,7 +435,7 @@ impl EventLoop {
Endian::Little, Endian::Little,
|a, _| a |a, _| a
); );
}, }
sys::AsioSampleType::ASIOSTFloat64LSB => { sys::AsioSampleType::ASIOSTFloat64LSB => {
try_callback!( try_callback!(
F32, F32,
@ -433,7 +449,7 @@ impl EventLoop {
Endian::Little, Endian::Little,
|a, _| a |a, _| a
); );
}, }
sys::AsioSampleType::ASIOSTFloat32MSB => { sys::AsioSampleType::ASIOSTFloat32MSB => {
try_callback!( try_callback!(
F32, F32,
@ -447,7 +463,7 @@ impl EventLoop {
Endian::Big, Endian::Big,
|a, _| a |a, _| a
); );
}, }
sys::AsioSampleType::ASIOSTFloat64MSB => { sys::AsioSampleType::ASIOSTFloat64MSB => {
try_callback!( try_callback!(
F32, F32,
@ -461,10 +477,10 @@ impl EventLoop {
Endian::Big, Endian::Big,
|a, _| a |a, _| a
); );
}, }
_ => println!("unsupported format {:?}", stream_type), _ => println!("unsupported format {:?}", stream_type),
} }
}, }
None => return (), None => return (),
} }
} }
@ -480,7 +496,9 @@ impl EventLoop {
/// Create the an output cpal stream. /// Create the an output cpal stream.
pub fn build_output_stream( pub fn build_output_stream(
&self, device: &Device, format: &Format, &self,
device: &Device,
format: &Format,
) -> Result<StreamId, CreationError> { ) -> Result<StreamId, CreationError> {
let Device { drivers, .. } = device; let Device { drivers, .. } = device;
let num_channels = format.channels.clone(); let num_channels = format.channels.clone();
@ -538,13 +556,20 @@ impl EventLoop {
// Convert sample depending on the sample type // Convert sample depending on the sample type
macro_rules! convert_sample { macro_rules! convert_sample {
($AsioTypeIdent:ident, ($AsioTypeIdent:ident,
$AsioType:ty, f32,
u16, $SampleTypeIdent:ident,
$Sample:expr $Sample:expr
) => { ) => {
((*$Sample as i64 * ::std::$AsioTypeIdent::MAX as i64 (*$Sample as f64
/ ::std::u16::MAX as i64) / ::std::$SampleTypeIdent::MAX as f64) as f32
- $AsioTypeIdent::MAX as i64) as $AsioType };
($AsioTypeIdent:ident,
f64,
$SampleTypeIdent:ident,
$Sample:expr
) => {
*$Sample as f64
/ ::std::$SampleTypeIdent::MAX as f64
}; };
($AsioTypeIdent:ident, ($AsioTypeIdent:ident,
$AsioType:ty, $AsioType:ty,
@ -557,7 +582,7 @@ impl EventLoop {
}; };
// Theres only a single callback because theres only one event loop // Theres only a single callback because theres only one event loop
match callbacks.first_mut() { match callbacks.as_mut() {
Some(callback) => { Some(callback) => {
macro_rules! try_callback { macro_rules! try_callback {
($SampleFormat:ident, ($SampleFormat:ident,
@ -675,7 +700,7 @@ impl EventLoop {
Endian::Little, Endian::Little,
convert_endian_from convert_endian_from
); );
}, }
sys::AsioSampleType::ASIOSTInt16LSB => { sys::AsioSampleType::ASIOSTInt16LSB => {
try_callback!( try_callback!(
I16, I16,
@ -689,7 +714,7 @@ impl EventLoop {
Endian::Little, Endian::Little,
convert_endian_from convert_endian_from
); );
}, }
sys::AsioSampleType::ASIOSTInt32MSB => { sys::AsioSampleType::ASIOSTInt32MSB => {
try_callback!( try_callback!(
I16, I16,
@ -703,7 +728,7 @@ impl EventLoop {
Endian::Big, Endian::Big,
convert_endian_from convert_endian_from
); );
}, }
sys::AsioSampleType::ASIOSTInt16MSB => { sys::AsioSampleType::ASIOSTInt16MSB => {
try_callback!( try_callback!(
I16, I16,
@ -717,7 +742,7 @@ impl EventLoop {
Endian::Big, Endian::Big,
convert_endian_from convert_endian_from
); );
}, }
sys::AsioSampleType::ASIOSTFloat32LSB => { sys::AsioSampleType::ASIOSTFloat32LSB => {
try_callback!( try_callback!(
F32, F32,
@ -731,7 +756,7 @@ impl EventLoop {
Endian::Little, Endian::Little,
|a, _| a |a, _| a
); );
}, }
sys::AsioSampleType::ASIOSTFloat64LSB => { sys::AsioSampleType::ASIOSTFloat64LSB => {
try_callback!( try_callback!(
F32, F32,
@ -745,7 +770,7 @@ impl EventLoop {
Endian::Little, Endian::Little,
|a, _| a |a, _| a
); );
}, }
sys::AsioSampleType::ASIOSTFloat32MSB => { sys::AsioSampleType::ASIOSTFloat32MSB => {
try_callback!( try_callback!(
F32, F32,
@ -759,7 +784,7 @@ impl EventLoop {
Endian::Big, Endian::Big,
|a, _| a |a, _| a
); );
}, }
sys::AsioSampleType::ASIOSTFloat64MSB => { sys::AsioSampleType::ASIOSTFloat64MSB => {
try_callback!( try_callback!(
F32, F32,
@ -773,10 +798,10 @@ impl EventLoop {
Endian::Big, Endian::Big,
|a, _| a |a, _| a
); );
}, }
_ => println!("unsupported format {:?}", stream_type), _ => println!("unsupported format {:?}", stream_type),
} }
}, }
None => return (), None => return (),
} }
} }
@ -842,10 +867,10 @@ impl EventLoop {
F: FnMut(StreamId, StreamData) + Send, F: FnMut(StreamId, StreamData) + Send,
{ {
let callback: &mut (FnMut(StreamId, StreamData) + Send) = &mut callback; 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() .lock()
.unwrap() .unwrap() = Some(unsafe { mem::transmute(callback) });
.push(unsafe { mem::transmute(callback) });
loop { loop {
// A sleep here to prevent the loop being // A sleep here to prevent the loop being
// removed in --release // removed in --release