Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 144 additions & 6 deletions cubeb-api/src/context.rs
Original file line number Diff line number Diff line change
@@ -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<T: Into<Vec<u8>>>(name: T) -> Result<Context> {
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<Self> {
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<u32> {
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<u32> {
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<u32> {
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<InputProcessingParams> {
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<Stream> {
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<'_>> {
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) }
}
}
59 changes: 59 additions & 0 deletions cubeb-api/src/device_collection.rs
Original file line number Diff line number Diff line change
@@ -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<DeviceCollection<'_>> {
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
}
}
9 changes: 9 additions & 0 deletions cubeb-api/src/entry.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use std::ffi::CString;
use {Context, Result};

/// Initialize a new cubeb [`Context`]
pub fn init<T: Into<Vec<u8>>>(name: T) -> Result<Context> {
let name = CString::new(name)?;

Context::init(Some(name.as_c_str()), None)
}
13 changes: 8 additions & 5 deletions cubeb-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down
4 changes: 2 additions & 2 deletions cubeb-api/src/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down Expand Up @@ -271,7 +271,7 @@ impl<'a, F> StreamBuilder<'a, F> {
}

/// Build the stream
pub fn init(self, ctx: &ContextRef) -> Result<Stream<F>> {
pub fn init(self, ctx: &Context) -> Result<Stream<F>> {
if self.data_cb.is_none() || self.state_cb.is_none() {
return Err(Error::Error);
}
Expand Down
Loading