summaryrefslogtreecommitdiffstats
path: root/third_party/rust/cubeb/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/cubeb/src
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--third_party/rust/cubeb/src/context.rs8
-rw-r--r--third_party/rust/cubeb/src/frame.rs40
-rw-r--r--third_party/rust/cubeb/src/lib.rs37
-rw-r--r--third_party/rust/cubeb/src/log.rs59
-rw-r--r--third_party/rust/cubeb/src/sample.rs24
-rw-r--r--third_party/rust/cubeb/src/stream.rs271
6 files changed, 439 insertions, 0 deletions
diff --git a/third_party/rust/cubeb/src/context.rs b/third_party/rust/cubeb/src/context.rs
new file mode 100644
index 0000000000..c45876a71f
--- /dev/null
+++ b/third_party/rust/cubeb/src/context.rs
@@ -0,0 +1,8 @@
+use {Context, Result};
+use std::ffi::CString;
+
+pub fn init<T: Into<Vec<u8>>>(name: T) -> Result<Context> {
+ let name = CString::new(name)?;
+
+ Context::init(Some(name.as_c_str()), None)
+}
diff --git a/third_party/rust/cubeb/src/frame.rs b/third_party/rust/cubeb/src/frame.rs
new file mode 100644
index 0000000000..70b7d6a231
--- /dev/null
+++ b/third_party/rust/cubeb/src/frame.rs
@@ -0,0 +1,40 @@
+//! Frame utilities
+
+use std::{mem, slice};
+
+/// A `Frame` is a collection of samples which have a a specific
+/// layout represented by `ChannelLayout`
+pub trait Frame {}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+/// A monaural frame.
+pub struct MonoFrame<T> {
+ /// Mono channel
+ pub m: T,
+}
+
+impl<T> Frame for MonoFrame<T> {}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+/// A stereo frame.
+pub struct StereoFrame<T> {
+ /// Left channel
+ pub l: T,
+ /// Right channel
+ pub r: T,
+}
+
+impl<T> Frame for StereoFrame<T> {}
+
+pub unsafe fn frame_from_bytes<F: Frame>(bytes: &[u8]) -> &[F] {
+ debug_assert_eq!(bytes.len() % mem::size_of::<F>(), 0);
+ slice::from_raw_parts(
+ bytes.as_ptr() as *const F,
+ bytes.len() / mem::size_of::<F>(),
+ )
+}
+
+pub unsafe fn frame_from_bytes_mut<F: Frame>(bytes: &mut [u8]) -> &mut [F] {
+ debug_assert!(bytes.len() % mem::size_of::<F>() == 0);
+ slice::from_raw_parts_mut(bytes.as_ptr() as *mut F, bytes.len() / mem::size_of::<F>())
+}
diff --git a/third_party/rust/cubeb/src/lib.rs b/third_party/rust/cubeb/src/lib.rs
new file mode 100644
index 0000000000..f828d1945b
--- /dev/null
+++ b/third_party/rust/cubeb/src/lib.rs
@@ -0,0 +1,37 @@
+//! # libcubeb bindings for rust
+//!
+//! This library contains bindings to the [cubeb][1] C library which
+//! is used to interact with system audio. The library itself is a
+//! work in progress and is likely lacking documentation and test.
+//!
+//! [1]: https://github.com/kinetiknz/cubeb/
+//!
+//! The cubeb-rs library exposes the user API of libcubeb. It doesn't
+//! expose the internal interfaces, so isn't suitable for extending
+//! libcubeb. See [cubeb-pulse-rs][2] for an example of extending
+//! libcubeb via implementing a cubeb backend in rust.
+
+// Copyright © 2017-2018 Mozilla Foundation
+//
+// This program is made available under an ISC-style license. See the
+// accompanying file LICENSE for details.
+
+extern crate cubeb_core;
+
+mod context;
+mod frame;
+#[macro_use]
+mod log;
+mod sample;
+mod stream;
+
+pub use context::*;
+// Re-export cubeb_core types
+pub use cubeb_core::{ffi, ChannelLayout, Context, ContextRef, Device, DeviceCollection,
+ DeviceCollectionRef, DeviceFormat, DeviceId, DeviceInfo, DeviceInfoRef,
+ DeviceRef, DeviceState, DeviceType, Error, ErrorCode, LogLevel, Result,
+ SampleFormat, State, StreamParams, StreamParamsBuilder, StreamParamsRef,
+ StreamPrefs, StreamRef};
+pub use frame::*;
+pub use sample::*;
+pub use stream::*;
diff --git a/third_party/rust/cubeb/src/log.rs b/third_party/rust/cubeb/src/log.rs
new file mode 100644
index 0000000000..eb0170d947
--- /dev/null
+++ b/third_party/rust/cubeb/src/log.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.
+
+#[macro_export]
+macro_rules! cubeb_log_internal {
+ ($level: expr, $msg: expr) => {
+ #[allow(unused_unsafe)]
+ unsafe {
+ if $level <= $crate::ffi::g_cubeb_log_level.into() {
+ cubeb_log_internal!(__INTERNAL__ $msg);
+ }
+ }
+ };
+ ($level: expr, $fmt: expr, $($arg: expr),+) => {
+ #[allow(unused_unsafe)]
+ unsafe {
+ if $level <= $crate::ffi::g_cubeb_log_level.into() {
+ cubeb_log_internal!(__INTERNAL__ format!($fmt, $($arg),*));
+ }
+ }
+ };
+ (__INTERNAL__ $msg: expr) => {
+ if let Some(log_callback) = $crate::ffi::g_cubeb_log_callback {
+ let cstr = ::std::ffi::CString::new(format!("{}:{}: {}\n", file!(), line!(), $msg)).unwrap();
+ log_callback(cstr.as_ptr());
+ }
+ }
+}
+
+#[macro_export]
+macro_rules! cubeb_logv {
+ ($msg: expr) => (cubeb_log_internal!($crate::LogLevel::Verbose, $msg));
+ ($fmt: expr, $($arg: expr),+) => (cubeb_log_internal!($crate::LogLevel::Verbose, $fmt, $($arg),*));
+}
+
+#[macro_export]
+macro_rules! cubeb_log {
+ ($msg: expr) => (cubeb_log_internal!($crate::LogLevel::Normal, $msg));
+ ($fmt: expr, $($arg: expr),+) => (cubeb_log_internal!($crate::LogLevel::Normal, $fmt, $($arg),*));
+}
+
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn test_normal_logging() {
+ cubeb_log!("This is log at normal level");
+ cubeb_log!("{} Formatted log", 1);
+ cubeb_log!("{} Formatted {} log {}", 1, 2, 3);
+ }
+
+ #[test]
+ fn test_verbose_logging() {
+ cubeb_logv!("This is a log at verbose level");
+ cubeb_logv!("{} Formatted log", 1);
+ cubeb_logv!("{} Formatted {} log {}", 1, 2, 3);
+ }
+}
diff --git a/third_party/rust/cubeb/src/sample.rs b/third_party/rust/cubeb/src/sample.rs
new file mode 100644
index 0000000000..041de8448c
--- /dev/null
+++ b/third_party/rust/cubeb/src/sample.rs
@@ -0,0 +1,24 @@
+// Copyright © 2017-2018 Mozilla Foundation
+//
+// This program is made available under an ISC-style license. See the
+// accompanying file LICENSE for details.
+
+/// An extension trait which allows the implementation of converting
+/// void* buffers from libcubeb-sys into rust slices of the appropriate
+/// type.
+pub trait Sample: Send + Copy {
+ /// Map f32 in range [-1,1] to sample type
+ fn from_float(f32) -> Self;
+}
+
+impl Sample for i16 {
+ fn from_float(x: f32) -> i16 {
+ (x * f32::from(i16::max_value())) as i16
+ }
+}
+
+impl Sample for f32 {
+ fn from_float(x: f32) -> f32 {
+ x
+ }
+}
diff --git a/third_party/rust/cubeb/src/stream.rs b/third_party/rust/cubeb/src/stream.rs
new file mode 100644
index 0000000000..38ea704f5a
--- /dev/null
+++ b/third_party/rust/cubeb/src/stream.rs
@@ -0,0 +1,271 @@
+// Copyright © 2017-2018 Mozilla Foundation
+//
+// This program is made available under an ISC-style license. See the
+// accompanying file LICENSE for details.
+
+//! Stream Functions
+//!
+//! # Example
+//! ```no_run
+//! extern crate cubeb;
+//! use std::thread;
+//! use std::time::Duration;
+//!
+//! type Frame = cubeb::MonoFrame<f32>;
+//!
+//! fn main() {
+//! let ctx = cubeb::init("Cubeb tone example").unwrap();
+//!
+//! let params = cubeb::StreamParamsBuilder::new()
+//! .format(cubeb::SampleFormat::Float32LE)
+//! .rate(44_100)
+//! .channels(1)
+//! .layout(cubeb::ChannelLayout::MONO)
+//! .prefs(cubeb::StreamPrefs::NONE)
+//! .take();
+//!
+//! let phase_inc = 440.0 / 44_100.0;
+//! let mut phase = 0.0;
+//! let volume = 0.25;
+//!
+//! let mut builder = cubeb::StreamBuilder::<Frame>::new();
+//! builder
+//! .name("Cubeb Square Wave")
+//! .default_output(&params)
+//! .latency(0x1000)
+//! .data_callback(move |_, output| {
+//! // Generate a square wave
+//! for x in output.iter_mut() {
+//! x.m = if phase < 0.5 { volume } else { -volume };
+//! phase = (phase + phase_inc) % 1.0;
+//! }
+//!
+//! output.len() as isize
+//! })
+//! .state_callback(|state| {
+//! println!("stream {:?}", state);
+//! });
+//! let stream = builder.init(&ctx).expect("Failed to create stream.");
+//!
+//! // Start playback
+//! stream.start().unwrap();
+//!
+//! // Play for 1/2 second
+//! thread::sleep(Duration::from_millis(500));
+//!
+//! // Shutdown
+//! stream.stop().unwrap();
+//! }
+//! ```
+
+use {ContextRef, DeviceId, Error, Result, State, StreamParamsRef};
+use cubeb_core;
+use ffi;
+use std::{ops, panic, ptr};
+use std::ffi::CString;
+use std::marker::PhantomData;
+use std::mem::ManuallyDrop;
+use std::os::raw::{c_long, c_void};
+use std::slice::{from_raw_parts, from_raw_parts_mut};
+
+pub type DataCallback<F> = dyn FnMut(&[F], &mut [F]) -> isize + Send + Sync + 'static;
+pub type StateCallback = dyn FnMut(State) + Send + Sync + 'static;
+pub type DeviceChangedCallback = dyn FnMut() + Send + Sync + 'static;
+
+pub struct StreamCallbacks<F> {
+ pub(crate) data: Box<DataCallback<F>>,
+ pub(crate) state: Box<StateCallback>,
+ pub(crate) device_changed: Option<Box<DeviceChangedCallback>>,
+}
+
+pub struct Stream<F>(ManuallyDrop<cubeb_core::Stream>,
+ PhantomData<*const F>);
+
+impl<F> Stream<F> {
+ fn new(s: cubeb_core::Stream) -> Stream<F> {
+ Stream(ManuallyDrop::new(s), PhantomData)
+ }
+}
+
+impl<F> Drop for Stream<F> {
+ fn drop(&mut self) {
+ let user_ptr = self.user_ptr();
+ unsafe { ManuallyDrop::drop(&mut self.0) };
+ let _ = unsafe { Box::from_raw(user_ptr as *mut StreamCallbacks<F>) };
+ }
+}
+
+impl<F> ops::Deref for Stream<F> {
+ type Target = cubeb_core::Stream;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+pub struct StreamBuilder<'a, F> {
+ name: Option<CString>,
+ input: Option<(DeviceId, &'a StreamParamsRef)>,
+ output: Option<(DeviceId, &'a StreamParamsRef)>,
+ latency: Option<u32>,
+ data_cb: Option<Box<DataCallback<F>>>,
+ state_cb: Option<Box<StateCallback>>,
+ device_changed_cb: Option<Box<DeviceChangedCallback>>,
+}
+
+impl<'a, F> StreamBuilder<'a, F> {
+ pub fn new() -> StreamBuilder<'a, F> {
+ StreamBuilder {
+ name: None,
+ input: None,
+ output: None,
+ latency: None,
+ data_cb: None,
+ state_cb: None,
+ device_changed_cb: None,
+ }
+ }
+
+ pub fn data_callback<D>(&mut self, cb: D) -> &mut Self
+ where
+ D: FnMut(&[F], &mut [F]) -> isize + Send + Sync + 'static,
+ {
+ self.data_cb = Some(Box::new(cb) as Box<DataCallback<F>>);
+ self
+ }
+ pub fn state_callback<S>(&mut self, cb: S) -> &mut Self
+ where
+ S: FnMut(State) + Send + Sync + 'static,
+ {
+ self.state_cb = Some(Box::new(cb) as Box<StateCallback>);
+ self
+ }
+
+ pub fn name<T: Into<Vec<u8>>>(&mut self, name: T) -> &mut Self {
+ self.name = Some(CString::new(name).unwrap());
+ self
+ }
+
+ pub fn default_input(&mut self, params: &'a StreamParamsRef) -> &mut Self {
+ self.input = Some((ptr::null(), params));
+ self
+ }
+
+ pub fn input(&mut self, device: DeviceId, params: &'a StreamParamsRef) -> &mut Self {
+ self.input = Some((device, params));
+ self
+ }
+
+ pub fn default_output(&mut self, params: &'a StreamParamsRef) -> &mut Self {
+ self.output = Some((ptr::null(), params));
+ self
+ }
+
+ pub fn output(&mut self, device: DeviceId, params: &'a StreamParamsRef) -> &mut Self {
+ self.output = Some((device, params));
+ self
+ }
+
+ pub fn latency(&mut self, latency: u32) -> &mut Self {
+ self.latency = Some(latency);
+ self
+ }
+
+ pub fn device_changed_cb<CB>(&mut self, cb: CB) -> &mut Self
+ where
+ CB: FnMut() + Send + Sync + 'static,
+ {
+ self.device_changed_cb = Some(Box::new(cb) as Box<DeviceChangedCallback>);
+ self
+ }
+
+ pub fn init(self, ctx: &ContextRef) -> Result<Stream<F>> {
+ if self.data_cb.is_none() || self.state_cb.is_none() {
+ return Err(Error::error());
+ }
+
+ let has_device_changed = self.device_changed_cb.is_some();
+ let cbs = Box::into_raw(Box::new(StreamCallbacks {
+ data: self.data_cb.unwrap(),
+ state: self.state_cb.unwrap(),
+ device_changed: self.device_changed_cb,
+ }));
+
+ let stream_name = self.name.as_ref().and_then(|n| Some(n.as_c_str()));
+ let (input_device, input_stream_params) =
+ self.input.map_or((ptr::null(), None), |x| (x.0, Some(x.1)));
+ let (output_device, output_stream_params) = self.output
+ .map_or((ptr::null(), None), |x| (x.0, Some(x.1)));
+ let latency = self.latency.unwrap_or(1);
+ let data_callback: ffi::cubeb_data_callback = Some(data_cb_c::<F>);
+ let state_callback: ffi::cubeb_state_callback = Some(state_cb_c::<F>);
+
+ let stream = unsafe {
+ ctx.stream_init(
+ stream_name,
+ input_device,
+ input_stream_params,
+ output_device,
+ output_stream_params,
+ latency,
+ data_callback,
+ state_callback,
+ cbs as *mut _
+ )?
+ };
+ if has_device_changed {
+ let device_changed_callback: ffi::cubeb_device_changed_callback =
+ Some(device_changed_cb_c::<F>);
+ stream.register_device_changed_callback(device_changed_callback)?;
+ }
+ Ok(Stream::new(stream))
+ }
+}
+
+// C callable callbacks
+unsafe extern "C" fn data_cb_c<F>(
+ _: *mut ffi::cubeb_stream,
+ user_ptr: *mut c_void,
+ input_buffer: *const c_void,
+ output_buffer: *mut c_void,
+ nframes: c_long,
+) -> c_long {
+ let ok = panic::catch_unwind(|| {
+ let cbs = &mut *(user_ptr as *mut StreamCallbacks<F>);
+ let input: &[F] = if input_buffer.is_null() {
+ &[]
+ } else {
+ from_raw_parts(input_buffer as *const _, nframes as usize)
+ };
+ let output: &mut [F] = if output_buffer.is_null() {
+ &mut []
+ } else {
+ from_raw_parts_mut(output_buffer as *mut _, nframes as usize)
+ };
+ (cbs.data)(input, output) as c_long
+ });
+ ok.unwrap_or(0)
+}
+
+unsafe extern "C" fn state_cb_c<F>(
+ _: *mut ffi::cubeb_stream,
+ user_ptr: *mut c_void,
+ state: ffi::cubeb_state,
+) {
+ let ok = panic::catch_unwind(|| {
+ let state = State::from(state);
+ let cbs = &mut *(user_ptr as *mut StreamCallbacks<F>);
+ (cbs.state)(state);
+ });
+ ok.expect("State callback panicked");
+}
+
+unsafe extern "C" fn device_changed_cb_c<F>(user_ptr: *mut c_void) {
+ let ok = panic::catch_unwind(|| {
+ let cbs = &mut *(user_ptr as *mut StreamCallbacks<F>);
+ if let Some(ref mut device_changed) = cbs.device_changed {
+ device_changed();
+ }
+ });
+ ok.expect("Device changed callback panicked");
+}