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 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<AsioBufferInfo>,
&self, mut buffer_infos: Vec<AsioBufferInfo>,
) -> Result<(Vec<AsioBufferInfo>, 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<AsioBufferInfo>, Vec<ai::ASIOBufferInfo>>(buffer_infos);
let mut callbacks_convert =
mem::transmute::<AsioCallbacks, ai::ASIOCallbacks>(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<ai::ASIOBufferInfo>,
Vec<AsioBufferInfo>,
>(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 {

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")]
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;
#[derive(Clone)]
@ -6,22 +10,23 @@ pub enum BackEnd {
Asio,
}
//static BACKEND: BackEnd = BackEnd::Asio;
lazy_static! {
static ref BACK_END: Mutex<BackEnd> = 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(())

View File

@ -15,6 +15,10 @@ pub fn interleave<T>(channels: &[Vec<T>], target: &mut Vec<T>)
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<T>(cpal_buffer: &[T], asio_channels: &mut [Vec<T>])
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();
}

View File

@ -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::<i32>::new();
let mut result = Vec::<i32>::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);
}

View File

@ -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<Mutex<Vec<&'static mut (FnMut(StreamId, StreamData) + Send)>>>,
callbacks: Arc<Mutex<Option<&'static mut (FnMut(StreamId, StreamData) + Send)>>>,
}
/// 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<usize, CreationError> {
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<usize, CreationError> {
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<StreamId, CreationError> {
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<StreamId, CreationError> {
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