diff --git a/src/platform/windows/asio/asio_utils/mod.rs b/src/host/asio/asio_utils/mod.rs similarity index 100% rename from src/platform/windows/asio/asio_utils/mod.rs rename to src/host/asio/asio_utils/mod.rs diff --git a/src/platform/windows/asio/asio_utils/tests.rs b/src/host/asio/asio_utils/tests.rs similarity index 100% rename from src/platform/windows/asio/asio_utils/tests.rs rename to src/host/asio/asio_utils/tests.rs diff --git a/src/platform/windows/asio/device.rs b/src/host/asio/device.rs similarity index 92% rename from src/platform/windows/asio/device.rs rename to src/host/asio/device.rs index e31d9de..c6c085b 100644 --- a/src/platform/windows/asio/device.rs +++ b/src/host/asio/device.rs @@ -4,11 +4,12 @@ pub type SupportedOutputFormats = std::vec::IntoIter; 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 { + 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 { + ) -> Result { // 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 { + ) -> Result { // 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 { let driver_names = online_devices(); - Devices { + Ok(Devices { drivers: driver_names.into_iter(), - } + }) } } diff --git a/src/host/asio/mod.rs b/src/host/asio/mod.rs new file mode 100644 index 0000000..f1b22dc --- /dev/null +++ b/src/host/asio/mod.rs @@ -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 { + 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 { + Devices::new() + } + + fn default_input_device(&self) -> Option { + default_input_device() + } + + fn default_output_device(&self) -> Option { + 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 { + Device::name(self) + } + + fn supported_input_formats(&self) -> Result { + Device::supported_input_formats(self) + } + + fn supported_output_formats(&self) -> Result { + Device::supported_output_formats(self) + } + + fn default_input_format(&self) -> Result { + Device::default_input_format(self) + } + + fn default_output_format(&self) -> Result { + 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 { + EventLoop::build_input_stream(self, device, format) + } + + fn build_output_stream( + &self, + device: &Self::Device, + format: &Format, + ) -> Result { + 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(&self, callback: F) -> ! + where + F: FnMut(Self::StreamId, StreamDataResult) + Send, + { + EventLoop::run(self, callback) + } +} + +impl StreamIdTrait for StreamId {} diff --git a/src/platform/windows/asio/stream.rs b/src/host/asio/stream.rs similarity index 93% rename from src/platform/windows/asio/stream.rs rename to src/host/asio/stream.rs index d852d0a..2beb37d 100644 --- a/src/platform/windows/asio/stream.rs +++ b/src/host/asio/stream.rs @@ -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>>, + callbacks: Arc>>, } /// 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 { + ) -> Result { 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 { + ) -> Result { 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 { + ) -> Result { 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 { + ) -> Result { 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(&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(sample: T, endian: Endian) -> T { match endian { diff --git a/src/host/mod.rs b/src/host/mod.rs index 0c3155e..2bc9b2e 100644 --- a/src/host/mod.rs +++ b/src/host/mod.rs @@ -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")] diff --git a/src/os/mod.rs b/src/os/mod.rs deleted file mode 100644 index 3716caa..0000000 --- a/src/os/mod.rs +++ /dev/null @@ -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 = "") to ensure that you only -/// use the OS-specific items when compiling for that OS. -#[cfg(target_os = "windows")] -pub mod windows; diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs deleted file mode 100644 index e148c81..0000000 --- a/src/os/windows/mod.rs +++ /dev/null @@ -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 = 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; \ No newline at end of file diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 74030a5..f97697e 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -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 { diff --git a/src/platform/windows/asio/mod.rs b/src/platform/windows/asio/mod.rs deleted file mode 100644 index 1d49f42..0000000 --- a/src/platform/windows/asio/mod.rs +++ /dev/null @@ -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; \ No newline at end of file diff --git a/src/platform/windows/mod.rs b/src/platform/windows/mod.rs deleted file mode 100644 index b274ec0..0000000 --- a/src/platform/windows/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[cfg(windows)] -mod asio;