diff --git a/cubeb-api/src/context.rs b/cubeb-api/src/context.rs index 6b9d8fc..3e11d96 100644 --- a/cubeb-api/src/context.rs +++ b/cubeb-api/src/context.rs @@ -1,9 +1,147 @@ -use std::ffi::CString; -use {Context, Result}; +// Copyright © 2017-2018 Mozilla Foundation +// +// This program is made available under an ISC-style license. See the +// accompanying file LICENSE for details. -/// Initialize a new cubeb [`Context`] -pub fn init>>(name: T) -> Result { - let name = CString::new(name)?; +use cubeb_core::{InputProcessingParams, Stream}; +use ffi; +use std::ffi::CStr; +use std::os::raw::c_void; +use std::{ptr, str}; +use Error; - Context::init(Some(name.as_c_str()), None) +use {DeviceCollection, DeviceId, DeviceType, Result, StreamParamsRef}; + +macro_rules! as_ptr { + ($e:expr) => { + $e.map(|s| s.as_ptr()).unwrap_or(ptr::null_mut()) + }; +} + +#[derive(Debug)] +pub struct Context(pub(crate) *mut ffi::cubeb); + +impl Context { + pub fn init(context_name: Option<&CStr>, backend_name: Option<&CStr>) -> Result { + let mut context: *mut ffi::cubeb = ptr::null_mut(); + let context_name = as_ptr!(context_name); + let backend_name = as_ptr!(backend_name); + unsafe { + Error::wrap(ffi::cubeb_init(&mut context, context_name, backend_name))?; + Ok(Context(context)) + } + } + + pub fn backend_id(&self) -> &str { + // SAFETY: The returned pointer is guaranted to be a valid, read-only C string + unsafe { CStr::from_ptr(ffi::cubeb_get_backend_id(self.0)) } + .to_str() + .expect("Backend ID is not a valid UTF-8 string.") + } + + pub fn max_channel_count(&self) -> Result { + let mut channel_count = 0u32; + unsafe { + Error::wrap(ffi::cubeb_get_max_channel_count(self.0, &mut channel_count))?; + } + Ok(channel_count) + } + + pub fn min_latency(&self, params: &StreamParamsRef) -> Result { + let mut latency = 0u32; + unsafe { + Error::wrap(ffi::cubeb_get_min_latency( + self.0, + params.as_ptr(), + &mut latency, + ))?; + } + Ok(latency) + } + + pub fn preferred_sample_rate(&self) -> Result { + let mut rate = 0u32; + unsafe { + Error::wrap(ffi::cubeb_get_preferred_sample_rate(self.0, &mut rate))?; + } + Ok(rate) + } + + pub fn supported_input_processing_params(&self) -> Result { + let mut params = ffi::CUBEB_INPUT_PROCESSING_PARAM_NONE; + unsafe { + Error::wrap(ffi::cubeb_get_supported_input_processing_params( + self.0, + &mut params, + ))?; + }; + Ok(InputProcessingParams::from_bits_truncate(params)) + } + + /// # Safety + /// + /// This function is unsafe because it dereferences the given `data_callback`, `state_callback`, and `user_ptr` pointers. + /// The caller should ensure those pointers are valid. + #[allow(clippy::too_many_arguments)] + pub unsafe fn stream_init( + &self, + stream_name: Option<&CStr>, + input_device: DeviceId, + input_stream_params: Option<&StreamParamsRef>, + output_device: DeviceId, + output_stream_params: Option<&StreamParamsRef>, + latency_frames: u32, + data_callback: ffi::cubeb_data_callback, + state_callback: ffi::cubeb_state_callback, + user_ptr: *mut c_void, + ) -> Result { + let mut stm: *mut ffi::cubeb_stream = ptr::null_mut(); + + let stream_name = as_ptr!(stream_name); + let input_stream_params = as_ptr!(input_stream_params); + let output_stream_params = as_ptr!(output_stream_params); + + Error::wrap(ffi::cubeb_stream_init( + self.0, + &mut stm, + stream_name, + input_device, + input_stream_params, + output_device, + output_stream_params, + latency_frames, + data_callback, + state_callback, + user_ptr, + ))?; + Ok(Stream::from_ptr(stm)) + } + + pub fn enumerate_devices(&self, devtype: DeviceType) -> Result> { + DeviceCollection::new(self, devtype) + } + + /// # Safety + /// + /// This function is unsafe because it dereferences the given `callback` and `user_ptr` pointers. + /// The caller should ensure those pointers are valid. + pub unsafe fn register_device_collection_changed( + &self, + devtype: DeviceType, + callback: ffi::cubeb_device_collection_changed_callback, + user_ptr: *mut c_void, + ) -> Result<()> { + Error::wrap(ffi::cubeb_register_device_collection_changed( + self.0, + devtype.bits(), + callback, + user_ptr, + )) + } +} + +impl Drop for Context { + fn drop(&mut self) { + unsafe { ffi::cubeb_destroy(self.0) } + } } diff --git a/cubeb-api/src/device_collection.rs b/cubeb-api/src/device_collection.rs new file mode 100644 index 0000000..8bcd66f --- /dev/null +++ b/cubeb-api/src/device_collection.rs @@ -0,0 +1,59 @@ +// Copyright © 2017-2018 Mozilla Foundation +// +// This program is made available under an ISC-style license. See the +// accompanying file LICENSE for details. + +use ffi; +use std::mem::MaybeUninit; +use {Context, DeviceInfo, DeviceType, Error, Result}; + +/// A collection of `DeviceInfo` used by libcubeb +#[derive(Debug)] +pub struct DeviceCollection<'ctx> { + inner: &'ctx mut [DeviceInfo], + ctx: &'ctx Context, +} + +impl DeviceCollection<'_> { + pub(crate) fn new(ctx: &Context, devtype: DeviceType) -> Result> { + let mut coll = MaybeUninit::uninit(); + unsafe { + Error::wrap(ffi::cubeb_enumerate_devices( + ctx.0, + devtype.bits(), + coll.as_mut_ptr(), + ))?; + } + + // SAFETY: It is the responsibility of the cubeb_enumerate_devices to initialize the + // device collection struct with a valid array of device infos. + let inner = unsafe { + let coll = coll.assume_init(); + std::slice::from_raw_parts_mut(coll.device as *mut _, coll.count) + }; + Ok(DeviceCollection { inner, ctx }) + } +} + +impl Drop for DeviceCollection<'_> { + fn drop(&mut self) { + let mut coll = ffi::cubeb_device_collection { + device: self.inner.as_mut_ptr() as *mut _, + count: self.inner.len(), + }; + unsafe { + // This drops the self.inner, do not interact with it past this point + let res = ffi::cubeb_device_collection_destroy(self.ctx.0, &mut coll); + debug_assert!(res == 0) + } + } +} + +impl ::std::ops::Deref for DeviceCollection<'_> { + type Target = [DeviceInfo]; + + #[inline] + fn deref(&self) -> &Self::Target { + self.inner + } +} diff --git a/cubeb-api/src/entry.rs b/cubeb-api/src/entry.rs new file mode 100644 index 0000000..6b9d8fc --- /dev/null +++ b/cubeb-api/src/entry.rs @@ -0,0 +1,9 @@ +use std::ffi::CString; +use {Context, Result}; + +/// Initialize a new cubeb [`Context`] +pub fn init>>(name: T) -> Result { + let name = CString::new(name)?; + + Context::init(Some(name.as_c_str()), None) +} diff --git a/cubeb-api/src/lib.rs b/cubeb-api/src/lib.rs index ed9912d..f2e4d92 100644 --- a/cubeb-api/src/lib.rs +++ b/cubeb-api/src/lib.rs @@ -23,17 +23,20 @@ extern crate cubeb_core; mod context; +mod device_collection; +mod entry; mod frame; mod sample; mod stream; -pub use context::*; +pub use context::Context; +pub use device_collection::DeviceCollection; +pub use entry::init; // Re-export cubeb_core types pub use cubeb_core::{ - ffi, ChannelLayout, Context, ContextRef, Device, DeviceCollection, DeviceCollectionRef, - DeviceFormat, DeviceId, DeviceInfo, DeviceInfoRef, DeviceRef, DeviceState, DeviceType, Error, - LogLevel, Result, SampleFormat, State, StreamParams, StreamParamsBuilder, StreamParamsRef, - StreamPrefs, StreamRef, + ffi, ChannelLayout, Device, DeviceFormat, DeviceId, DeviceInfo, DeviceInfoRef, DeviceRef, + DeviceState, DeviceType, Error, LogLevel, Result, SampleFormat, State, StreamParams, + StreamParamsBuilder, StreamParamsRef, StreamPrefs, StreamRef, }; pub use frame::*; pub use sample::*; diff --git a/cubeb-api/src/stream.rs b/cubeb-api/src/stream.rs index fabbc61..5962457 100644 --- a/cubeb-api/src/stream.rs +++ b/cubeb-api/src/stream.rs @@ -11,7 +11,7 @@ use std::mem::ManuallyDrop; use std::os::raw::{c_long, c_void}; use std::slice::{from_raw_parts, from_raw_parts_mut}; use std::{ops, panic, ptr}; -use {ContextRef, DeviceId, Error, Result, State, StreamParamsRef}; +use {Context, DeviceId, Error, Result, State, StreamParamsRef}; /// User supplied data callback. /// @@ -271,7 +271,7 @@ impl<'a, F> StreamBuilder<'a, F> { } /// Build the stream - pub fn init(self, ctx: &ContextRef) -> Result> { + pub fn init(self, ctx: &Context) -> Result> { if self.data_cb.is_none() || self.state_cb.is_none() { return Err(Error::Error); } diff --git a/cubeb-core/src/context.rs b/cubeb-core/src/context.rs deleted file mode 100644 index a6191a6..0000000 --- a/cubeb-core/src/context.rs +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright © 2017-2018 Mozilla Foundation -// -// This program is made available under an ISC-style license. See the -// accompanying file LICENSE for details. - -use ffi; -use std::ffi::CStr; -use std::os::raw::c_void; -use std::{ptr, str}; -use util::opt_bytes; -use { - DeviceCollection, DeviceId, DeviceType, InputProcessingParams, Result, Stream, StreamParamsRef, -}; - -macro_rules! as_ptr { - ($e:expr) => { - $e.map(|s| s.as_ptr()).unwrap_or(ptr::null_mut()) - }; -} - -ffi_type_heap! { - type CType = ffi::cubeb; - fn drop = ffi::cubeb_destroy; - pub struct Context; - pub struct ContextRef; -} - -impl Context { - pub fn init(context_name: Option<&CStr>, backend_name: Option<&CStr>) -> Result { - let mut context: *mut ffi::cubeb = ptr::null_mut(); - let context_name = as_ptr!(context_name); - let backend_name = as_ptr!(backend_name); - unsafe { - call!(ffi::cubeb_init(&mut context, context_name, backend_name))?; - Ok(Context::from_ptr(context)) - } - } -} - -impl ContextRef { - pub fn backend_id(&self) -> &str { - str::from_utf8(self.backend_id_bytes()).unwrap() - } - - pub fn backend_id_bytes(&self) -> &[u8] { - unsafe { opt_bytes(ffi::cubeb_get_backend_id(self.as_ptr())).unwrap() } - } - - pub fn max_channel_count(&self) -> Result { - let mut channel_count = 0u32; - unsafe { - call!(ffi::cubeb_get_max_channel_count( - self.as_ptr(), - &mut channel_count - ))?; - } - Ok(channel_count) - } - - pub fn min_latency(&self, params: &StreamParamsRef) -> Result { - let mut latency = 0u32; - unsafe { - call!(ffi::cubeb_get_min_latency( - self.as_ptr(), - params.as_ptr(), - &mut latency - ))?; - } - Ok(latency) - } - - pub fn preferred_sample_rate(&self) -> Result { - let mut rate = 0u32; - unsafe { - call!(ffi::cubeb_get_preferred_sample_rate( - self.as_ptr(), - &mut rate - ))?; - } - Ok(rate) - } - - pub fn supported_input_processing_params(&self) -> Result { - let mut params = ffi::CUBEB_INPUT_PROCESSING_PARAM_NONE; - unsafe { - call!(ffi::cubeb_get_supported_input_processing_params( - self.as_ptr(), - &mut params - ))?; - }; - Ok(InputProcessingParams::from_bits_truncate(params)) - } - - /// # Safety - /// - /// This function is unsafe because it dereferences the given `data_callback`, `state_callback`, and `user_ptr` pointers. - /// The caller should ensure those pointers are valid. - #[allow(clippy::too_many_arguments)] - pub unsafe fn stream_init( - &self, - stream_name: Option<&CStr>, - input_device: DeviceId, - input_stream_params: Option<&StreamParamsRef>, - output_device: DeviceId, - output_stream_params: Option<&StreamParamsRef>, - latency_frames: u32, - data_callback: ffi::cubeb_data_callback, - state_callback: ffi::cubeb_state_callback, - user_ptr: *mut c_void, - ) -> Result { - let mut stm: *mut ffi::cubeb_stream = ptr::null_mut(); - - let stream_name = as_ptr!(stream_name); - let input_stream_params = as_ptr!(input_stream_params); - let output_stream_params = as_ptr!(output_stream_params); - - call!(ffi::cubeb_stream_init( - self.as_ptr(), - &mut stm, - stream_name, - input_device, - input_stream_params, - output_device, - output_stream_params, - latency_frames, - data_callback, - state_callback, - user_ptr - ))?; - Ok(Stream::from_ptr(stm)) - } - - pub fn enumerate_devices(&self, devtype: DeviceType) -> Result> { - let mut coll = ffi::cubeb_device_collection::default(); - unsafe { - call!(ffi::cubeb_enumerate_devices( - self.as_ptr(), - devtype.bits(), - &mut coll - ))?; - } - Ok(DeviceCollection::init_with_ctx(self, coll)) - } - - /// # Safety - /// - /// This function is unsafe because it dereferences the given `callback` and `user_ptr` pointers. - /// The caller should ensure those pointers are valid. - pub unsafe fn register_device_collection_changed( - &self, - devtype: DeviceType, - callback: ffi::cubeb_device_collection_changed_callback, - user_ptr: *mut c_void, - ) -> Result<()> { - call!(ffi::cubeb_register_device_collection_changed( - self.as_ptr(), - devtype.bits(), - callback, - user_ptr - ))?; - - Ok(()) - } -} diff --git a/cubeb-core/src/device.rs b/cubeb-core/src/device.rs index 4854aa2..92c554d 100644 --- a/cubeb-core/src/device.rs +++ b/cubeb-core/src/device.rs @@ -103,6 +103,7 @@ ffi_type_stack! { /// returns these structures via `device_collection` and must be /// destroyed via `device_collection_destroy`. type CType = ffi::cubeb_device_info; + #[derive(Debug)] pub struct DeviceInfo; pub struct DeviceInfoRef; } diff --git a/cubeb-core/src/device_collection.rs b/cubeb-core/src/device_collection.rs deleted file mode 100644 index bf2df9a..0000000 --- a/cubeb-core/src/device_collection.rs +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright © 2017-2018 Mozilla Foundation -// -// This program is made available under an ISC-style license. See the -// accompanying file LICENSE for details. - -use ffi; -use ffi_types; -use std::{ops, slice}; -use {ContextRef, DeviceInfo}; - -/// A collection of `DeviceInfo` used by libcubeb -type CType = ffi::cubeb_device_collection; - -#[derive(Debug)] -pub struct DeviceCollection<'ctx>(CType, &'ctx ContextRef); - -impl DeviceCollection<'_> { - pub(crate) fn init_with_ctx(ctx: &ContextRef, coll: CType) -> DeviceCollection<'_> { - DeviceCollection(coll, ctx) - } - - pub fn as_ptr(&self) -> *mut CType { - &self.0 as *const CType as *mut CType - } -} - -impl Drop for DeviceCollection<'_> { - fn drop(&mut self) { - unsafe { - let _ = call!(ffi::cubeb_device_collection_destroy( - self.1.as_ptr(), - &mut self.0 - )); - } - } -} - -impl ::std::ops::Deref for DeviceCollection<'_> { - type Target = DeviceCollectionRef; - - #[inline] - fn deref(&self) -> &DeviceCollectionRef { - let ptr = &self.0 as *const CType as *mut CType; - unsafe { DeviceCollectionRef::from_ptr(ptr) } - } -} - -impl ::std::convert::AsRef for DeviceCollection<'_> { - #[inline] - fn as_ref(&self) -> &DeviceCollectionRef { - self - } -} - -pub struct DeviceCollectionRef(ffi_types::Opaque); - -impl DeviceCollectionRef { - /// # Safety - /// - /// This function is unsafe because it dereferences the given `ptr` pointer. - /// The caller should ensure that pointer is valid. - #[inline] - pub unsafe fn from_ptr<'a>(ptr: *mut CType) -> &'a Self { - &*(ptr as *mut _) - } - - /// # Safety - /// - /// This function is unsafe because it dereferences the given `ptr` pointer. - /// The caller should ensure that pointer is valid. - #[inline] - pub unsafe fn from_ptr_mut<'a>(ptr: *mut CType) -> &'a mut Self { - &mut *(ptr as *mut _) - } - - #[inline] - pub fn as_ptr(&self) -> *mut CType { - self as *const _ as *mut _ - } -} - -impl ::std::fmt::Debug for DeviceCollectionRef { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - let ptr = self as *const DeviceCollectionRef as usize; - f.debug_tuple(stringify!(DeviceCollectionRef)) - .field(&ptr) - .finish() - } -} - -impl ops::Deref for DeviceCollectionRef { - type Target = [DeviceInfo]; - fn deref(&self) -> &[DeviceInfo] { - unsafe { - let coll: &ffi::cubeb_device_collection = &*self.as_ptr(); - slice::from_raw_parts(coll.device as *const DeviceInfo, coll.count) - } - } -} diff --git a/cubeb-core/src/lib.rs b/cubeb-core/src/lib.rs index ddcf674..6a0dd88 100644 --- a/cubeb-core/src/lib.rs +++ b/cubeb-core/src/lib.rs @@ -14,9 +14,7 @@ mod call; mod builders; mod channel; -mod context; mod device; -mod device_collection; mod error; mod format; mod log; @@ -25,9 +23,7 @@ mod util; pub use builders::*; pub use channel::*; -pub use context::*; pub use device::*; -pub use device_collection::*; pub use error::*; pub use format::*; pub use log::*; diff --git a/cubeb-sys/src/device.rs b/cubeb-sys/src/device.rs index bac4a86..ae58417 100644 --- a/cubeb-sys/src/device.rs +++ b/cubeb-sys/src/device.rs @@ -133,24 +133,6 @@ pub struct cubeb_device_collection { pub count: usize, } -impl Default for cubeb_device_collection { - fn default() -> Self { - unsafe { mem::zeroed() } - } -} - -impl fmt::Debug for cubeb_device_collection { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let devices = ptr::slice_from_raw_parts(self.device, self.count); - let devices = unsafe { &*devices }; - let mut dbg = f.debug_list(); - for d in devices { - dbg.entry(d); - } - dbg.finish() - } -} - #[repr(C)] pub struct cubeb_device_info { pub devid: cubeb_devid,