From 056630cca37ee3e2a0a076e50b06bcb3c419311b Mon Sep 17 00:00:00 2001 From: Tom Gowan Date: Fri, 12 Oct 2018 23:38:55 +1100 Subject: [PATCH] removed allocation from output --- .../windows/asio/asio_utils/src/lib.rs | 47 +----- src/platform/windows/asio/stream.rs | 158 ++++++++++-------- 2 files changed, 91 insertions(+), 114 deletions(-) diff --git a/src/platform/windows/asio/asio_utils/src/lib.rs b/src/platform/windows/asio/asio_utils/src/lib.rs index 765ce6f..f9f78fe 100644 --- a/src/platform/windows/asio/asio_utils/src/lib.rs +++ b/src/platform/windows/asio/asio_utils/src/lib.rs @@ -1,7 +1,3 @@ -use std::cell::RefCell; -use std::iter::Cloned; -use std::slice::{Iter, IterMut}; - #[cfg(test)] mod tests; @@ -22,27 +18,6 @@ where let frames = channels[0].len(); target.extend((0 .. frames).flat_map(|f| channels.iter().map(move |ch| ch[f]))); } -/* -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 @@ -63,24 +38,4 @@ where for &sample in cpal_buffer.iter() { asio_channels[ch.next().unwrap()].push(sample); } -} -/* -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, - } - } -} -*/ +} \ No newline at end of file diff --git a/src/platform/windows/asio/stream.rs b/src/platform/windows/asio/stream.rs index fa1cd9c..1f90bd1 100644 --- a/src/platform/windows/asio/stream.rs +++ b/src/platform/windows/asio/stream.rs @@ -31,6 +31,27 @@ pub struct OutputBuffer<'a, T: 'a> { buffer: &'a mut [T], } +#[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, +} + impl EventLoop { pub fn new() -> EventLoop { EventLoop { @@ -64,26 +85,6 @@ impl EventLoop { 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 => { @@ -140,22 +141,6 @@ impl EventLoop { $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 @@ -247,6 +232,10 @@ pub fn build_output_stream( 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) => { + 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); } @@ -255,15 +244,50 @@ pub fn build_output_stream( 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 + // Create buffers + let channel_len = cpal_num_samples + / num_channels as usize; + + + let mut re_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 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 @@ -274,13 +298,18 @@ pub fn build_output_stream( $SampleType:ty, $SampleTypeIdent:ident, $AsioType:ty, - $AsioTypeIdent:ident) => { + $AsioTypeIdent:ident, + $Buffers:expr, + $BuffersType:ty, + $BuffersTypeIdent:ident + ) => { + let mut my_buffers = $Buffers; // Buffer that is filled by cpal. - let mut cpal_buffer: Vec<$SampleType> = vec![0 as $SampleType; cpal_num_samples]; + //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 + buffer: &mut my_buffers.cpal }; callback( StreamId(count), @@ -292,30 +321,19 @@ pub fn build_output_stream( } ); } - // 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[..]); + { + let $BuffersTypeIdent { + cpal: ref mut c_buffer, + channel: ref mut channels, + } = my_buffers; + au::deinterleave(&c_buffer[..], 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(){ + for (i, channel) in my_buffers.channel.iter().enumerate(){ let buff_ptr = (asio_stream .buffer_infos[i] .buffers[index as usize] as *mut $AsioType) @@ -325,7 +343,7 @@ pub fn build_output_stream( buff_ptr, asio_stream.buffer_size as usize); for (asio_s, cpal_s) in asio_buffer.iter_mut() - .zip(&channel){ + .zip(channel){ *asio_s = (*cpal_s as i64 * ::std::$AsioTypeIdent::MAX as i64 / ::std::$SampleTypeIdent::MAX as i64) as $AsioType; @@ -338,16 +356,20 @@ pub fn build_output_stream( // TODO check for endianess match stream_type { sys::AsioSampleType::ASIOSTInt32LSB => { - try_callback!(I16, i16, i16, i32, i32); + try_callback!(I16, i16, i16, i32, i32, + &mut re_buffers.i16_buff, I16Buffer, I16Buffer); } sys::AsioSampleType::ASIOSTInt16LSB => { - try_callback!(I16, i16, i16, i16, i16); + try_callback!(I16, i16, i16, i16, i16, + &mut re_buffers.i16_buff, I16Buffer, I16Buffer); } sys::AsioSampleType::ASIOSTFloat32LSB => { - try_callback!(F32, f32, f32, f32, f32); + try_callback!(F32, f32, f32, f32, f32, + &mut re_buffers.f32_buff, F32Buffer, F32Buffer); } sys::AsioSampleType::ASIOSTFloat64LSB => { - try_callback!(F32, f32, f32, f64, f64); + try_callback!(F32, f32, f32, f64, f64, + &mut re_buffers.f32_buff, F32Buffer, F32Buffer); } _ => println!("unsupported format {:?}", stream_type), }