From d430000d2c8743f8c851b8f2ec33d07f3a2d8978 Mon Sep 17 00:00:00 2001 From: Tom Gowan Date: Fri, 12 Oct 2018 16:54:26 +1100 Subject: [PATCH] beep working --- Cargo.toml | 2 +- .../windows/asio/asio_utils/Cargo.toml | 6 + .../windows/asio/asio_utils/src/lib.rs | 60 +++ .../windows/asio/asio_utils/src/tests.rs | 89 ++++ src/platform/windows/asio/stream.rs | 413 ++++++++++++++++++ 5 files changed, 569 insertions(+), 1 deletion(-) create mode 100644 src/platform/windows/asio/asio_utils/Cargo.toml create mode 100644 src/platform/windows/asio/asio_utils/src/lib.rs create mode 100644 src/platform/windows/asio/asio_utils/src/tests.rs create mode 100644 src/platform/windows/asio/stream.rs diff --git a/Cargo.toml b/Cargo.toml index f0c977c..e863431 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ hound = "3.4" [target.'cfg(any(target_os = "windows" ))'.dependencies] asio-sys = { version = "0.1", path = "asio-sys" } -asio-utils = { version = "0.1", path = "srs/platform/windows/asio/asio_utils" } +asio_utils = { version = "0.1", path = "src/platform/windows/asio/asio_utils" } [target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3", features = ["audiosessiontypes", "audioclient", "coml2api", "combaseapi", "debug", "devpkey", "handleapi", "ksmedia", "mmdeviceapi", "objbase", "std", "synchapi", "winuser"] } diff --git a/src/platform/windows/asio/asio_utils/Cargo.toml b/src/platform/windows/asio/asio_utils/Cargo.toml new file mode 100644 index 0000000..e949bad --- /dev/null +++ b/src/platform/windows/asio/asio_utils/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "asio_utils" +version = "0.1.0" +authors = ["Tom Gowan "] + +[dependencies] diff --git a/src/platform/windows/asio/asio_utils/src/lib.rs b/src/platform/windows/asio/asio_utils/src/lib.rs new file mode 100644 index 0000000..050079f --- /dev/null +++ b/src/platform/windows/asio/asio_utils/src/lib.rs @@ -0,0 +1,60 @@ +use std::cell::RefCell; +use std::iter::Cloned; +use std::slice::{Iter, IterMut}; + +#[cfg(test)] +mod tests; + +/// Interleave the buffer from asio to cpal +/// asio: LLLLRRRR +/// cpal: LRLRLRLR +/// More then stereo: +/// asio: 111122223333 +/// cpal: 123123123123 +/// cpal buffer must have a length of exactly sum( all asio channel lengths ) +/// this check is ommited for performance +pub fn interleave(channel_buffer: &[Vec], cpal_buffer: &mut [T]) +where + T: std::marker::Copy, +{ + // TODO avoid this heap allocation + // But we don't know how many channels we need. + // Could use arrayvec if we make an upper limit + let channels: Vec>>> = channel_buffer + .iter() + .map(|c| RefCell::new(c.iter().cloned())) + .collect(); + + for (c_buff, channel) in cpal_buffer.iter_mut().zip(channels.iter().cycle()) { + match channel.borrow_mut().next() { + Some(c) => *c_buff = c, + None => break, + } + } +} + +/// Function for deinterleaving because +/// cpal writes to buffer interleaved +/// cpal: LRLRLRLR +/// asio: LLLLRRRR +/// More then stereo: +/// cpal: 123123123123 +/// asio: 111122223333 +pub fn deinterleave(cpal_buffer: &[T], asio_channels: &mut [Vec]) +where + T: std::marker::Copy, +{ + // TODO avoid this heap allocation + // Possibly use arrayvec and some max channels + let channels: Vec>> = asio_channels + .iter_mut() + .map(|c| RefCell::new(c.iter_mut())) + .collect(); + + for (c_buff, a_channel) in cpal_buffer.iter().zip(channels.iter().cycle()) { + match a_channel.borrow_mut().next() { + Some(c) => *c = *c_buff, + None => break, + } + } +} diff --git a/src/platform/windows/asio/asio_utils/src/tests.rs b/src/platform/windows/asio/asio_utils/src/tests.rs new file mode 100644 index 0000000..c0d9ffd --- /dev/null +++ b/src/platform/windows/asio/asio_utils/src/tests.rs @@ -0,0 +1,89 @@ +use super::{deinterleave, deinterleave_index, interleave}; + +#[test] +fn interleave_two() { + let a = vec![vec![1, 1, 1, 1], vec![2, 2, 2, 2]]; + let goal = vec![1, 2, 1, 2, 1, 2, 1, 2]; + let mut result = vec![0; 8]; + + interleave(&a[..], &mut result[..]); + + assert_eq!(goal, result); +} + +#[test] +fn interleave_three() { + let a = vec![vec![1, 1, 1, 1], vec![2, 2, 2, 2], vec![3, 3, 3, 3]]; + 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[..]); + + assert_eq!(goal, result); +} + +#[test] +fn interleave_none() { + let a = vec![Vec::::new()]; + let goal = Vec::::new(); + let mut result = Vec::::new(); + + interleave(&a[..], &mut result[..]); + + assert_eq!(goal, result); +} + +#[test] +fn interleave_two_diff() { + let a = vec![vec![1, 2, 3, 4], vec![5, 6, 7, 8]]; + let goal = vec![1, 5, 2, 6, 3, 7, 4, 8]; + let mut result = vec![0; 8]; + + interleave(&a[..], &mut result[..]); + + assert_eq!(goal, result); +} + +#[test] +fn deinterleave_two() { + let goal = vec![vec![1, 1, 1, 1], vec![2, 2, 2, 2]]; + let a = vec![1, 2, 1, 2, 1, 2, 1, 2]; + let mut result = vec![vec![0; 4]; 2]; + + deinterleave(&a[..], &mut result[..]); + + assert_eq!(goal, result); +} + +#[test] +fn deinterleave_three() { + let goal = vec![vec![1, 1, 1, 1], vec![2, 2, 2, 2], vec![3, 3, 3, 3]]; + let a = vec![1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]; + let mut result = vec![vec![0; 4]; 3]; + + deinterleave(&a[..], &mut result[..]); + + assert_eq!(goal, result); +} + +#[test] +fn deinterleave_none() { + let goal = vec![Vec::::new()]; + let a = Vec::::new(); + let mut result = vec![Vec::::new()]; + + deinterleave(&a[..], &mut result[..]); + + assert_eq!(goal, result); +} + +#[test] +fn deinterleave_two_diff() { + let goal = vec![vec![1, 2, 3, 4], vec![5, 6, 7, 8]]; + let a = vec![1, 5, 2, 6, 3, 7, 4, 8]; + let mut result = vec![vec![0; 4]; 2]; + + deinterleave(&a[..], &mut result[..]); + + assert_eq!(goal, result); +} diff --git a/src/platform/windows/asio/stream.rs b/src/platform/windows/asio/stream.rs new file mode 100644 index 0000000..5f8023e --- /dev/null +++ b/src/platform/windows/asio/stream.rs @@ -0,0 +1,413 @@ +extern crate asio_sys as sys; +extern crate asio_utils as au; + +use std; +use Format; +use CreationError; +use StreamData; +use std::marker::PhantomData; +use super::Device; +use std::cell::Cell; +use UnknownTypeOutputBuffer; +use UnknownTypeInputBuffer; +use std::sync::{Arc, Mutex}; +use std::mem; +use std::sync::atomic::{AtomicUsize, Ordering}; +use SampleFormat; + +pub struct EventLoop { + asio_stream: Arc>>, + stream_count: Arc, + callbacks: Arc>>, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct StreamId(usize); + +pub struct InputBuffer<'a, T: 'a> { + buffer: &'a [T], +} +pub struct OutputBuffer<'a, T: 'a> { + buffer: &'a mut [T], +} + +impl EventLoop { + pub fn new() -> EventLoop { + EventLoop { + asio_stream: Arc::new(Mutex::new(None)), + stream_count: Arc::new(AtomicUsize::new(0)), + callbacks: Arc::new(Mutex::new(Vec::new())), + } + } + + pub fn build_input_stream( + &self, + device: &Device, + format: &Format, + ) -> Result { + let stream_type = sys::get_data_type(&device.driver_name).expect("Couldn't load data type"); + match sys::prepare_input_stream(&device.driver_name) { + Ok(stream) => { + let num_channels = format.channels.clone(); + let cpal_num_samples = + (stream.buffer_size as usize) * num_channels as usize; + { + *self.asio_stream.lock().unwrap() = Some(stream); + } + let count = self.stream_count.load(Ordering::SeqCst); + self.stream_count.store(count + 1, Ordering::SeqCst); + let asio_stream = self.asio_stream.clone(); + let callbacks = self.callbacks.clone(); + let bytes_per_channel = format.data_type.sample_size(); + + // Create buffers + let channel_len = cpal_num_samples + / num_channels as usize; + + #[derive(Default)] + struct I16Buffer{ + cpal: Vec, + channel: Vec>, + } + #[derive(Default)] + struct U16Buffer{ + cpal: Vec, + channel: Vec>, + } + #[derive(Default)] + struct F32Buffer{ + cpal: Vec, + channel: Vec>, + } + struct Buffers { + i16_buff: I16Buffer, + u16_buff: U16Buffer, + f32_buff: F32Buffer, + } + + let mut buffers = match format.data_type{ + SampleFormat::I16 => { + Buffers{ + i16_buff: I16Buffer{ + cpal: vec![0 as i16; cpal_num_samples], + channel: (0..num_channels) + .map(|_| Vec::with_capacity(channel_len)) + .collect()}, + u16_buff: U16Buffer::default(), + f32_buff: F32Buffer::default(), + } + } + SampleFormat::U16 => { + Buffers{ + i16_buff: I16Buffer::default(), + u16_buff: U16Buffer{ + cpal: vec![0 as u16; cpal_num_samples], + channel: (0..num_channels) + .map(|_| Vec::with_capacity(channel_len)) + .collect()}, + f32_buff: F32Buffer::default(), + } + } + SampleFormat::F32 => { + Buffers{ + i16_buff: I16Buffer::default(), + u16_buff: U16Buffer::default(), + f32_buff: F32Buffer{ + cpal: vec![0 as f32; cpal_num_samples], + channel: (0..num_channels) + .map(|_| Vec::with_capacity(channel_len)) + .collect()}, + } + } + }; + + sys::set_callback(move |index| unsafe { + if let Some(ref asio_stream) = *asio_stream.lock().unwrap() { + // Number of samples needed total + let mut callbacks = callbacks.lock().unwrap(); + + // Assuming only one callback, probably needs to change + match callbacks.first_mut() { + Some(callback) => { + macro_rules! try_callback { + ($SampleFormat:ident, + $SampleType:ty, + $SampleTypeIdent:ident, + $AsioType:ty, + $AsioTypeIdent:ident, + $Buffers:expr, + $BuffersType:ty, + $BuffersTypeIdent:ident + ) => { + + // Function for interleaving because + // cpal writes to buffer interleaved + /* + let $BuffersTypeIdent { + cpal: ref mut buffer, + channel: ref channels, + } = *buffers; + let length = channels[0].len(); + for i in 0..length{ + for channel in channels{ + buffer.push(channel[i]); + } + } + } + */ + + // For each channel write the cpal data to + // the asio buffer + // Also need to check for Endian + + for (i, channel) in $Buffers.channel.iter_mut().enumerate(){ + let buff_ptr = asio_stream + .buffer_infos[i] + .buffers[index as usize] as *mut $AsioType; + //.offset(asio_stream.buffer_size as isize * i as isize); + let asio_buffer: &'static [$AsioType] = + std::slice::from_raw_parts( + buff_ptr, + asio_stream.buffer_size as usize); + for asio_s in asio_buffer.iter(){ + channel.push( (*asio_s as i64 * + ::std::$SampleTypeIdent::MAX as i64 / + ::std::$AsioTypeIdent::MAX as i64) as $SampleType); + } + } + + + // interleave all the channels + { + let $BuffersTypeIdent { + cpal: ref mut c_buffer, + channel: ref channels, + } = $Buffers; + au::interleave(&channels, c_buffer); + } + + + let buff = InputBuffer{ + buffer: &mut $Buffers.cpal, + }; + callback( + StreamId(count), + StreamData::Input{ + buffer: UnknownTypeInputBuffer::$SampleFormat( + ::InputBuffer{ + buffer: Some(super::super::InputBuffer::Asio(buff)) + }) + } + ); + } + }; + // Generic over types + // TODO check for endianess + match stream_type { + sys::AsioSampleType::ASIOSTInt32LSB => { + try_callback!(I16, i16, i16, i32, i32, + buffers.i16_buff, I16Buffer, I16Buffer); + } + sys::AsioSampleType::ASIOSTInt16LSB => { + try_callback!(I16, i16, i16, i16, i16, + buffers.i16_buff, I16Buffer, I16Buffer); + } + sys::AsioSampleType::ASIOSTFloat32LSB => { + try_callback!(F32, f32, f32, f32, f32, + buffers.f32_buff, F32Buffer, F32Buffer); + } + sys::AsioSampleType::ASIOSTFloat64LSB => { + try_callback!(F32, f32, f32, f64, f64, + buffers.f32_buff, F32Buffer, F32Buffer); + } + _ => println!("unsupported format {:?}", stream_type), + } + } + None => return (), + } + } + }); + Ok(StreamId(count)) + } + Err(ref e) => { + println!("Error preparing stream: {}", e); + Err(CreationError::DeviceNotAvailable) + } + } + } + +pub fn build_output_stream( + &self, + device: &Device, + format: &Format, + ) -> Result { + let stream_type = sys::get_data_type(&device.driver_name).expect("Couldn't load data type"); + match sys::prepare_stream(&device.driver_name) { + Ok(stream) => { + { + *self.asio_stream.lock().unwrap() = Some(stream); + } + let count = self.stream_count.load(Ordering::SeqCst); + self.stream_count.store(count + 1, Ordering::SeqCst); + let asio_stream = self.asio_stream.clone(); + let callbacks = self.callbacks.clone(); + let bytes_per_channel = format.data_type.sample_size(); + let num_channels = format.channels.clone(); + + // Get stream types + + sys::set_callback(move |index| unsafe { + if let Some(ref asio_stream) = *asio_stream.lock().unwrap() { + // Number of samples needed total + let cpal_num_samples = + (asio_stream.buffer_size as usize) * num_channels as usize; + let mut callbacks = callbacks.lock().unwrap(); + + // Assuming only one callback, probably needs to change + match callbacks.first_mut() { + Some(callback) => { + macro_rules! try_callback { + ($SampleFormat:ident, + $SampleType:ty, + $SampleTypeIdent:ident, + $AsioType:ty, + $AsioTypeIdent:ident) => { + // Buffer that is filled by cpal. + let mut cpal_buffer: Vec<$SampleType> = vec![0 as $SampleType; cpal_num_samples]; + // Call in block because of mut borrow + { + let buff = OutputBuffer{ + buffer: &mut cpal_buffer + }; + callback( + StreamId(count), + StreamData::Output{ + buffer: UnknownTypeOutputBuffer::$SampleFormat( + ::OutputBuffer{ + target: Some(super::super::OutputBuffer::Asio(buff)) + }) + } + ); + } + // Function for deinterleaving because + // cpal writes to buffer interleaved + /* + fn deinterleave(data_slice: &mut [$SampleType], + num_channels: usize, + channels: &mut [Vec<$SampleType>]) { + for (i, &sample) in data_slice.iter().enumerate() { + let ch = i % num_channels; + channels[ch].push(sample); + } + } + */ + // Deinter all the channels + let channel_len = cpal_buffer.len() + / num_channels as usize; + let mut deinter_channels: Vec<_> = (0..num_channels) + .map(|_| vec![0.0 as $SampleType; channel_len]) + .collect(); + au::deinterleave(&cpal_buffer[..], &mut deinter_channels[..]); + + // For each channel write the cpal data to + // the asio buffer + // Also need to check for Endian + for (i, channel) in deinter_channels.into_iter().enumerate(){ + let buff_ptr = (asio_stream + .buffer_infos[i] + .buffers[index as usize] as *mut $AsioType) + .offset(asio_stream.buffer_size as isize * i as isize); + let asio_buffer: &'static mut [$AsioType] = + std::slice::from_raw_parts_mut( + buff_ptr, + asio_stream.buffer_size as usize); + for (asio_s, cpal_s) in asio_buffer.iter_mut() + .zip(&channel){ + *asio_s = (*cpal_s as i64 * + ::std::$AsioTypeIdent::MAX as i64 / + ::std::$SampleTypeIdent::MAX as i64) as $AsioType; + } + + } + }; + } + // Generic over types + // TODO check for endianess + match stream_type { + sys::AsioSampleType::ASIOSTInt32LSB => { + try_callback!(I16, i16, i16, i32, i32); + } + sys::AsioSampleType::ASIOSTInt16LSB => { + try_callback!(I16, i16, i16, i16, i16); + } + sys::AsioSampleType::ASIOSTFloat32LSB => { + try_callback!(F32, f32, f32, f32, f32); + } + sys::AsioSampleType::ASIOSTFloat64LSB => { + try_callback!(F32, f32, f32, f64, f64); + } + _ => println!("unsupported format {:?}", stream_type), + } + } + None => return (), + } + } + }); + Ok(StreamId(count)) + } + Err(ref e) => { + println!("Error preparing stream: {}", e); + Err(CreationError::DeviceNotAvailable) + } + } +} + +pub fn play_stream(&self, stream: StreamId) { + sys::play(); +} + +pub fn pause_stream(&self, stream: StreamId) { + sys::stop(); +} +pub fn destroy_stream(&self, stream_id: StreamId) { + let mut asio_stream_lock = self.asio_stream.lock().unwrap(); + let old_stream = mem::replace(&mut *asio_stream_lock, None); + if let Some(old_stream) = old_stream { + sys::destroy_stream(old_stream); + } +} +pub fn run(&self, mut callback: F) -> ! +where +F: FnMut(StreamId, StreamData) + Send, +{ + let callback: &mut (FnMut(StreamId, StreamData) + Send) = &mut callback; + self.callbacks + .lock() + .unwrap() + .push(unsafe { mem::transmute(callback) }); + loop { + // Might need a sleep here to prevent the loop being + // removed in --release + } +} +} + +impl<'a, T> InputBuffer<'a, T> { + pub fn buffer(&self) -> &[T] { + &self.buffer + } + pub fn finish(self) { + } +} + +impl<'a, T> OutputBuffer<'a, T> { + pub fn buffer(&mut self) -> &mut [T] { + &mut self.buffer + } + + pub fn len(&self) -> usize { + self.buffer.len() + } + + pub fn finish(self) {} +}