asio: Re-add possibility to create stream individually
This commit is contained in:
parent
bfda575218
commit
972cce0f8c
|
@ -24,6 +24,7 @@ ringbuf = "0.1.6"
|
|||
[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"] }
|
||||
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]
|
||||
alsa-sys = { version = "0.1", path = "alsa-sys" }
|
||||
|
|
|
@ -3,7 +3,7 @@ pub type SupportedInputFormats = std::vec::IntoIter<SupportedFormat>;
|
|||
pub type SupportedOutputFormats = std::vec::IntoIter<SupportedFormat>;
|
||||
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::sync::{Mutex, Arc};
|
||||
use std::sync::{Arc};
|
||||
use BackendSpecificError;
|
||||
use DefaultFormatError;
|
||||
use DeviceNameError;
|
||||
|
@ -14,6 +14,7 @@ use SampleRate;
|
|||
use SupportedFormat;
|
||||
use SupportedFormatsError;
|
||||
use super::sys;
|
||||
use super::parking_lot::Mutex;
|
||||
|
||||
/// A ASIO Device
|
||||
pub struct Device {
|
||||
|
@ -23,7 +24,7 @@ pub struct Device {
|
|||
// Input and/or Output stream.
|
||||
// An driver can only have one of each.
|
||||
// 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.
|
||||
|
@ -154,7 +155,10 @@ impl Iterator for Devices {
|
|||
Some(name) => match self.asio.load_driver(&name) {
|
||||
Ok(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 });
|
||||
}
|
||||
Err(_) => continue,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
extern crate asio_sys as sys;
|
||||
extern crate parking_lot;
|
||||
|
||||
use {
|
||||
BuildStreamError,
|
||||
|
|
|
@ -78,8 +78,8 @@ impl Device {
|
|||
}
|
||||
|
||||
let num_channels = format.channels.clone();
|
||||
let asio_stream = self.get_or_create_input_stream(format)?;
|
||||
let cpal_num_samples = asio_stream.buffer_size as usize * num_channels as usize;
|
||||
let buffer_size = self.get_or_create_input_stream(format)?;
|
||||
let cpal_num_samples = buffer_size * num_channels as usize;
|
||||
|
||||
// Create the buffer depending on the size of the data type.
|
||||
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 playing = Arc::clone(&stream_playing);
|
||||
let asio_streams = self.asio_streams.clone();
|
||||
|
||||
// Set the input callback.
|
||||
// This is most performance critical part of the ASIO bindings.
|
||||
|
@ -96,6 +97,13 @@ impl Device {
|
|||
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.
|
||||
/// 2. Deliver the CPAL buffer to the user callback.
|
||||
unsafe fn process_input_callback<A, B, D, F, G>(
|
||||
|
@ -134,7 +142,7 @@ impl Device {
|
|||
process_input_callback::<i16, i16, _, _, _>(
|
||||
&mut data_callback,
|
||||
&mut interleaved,
|
||||
&asio_stream,
|
||||
asio_stream,
|
||||
buffer_index as usize,
|
||||
from_le,
|
||||
std::convert::identity::<i16>,
|
||||
|
@ -144,7 +152,7 @@ impl Device {
|
|||
process_input_callback::<i16, i16, _, _, _>(
|
||||
&mut data_callback,
|
||||
&mut interleaved,
|
||||
&asio_stream,
|
||||
asio_stream,
|
||||
buffer_index as usize,
|
||||
from_be,
|
||||
std::convert::identity::<i16>,
|
||||
|
@ -158,7 +166,7 @@ impl Device {
|
|||
process_input_callback::<f32, f32, _, _, _>(
|
||||
&mut data_callback,
|
||||
&mut interleaved,
|
||||
&asio_stream,
|
||||
asio_stream,
|
||||
buffer_index as usize,
|
||||
std::convert::identity::<f32>,
|
||||
std::convert::identity::<f32>,
|
||||
|
@ -172,7 +180,7 @@ impl Device {
|
|||
process_input_callback::<i32, i16, _, _, _>(
|
||||
&mut data_callback,
|
||||
&mut interleaved,
|
||||
&asio_stream,
|
||||
asio_stream,
|
||||
buffer_index as usize,
|
||||
from_le,
|
||||
|s| (s >> 16) as i16,
|
||||
|
@ -182,7 +190,7 @@ impl Device {
|
|||
process_input_callback::<i32, i16, _, _, _>(
|
||||
&mut data_callback,
|
||||
&mut interleaved,
|
||||
&asio_stream,
|
||||
asio_stream,
|
||||
buffer_index as usize,
|
||||
from_be,
|
||||
|s| (s >> 16) as i16,
|
||||
|
@ -195,7 +203,7 @@ impl Device {
|
|||
process_input_callback::<f64, f32, _, _, _>(
|
||||
&mut data_callback,
|
||||
&mut interleaved,
|
||||
&asio_stream,
|
||||
asio_stream,
|
||||
buffer_index as usize,
|
||||
std::convert::identity::<f64>,
|
||||
|s| s as f32,
|
||||
|
@ -235,8 +243,8 @@ impl Device {
|
|||
}
|
||||
|
||||
let num_channels = format.channels.clone();
|
||||
let asio_stream = self.get_or_create_output_stream(format)?;
|
||||
let cpal_num_samples = asio_stream.buffer_size as usize * num_channels as usize;
|
||||
let buffer_size = self.get_or_create_output_stream(format)?;
|
||||
let cpal_num_samples = buffer_size * num_channels as usize;
|
||||
|
||||
// Create buffers depending on data type.
|
||||
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 playing = Arc::clone(&stream_playing);
|
||||
let asio_streams = self.asio_streams.clone();
|
||||
|
||||
self.driver.set_callback(move |buffer_index| unsafe {
|
||||
// If not playing, return early.
|
||||
|
@ -252,6 +261,13 @@ impl Device {
|
|||
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.
|
||||
//
|
||||
// This checks if any other callbacks have already silenced the buffer associated with
|
||||
|
@ -325,7 +341,7 @@ impl Device {
|
|||
&mut data_callback,
|
||||
&mut interleaved,
|
||||
silence,
|
||||
&asio_stream,
|
||||
asio_stream,
|
||||
buffer_index as usize,
|
||||
std::convert::identity::<i16>,
|
||||
to_le,
|
||||
|
@ -336,7 +352,7 @@ impl Device {
|
|||
&mut data_callback,
|
||||
&mut interleaved,
|
||||
silence,
|
||||
&asio_stream,
|
||||
asio_stream,
|
||||
buffer_index as usize,
|
||||
std::convert::identity::<i16>,
|
||||
to_be,
|
||||
|
@ -351,7 +367,7 @@ impl Device {
|
|||
&mut data_callback,
|
||||
&mut interleaved,
|
||||
silence,
|
||||
&asio_stream,
|
||||
asio_stream,
|
||||
buffer_index as usize,
|
||||
std::convert::identity::<f32>,
|
||||
std::convert::identity::<f32>,
|
||||
|
@ -366,7 +382,7 @@ impl Device {
|
|||
&mut data_callback,
|
||||
&mut interleaved,
|
||||
silence,
|
||||
&asio_stream,
|
||||
asio_stream,
|
||||
buffer_index as usize,
|
||||
|s| (s as i32) << 16,
|
||||
to_le,
|
||||
|
@ -377,7 +393,7 @@ impl Device {
|
|||
&mut data_callback,
|
||||
&mut interleaved,
|
||||
silence,
|
||||
&asio_stream,
|
||||
asio_stream,
|
||||
buffer_index as usize,
|
||||
|s| (s as i32) << 16,
|
||||
to_be,
|
||||
|
@ -391,7 +407,7 @@ impl Device {
|
|||
&mut data_callback,
|
||||
&mut interleaved,
|
||||
silence,
|
||||
&asio_stream,
|
||||
asio_stream,
|
||||
buffer_index as usize,
|
||||
|s| s as f64,
|
||||
std::convert::identity::<f64>,
|
||||
|
@ -419,7 +435,7 @@ impl Device {
|
|||
fn get_or_create_input_stream(
|
||||
&self,
|
||||
format: &Format,
|
||||
) -> Result<sys::AsioStream, BuildStreamError> {
|
||||
) -> Result<usize, BuildStreamError> {
|
||||
match self.default_input_format() {
|
||||
Ok(f) => {
|
||||
let num_asio_channels = f.channels;
|
||||
|
@ -428,27 +444,26 @@ impl Device {
|
|||
Err(_) => Err(BuildStreamError::FormatNotSupported),
|
||||
}?;
|
||||
let num_channels = format.channels as usize;
|
||||
let ref mut streams = *self.asio_streams.lock().unwrap();
|
||||
match streams {
|
||||
Some(streams) => match streams.input.take() {
|
||||
Some(input) => Ok(input),
|
||||
let ref mut streams = *self.asio_streams.lock();
|
||||
// Either create a stream if thers none or had back the
|
||||
// size of the current one.
|
||||
match streams.input {
|
||||
Some(ref input) => Ok(input.buffer_size as usize),
|
||||
None => {
|
||||
println!("ASIO streams have been already created");
|
||||
Err(BuildStreamError::DeviceNotAvailable)
|
||||
}
|
||||
},
|
||||
None => {
|
||||
match self.driver.prepare_input_stream(None, num_channels) {
|
||||
Ok(mut new_streams) => {
|
||||
let input = new_streams.input.take().expect("missing input stream");
|
||||
*streams = Some(new_streams);
|
||||
Ok(input)
|
||||
}
|
||||
Err(e) => {
|
||||
let output = streams.output.take();
|
||||
self.driver
|
||||
.prepare_input_stream(output, num_channels)
|
||||
.map(|new_streams| {
|
||||
let bs = match new_streams.input {
|
||||
Some(ref inp) => inp.buffer_size as usize,
|
||||
None => unreachable!(),
|
||||
};
|
||||
*streams = new_streams;
|
||||
bs
|
||||
}).map_err(|ref e| {
|
||||
println!("Error preparing stream: {}", e);
|
||||
Err(BuildStreamError::DeviceNotAvailable)
|
||||
}
|
||||
}
|
||||
BuildStreamError::DeviceNotAvailable
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -459,7 +474,7 @@ impl Device {
|
|||
fn get_or_create_output_stream(
|
||||
&self,
|
||||
format: &Format,
|
||||
) -> Result<sys::AsioStream, BuildStreamError> {
|
||||
) -> Result<usize, BuildStreamError> {
|
||||
match self.default_output_format() {
|
||||
Ok(f) => {
|
||||
let num_asio_channels = f.channels;
|
||||
|
@ -468,27 +483,26 @@ impl Device {
|
|||
Err(_) => Err(BuildStreamError::FormatNotSupported),
|
||||
}?;
|
||||
let num_channels = format.channels as usize;
|
||||
let ref mut streams = *self.asio_streams.lock().unwrap();
|
||||
match streams {
|
||||
Some(streams) => match streams.output.take() {
|
||||
Some(output) => Ok(output),
|
||||
let ref mut streams = *self.asio_streams.lock();
|
||||
// Either create a stream if thers none or had back the
|
||||
// size of the current one.
|
||||
match streams.output {
|
||||
Some(ref output) => Ok(output.buffer_size as usize),
|
||||
None => {
|
||||
println!("ASIO streams have been already created");
|
||||
Err(BuildStreamError::DeviceNotAvailable)
|
||||
}
|
||||
},
|
||||
None => {
|
||||
match self.driver.prepare_output_stream(None, num_channels) {
|
||||
Ok(mut new_streams) => {
|
||||
let output = new_streams.output.take().expect("missing output stream");
|
||||
*streams = Some(new_streams);
|
||||
Ok(output)
|
||||
}
|
||||
Err(e) => {
|
||||
let output = streams.output.take();
|
||||
self.driver
|
||||
.prepare_output_stream(output, num_channels)
|
||||
.map(|new_streams| {
|
||||
let bs = match new_streams.output {
|
||||
Some(ref out) => out.buffer_size as usize,
|
||||
None => unreachable!(),
|
||||
};
|
||||
*streams = new_streams;
|
||||
bs
|
||||
}).map_err(|ref e| {
|
||||
println!("Error preparing stream: {}", e);
|
||||
Err(BuildStreamError::DeviceNotAvailable)
|
||||
}
|
||||
}
|
||||
BuildStreamError::DeviceNotAvailable
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue