зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
487c109761
Коммит
f6d72027f3
|
@ -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;
|
Загрузка…
Ссылка в новой задаче