diff --git a/src/host/asio/device.rs b/src/host/asio/device.rs index b1532258d..30db00687 100644 --- a/src/host/asio/device.rs +++ b/src/host/asio/device.rs @@ -1,4 +1,3 @@ -use std; pub type SupportedInputConfigs = std::vec::IntoIter; pub type SupportedOutputConfigs = std::vec::IntoIter; @@ -15,6 +14,7 @@ use crate::SupportedStreamConfig; use crate::SupportedStreamConfigRange; use crate::SupportedStreamConfigsError; use std::hash::{Hash, Hasher}; +use std::sync::atomic::AtomicI32; use std::sync::Arc; /// A ASIO Device @@ -27,6 +27,7 @@ pub struct Device { // A driver can only have one of each. // They need to be created at the same time. pub asio_streams: Arc>, + pub current_buffer_index: Arc, } /// All available devices. @@ -83,8 +84,8 @@ impl Device { channels, min_sample_rate: rate, max_sample_rate: rate, - buffer_size: f.buffer_size.clone(), - sample_format: f.sample_format.clone(), + buffer_size: f.buffer_size, + sample_format: f.sample_format, }) } } @@ -120,8 +121,8 @@ impl Device { channels, min_sample_rate: rate, max_sample_rate: rate, - buffer_size: f.buffer_size.clone(), - sample_format: f.sample_format.clone(), + buffer_size: f.buffer_size, + sample_format: f.sample_format, }) } } @@ -194,6 +195,7 @@ impl Iterator for Devices { return Some(Device { driver, asio_streams, + current_buffer_index: Arc::new(AtomicI32::new(-1)), }); } Err(_) => continue, diff --git a/src/host/asio/stream.rs b/src/host/asio/stream.rs index a0dfe83a5..f151da258 100644 --- a/src/host/asio/stream.rs +++ b/src/host/asio/stream.rs @@ -6,22 +6,12 @@ use super::parking_lot::Mutex; use super::Device; use crate::{ BackendSpecificError, BufferSize, BuildStreamError, Data, InputCallbackInfo, - OutputCallbackInfo, PauseStreamError, PlayStreamError, SampleFormat, SizedSample, StreamConfig, - StreamError, + OutputCallbackInfo, PauseStreamError, PlayStreamError, SampleFormat, StreamConfig, StreamError, }; -use std; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::time::Duration; -// Used to keep track of whether or not the current asio stream buffer requires -// being silencing before summing audio. -#[derive(Default)] -struct SilenceAsioBuffer { - first: bool, - second: bool, -} - pub struct Stream { playing: Arc, // Ensure the `Driver` does not terminate until the last stream is dropped. @@ -65,7 +55,7 @@ impl Device { return Err(BuildStreamError::StreamConfigNotSupported); } - let num_channels = config.channels.clone(); + let num_channels = config.channels; let buffer_size = self.get_or_create_input_stream(config, sample_format)?; let cpal_num_samples = buffer_size * num_channels as usize; @@ -88,7 +78,7 @@ impl Device { // There is 0% chance of lock contention the host only locks when recreating streams. let stream_lock = asio_streams.lock(); - let ref asio_stream = match stream_lock.input { + let asio_stream = match stream_lock.input { Some(ref asio_stream) => asio_stream, None => return, }; @@ -101,9 +91,10 @@ impl Device { asio_stream: &sys::AsioStream, asio_info: &sys::CallbackInfo, sample_rate: crate::SampleRate, + format: SampleFormat, from_endianness: F, ) where - A: SizedSample, + A: Copy, D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, F: Fn(A) -> A, { @@ -122,7 +113,7 @@ impl Device { // 2. Deliver the interleaved buffer to the callback. let data = interleaved.as_mut_ptr() as *mut (); let len = interleaved.len(); - let data = Data::from_parts(data, len, A::FORMAT); + let data = Data::from_parts(data, len, format); let callback = system_time_to_stream_instant(asio_info.system_time); let delay = frames_to_duration(n_frames, sample_rate); let capture = callback @@ -141,6 +132,7 @@ impl Device { asio_stream, callback_info, config.sample_rate, + SampleFormat::I16, from_le, ); } @@ -151,21 +143,31 @@ impl Device { asio_stream, callback_info, config.sample_rate, + SampleFormat::I16, from_be, ); } - // TODO: Handle endianness conversion for floats? We currently use the `PrimInt` - // trait for the `to_le` and `to_be` methods, but this does not support floats. - (&sys::AsioSampleType::ASIOSTFloat32LSB, SampleFormat::F32) - | (&sys::AsioSampleType::ASIOSTFloat32MSB, SampleFormat::F32) => { - process_input_callback::( + (&sys::AsioSampleType::ASIOSTFloat32LSB, SampleFormat::F32) => { + process_input_callback::( + &mut data_callback, + &mut interleaved, + asio_stream, + callback_info, + config.sample_rate, + SampleFormat::F32, + from_le, + ); + } + (&sys::AsioSampleType::ASIOSTFloat32MSB, SampleFormat::F32) => { + process_input_callback::( &mut data_callback, &mut interleaved, asio_stream, callback_info, config.sample_rate, - std::convert::identity::, + SampleFormat::F32, + from_be, ); } @@ -176,6 +178,7 @@ impl Device { asio_stream, callback_info, config.sample_rate, + SampleFormat::I32, from_le, ); } @@ -186,21 +189,31 @@ impl Device { asio_stream, callback_info, config.sample_rate, + SampleFormat::I32, from_be, ); } - // TODO: Handle endianness conversion for floats? We currently use the `PrimInt` - // trait for the `to_le` and `to_be` methods, but this does not support floats. - (&sys::AsioSampleType::ASIOSTFloat64LSB, SampleFormat::F64) - | (&sys::AsioSampleType::ASIOSTFloat64MSB, SampleFormat::F64) => { - process_input_callback::( + (&sys::AsioSampleType::ASIOSTFloat64LSB, SampleFormat::F64) => { + process_input_callback::( &mut data_callback, &mut interleaved, asio_stream, callback_info, config.sample_rate, - std::convert::identity::, + SampleFormat::F64, + from_le, + ); + } + (&sys::AsioSampleType::ASIOSTFloat64MSB, SampleFormat::F64) => { + process_input_callback::( + &mut data_callback, + &mut interleaved, + asio_stream, + callback_info, + config.sample_rate, + SampleFormat::F64, + from_be, ); } @@ -247,14 +260,14 @@ impl Device { return Err(BuildStreamError::StreamConfigNotSupported); } - let num_channels = config.channels.clone(); + let num_channels = config.channels; let buffer_size = self.get_or_create_output_stream(config, sample_format)?; let cpal_num_samples = buffer_size * num_channels as usize; // Create buffers depending on data type. let len_bytes = cpal_num_samples * sample_format.sample_size(); let mut interleaved = vec![0u8; len_bytes]; - let mut silence_asio_buffer = SilenceAsioBuffer::default(); + let current_buffer_index = self.current_buffer_index.clone(); let stream_playing = Arc::new(AtomicBool::new(false)); let playing = Arc::clone(&stream_playing); @@ -268,9 +281,9 @@ impl Device { } // There is 0% chance of lock contention the host only locks when recreating streams. - let stream_lock = asio_streams.lock(); - let ref asio_stream = match stream_lock.output { - Some(ref asio_stream) => asio_stream, + let mut stream_lock = asio_streams.lock(); + let asio_stream = match stream_lock.output { + Some(ref mut asio_stream) => asio_stream, None => return, }; @@ -278,23 +291,12 @@ impl Device { // // This checks if any other callbacks have already silenced the buffer associated with // the current `buffer_index`. - // - // If not, we will silence it and set the opposite buffer half to unsilenced. - let silence = match callback_info.buffer_index { - 0 if !silence_asio_buffer.first => { - silence_asio_buffer.first = true; - silence_asio_buffer.second = false; - true - } - 0 => false, - 1 if !silence_asio_buffer.second => { - silence_asio_buffer.second = true; - silence_asio_buffer.first = false; - true - } - 1 => false, - _ => unreachable!("ASIO uses a double-buffer so there should only be 2"), - }; + let silence = + current_buffer_index.load(Ordering::Acquire) != callback_info.buffer_index; + + if silence { + current_buffer_index.store(callback_info.buffer_index, Ordering::Release); + } /// 1. Render the given callback to the given buffer of interleaved samples. /// 2. If required, silence the ASIO buffer. @@ -304,20 +306,21 @@ impl Device { data_callback: &mut D, interleaved: &mut [u8], silence_asio_buffer: bool, - asio_stream: &sys::AsioStream, + asio_stream: &mut sys::AsioStream, asio_info: &sys::CallbackInfo, sample_rate: crate::SampleRate, - to_endianness: F, + format: SampleFormat, + mix_samples: F, ) where - A: SizedSample + std::ops::Add, + A: Copy, D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, - F: Fn(A) -> A, + F: Fn(A, A) -> A, { // 1. Render interleaved buffer from callback. let interleaved: &mut [A] = cast_slice_mut(interleaved); let data = interleaved.as_mut_ptr() as *mut (); let len = interleaved.len(); - let mut data = Data::from_parts(data, len, A::FORMAT); + let mut data = Data::from_parts(data, len, format); let callback = system_time_to_stream_instant(asio_info.system_time); let n_frames = asio_stream.buffer_size as usize; let delay = frames_to_duration(n_frames, sample_rate); @@ -335,9 +338,7 @@ impl Device { for ch_ix in 0..n_channels { let asio_channel = asio_channel_slice_mut::(asio_stream, buffer_index, ch_ix); - asio_channel - .iter_mut() - .for_each(|s| *s = to_endianness(A::EQUILIBRIUM)); + asio_channel.align_to_mut::().1.fill(0); } } @@ -346,7 +347,7 @@ impl Device { let asio_channel = asio_channel_slice_mut::(asio_stream, buffer_index, ch_ix); for (frame, s_asio) in interleaved.chunks(n_channels).zip(asio_channel) { - *s_asio = *s_asio + to_endianness(A::from_sample(frame[ch_ix])); + *s_asio = mix_samples(*s_asio, frame[ch_ix]); } } } @@ -360,7 +361,10 @@ impl Device { asio_stream, callback_info, config.sample_rate, - to_le, + SampleFormat::I16, + |old_sample, new_sample| { + from_le(old_sample).saturating_add(new_sample).to_le() + }, ); } (SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt16MSB) => { @@ -371,22 +375,43 @@ impl Device { asio_stream, callback_info, config.sample_rate, - to_be, + SampleFormat::I16, + |old_sample, new_sample| { + from_be(old_sample).saturating_add(new_sample).to_be() + }, + ); + } + (SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat32LSB) => { + process_output_callback::( + &mut data_callback, + &mut interleaved, + silence, + asio_stream, + callback_info, + config.sample_rate, + SampleFormat::F32, + |old_sample, new_sample| { + (f32::from_bits(from_le(old_sample)) + f32::from_bits(new_sample)) + .to_bits() + .to_le() + }, ); } - // TODO: Handle endianness conversion for floats? We currently use the `PrimInt` - // trait for the `to_le` and `to_be` methods, but this does not support floats. - (SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat32LSB) - | (SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat32MSB) => { - process_output_callback::( + (SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat32MSB) => { + process_output_callback::( &mut data_callback, &mut interleaved, silence, asio_stream, callback_info, config.sample_rate, - std::convert::identity::, + SampleFormat::F32, + |old_sample, new_sample| { + (f32::from_bits(from_be(old_sample)) + f32::from_bits(new_sample)) + .to_bits() + .to_be() + }, ); } @@ -398,7 +423,10 @@ impl Device { asio_stream, callback_info, config.sample_rate, - to_le, + SampleFormat::I32, + |old_sample, new_sample| { + from_le(old_sample).saturating_add(new_sample).to_le() + }, ); } (SampleFormat::I32, &sys::AsioSampleType::ASIOSTInt32MSB) => { @@ -409,22 +437,44 @@ impl Device { asio_stream, callback_info, config.sample_rate, - to_be, + SampleFormat::I32, + |old_sample, new_sample| { + from_be(old_sample).saturating_add(new_sample).to_be() + }, ); } - // TODO: Handle endianness conversion for floats? We currently use the `PrimInt` - // trait for the `to_le` and `to_be` methods, but this does not support floats. - (SampleFormat::F64, &sys::AsioSampleType::ASIOSTFloat64LSB) - | (SampleFormat::F64, &sys::AsioSampleType::ASIOSTFloat64MSB) => { - process_output_callback::( + (SampleFormat::F64, &sys::AsioSampleType::ASIOSTFloat64LSB) => { + process_output_callback::( &mut data_callback, &mut interleaved, silence, asio_stream, callback_info, config.sample_rate, - std::convert::identity::, + SampleFormat::F64, + |old_sample, new_sample| { + (f64::from_bits(from_le(old_sample)) + f64::from_bits(new_sample)) + .to_bits() + .to_le() + }, + ); + } + + (SampleFormat::F64, &sys::AsioSampleType::ASIOSTFloat64MSB) => { + process_output_callback::( + &mut data_callback, + &mut interleaved, + silence, + asio_stream, + callback_info, + config.sample_rate, + SampleFormat::F64, + |old_sample, new_sample| { + (f64::from_bits(from_be(old_sample)) + f64::from_bits(new_sample)) + .to_bits() + .to_be() + }, ); } @@ -468,7 +518,7 @@ impl Device { Err(_) => Err(BuildStreamError::StreamConfigNotSupported), }?; let num_channels = config.channels as usize; - let ref mut streams = *self.asio_streams.lock(); + let mut streams = self.asio_streams.lock(); let buffer_size = match config.buffer_size { BufferSize::Fixed(v) => Some(v as i32), @@ -515,7 +565,7 @@ impl Device { Err(_) => Err(BuildStreamError::StreamConfigNotSupported), }?; let num_channels = config.channels as usize; - let ref mut streams = *self.asio_streams.lock(); + let mut streams = self.asio_streams.lock(); let buffer_size = match config.buffer_size { BufferSize::Fixed(v) => Some(v as i32), @@ -623,16 +673,6 @@ unsafe fn cast_slice_mut(v: &mut [u8]) -> &mut [T] { std::slice::from_raw_parts_mut(v.as_mut_ptr() as *mut T, v.len() / std::mem::size_of::()) } -/// Helper function to convert to little endianness. -fn to_le(t: T) -> T { - t.to_le() -} - -/// Helper function to convert to big endianness. -fn to_be(t: T) -> T { - t.to_be() -} - /// Helper function to convert from little endianness. fn from_le(t: T) -> T { T::from_le(t) @@ -644,23 +684,19 @@ fn from_be(t: T) -> T { } /// Shorthand for retrieving the asio buffer slice associated with a channel. -/// -/// Safety: it's up to the user to ensure that this function is not called multiple times for the -/// same channel. unsafe fn asio_channel_slice( asio_stream: &sys::AsioStream, buffer_index: usize, channel_index: usize, ) -> &[T] { - asio_channel_slice_mut(asio_stream, buffer_index, channel_index) + let buff_ptr: *const T = + asio_stream.buffer_infos[channel_index].buffers[buffer_index as usize] as *const _; + std::slice::from_raw_parts(buff_ptr, asio_stream.buffer_size as usize) } /// Shorthand for retrieving the asio buffer slice associated with a channel. -/// -/// Safety: it's up to the user to ensure that this function is not called multiple times for the -/// same channel. unsafe fn asio_channel_slice_mut( - asio_stream: &sys::AsioStream, + asio_stream: &mut sys::AsioStream, buffer_index: usize, channel_index: usize, ) -> &mut [T] {