Update ASIO implementation for new error handling and Host API

This is currently untested and will almost certainly not build. I'm
about to move into a Windows VM to get everything running properly
again.
This commit is contained in:
mitchmindtree 2019-06-26 16:24:23 +02:00
parent 2bc9f85970
commit 09fd5562be
11 changed files with 185 additions and 129 deletions

View File

@ -4,11 +4,12 @@ pub type SupportedOutputFormats = std::vec::IntoIter<SupportedFormat>;
use std::hash::{Hash, Hasher};
use DefaultFormatError;
use DeviceNameError;
use Format;
use FormatsEnumerationError;
use SampleFormat;
use SampleRate;
use SupportedFormat;
use SupportedFormatsError;
use super::sys;
/// A ASIO Device
@ -40,8 +41,8 @@ impl Hash for Device {
}
impl Device {
pub fn name(&self) -> String {
self.name.clone()
pub fn name(&self) -> Result<String, DeviceNameError> {
Ok(self.name.clone())
}
/// Gets the supported input formats.
@ -49,11 +50,11 @@ impl Device {
/// Need to find all possible formats.
pub fn supported_input_formats(
&self,
) -> Result<SupportedInputFormats, FormatsEnumerationError> {
) -> Result<SupportedInputFormats, SupportedFormatsError> {
// Retrieve the default format for the total supported channels and supported sample
// format.
let mut f = match self.default_input_format() {
Err(_) => return Err(FormatsEnumerationError::DeviceNotAvailable),
Err(_) => return Err(SupportedFormatsError::DeviceNotAvailable),
Ok(f) => f,
};
@ -77,11 +78,11 @@ impl Device {
/// Need to find all possible formats.
pub fn supported_output_formats(
&self,
) -> Result<SupportedOutputFormats, FormatsEnumerationError> {
) -> Result<SupportedOutputFormats, SupportedFormatsError> {
// Retrieve the default format for the total supported channels and supported sample
// format.
let mut f = match self.default_output_format() {
Err(_) => return Err(FormatsEnumerationError::DeviceNotAvailable),
Err(_) => return Err(SupportedFormatsError::DeviceNotAvailable),
Ok(f) => f,
};
@ -140,12 +141,12 @@ impl Device {
}
}
impl Default for Devices {
fn default() -> Devices {
impl Devices {
pub fn new() -> Result<Self, DevicesError> {
let driver_names = online_devices();
Devices {
Ok(Devices {
drivers: driver_names.into_iter(),
}
})
}
}

127
src/host/asio/mod.rs Normal file
View File

@ -0,0 +1,127 @@
extern crate asio_sys as sys;
use {
BuildStreamError,
DefaultFormatError,
Device as DeviceTrait,
DeviceNameError,
DevicesError,
EventLoop as EventLoopTrait,
Host as HostTrait,
PauseStreamError,
PlayStreamError,
StreamDataResult,
StreamId as StreamIdTrait,
SupportedFormatsError,
}
pub use self::device::{Device, Devices, SupportedInputFormats, SupportedOutputFormats, default_input_device, default_output_device};
pub use self::stream::{EventLoop, StreamId};
mod device;
mod stream;
mod asio_utils;
/// The host for ASIO.
#[derive(Debug)]
pub struct Host;
impl Host {
pub fn new() -> Result<Self, crate::HostUnavailable> {
unimplemented!("asio as an initialisation and termination process that needs to be impld");
//Ok(Host)
}
}
impl HostTrait for Host {
type Devices = Devices;
type Device = Device;
type EventLoop = EventLoop;
fn is_available() -> bool {
unimplemented!("check how to do this using asio-sys")
}
fn devices(&self) -> Result<Self::Devices, DevicesError> {
Devices::new()
}
fn default_input_device(&self) -> Option<Self::Device> {
default_input_device()
}
fn default_output_device(&self) -> Option<Self::Device> {
default_output_device()
}
fn event_loop(&self) -> Self::EventLoop {
EventLoop::new()
}
}
impl DeviceTrait for Device {
type SupportedInputFormats = SupportedInputFormats;
type SupportedOutputFormats = SupportedOutputFormats;
fn name(&self) -> Result<String, DeviceNameError> {
Device::name(self)
}
fn supported_input_formats(&self) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
Device::supported_input_formats(self)
}
fn supported_output_formats(&self) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
Device::supported_output_formats(self)
}
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
Device::default_input_format(self)
}
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
Device::default_output_format(self)
}
}
impl EventLoopTrait for EventLoop {
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
F: FnMut(Self::StreamId, StreamDataResult) + Send,
{
EventLoop::run(self, callback)
}
}
impl StreamIdTrait for StreamId {}

View File

@ -10,10 +10,13 @@ use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use CreationError;
use BuildStreamError;
use Format;
use PauseStreamError;
use PlayStreamError;
use SampleFormat;
use StreamData;
use StreamDataResult;
use UnknownTypeInputBuffer;
use UnknownTypeOutputBuffer;
@ -26,7 +29,7 @@ pub struct EventLoop {
/// 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, StreamData) + Send)>>>,
callbacks: Arc<Mutex<Option<&'static mut (FnMut(StreamId, StreamDataResult) + Send)>>>,
}
/// Id for each stream.
@ -35,13 +38,6 @@ pub struct EventLoop {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StreamId(usize);
pub struct InputBuffer<'a, T: 'a> {
buffer: &'a [T],
}
pub struct OutputBuffer<'a, T: 'a> {
buffer: &'a mut [T],
}
/// CPAL stream.
/// This decouples the many cpal streams
/// from the single input and single output
@ -92,8 +88,8 @@ impl EventLoop {
&self,
drivers: &sys::Drivers,
format: &Format,
num_asio_channels: u16,
) -> Result<(), CreationError> {
num_asio_channels: u16,
) -> Result<(), BuildStreamError> {
let Format {
channels,
sample_rate,
@ -107,16 +103,16 @@ impl EventLoop {
.set_sample_rate(sample_rate)
.expect("Unsupported sample rate");
} else {
return Err(CreationError::FormatNotSupported);
return Err(BuildStreamError::FormatNotSupported);
}
}
// unsigned formats are not supported by asio
match data_type {
SampleFormat::I16 | SampleFormat::F32 => (),
SampleFormat::U16 => return Err(CreationError::FormatNotSupported),
SampleFormat::U16 => return Err(BuildStreamError::FormatNotSupported),
}
if *channels > num_asio_channels {
return Err(CreationError::FormatNotSupported);
return Err(BuildStreamError::FormatNotSupported);
}
Ok(())
}
@ -129,13 +125,13 @@ impl EventLoop {
drivers: &sys::Drivers,
format: &Format,
device: &Device,
) -> Result<usize, CreationError> {
) -> Result<usize, BuildStreamError> {
match device.default_input_format() {
Ok(f) => {
let num_asio_channels = f.channels;
self.check_format(drivers, format, num_asio_channels)
},
Err(_) => Err(CreationError::FormatNotSupported),
Err(_) => Err(BuildStreamError::FormatNotSupported),
}?;
let num_channels = format.channels as usize;
let ref mut streams = *self.asio_streams.lock().unwrap();
@ -156,7 +152,7 @@ impl EventLoop {
bs
}).map_err(|ref e| {
println!("Error preparing stream: {}", e);
CreationError::DeviceNotAvailable
BuildStreamError::DeviceNotAvailable
})
}
}
@ -170,13 +166,13 @@ impl EventLoop {
drivers: &sys::Drivers,
format: &Format,
device: &Device,
) -> Result<usize, CreationError> {
) -> Result<usize, BuildStreamError> {
match device.default_output_format() {
Ok(f) => {
let num_asio_channels = f.channels;
self.check_format(drivers, format, num_asio_channels)
},
Err(_) => Err(CreationError::FormatNotSupported),
Err(_) => Err(BuildStreamError::FormatNotSupported),
}?;
let num_channels = format.channels as usize;
let ref mut streams = *self.asio_streams.lock().unwrap();
@ -197,18 +193,18 @@ impl EventLoop {
bs
}).map_err(|ref e| {
println!("Error preparing stream: {}", e);
CreationError::DeviceNotAvailable
BuildStreamError::DeviceNotAvailable
})
}
}
}
/// Builds a new cpal input stream
/// Builds a new cpal input stream
pub fn build_input_stream(
&self,
device: &Device,
format: &Format,
) -> Result<StreamId, CreationError> {
) -> Result<StreamId, BuildStreamError> {
let Device { drivers, .. } = device;
let num_channels = format.channels.clone();
let stream_type = drivers.get_data_type().expect("Couldn't load data type");
@ -328,7 +324,7 @@ impl EventLoop {
$SampleTypeIdent:ident,
$Sample:expr
) => {
(*$Sample as f64 * ::std::$SampleTypeIdent::MAX as f64) as $SampleType
(*$Sample as f64 * ::std::$SampleTypeIdent::MAX as f64) as $SampleType
};
(f64,
$SampleType:ty,
@ -396,18 +392,14 @@ impl EventLoop {
}
}
// Wrap the buffer in the CPAL type
let buff = InputBuffer {
buffer: &mut $Buffers.cpal,
};
// Call the users callback with the buffer
callback(
StreamId(count),
StreamData::Input {
Ok(StreamData::Input {
buffer: UnknownTypeInputBuffer::$SampleFormat(::InputBuffer {
buffer: Some(super::super::InputBuffer::Asio(buff)),
buffer: &$Buffers.cpal,
}),
},
}),
);
};
};
@ -542,7 +534,7 @@ impl EventLoop {
&self,
device: &Device,
format: &Format,
) -> Result<StreamId, CreationError> {
) -> Result<StreamId, BuildStreamError> {
let Device { drivers, .. } = device;
let num_channels = format.channels.clone();
let stream_type = drivers.get_data_type().expect("Couldn't load data type");
@ -626,28 +618,28 @@ impl EventLoop {
f32,
$Sample:expr
) => {
*$Sample as f64
*$Sample as f64
};
($AsioTypeIdent:ident,
f32,
f64,
$Sample:expr
) => {
*$Sample as f32
*$Sample as f32
};
($AsioTypeIdent:ident,
$AsioType:ty,
f32,
$Sample:expr
) => {
(*$Sample as f64 * ::std::$AsioTypeIdent::MAX as f64) as $AsioType
(*$Sample as f64 * ::std::$AsioTypeIdent::MAX as f64) as $AsioType
};
($AsioTypeIdent:ident,
$AsioType:ty,
f64,
$Sample:expr
) => {
(*$Sample as f64 * ::std::$AsioTypeIdent::MAX as f64) as $AsioType
(*$Sample as f64 * ::std::$AsioTypeIdent::MAX as f64) as $AsioType
};
($AsioTypeIdent:ident,
f32,
@ -695,15 +687,13 @@ impl EventLoop {
// users data
callback(
StreamId(count),
StreamData::Output {
Ok(StreamData::Output {
buffer: UnknownTypeOutputBuffer::$SampleFormat(
::OutputBuffer {
target: Some(super::super::OutputBuffer::Asio(
buff,
)),
buffer: &mut my_buffers.cpal,
},
),
},
}),
);
}
// Deinter all the channels
@ -897,7 +887,7 @@ impl EventLoop {
/// Play the cpal stream for the given ID.
/// Also play The ASIO streams if they are not already.
pub fn play_stream(&self, stream_id: StreamId) {
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;
@ -908,7 +898,7 @@ impl EventLoop {
/// Pause the cpal stream for the given ID.
/// Pause the ASIO streams if there are no CPAL streams palying.
pub fn pause_stream(&self, stream_id: StreamId) {
pub fn pause_stream(&self, stream_id: StreamId) -> Result<(), PauseStreamError> {
let mut streams = self.cpal_streams.lock().unwrap();
if let Some(s) = streams
.get_mut(stream_id.0)
@ -933,9 +923,9 @@ impl EventLoop {
/// Run the cpal callbacks
pub fn run<F>(&self, mut callback: F) -> !
where
F: FnMut(StreamId, StreamData) + Send,
F: FnMut(StreamId, StreamDataResult) + Send,
{
let callback: &mut (FnMut(StreamId, StreamData) + Send) = &mut callback;
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 {
@ -958,25 +948,6 @@ impl Drop for EventLoop {
}
}
impl<'a, T> InputBuffer<'a, T> {
pub fn buffer(&self) -> &[T] {
&self.buffer
}
pub fn finish(self) {}
}
impl<'a, T> OutputBuffer<'a, T> {
pub fn buffer(&mut self) -> &mut [T] {
&mut self.buffer
}
pub fn len(&self) -> usize {
self.buffer.len()
}
pub fn finish(self) {}
}
/// Helper function to convert to system endianness
fn convert_endian_to<T: PrimInt>(sample: T, endian: Endian) -> T {
match endian {

View File

@ -1,5 +1,7 @@
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
pub(crate) mod alsa;
#[cfg(windows)]
pub(crate) mod asio;
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub(crate) mod coreaudio;
#[cfg(target_os = "emscripten")]

View File

@ -1,7 +0,0 @@
/// The cpal::os module provides operating-system-specific
/// functionality. If you are using this module within a
/// cross-platform project, you may wish to use
/// cfg(target_os = "<os_name>") to ensure that you only
/// use the OS-specific items when compiling for that OS.
#[cfg(target_os = "windows")]
pub mod windows;

View File

@ -1,36 +0,0 @@
/// This allows you to choose either Wasapi or ASIO
/// as your back end. Wasapi is the default.
/// The CPAL_ASIO_DIR must be set to the ASIO SDK
/// directory for use_asio_backend to be available.
use std::sync::Mutex;
#[derive(Clone)]
pub enum BackEnd {
Wasapi,
Asio,
}
lazy_static! {
static ref BACK_END: Mutex<BackEnd> = Mutex::new(BackEnd::Wasapi);
}
/// See which beackend is currently set.
pub fn which_backend() -> BackEnd {
(*BACK_END.lock().unwrap()).clone()
}
#[cfg(asio)]
/// Choose ASIO as the backend
pub fn use_asio_backend() -> Result<(), BackEndError> {
*BACK_END.lock().unwrap() = BackEnd::Asio;
Ok(())
}
/// Choose Wasapi as the backend
pub fn use_wasapi_backend() -> Result<(), BackEndError> {
*BACK_END.lock().unwrap() = BackEnd::Wasapi;
Ok(())
}
#[derive(Debug)]
pub struct BackEndError;

View File

@ -516,6 +516,15 @@ mod platform_impl {
// TODO: Add `Asio asio` once #221 lands.
#[cfg(windows)]
mod platform_impl {
pub use crate::host::asio::{
Device as AsioDevice,
Devices as AsioDevices,
EventLoop as AsioEventLoop,
Host as AsioHost,
StreamId as AsioStreamId,
SupportedInputFormats as AsioSupportedInputFormats,
SupportedOutputFormats as AsioSupportedOutputFormats,
};
pub use crate::host::wasapi::{
Device as WasapiDevice,
Devices as WasapiDevices,
@ -526,7 +535,7 @@ mod platform_impl {
SupportedOutputFormats as WasapiSupportedOutputFormats,
};
impl_platform_host!(Wasapi wasapi);
impl_platform_host!(Asio asio, Wasapi wasapi);
/// The default host for the current compilation target platform.
pub fn default_host() -> Host {

View File

@ -1,9 +0,0 @@
extern crate asio_sys as sys;
pub use self::device::{Device, Devices, SupportedInputFormats, SupportedOutputFormats, default_input_device, default_output_device};
pub use self::stream::{InputBuffer, OutputBuffer, EventLoop, StreamId};
mod device;
mod stream;
mod asio_utils;

View File

@ -1,2 +0,0 @@
#[cfg(windows)]
mod asio;