Bug 1530715 - P2: Import oxidized cubeb_audiounit.cpp. r=padenot

Differential Revision: https://phabricator.services.mozilla.com/D23431

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Chun-Min Chang 2019-07-17 04:48:25 +00:00
Родитель 487c109761
Коммит f6d72027f3
20 изменённых файлов: 5731 добавлений и 1 удалений

Просмотреть файл

@ -0,0 +1,18 @@
[package]
name = "cubeb-coreaudio"
version = "0.1.0"
authors = ["Chun-Min Chang <chun.m.chang@gmail.com>"]
[lib]
crate-type = ["staticlib", "rlib"]
[dependencies]
atomic = "0.4"
bitflags = "1.0"
core-foundation-sys = { version = "0.6" }
coreaudio-sys-utils = { path = "coreaudio-sys-utils" }
# BMO 1532645: https://bugzilla.mozilla.org/show_bug.cgi?id=1532645
# To workaround https://github.com/rust-lang/rust/issues/58881 and make `cubeb_logv!` work,
# the minimal version of cubeb-backend is 0.5.1.
cubeb-backend = "0.5.3"
libc = "0.2"

Просмотреть файл

@ -0,0 +1,13 @@
Copyright © 2018 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.

Просмотреть файл

@ -3,4 +3,4 @@ git repository using the update.sh script.
The cubeb-coreaudio-rs git repository is: https://github.com/ChunMinChang/cubeb-coreaudio-rs
The git commit ID used was 0000000000000000000000000000000000000000 (0000-00-00 00:00:00)
The git commit ID used was 9a78b6e739eb73911b77ae83e73456ad9586ba2d (2019-06-21 14:09:49 -0700)

Просмотреть файл

@ -0,0 +1,14 @@
[package]
name = "coreaudio-sys-utils"
version = "0.1.0"
authors = ["Chun-Min Chang <chun.m.chang@gmail.com>"]
edition = "2018"
[dependencies]
core-foundation-sys = { version = "0.6" }
[dependencies.coreaudio-sys]
default-features = false
features = ["audio_unit", "core_audio"]
git = "https://github.com/ChunMinChang/coreaudio-sys"
branch = "gecko-build"

Просмотреть файл

@ -0,0 +1,17 @@
// A compile-time static string mapped to kAudioAggregateDeviceNameKey
pub const AGGREGATE_DEVICE_NAME_KEY: &str = "name";
// A compile-time static string mapped to kAudioAggregateDeviceUIDKey
pub const AGGREGATE_DEVICE_UID_KEY: &str = "uid";
// A compile-time static string mapped to kAudioAggregateDeviceIsPrivateKey
pub const AGGREGATE_DEVICE_PRIVATE_KEY: &str = "private";
// A compile-time static string mapped to kAudioAggregateDeviceIsStackedKey
pub const AGGREGATE_DEVICE_STACKED_KEY: &str = "stacked";
// A compile-time static string mapped to kAudioAggregateDeviceSubDeviceListKey
pub const AGGREGATE_DEVICE_SUB_DEVICE_LIST_KEY: &str = "subdevices";
// A compile-time static string mapped to kAudioSubDeviceUIDKey
pub const SUB_DEVICE_UID_KEY: &str = "uid";

Просмотреть файл

@ -0,0 +1,139 @@
use coreaudio_sys::*;
use std::fmt;
use std::os::raw::c_void;
use std::ptr;
pub fn audio_object_has_property(id: AudioObjectID, address: &AudioObjectPropertyAddress) -> bool {
unsafe { AudioObjectHasProperty(id, address) != 0 }
}
pub fn audio_object_get_property_data<T>(
id: AudioObjectID,
address: &AudioObjectPropertyAddress,
size: *mut usize,
data: *mut T,
) -> OSStatus {
unsafe {
AudioObjectGetPropertyData(
id,
address,
0,
ptr::null(),
size as *mut UInt32,
data as *mut c_void,
)
}
}
pub fn audio_object_get_property_data_with_qualifier<T, Q>(
id: AudioObjectID,
address: &AudioObjectPropertyAddress,
qualifier_size: usize,
qualifier_data: *const Q,
size: *mut usize,
data: *mut T,
) -> OSStatus {
unsafe {
AudioObjectGetPropertyData(
id,
address,
qualifier_size as UInt32,
qualifier_data as *const c_void,
size as *mut UInt32,
data as *mut c_void,
)
}
}
pub fn audio_object_get_property_data_size(
id: AudioObjectID,
address: &AudioObjectPropertyAddress,
size: *mut usize,
) -> OSStatus {
unsafe { AudioObjectGetPropertyDataSize(id, address, 0, ptr::null(), size as *mut UInt32) }
}
pub fn audio_object_get_property_data_size_with_qualifier<Q>(
id: AudioObjectID,
address: &AudioObjectPropertyAddress,
qualifier_size: usize,
qualifier_data: *const Q,
size: *mut usize,
) -> OSStatus {
unsafe {
AudioObjectGetPropertyDataSize(
id,
address,
qualifier_size as UInt32,
qualifier_data as *const c_void,
size as *mut UInt32,
)
}
}
pub fn audio_object_set_property_data<T>(
id: AudioObjectID,
address: &AudioObjectPropertyAddress,
size: usize,
data: *const T,
) -> OSStatus {
unsafe {
AudioObjectSetPropertyData(
id,
address,
0,
ptr::null(),
size as UInt32,
data as *const c_void,
)
}
}
#[allow(non_camel_case_types)]
pub type audio_object_property_listener_proc =
extern "C" fn(AudioObjectID, u32, *const AudioObjectPropertyAddress, *mut c_void) -> OSStatus;
pub fn audio_object_add_property_listener<T>(
id: AudioObjectID,
address: &AudioObjectPropertyAddress,
listener: audio_object_property_listener_proc,
data: *mut T,
) -> OSStatus {
unsafe { AudioObjectAddPropertyListener(id, address, Some(listener), data as *mut c_void) }
}
pub fn audio_object_remove_property_listener<T>(
id: AudioObjectID,
address: &AudioObjectPropertyAddress,
listener: audio_object_property_listener_proc,
data: *mut T,
) -> OSStatus {
unsafe { AudioObjectRemovePropertyListener(id, address, Some(listener), data as *mut c_void) }
}
#[derive(Debug)]
pub struct PropertySelector(AudioObjectPropertySelector);
impl PropertySelector {
pub fn new(selector: AudioObjectPropertySelector) -> Self {
Self(selector)
}
}
impl fmt::Display for PropertySelector {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use coreaudio_sys as sys;
let s = match self.0 {
sys::kAudioHardwarePropertyDefaultOutputDevice => {
"kAudioHardwarePropertyDefaultOutputDevice"
}
sys::kAudioHardwarePropertyDefaultInputDevice => {
"kAudioHardwarePropertyDefaultInputDevice"
}
sys::kAudioDevicePropertyDeviceIsAlive => "kAudioDevicePropertyDeviceIsAlive",
sys::kAudioDevicePropertyDataSource => "kAudioDevicePropertyDataSource",
_ => "Unknown",
};
write!(f, "{}", s)
}
}

Просмотреть файл

@ -0,0 +1,165 @@
use coreaudio_sys::*;
use std::os::raw::c_void;
pub fn audio_unit_get_property_info(
unit: AudioUnit,
property: AudioUnitPropertyID,
scope: AudioUnitScope,
element: AudioUnitElement,
size: *mut usize,
writable: *mut Boolean,
) -> OSStatus {
assert!(!unit.is_null());
unsafe {
AudioUnitGetPropertyInfo(
unit,
property,
scope,
element,
size as *mut UInt32,
writable,
)
}
}
pub fn audio_unit_get_property<T>(
unit: AudioUnit,
property: AudioUnitPropertyID,
scope: AudioUnitScope,
element: AudioUnitElement,
data: *mut T,
size: *mut usize,
) -> OSStatus {
assert!(!unit.is_null());
unsafe {
AudioUnitGetProperty(
unit,
property,
scope,
element,
data as *mut c_void,
size as *mut UInt32,
)
}
}
pub fn audio_unit_set_property<T>(
unit: AudioUnit,
property: AudioUnitPropertyID,
scope: AudioUnitScope,
element: AudioUnitElement,
data: *const T,
size: usize,
) -> OSStatus {
assert!(!unit.is_null());
unsafe {
AudioUnitSetProperty(
unit,
property,
scope,
element,
data as *const c_void,
size as UInt32,
)
}
}
pub fn audio_unit_get_parameter(
unit: AudioUnit,
id: AudioUnitParameterID,
scope: AudioUnitScope,
element: AudioUnitElement,
value: &mut AudioUnitParameterValue,
) -> OSStatus {
assert!(!unit.is_null());
unsafe {
AudioUnitGetParameter(
unit,
id,
scope,
element,
value as *mut AudioUnitParameterValue,
)
}
}
pub fn audio_unit_set_parameter(
unit: AudioUnit,
id: AudioUnitParameterID,
scope: AudioUnitScope,
element: AudioUnitElement,
value: AudioUnitParameterValue,
buffer_offset_in_frames: UInt32,
) -> OSStatus {
assert!(!unit.is_null());
unsafe { AudioUnitSetParameter(unit, id, scope, element, value, buffer_offset_in_frames) }
}
pub fn audio_unit_initialize(unit: AudioUnit) -> OSStatus {
assert!(!unit.is_null());
unsafe { AudioUnitInitialize(unit) }
}
pub fn audio_unit_uninitialize(unit: AudioUnit) -> OSStatus {
assert!(!unit.is_null());
unsafe { AudioUnitUninitialize(unit) }
}
pub fn dispose_audio_unit(unit: AudioUnit) -> OSStatus {
unsafe { AudioComponentInstanceDispose(unit) }
}
pub fn audio_output_unit_start(unit: AudioUnit) -> OSStatus {
assert!(!unit.is_null());
unsafe { AudioOutputUnitStart(unit) }
}
pub fn audio_output_unit_stop(unit: AudioUnit) -> OSStatus {
assert!(!unit.is_null());
unsafe { AudioOutputUnitStop(unit) }
}
pub fn audio_unit_render(
in_unit: AudioUnit,
io_action_flags: *mut AudioUnitRenderActionFlags,
in_time_stamp: *const AudioTimeStamp,
in_output_bus_number: u32,
in_number_frames: u32,
io_data: *mut AudioBufferList,
) -> OSStatus {
assert!(!in_unit.is_null());
unsafe {
AudioUnitRender(
in_unit,
io_action_flags,
in_time_stamp,
in_output_bus_number,
in_number_frames,
io_data,
)
}
}
#[allow(non_camel_case_types)]
pub type audio_unit_property_listener_proc =
extern "C" fn(*mut c_void, AudioUnit, AudioUnitPropertyID, AudioUnitScope, AudioUnitElement);
pub fn audio_unit_add_property_listener<T>(
unit: AudioUnit,
id: AudioUnitPropertyID,
listener: audio_unit_property_listener_proc,
data: *mut T,
) -> OSStatus {
assert!(!unit.is_null());
unsafe { AudioUnitAddPropertyListener(unit, id, Some(listener), data as *mut c_void) }
}
pub fn audio_unit_remove_property_listener_with_user_data<T>(
unit: AudioUnit,
id: AudioUnitPropertyID,
listener: audio_unit_property_listener_proc,
data: *mut T,
) -> OSStatus {
assert!(!unit.is_null());
unsafe { AudioUnitRemovePropertyListenerWithUserData(unit, id, Some(listener), data as *mut c_void) }
}

Просмотреть файл

@ -0,0 +1,37 @@
use coreaudio_sys::*;
use std::os::raw::c_void;
pub struct CFMutableDictRef(CFMutableDictionaryRef);
impl CFMutableDictRef {
pub fn add_value<K, V>(&self, key: *const K, value: *const V) {
assert!(!self.0.is_null());
unsafe {
CFDictionaryAddValue(self.0, key as *const c_void, value as *const c_void);
}
}
}
impl Default for CFMutableDictRef {
fn default() -> Self {
let dict = unsafe {
CFDictionaryCreateMutable(
kCFAllocatorDefault,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks,
)
};
assert!(!dict.is_null());
Self(dict)
}
}
impl Drop for CFMutableDictRef {
fn drop(&mut self) {
assert!(!self.0.is_null());
unsafe {
CFRelease(self.0 as *const c_void);
}
}
}

Просмотреть файл

@ -0,0 +1,151 @@
use coreaudio_sys::*;
use std::ffi::CString;
use std::mem;
use std::os::raw::c_void;
use std::ptr;
pub const DISPATCH_QUEUE_SERIAL: dispatch_queue_attr_t = ptr::null_mut::<dispatch_queue_attr_s>();
pub fn create_dispatch_queue(
label: &'static str,
queue_attr: dispatch_queue_attr_t,
) -> dispatch_queue_t {
let label = CString::new(label).unwrap();
let c_string = label.as_ptr();
unsafe { dispatch_queue_create(c_string, queue_attr) }
}
pub fn release_dispatch_queue(queue: dispatch_queue_t) {
// TODO: This is incredibly unsafe. Find another way to release the queue.
unsafe {
dispatch_release(mem::transmute::<dispatch_queue_t, dispatch_object_t>(queue));
}
}
pub fn async_dispatch<F>(queue: dispatch_queue_t, work: F)
where
F: Send + FnOnce(),
{
let (closure, executor) = create_closure_and_executor(work);
unsafe {
dispatch_async_f(queue, closure, executor);
}
}
pub fn sync_dispatch<F>(queue: dispatch_queue_t, work: F)
where
F: Send + FnOnce(),
{
let (closure, executor) = create_closure_and_executor(work);
unsafe {
dispatch_sync_f(queue, closure, executor);
}
}
// Return an raw pointer to a (unboxed) closure and an executor that
// will run the closure (after re-boxing the closure) when it's called.
fn create_closure_and_executor<F>(closure: F) -> (*mut c_void, dispatch_function_t)
where
F: FnOnce(),
{
extern "C" fn closure_executer<F>(unboxed_closure: *mut c_void)
where
F: FnOnce(),
{
// Retake the leaked closure.
let closure: Box<F> = unsafe { Box::from_raw(unboxed_closure as *mut F) };
// Execute the closure.
(*closure)();
// closure is released after finishiing this function call.
}
let closure: Box<F> = Box::new(closure); // Allocate closure on heap.
let executor: dispatch_function_t = Some(closure_executer::<F>);
(
Box::into_raw(closure) as *mut c_void, // Leak the closure.
executor,
)
}
#[cfg(test)]
mod test {
use super::*;
use std::sync::{Arc, Mutex};
const COUNT: u32 = 10;
#[test]
fn test_async_dispatch() {
use std::sync::mpsc::channel;
get_queue_and_resource("Run with async dispatch api wrappers", |queue, resource| {
let (tx, rx) = channel();
for i in 0..COUNT {
let (res, tx) = (Arc::clone(&resource), tx.clone());
async_dispatch(queue, move || {
let mut res = res.lock().unwrap();
assert_eq!(res.last_touched, if i == 0 { None } else { Some(i - 1) });
assert_eq!(res.touched_count, i);
res.touch(i);
if i == COUNT - 1 {
tx.send(()).unwrap();
}
});
}
rx.recv().unwrap(); // Wait until it's touched COUNT times.
let resource = resource.lock().unwrap();
assert_eq!(resource.touched_count, COUNT);
assert_eq!(resource.last_touched.unwrap(), COUNT - 1);
});
}
#[test]
fn test_sync_dispatch() {
get_queue_and_resource("Run with sync dispatch api wrappers", |queue, resource| {
for i in 0..COUNT {
let res = Arc::clone(&resource);
sync_dispatch(queue, move || {
let mut res = res.lock().unwrap();
assert_eq!(res.last_touched, if i == 0 { None } else { Some(i - 1) });
assert_eq!(res.touched_count, i);
res.touch(i);
});
}
let resource = resource.lock().unwrap();
assert_eq!(resource.touched_count, COUNT);
assert_eq!(resource.last_touched.unwrap(), COUNT - 1);
});
}
struct Resource {
last_touched: Option<u32>,
touched_count: u32,
}
impl Resource {
fn new() -> Self {
Resource {
last_touched: None,
touched_count: 0,
}
}
fn touch(&mut self, who: u32) {
self.last_touched = Some(who);
self.touched_count += 1;
}
}
fn get_queue_and_resource<F>(label: &'static str, callback: F)
where
F: FnOnce(dispatch_queue_t, Arc<Mutex<Resource>>),
{
let queue = create_dispatch_queue(label, DISPATCH_QUEUE_SERIAL);
let resource = Arc::new(Mutex::new(Resource::new()));
callback(queue, resource);
// Release the queue.
release_dispatch_queue(queue);
}
}

Просмотреть файл

@ -0,0 +1,13 @@
extern crate core_foundation_sys;
extern crate coreaudio_sys;
pub mod aggregate_device;
pub mod audio_object;
pub mod audio_unit;
pub mod cf_mutable_dict;
pub mod dispatch;
pub mod string;
pub mod sys {
pub use coreaudio_sys::*;
}

Просмотреть файл

@ -0,0 +1,128 @@
use core_foundation_sys::base::{kCFAllocatorDefault, kCFAllocatorNull, Boolean, CFIndex};
use core_foundation_sys::string::{
kCFStringEncodingUTF8, CFStringCreateWithBytes, CFStringCreateWithBytesNoCopy,
};
pub fn cfstringref_from_static_string(string: &'static str) -> coreaudio_sys::CFStringRef {
// Set deallocator to kCFAllocatorNull to prevent the the memory of the parameter `string`
// from being released by CFRelease. We manage the string memory by ourselves.
let cfstringref = unsafe {
CFStringCreateWithBytesNoCopy(
kCFAllocatorDefault,
string.as_ptr(),
string.len() as CFIndex,
kCFStringEncodingUTF8,
false as Boolean,
kCFAllocatorNull,
)
};
cfstringref as coreaudio_sys::CFStringRef
}
pub fn cfstringref_from_string(string: &str) -> coreaudio_sys::CFStringRef {
let cfstringref = unsafe {
CFStringCreateWithBytes(
kCFAllocatorDefault,
string.as_ptr(),
string.len() as CFIndex,
kCFStringEncodingUTF8,
false as Boolean,
)
};
cfstringref as coreaudio_sys::CFStringRef
}
#[cfg(test)]
mod test {
use super::*;
use core_foundation_sys::base::{CFRange, CFRelease};
use core_foundation_sys::string::{CFStringGetBytes, CFStringGetLength, CFStringRef};
const STATIC_STRING: &str = "static string for testing";
#[test]
fn test_create_static_cfstring_ref() {
let stringref =
StringRef::new(cfstringref_from_static_string(STATIC_STRING) as CFStringRef);
assert_eq!(STATIC_STRING, stringref.into_string());
// TODO: Find a way to check the string's inner pointer is same.
}
#[test]
fn test_create_cfstring_ref() {
let expected = "Rustaceans 🦀";
let stringref = StringRef::new(cfstringref_from_string(expected) as CFStringRef);
assert_eq!(expected, stringref.into_string());
// TODO: Find a way to check the string's inner pointer is different.
}
#[derive(Debug)]
struct StringRef(CFStringRef);
impl StringRef {
fn new(string_ref: CFStringRef) -> Self {
assert!(!string_ref.is_null());
Self(string_ref)
}
fn to_string(&self) -> String {
String::from_utf8(utf8_from_cfstringref(self.0)).unwrap()
}
fn into_string(self) -> String {
self.to_string()
}
}
impl Drop for StringRef {
fn drop(&mut self) {
use std::os::raw::c_void;
unsafe { CFRelease(self.0 as *mut c_void) };
}
}
fn utf8_from_cfstringref(string_ref: CFStringRef) -> Vec<u8> {
use std::ptr;
assert!(!string_ref.is_null());
let length: CFIndex = unsafe { CFStringGetLength(string_ref) };
assert!(length > 0);
let range: CFRange = CFRange {
location: 0,
length,
};
let mut size: CFIndex = 0;
let mut converted_chars: CFIndex = unsafe {
CFStringGetBytes(
string_ref,
range,
kCFStringEncodingUTF8,
0,
false as Boolean,
ptr::null_mut() as *mut u8,
0,
&mut size,
)
};
assert!(converted_chars > 0 && size > 0);
// Then, allocate the buffer with the required size and actually copy data into it.
let mut buffer = vec![b'\x00'; size as usize];
converted_chars = unsafe {
CFStringGetBytes(
string_ref,
range,
kCFStringEncodingUTF8,
0,
false as Boolean,
buffer.as_mut_ptr(),
size,
ptr::null_mut() as *mut CFIndex,
)
};
assert!(converted_chars > 0);
buffer
}
}

Просмотреть файл

@ -0,0 +1,11 @@
diff --git a/media/libcubeb/cubeb-coreaudio-rs/src/backend/mod.rs b/media/libcubeb/cubeb-coreaudio-rs/src/backend/mod.rs
index f2da36fee65b..d9c402ee14b8 100644
--- a/media/libcubeb/cubeb-coreaudio-rs/src/backend/mod.rs
+++ b/media/libcubeb/cubeb-coreaudio-rs/src/backend/mod.rs
@@ -4118,6 +4118,3 @@ impl<'ctx> StreamOps for AudioUnitStream<'ctx> {
// An unsafe workaround to pass AudioUnitStream across threads.
unsafe impl<'ctx> Send for AudioUnitStream<'ctx> {}
unsafe impl<'ctx> Sync for AudioUnitStream<'ctx> {}
-
-#[cfg(test)]
-mod tests;

Просмотреть файл

@ -0,0 +1,193 @@
use std::fmt::Debug;
use std::os::raw::c_void;
use std::ptr;
use std::slice;
pub trait AutoArrayWrapper: Debug {
fn push(&mut self, data: *const c_void, elements: usize);
fn push_zeros(&mut self, elements: usize);
fn pop(&mut self, elements: usize) -> bool;
fn clear(&mut self);
fn elements(&self) -> usize;
fn as_ptr(&self) -> *const c_void;
fn as_mut_ptr(&mut self) -> *mut c_void;
}
#[derive(Debug)]
pub struct AutoArrayImpl<T: Clone + Debug + Zero> {
ar: Vec<T>,
}
impl<T: Clone + Debug + Zero> AutoArrayImpl<T> {
pub fn new(size: usize) -> Self {
AutoArrayImpl {
ar: Vec::<T>::with_capacity(size),
}
}
}
impl<T: Clone + Debug + Zero> AutoArrayWrapper for AutoArrayImpl<T> {
fn push(&mut self, data: *const c_void, elements: usize) {
let slice = unsafe { slice::from_raw_parts(data as *const T, elements) };
self.ar.extend_from_slice(slice);
}
fn push_zeros(&mut self, elements: usize) {
let len = self.ar.len();
self.ar.resize(len + elements, T::zero());
}
fn pop(&mut self, elements: usize) -> bool {
if elements > self.ar.len() {
return false;
}
self.ar.drain(0..elements);
true
}
fn clear(&mut self) {
self.ar.clear();
}
fn elements(&self) -> usize {
self.ar.len()
}
fn as_ptr(&self) -> *const c_void {
if self.ar.is_empty() {
return ptr::null();
}
self.ar.as_ptr() as *const c_void
}
fn as_mut_ptr(&mut self) -> *mut c_void {
if self.ar.is_empty() {
return ptr::null_mut();
}
self.ar.as_mut_ptr() as *mut c_void
}
}
// Define the zero values for the different types.
// With Zero trait, AutoArrayImpl can be constructed
// only with the limited types.
pub trait Zero {
fn zero() -> Self;
}
impl Zero for f32 {
fn zero() -> Self {
0.0
}
}
impl Zero for i16 {
fn zero() -> Self {
0
}
}
#[cfg(test)]
fn test_auto_array_impl<T: Clone + Debug + PartialEq + Zero>(buf: &[T]) {
let mut auto_array = AutoArrayImpl::<T>::new(5);
assert_eq!(auto_array.elements(), 0);
assert!(auto_array.as_ptr().is_null());
// Check if push works.
auto_array.push(buf.as_ptr() as *const c_void, buf.len());
assert_eq!(auto_array.elements(), buf.len());
let data = auto_array.as_ptr() as *const T;
for (idx, item) in buf.iter().enumerate() {
unsafe {
assert_eq!(*data.add(idx), *item);
}
}
// Check if pop works.
assert!(!auto_array.pop(buf.len() + 1));
const POP: usize = 3;
assert!(POP < buf.len());
assert!(auto_array.pop(POP));
assert_eq!(auto_array.elements(), buf.len() - POP);
let data = auto_array.as_ptr() as *const T;
for i in 0..buf.len() - POP {
unsafe {
assert_eq!(*data.add(i), buf[POP + i]);
}
}
// Check if extend_with_value works.
const ZEROS: usize = 5;
let len = auto_array.elements();
auto_array.push_zeros(ZEROS);
assert_eq!(auto_array.elements(), len + ZEROS);
let data = auto_array.as_ptr() as *const T;
for i in len..len + ZEROS {
unsafe {
assert_eq!(*data.add(i), T::zero());
}
}
}
#[cfg(test)]
fn test_auto_array_wrapper<T: Clone + Debug + PartialEq + Zero>(buf: &[T]) {
let mut auto_array: Option<Box<AutoArrayWrapper>> = None;
auto_array = Some(Box::new(AutoArrayImpl::<T>::new(5)));
assert_eq!(auto_array.as_ref().unwrap().elements(), 0);
let data = auto_array.as_ref().unwrap().as_ptr() as *const T;
assert!(data.is_null());
// Check if push works.
auto_array
.as_mut()
.unwrap()
.push(buf.as_ptr() as *const c_void, buf.len());
assert_eq!(auto_array.as_ref().unwrap().elements(), buf.len());
let data = auto_array.as_ref().unwrap().as_ptr() as *const T;
for i in 0..buf.len() {
unsafe {
assert_eq!(*data.add(i), buf[i]);
}
}
// Check if pop works.
assert!(!auto_array.as_mut().unwrap().pop(buf.len() + 1));
const POP: usize = 3;
assert!(POP < buf.len());
assert!(auto_array.as_mut().unwrap().pop(POP));
assert_eq!(auto_array.as_ref().unwrap().elements(), buf.len() - POP);
let data = auto_array.as_ref().unwrap().as_ptr() as *const T;
for i in 0..buf.len() - POP {
unsafe {
assert_eq!(*data.add(i), buf[POP + i]);
}
}
// Check if push_zeros works.
const ZEROS: usize = 5;
let len = auto_array.as_ref().unwrap().elements();
auto_array.as_mut().unwrap().push_zeros(ZEROS);
assert_eq!(auto_array.as_ref().unwrap().elements(), len + ZEROS);
let data = auto_array.as_ref().unwrap().as_ptr() as *const T;
for i in len..len + ZEROS {
unsafe {
assert_eq!(*data.add(i), T::zero());
}
}
}
#[test]
fn test_auto_array() {
let buf_f32 = [1.0_f32, 2.1, 3.2, 4.3, 5.4];
test_auto_array_impl(&buf_f32);
test_auto_array_wrapper(&buf_f32);
let buf_i16 = [5_i16, 8, 13, 21, 34, 55, 89, 144];
test_auto_array_impl(&buf_i16);
test_auto_array_wrapper(&buf_i16);
}

Просмотреть файл

@ -0,0 +1,77 @@
use std::fmt;
pub struct AutoRelease<T> {
ptr: *mut T,
release_func: unsafe extern "C" fn(*mut T),
}
impl<T> AutoRelease<T> {
pub fn new(ptr: *mut T, release_func: unsafe extern "C" fn(*mut T)) -> Self {
Self { ptr, release_func }
}
pub fn reset(&mut self, ptr: *mut T) {
self.release();
self.ptr = ptr;
}
pub fn as_ref(&self) -> &T {
assert!(!self.ptr.is_null());
unsafe { &*self.ptr }
}
pub fn as_mut(&mut self) -> &mut T {
assert!(!self.ptr.is_null());
unsafe { &mut *self.ptr }
}
pub fn as_ptr(&self) -> *const T {
self.ptr
}
fn release(&self) {
if !self.ptr.is_null() {
unsafe {
(self.release_func)(self.ptr);
}
}
}
}
impl<T> Drop for AutoRelease<T> {
fn drop(&mut self) {
self.release();
}
}
// Explicit Debug impl to work for the type T
// that doesn't implement Debug trait.
impl<T> fmt::Debug for AutoRelease<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("AutoRelease")
.field("ptr", &self.ptr)
.field("release_func", &self.release_func)
.finish()
}
}
#[test]
fn test_auto_release() {
use std::mem;
use std::ptr;
unsafe extern "C" fn allocate() -> *mut libc::c_void {
// println!("Allocate!");
libc::calloc(1, mem::size_of::<u32>())
}
unsafe extern "C" fn deallocate(ptr: *mut libc::c_void) {
// println!("Deallocate!");
libc::free(ptr);
}
let mut auto_release = AutoRelease::new(ptr::null_mut(), deallocate);
let ptr = unsafe { allocate() };
auto_release.reset(ptr);
assert_eq!(auto_release.as_ptr(), ptr);
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,252 @@
// Copyright © 2018 Mozilla Foundation
//
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details.
extern crate libc;
use self::libc::*;
use std::{fmt, mem};
pub struct OwnedCriticalSection {
mutex: pthread_mutex_t,
}
// Notice that `OwnedCriticalSection` only works after `init` is called.
//
// Since `pthread_mutex_t` cannot be copied, we don't initialize the `mutex` in
// `new()`. The `let x = OwnedCriticalSection::new()` will temporarily creat a
// `OwnedCriticalSection` struct inside `new()` and copy the temporary struct
// to `x`, and then destroy the temporary struct. We should initialize the
// `x.mutex` instead of the `mutex` created temporarily inside `new()`.
//
// Example:
// let mut mutex = OwnedCriticalSection::new();
// mutex.init();
impl OwnedCriticalSection {
pub fn new() -> Self {
OwnedCriticalSection {
mutex: PTHREAD_MUTEX_INITIALIZER,
}
}
pub fn init(&mut self) {
unsafe {
let mut attr: pthread_mutexattr_t = mem::zeroed();
let r = pthread_mutexattr_init(&mut attr);
assert_eq!(r, 0);
let r = pthread_mutexattr_settype(&mut attr, PTHREAD_MUTEX_ERRORCHECK);
assert_eq!(r, 0);
let r = pthread_mutex_init(&mut self.mutex, &attr);
assert_eq!(r, 0);
let _ = pthread_mutexattr_destroy(&mut attr);
}
}
fn destroy(&mut self) {
unsafe {
let r = pthread_mutex_destroy(&mut self.mutex);
assert_eq!(r, 0);
}
}
fn lock(&mut self) {
unsafe {
let r = pthread_mutex_lock(&mut self.mutex);
assert_eq!(r, 0, "Deadlock");
}
}
fn unlock(&mut self) {
unsafe {
let r = pthread_mutex_unlock(&mut self.mutex);
assert_eq!(r, 0, "Unlocking unlocked mutex");
}
}
pub fn assert_current_thread_owns(&mut self) {
unsafe {
let r = pthread_mutex_lock(&mut self.mutex);
assert_eq!(r, EDEADLK);
}
}
}
impl Drop for OwnedCriticalSection {
fn drop(&mut self) {
self.destroy();
}
}
impl fmt::Debug for OwnedCriticalSection {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "OwnedCriticalSection {{ mutex @ {:p} }}", &self.mutex)
}
}
pub struct AutoLock<'a> {
mutex: &'a mut OwnedCriticalSection,
}
impl<'a> AutoLock<'a> {
pub fn new(mutex: &'a mut OwnedCriticalSection) -> Self {
mutex.lock();
AutoLock { mutex }
}
}
impl<'a> Drop for AutoLock<'a> {
fn drop(&mut self) {
self.mutex.unlock();
}
}
#[test]
fn test_create_critical_section() {
let mut section = OwnedCriticalSection::new();
section.init();
section.lock();
section.assert_current_thread_owns();
section.unlock();
}
#[test]
#[should_panic]
fn test_critical_section_destroy_without_unlocking_locked() {
let mut section = OwnedCriticalSection::new();
section.init();
section.lock();
section.assert_current_thread_owns();
// Get EBUSY(16) since we destroy the object
// referenced by mutex while it is locked.
}
#[test]
#[should_panic]
fn test_critical_section_unlock_without_locking() {
let mut section = OwnedCriticalSection::new();
section.init();
section.unlock();
// Get EPERM(1) since it has no privilege to
// perform the operation.
}
// #[test]
// #[should_panic]
// fn test_critical_section_assert_without_locking() {
// let mut section = OwnedCriticalSection::new();
// section.init();
// section.assert_current_thread_owns();
// // pthread_mutex_lock() in assert_current_thread_owns() returns 0
// // since lock() wasn't called, so the `assert_eq` in
// // assert_current_thread_owns() fails.
// // When we finish this test since the above assertion fails,
// // `destroy()` will be called within `drop()`. However,
// // `destroy()` also will fail on its assertion since
// // we didn't unlock the section.
// }
#[test]
fn test_critical_section_multithread() {
use std::thread;
use std::time::Duration;
struct Resource {
value: u32,
mutex: OwnedCriticalSection,
}
let mut resource = Resource {
value: 0,
mutex: OwnedCriticalSection::new(),
};
resource.mutex.init();
// Make a vector to hold the children which are spawned.
let mut children = vec![];
// Rust compilter doesn't allow a pointer to be passed across threads.
// A hacky way to do that is to cast the pointer into a value, then
// the value, which is actually an address, can be copied into threads.
let resource_ptr = &mut resource as *mut Resource as usize;
for i in 0..10 {
// Spin up another thread
children.push(thread::spawn(move || {
let res = unsafe {
let ptr = resource_ptr as *mut Resource;
&mut (*ptr)
};
assert_eq!(res as *mut Resource as usize, resource_ptr);
// Test fails after commenting `AutoLock` and since the order
// to run the threads is random.
// The scope of `_guard` is a critical section.
let _guard = AutoLock::new(&mut res.mutex); // -------------+
// |
res.value = i; // | critical
thread::sleep(Duration::from_millis(1)); // | section
// |
(i, res.value) // |
})); // <-------------------------------------------------------+
}
for child in children {
// Wait for the thread to finish.
let (num, value) = child.join().unwrap();
assert_eq!(num, value)
}
}
#[test]
fn test_dummy_mutex_multithread() {
use std::sync::Mutex;
use std::thread;
use std::time::Duration;
struct Resource {
value: u32,
mutex: Mutex<()>,
}
let mut resource = Resource {
value: 0,
mutex: Mutex::new(()),
};
// Make a vector to hold the children which are spawned.
let mut children = vec![];
// Rust compilter doesn't allow a pointer to be passed across threads.
// A hacky way to do that is to cast the pointer into a value, then
// the value, which is actually an address, can be copied into threads.
let resource_ptr = &mut resource as *mut Resource as usize;
for i in 0..10 {
// Spin up another thread
children.push(thread::spawn(move || {
let res = unsafe {
let ptr = resource_ptr as *mut Resource;
&mut (*ptr)
};
assert_eq!(res as *mut Resource as usize, resource_ptr);
// Test fails after commenting res.mutex.lock() since the order
// to run the threads is random.
// The scope of `_guard` is a critical section.
let _guard = res.mutex.lock().unwrap(); // -----------------+
// |
res.value = i; // | critical
thread::sleep(Duration::from_millis(1)); // | section
// |
(i, res.value) // |
})); // <-------------------------------------------------------+
}
for child in children {
// Wait for the thread to finish.
let (num, value) = child.join().unwrap();
assert_eq!(num, value)
}
}

