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:
parent
2bc9f85970
commit
09fd5562be
|
@ -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(),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {}
|
|
@ -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 {
|
|
@ -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")]
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
|
@ -1,2 +0,0 @@
|
|||
#[cfg(windows)]
|
||||
mod asio;
|
Loading…
Reference in New Issue