Port ASIO host to new stream-based API
This commit is contained in:
parent
64ba84de81
commit
bfda575218
|
@ -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::Arc;
|
use std::sync::{Mutex, Arc};
|
||||||
use BackendSpecificError;
|
use BackendSpecificError;
|
||||||
use DefaultFormatError;
|
use DefaultFormatError;
|
||||||
use DeviceNameError;
|
use DeviceNameError;
|
||||||
|
@ -16,10 +16,14 @@ use SupportedFormatsError;
|
||||||
use super::sys;
|
use super::sys;
|
||||||
|
|
||||||
/// A ASIO Device
|
/// A ASIO Device
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Device {
|
pub struct Device {
|
||||||
/// The driver represented by this device.
|
/// The driver represented by this device.
|
||||||
pub driver: Arc<sys::Driver>,
|
pub driver: Arc<sys::Driver>,
|
||||||
|
|
||||||
|
// 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>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// All available devices.
|
/// All available devices.
|
||||||
|
@ -148,7 +152,11 @@ impl Iterator for Devices {
|
||||||
loop {
|
loop {
|
||||||
match self.drivers.next() {
|
match self.drivers.next() {
|
||||||
Some(name) => match self.asio.load_driver(&name) {
|
Some(name) => match self.asio.load_driver(&name) {
|
||||||
Ok(driver) => return Some(Device { driver: Arc::new(driver) }),
|
Ok(driver) => {
|
||||||
|
let driver = Arc::new(driver);
|
||||||
|
let asio_streams = Arc::new(Mutex::new(None));
|
||||||
|
return Some(Device { driver, asio_streams });
|
||||||
|
}
|
||||||
Err(_) => continue,
|
Err(_) => continue,
|
||||||
}
|
}
|
||||||
None => return None,
|
None => return None,
|
||||||
|
|
|
@ -8,18 +8,18 @@ use {
|
||||||
Format,
|
Format,
|
||||||
PauseStreamError,
|
PauseStreamError,
|
||||||
PlayStreamError,
|
PlayStreamError,
|
||||||
StreamDataResult,
|
|
||||||
SupportedFormatsError,
|
SupportedFormatsError,
|
||||||
|
StreamData,
|
||||||
|
StreamError,
|
||||||
};
|
};
|
||||||
use traits::{
|
use traits::{
|
||||||
DeviceTrait,
|
DeviceTrait,
|
||||||
EventLoopTrait,
|
|
||||||
HostTrait,
|
HostTrait,
|
||||||
StreamIdTrait,
|
StreamTrait,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use self::device::{Device, Devices, SupportedInputFormats, SupportedOutputFormats};
|
pub use self::device::{Device, Devices, SupportedInputFormats, SupportedOutputFormats};
|
||||||
pub use self::stream::{EventLoop, StreamId};
|
pub use self::stream::Stream;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
mod device;
|
mod device;
|
||||||
|
@ -42,7 +42,6 @@ impl Host {
|
||||||
impl HostTrait for Host {
|
impl HostTrait for Host {
|
||||||
type Devices = Devices;
|
type Devices = Devices;
|
||||||
type Device = Device;
|
type Device = Device;
|
||||||
type EventLoop = EventLoop;
|
|
||||||
|
|
||||||
fn is_available() -> bool {
|
fn is_available() -> bool {
|
||||||
true
|
true
|
||||||
|
@ -62,15 +61,12 @@ impl HostTrait for Host {
|
||||||
// ASIO has no concept of a default device, so just use the first.
|
// ASIO has no concept of a default device, so just use the first.
|
||||||
self.output_devices().ok().and_then(|mut ds| ds.next())
|
self.output_devices().ok().and_then(|mut ds| ds.next())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn event_loop(&self) -> Self::EventLoop {
|
|
||||||
EventLoop::new()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeviceTrait for Device {
|
impl DeviceTrait for Device {
|
||||||
type SupportedInputFormats = SupportedInputFormats;
|
type SupportedInputFormats = SupportedInputFormats;
|
||||||
type SupportedOutputFormats = SupportedOutputFormats;
|
type SupportedOutputFormats = SupportedOutputFormats;
|
||||||
|
type Stream = Stream;
|
||||||
|
|
||||||
fn name(&self) -> Result<String, DeviceNameError> {
|
fn name(&self) -> Result<String, DeviceNameError> {
|
||||||
Device::name(self)
|
Device::name(self)
|
||||||
|
@ -91,46 +87,28 @@ impl DeviceTrait for Device {
|
||||||
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||||
Device::default_output_format(self)
|
Device::default_output_format(self)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl EventLoopTrait for EventLoop {
|
fn build_input_stream<D, E>(&self, format: &Format, data_callback: D, error_callback: E) -> Result<Self::Stream, BuildStreamError>
|
||||||
type Device = Device;
|
|
||||||
type StreamId = StreamId;
|
|
||||||
|
|
||||||
fn build_input_stream(
|
|
||||||
&self,
|
|
||||||
device: &Self::Device,
|
|
||||||
format: &Format,
|
|
||||||
) -> Result<Self::StreamId, BuildStreamError> {
|
|
||||||
EventLoop::build_input_stream(self, device, format)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_output_stream(
|
|
||||||
&self,
|
|
||||||
device: &Self::Device,
|
|
||||||
format: &Format,
|
|
||||||
) -> Result<Self::StreamId, BuildStreamError> {
|
|
||||||
EventLoop::build_output_stream(self, device, format)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn play_stream(&self, stream: Self::StreamId) -> Result<(), PlayStreamError> {
|
|
||||||
EventLoop::play_stream(self, stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pause_stream(&self, stream: Self::StreamId) -> Result<(), PauseStreamError> {
|
|
||||||
EventLoop::pause_stream(self, stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn destroy_stream(&self, stream: Self::StreamId) {
|
|
||||||
EventLoop::destroy_stream(self, stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run<F>(&self, callback: F) -> !
|
|
||||||
where
|
where
|
||||||
F: FnMut(Self::StreamId, StreamDataResult) + Send,
|
D: FnMut(StreamData) + Send + 'static, E: FnMut(StreamError) + Send + 'static
|
||||||
{
|
{
|
||||||
EventLoop::run(self, callback)
|
Device::build_input_stream(self, format, data_callback, error_callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_output_stream<D, E>(&self, format: &Format, data_callback: D, error_callback: E) -> Result<Self::Stream, BuildStreamError>
|
||||||
|
where
|
||||||
|
D: FnMut(StreamData) + Send + 'static, E: FnMut(StreamError) + Send + 'static
|
||||||
|
{
|
||||||
|
Device::build_output_stream(self, format, data_callback, error_callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StreamIdTrait for StreamId {}
|
impl StreamTrait for Stream {
|
||||||
|
fn play(&self) -> Result<(), PlayStreamError> {
|
||||||
|
Stream::play(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pause(&self) -> Result<(), PauseStreamError> {
|
||||||
|
Stream::pause(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,11 +4,8 @@ extern crate num_traits;
|
||||||
use self::num_traits::PrimInt;
|
use self::num_traits::PrimInt;
|
||||||
use super::Device;
|
use super::Device;
|
||||||
use std;
|
use std;
|
||||||
use std::mem;
|
use std::sync::atomic::{Ordering, AtomicBool};
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::Arc;
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use std::thread;
|
|
||||||
use std::time::Duration;
|
|
||||||
use BackendSpecificError;
|
use BackendSpecificError;
|
||||||
use BuildStreamError;
|
use BuildStreamError;
|
||||||
use Format;
|
use Format;
|
||||||
|
@ -16,9 +13,9 @@ use PauseStreamError;
|
||||||
use PlayStreamError;
|
use PlayStreamError;
|
||||||
use SampleFormat;
|
use SampleFormat;
|
||||||
use StreamData;
|
use StreamData;
|
||||||
use StreamDataResult;
|
|
||||||
use UnknownTypeInputBuffer;
|
use UnknownTypeInputBuffer;
|
||||||
use UnknownTypeOutputBuffer;
|
use UnknownTypeOutputBuffer;
|
||||||
|
use StreamError;
|
||||||
|
|
||||||
/// Sample types whose constant silent value is known.
|
/// Sample types whose constant silent value is known.
|
||||||
trait Silence {
|
trait Silence {
|
||||||
|
@ -34,35 +31,6 @@ trait InterleavedSample: Clone + Copy + Silence {
|
||||||
/// Constraints on the ASIO sample types.
|
/// Constraints on the ASIO sample types.
|
||||||
trait AsioSample: Clone + Copy + Silence + std::ops::Add<Self, Output = Self> {}
|
trait AsioSample: Clone + Copy + Silence + std::ops::Add<Self, Output = Self> {}
|
||||||
|
|
||||||
/// Controls all streams
|
|
||||||
pub struct EventLoop {
|
|
||||||
/// The input and output ASIO streams
|
|
||||||
asio_streams: Arc<Mutex<sys::AsioStreams>>,
|
|
||||||
/// List of all CPAL streams
|
|
||||||
cpal_streams: Arc<Mutex<Vec<Option<Stream>>>>,
|
|
||||||
/// Total stream count.
|
|
||||||
stream_count: AtomicUsize,
|
|
||||||
/// The CPAL callback that the user gives to fill the buffers.
|
|
||||||
callbacks: Arc<Mutex<Option<&'static mut (FnMut(StreamId, StreamDataResult) + Send)>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Id for each stream.
|
|
||||||
/// Created depending on the number they are created.
|
|
||||||
/// Starting at one! not zero.
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub struct StreamId(usize);
|
|
||||||
|
|
||||||
/// CPAL stream.
|
|
||||||
/// This decouples the many cpal streams
|
|
||||||
/// from the single input and single output
|
|
||||||
/// ASIO streams.
|
|
||||||
/// Each stream can be playing or paused.
|
|
||||||
struct Stream {
|
|
||||||
playing: bool,
|
|
||||||
// The driver associated with this stream.
|
|
||||||
driver: Arc<sys::Driver>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used to keep track of whether or not the current current asio stream buffer requires
|
// Used to keep track of whether or not the current current asio stream buffer requires
|
||||||
// being silencing before summing audio.
|
// being silencing before summing audio.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -71,114 +39,36 @@ struct SilenceAsioBuffer {
|
||||||
second: bool,
|
second: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventLoop {
|
pub struct Stream {
|
||||||
pub fn new() -> EventLoop {
|
playing: Arc<AtomicBool>,
|
||||||
EventLoop {
|
}
|
||||||
asio_streams: Arc::new(Mutex::new(sys::AsioStreams {
|
|
||||||
input: None,
|
impl Stream {
|
||||||
output: None,
|
pub fn play(&self) -> Result<(), PlayStreamError> {
|
||||||
})),
|
self.playing.store(true, Ordering::SeqCst);
|
||||||
cpal_streams: Arc::new(Mutex::new(Vec::new())),
|
Ok(())
|
||||||
// This is why the Id's count from one not zero
|
|
||||||
// because at this point there is no streams
|
|
||||||
stream_count: AtomicUsize::new(0),
|
|
||||||
callbacks: Arc::new(Mutex::new(None)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new CPAL Input Stream.
|
pub fn pause(&self) -> Result<(), PauseStreamError> {
|
||||||
///
|
self.playing.store(false, Ordering::SeqCst);
|
||||||
/// If there is no existing ASIO Input Stream it will be created.
|
Ok(())
|
||||||
///
|
|
||||||
/// On success, the buffer size of the stream is returned.
|
|
||||||
fn get_or_create_input_stream(
|
|
||||||
&self,
|
|
||||||
driver: &sys::Driver,
|
|
||||||
format: &Format,
|
|
||||||
device: &Device,
|
|
||||||
) -> Result<usize, BuildStreamError> {
|
|
||||||
match device.default_input_format() {
|
|
||||||
Ok(f) => {
|
|
||||||
let num_asio_channels = f.channels;
|
|
||||||
check_format(driver, format, num_asio_channels)
|
|
||||||
},
|
|
||||||
Err(_) => Err(BuildStreamError::FormatNotSupported),
|
|
||||||
}?;
|
|
||||||
let num_channels = format.channels as usize;
|
|
||||||
let ref mut streams = *self.asio_streams.lock().unwrap();
|
|
||||||
// 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 => {
|
|
||||||
let output = streams.output.take();
|
|
||||||
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);
|
|
||||||
BuildStreamError::DeviceNotAvailable
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new CPAL Output Stream.
|
// TODO: drop implementation
|
||||||
///
|
|
||||||
/// If there is no existing ASIO Output Stream it will be created.
|
|
||||||
///
|
|
||||||
/// On success, the buffer size of the stream is returned.
|
|
||||||
fn get_or_create_output_stream(
|
|
||||||
&self,
|
|
||||||
driver: &sys::Driver,
|
|
||||||
format: &Format,
|
|
||||||
device: &Device,
|
|
||||||
) -> Result<usize, BuildStreamError> {
|
|
||||||
match device.default_output_format() {
|
|
||||||
Ok(f) => {
|
|
||||||
let num_asio_channels = f.channels;
|
|
||||||
check_format(driver, format, num_asio_channels)
|
|
||||||
},
|
|
||||||
Err(_) => Err(BuildStreamError::FormatNotSupported),
|
|
||||||
}?;
|
|
||||||
let num_channels = format.channels as usize;
|
|
||||||
let ref mut streams = *self.asio_streams.lock().unwrap();
|
|
||||||
// Either create a stream if there's none or return the size of the current one.
|
|
||||||
match streams.output {
|
|
||||||
Some(ref output) => Ok(output.buffer_size as usize),
|
|
||||||
None => {
|
|
||||||
let input = streams.input.take();
|
|
||||||
driver
|
|
||||||
.prepare_output_stream(input, 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);
|
|
||||||
BuildStreamError::DeviceNotAvailable
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Builds a new cpal input stream
|
impl Device {
|
||||||
pub fn build_input_stream(
|
pub fn build_input_stream<D, E>(
|
||||||
&self,
|
&self,
|
||||||
device: &Device,
|
|
||||||
format: &Format,
|
format: &Format,
|
||||||
) -> Result<StreamId, BuildStreamError> {
|
mut data_callback: D,
|
||||||
let Device { driver, .. } = device;
|
_error_callback: E,
|
||||||
let stream_type = driver.input_data_type().map_err(build_stream_err)?;
|
) -> Result<Stream, BuildStreamError>
|
||||||
|
where
|
||||||
|
D: FnMut(StreamData) + Send + 'static,
|
||||||
|
E: FnMut(StreamError) + Send + 'static
|
||||||
|
{
|
||||||
|
let stream_type = self.driver.input_data_type().map_err(build_stream_err)?;
|
||||||
|
|
||||||
// Ensure that the desired sample type is supported.
|
// Ensure that the desired sample type is supported.
|
||||||
let data_type = super::device::convert_data_type(&stream_type)
|
let data_type = super::device::convert_data_type(&stream_type)
|
||||||
|
@ -188,48 +78,28 @@ impl EventLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
let num_channels = format.channels.clone();
|
let num_channels = format.channels.clone();
|
||||||
let stream_buffer_size = self.get_or_create_input_stream(&driver, format, device)?;
|
let asio_stream = self.get_or_create_input_stream(format)?;
|
||||||
let cpal_num_samples = stream_buffer_size * num_channels as usize;
|
let cpal_num_samples = asio_stream.buffer_size as usize * num_channels as usize;
|
||||||
let count = self.stream_count.fetch_add(1, Ordering::SeqCst);
|
|
||||||
let asio_streams = self.asio_streams.clone();
|
|
||||||
let cpal_streams = self.cpal_streams.clone();
|
|
||||||
let callbacks = self.callbacks.clone();
|
|
||||||
|
|
||||||
// Create the buffer depending on the size of the data type.
|
// Create the buffer depending on the size of the data type.
|
||||||
let stream_id = StreamId(count);
|
|
||||||
let len_bytes = cpal_num_samples * data_type.sample_size();
|
let len_bytes = cpal_num_samples * data_type.sample_size();
|
||||||
let mut interleaved = vec![0u8; len_bytes];
|
let mut interleaved = vec![0u8; len_bytes];
|
||||||
|
|
||||||
|
let stream_playing = Arc::new(AtomicBool::new(false));
|
||||||
|
let playing = Arc::clone(&stream_playing);
|
||||||
|
|
||||||
// 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.
|
||||||
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.
|
||||||
// TODO: Don't assume `count` is valid - we should search for the matching `StreamId`.
|
if !playing.load(Ordering::SeqCst) {
|
||||||
if let Some(s) = cpal_streams.lock().unwrap().get(count) {
|
return
|
||||||
if let Some(s) = s {
|
|
||||||
if !s.playing {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Acquire the stream and callback.
|
|
||||||
let stream_lock = asio_streams.lock().unwrap();
|
|
||||||
let ref asio_stream = match stream_lock.input {
|
|
||||||
Some(ref asio_stream) => asio_stream,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
let mut callbacks = callbacks.lock().unwrap();
|
|
||||||
let callback = match callbacks.as_mut() {
|
|
||||||
Some(callback) => callback,
|
|
||||||
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, F, G>(
|
unsafe fn process_input_callback<A, B, D, F, G>(
|
||||||
stream_id: StreamId,
|
callback: &mut D,
|
||||||
callback: &mut (dyn FnMut(StreamId, StreamDataResult) + Send),
|
|
||||||
interleaved: &mut [u8],
|
interleaved: &mut [u8],
|
||||||
asio_stream: &sys::AsioStream,
|
asio_stream: &sys::AsioStream,
|
||||||
buffer_index: usize,
|
buffer_index: usize,
|
||||||
|
@ -239,6 +109,7 @@ impl EventLoop {
|
||||||
where
|
where
|
||||||
A: AsioSample,
|
A: AsioSample,
|
||||||
B: InterleavedSample,
|
B: InterleavedSample,
|
||||||
|
D: FnMut(StreamData) + Send + 'static,
|
||||||
F: Fn(A) -> A,
|
F: Fn(A) -> A,
|
||||||
G: Fn(A) -> B,
|
G: Fn(A) -> B,
|
||||||
{
|
{
|
||||||
|
@ -254,29 +125,26 @@ impl EventLoop {
|
||||||
|
|
||||||
// 2. Deliver the interleaved buffer to the callback.
|
// 2. Deliver the interleaved buffer to the callback.
|
||||||
callback(
|
callback(
|
||||||
stream_id,
|
StreamData::Input { buffer: B::unknown_type_input_buffer(interleaved) },
|
||||||
Ok(StreamData::Input { buffer: B::unknown_type_input_buffer(interleaved) }),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
match (&stream_type, data_type) {
|
match (&stream_type, data_type) {
|
||||||
(&sys::AsioSampleType::ASIOSTInt16LSB, SampleFormat::I16) => {
|
(&sys::AsioSampleType::ASIOSTInt16LSB, SampleFormat::I16) => {
|
||||||
process_input_callback::<i16, i16, _, _>(
|
process_input_callback::<i16, i16, _, _, _>(
|
||||||
stream_id,
|
&mut data_callback,
|
||||||
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>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
(&sys::AsioSampleType::ASIOSTInt16MSB, SampleFormat::I16) => {
|
(&sys::AsioSampleType::ASIOSTInt16MSB, SampleFormat::I16) => {
|
||||||
process_input_callback::<i16, i16, _, _>(
|
process_input_callback::<i16, i16, _, _, _>(
|
||||||
stream_id,
|
&mut data_callback,
|
||||||
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>,
|
||||||
|
@ -287,11 +155,10 @@ impl EventLoop {
|
||||||
// trait for the `to_le` and `to_be` methods, but this does not support floats.
|
// trait for the `to_le` and `to_be` methods, but this does not support floats.
|
||||||
(&sys::AsioSampleType::ASIOSTFloat32LSB, SampleFormat::F32) |
|
(&sys::AsioSampleType::ASIOSTFloat32LSB, SampleFormat::F32) |
|
||||||
(&sys::AsioSampleType::ASIOSTFloat32MSB, SampleFormat::F32) => {
|
(&sys::AsioSampleType::ASIOSTFloat32MSB, SampleFormat::F32) => {
|
||||||
process_input_callback::<f32, f32, _, _>(
|
process_input_callback::<f32, f32, _, _, _>(
|
||||||
stream_id,
|
&mut data_callback,
|
||||||
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>,
|
||||||
|
@ -302,22 +169,20 @@ impl EventLoop {
|
||||||
// `process_output_callback` function above by removing the unnecessary sample
|
// `process_output_callback` function above by removing the unnecessary sample
|
||||||
// conversion function.
|
// conversion function.
|
||||||
(&sys::AsioSampleType::ASIOSTInt32LSB, SampleFormat::I16) => {
|
(&sys::AsioSampleType::ASIOSTInt32LSB, SampleFormat::I16) => {
|
||||||
process_input_callback::<i32, i16, _, _>(
|
process_input_callback::<i32, i16, _, _, _>(
|
||||||
stream_id,
|
&mut data_callback,
|
||||||
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,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
(&sys::AsioSampleType::ASIOSTInt32MSB, SampleFormat::I16) => {
|
(&sys::AsioSampleType::ASIOSTInt32MSB, SampleFormat::I16) => {
|
||||||
process_input_callback::<i32, i16, _, _>(
|
process_input_callback::<i32, i16, _, _, _>(
|
||||||
stream_id,
|
&mut data_callback,
|
||||||
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,
|
||||||
|
@ -327,11 +192,10 @@ impl EventLoop {
|
||||||
// trait for the `to_le` and `to_be` methods, but this does not support floats.
|
// trait for the `to_le` and `to_be` methods, but this does not support floats.
|
||||||
(&sys::AsioSampleType::ASIOSTFloat64LSB, SampleFormat::F32) |
|
(&sys::AsioSampleType::ASIOSTFloat64LSB, SampleFormat::F32) |
|
||||||
(&sys::AsioSampleType::ASIOSTFloat64MSB, SampleFormat::F32) => {
|
(&sys::AsioSampleType::ASIOSTFloat64MSB, SampleFormat::F32) => {
|
||||||
process_input_callback::<f64, f32, _, _>(
|
process_input_callback::<f64, f32, _, _, _>(
|
||||||
stream_id,
|
&mut data_callback,
|
||||||
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,
|
||||||
|
@ -345,23 +209,23 @@ impl EventLoop {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create stream and set to paused
|
// Immediately start the device?
|
||||||
self.cpal_streams
|
self.driver.start().map_err(build_stream_err)?;
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.push(Some(Stream { driver: driver.clone(), playing: false }));
|
|
||||||
|
|
||||||
Ok(StreamId(count))
|
Ok(Stream { playing: stream_playing })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create the an output cpal stream.
|
pub fn build_output_stream<D, E>(
|
||||||
pub fn build_output_stream(
|
|
||||||
&self,
|
&self,
|
||||||
device: &Device,
|
|
||||||
format: &Format,
|
format: &Format,
|
||||||
) -> Result<StreamId, BuildStreamError> {
|
mut data_callback: D,
|
||||||
let Device { driver, .. } = device;
|
_error_callback: E,
|
||||||
let stream_type = driver.output_data_type().map_err(build_stream_err)?;
|
) -> Result<Stream, BuildStreamError>
|
||||||
|
where
|
||||||
|
D: FnMut(StreamData) + Send + 'static,
|
||||||
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
|
{
|
||||||
|
let stream_type = self.driver.output_data_type().map_err(build_stream_err)?;
|
||||||
|
|
||||||
// Ensure that the desired sample type is supported.
|
// Ensure that the desired sample type is supported.
|
||||||
let data_type = super::device::convert_data_type(&stream_type)
|
let data_type = super::device::convert_data_type(&stream_type)
|
||||||
|
@ -371,38 +235,22 @@ impl EventLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
let num_channels = format.channels.clone();
|
let num_channels = format.channels.clone();
|
||||||
let stream_buffer_size = self.get_or_create_output_stream(&driver, format, device)?;
|
let asio_stream = self.get_or_create_output_stream(format)?;
|
||||||
let cpal_num_samples = stream_buffer_size * num_channels as usize;
|
let cpal_num_samples = asio_stream.buffer_size as usize * num_channels as usize;
|
||||||
let count = self.stream_count.fetch_add(1, Ordering::SeqCst);
|
|
||||||
let asio_streams = self.asio_streams.clone();
|
|
||||||
let cpal_streams = self.cpal_streams.clone();
|
|
||||||
let callbacks = self.callbacks.clone();
|
|
||||||
|
|
||||||
// Create buffers depending on data type.
|
// Create buffers depending on data type.
|
||||||
let stream_id = StreamId(count);
|
|
||||||
let len_bytes = cpal_num_samples * data_type.sample_size();
|
let len_bytes = cpal_num_samples * data_type.sample_size();
|
||||||
let mut interleaved = vec![0u8; len_bytes];
|
let mut interleaved = vec![0u8; len_bytes];
|
||||||
let mut silence_asio_buffer = SilenceAsioBuffer::default();
|
let mut silence_asio_buffer = SilenceAsioBuffer::default();
|
||||||
|
|
||||||
driver.set_callback(move |buffer_index| unsafe {
|
let stream_playing = Arc::new(AtomicBool::new(false));
|
||||||
// If not playing, return early.
|
let playing = Arc::clone(&stream_playing);
|
||||||
// TODO: Don't assume `count` is valid - we should search for the matching `StreamId`.
|
|
||||||
if let Some(s) = cpal_streams.lock().unwrap().get(count) {
|
|
||||||
if let Some(s) = s {
|
|
||||||
if !s.playing {
|
|
||||||
return ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Acquire the stream and callback.
|
self.driver.set_callback(move |buffer_index| unsafe {
|
||||||
let stream_lock = asio_streams.lock().unwrap();
|
// If not playing, return early.
|
||||||
let ref asio_stream = match stream_lock.output {
|
if !playing.load(Ordering::SeqCst) {
|
||||||
Some(ref asio_stream) => asio_stream,
|
return
|
||||||
None => return,
|
}
|
||||||
};
|
|
||||||
let mut callbacks = callbacks.lock().unwrap();
|
|
||||||
let callback = callbacks.as_mut();
|
|
||||||
|
|
||||||
// Silence the ASIO buffer that is about to be used.
|
// Silence the ASIO buffer that is about to be used.
|
||||||
//
|
//
|
||||||
|
@ -430,9 +278,8 @@ impl EventLoop {
|
||||||
/// 2. If required, silence the ASIO buffer.
|
/// 2. If required, silence the ASIO buffer.
|
||||||
/// 3. Finally, write the interleaved data to the non-interleaved ASIO buffer,
|
/// 3. Finally, write the interleaved data to the non-interleaved ASIO buffer,
|
||||||
/// performing endianness conversions as necessary.
|
/// performing endianness conversions as necessary.
|
||||||
unsafe fn process_output_callback<A, B, F, G>(
|
unsafe fn process_output_callback<A, B, D, F, G>(
|
||||||
stream_id: StreamId,
|
callback: &mut D,
|
||||||
callback: Option<&mut &mut (dyn FnMut(StreamId, StreamDataResult) + Send)>,
|
|
||||||
interleaved: &mut [u8],
|
interleaved: &mut [u8],
|
||||||
silence_asio_buffer: bool,
|
silence_asio_buffer: bool,
|
||||||
asio_stream: &sys::AsioStream,
|
asio_stream: &sys::AsioStream,
|
||||||
|
@ -443,18 +290,14 @@ impl EventLoop {
|
||||||
where
|
where
|
||||||
A: InterleavedSample,
|
A: InterleavedSample,
|
||||||
B: AsioSample,
|
B: AsioSample,
|
||||||
|
D: FnMut(StreamData) + Send + 'static,
|
||||||
F: Fn(A) -> B,
|
F: Fn(A) -> B,
|
||||||
G: Fn(B) -> B,
|
G: Fn(B) -> B,
|
||||||
{
|
{
|
||||||
// 1. Render interleaved buffer from callback.
|
// 1. Render interleaved buffer from callback.
|
||||||
let interleaved: &mut [A] = cast_slice_mut(interleaved);
|
let interleaved: &mut [A] = cast_slice_mut(interleaved);
|
||||||
match callback {
|
|
||||||
None => interleaved.iter_mut().for_each(|s| *s = A::SILENCE),
|
|
||||||
Some(callback) => {
|
|
||||||
let buffer = A::unknown_type_output_buffer(interleaved);
|
let buffer = A::unknown_type_output_buffer(interleaved);
|
||||||
callback(stream_id, Ok(StreamData::Output { buffer }));
|
callback(StreamData::Output { buffer });
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Silence ASIO channels if necessary.
|
// 2. Silence ASIO channels if necessary.
|
||||||
let n_channels = interleaved.len() / asio_stream.buffer_size as usize;
|
let n_channels = interleaved.len() / asio_stream.buffer_size as usize;
|
||||||
|
@ -478,24 +321,22 @@ impl EventLoop {
|
||||||
|
|
||||||
match (data_type, &stream_type) {
|
match (data_type, &stream_type) {
|
||||||
(SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt16LSB) => {
|
(SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt16LSB) => {
|
||||||
process_output_callback::<i16, i16, _, _>(
|
process_output_callback::<i16, i16, _, _, _>(
|
||||||
stream_id,
|
&mut data_callback,
|
||||||
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,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
(SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt16MSB) => {
|
(SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt16MSB) => {
|
||||||
process_output_callback::<i16, i16, _, _>(
|
process_output_callback::<i16, i16, _, _, _>(
|
||||||
stream_id,
|
&mut data_callback,
|
||||||
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,
|
||||||
|
@ -506,12 +347,11 @@ impl EventLoop {
|
||||||
// trait for the `to_le` and `to_be` methods, but this does not support floats.
|
// trait for the `to_le` and `to_be` methods, but this does not support floats.
|
||||||
(SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat32LSB) |
|
(SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat32LSB) |
|
||||||
(SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat32MSB) => {
|
(SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat32MSB) => {
|
||||||
process_output_callback::<f32, f32, _, _>(
|
process_output_callback::<f32, f32, _, _, _>(
|
||||||
stream_id,
|
&mut data_callback,
|
||||||
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>,
|
||||||
|
@ -522,24 +362,22 @@ impl EventLoop {
|
||||||
// `process_output_callback` function above by removing the unnecessary sample
|
// `process_output_callback` function above by removing the unnecessary sample
|
||||||
// conversion function.
|
// conversion function.
|
||||||
(SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt32LSB) => {
|
(SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt32LSB) => {
|
||||||
process_output_callback::<i16, i32, _, _>(
|
process_output_callback::<i16, i32, _, _, _>(
|
||||||
stream_id,
|
&mut data_callback,
|
||||||
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,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
(SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt32MSB) => {
|
(SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt32MSB) => {
|
||||||
process_output_callback::<i16, i32, _, _>(
|
process_output_callback::<i16, i32, _, _, _>(
|
||||||
stream_id,
|
&mut data_callback,
|
||||||
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,
|
||||||
|
@ -549,12 +387,11 @@ impl EventLoop {
|
||||||
// trait for the `to_le` and `to_be` methods, but this does not support floats.
|
// trait for the `to_le` and `to_be` methods, but this does not support floats.
|
||||||
(SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat64LSB) |
|
(SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat64LSB) |
|
||||||
(SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat64MSB) => {
|
(SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat64MSB) => {
|
||||||
process_output_callback::<f32, f64, _, _>(
|
process_output_callback::<f32, f64, _, _, _>(
|
||||||
stream_id,
|
&mut data_callback,
|
||||||
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>,
|
||||||
|
@ -568,78 +405,92 @@ impl EventLoop {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create the stream paused
|
// Immediately start the device?
|
||||||
self.cpal_streams
|
self.driver.start().map_err(build_stream_err)?;
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.push(Some(Stream { driver: driver.clone(), playing: false }));
|
|
||||||
|
|
||||||
// Give the ID based on the stream count
|
Ok(Stream { playing: stream_playing })
|
||||||
Ok(StreamId(count))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Play the cpal stream for the given ID.
|
/// Create a new CPAL Input Stream.
|
||||||
pub fn play_stream(&self, stream_id: StreamId) -> Result<(), PlayStreamError> {
|
|
||||||
let mut streams = self.cpal_streams.lock().unwrap();
|
|
||||||
if let Some(s) = streams.get_mut(stream_id.0).expect("Bad play stream index") {
|
|
||||||
s.playing = true;
|
|
||||||
// Calling play when already playing is a no-op
|
|
||||||
s.driver.start().map_err(play_stream_err)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pause the cpal stream for the given ID.
|
|
||||||
///
|
///
|
||||||
/// Pause the ASIO streams if there are no other CPAL streams playing, as ASIO only allows
|
/// If there is no existing ASIO Input Stream it will be created.
|
||||||
/// stopping the entire driver.
|
///
|
||||||
pub fn pause_stream(&self, stream_id: StreamId) -> Result<(), PauseStreamError> {
|
/// On success, the buffer size of the stream is returned.
|
||||||
let mut streams = self.cpal_streams.lock().unwrap();
|
fn get_or_create_input_stream(
|
||||||
let streams_playing = streams.iter()
|
&self,
|
||||||
.filter(|s| s.as_ref().map(|s| s.playing).unwrap_or(false))
|
format: &Format,
|
||||||
.count();
|
) -> Result<sys::AsioStream, BuildStreamError> {
|
||||||
if let Some(s) = streams.get_mut(stream_id.0).expect("Bad pause stream index") {
|
match self.default_input_format() {
|
||||||
if streams_playing <= 1 {
|
Ok(f) => {
|
||||||
s.driver.stop().map_err(pause_stream_err)?;
|
let num_asio_channels = f.channels;
|
||||||
|
check_format(&self.driver, format, num_asio_channels)
|
||||||
|
},
|
||||||
|
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),
|
||||||
|
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) => {
|
||||||
|
println!("Error preparing stream: {}", e);
|
||||||
|
Err(BuildStreamError::DeviceNotAvailable)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
s.playing = false;
|
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Destroy the cpal stream based on the ID.
|
/// Create a new CPAL Output Stream.
|
||||||
pub fn destroy_stream(&self, stream_id: StreamId) {
|
///
|
||||||
// TODO: Should we not also remove an ASIO stream here?
|
/// If there is no existing ASIO Output Stream it will be created.
|
||||||
// Yes, and we should update the logic in the callbacks to search for the stream with
|
fn get_or_create_output_stream(
|
||||||
// the matching ID, rather than assuming the index associated with the ID is valid.
|
&self,
|
||||||
let mut streams = self.cpal_streams.lock().unwrap();
|
format: &Format,
|
||||||
streams.get_mut(stream_id.0).take();
|
) -> Result<sys::AsioStream, BuildStreamError> {
|
||||||
|
match self.default_output_format() {
|
||||||
|
Ok(f) => {
|
||||||
|
let num_asio_channels = f.channels;
|
||||||
|
check_format(&self.driver, format, num_asio_channels)
|
||||||
|
},
|
||||||
|
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),
|
||||||
|
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) => {
|
||||||
|
println!("Error preparing stream: {}", e);
|
||||||
|
Err(BuildStreamError::DeviceNotAvailable)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run the cpal callbacks
|
|
||||||
pub fn run<F>(&self, mut callback: F) -> !
|
|
||||||
where
|
|
||||||
F: FnMut(StreamId, StreamDataResult) + Send,
|
|
||||||
{
|
|
||||||
let callback: &mut (FnMut(StreamId, StreamDataResult) + Send) = &mut callback;
|
|
||||||
// Transmute needed to convince the compiler that the callback has a static lifetime
|
|
||||||
*self.callbacks.lock().unwrap() = Some(unsafe { mem::transmute(callback) });
|
|
||||||
loop {
|
|
||||||
// A sleep here to prevent the loop being
|
|
||||||
// removed in --release
|
|
||||||
thread::sleep(Duration::new(1u64, 0u32));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Clean up if event loop is dropped.
|
|
||||||
/// Currently event loop is never dropped.
|
|
||||||
impl Drop for EventLoop {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
*self.asio_streams.lock().unwrap() = sys::AsioStreams {
|
|
||||||
output: None,
|
|
||||||
input: None,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -790,25 +641,3 @@ fn build_stream_err(e: sys::AsioError) -> BuildStreamError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pause_stream_err(e: sys::AsioError) -> PauseStreamError {
|
|
||||||
match e {
|
|
||||||
sys::AsioError::NoDrivers |
|
|
||||||
sys::AsioError::HardwareMalfunction => PauseStreamError::DeviceNotAvailable,
|
|
||||||
err => {
|
|
||||||
let description = format!("{}", err);
|
|
||||||
BackendSpecificError { description }.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn play_stream_err(e: sys::AsioError) -> PlayStreamError {
|
|
||||||
match e {
|
|
||||||
sys::AsioError::NoDrivers |
|
|
||||||
sys::AsioError::HardwareMalfunction => PlayStreamError::DeviceNotAvailable,
|
|
||||||
err => {
|
|
||||||
let description = format!("{}", err);
|
|
||||||
BackendSpecificError { description }.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -202,6 +202,7 @@ pub struct SupportedFormat {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stream data passed to the `EventLoop::run` callback.
|
/// Stream data passed to the `EventLoop::run` callback.
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum StreamData<'a> {
|
pub enum StreamData<'a> {
|
||||||
Input {
|
Input {
|
||||||
buffer: UnknownTypeInputBuffer<'a>,
|
buffer: UnknownTypeInputBuffer<'a>,
|
||||||
|
@ -217,6 +218,7 @@ pub enum StreamData<'a> {
|
||||||
/// same way as reading from a `Vec` or any other kind of Rust array.
|
/// same way as reading from a `Vec` or any other kind of Rust array.
|
||||||
// TODO: explain audio stuff in general
|
// TODO: explain audio stuff in general
|
||||||
// TODO: remove the wrapper and just use slices in next major version
|
// TODO: remove the wrapper and just use slices in next major version
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct InputBuffer<'a, T: 'a>
|
pub struct InputBuffer<'a, T: 'a>
|
||||||
where
|
where
|
||||||
T: Sample,
|
T: Sample,
|
||||||
|
@ -232,6 +234,7 @@ where
|
||||||
// TODO: explain audio stuff in general
|
// TODO: explain audio stuff in general
|
||||||
// TODO: remove the wrapper and just use slices
|
// TODO: remove the wrapper and just use slices
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct OutputBuffer<'a, T: 'a>
|
pub struct OutputBuffer<'a, T: 'a>
|
||||||
where
|
where
|
||||||
T: Sample,
|
T: Sample,
|
||||||
|
@ -242,6 +245,7 @@ where
|
||||||
/// This is the struct that is provided to you by cpal when you want to read samples from a buffer.
|
/// This is the struct that is provided to you by cpal when you want to read samples from a buffer.
|
||||||
///
|
///
|
||||||
/// Since the type of data is only known at runtime, you have to read the right buffer.
|
/// Since the type of data is only known at runtime, you have to read the right buffer.
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum UnknownTypeInputBuffer<'a> {
|
pub enum UnknownTypeInputBuffer<'a> {
|
||||||
/// Samples whose format is `u16`.
|
/// Samples whose format is `u16`.
|
||||||
U16(InputBuffer<'a, u16>),
|
U16(InputBuffer<'a, u16>),
|
||||||
|
@ -254,6 +258,7 @@ pub enum UnknownTypeInputBuffer<'a> {
|
||||||
/// This is the struct that is provided to you by cpal when you want to write samples to a buffer.
|
/// This is the struct that is provided to you by cpal when you want to write samples to a buffer.
|
||||||
///
|
///
|
||||||
/// Since the type of data is only known at runtime, you have to fill the right buffer.
|
/// Since the type of data is only known at runtime, you have to fill the right buffer.
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum UnknownTypeOutputBuffer<'a> {
|
pub enum UnknownTypeOutputBuffer<'a> {
|
||||||
/// Samples whose format is `u16`.
|
/// Samples whose format is `u16`.
|
||||||
U16(OutputBuffer<'a, u16>),
|
U16(OutputBuffer<'a, u16>),
|
||||||
|
|
|
@ -457,9 +457,8 @@ mod platform_impl {
|
||||||
pub use crate::host::asio::{
|
pub use crate::host::asio::{
|
||||||
Device as AsioDevice,
|
Device as AsioDevice,
|
||||||
Devices as AsioDevices,
|
Devices as AsioDevices,
|
||||||
EventLoop as AsioEventLoop,
|
Stream as AsioStream,
|
||||||
Host as AsioHost,
|
Host as AsioHost,
|
||||||
StreamId as AsioStreamId,
|
|
||||||
SupportedInputFormats as AsioSupportedInputFormats,
|
SupportedInputFormats as AsioSupportedInputFormats,
|
||||||
SupportedOutputFormats as AsioSupportedOutputFormats,
|
SupportedOutputFormats as AsioSupportedOutputFormats,
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue