summaryrefslogtreecommitdiffstats
path: root/third_party/rust/cubeb-core/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rust/cubeb-core/src
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/cubeb-core/src')
-rw-r--r--third_party/rust/cubeb-core/src/builders.rs260
-rw-r--r--third_party/rust/cubeb-core/src/call.rs21
-rw-r--r--third_party/rust/cubeb-core/src/channel.rs150
-rw-r--r--third_party/rust/cubeb-core/src/context.rs164
-rw-r--r--third_party/rust/cubeb-core/src/device.rs239
-rw-r--r--third_party/rust/cubeb-core/src/device_collection.rs99
-rw-r--r--third_party/rust/cubeb-core/src/error.rs162
-rw-r--r--third_party/rust/cubeb-core/src/ffi_types.rs242
-rw-r--r--third_party/rust/cubeb-core/src/format.rs44
-rw-r--r--third_party/rust/cubeb-core/src/lib.rs38
-rw-r--r--third_party/rust/cubeb-core/src/log.rs42
-rw-r--r--third_party/rust/cubeb-core/src/stream.rs360
-rw-r--r--third_party/rust/cubeb-core/src/util.rs15
13 files changed, 1836 insertions, 0 deletions
diff --git a/third_party/rust/cubeb-core/src/builders.rs b/third_party/rust/cubeb-core/src/builders.rs
new file mode 100644
index 0000000000..c79b83569f
--- /dev/null
+++ b/third_party/rust/cubeb-core/src/builders.rs
@@ -0,0 +1,260 @@
+// 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 {ChannelLayout, SampleFormat, StreamParams, StreamPrefs};
+
+///
+#[derive(Debug)]
+pub struct StreamParamsBuilder(ffi::cubeb_stream_params);
+
+impl Default for StreamParamsBuilder {
+ fn default() -> Self {
+ StreamParamsBuilder(ffi::cubeb_stream_params {
+ format: ffi::CUBEB_SAMPLE_S16NE,
+ ..Default::default()
+ })
+ }
+}
+
+impl StreamParamsBuilder {
+ pub fn new() -> Self {
+ Default::default()
+ }
+
+ pub fn format(mut self, format: SampleFormat) -> Self {
+ self.0.format = format.into();
+ self
+ }
+
+ pub fn rate(mut self, rate: u32) -> Self {
+ self.0.rate = rate;
+ self
+ }
+
+ pub fn channels(mut self, channels: u32) -> Self {
+ self.0.channels = channels;
+ self
+ }
+
+ pub fn layout(mut self, layout: ChannelLayout) -> Self {
+ self.0.layout = layout.into();
+ self
+ }
+
+ pub fn prefs(mut self, prefs: StreamPrefs) -> Self {
+ self.0.prefs = prefs.bits();
+ self
+ }
+
+ pub fn take(&self) -> StreamParams {
+ StreamParams::from(self.0)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use SampleFormat;
+ use {ffi, StreamParamsBuilder, StreamPrefs};
+
+ #[test]
+ fn stream_params_builder_channels() {
+ let params = StreamParamsBuilder::new().channels(2).take();
+ assert_eq!(params.channels(), 2);
+ }
+
+ #[test]
+ fn stream_params_builder_format() {
+ macro_rules! check(
+ ($($real:ident),*) => (
+ $(let params = StreamParamsBuilder::new()
+ .format(super::SampleFormat::$real)
+ .take();
+ assert_eq!(params.format(), super::SampleFormat::$real);
+ )*
+ ) );
+
+ check!(S16LE, S16BE, Float32LE, Float32BE);
+ }
+
+ #[test]
+ fn stream_params_builder_format_native_endian() {
+ let params = StreamParamsBuilder::new()
+ .format(SampleFormat::S16NE)
+ .take();
+ assert_eq!(
+ params.format(),
+ if cfg!(target_endian = "little") {
+ super::SampleFormat::S16LE
+ } else {
+ super::SampleFormat::S16BE
+ }
+ );
+
+ let params = StreamParamsBuilder::new()
+ .format(SampleFormat::Float32NE)
+ .take();
+ assert_eq!(
+ params.format(),
+ if cfg!(target_endian = "little") {
+ SampleFormat::Float32LE
+ } else {
+ SampleFormat::Float32BE
+ }
+ );
+ }
+
+ #[test]
+ fn stream_params_builder_layout() {
+ macro_rules! check(
+ ($($real:ident),*) => (
+ $(let params = StreamParamsBuilder::new()
+ .layout(super::ChannelLayout::$real)
+ .take();
+ assert_eq!(params.layout(), super::ChannelLayout::$real);
+ )*
+ ) );
+
+ check!(
+ UNDEFINED,
+ MONO,
+ MONO_LFE,
+ STEREO,
+ STEREO_LFE,
+ _3F,
+ _3F_LFE,
+ _2F1,
+ _2F1_LFE,
+ _3F1,
+ _3F1_LFE,
+ _2F2,
+ _2F2_LFE,
+ QUAD,
+ QUAD_LFE,
+ _3F2,
+ _3F2_LFE,
+ _3F2_BACK,
+ _3F2_LFE_BACK,
+ _3F3R_LFE,
+ _3F4_LFE
+ );
+ }
+
+ #[test]
+ fn stream_params_builder_rate() {
+ let params = StreamParamsBuilder::new().rate(44100).take();
+ assert_eq!(params.rate(), 44100);
+ }
+
+ #[test]
+ fn stream_params_builder_to_raw_channels() {
+ let params = StreamParamsBuilder::new().channels(2).take();
+ let raw = unsafe { &*params.as_ptr() };
+ assert_eq!(raw.channels, 2);
+ }
+
+ #[test]
+ fn stream_params_builder_to_raw_format() {
+ macro_rules! check(
+ ($($real:ident => $raw:ident),*) => (
+ $(let params = super::StreamParamsBuilder::new()
+ .format(SampleFormat::$real)
+ .take();
+ let raw = unsafe { &*params.as_ptr() };
+ assert_eq!(raw.format, ffi::$raw);
+ )*
+ ) );
+
+ check!(S16LE => CUBEB_SAMPLE_S16LE,
+ S16BE => CUBEB_SAMPLE_S16BE,
+ Float32LE => CUBEB_SAMPLE_FLOAT32LE,
+ Float32BE => CUBEB_SAMPLE_FLOAT32BE);
+ }
+
+ #[test]
+ fn stream_params_builder_format_to_raw_native_endian() {
+ let params = StreamParamsBuilder::new()
+ .format(SampleFormat::S16NE)
+ .take();
+ let raw = unsafe { &*params.as_ptr() };
+ assert_eq!(
+ raw.format,
+ if cfg!(target_endian = "little") {
+ ffi::CUBEB_SAMPLE_S16LE
+ } else {
+ ffi::CUBEB_SAMPLE_S16BE
+ }
+ );
+
+ let params = StreamParamsBuilder::new()
+ .format(SampleFormat::Float32NE)
+ .take();
+ let raw = unsafe { &*params.as_ptr() };
+ assert_eq!(
+ raw.format,
+ if cfg!(target_endian = "little") {
+ ffi::CUBEB_SAMPLE_FLOAT32LE
+ } else {
+ ffi::CUBEB_SAMPLE_FLOAT32BE
+ }
+ );
+ }
+
+ #[test]
+ fn stream_params_builder_to_raw_layout() {
+ macro_rules! check(
+ ($($real:ident => $raw:ident),*) => (
+ $(let params = super::StreamParamsBuilder::new()
+ .layout(super::ChannelLayout::$real)
+ .take();
+ let raw = unsafe { &*params.as_ptr() };
+ assert_eq!(raw.layout, ffi::$raw);
+ )*
+ ) );
+
+ check!(UNDEFINED => CUBEB_LAYOUT_UNDEFINED,
+ MONO => CUBEB_LAYOUT_MONO,
+ MONO_LFE => CUBEB_LAYOUT_MONO_LFE,
+ STEREO => CUBEB_LAYOUT_STEREO,
+ STEREO_LFE => CUBEB_LAYOUT_STEREO_LFE,
+ _3F => CUBEB_LAYOUT_3F,
+ _3F_LFE => CUBEB_LAYOUT_3F_LFE,
+ _2F1 => CUBEB_LAYOUT_2F1,
+ _2F1_LFE=> CUBEB_LAYOUT_2F1_LFE,
+ _3F1 => CUBEB_LAYOUT_3F1,
+ _3F1_LFE => CUBEB_LAYOUT_3F1_LFE,
+ _2F2 => CUBEB_LAYOUT_2F2,
+ _2F2_LFE => CUBEB_LAYOUT_2F2_LFE,
+ QUAD => CUBEB_LAYOUT_QUAD,
+ QUAD_LFE => CUBEB_LAYOUT_QUAD_LFE,
+ _3F2 => CUBEB_LAYOUT_3F2,
+ _3F2_LFE => CUBEB_LAYOUT_3F2_LFE,
+ _3F2_BACK => CUBEB_LAYOUT_3F2_BACK,
+ _3F2_LFE_BACK => CUBEB_LAYOUT_3F2_LFE_BACK,
+ _3F3R_LFE => CUBEB_LAYOUT_3F3R_LFE,
+ _3F4_LFE => CUBEB_LAYOUT_3F4_LFE);
+ }
+
+ #[test]
+ fn stream_params_builder_to_raw_rate() {
+ let params = StreamParamsBuilder::new().rate(44100).take();
+ let raw = unsafe { &*params.as_ptr() };
+ assert_eq!(raw.rate, 44100);
+ }
+
+ #[test]
+ fn stream_params_builder_prefs_default() {
+ let params = StreamParamsBuilder::new().take();
+ assert_eq!(params.prefs(), StreamPrefs::NONE);
+ }
+
+ #[test]
+ fn stream_params_builder_prefs() {
+ let params = StreamParamsBuilder::new()
+ .prefs(StreamPrefs::LOOPBACK)
+ .take();
+ assert_eq!(params.prefs(), StreamPrefs::LOOPBACK);
+ }
+}
diff --git a/third_party/rust/cubeb-core/src/call.rs b/third_party/rust/cubeb-core/src/call.rs
new file mode 100644
index 0000000000..4a78b054e9
--- /dev/null
+++ b/third_party/rust/cubeb-core/src/call.rs
@@ -0,0 +1,21 @@
+// Copyright © 2017-2018 Mozilla Foundation
+//
+// This program is made available under an ISC-style license. See the
+// accompanying file LICENSE for details.
+#![macro_use]
+
+use std::os::raw::c_int;
+use Error;
+
+pub fn cvt_r(ret: c_int) -> Result<(), Error> {
+ match ret {
+ n if n < 0 => Err(Error::from_raw(n)),
+ _ => Ok(()),
+ }
+}
+
+macro_rules! call {
+ (ffi::$p:ident ($($e:expr),*)) => ({
+ ::call::cvt_r(ffi::$p($($e),*))
+ })
+}
diff --git a/third_party/rust/cubeb-core/src/channel.rs b/third_party/rust/cubeb-core/src/channel.rs
new file mode 100644
index 0000000000..800c475e74
--- /dev/null
+++ b/third_party/rust/cubeb-core/src/channel.rs
@@ -0,0 +1,150 @@
+// Copyright © 2017-2018 Mozilla Foundation
+//
+// This program is made available under an ISC-style license. See the
+// accompanying file LICENSE for details.
+
+use ffi;
+
+bitflags! {
+ /// Some common layout definitions
+ pub struct ChannelLayout: ffi::cubeb_channel_layout {
+ ///
+ const FRONT_LEFT = ffi::CHANNEL_FRONT_LEFT;
+ const FRONT_RIGHT = ffi::CHANNEL_FRONT_RIGHT;
+ const FRONT_CENTER = ffi::CHANNEL_FRONT_CENTER;
+ const LOW_FREQUENCY = ffi::CHANNEL_LOW_FREQUENCY;
+ const BACK_LEFT = ffi::CHANNEL_BACK_LEFT;
+ const BACK_RIGHT = ffi::CHANNEL_BACK_RIGHT;
+ const FRONT_LEFT_OF_CENTER = ffi::CHANNEL_FRONT_LEFT_OF_CENTER;
+ const FRONT_RIGHT_OF_CENTER = ffi::CHANNEL_FRONT_RIGHT_OF_CENTER;
+ const BACK_CENTER = ffi::CHANNEL_BACK_CENTER;
+ const SIDE_LEFT = ffi::CHANNEL_SIDE_LEFT;
+ const SIDE_RIGHT = ffi::CHANNEL_SIDE_RIGHT;
+ const TOP_CENTER = ffi::CHANNEL_TOP_CENTER;
+ const TOP_FRONT_LEFT = ffi::CHANNEL_TOP_FRONT_LEFT;
+ const TOP_FRONT_CENTER = ffi::CHANNEL_TOP_FRONT_CENTER;
+ const TOP_FRONT_RIGHT = ffi::CHANNEL_TOP_FRONT_RIGHT;
+ const TOP_BACK_LEFT = ffi::CHANNEL_TOP_BACK_LEFT;
+ const TOP_BACK_CENTER = ffi::CHANNEL_TOP_BACK_CENTER;
+ const TOP_BACK_RIGHT = ffi::CHANNEL_TOP_BACK_RIGHT;
+ }
+}
+
+macro_rules! bits {
+ ($($x:ident => $y:ident),*) => {
+ $(pub const $x: ChannelLayout = ChannelLayout::from_bits_truncate(ffi::$y);)*
+ }
+}
+
+impl ChannelLayout {
+ bits!(UNDEFINED => CUBEB_LAYOUT_UNDEFINED,
+ MONO => CUBEB_LAYOUT_MONO,
+ MONO_LFE => CUBEB_LAYOUT_MONO_LFE,
+ STEREO => CUBEB_LAYOUT_STEREO,
+ STEREO_LFE => CUBEB_LAYOUT_STEREO_LFE,
+ _3F => CUBEB_LAYOUT_3F,
+ _3F_LFE => CUBEB_LAYOUT_3F_LFE,
+ _2F1 => CUBEB_LAYOUT_2F1,
+ _2F1_LFE => CUBEB_LAYOUT_2F1_LFE,
+ _3F1 => CUBEB_LAYOUT_3F1,
+ _3F1_LFE => CUBEB_LAYOUT_3F1_LFE,
+ _2F2 => CUBEB_LAYOUT_2F2,
+ _2F2_LFE => CUBEB_LAYOUT_2F2_LFE,
+ QUAD => CUBEB_LAYOUT_QUAD,
+ QUAD_LFE => CUBEB_LAYOUT_QUAD_LFE,
+ _3F2 => CUBEB_LAYOUT_3F2,
+ _3F2_LFE => CUBEB_LAYOUT_3F2_LFE,
+ _3F2_BACK => CUBEB_LAYOUT_3F2_BACK,
+ _3F2_LFE_BACK => CUBEB_LAYOUT_3F2_LFE_BACK,
+ _3F3R_LFE => CUBEB_LAYOUT_3F3R_LFE,
+ _3F4_LFE => CUBEB_LAYOUT_3F4_LFE
+ );
+}
+
+impl From<ffi::cubeb_channel> for ChannelLayout {
+ fn from(x: ffi::cubeb_channel) -> Self {
+ ChannelLayout::from_bits_truncate(x)
+ }
+}
+
+impl From<ChannelLayout> for ffi::cubeb_channel {
+ fn from(x: ChannelLayout) -> Self {
+ x.bits()
+ }
+}
+
+impl ChannelLayout {
+ pub fn num_channels(&self) -> u32 {
+ unsafe { ffi::cubeb_channel_layout_nb_channels(self.bits()) }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use ffi;
+
+ #[test]
+ fn channel_layout_from_raw() {
+ macro_rules! check(
+ ($($raw:ident => $real:ident),*) => (
+ $(let x = super::ChannelLayout::from(ffi::$raw);
+ assert_eq!(x, super::ChannelLayout::$real);
+ )*
+ ) );
+
+ check!(CUBEB_LAYOUT_UNDEFINED => UNDEFINED,
+ CUBEB_LAYOUT_MONO => MONO,
+ CUBEB_LAYOUT_MONO_LFE => MONO_LFE,
+ CUBEB_LAYOUT_STEREO => STEREO,
+ CUBEB_LAYOUT_STEREO_LFE => STEREO_LFE,
+ CUBEB_LAYOUT_3F => _3F,
+ CUBEB_LAYOUT_3F_LFE => _3F_LFE,
+ CUBEB_LAYOUT_2F1 => _2F1,
+ CUBEB_LAYOUT_2F1_LFE => _2F1_LFE,
+ CUBEB_LAYOUT_3F1 => _3F1,
+ CUBEB_LAYOUT_3F1_LFE => _3F1_LFE,
+ CUBEB_LAYOUT_2F2 => _2F2,
+ CUBEB_LAYOUT_2F2_LFE => _2F2_LFE,
+ CUBEB_LAYOUT_QUAD => QUAD,
+ CUBEB_LAYOUT_QUAD_LFE => QUAD_LFE,
+ CUBEB_LAYOUT_3F2 => _3F2,
+ CUBEB_LAYOUT_3F2_LFE => _3F2_LFE,
+ CUBEB_LAYOUT_3F2_BACK => _3F2_BACK,
+ CUBEB_LAYOUT_3F2_LFE_BACK => _3F2_LFE_BACK,
+ CUBEB_LAYOUT_3F3R_LFE => _3F3R_LFE,
+ CUBEB_LAYOUT_3F4_LFE => _3F4_LFE);
+ }
+
+ #[test]
+ fn channel_layout_into_raw() {
+ macro_rules! check(
+ ($($real:ident => $raw:ident),*) => (
+ $(let x = super::ChannelLayout::$real;
+ let x: ffi::cubeb_channel_layout = x.into();
+ assert_eq!(x, ffi::$raw);
+ )*
+ ) );
+
+ check!(UNDEFINED => CUBEB_LAYOUT_UNDEFINED,
+ MONO => CUBEB_LAYOUT_MONO,
+ MONO_LFE => CUBEB_LAYOUT_MONO_LFE,
+ STEREO => CUBEB_LAYOUT_STEREO,
+ STEREO_LFE => CUBEB_LAYOUT_STEREO_LFE,
+ _3F => CUBEB_LAYOUT_3F,
+ _3F_LFE => CUBEB_LAYOUT_3F_LFE,
+ _2F1 => CUBEB_LAYOUT_2F1,
+ _2F1_LFE=> CUBEB_LAYOUT_2F1_LFE,
+ _3F1 => CUBEB_LAYOUT_3F1,
+ _3F1_LFE => CUBEB_LAYOUT_3F1_LFE,
+ _2F2 => CUBEB_LAYOUT_2F2,
+ _2F2_LFE => CUBEB_LAYOUT_2F2_LFE,
+ QUAD => CUBEB_LAYOUT_QUAD,
+ QUAD_LFE => CUBEB_LAYOUT_QUAD_LFE,
+ _3F2 => CUBEB_LAYOUT_3F2,
+ _3F2_LFE => CUBEB_LAYOUT_3F2_LFE,
+ _3F2_BACK => CUBEB_LAYOUT_3F2_BACK,
+ _3F2_LFE_BACK => CUBEB_LAYOUT_3F2_LFE_BACK,
+ _3F3R_LFE => CUBEB_LAYOUT_3F3R_LFE,
+ _3F4_LFE => CUBEB_LAYOUT_3F4_LFE);
+ }
+}
diff --git a/third_party/rust/cubeb-core/src/context.rs b/third_party/rust/cubeb-core/src/context.rs
new file mode 100644
index 0000000000..84c362789a
--- /dev/null
+++ b/third_party/rust/cubeb-core/src/context.rs
@@ -0,0 +1,164 @@
+// 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<Context> {
+ 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<u32> {
+ 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<u32> {
+ 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<u32> {
+ 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<InputProcessingParams> {
+ 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.
+ #[cfg_attr(feature = "cargo-clippy", 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);
+
+ 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<DeviceCollection> {
+ 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/third_party/rust/cubeb-core/src/device.rs b/third_party/rust/cubeb-core/src/device.rs
new file mode 100644
index 0000000000..4854aa208f
--- /dev/null
+++ b/third_party/rust/cubeb-core/src/device.rs
@@ -0,0 +1,239 @@
+// 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::str;
+use util::opt_bytes;
+
+/// The state of a device.
+#[derive(PartialEq, Eq, Clone, Debug, Copy)]
+pub enum DeviceState {
+ /// The device has been disabled at the system level.
+ Disabled,
+ /// The device is enabled, but nothing is plugged into it.
+ Unplugged,
+ /// The device is enabled.
+ Enabled,
+}
+
+bitflags! {
+ /// Architecture specific sample type.
+ pub struct DeviceFormat: ffi::cubeb_device_fmt {
+ const S16LE = ffi::CUBEB_DEVICE_FMT_S16LE;
+ const S16BE = ffi::CUBEB_DEVICE_FMT_S16BE;
+ const F32LE = ffi::CUBEB_DEVICE_FMT_F32LE;
+ const F32BE = ffi::CUBEB_DEVICE_FMT_F32BE;
+ }
+}
+
+bitflags! {
+ /// Channel type for a `cubeb_stream`. Depending on the backend and platform
+ /// used, this can control inter-stream interruption, ducking, and volume
+ /// control.
+ pub struct DevicePref: ffi::cubeb_device_pref {
+ const MULTIMEDIA = ffi::CUBEB_DEVICE_PREF_MULTIMEDIA;
+ const VOICE = ffi::CUBEB_DEVICE_PREF_VOICE;
+ const NOTIFICATION = ffi::CUBEB_DEVICE_PREF_NOTIFICATION;
+ const ALL = ffi::CUBEB_DEVICE_PREF_ALL;
+ }
+}
+
+impl DevicePref {
+ pub const NONE: Self = Self::empty();
+}
+
+bitflags! {
+ /// Whether a particular device is an input device (e.g. a microphone), or an
+ /// output device (e.g. headphones).
+ pub struct DeviceType: ffi::cubeb_device_type {
+ const INPUT = ffi::CUBEB_DEVICE_TYPE_INPUT as _;
+ const OUTPUT = ffi::CUBEB_DEVICE_TYPE_OUTPUT as _;
+ }
+}
+
+impl DeviceType {
+ pub const UNKNOWN: Self = Self::empty();
+}
+
+/// An opaque handle used to refer to a particular input or output device
+/// across calls.
+pub type DeviceId = ffi::cubeb_devid;
+
+ffi_type_heap! {
+ /// Audio device description
+ type CType = ffi::cubeb_device;
+ #[derive(Debug)]
+ pub struct Device;
+ pub struct DeviceRef;
+}
+
+impl DeviceRef {
+ fn get_ref(&self) -> &ffi::cubeb_device {
+ unsafe { &*self.as_ptr() }
+ }
+
+ /// Gets the output device name.
+ ///
+ /// May return `None` if there is no output device.
+ pub fn output_name(&self) -> Option<&str> {
+ self.output_name_bytes().map(|b| str::from_utf8(b).unwrap())
+ }
+
+ pub fn output_name_bytes(&self) -> Option<&[u8]> {
+ unsafe { opt_bytes(self.get_ref().output_name) }
+ }
+
+ /// Gets the input device name.
+ ///
+ /// May return `None` if there is no input device.
+ pub fn input_name(&self) -> Option<&str> {
+ self.input_name_bytes().map(|b| str::from_utf8(b).unwrap())
+ }
+
+ pub fn input_name_bytes(&self) -> Option<&[u8]> {
+ unsafe { opt_bytes(self.get_ref().input_name) }
+ }
+}
+
+ffi_type_stack! {
+ /// This structure holds the characteristics of an input or output
+ /// audio device. It is obtained using `enumerate_devices`, which
+ /// returns these structures via `device_collection` and must be
+ /// destroyed via `device_collection_destroy`.
+ type CType = ffi::cubeb_device_info;
+ pub struct DeviceInfo;
+ pub struct DeviceInfoRef;
+}
+
+impl DeviceInfoRef {
+ fn get_ref(&self) -> &ffi::cubeb_device_info {
+ unsafe { &*self.as_ptr() }
+ }
+
+ /// Device identifier handle.
+ pub fn devid(&self) -> DeviceId {
+ self.get_ref().devid
+ }
+
+ /// Device identifier which might be presented in a UI.
+ pub fn device_id(&self) -> Option<&str> {
+ self.device_id_bytes().map(|b| str::from_utf8(b).unwrap())
+ }
+
+ pub fn device_id_bytes(&self) -> Option<&[u8]> {
+ unsafe { opt_bytes(self.get_ref().device_id) }
+ }
+
+ /// Friendly device name which might be presented in a UI.
+ pub fn friendly_name(&self) -> Option<&str> {
+ self.friendly_name_bytes()
+ .map(|b| str::from_utf8(b).unwrap())
+ }
+
+ pub fn friendly_name_bytes(&self) -> Option<&[u8]> {
+ unsafe { opt_bytes(self.get_ref().friendly_name) }
+ }
+
+ /// Two devices have the same group identifier if they belong to
+ /// the same physical device; for example a headset and
+ /// microphone.
+ pub fn group_id(&self) -> Option<&str> {
+ self.group_id_bytes().map(|b| str::from_utf8(b).unwrap())
+ }
+
+ pub fn group_id_bytes(&self) -> Option<&[u8]> {
+ unsafe { opt_bytes(self.get_ref().group_id) }
+ }
+
+ /// Optional vendor name, may be None.
+ pub fn vendor_name(&self) -> Option<&str> {
+ self.vendor_name_bytes().map(|b| str::from_utf8(b).unwrap())
+ }
+
+ pub fn vendor_name_bytes(&self) -> Option<&[u8]> {
+ unsafe { opt_bytes(self.get_ref().vendor_name) }
+ }
+
+ /// Type of device (Input/Output).
+ pub fn device_type(&self) -> DeviceType {
+ DeviceType::from_bits_truncate(self.get_ref().device_type)
+ }
+
+ /// State of device disabled/enabled/unplugged.
+ pub fn state(&self) -> DeviceState {
+ let state = self.get_ref().state;
+ macro_rules! check( ($($raw:ident => $real:ident),*) => (
+ $(if state == ffi::$raw {
+ DeviceState::$real
+ }) else *
+ else {
+ panic!("unknown device state: {}", state)
+ }
+ ));
+
+ check!(CUBEB_DEVICE_STATE_DISABLED => Disabled,
+ CUBEB_DEVICE_STATE_UNPLUGGED => Unplugged,
+ CUBEB_DEVICE_STATE_ENABLED => Enabled)
+ }
+
+ /// Preferred device.
+ pub fn preferred(&self) -> DevicePref {
+ DevicePref::from_bits(self.get_ref().preferred).unwrap()
+ }
+
+ /// Sample format supported.
+ pub fn format(&self) -> DeviceFormat {
+ DeviceFormat::from_bits(self.get_ref().format).unwrap()
+ }
+
+ /// The default sample format for this device.
+ pub fn default_format(&self) -> DeviceFormat {
+ DeviceFormat::from_bits(self.get_ref().default_format).unwrap()
+ }
+
+ /// Channels.
+ pub fn max_channels(&self) -> u32 {
+ self.get_ref().max_channels
+ }
+
+ /// Default/Preferred sample rate.
+ pub fn default_rate(&self) -> u32 {
+ self.get_ref().default_rate
+ }
+
+ /// Maximum sample rate supported.
+ pub fn max_rate(&self) -> u32 {
+ self.get_ref().max_rate
+ }
+
+ /// Minimum sample rate supported.
+ pub fn min_rate(&self) -> u32 {
+ self.get_ref().min_rate
+ }
+
+ /// Lowest possible latency in frames.
+ pub fn latency_lo(&self) -> u32 {
+ self.get_ref().latency_lo
+ }
+
+ /// Higest possible latency in frames.
+ pub fn latency_hi(&self) -> u32 {
+ self.get_ref().latency_hi
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use ffi::cubeb_device;
+ use Device;
+
+ #[test]
+ fn device_device_ref_same_ptr() {
+ let ptr: *mut cubeb_device = 0xDEAD_BEEF as *mut _;
+ let device = unsafe { Device::from_ptr(ptr) };
+ assert_eq!(device.as_ptr(), ptr);
+ assert_eq!(device.as_ptr(), device.as_ref().as_ptr());
+ }
+}
diff --git a/third_party/rust/cubeb-core/src/device_collection.rs b/third_party/rust/cubeb-core/src/device_collection.rs
new file mode 100644
index 0000000000..733c068f14
--- /dev/null
+++ b/third_party/rust/cubeb-core/src/device_collection.rs
@@ -0,0 +1,99 @@
+// 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<'ctx> DeviceCollection<'ctx> {
+ 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<'ctx> Drop for DeviceCollection<'ctx> {
+ fn drop(&mut self) {
+ unsafe {
+ let _ = call!(ffi::cubeb_device_collection_destroy(
+ self.1.as_ptr(),
+ &mut self.0
+ ));
+ }
+ }
+}
+
+impl<'ctx> ::std::ops::Deref for DeviceCollection<'ctx> {
+ type Target = DeviceCollectionRef;
+
+ #[inline]
+ fn deref(&self) -> &DeviceCollectionRef {
+ let ptr = &self.0 as *const CType as *mut CType;
+ unsafe { DeviceCollectionRef::from_ptr(ptr) }
+ }
+}
+
+impl<'ctx> ::std::convert::AsRef<DeviceCollectionRef> for DeviceCollection<'ctx> {
+ #[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/third_party/rust/cubeb-core/src/error.rs b/third_party/rust/cubeb-core/src/error.rs
new file mode 100644
index 0000000000..37c90d2b24
--- /dev/null
+++ b/third_party/rust/cubeb-core/src/error.rs
@@ -0,0 +1,162 @@
+use ffi;
+use std::ffi::NulError;
+use std::os::raw::c_int;
+use std::{error, fmt};
+
+pub type Result<T> = ::std::result::Result<T, Error>;
+
+/// An enumeration of possible errors that can happen when working with cubeb.
+#[derive(PartialEq, Eq, Clone, Debug, Copy)]
+pub enum ErrorCode {
+ /// GenericError
+ Error,
+ /// Requested format is invalid
+ InvalidFormat,
+ /// Requested parameter is invalid
+ InvalidParameter,
+ /// Requested operation is not supported
+ NotSupported,
+ /// Requested device is unavailable
+ DeviceUnavailable,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct Error {
+ code: ErrorCode,
+}
+
+impl Error {
+ #[allow(clippy::self_named_constructors)]
+ pub fn error() -> Self {
+ Error {
+ code: ErrorCode::Error,
+ }
+ }
+ pub fn invalid_format() -> Self {
+ Error {
+ code: ErrorCode::InvalidFormat,
+ }
+ }
+ pub fn invalid_parameter() -> Self {
+ Error {
+ code: ErrorCode::InvalidParameter,
+ }
+ }
+ pub fn not_supported() -> Self {
+ Error {
+ code: ErrorCode::NotSupported,
+ }
+ }
+ pub fn device_unavailable() -> Self {
+ Error {
+ code: ErrorCode::DeviceUnavailable,
+ }
+ }
+
+ pub fn from_raw(code: c_int) -> Error {
+ let code = match code {
+ ffi::CUBEB_ERROR_INVALID_FORMAT => ErrorCode::InvalidFormat,
+ ffi::CUBEB_ERROR_INVALID_PARAMETER => ErrorCode::InvalidParameter,
+ ffi::CUBEB_ERROR_NOT_SUPPORTED => ErrorCode::NotSupported,
+ ffi::CUBEB_ERROR_DEVICE_UNAVAILABLE => ErrorCode::DeviceUnavailable,
+ // Everything else is just the generic error
+ _ => ErrorCode::Error,
+ };
+
+ Error { code }
+ }
+
+ pub fn code(&self) -> ErrorCode {
+ self.code
+ }
+
+ pub fn raw_code(&self) -> c_int {
+ match self.code {
+ ErrorCode::Error => ffi::CUBEB_ERROR,
+ ErrorCode::InvalidFormat => ffi::CUBEB_ERROR_INVALID_FORMAT,
+ ErrorCode::InvalidParameter => ffi::CUBEB_ERROR_INVALID_PARAMETER,
+ ErrorCode::NotSupported => ffi::CUBEB_ERROR_NOT_SUPPORTED,
+ ErrorCode::DeviceUnavailable => ffi::CUBEB_ERROR_DEVICE_UNAVAILABLE,
+ }
+ }
+}
+
+impl Default for Error {
+ fn default() -> Self {
+ Error::error()
+ }
+}
+
+impl error::Error for Error {
+ fn description(&self) -> &str {
+ match self.code {
+ ErrorCode::Error => "Error",
+ ErrorCode::InvalidFormat => "Invalid format",
+ ErrorCode::InvalidParameter => "Invalid parameter",
+ ErrorCode::NotSupported => "Not supported",
+ ErrorCode::DeviceUnavailable => "Device unavailable",
+ }
+ }
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{:?}", self)
+ }
+}
+
+impl From<ErrorCode> for Error {
+ fn from(code: ErrorCode) -> Error {
+ Error { code }
+ }
+}
+
+impl From<NulError> for Error {
+ fn from(_: NulError) -> Error {
+ Error::from_raw(ffi::CUBEB_ERROR)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use ffi;
+
+ #[test]
+ fn test_from_raw() {
+ macro_rules! test {
+ ( $($raw:ident => $err:ident),* ) => {{
+ $(
+ let e = Error::from_raw(ffi::$raw);
+ assert_eq!(e.raw_code(), ffi::$raw);
+ assert_eq!(e.code(), ErrorCode::$err);
+ )*
+ }};
+ }
+ test!(CUBEB_ERROR => Error,
+ CUBEB_ERROR_INVALID_FORMAT => InvalidFormat,
+ CUBEB_ERROR_INVALID_PARAMETER => InvalidParameter,
+ CUBEB_ERROR_NOT_SUPPORTED => NotSupported,
+ CUBEB_ERROR_DEVICE_UNAVAILABLE => DeviceUnavailable
+ );
+ }
+
+ #[test]
+ fn test_from_error_code() {
+ macro_rules! test {
+ ( $($raw:ident => $err:ident),* ) => {{
+ $(
+ let e = Error::from(ErrorCode::$err);
+ assert_eq!(e.raw_code(), ffi::$raw);
+ assert_eq!(e.code(), ErrorCode::$err);
+ )*
+ }};
+ }
+ test!(CUBEB_ERROR => Error,
+ CUBEB_ERROR_INVALID_FORMAT => InvalidFormat,
+ CUBEB_ERROR_INVALID_PARAMETER => InvalidParameter,
+ CUBEB_ERROR_NOT_SUPPORTED => NotSupported,
+ CUBEB_ERROR_DEVICE_UNAVAILABLE => DeviceUnavailable
+ );
+ }
+}
diff --git a/third_party/rust/cubeb-core/src/ffi_types.rs b/third_party/rust/cubeb-core/src/ffi_types.rs
new file mode 100644
index 0000000000..221dccfa28
--- /dev/null
+++ b/third_party/rust/cubeb-core/src/ffi_types.rs
@@ -0,0 +1,242 @@
+// Copyright © 2017-2018 Mozilla Foundation
+//
+// This program is made available under an ISC-style license. See the
+// accompanying file LICENSE for details.
+
+use std::cell::UnsafeCell;
+pub struct Opaque(UnsafeCell<()>);
+
+/// Generate a newtype wrapper `$owned` and reference wrapper
+/// `$borrowed` around a POD FFI type that lives on the heap.
+macro_rules! ffi_type_heap {
+ (
+ $(#[$impl_attr:meta])*
+ type CType = $ctype:ty;
+ $(fn drop = $drop:expr;)*
+ $(fn clone = $clone:expr;)*
+ $(#[$owned_attr:meta])*
+ pub struct $owned:ident;
+ $(#[$borrowed_attr:meta])*
+ pub struct $borrowed:ident;
+ ) => {
+ $(#[$owned_attr])*
+ pub struct $owned(*mut $ctype);
+
+ impl $owned {
+ /// # 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(ptr: *mut $ctype) -> $owned {
+ $owned(ptr)
+ }
+
+ #[inline]
+ pub fn as_ptr(&self) -> *mut $ctype {
+ self.0
+ }
+ }
+
+ $(
+ impl Drop for $owned {
+ #[inline]
+ fn drop(&mut self) {
+ unsafe { $drop(self.0) }
+ }
+ }
+ )*
+
+ $(
+ impl Clone for $owned {
+ #[inline]
+ fn clone(&self) -> $owned {
+ unsafe {
+ let handle: *mut $ctype = $clone(self.0);
+ $owned::from_ptr(handle)
+ }
+ }
+ }
+
+ impl ::std::borrow::ToOwned for $borrowed {
+ type Owned = $owned;
+ #[inline]
+ fn to_owned(&self) -> $owned {
+ unsafe {
+ let handle: *mut $ctype = $clone(self.as_ptr());
+ $owned::from_ptr(handle)
+ }
+ }
+ }
+ )*
+
+ impl ::std::ops::Deref for $owned {
+ type Target = $borrowed;
+
+ #[inline]
+ fn deref(&self) -> &$borrowed {
+ unsafe { $borrowed::from_ptr(self.0) }
+ }
+ }
+
+ impl ::std::ops::DerefMut for $owned {
+ #[inline]
+ fn deref_mut(&mut self) -> &mut $borrowed {
+ unsafe { $borrowed::from_ptr_mut(self.0) }
+ }
+ }
+
+ impl ::std::borrow::Borrow<$borrowed> for $owned {
+ #[inline]
+ fn borrow(&self) -> &$borrowed {
+ &**self
+ }
+ }
+
+ impl ::std::convert::AsRef<$borrowed> for $owned {
+ #[inline]
+ fn as_ref(&self) -> &$borrowed {
+ &**self
+ }
+ }
+
+ $(#[$borrowed_attr])*
+ pub struct $borrowed($crate::ffi_types::Opaque);
+
+ impl $borrowed {
+ /// # 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 $borrowed {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ let ptr = self as *const $borrowed as usize;
+ f.debug_tuple(stringify!($borrowed))
+ .field(&ptr)
+ .finish()
+ }
+ }
+ }
+}
+
+/// Generate a newtype wrapper `$owned` and reference wrapper
+/// `$borrowed` around a POD FFI type that lives on the stack.
+macro_rules! ffi_type_stack {
+ ($(#[$impl_attr:meta])*
+ type CType = $ctype:ty;
+ $(#[$owned_attr:meta])*
+ pub struct $owned:ident;
+ $(#[$borrowed_attr:meta])*
+ pub struct $borrowed:ident;
+ ) => {
+ $(#[$owned_attr])*
+ pub struct $owned($ctype);
+
+ impl $owned {
+ pub fn as_ptr(&self) -> *mut $ctype {
+ &self.0 as *const $ctype as *mut $ctype
+ }
+ }
+
+ impl Default for $owned {
+ fn default() -> $owned {
+ $owned(Default::default())
+ }
+ }
+
+ impl From<$ctype> for $owned {
+ fn from(x: $ctype) -> $owned {
+ $owned(x)
+ }
+ }
+
+ impl ::std::ops::Deref for $owned {
+ type Target = $borrowed;
+
+ #[inline]
+ fn deref(&self) -> &$borrowed {
+ let ptr = &self.0 as *const $ctype as *mut $ctype;
+ unsafe { $borrowed::from_ptr(ptr) }
+ }
+ }
+
+ impl ::std::ops::DerefMut for $owned {
+ #[inline]
+ fn deref_mut(&mut self) -> &mut $borrowed {
+ let ptr = &self.0 as *const $ctype as *mut $ctype;
+ unsafe { $borrowed::from_ptr_mut(ptr) }
+ }
+ }
+
+ impl ::std::borrow::Borrow<$borrowed> for $owned {
+ #[inline]
+ fn borrow(&self) -> &$borrowed {
+ &**self
+ }
+ }
+
+ impl ::std::convert::AsRef<$borrowed> for $owned {
+ #[inline]
+ fn as_ref(&self) -> &$borrowed {
+ &**self
+ }
+ }
+
+ $(#[$borrowed_attr])*
+ pub struct $borrowed($crate::ffi_types::Opaque);
+
+ impl $borrowed {
+ /// # 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 $borrowed {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ let ptr = self as *const $borrowed as usize;
+ f.debug_tuple(stringify!($borrowed))
+ .field(&ptr)
+ .finish()
+ }
+ }
+ }
+}
diff --git a/third_party/rust/cubeb-core/src/format.rs b/third_party/rust/cubeb-core/src/format.rs
new file mode 100644
index 0000000000..10dff83fe4
--- /dev/null
+++ b/third_party/rust/cubeb-core/src/format.rs
@@ -0,0 +1,44 @@
+// Copyright © 2017-2018 Mozilla Foundation
+//
+// This program is made available under an ISC-style license. See the
+// accompanying file LICENSE for details.
+
+use ffi;
+
+#[derive(PartialEq, Eq, Clone, Debug, Copy)]
+pub enum SampleFormat {
+ S16LE,
+ S16BE,
+ Float32LE,
+ Float32BE,
+ // Maps to the platform native endian
+ S16NE,
+ Float32NE,
+}
+
+impl From<ffi::cubeb_sample_format> for SampleFormat {
+ fn from(x: ffi::cubeb_sample_format) -> SampleFormat {
+ match x {
+ ffi::CUBEB_SAMPLE_S16LE => SampleFormat::S16LE,
+ ffi::CUBEB_SAMPLE_S16BE => SampleFormat::S16BE,
+ ffi::CUBEB_SAMPLE_FLOAT32LE => SampleFormat::Float32LE,
+ ffi::CUBEB_SAMPLE_FLOAT32BE => SampleFormat::Float32BE,
+ // TODO: Implement TryFrom
+ _ => SampleFormat::S16NE,
+ }
+ }
+}
+
+impl From<SampleFormat> for ffi::cubeb_sample_format {
+ fn from(x: SampleFormat) -> Self {
+ use SampleFormat::*;
+ match x {
+ S16LE => ffi::CUBEB_SAMPLE_S16LE,
+ S16BE => ffi::CUBEB_SAMPLE_S16BE,
+ Float32LE => ffi::CUBEB_SAMPLE_FLOAT32LE,
+ Float32BE => ffi::CUBEB_SAMPLE_FLOAT32BE,
+ S16NE => ffi::CUBEB_SAMPLE_S16NE,
+ Float32NE => ffi::CUBEB_SAMPLE_FLOAT32NE,
+ }
+ }
+}
diff --git a/third_party/rust/cubeb-core/src/lib.rs b/third_party/rust/cubeb-core/src/lib.rs
new file mode 100644
index 0000000000..ddcf674c44
--- /dev/null
+++ b/third_party/rust/cubeb-core/src/lib.rs
@@ -0,0 +1,38 @@
+// Copyright © 2017-2018 Mozilla Foundation
+//
+// This program is made available under an ISC-style license. See the
+// accompanying file LICENSE for details.
+
+#[macro_use]
+extern crate bitflags;
+extern crate cubeb_sys;
+
+#[macro_use]
+mod ffi_types;
+
+mod call;
+
+mod builders;
+mod channel;
+mod context;
+mod device;
+mod device_collection;
+mod error;
+mod format;
+mod log;
+mod stream;
+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::*;
+pub use stream::*;
+
+pub mod ffi {
+ pub use cubeb_sys::*;
+}
diff --git a/third_party/rust/cubeb-core/src/log.rs b/third_party/rust/cubeb-core/src/log.rs
new file mode 100644
index 0000000000..6bf6ba914d
--- /dev/null
+++ b/third_party/rust/cubeb-core/src/log.rs
@@ -0,0 +1,42 @@
+// Copyright © 2017-2018 Mozilla Foundation
+//
+// This program is made available under an ISC-style license. See the
+// accompanying file LICENSE for details.
+
+use ffi;
+
+/// Level (verbosity) of logging for a particular cubeb context.
+#[derive(PartialEq, Eq, Clone, Debug, Copy, PartialOrd, Ord)]
+pub enum LogLevel {
+ /// Logging disabled
+ Disabled,
+ /// Logging lifetime operation (creation/destruction).
+ Normal,
+ /// Verbose logging of callbacks, can have performance implications.
+ Verbose,
+}
+
+impl From<ffi::cubeb_log_level> for LogLevel {
+ fn from(x: ffi::cubeb_log_level) -> Self {
+ use LogLevel::*;
+ match x {
+ ffi::CUBEB_LOG_NORMAL => Normal,
+ ffi::CUBEB_LOG_VERBOSE => Verbose,
+ _ => Disabled,
+ }
+ }
+}
+
+pub fn log_enabled() -> bool {
+ unsafe { ffi::cubeb_log_get_level() != LogLevel::Disabled as _ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_logging_disabled_by_default() {
+ assert!(!log_enabled());
+ }
+}
diff --git a/third_party/rust/cubeb-core/src/stream.rs b/third_party/rust/cubeb-core/src/stream.rs
new file mode 100644
index 0000000000..49d3b1f482
--- /dev/null
+++ b/third_party/rust/cubeb-core/src/stream.rs
@@ -0,0 +1,360 @@
+// 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_int, c_void};
+use std::ptr;
+use {ChannelLayout, DeviceRef, Result, SampleFormat};
+
+/// Stream states signaled via `state_callback`.
+#[derive(PartialEq, Eq, Clone, Debug, Copy)]
+pub enum State {
+ /// Stream started.
+ Started,
+ /// Stream stopped.
+ Stopped,
+ /// Stream drained.
+ Drained,
+ /// Stream disabled due to error.
+ Error,
+}
+
+impl From<ffi::cubeb_state> for State {
+ fn from(x: ffi::cubeb_state) -> Self {
+ use State::*;
+ match x {
+ ffi::CUBEB_STATE_STARTED => Started,
+ ffi::CUBEB_STATE_STOPPED => Stopped,
+ ffi::CUBEB_STATE_DRAINED => Drained,
+ _ => Error,
+ }
+ }
+}
+
+impl From<State> for ffi::cubeb_state {
+ fn from(x: State) -> Self {
+ use State::*;
+ match x {
+ Started => ffi::CUBEB_STATE_STARTED,
+ Stopped => ffi::CUBEB_STATE_STOPPED,
+ Drained => ffi::CUBEB_STATE_DRAINED,
+ Error => ffi::CUBEB_STATE_ERROR,
+ }
+ }
+}
+
+bitflags! {
+ /// Miscellaneous stream preferences.
+ pub struct StreamPrefs: ffi::cubeb_stream_prefs {
+ const LOOPBACK = ffi::CUBEB_STREAM_PREF_LOOPBACK;
+ const DISABLE_DEVICE_SWITCHING = ffi::CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING;
+ const VOICE = ffi::CUBEB_STREAM_PREF_VOICE;
+ }
+}
+
+impl StreamPrefs {
+ pub const NONE: Self = Self::empty();
+}
+
+bitflags! {
+ /// Input stream processing parameters.
+ pub struct InputProcessingParams: ffi::cubeb_input_processing_params {
+ const ECHO_CANCELLATION = ffi::CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION;
+ const NOISE_SUPPRESSION = ffi::CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION;
+ const AUTOMATIC_GAIN_CONTROL = ffi::CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL;
+ const VOICE_ISOLATION = ffi::CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION;
+ }
+}
+
+impl InputProcessingParams {
+ pub const NONE: Self = Self::empty();
+}
+
+ffi_type_stack! {
+ /// Stream format initialization parameters.
+ type CType = ffi::cubeb_stream_params;
+ #[derive(Debug)]
+ pub struct StreamParams;
+ pub struct StreamParamsRef;
+}
+
+impl StreamParamsRef {
+ fn get_ref(&self) -> &ffi::cubeb_stream_params {
+ unsafe { &*self.as_ptr() }
+ }
+
+ pub fn format(&self) -> SampleFormat {
+ use SampleFormat::*;
+ match self.get_ref().format {
+ ffi::CUBEB_SAMPLE_S16LE => S16LE,
+ ffi::CUBEB_SAMPLE_S16BE => S16BE,
+ ffi::CUBEB_SAMPLE_FLOAT32LE => Float32LE,
+ ffi::CUBEB_SAMPLE_FLOAT32BE => Float32BE,
+ x => panic!("unknown sample format: {}", x),
+ }
+ }
+
+ pub fn rate(&self) -> u32 {
+ self.get_ref().rate
+ }
+ pub fn channels(&self) -> u32 {
+ self.get_ref().channels
+ }
+
+ pub fn layout(&self) -> ChannelLayout {
+ ChannelLayout::from(self.get_ref().layout)
+ }
+
+ pub fn prefs(&self) -> StreamPrefs {
+ StreamPrefs::from_bits_truncate(self.get_ref().prefs)
+ }
+}
+
+unsafe fn wrapped_cubeb_stream_destroy(stream: *mut ffi::cubeb_stream) {
+ ffi::cubeb_stream_stop(stream);
+ ffi::cubeb_stream_destroy(stream);
+}
+
+ffi_type_heap! {
+ type CType = ffi::cubeb_stream;
+ fn drop = wrapped_cubeb_stream_destroy;
+ pub struct Stream;
+ pub struct StreamRef;
+}
+
+impl StreamRef {
+ /// Start playback.
+ pub fn start(&self) -> Result<()> {
+ unsafe { call!(ffi::cubeb_stream_start(self.as_ptr())) }
+ }
+
+ /// Stop playback.
+ pub fn stop(&self) -> Result<()> {
+ unsafe { call!(ffi::cubeb_stream_stop(self.as_ptr())) }
+ }
+
+ /// Get the current stream playback position.
+ pub fn position(&self) -> Result<u64> {
+ let mut position = 0u64;
+ unsafe {
+ call!(ffi::cubeb_stream_get_position(self.as_ptr(), &mut position))?;
+ }
+ Ok(position)
+ }
+
+ /// Get the latency for this stream, in frames. This is the number
+ /// of frames between the time cubeb acquires the data in the
+ /// callback and the listener can hear the sound.
+ pub fn latency(&self) -> Result<u32> {
+ let mut latency = 0u32;
+ unsafe {
+ call!(ffi::cubeb_stream_get_latency(self.as_ptr(), &mut latency))?;
+ }
+ Ok(latency)
+ }
+
+ /// Get the input latency for this stream, in frames. This is the number of frames between the
+ /// time the audio input device records the audio, and the cubeb callback delivers it.
+ /// This returns an error if the stream is output-only.
+ pub fn input_latency(&self) -> Result<u32> {
+ let mut latency = 0u32;
+ unsafe {
+ call!(ffi::cubeb_stream_get_input_latency(
+ self.as_ptr(),
+ &mut latency
+ ))?;
+ }
+ Ok(latency)
+ }
+
+ /// Set the volume for a stream.
+ pub fn set_volume(&self, volume: f32) -> Result<()> {
+ unsafe { call!(ffi::cubeb_stream_set_volume(self.as_ptr(), volume)) }
+ }
+
+ /// Change a stream's name
+ pub fn set_name(&self, name: &CStr) -> Result<()> {
+ unsafe { call!(ffi::cubeb_stream_set_name(self.as_ptr(), name.as_ptr())) }
+ }
+
+ /// Get the current output device for this stream.
+ pub fn current_device(&self) -> Result<&DeviceRef> {
+ let mut device: *mut ffi::cubeb_device = ptr::null_mut();
+ unsafe {
+ call!(ffi::cubeb_stream_get_current_device(
+ self.as_ptr(),
+ &mut device
+ ))?;
+ Ok(DeviceRef::from_ptr(device))
+ }
+ }
+
+ /// Set the mute state for an input stream.
+ pub fn set_input_mute(&self, mute: bool) -> Result<()> {
+ let mute: c_int = if mute { 1 } else { 0 };
+ unsafe { call!(ffi::cubeb_stream_set_input_mute(self.as_ptr(), mute)) }
+ }
+
+ /// Set the processing parameters for an input stream.
+ pub fn set_input_processing_params(&self, params: InputProcessingParams) -> Result<()> {
+ unsafe {
+ call!(ffi::cubeb_stream_set_input_processing_params(
+ self.as_ptr(),
+ params.bits()
+ ))
+ }
+ }
+
+ /// Destroy a cubeb_device structure.
+ pub fn device_destroy(&self, device: DeviceRef) -> Result<()> {
+ unsafe {
+ call!(ffi::cubeb_stream_device_destroy(
+ self.as_ptr(),
+ device.as_ptr()
+ ))
+ }
+ }
+
+ /// Set a callback to be notified when the output device changes.
+ pub fn register_device_changed_callback(
+ &self,
+ callback: ffi::cubeb_device_changed_callback,
+ ) -> Result<()> {
+ unsafe {
+ call!(ffi::cubeb_stream_register_device_changed_callback(
+ self.as_ptr(),
+ callback
+ ))
+ }
+ }
+
+ pub fn user_ptr(&self) -> *mut c_void {
+ unsafe { ffi::cubeb_stream_user_ptr(self.as_ptr()) }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::mem;
+ use {StreamParams, StreamParamsRef, StreamPrefs};
+
+ #[test]
+ fn stream_params_default() {
+ let params = StreamParams::default();
+ assert_eq!(params.channels(), 0);
+ }
+
+ #[test]
+ fn stream_params_raw_channels() {
+ let mut raw: super::ffi::cubeb_stream_params = unsafe { mem::zeroed() };
+ raw.channels = 2;
+ let params = unsafe { StreamParamsRef::from_ptr(&mut raw) };
+ assert_eq!(params.channels(), 2);
+ }
+
+ #[test]
+ fn stream_params_raw_format() {
+ let mut raw: super::ffi::cubeb_stream_params = unsafe { mem::zeroed() };
+ macro_rules! check(
+ ($($raw:ident => $real:ident),*) => (
+ $(raw.format = super::ffi::$raw;
+ let params = unsafe {
+ StreamParamsRef::from_ptr(&mut raw)
+ };
+ assert_eq!(params.format(), super::SampleFormat::$real);
+ )*
+ ) );
+
+ check!(CUBEB_SAMPLE_S16LE => S16LE,
+ CUBEB_SAMPLE_S16BE => S16BE,
+ CUBEB_SAMPLE_FLOAT32LE => Float32LE,
+ CUBEB_SAMPLE_FLOAT32BE => Float32BE);
+ }
+
+ #[test]
+ fn stream_params_raw_format_native_endian() {
+ let mut raw: super::ffi::cubeb_stream_params = unsafe { mem::zeroed() };
+ raw.format = super::ffi::CUBEB_SAMPLE_S16NE;
+ let params = unsafe { StreamParamsRef::from_ptr(&mut raw) };
+ assert_eq!(
+ params.format(),
+ if cfg!(target_endian = "little") {
+ super::SampleFormat::S16LE
+ } else {
+ super::SampleFormat::S16BE
+ }
+ );
+
+ raw.format = super::ffi::CUBEB_SAMPLE_FLOAT32NE;
+ let params = unsafe { StreamParamsRef::from_ptr(&mut raw) };
+ assert_eq!(
+ params.format(),
+ if cfg!(target_endian = "little") {
+ super::SampleFormat::Float32LE
+ } else {
+ super::SampleFormat::Float32BE
+ }
+ );
+ }
+
+ #[test]
+ fn stream_params_raw_layout() {
+ let mut raw: super::ffi::cubeb_stream_params = unsafe { mem::zeroed() };
+ macro_rules! check(
+ ($($raw:ident => $real:ident),*) => (
+ $(raw.layout = super::ffi::$raw;
+ let params = unsafe {
+ StreamParamsRef::from_ptr(&mut raw)
+ };
+ assert_eq!(params.layout(), super::ChannelLayout::$real);
+ )*
+ ) );
+
+ check!(CUBEB_LAYOUT_UNDEFINED => UNDEFINED,
+ CUBEB_LAYOUT_MONO => MONO,
+ CUBEB_LAYOUT_MONO_LFE => MONO_LFE,
+ CUBEB_LAYOUT_STEREO => STEREO,
+ CUBEB_LAYOUT_STEREO_LFE => STEREO_LFE,
+ CUBEB_LAYOUT_3F => _3F,
+ CUBEB_LAYOUT_3F_LFE => _3F_LFE,
+ CUBEB_LAYOUT_2F1 => _2F1,
+ CUBEB_LAYOUT_2F1_LFE => _2F1_LFE,
+ CUBEB_LAYOUT_3F1 => _3F1,
+ CUBEB_LAYOUT_3F1_LFE => _3F1_LFE,
+ CUBEB_LAYOUT_2F2 => _2F2,
+ CUBEB_LAYOUT_2F2_LFE => _2F2_LFE,
+ CUBEB_LAYOUT_QUAD => QUAD,
+ CUBEB_LAYOUT_QUAD_LFE => QUAD_LFE,
+ CUBEB_LAYOUT_3F2 => _3F2,
+ CUBEB_LAYOUT_3F2_LFE => _3F2_LFE,
+ CUBEB_LAYOUT_3F2_BACK => _3F2_BACK,
+ CUBEB_LAYOUT_3F2_LFE_BACK => _3F2_LFE_BACK,
+ CUBEB_LAYOUT_3F3R_LFE => _3F3R_LFE,
+ CUBEB_LAYOUT_3F4_LFE => _3F4_LFE);
+ }
+
+ #[test]
+ fn stream_params_raw_rate() {
+ let mut raw: super::ffi::cubeb_stream_params = unsafe { mem::zeroed() };
+ raw.rate = 44_100;
+ let params = unsafe { StreamParamsRef::from_ptr(&mut raw) };
+ assert_eq!(params.rate(), 44_100);
+ }
+
+ #[test]
+ fn stream_params_raw_prefs() {
+ let mut raw: super::ffi::cubeb_stream_params = unsafe { mem::zeroed() };
+ raw.prefs = super::ffi::CUBEB_STREAM_PREF_LOOPBACK;
+ let params = unsafe { StreamParamsRef::from_ptr(&mut raw) };
+ assert_eq!(params.prefs(), StreamPrefs::LOOPBACK);
+ }
+
+ #[test]
+ fn stream_params_stream_params_ref_ptr_match() {
+ let params = StreamParams::default();
+ assert_eq!(params.as_ptr(), params.as_ref().as_ptr());
+ }
+}
diff --git a/third_party/rust/cubeb-core/src/util.rs b/third_party/rust/cubeb-core/src/util.rs
new file mode 100644
index 0000000000..33008b668e
--- /dev/null
+++ b/third_party/rust/cubeb-core/src/util.rs
@@ -0,0 +1,15 @@
+// Copyright © 2017 Mozilla Foundation
+//
+// This program is made available under an ISC-style license. See the
+// accompanying file LICENSE for details.
+
+use std::ffi::CStr;
+use std::os::raw::c_char;
+
+pub unsafe fn opt_bytes<'a>(c: *const c_char) -> Option<&'a [u8]> {
+ if c.is_null() {
+ None
+ } else {
+ Some(CStr::from_ptr(c).to_bytes())
+ }
+}