asio: Re-add possibility to create stream individually

This commit is contained in:
msiglreith 2019-10-08 16:22:06 +02:00 committed by mitchmindtree
parent bfda575218
commit 972cce0f8c
4 changed files with 79 additions and 59 deletions

View File

@ -24,6 +24,7 @@ ringbuf = "0.1.6"
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3", features = ["audiosessiontypes", "audioclient", "coml2api", "combaseapi", "debug", "devpkey", "handleapi", "ksmedia", "mmdeviceapi", "objbase", "std", "synchapi", "winbase", "winuser"] } winapi = { version = "0.3", features = ["audiosessiontypes", "audioclient", "coml2api", "combaseapi", "debug", "devpkey", "handleapi", "ksmedia", "mmdeviceapi", "objbase", "std", "synchapi", "winbase", "winuser"] }
asio-sys = { version = "0.1", path = "asio-sys", optional = true } asio-sys = { version = "0.1", path = "asio-sys", optional = true }
parking_lot = "0.9"
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))'.dependencies] [target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))'.dependencies]
alsa-sys = { version = "0.1", path = "alsa-sys" } alsa-sys = { version = "0.1", path = "alsa-sys" }

View File

@ -3,7 +3,7 @@ pub type SupportedInputFormats = std::vec::IntoIter<SupportedFormat>;
pub type SupportedOutputFormats = std::vec::IntoIter<SupportedFormat>; pub type SupportedOutputFormats = std::vec::IntoIter<SupportedFormat>;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::sync::{Mutex, Arc}; use std::sync::{Arc};
use BackendSpecificError; use BackendSpecificError;
use DefaultFormatError; use DefaultFormatError;
use DeviceNameError; use DeviceNameError;
@ -14,6 +14,7 @@ use SampleRate;
use SupportedFormat; use SupportedFormat;
use SupportedFormatsError; use SupportedFormatsError;
use super::sys; use super::sys;
use super::parking_lot::Mutex;
/// A ASIO Device /// A ASIO Device
pub struct Device { pub struct Device {
@ -23,7 +24,7 @@ pub struct Device {
// Input and/or Output stream. // Input and/or Output stream.
// An driver can only have one of each. // An driver can only have one of each.
// They need to be created at the same time. // They need to be created at the same time.
pub asio_streams: Arc<Mutex<Option<sys::AsioStreams>>>, pub asio_streams: Arc<Mutex<sys::AsioStreams>>,
} }
/// All available devices. /// All available devices.
@ -154,7 +155,10 @@ impl Iterator for Devices {
Some(name) => match self.asio.load_driver(&name) { Some(name) => match self.asio.load_driver(&name) {
Ok(driver) => { Ok(driver) => {
let driver = Arc::new(driver); let driver = Arc::new(driver);
let asio_streams = Arc::new(Mutex::new(None)); let asio_streams = Arc::new(Mutex::new(sys::AsioStreams {
input: None,
output: None,
}));
return Some(Device { driver, asio_streams }); return Some(Device { driver, asio_streams });
} }
Err(_) => continue, Err(_) => continue,

View File

@ -1,4 +1,5 @@
extern crate asio_sys as sys; extern crate asio_sys as sys;
extern crate parking_lot;
use { use {
BuildStreamError, BuildStreamError,

View File

@ -78,8 +78,8 @@ impl Device {
} }
let num_channels = format.channels.clone(); let num_channels = format.channels.clone();
let asio_stream = self.get_or_create_input_stream(format)?; let buffer_size = self.get_or_create_input_stream(format)?;
let cpal_num_samples = asio_stream.buffer_size as usize * num_channels as usize; let cpal_num_samples = buffer_size * num_channels as usize;
// Create the buffer depending on the size of the data type. // Create the buffer depending on the size of the data type.
let len_bytes = cpal_num_samples * data_type.sample_size(); let len_bytes = cpal_num_samples * data_type.sample_size();
@ -87,6 +87,7 @@ impl Device {
let stream_playing = Arc::new(AtomicBool::new(false)); let stream_playing = Arc::new(AtomicBool::new(false));
let playing = Arc::clone(&stream_playing); let playing = Arc::clone(&stream_playing);
let asio_streams = self.asio_streams.clone();
// Set the input callback. // Set the input callback.
// This is most performance critical part of the ASIO bindings. // This is most performance critical part of the ASIO bindings.
@ -96,6 +97,13 @@ impl Device {
return return
} }
// 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 {
Some(ref asio_stream) => asio_stream,
None => return,
};
/// 1. Write from the ASIO buffer to the interleaved CPAL buffer. /// 1. Write from the ASIO buffer to the interleaved CPAL buffer.
/// 2. Deliver the CPAL buffer to the user callback. /// 2. Deliver the CPAL buffer to the user callback.
unsafe fn process_input_callback<A, B, D, F, G>( unsafe fn process_input_callback<A, B, D, F, G>(
@ -134,7 +142,7 @@ impl Device {
process_input_callback::<i16, i16, _, _, _>( process_input_callback::<i16, i16, _, _, _>(
&mut data_callback, &mut data_callback,
&mut interleaved, &mut interleaved,
&asio_stream, asio_stream,
buffer_index as usize, buffer_index as usize,
from_le, from_le,
std::convert::identity::<i16>, std::convert::identity::<i16>,
@ -144,7 +152,7 @@ impl Device {
process_input_callback::<i16, i16, _, _, _>( process_input_callback::<i16, i16, _, _, _>(
&mut data_callback, &mut data_callback,
&mut interleaved, &mut interleaved,
&asio_stream, asio_stream,
buffer_index as usize, buffer_index as usize,
from_be, from_be,
std::convert::identity::<i16>, std::convert::identity::<i16>,
@ -158,7 +166,7 @@ impl Device {
process_input_callback::<f32, f32, _, _, _>( process_input_callback::<f32, f32, _, _, _>(
&mut data_callback, &mut data_callback,
&mut interleaved, &mut interleaved,
&asio_stream, asio_stream,
buffer_index as usize, buffer_index as usize,
std::convert::identity::<f32>, std::convert::identity::<f32>,
std::convert::identity::<f32>, std::convert::identity::<f32>,
@ -172,7 +180,7 @@ impl Device {
process_input_callback::<i32, i16, _, _, _>( process_input_callback::<i32, i16, _, _, _>(
&mut data_callback, &mut data_callback,
&mut interleaved, &mut interleaved,
&asio_stream, asio_stream,
buffer_index as usize, buffer_index as usize,
from_le, from_le,
|s| (s >> 16) as i16, |s| (s >> 16) as i16,
@ -182,7 +190,7 @@ impl Device {
process_input_callback::<i32, i16, _, _, _>( process_input_callback::<i32, i16, _, _, _>(
&mut data_callback, &mut data_callback,
&mut interleaved, &mut interleaved,
&asio_stream, asio_stream,
buffer_index as usize, buffer_index as usize,
from_be, from_be,
|s| (s >> 16) as i16, |s| (s >> 16) as i16,
@ -195,7 +203,7 @@ impl Device {
process_input_callback::<f64, f32, _, _, _>( process_input_callback::<f64, f32, _, _, _>(
&mut data_callback, &mut data_callback,
&mut interleaved, &mut interleaved,
&asio_stream, asio_stream,
buffer_index as usize, buffer_index as usize,
std::convert::identity::<f64>, std::convert::identity::<f64>,
|s| s as f32, |s| s as f32,
@ -235,8 +243,8 @@ impl Device {
} }
let num_channels = format.channels.clone(); let num_channels = format.channels.clone();
let asio_stream = self.get_or_create_output_stream(format)?; let buffer_size = self.get_or_create_output_stream(format)?;
let cpal_num_samples = asio_stream.buffer_size as usize * num_channels as usize; let cpal_num_samples = buffer_size * num_channels as usize;
// Create buffers depending on data type. // Create buffers depending on data type.
let len_bytes = cpal_num_samples * data_type.sample_size(); let len_bytes = cpal_num_samples * data_type.sample_size();
@ -245,6 +253,7 @@ impl Device {
let stream_playing = Arc::new(AtomicBool::new(false)); let stream_playing = Arc::new(AtomicBool::new(false));
let playing = Arc::clone(&stream_playing); let playing = Arc::clone(&stream_playing);
let asio_streams = self.asio_streams.clone();
self.driver.set_callback(move |buffer_index| unsafe { self.driver.set_callback(move |buffer_index| unsafe {
// If not playing, return early. // If not playing, return early.
@ -252,6 +261,13 @@ impl Device {
return return
} }
// 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,
None => return,
};
// Silence the ASIO buffer that is about to be used. // Silence the ASIO buffer that is about to be used.
// //
// This checks if any other callbacks have already silenced the buffer associated with // This checks if any other callbacks have already silenced the buffer associated with
@ -325,7 +341,7 @@ impl Device {
&mut data_callback, &mut data_callback,
&mut interleaved, &mut interleaved,
silence, silence,
&asio_stream, asio_stream,
buffer_index as usize, buffer_index as usize,
std::convert::identity::<i16>, std::convert::identity::<i16>,
to_le, to_le,
@ -336,7 +352,7 @@ impl Device {
&mut data_callback, &mut data_callback,
&mut interleaved, &mut interleaved,
silence, silence,
&asio_stream, asio_stream,
buffer_index as usize, buffer_index as usize,
std::convert::identity::<i16>, std::convert::identity::<i16>,
to_be, to_be,
@ -351,7 +367,7 @@ impl Device {
&mut data_callback, &mut data_callback,
&mut interleaved, &mut interleaved,
silence, silence,
&asio_stream, asio_stream,
buffer_index as usize, buffer_index as usize,
std::convert::identity::<f32>, std::convert::identity::<f32>,
std::convert::identity::<f32>, std::convert::identity::<f32>,
@ -366,7 +382,7 @@ impl Device {
&mut data_callback, &mut data_callback,
&mut interleaved, &mut interleaved,
silence, silence,
&asio_stream, asio_stream,
buffer_index as usize, buffer_index as usize,
|s| (s as i32) << 16, |s| (s as i32) << 16,
to_le, to_le,
@ -377,7 +393,7 @@ impl Device {
&mut data_callback, &mut data_callback,
&mut interleaved, &mut interleaved,
silence, silence,
&asio_stream, asio_stream,
buffer_index as usize, buffer_index as usize,
|s| (s as i32) << 16, |s| (s as i32) << 16,
to_be, to_be,
@ -391,7 +407,7 @@ impl Device {
&mut data_callback, &mut data_callback,
&mut interleaved, &mut interleaved,
silence, silence,
&asio_stream, asio_stream,
buffer_index as usize, buffer_index as usize,
|s| s as f64, |s| s as f64,
std::convert::identity::<f64>, std::convert::identity::<f64>,
@ -419,7 +435,7 @@ impl Device {
fn get_or_create_input_stream( fn get_or_create_input_stream(
&self, &self,
format: &Format, format: &Format,
) -> Result<sys::AsioStream, BuildStreamError> { ) -> Result<usize, BuildStreamError> {
match self.default_input_format() { match self.default_input_format() {
Ok(f) => { Ok(f) => {
let num_asio_channels = f.channels; let num_asio_channels = f.channels;
@ -428,27 +444,26 @@ impl Device {
Err(_) => Err(BuildStreamError::FormatNotSupported), Err(_) => Err(BuildStreamError::FormatNotSupported),
}?; }?;
let num_channels = format.channels as usize; let num_channels = format.channels as usize;
let ref mut streams = *self.asio_streams.lock().unwrap(); let ref mut streams = *self.asio_streams.lock();
match streams { // Either create a stream if thers none or had back the
Some(streams) => match streams.input.take() { // size of the current one.
Some(input) => Ok(input), match streams.input {
None => { Some(ref input) => Ok(input.buffer_size as usize),
println!("ASIO streams have been already created");
Err(BuildStreamError::DeviceNotAvailable)
}
},
None => { None => {
match self.driver.prepare_input_stream(None, num_channels) { let output = streams.output.take();
Ok(mut new_streams) => { self.driver
let input = new_streams.input.take().expect("missing input stream"); .prepare_input_stream(output, num_channels)
*streams = Some(new_streams); .map(|new_streams| {
Ok(input) let bs = match new_streams.input {
} Some(ref inp) => inp.buffer_size as usize,
Err(e) => { None => unreachable!(),
};
*streams = new_streams;
bs
}).map_err(|ref e| {
println!("Error preparing stream: {}", e); println!("Error preparing stream: {}", e);
Err(BuildStreamError::DeviceNotAvailable) BuildStreamError::DeviceNotAvailable
} })
}
} }
} }
} }
@ -459,7 +474,7 @@ impl Device {
fn get_or_create_output_stream( fn get_or_create_output_stream(
&self, &self,
format: &Format, format: &Format,
) -> Result<sys::AsioStream, BuildStreamError> { ) -> Result<usize, BuildStreamError> {
match self.default_output_format() { match self.default_output_format() {
Ok(f) => { Ok(f) => {
let num_asio_channels = f.channels; let num_asio_channels = f.channels;
@ -468,27 +483,26 @@ impl Device {
Err(_) => Err(BuildStreamError::FormatNotSupported), Err(_) => Err(BuildStreamError::FormatNotSupported),
}?; }?;
let num_channels = format.channels as usize; let num_channels = format.channels as usize;
let ref mut streams = *self.asio_streams.lock().unwrap(); let ref mut streams = *self.asio_streams.lock();
match streams { // Either create a stream if thers none or had back the
Some(streams) => match streams.output.take() { // size of the current one.
Some(output) => Ok(output), match streams.output {
None => { Some(ref output) => Ok(output.buffer_size as usize),
println!("ASIO streams have been already created");
Err(BuildStreamError::DeviceNotAvailable)
}
},
None => { None => {
match self.driver.prepare_output_stream(None, num_channels) { let output = streams.output.take();
Ok(mut new_streams) => { self.driver
let output = new_streams.output.take().expect("missing output stream"); .prepare_output_stream(output, num_channels)
*streams = Some(new_streams); .map(|new_streams| {
Ok(output) let bs = match new_streams.output {
} Some(ref out) => out.buffer_size as usize,
Err(e) => { None => unreachable!(),
};
*streams = new_streams;
bs
}).map_err(|ref e| {
println!("Error preparing stream: {}", e); println!("Error preparing stream: {}", e);
Err(BuildStreamError::DeviceNotAvailable) BuildStreamError::DeviceNotAvailable
} })
}
} }
} }
} }