diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/cubeb-core | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/cubeb-core')
-rw-r--r-- | third_party/rust/cubeb-core/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | third_party/rust/cubeb-core/Cargo.toml | 35 | ||||
-rw-r--r-- | third_party/rust/cubeb-core/LICENSE | 13 | ||||
-rw-r--r-- | third_party/rust/cubeb-core/src/builders.rs | 260 | ||||
-rw-r--r-- | third_party/rust/cubeb-core/src/call.rs | 21 | ||||
-rw-r--r-- | third_party/rust/cubeb-core/src/channel.rs | 151 | ||||
-rw-r--r-- | third_party/rust/cubeb-core/src/context.rs | 151 | ||||
-rw-r--r-- | third_party/rust/cubeb-core/src/device.rs | 233 | ||||
-rw-r--r-- | third_party/rust/cubeb-core/src/device_collection.rs | 99 | ||||
-rw-r--r-- | third_party/rust/cubeb-core/src/error.rs | 162 | ||||
-rw-r--r-- | third_party/rust/cubeb-core/src/ffi_types.rs | 242 | ||||
-rw-r--r-- | third_party/rust/cubeb-core/src/format.rs | 44 | ||||
-rw-r--r-- | third_party/rust/cubeb-core/src/lib.rs | 38 | ||||
-rw-r--r-- | third_party/rust/cubeb-core/src/log.rs | 42 | ||||
-rw-r--r-- | third_party/rust/cubeb-core/src/stream.rs | 327 | ||||
-rw-r--r-- | third_party/rust/cubeb-core/src/util.rs | 15 |
16 files changed, 1834 insertions, 0 deletions
diff --git a/third_party/rust/cubeb-core/.cargo-checksum.json b/third_party/rust/cubeb-core/.cargo-checksum.json new file mode 100644 index 0000000000..ea576dcc1b --- /dev/null +++ b/third_party/rust/cubeb-core/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"d839b4f63507b93786807223af9badee4eb8f015cbe9f442f82460b463e535d6","LICENSE":"8c044baa5d883274736eeece0b955249076c2697b826e576fce59496235b2cf5","src/builders.rs":"87adbbdd63fde848eeaae4a79a04859ed02ca982fb644f6b8b5fe7c158a8944c","src/call.rs":"cf8949c2cf53f5418d8e5222f570b9301a98a476a18c734fc2ebbc8b53ac0165","src/channel.rs":"676d0cfa733c09ef039ed16138cb78ae6bd10b58d0eb93292e6d8accc69086ff","src/context.rs":"9d7fd770511910e8c416ff47354863f547915ff94fb19ef2dd6796a70c2658cd","src/device.rs":"5fa27f2ba7f108e4ae32ad776b1639b0e8c8ad4c401ce171c512c8126ea5a517","src/device_collection.rs":"f86535ffeee73b889bdbac1c5a3432b01cca86df819a7cc26eaa0c983d30cbbe","src/error.rs":"3693bf575e9504b9b1ab114de3e2ce7132039acf27ec68967dae66c2ce1581aa","src/ffi_types.rs":"71948d0949675df876e18c4340f4e693fc6a14c4de9d63a3885450c95577596b","src/format.rs":"7162c1550be53f5fe94b0bba4c71fea2f0304462108657c62a20ea47207ca413","src/lib.rs":"57ecf793ab1cc052c021ccbf9ad011ea52d1303e2dbc54adea90a95b5d7a36cf","src/log.rs":"4bef74d7a7fd9ffdf249be5ef95781969f00a7ecff2d3a190d5b1536d1635a73","src/stream.rs":"d34a66ee11fc3818a9685923c7211b96544cd2bb6b72f5fa4b2727c57d84d929","src/util.rs":"308cfbaacd615ff600e74415c52daeef007fff34a4a0648a73c0042f6067f84f"},"package":"6203cd567a2ae4382077cc2160738517462c93cf77f3910fa18b2a74549abc84"}
\ No newline at end of file diff --git a/third_party/rust/cubeb-core/Cargo.toml b/third_party/rust/cubeb-core/Cargo.toml new file mode 100644 index 0000000000..3a75b88a37 --- /dev/null +++ b/third_party/rust/cubeb-core/Cargo.toml @@ -0,0 +1,35 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +name = "cubeb-core" +version = "0.10.3" +authors = ["Dan Glastonbury <dglastonbury@mozilla.com>"] +description = """ +Common types and definitions for cubeb rust and C bindings. Not intended for direct use. +""" +homepage = "https://github.com/mozilla/cubeb-rs" +keywords = ["cubeb"] +categories = ["api-bindings"] +license = "ISC" +repository = "https://github.com/mozilla/cubeb-rs" + +[dependencies.bitflags] +version = "1.2.0" + +[dependencies.cubeb-sys] +version = "0.10.3" + +[features] +gecko-in-tree = ["cubeb-sys/gecko-in-tree"] + +[badges.circle-ci] +repository = "mozilla/cubeb-rs" diff --git a/third_party/rust/cubeb-core/LICENSE b/third_party/rust/cubeb-core/LICENSE new file mode 100644 index 0000000000..ec9718cf02 --- /dev/null +++ b/third_party/rust/cubeb-core/LICENSE @@ -0,0 +1,13 @@ +Copyright © 2017 Mozilla Foundation + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 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..19bc956726 --- /dev/null +++ b/third_party/rust/cubeb-core/src/channel.rs @@ -0,0 +1,151 @@ +// 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 { bits: 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 { + let layout = *self; + unsafe { ffi::cubeb_channel_layout_nb_channels(layout.into()) } + } +} + +#[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..dba1f1731d --- /dev/null +++ b/third_party/rust/cubeb-core/src/context.rs @@ -0,0 +1,151 @@ +// 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, 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) + } + + /// # 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..43fb1c3927 --- /dev/null +++ b/third_party/rust/cubeb-core/src/device.rs @@ -0,0 +1,233 @@ +// 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 NONE = ffi::CUBEB_DEVICE_PREF_NONE; + 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; + } +} + +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 UNKNOWN = ffi::CUBEB_DEVICE_TYPE_UNKNOWN as _; + const INPUT = ffi::CUBEB_DEVICE_TYPE_INPUT as _; + const OUTPUT = ffi::CUBEB_DEVICE_TYPE_OUTPUT as _; + } +} + +/// 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..8e4fb96976 --- /dev/null +++ b/third_party/rust/cubeb-core/src/stream.rs @@ -0,0 +1,327 @@ +// 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; +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 NONE = ffi::CUBEB_STREAM_PREF_NONE; + 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; + } +} + +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)) + } + } + + /// 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()) + } +} |