Просмотреть файл

@ -0,0 +1,42 @@
use super::coreaudio_sys_utils::sys::*;
pub const DEFAULT_INPUT_DEVICE_PROPERTY_ADDRESS: AudioObjectPropertyAddress =
AudioObjectPropertyAddress {
mSelector: kAudioHardwarePropertyDefaultInputDevice,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
pub const DEFAULT_OUTPUT_DEVICE_PROPERTY_ADDRESS: AudioObjectPropertyAddress =
AudioObjectPropertyAddress {
mSelector: kAudioHardwarePropertyDefaultOutputDevice,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
pub const DEVICE_IS_ALIVE_PROPERTY_ADDRESS: AudioObjectPropertyAddress =
AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyDeviceIsAlive,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
pub const DEVICES_PROPERTY_ADDRESS: AudioObjectPropertyAddress = AudioObjectPropertyAddress {
mSelector: kAudioHardwarePropertyDevices,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
pub const INPUT_DATA_SOURCE_PROPERTY_ADDRESS: AudioObjectPropertyAddress =
AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyDataSource,
mScope: kAudioDevicePropertyScopeInput,
mElement: kAudioObjectPropertyElementMaster,
};
pub const OUTPUT_DATA_SOURCE_PROPERTY_ADDRESS: AudioObjectPropertyAddress =
AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyDataSource,
mScope: kAudioDevicePropertyScopeOutput,
mElement: kAudioObjectPropertyElementMaster,
};

Просмотреть файл

@ -0,0 +1,68 @@
// Copyright © 2018 Mozilla Foundation
//
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details.
use cubeb_backend::SampleFormat as fmt;
use std::mem;
pub fn allocate_array_by_size<T>(size: usize) -> Vec<T> {
assert_eq!(size % mem::size_of::<T>(), 0);
let elements = size / mem::size_of::<T>();
allocate_array::<T>(elements)
}
pub fn allocate_array<T>(elements: usize) -> Vec<T> {
let mut array = Vec::<T>::with_capacity(elements);
unsafe {
array.set_len(elements);
}
array
}
pub fn forget_vec<T>(v: Vec<T>) -> (*mut T, usize) {
// Drop any excess capacity by into_boxed_slice.
let mut slice = v.into_boxed_slice();
let ptr_and_len = (slice.as_mut_ptr(), slice.len());
mem::forget(slice); // Leak the memory to the external code.
ptr_and_len
}
#[inline]
pub fn retake_forgotten_vec<T>(ptr: *mut T, len: usize) -> Vec<T> {
unsafe { Vec::from_raw_parts(ptr, len, len) }
}
pub fn cubeb_sample_size(format: fmt) -> usize {
match format {
fmt::S16LE | fmt::S16BE | fmt::S16NE => mem::size_of::<i16>(),
fmt::Float32LE | fmt::Float32BE | fmt::Float32NE => mem::size_of::<f32>(),
}
}
#[test]
fn test_forget_vec_and_retake_it() {
let expected: Vec<u32> = (10..20).collect();
let leaked = expected.clone();
let (ptr, len) = forget_vec(leaked);
let retaken = retake_forgotten_vec(ptr, len);
for (idx, data) in retaken.iter().enumerate() {
assert_eq!(*data, expected[idx]);
}
}
#[test]
fn test_cubeb_sample_size() {
let pairs = [
(fmt::S16LE, mem::size_of::<i16>()),
(fmt::S16BE, mem::size_of::<i16>()),
(fmt::S16NE, mem::size_of::<i16>()),
(fmt::Float32LE, mem::size_of::<f32>()),
(fmt::Float32BE, mem::size_of::<f32>()),
(fmt::Float32NE, mem::size_of::<f32>()),
];
for pair in pairs.iter() {
let (fotmat, size) = pair;
assert_eq!(cubeb_sample_size(*fotmat), *size);
}
}

Просмотреть файл

@ -0,0 +1,17 @@
// Copyright © 2018 Mozilla Foundation
//
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details.
use crate::backend::AudioUnitContext;
use cubeb_backend::{capi, ffi};
use std::os::raw::{c_char, c_int};
// Entry point from C code.
#[no_mangle]
pub unsafe extern "C" fn audiounit_rust_init(
c: *mut *mut ffi::cubeb,
context_name: *const c_char,
) -> c_int {
capi::capi_init::<AudioUnitContext>(c, context_name)
}

Просмотреть файл

@ -0,0 +1,15 @@
// Copyright © 2018 Mozilla Foundation
//
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details.
extern crate atomic;
#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate cubeb_backend;
mod backend;
mod capi;
pub use crate::capi::audiounit_rust_init;