зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1880244 - Update cubeb-coreaudio-rs to 7b10217149. r=cubeb-reviewers,padenot
Differential Revision: https://phabricator.services.mozilla.com/D204124
This commit is contained in:
Родитель
5d779058a8
Коммит
9b115afb13
|
@ -65,9 +65,9 @@ git = "https://github.com/mozilla/audioipc"
|
|||
rev = "409e11f8de6288e9ddfe269654523735302e59e6"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source."git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=d23ab55eab684b46f46e1da177c8814f6103a009"]
|
||||
[source."git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=7b1021714989bedef3d40a843a516273db38beaa"]
|
||||
git = "https://github.com/mozilla/cubeb-coreaudio-rs"
|
||||
rev = "d23ab55eab684b46f46e1da177c8814f6103a009"
|
||||
rev = "7b1021714989bedef3d40a843a516273db38beaa"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source."git+https://github.com/mozilla/cubeb-pulse-rs?rev=8ff972c8e2ec1782ff262ac4071c0415e69b1367"]
|
||||
|
|
|
@ -910,7 +910,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "coreaudio-sys-utils"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=d23ab55eab684b46f46e1da177c8814f6103a009#d23ab55eab684b46f46e1da177c8814f6103a009"
|
||||
source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=7b1021714989bedef3d40a843a516273db38beaa#7b1021714989bedef3d40a843a516273db38beaa"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"coreaudio-sys",
|
||||
|
@ -1156,7 +1156,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "cubeb-coreaudio"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=d23ab55eab684b46f46e1da177c8814f6103a009#d23ab55eab684b46f46e1da177c8814f6103a009"
|
||||
source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=7b1021714989bedef3d40a843a516273db38beaa#7b1021714989bedef3d40a843a516273db38beaa"
|
||||
dependencies = [
|
||||
"atomic",
|
||||
"audio-mixer",
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"87292d055a2fc0f070f54abd549a5f79ec8ac33611ecde80ba394f256b88294c","src/aggregate_device.rs":"7d2bd5f5fd7f3d008ebb69ad81f522ca0cb73db6d7b3e50ed1a63ea26ff721f4","src/audio_device_extensions.rs":"2852f9ce65581cb5cf3f8e581f2652087eae0a569ed429be362e636db6441b6b","src/audio_object.rs":"5447179330a862659a25bceedfdc5d29a1296f63490908d1c868c6b21c5f95a1","src/audio_unit.rs":"d783878930df4923b57ad230138c0f3fd6b0b9bb80a39725092ff4c6615162d8","src/cf_mutable_dict.rs":"fc42edd270c6dfb02f123214d2d8e487bbd62b5bd923b71eec13190fd0104d2a","src/dispatch.rs":"f6267fe587217c3d3ad5fe7f3a35955221c936103bf853c477a2e44eba5f1e46","src/lib.rs":"c93ed1411dd6cc39db44f57e0d7683bbc54745f84a3c9f9533a088895ec97abe","src/string.rs":"28f88b816c768bcfcc674a60d962b93f1c94e5e0f4cc8ed2a1301138b91039e7"},"package":null}
|
||||
{"files":{"Cargo.toml":"87292d055a2fc0f070f54abd549a5f79ec8ac33611ecde80ba394f256b88294c","src/aggregate_device.rs":"7d2bd5f5fd7f3d008ebb69ad81f522ca0cb73db6d7b3e50ed1a63ea26ff721f4","src/audio_device_extensions.rs":"5c869d791947d15eec8bffe0bb302fe32d0578111ffe0049213e720eb60a34e1","src/audio_object.rs":"34f7e038c1ed30d503d669d89f01864ae90e009a2fa74ef50fac343a53113ff2","src/audio_unit.rs":"d38007faed2ce4d88efb70054a1fdfadf8249d0e55b900eb3ac8eae04355bf2b","src/cf_mutable_dict.rs":"fc42edd270c6dfb02f123214d2d8e487bbd62b5bd923b71eec13190fd0104d2a","src/dispatch.rs":"24b6bcf0dcaa6618e03039cd060a274c8f9ed48264e14de465ae3aacb2daad57","src/lib.rs":"c93ed1411dd6cc39db44f57e0d7683bbc54745f84a3c9f9533a088895ec97abe","src/string.rs":"28f88b816c768bcfcc674a60d962b93f1c94e5e0f4cc8ed2a1301138b91039e7"},"package":null}
|
|
@ -1,3 +1,4 @@
|
|||
use crate::dispatch::*;
|
||||
use coreaudio_sys::*;
|
||||
|
||||
// See https://opensource.apple.com/source/WebCore/WebCore-7604.5.6/platform/spi/cf/CoreAudioSPI.h.auto.html
|
||||
|
@ -18,5 +19,6 @@ pub fn audio_device_duck(
|
|||
in_start_time: *const AudioTimeStamp,
|
||||
in_ramp_duration: f32,
|
||||
) -> OSStatus {
|
||||
debug_assert_running_serially();
|
||||
unsafe { AudioDeviceDuck(in_device, in_ducked_level, in_start_time, in_ramp_duration) }
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::dispatch::*;
|
||||
use coreaudio_sys::*;
|
||||
use std::fmt;
|
||||
use std::os::raw::c_void;
|
||||
|
@ -77,6 +78,7 @@ pub fn audio_object_set_property_data<T>(
|
|||
size: usize,
|
||||
data: *const T,
|
||||
) -> OSStatus {
|
||||
debug_assert_running_serially();
|
||||
unsafe {
|
||||
AudioObjectSetPropertyData(
|
||||
id,
|
||||
|
@ -99,6 +101,7 @@ pub fn audio_object_add_property_listener<T>(
|
|||
listener: audio_object_property_listener_proc,
|
||||
data: *mut T,
|
||||
) -> OSStatus {
|
||||
debug_assert_running_serially();
|
||||
unsafe { AudioObjectAddPropertyListener(id, address, Some(listener), data as *mut c_void) }
|
||||
}
|
||||
|
||||
|
@ -108,6 +111,7 @@ pub fn audio_object_remove_property_listener<T>(
|
|||
listener: audio_object_property_listener_proc,
|
||||
data: *mut T,
|
||||
) -> OSStatus {
|
||||
debug_assert_running_serially();
|
||||
unsafe { AudioObjectRemovePropertyListener(id, address, Some(listener), data as *mut c_void) }
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::dispatch::debug_assert_running_serially;
|
||||
use coreaudio_sys::*;
|
||||
use std::convert::TryFrom;
|
||||
use std::os::raw::c_void;
|
||||
|
@ -13,6 +14,7 @@ pub fn audio_unit_get_property_info(
|
|||
) -> OSStatus {
|
||||
assert!(!unit.is_null());
|
||||
assert!(UInt32::try_from(*size).is_ok()); // Check if `size` can be converted to a UInt32.
|
||||
debug_assert_running_serially();
|
||||
unsafe {
|
||||
AudioUnitGetPropertyInfo(
|
||||
unit,
|
||||
|
@ -35,6 +37,7 @@ pub fn audio_unit_get_property<T>(
|
|||
) -> OSStatus {
|
||||
assert!(!unit.is_null());
|
||||
assert!(UInt32::try_from(*size).is_ok()); // Check if `size` can be converted to a UInt32.
|
||||
debug_assert_running_serially();
|
||||
unsafe {
|
||||
AudioUnitGetProperty(
|
||||
unit,
|
||||
|
@ -56,6 +59,7 @@ pub fn audio_unit_set_property<T>(
|
|||
size: usize,
|
||||
) -> OSStatus {
|
||||
assert!(!unit.is_null());
|
||||
debug_assert_running_serially();
|
||||
unsafe {
|
||||
AudioUnitSetProperty(
|
||||
unit,
|
||||
|
@ -76,6 +80,7 @@ pub fn audio_unit_get_parameter(
|
|||
value: &mut AudioUnitParameterValue,
|
||||
) -> OSStatus {
|
||||
assert!(!unit.is_null());
|
||||
debug_assert_running_serially();
|
||||
unsafe {
|
||||
AudioUnitGetParameter(
|
||||
unit,
|
||||
|
@ -96,30 +101,36 @@ pub fn audio_unit_set_parameter(
|
|||
buffer_offset_in_frames: UInt32,
|
||||
) -> OSStatus {
|
||||
assert!(!unit.is_null());
|
||||
debug_assert_running_serially();
|
||||
unsafe { AudioUnitSetParameter(unit, id, scope, element, value, buffer_offset_in_frames) }
|
||||
}
|
||||
|
||||
pub fn audio_unit_initialize(unit: AudioUnit) -> OSStatus {
|
||||
assert!(!unit.is_null());
|
||||
debug_assert_running_serially();
|
||||
unsafe { AudioUnitInitialize(unit) }
|
||||
}
|
||||
|
||||
pub fn audio_unit_uninitialize(unit: AudioUnit) -> OSStatus {
|
||||
assert!(!unit.is_null());
|
||||
debug_assert_running_serially();
|
||||
unsafe { AudioUnitUninitialize(unit) }
|
||||
}
|
||||
|
||||
pub fn dispose_audio_unit(unit: AudioUnit) -> OSStatus {
|
||||
debug_assert_running_serially();
|
||||
unsafe { AudioComponentInstanceDispose(unit) }
|
||||
}
|
||||
|
||||
pub fn audio_output_unit_start(unit: AudioUnit) -> OSStatus {
|
||||
assert!(!unit.is_null());
|
||||
debug_assert_running_serially();
|
||||
unsafe { AudioOutputUnitStart(unit) }
|
||||
}
|
||||
|
||||
pub fn audio_output_unit_stop(unit: AudioUnit) -> OSStatus {
|
||||
assert!(!unit.is_null());
|
||||
debug_assert_running_serially();
|
||||
unsafe { AudioOutputUnitStop(unit) }
|
||||
}
|
||||
|
||||
|
@ -155,6 +166,7 @@ pub fn audio_unit_add_property_listener<T>(
|
|||
data: *mut T,
|
||||
) -> OSStatus {
|
||||
assert!(!unit.is_null());
|
||||
debug_assert_running_serially();
|
||||
unsafe { AudioUnitAddPropertyListener(unit, id, Some(listener), data as *mut c_void) }
|
||||
}
|
||||
|
||||
|
@ -165,6 +177,7 @@ pub fn audio_unit_remove_property_listener_with_user_data<T>(
|
|||
data: *mut T,
|
||||
) -> OSStatus {
|
||||
assert!(!unit.is_null());
|
||||
debug_assert_running_serially();
|
||||
unsafe {
|
||||
AudioUnitRemovePropertyListenerWithUserData(unit, id, Some(listener), data as *mut c_void)
|
||||
}
|
||||
|
|
|
@ -3,40 +3,118 @@ use coreaudio_sys::*;
|
|||
use std::ffi::CString;
|
||||
use std::mem;
|
||||
use std::os::raw::c_void;
|
||||
use std::panic;
|
||||
use std::ptr;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
#[cfg(test)]
|
||||
use std::thread;
|
||||
#[cfg(test)]
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
|
||||
// Queue: A wrapper around `dispatch_queue_t`.
|
||||
pub const DISPATCH_QUEUE_LABEL: &str = "org.mozilla.cubeb";
|
||||
|
||||
pub fn get_serial_queue_singleton() -> &'static Queue {
|
||||
static SERIAL_QUEUE: OnceLock<Queue> = OnceLock::new();
|
||||
SERIAL_QUEUE.get_or_init(|| Queue::new(DISPATCH_QUEUE_LABEL))
|
||||
}
|
||||
|
||||
pub fn debug_assert_running_serially() {
|
||||
get_serial_queue_singleton().debug_assert_is_current();
|
||||
}
|
||||
|
||||
pub fn debug_assert_not_running_serially() {
|
||||
get_serial_queue_singleton().debug_assert_is_not_current();
|
||||
}
|
||||
|
||||
pub fn run_serially<F, B>(work: F) -> B
|
||||
where
|
||||
F: FnOnce() -> B,
|
||||
{
|
||||
get_serial_queue_singleton().run_sync(|| work()).unwrap()
|
||||
}
|
||||
|
||||
pub fn run_serially_forward_panics<F, B>(work: F) -> B
|
||||
where
|
||||
F: panic::UnwindSafe + FnOnce() -> B,
|
||||
{
|
||||
match run_serially(|| panic::catch_unwind(|| work())) {
|
||||
Ok(res) => res,
|
||||
Err(e) => panic::resume_unwind(e),
|
||||
}
|
||||
}
|
||||
|
||||
// Queue: A wrapper around `dispatch_queue_t` that is always serial.
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
#[derive(Debug)]
|
||||
pub struct Queue(dispatch_queue_t);
|
||||
pub struct Queue {
|
||||
queue: Mutex<dispatch_queue_t>,
|
||||
owned: AtomicBool,
|
||||
}
|
||||
|
||||
impl Queue {
|
||||
pub fn new(label: &str) -> Self {
|
||||
pub fn new_with_target(label: &str, target: &Queue) -> Self {
|
||||
const DISPATCH_QUEUE_SERIAL: dispatch_queue_attr_t =
|
||||
ptr::null_mut::<dispatch_queue_attr_s>();
|
||||
let label = CString::new(label).unwrap();
|
||||
let c_string = label.as_ptr();
|
||||
let queue = Self(unsafe { dispatch_queue_create(c_string, DISPATCH_QUEUE_SERIAL) });
|
||||
let queue = {
|
||||
let target_guard = target.queue.lock().unwrap();
|
||||
Self {
|
||||
queue: Mutex::new(unsafe {
|
||||
dispatch_queue_create_with_target(
|
||||
c_string,
|
||||
DISPATCH_QUEUE_SERIAL,
|
||||
*target_guard,
|
||||
)
|
||||
}),
|
||||
owned: AtomicBool::new(true),
|
||||
}
|
||||
};
|
||||
queue.set_should_cancel(Box::new(AtomicBool::new(false)));
|
||||
queue
|
||||
}
|
||||
|
||||
pub fn new(label: &str) -> Self {
|
||||
Queue::new_with_target(label, &Queue::get_global_queue())
|
||||
}
|
||||
|
||||
pub fn get_global_queue() -> Self {
|
||||
Self {
|
||||
queue: Mutex::new(unsafe { dispatch_get_global_queue(QOS_CLASS_DEFAULT as isize, 0) }),
|
||||
owned: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
pub fn debug_assert_is_current(&self) {
|
||||
let guard = self.queue.lock().unwrap();
|
||||
unsafe {
|
||||
dispatch_assert_queue(self.0);
|
||||
dispatch_assert_queue(*guard);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
pub fn debug_assert_is_current(&self) {}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
pub fn debug_assert_is_not_current(&self) {
|
||||
let guard = self.queue.lock().unwrap();
|
||||
unsafe {
|
||||
dispatch_assert_queue_not(*guard);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
pub fn debug_assert_is_not_current(&self) {}
|
||||
|
||||
pub fn run_async<F>(&self, work: F)
|
||||
where
|
||||
F: Send + FnOnce(),
|
||||
{
|
||||
let should_cancel = self.get_should_cancel();
|
||||
let guard = self.queue.lock().unwrap();
|
||||
let should_cancel = self.get_should_cancel(*guard);
|
||||
let (closure, executor) = Self::create_closure_and_executor(|| {
|
||||
if should_cancel.map_or(false, |v| v.load(Ordering::SeqCst)) {
|
||||
return;
|
||||
|
@ -44,15 +122,22 @@ impl Queue {
|
|||
work();
|
||||
});
|
||||
unsafe {
|
||||
dispatch_async_f(self.0, closure, executor);
|
||||
dispatch_async_f(*guard, closure, executor);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_sync<F>(&self, work: F)
|
||||
pub fn run_after<F>(&self, when: Instant, work: F)
|
||||
where
|
||||
F: Send + FnOnce(),
|
||||
{
|
||||
let should_cancel = self.get_should_cancel();
|
||||
let now = Instant::now();
|
||||
if when <= now {
|
||||
return self.run_async(work);
|
||||
}
|
||||
let nanos = (when - now).as_nanos() as i64;
|
||||
let when = unsafe { dispatch_time(DISPATCH_TIME_NOW.into(), nanos) };
|
||||
let guard = self.queue.lock().unwrap();
|
||||
let should_cancel = self.get_should_cancel(*guard);
|
||||
let (closure, executor) = Self::create_closure_and_executor(|| {
|
||||
if should_cancel.map_or(false, |v| v.load(Ordering::SeqCst)) {
|
||||
return;
|
||||
|
@ -60,38 +145,85 @@ impl Queue {
|
|||
work();
|
||||
});
|
||||
unsafe {
|
||||
dispatch_sync_f(self.0, closure, executor);
|
||||
dispatch_after_f(when, *guard, closure, executor);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_final<F>(&self, work: F)
|
||||
pub fn run_sync<F, B>(&self, work: F) -> Option<B>
|
||||
where
|
||||
F: Send + FnOnce(),
|
||||
F: FnOnce() -> B,
|
||||
{
|
||||
let should_cancel = self.get_should_cancel();
|
||||
let (closure, executor) = Self::create_closure_and_executor(|| {
|
||||
work();
|
||||
should_cancel
|
||||
.expect("dispatch context should be allocated!")
|
||||
.store(true, Ordering::SeqCst);
|
||||
});
|
||||
unsafe {
|
||||
dispatch_sync_f(self.0, closure, executor);
|
||||
let queue: Option<dispatch_queue_t>;
|
||||
let mut res: Option<B> = None;
|
||||
let cex: Option<(*mut c_void, dispatch_function_t)>;
|
||||
{
|
||||
let guard = self.queue.lock().unwrap();
|
||||
queue = Some(*guard);
|
||||
let should_cancel = self.get_should_cancel(*guard);
|
||||
cex = Some(Self::create_closure_and_executor(|| {
|
||||
if should_cancel.map_or(false, |v| v.load(Ordering::SeqCst)) {
|
||||
return;
|
||||
}
|
||||
res = Some(work());
|
||||
}));
|
||||
}
|
||||
let (closure, executor) = cex.unwrap();
|
||||
unsafe {
|
||||
dispatch_sync_f(queue.unwrap(), closure, executor);
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn get_should_cancel(&self) -> Option<&mut AtomicBool> {
|
||||
pub fn run_final<F, B>(&self, work: F) -> Option<B>
|
||||
where
|
||||
F: FnOnce() -> B,
|
||||
{
|
||||
assert!(
|
||||
self.owned.load(Ordering::SeqCst),
|
||||
"Doesn't make sense to finalize global queue"
|
||||
);
|
||||
let queue: Option<dispatch_queue_t>;
|
||||
let mut res: Option<B> = None;
|
||||
let cex: Option<(*mut c_void, dispatch_function_t)>;
|
||||
{
|
||||
let guard = self.queue.lock().unwrap();
|
||||
queue = Some(*guard);
|
||||
let should_cancel = self.get_should_cancel(*guard);
|
||||
debug_assert!(
|
||||
should_cancel.is_some(),
|
||||
"dispatch context should be allocated!"
|
||||
);
|
||||
cex = Some(Self::create_closure_and_executor(|| {
|
||||
res = Some(work());
|
||||
should_cancel
|
||||
.expect("dispatch context should be allocated!")
|
||||
.store(true, Ordering::SeqCst);
|
||||
}));
|
||||
}
|
||||
let (closure, executor) = cex.unwrap();
|
||||
unsafe {
|
||||
let context = dispatch_get_context(
|
||||
mem::transmute::<dispatch_queue_t, dispatch_object_t>(self.0),
|
||||
) as *mut AtomicBool;
|
||||
dispatch_sync_f(queue.unwrap(), closure, executor);
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn get_should_cancel(&self, queue: dispatch_queue_t) -> Option<&mut AtomicBool> {
|
||||
if !self.owned.load(Ordering::SeqCst) {
|
||||
return None;
|
||||
}
|
||||
unsafe {
|
||||
let context =
|
||||
dispatch_get_context(mem::transmute::<dispatch_queue_t, dispatch_object_t>(queue))
|
||||
as *mut AtomicBool;
|
||||
context.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
fn set_should_cancel(&self, context: Box<AtomicBool>) {
|
||||
assert!(self.owned.load(Ordering::SeqCst));
|
||||
unsafe {
|
||||
let queue = mem::transmute::<dispatch_queue_t, dispatch_object_t>(self.0);
|
||||
let guard = self.queue.lock().unwrap();
|
||||
let queue = mem::transmute::<dispatch_queue_t, dispatch_object_t>(*guard);
|
||||
// Leak the context from Box.
|
||||
dispatch_set_context(queue, Box::into_raw(context) as *mut c_void);
|
||||
|
||||
|
@ -106,13 +238,13 @@ impl Queue {
|
|||
}
|
||||
|
||||
fn release(&self) {
|
||||
let guard = self.queue.lock().unwrap();
|
||||
let queue = *guard;
|
||||
unsafe {
|
||||
// This will release the inner `dispatch_queue_t` asynchronously.
|
||||
// TODO: It's incredibly unsafe to call `transmute` directly.
|
||||
// Find another way to release the queue.
|
||||
dispatch_release(mem::transmute::<dispatch_queue_t, dispatch_object_t>(
|
||||
self.0,
|
||||
));
|
||||
dispatch_release(mem::transmute::<dispatch_queue_t, dispatch_object_t>(queue));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -143,23 +275,35 @@ impl Queue {
|
|||
|
||||
impl Drop for Queue {
|
||||
fn drop(&mut self) {
|
||||
self.release();
|
||||
if self.owned.load(Ordering::SeqCst) {
|
||||
self.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Queue {
|
||||
fn clone(&self) -> Self {
|
||||
assert!(
|
||||
self.owned.load(Ordering::SeqCst),
|
||||
"No need to clone a static queue"
|
||||
);
|
||||
let guard = self.queue.lock().unwrap();
|
||||
let queue = *guard;
|
||||
// TODO: It's incredibly unsafe to call `transmute` directly.
|
||||
// Find another way to release the queue.
|
||||
unsafe {
|
||||
dispatch_retain(mem::transmute::<dispatch_queue_t, dispatch_object_t>(
|
||||
self.0,
|
||||
));
|
||||
dispatch_retain(mem::transmute::<dispatch_queue_t, dispatch_object_t>(queue));
|
||||
}
|
||||
Self {
|
||||
queue: Mutex::new(queue),
|
||||
owned: AtomicBool::new(true),
|
||||
}
|
||||
Self(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Queue {}
|
||||
unsafe impl Sync for Queue {}
|
||||
|
||||
#[test]
|
||||
fn run_tasks_in_order() {
|
||||
let mut visited = Vec::<u32>::new();
|
||||
|
@ -176,12 +320,12 @@ fn run_tasks_in_order() {
|
|||
|
||||
let queue = Queue::new("Run tasks in order");
|
||||
|
||||
queue.run_sync(move || visit(1, ptr));
|
||||
queue.run_sync(move || visit(2, ptr));
|
||||
queue.run_async(move || visit(3, ptr));
|
||||
queue.run_async(move || visit(4, ptr));
|
||||
queue.run_sync(|| visit(1, ptr));
|
||||
queue.run_sync(|| visit(2, ptr));
|
||||
queue.run_async(|| visit(3, ptr));
|
||||
queue.run_async(|| visit(4, ptr));
|
||||
// Call sync here to block the current thread and make sure all the tasks are done.
|
||||
queue.run_sync(move || visit(5, ptr));
|
||||
queue.run_sync(|| visit(5, ptr));
|
||||
|
||||
assert_eq!(visited, vec![1, 2, 3, 4, 5]);
|
||||
}
|
||||
|
@ -203,14 +347,52 @@ fn run_final_task() {
|
|||
|
||||
let queue = Queue::new("Task after run_final will be cancelled");
|
||||
|
||||
queue.run_sync(move || visit(1, ptr));
|
||||
queue.run_async(move || visit(2, ptr));
|
||||
queue.run_final(move || visit(3, ptr));
|
||||
queue.run_async(move || visit(4, ptr));
|
||||
queue.run_sync(move || visit(5, ptr));
|
||||
queue.run_sync(|| visit(1, ptr));
|
||||
queue.run_async(|| visit(2, ptr));
|
||||
queue.run_final(|| visit(3, ptr));
|
||||
queue.run_async(|| visit(4, ptr));
|
||||
queue.run_sync(|| visit(5, ptr));
|
||||
}
|
||||
// `queue` will be dropped asynchronously and then the `finalizer` of the `queue`
|
||||
// should be fired to clean up the `context` set in the `queue`.
|
||||
|
||||
assert_eq!(visited, vec![1, 2, 3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sync_return_value() {
|
||||
let q = Queue::new("Test queue");
|
||||
assert_eq!(q.run_sync(|| 42), Some(42));
|
||||
assert_eq!(q.run_final(|| "foo"), Some("foo"));
|
||||
assert_eq!(q.run_sync(|| Ok::<(), u32>(())), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_after() {
|
||||
let mut visited = Vec::<u32>::new();
|
||||
|
||||
{
|
||||
// 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 ptr = &mut visited as *mut Vec<u32> as usize;
|
||||
|
||||
fn visit(v: u32, visited_ptr: usize) {
|
||||
let visited = unsafe { &mut *(visited_ptr as *mut Vec<u32>) };
|
||||
visited.push(v);
|
||||
}
|
||||
|
||||
let queue = Queue::new("Task after run_final will be cancelled");
|
||||
|
||||
queue.run_async(|| visit(1, ptr));
|
||||
queue.run_after(Instant::now() + Duration::from_millis(10), || visit(2, ptr));
|
||||
queue.run_after(Instant::now() + Duration::from_secs(1), || visit(3, ptr));
|
||||
queue.run_async(|| visit(4, ptr));
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
queue.run_final(|| visit(5, ptr));
|
||||
}
|
||||
// `queue` will be dropped asynchronously and then the `finalizer` of the `queue`
|
||||
// should be fired to clean up the `context` set in the `queue`.
|
||||
|
||||
assert_eq!(visited, vec![1, 4, 2, 5]);
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{".circleci/config.yml":"7f3dc865105ca8f33965a7958b1fe2e627ae2d5a703f3b2a4ab6e2e796018597",".editorconfig":"4e53b182bcc78b83d7e1b5c03efa14d22d4955c4ed2514d1ba4e99c1eb1a50ba",".githooks/pre-push":"8b8b26544cd56f54c0c33812551f786bb25cb08c86dbfeb6bf3daad881c826a1",".github/workflows/test.yml":"aa1998a3b104ad131805ca3513832cef3f65300192824f8b1efc9a5a0cc108f6",".travis.yml":"dc07bac53f70f16c9bdf52264bdc58500ae6018c1b4c567bc7642f6b4ca3cc35","Cargo.toml":"d7e757e664c23fae52028f1dfc5917f92523c08702e3a1f95e1fd38ed714416c","LICENSE":"6e6f56aff5bbf3cbc60747e152fb1a719bd0716aaf6d711c554f57d92e96297c","README.md":"0007782a05a5330f739ad789c19c82562c82e32386b0447000fc72c0d48405bc","build-audiounit-rust-in-cubeb.sh":"d228a05985dcd02ec1ecac66a2b64dae5a530804a25a7054ccc95905aedfb7ef","install_git_hook.sh":"d38c8e51e636f6b90b489621ac34ccd1d1b1f40dccce3d178ed1da1c5068f16d","install_rustfmt_clippy.sh":"4ae90d8dcb9757cb3ae4ae142ef80e5377c0dde61c63f4a3c32418646e80ca7b","run_device_tests.sh":"d717e598c96e4911d9494b18382d6bd3a8d5038b7d68d3166ad4336e237a97d8","run_sanitizers.sh":"84e93a0da137803018f37403511e8c92760be730426bf6cea34419d93d1a7ff8","run_tests.sh":"916a7ae4a406d2274417d6eca939a878db5adcb6144e5680d9d148bf90178f1c","src/backend/aggregate_device.rs":"43511107ba2a75a19340ac663c981362ca1b75b679b6c295d88b5035bd7e3619","src/backend/auto_release.rs":"050fdcee74cf46b9a8a85a877e166d72a853d33220f59cf734cbb6ea09daa441","src/backend/buffer_manager.rs":"e9bcf964347daa8952f98caa2746e34a31ea8908375204896593f56e4b6147ca","src/backend/device_property.rs":"a7622feaa41db1cd76fd35a85a022e44f4894e396a104a59008d5b8757d2ab4e","src/backend/mixer.rs":"ed299d3954e2a823060c870a8244673a7d4bca530830cb66b964d047a80ee3af","src/backend/mod.rs":"1591669c30a3d07754bfb39c9cb042cdd101f0ab89be13f6cdf74d376e441cf8","src/backend/resampler.rs":"48bf8f56ae8d60dbabca6417b768000619abee8731ac3902164b45651ac08a4d","src/backend/tests/aggregate_device.rs":"e3f94e118e1dd47941fbba4417de40bddc4254d9f06b1e938f58d8f1aa566a5c","src/backend/tests/api.rs":"cd7e7551e2e82b19da883621a494d2a6779c373f3ff2d12ee52fae8efec1e7b8","src/backend/tests/backlog.rs":"3b189a7e036543c467cc242af0ed3332721179ee2b1c8847a6db563546f1ac52","src/backend/tests/device_change.rs":"f68c2eaa55c3ec2a58894832fbca1e2a2e79e740b145f76a0f45452af465a934","src/backend/tests/device_property.rs":"ea0be5f8834be494cb33f854ce9d334b5763dc5287f949bcb4bd025d8a8b2d3b","src/backend/tests/interfaces.rs":"af8e3fdeb58226621699b29f1a90621b2260e3f17292dac54860cd05fe4eec71","src/backend/tests/manual.rs":"4a1634e86beb145d2703722a8be057a762953241329c82ee09acf7dc0f0d9d0c","src/backend/tests/mod.rs":"8dba770023d7f9c4228f0e11915347f0e07da5fd818e3ee4478c4b197af9aa2a","src/backend/tests/parallel.rs":"59632744e70616ab7037facb0787db339b96800c8cc397d203241548c5cfb7f5","src/backend/tests/tone.rs":"779cc14fc2a362bf7f26ce66ad70c0639501176175655a99b7fefb3c59d56c7a","src/backend/tests/utils.rs":"efb8b3709aff7ed5e2923566084de3e0709f3bd9c18a04f3310d7a3b86fa4b71","src/backend/utils.rs":"6c3ffbcd602e6cc9f56deb9ecb07b2eef2e6f074ef924178e466f380aae5c595","src/capi.rs":"21b66b70545bf04ec719928004d1d9adb45b24ced51288f5b2993d79aaf78f5f","src/lib.rs":"5e586d45cd6b3722f0a6736d9252593299269817a153eef1930a5fb9bfbb56f5","todo.md":"efc1f012eb9a331a040cad4ac03aa79307f25885f71b6fb38f3ad7af8d7d515c"},"package":null}
|
||||
{"files":{".circleci/config.yml":"7f3dc865105ca8f33965a7958b1fe2e627ae2d5a703f3b2a4ab6e2e796018597",".editorconfig":"4e53b182bcc78b83d7e1b5c03efa14d22d4955c4ed2514d1ba4e99c1eb1a50ba",".githooks/pre-push":"8b8b26544cd56f54c0c33812551f786bb25cb08c86dbfeb6bf3daad881c826a1",".github/workflows/test.yml":"ac8f4cf5b7631b5c738d50c0cf78113bd395940b9e76593904bbaf2d02d16a70",".travis.yml":"dc07bac53f70f16c9bdf52264bdc58500ae6018c1b4c567bc7642f6b4ca3cc35","Cargo.toml":"d7e757e664c23fae52028f1dfc5917f92523c08702e3a1f95e1fd38ed714416c","LICENSE":"6e6f56aff5bbf3cbc60747e152fb1a719bd0716aaf6d711c554f57d92e96297c","README.md":"0007782a05a5330f739ad789c19c82562c82e32386b0447000fc72c0d48405bc","build-audiounit-rust-in-cubeb.sh":"d228a05985dcd02ec1ecac66a2b64dae5a530804a25a7054ccc95905aedfb7ef","install_git_hook.sh":"d38c8e51e636f6b90b489621ac34ccd1d1b1f40dccce3d178ed1da1c5068f16d","install_rustfmt_clippy.sh":"4ae90d8dcb9757cb3ae4ae142ef80e5377c0dde61c63f4a3c32418646e80ca7b","run_device_tests.sh":"90c2542fa3ff8a35fed894fae3a1aa0157117b7f0e28df14b8e6f7b1f1f43797","run_sanitizers.sh":"84e93a0da137803018f37403511e8c92760be730426bf6cea34419d93d1a7ff8","run_tests.sh":"bae82f66dd47a060b6fdcc238520084aec1079d5b1b1d66d103baa1ffaa8773d","src/backend/aggregate_device.rs":"db7d644358090b1d65ff2d53ad854369790ae4ad7dfa12b79888c0002c1b4950","src/backend/auto_release.rs":"050fdcee74cf46b9a8a85a877e166d72a853d33220f59cf734cbb6ea09daa441","src/backend/buffer_manager.rs":"e9bcf964347daa8952f98caa2746e34a31ea8908375204896593f56e4b6147ca","src/backend/device_property.rs":"a7622feaa41db1cd76fd35a85a022e44f4894e396a104a59008d5b8757d2ab4e","src/backend/mixer.rs":"ed299d3954e2a823060c870a8244673a7d4bca530830cb66b964d047a80ee3af","src/backend/mod.rs":"44652840c745dc8be30125f1111e29e365b39ff06853c8628818083148e3c931","src/backend/resampler.rs":"48bf8f56ae8d60dbabca6417b768000619abee8731ac3902164b45651ac08a4d","src/backend/tests/aggregate_device.rs":"770cf90f32b5ab2203476031c1fbc8379b713baa97bec36f7fd0d77fef1efd60","src/backend/tests/api.rs":"d72d7c0de8d12e880966948be4686bcf8c789f0ef19cb435c242fd72f2d252f9","src/backend/tests/backlog.rs":"3b189a7e036543c467cc242af0ed3332721179ee2b1c8847a6db563546f1ac52","src/backend/tests/device_change.rs":"babf50326fb38db24fe80f24f546e1b6ad04319ae8835bb372d893fc9b3038a2","src/backend/tests/device_property.rs":"73c25f579a995e8a59c9b7d391813afb75f739b5e2f825480cba04499a1d46e8","src/backend/tests/interfaces.rs":"3bb50c8bb8bb2a6e2b0195122e54bad8ae6ab1695f740d0ee0895a3a4517d451","src/backend/tests/manual.rs":"e550cc8bb7619bb80b68e49bf7f475c029e0f1b34323d1d30edcbe322cf4efc7","src/backend/tests/mod.rs":"8dba770023d7f9c4228f0e11915347f0e07da5fd818e3ee4478c4b197af9aa2a","src/backend/tests/parallel.rs":"a7ebd579339c40ca64c0757cc9da6baec641e670f226e1b2ec5049894700bd7a","src/backend/tests/tone.rs":"b028c67777b6453a26190b6a49785dfe28556adcbe179cb10862ce0d47ee8509","src/backend/tests/utils.rs":"80d7e4ebc06b23c63a4d2867e0c80e0bfe05449fa55edd21e785ed2c089bf7d5","src/backend/utils.rs":"6c3ffbcd602e6cc9f56deb9ecb07b2eef2e6f074ef924178e466f380aae5c595","src/capi.rs":"21b66b70545bf04ec719928004d1d9adb45b24ced51288f5b2993d79aaf78f5f","src/lib.rs":"5e586d45cd6b3722f0a6736d9252593299269817a153eef1930a5fb9bfbb56f5","todo.md":"efc1f012eb9a331a040cad4ac03aa79307f25885f71b6fb38f3ad7af8d7d515c"},"package":null}
|
|
@ -4,36 +4,52 @@ on: [push, pull_request]
|
|||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macOS-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
continue-on-error: ${{ matrix.experimental }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-12, macos-13, macos-14]
|
||||
rust: [stable]
|
||||
experimental: [false]
|
||||
include:
|
||||
- rust: nightly
|
||||
- os: macos-14
|
||||
rust: nightly
|
||||
experimental: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install Rust
|
||||
run: rustup toolchain install ${{ matrix.rust }} --profile minimal --component rustfmt clippy
|
||||
|
||||
- name: Setup
|
||||
|
||||
- name: Setup Rust
|
||||
run: |
|
||||
rustup default ${{ matrix.rust }}
|
||||
toolchain=$(rustup default)
|
||||
echo "Use Rust toolchain: $toolchain"
|
||||
rustc --version
|
||||
cargo --version
|
||||
|
||||
|
||||
- name: Setup Audio
|
||||
if: ${{ matrix.os == 'macos-13' || matrix.os == 'macos-14' }}
|
||||
run: |
|
||||
brew install switchaudio-osx
|
||||
brew install blackhole-2ch
|
||||
SwitchAudioSource -s "BlackHole 2ch" -t input
|
||||
SwitchAudioSource -s "BlackHole 2ch" -t output
|
||||
|
||||
- name: Grant microphone access
|
||||
if: ${{ matrix.os == 'macos-13' || matrix.os == 'macos-14' }}
|
||||
env:
|
||||
tcc_extra_columns: ${{ matrix.os == 'macos-14' && ',NULL,NULL,''UNUSED'',1687786159' || '' }}
|
||||
run: sqlite3 $HOME/Library/Application\ Support/com.apple.TCC/TCC.db "INSERT OR IGNORE INTO access VALUES ('kTCCServiceMicrophone','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159${{ env.tcc_extra_columns }});"
|
||||
|
||||
- name: Build
|
||||
run: cargo build --verbose
|
||||
|
||||
|
||||
- name: Regular Test
|
||||
run: sh run_tests.sh
|
||||
|
||||
|
|
|
@ -15,8 +15,6 @@ cargo test test_plug_and_unplug_device -- --ignored --nocapture
|
|||
cargo test test_register_device_changed_callback_to_check_default_device_changed_input -- --ignored --nocapture
|
||||
cargo test test_register_device_changed_callback_to_check_default_device_changed_output -- --ignored --nocapture
|
||||
cargo test test_register_device_changed_callback_to_check_default_device_changed_duplex -- --ignored --nocapture
|
||||
cargo test test_register_device_changed_callback_to_check_input_alive_changed_input -- --ignored --nocapture
|
||||
cargo test test_register_device_changed_callback_to_check_input_alive_changed_duplex -- --ignored --nocapture
|
||||
|
||||
cargo test test_destroy_input_stream_after_unplugging_a_nondefault_input_device -- --ignored --nocapture
|
||||
cargo test test_suspend_input_stream_by_unplugging_a_nondefault_input_device -- --ignored --nocapture
|
||||
|
|
|
@ -39,8 +39,9 @@ cargo clippy -- -D warnings
|
|||
|
||||
# Regular Tests
|
||||
cargo test --verbose
|
||||
cargo test test_configure_output -- --ignored
|
||||
cargo test test_aggregate -- --ignored --test-threads=1
|
||||
|
||||
# Timing sensitive tests must run serially so they cannot be impacted by other tasks on the queue
|
||||
cargo test test_ops_timing_sensitive -- --ignored --test-threads=1
|
||||
|
||||
# Parallel Tests
|
||||
cargo test test_parallel -- --ignored --nocapture --test-threads=1
|
||||
|
|
|
@ -69,6 +69,7 @@ impl AggregateDevice {
|
|||
input_id: AudioObjectID,
|
||||
output_id: AudioObjectID,
|
||||
) -> std::result::Result<Self, Error> {
|
||||
debug_assert_running_serially();
|
||||
let plugin_id = Self::get_system_plugin_id()?;
|
||||
let device_id = Self::create_blank_device_sync(plugin_id)?;
|
||||
|
||||
|
@ -399,12 +400,12 @@ impl AggregateDevice {
|
|||
let sub_devices = CFArrayCreateMutable(ptr::null(), 0, &kCFTypeArrayCallBacks);
|
||||
// The order of the items in the array is significant and is used to determine the order of the streams
|
||||
// of the AudioAggregateDevice.
|
||||
for device in output_sub_devices {
|
||||
for device in input_sub_devices {
|
||||
let uid = get_device_global_uid(device)?;
|
||||
CFArrayAppendValue(sub_devices, uid.get_raw() as *const c_void);
|
||||
}
|
||||
|
||||
for device in input_sub_devices {
|
||||
for device in output_sub_devices {
|
||||
let uid = get_device_global_uid(device)?;
|
||||
CFArrayAppendValue(sub_devices, uid.get_raw() as *const c_void);
|
||||
}
|
||||
|
@ -466,6 +467,28 @@ impl AggregateDevice {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_master_device_uid(device_id: AudioDeviceID) -> std::result::Result<String, Error> {
|
||||
let address = AudioObjectPropertyAddress {
|
||||
mSelector: kAudioAggregateDevicePropertyMainSubDevice,
|
||||
mScope: kAudioObjectPropertyScopeGlobal,
|
||||
mElement: kAudioObjectPropertyElementMaster,
|
||||
};
|
||||
|
||||
let mut master: CFStringRef = ptr::null_mut();
|
||||
let mut size = mem::size_of::<CFStringRef>();
|
||||
let status = audio_object_get_property_data(device_id, &address, &mut size, &mut master);
|
||||
if status != NO_ERR {
|
||||
return Err(Error::from(status));
|
||||
}
|
||||
|
||||
if master.is_null() {
|
||||
return Ok(String::default());
|
||||
}
|
||||
|
||||
let master = StringRef::new(master as _);
|
||||
Ok(master.into_string())
|
||||
}
|
||||
|
||||
pub fn set_master_device(
|
||||
device_id: AudioDeviceID,
|
||||
primary_id: AudioDeviceID,
|
||||
|
@ -480,12 +503,12 @@ impl AggregateDevice {
|
|||
);
|
||||
|
||||
let address = AudioObjectPropertyAddress {
|
||||
mSelector: kAudioAggregateDevicePropertyMasterSubDevice,
|
||||
mSelector: kAudioAggregateDevicePropertyMainSubDevice,
|
||||
mScope: kAudioObjectPropertyScopeGlobal,
|
||||
mElement: kAudioObjectPropertyElementMaster,
|
||||
};
|
||||
|
||||
// Master become the 1st sub device of the primary device
|
||||
// The master device will be the 1st sub device of the primary device.
|
||||
let output_sub_devices = Self::get_sub_devices(primary_id)?;
|
||||
assert!(!output_sub_devices.is_empty());
|
||||
let master_sub_device_uid = get_device_global_uid(output_sub_devices[0]).unwrap();
|
||||
|
@ -548,16 +571,23 @@ impl AggregateDevice {
|
|||
return Err(Error::from(status));
|
||||
}
|
||||
|
||||
let master_sub_device_uid = Self::get_master_device_uid(device_id)?;
|
||||
|
||||
let address = AudioObjectPropertyAddress {
|
||||
mSelector: kAudioSubDevicePropertyDriftCompensation,
|
||||
mScope: kAudioObjectPropertyScopeGlobal,
|
||||
mElement: kAudioObjectPropertyElementMaster,
|
||||
};
|
||||
|
||||
// Start from the second device since the first is the master clock
|
||||
for device in &sub_devices[1..] {
|
||||
for &device in &sub_devices {
|
||||
let uid = get_device_global_uid(device)
|
||||
.map(|sr| sr.into_string())
|
||||
.unwrap_or_default();
|
||||
if uid == master_sub_device_uid {
|
||||
continue;
|
||||
}
|
||||
let status = audio_object_set_property_data(
|
||||
*device,
|
||||
device,
|
||||
&address,
|
||||
mem::size_of::<u32>(),
|
||||
&DRIFT_COMPENSATION,
|
||||
|
@ -671,6 +701,7 @@ impl Default for AggregateDevice {
|
|||
|
||||
impl Drop for AggregateDevice {
|
||||
fn drop(&mut self) {
|
||||
debug_assert_running_serially();
|
||||
if self.plugin_id != kAudioObjectUnknown && self.device_id != kAudioObjectUnknown {
|
||||
if let Err(r) = Self::destroy_device(self.plugin_id, self.device_id) {
|
||||
cubeb_log!(
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -3,6 +3,8 @@ use super::utils::{
|
|||
test_get_drift_compensations, test_get_master_device, DeviceFilter, Scope,
|
||||
};
|
||||
use super::*;
|
||||
use std::iter::zip;
|
||||
use std::panic;
|
||||
|
||||
// AggregateDevice::set_sub_devices
|
||||
// ------------------------------------
|
||||
|
@ -19,21 +21,27 @@ fn test_aggregate_set_sub_devices_for_an_unknown_aggregate_device() {
|
|||
let default_input = default_input.unwrap();
|
||||
let default_output = default_output.unwrap();
|
||||
assert!(
|
||||
AggregateDevice::set_sub_devices(kAudioObjectUnknown, default_input, default_output)
|
||||
.is_err()
|
||||
run_serially_forward_panics(|| AggregateDevice::set_sub_devices(
|
||||
kAudioObjectUnknown,
|
||||
default_input,
|
||||
default_output
|
||||
))
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_aggregate_set_sub_devices_for_unknown_devices() {
|
||||
// If aggregate device id is kAudioObjectUnknown, we are unable to set device list.
|
||||
assert!(AggregateDevice::set_sub_devices(
|
||||
kAudioObjectUnknown,
|
||||
kAudioObjectUnknown,
|
||||
kAudioObjectUnknown
|
||||
)
|
||||
.is_err());
|
||||
run_serially_forward_panics(|| {
|
||||
// If aggregate device id is kAudioObjectUnknown, we are unable to set device list.
|
||||
assert!(AggregateDevice::set_sub_devices(
|
||||
kAudioObjectUnknown,
|
||||
kAudioObjectUnknown,
|
||||
kAudioObjectUnknown
|
||||
)
|
||||
.is_err());
|
||||
});
|
||||
}
|
||||
|
||||
// AggregateDevice::get_sub_devices
|
||||
|
@ -48,7 +56,13 @@ fn test_aggregate_get_sub_devices() {
|
|||
// containing `device` itself if it's not an aggregate device. This test assumes devices
|
||||
// is not an empty aggregate device (Test will panic when calling get_sub_devices with
|
||||
// an empty aggregate device).
|
||||
let sub_devices = AggregateDevice::get_sub_devices(device).unwrap();
|
||||
println!(
|
||||
"get_sub_devices({}={})",
|
||||
device,
|
||||
run_serially_forward_panics(|| get_device_uid(device))
|
||||
);
|
||||
let sub_devices =
|
||||
run_serially_forward_panics(|| AggregateDevice::get_sub_devices(device).unwrap());
|
||||
// TODO: If the device is a blank aggregate device, then the assertion fails!
|
||||
assert!(!sub_devices.is_empty());
|
||||
}
|
||||
|
@ -57,8 +71,10 @@ fn test_aggregate_get_sub_devices() {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_aggregate_get_sub_devices_for_a_unknown_device() {
|
||||
let devices = AggregateDevice::get_sub_devices(kAudioObjectUnknown).unwrap();
|
||||
assert!(devices.is_empty());
|
||||
run_serially_forward_panics(|| {
|
||||
let devices = AggregateDevice::get_sub_devices(kAudioObjectUnknown).unwrap();
|
||||
assert!(devices.is_empty());
|
||||
});
|
||||
}
|
||||
|
||||
// AggregateDevice::set_master_device
|
||||
|
@ -66,7 +82,11 @@ fn test_aggregate_get_sub_devices_for_a_unknown_device() {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_aggregate_set_master_device_for_an_unknown_aggregate_device() {
|
||||
assert!(AggregateDevice::set_master_device(kAudioObjectUnknown, kAudioObjectUnknown).is_err());
|
||||
run_serially_forward_panics(|| {
|
||||
assert!(
|
||||
AggregateDevice::set_master_device(kAudioObjectUnknown, kAudioObjectUnknown).is_err()
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// AggregateDevice::activate_clock_drift_compensation
|
||||
|
@ -74,7 +94,9 @@ fn test_aggregate_set_master_device_for_an_unknown_aggregate_device() {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_aggregate_activate_clock_drift_compensation_for_an_unknown_aggregate_device() {
|
||||
assert!(AggregateDevice::activate_clock_drift_compensation(kAudioObjectUnknown).is_err());
|
||||
run_serially_forward_panics(|| {
|
||||
assert!(AggregateDevice::activate_clock_drift_compensation(kAudioObjectUnknown).is_err());
|
||||
});
|
||||
}
|
||||
|
||||
// AggregateDevice::destroy_device
|
||||
|
@ -82,60 +104,55 @@ fn test_aggregate_activate_clock_drift_compensation_for_an_unknown_aggregate_dev
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_aggregate_destroy_device_for_unknown_plugin_and_aggregate_devices() {
|
||||
assert!(AggregateDevice::destroy_device(kAudioObjectUnknown, kAudioObjectUnknown).is_err())
|
||||
run_serially_forward_panics(|| {
|
||||
assert!(AggregateDevice::destroy_device(kAudioObjectUnknown, kAudioObjectUnknown).is_err())
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_aggregate_destroy_aggregate_device_for_a_unknown_aggregate_device() {
|
||||
let plugin = AggregateDevice::get_system_plugin_id().unwrap();
|
||||
assert!(AggregateDevice::destroy_device(plugin, kAudioObjectUnknown).is_err());
|
||||
run_serially_forward_panics(|| {
|
||||
let plugin = AggregateDevice::get_system_plugin_id().unwrap();
|
||||
assert!(AggregateDevice::destroy_device(plugin, kAudioObjectUnknown).is_err());
|
||||
});
|
||||
}
|
||||
|
||||
// Default Ignored Tests
|
||||
// ================================================================================================
|
||||
// The following tests that calls `AggregateDevice::create_blank_device` are marked `ignore` by
|
||||
// default since the device-collection-changed callbacks will be fired upon
|
||||
// `AggregateDevice::create_blank_device` is called (it will plug a new device in system!).
|
||||
// Some tests rely on the device-collection-changed callbacks in a certain way. The callbacks
|
||||
// fired from a unexpected `AggregateDevice::create_blank_device` will break those tests.
|
||||
|
||||
// AggregateDevice::create_blank_device_sync
|
||||
// ------------------------------------
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_aggregate_create_blank_device() {
|
||||
// TODO: Test this when there is no available devices.
|
||||
let plugin = AggregateDevice::get_system_plugin_id().unwrap();
|
||||
let device = AggregateDevice::create_blank_device_sync(plugin).unwrap();
|
||||
let plugin = run_serially(|| AggregateDevice::get_system_plugin_id()).unwrap();
|
||||
let device = run_serially(|| AggregateDevice::create_blank_device_sync(plugin)).unwrap();
|
||||
let devices = test_get_all_devices(DeviceFilter::IncludeAll);
|
||||
let device = devices.into_iter().find(|dev| dev == &device).unwrap();
|
||||
let uid = get_device_global_uid(device).unwrap().into_string();
|
||||
let uid = run_serially(|| get_device_global_uid(device).unwrap().into_string());
|
||||
assert!(uid.contains(PRIVATE_AGGREGATE_DEVICE_NAME));
|
||||
assert!(AggregateDevice::destroy_device(plugin, device).is_ok());
|
||||
assert!(run_serially(|| AggregateDevice::destroy_device(plugin, device)).is_ok());
|
||||
}
|
||||
|
||||
// AggregateDevice::get_sub_devices
|
||||
// ------------------------------------
|
||||
#[test]
|
||||
#[ignore]
|
||||
#[should_panic]
|
||||
fn test_aggregate_get_sub_devices_for_blank_aggregate_devices() {
|
||||
// TODO: Test this when there is no available devices.
|
||||
let plugin = AggregateDevice::get_system_plugin_id().unwrap();
|
||||
let device = AggregateDevice::create_blank_device_sync(plugin).unwrap();
|
||||
// There is no sub device in a blank aggregate device!
|
||||
// AggregateDevice::get_sub_devices guarantees returning a non-empty devices vector, so
|
||||
// the following call will panic!
|
||||
let sub_devices = AggregateDevice::get_sub_devices(device).unwrap();
|
||||
assert!(sub_devices.is_empty());
|
||||
assert!(AggregateDevice::destroy_device(plugin, device).is_ok());
|
||||
run_serially_forward_panics(|| {
|
||||
// TODO: Test this when there is no available devices.
|
||||
let plugin = AggregateDevice::get_system_plugin_id().unwrap();
|
||||
let device = AggregateDevice::create_blank_device_sync(plugin).unwrap();
|
||||
// There is no sub device in a blank aggregate device!
|
||||
// AggregateDevice::get_sub_devices guarantees returning a non-empty devices vector, so
|
||||
// the following call will panic!
|
||||
let sub_devices = AggregateDevice::get_sub_devices(device).unwrap();
|
||||
assert!(sub_devices.is_empty());
|
||||
assert!(AggregateDevice::destroy_device(plugin, device).is_ok());
|
||||
});
|
||||
}
|
||||
|
||||
// AggregateDevice::set_sub_devices_sync
|
||||
// ------------------------------------
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_aggregate_set_sub_devices() {
|
||||
let input_device = test_get_default_device(Scope::Input);
|
||||
let output_device = test_get_default_device(Scope::Output);
|
||||
|
@ -147,13 +164,20 @@ fn test_aggregate_set_sub_devices() {
|
|||
let input_device = input_device.unwrap();
|
||||
let output_device = output_device.unwrap();
|
||||
|
||||
let plugin = AggregateDevice::get_system_plugin_id().unwrap();
|
||||
let device = AggregateDevice::create_blank_device_sync(plugin).unwrap();
|
||||
assert!(AggregateDevice::set_sub_devices_sync(device, input_device, output_device).is_ok());
|
||||
let plugin = run_serially(|| AggregateDevice::get_system_plugin_id()).unwrap();
|
||||
let device = run_serially(|| AggregateDevice::create_blank_device_sync(plugin)).unwrap();
|
||||
assert!(run_serially(|| AggregateDevice::set_sub_devices_sync(
|
||||
device,
|
||||
input_device,
|
||||
output_device
|
||||
))
|
||||
.is_ok());
|
||||
|
||||
let sub_devices = AggregateDevice::get_sub_devices(device).unwrap();
|
||||
let input_sub_devices = AggregateDevice::get_sub_devices(input_device).unwrap();
|
||||
let output_sub_devices = AggregateDevice::get_sub_devices(output_device).unwrap();
|
||||
let sub_devices = run_serially(|| AggregateDevice::get_sub_devices(device)).unwrap();
|
||||
let input_sub_devices =
|
||||
run_serially(|| AggregateDevice::get_sub_devices(input_device)).unwrap();
|
||||
let output_sub_devices =
|
||||
run_serially(|| AggregateDevice::get_sub_devices(output_device)).unwrap();
|
||||
|
||||
// TODO: There may be overlapping devices between input_sub_devices and output_sub_devices,
|
||||
// but now AggregateDevice::set_sub_devices will add them directly.
|
||||
|
@ -168,10 +192,10 @@ fn test_aggregate_set_sub_devices() {
|
|||
assert!(sub_devices.contains(dev));
|
||||
}
|
||||
|
||||
let onwed_devices = test_get_all_onwed_devices(device);
|
||||
let onwed_device_uids = get_device_uids(&onwed_devices);
|
||||
let input_sub_device_uids = get_device_uids(&input_sub_devices);
|
||||
let output_sub_device_uids = get_device_uids(&output_sub_devices);
|
||||
let onwed_devices = run_serially(|| test_get_all_onwed_devices(device));
|
||||
let onwed_device_uids = run_serially(|| get_device_uids(&onwed_devices));
|
||||
let input_sub_device_uids = run_serially(|| get_device_uids(&input_sub_devices));
|
||||
let output_sub_device_uids = run_serially(|| get_device_uids(&output_sub_devices));
|
||||
for uid in &input_sub_device_uids {
|
||||
assert!(onwed_device_uids.contains(uid));
|
||||
}
|
||||
|
@ -179,11 +203,10 @@ fn test_aggregate_set_sub_devices() {
|
|||
assert!(onwed_device_uids.contains(uid));
|
||||
}
|
||||
|
||||
assert!(AggregateDevice::destroy_device(plugin, device).is_ok());
|
||||
assert!(run_serially(|| AggregateDevice::destroy_device(plugin, device)).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
#[should_panic]
|
||||
fn test_aggregate_set_sub_devices_for_unknown_input_devices() {
|
||||
let output_device = test_get_default_device(Scope::Output);
|
||||
|
@ -192,16 +215,19 @@ fn test_aggregate_set_sub_devices_for_unknown_input_devices() {
|
|||
}
|
||||
let output_device = output_device.unwrap();
|
||||
|
||||
let plugin = AggregateDevice::get_system_plugin_id().unwrap();
|
||||
let device = AggregateDevice::create_blank_device_sync(plugin).unwrap();
|
||||
run_serially_forward_panics(|| {
|
||||
let plugin = AggregateDevice::get_system_plugin_id().unwrap();
|
||||
let device = AggregateDevice::create_blank_device_sync(plugin).unwrap();
|
||||
|
||||
assert!(AggregateDevice::set_sub_devices(device, kAudioObjectUnknown, output_device).is_err());
|
||||
assert!(
|
||||
AggregateDevice::set_sub_devices(device, kAudioObjectUnknown, output_device).is_err()
|
||||
);
|
||||
|
||||
assert!(AggregateDevice::destroy_device(plugin, device).is_ok());
|
||||
assert!(AggregateDevice::destroy_device(plugin, device).is_ok());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
#[should_panic]
|
||||
fn test_aggregate_set_sub_devices_for_unknown_output_devices() {
|
||||
let input_device = test_get_default_device(Scope::Input);
|
||||
|
@ -210,12 +236,16 @@ fn test_aggregate_set_sub_devices_for_unknown_output_devices() {
|
|||
}
|
||||
let input_device = input_device.unwrap();
|
||||
|
||||
let plugin = AggregateDevice::get_system_plugin_id().unwrap();
|
||||
let device = AggregateDevice::create_blank_device_sync(plugin).unwrap();
|
||||
run_serially_forward_panics(|| {
|
||||
let plugin = AggregateDevice::get_system_plugin_id().unwrap();
|
||||
let device = AggregateDevice::create_blank_device_sync(plugin).unwrap();
|
||||
|
||||
assert!(AggregateDevice::set_sub_devices(device, input_device, kAudioObjectUnknown).is_err());
|
||||
assert!(
|
||||
AggregateDevice::set_sub_devices(device, input_device, kAudioObjectUnknown).is_err()
|
||||
);
|
||||
|
||||
assert!(AggregateDevice::destroy_device(plugin, device).is_ok());
|
||||
assert!(AggregateDevice::destroy_device(plugin, device).is_ok());
|
||||
});
|
||||
}
|
||||
|
||||
fn get_device_uids(devices: &Vec<AudioObjectID>) -> Vec<String> {
|
||||
|
@ -228,7 +258,6 @@ fn get_device_uids(devices: &Vec<AudioObjectID>) -> Vec<String> {
|
|||
// AggregateDevice::set_master_device
|
||||
// ------------------------------------
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_aggregate_set_master_device() {
|
||||
let input_device = test_get_default_device(Scope::Input);
|
||||
let output_device = test_get_default_device(Scope::Output);
|
||||
|
@ -240,22 +269,28 @@ fn test_aggregate_set_master_device() {
|
|||
let input_device = input_device.unwrap();
|
||||
let output_device = output_device.unwrap();
|
||||
|
||||
let plugin = AggregateDevice::get_system_plugin_id().unwrap();
|
||||
let device = AggregateDevice::create_blank_device_sync(plugin).unwrap();
|
||||
assert!(AggregateDevice::set_sub_devices_sync(device, input_device, output_device).is_ok());
|
||||
assert!(AggregateDevice::set_master_device(device, output_device).is_ok());
|
||||
let plugin = run_serially(|| AggregateDevice::get_system_plugin_id()).unwrap();
|
||||
let device = run_serially(|| AggregateDevice::create_blank_device_sync(plugin)).unwrap();
|
||||
assert!(run_serially(|| AggregateDevice::set_sub_devices_sync(
|
||||
device,
|
||||
input_device,
|
||||
output_device
|
||||
))
|
||||
.is_ok());
|
||||
assert!(run_serially(|| AggregateDevice::set_master_device(device, output_device)).is_ok());
|
||||
|
||||
// Check if master is set to the first sub device of the default output device.
|
||||
let first_output_sub_device_uid =
|
||||
get_device_uid(AggregateDevice::get_sub_devices(device).unwrap()[0]);
|
||||
let master_device_uid = test_get_master_device(device);
|
||||
let output_sub_devices =
|
||||
run_serially(|| AggregateDevice::get_sub_devices(output_device)).unwrap();
|
||||
let first_output_sub_device_uid = run_serially(|| get_device_uid(output_sub_devices[0]));
|
||||
|
||||
// Check that the first sub device of the output device is set as master device.
|
||||
let master_device_uid = run_serially(|| test_get_master_device(device));
|
||||
assert_eq!(first_output_sub_device_uid, master_device_uid);
|
||||
|
||||
assert!(AggregateDevice::destroy_device(plugin, device).is_ok());
|
||||
assert!(run_serially(|| AggregateDevice::destroy_device(plugin, device)).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_aggregate_set_master_device_for_a_blank_aggregate_device() {
|
||||
let output_device = test_get_default_device(Scope::Output);
|
||||
if output_device.is_none() {
|
||||
|
@ -263,9 +298,11 @@ fn test_aggregate_set_master_device_for_a_blank_aggregate_device() {
|
|||
return;
|
||||
}
|
||||
|
||||
let plugin = AggregateDevice::get_system_plugin_id().unwrap();
|
||||
let device = AggregateDevice::create_blank_device_sync(plugin).unwrap();
|
||||
assert!(AggregateDevice::set_master_device(device, output_device.unwrap()).is_ok());
|
||||
let plugin = run_serially(|| AggregateDevice::get_system_plugin_id()).unwrap();
|
||||
let device = run_serially(|| AggregateDevice::create_blank_device_sync(plugin)).unwrap();
|
||||
assert!(
|
||||
run_serially(|| AggregateDevice::set_master_device(device, output_device.unwrap())).is_ok()
|
||||
);
|
||||
|
||||
// TODO: it's really weird the aggregate device actually own nothing
|
||||
// but its master device can be set successfully!
|
||||
|
@ -275,17 +312,16 @@ fn test_aggregate_set_master_device_for_a_blank_aggregate_device() {
|
|||
// The CFStringRef of the master device returned from `test_get_master_device` is actually
|
||||
// non-null.
|
||||
|
||||
assert!(AggregateDevice::destroy_device(plugin, device).is_ok());
|
||||
assert!(run_serially(|| AggregateDevice::destroy_device(plugin, device)).is_ok());
|
||||
}
|
||||
|
||||
fn get_device_uid(id: AudioObjectID) -> String {
|
||||
get_device_global_uid(id).unwrap().into_string()
|
||||
get_device_global_uid(id).map_or(String::new(), |uid| uid.into_string())
|
||||
}
|
||||
|
||||
// AggregateDevice::activate_clock_drift_compensation
|
||||
// ------------------------------------
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_aggregate_activate_clock_drift_compensation() {
|
||||
let input_device = test_get_default_device(Scope::Input);
|
||||
let output_device = test_get_default_device(Scope::Output);
|
||||
|
@ -297,27 +333,40 @@ fn test_aggregate_activate_clock_drift_compensation() {
|
|||
let input_device = input_device.unwrap();
|
||||
let output_device = output_device.unwrap();
|
||||
|
||||
let plugin = AggregateDevice::get_system_plugin_id().unwrap();
|
||||
let device = AggregateDevice::create_blank_device_sync(plugin).unwrap();
|
||||
assert!(AggregateDevice::set_sub_devices_sync(device, input_device, output_device).is_ok());
|
||||
assert!(AggregateDevice::set_master_device(device, output_device).is_ok());
|
||||
assert!(AggregateDevice::activate_clock_drift_compensation(device).is_ok());
|
||||
let plugin = run_serially(|| AggregateDevice::get_system_plugin_id()).unwrap();
|
||||
let device = run_serially(|| AggregateDevice::create_blank_device_sync(plugin)).unwrap();
|
||||
assert!(run_serially(|| AggregateDevice::set_sub_devices_sync(
|
||||
device,
|
||||
input_device,
|
||||
output_device
|
||||
))
|
||||
.is_ok());
|
||||
assert!(run_serially(|| AggregateDevice::set_master_device(device, output_device)).is_ok());
|
||||
assert!(run_serially(|| AggregateDevice::activate_clock_drift_compensation(device)).is_ok());
|
||||
|
||||
// Check the compensations.
|
||||
let devices = test_get_all_onwed_devices(device);
|
||||
let compensations = get_drift_compensations(&devices);
|
||||
let devices = run_serially(|| test_get_all_onwed_devices(device));
|
||||
let compensations = run_serially(|| get_drift_compensations(&devices));
|
||||
let master_device_uid = run_serially(|| test_get_master_device(device));
|
||||
assert!(!compensations.is_empty());
|
||||
assert_eq!(devices.len(), compensations.len());
|
||||
|
||||
for (i, compensation) in compensations.iter().enumerate() {
|
||||
assert_eq!(*compensation, if i == 0 { 0 } else { DRIFT_COMPENSATION });
|
||||
for (device, compensation) in zip(devices, compensations) {
|
||||
let uid = get_device_uid(device);
|
||||
assert_eq!(
|
||||
compensation,
|
||||
if uid == master_device_uid {
|
||||
0
|
||||
} else {
|
||||
DRIFT_COMPENSATION
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
assert!(AggregateDevice::destroy_device(plugin, device).is_ok());
|
||||
assert!(run_serially(|| AggregateDevice::destroy_device(plugin, device)).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_aggregate_activate_clock_drift_compensation_for_an_aggregate_device_without_master_device()
|
||||
{
|
||||
let input_device = test_get_default_device(Scope::Input);
|
||||
|
@ -330,25 +379,32 @@ fn test_aggregate_activate_clock_drift_compensation_for_an_aggregate_device_with
|
|||
let input_device = input_device.unwrap();
|
||||
let output_device = output_device.unwrap();
|
||||
|
||||
let plugin = AggregateDevice::get_system_plugin_id().unwrap();
|
||||
let device = AggregateDevice::create_blank_device_sync(plugin).unwrap();
|
||||
assert!(AggregateDevice::set_sub_devices_sync(device, input_device, output_device).is_ok());
|
||||
let plugin = run_serially(|| AggregateDevice::get_system_plugin_id()).unwrap();
|
||||
let device = run_serially(|| AggregateDevice::create_blank_device_sync(plugin)).unwrap();
|
||||
assert!(run_serially(|| AggregateDevice::set_sub_devices_sync(
|
||||
device,
|
||||
input_device,
|
||||
output_device
|
||||
))
|
||||
.is_ok());
|
||||
|
||||
// TODO: Is the master device the first output sub device by default if we
|
||||
// don't set that ? Is it because we add the output sub device list
|
||||
// before the input's one ? (See implementation of
|
||||
// AggregateDevice::set_sub_devices).
|
||||
let first_output_sub_device_uid =
|
||||
get_device_uid(AggregateDevice::get_sub_devices(output_device).unwrap()[0]);
|
||||
let master_device_uid = test_get_master_device(device);
|
||||
assert_eq!(first_output_sub_device_uid, master_device_uid);
|
||||
// The master device is by default the first sub device in the list.
|
||||
// This happens to be the first sub device of the input device, see implementation of
|
||||
// AggregateDevice::set_sub_devices.
|
||||
let first_input_sub_device_uid =
|
||||
run_serially(|| get_device_uid(AggregateDevice::get_sub_devices(input_device).unwrap()[0]));
|
||||
let first_sub_device_uid =
|
||||
run_serially(|| get_device_uid(AggregateDevice::get_sub_devices(device).unwrap()[0]));
|
||||
assert_eq!(first_input_sub_device_uid, first_sub_device_uid);
|
||||
let master_device_uid = run_serially(|| test_get_master_device(device));
|
||||
assert_eq!(first_sub_device_uid, master_device_uid);
|
||||
|
||||
// Compensate the drift directly without setting master device.
|
||||
assert!(AggregateDevice::activate_clock_drift_compensation(device).is_ok());
|
||||
assert!(run_serially(|| AggregateDevice::activate_clock_drift_compensation(device)).is_ok());
|
||||
|
||||
// Check the compensations.
|
||||
let devices = test_get_all_onwed_devices(device);
|
||||
let compensations = get_drift_compensations(&devices);
|
||||
let devices = run_serially(|| test_get_all_onwed_devices(device));
|
||||
let compensations = run_serially(|| get_drift_compensations(&devices));
|
||||
assert!(!compensations.is_empty());
|
||||
assert_eq!(devices.len(), compensations.len());
|
||||
|
||||
|
@ -356,25 +412,26 @@ fn test_aggregate_activate_clock_drift_compensation_for_an_aggregate_device_with
|
|||
assert_eq!(*compensation, if i == 0 { 0 } else { DRIFT_COMPENSATION });
|
||||
}
|
||||
|
||||
assert!(AggregateDevice::destroy_device(plugin, device).is_ok());
|
||||
assert!(run_serially(|| AggregateDevice::destroy_device(plugin, device)).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
#[ignore]
|
||||
fn test_aggregate_activate_clock_drift_compensation_for_a_blank_aggregate_device() {
|
||||
let plugin = AggregateDevice::get_system_plugin_id().unwrap();
|
||||
let device = AggregateDevice::create_blank_device_sync(plugin).unwrap();
|
||||
run_serially_forward_panics(|| {
|
||||
let plugin = AggregateDevice::get_system_plugin_id().unwrap();
|
||||
let device = AggregateDevice::create_blank_device_sync(plugin).unwrap();
|
||||
|
||||
let sub_devices = AggregateDevice::get_sub_devices(device).unwrap();
|
||||
assert!(sub_devices.is_empty());
|
||||
let onwed_devices = test_get_all_onwed_devices(device);
|
||||
assert!(onwed_devices.is_empty());
|
||||
let sub_devices = AggregateDevice::get_sub_devices(device).unwrap();
|
||||
assert!(sub_devices.is_empty());
|
||||
let onwed_devices = test_get_all_onwed_devices(device);
|
||||
assert!(onwed_devices.is_empty());
|
||||
|
||||
// Get a panic since no sub devices to be set compensation.
|
||||
assert!(AggregateDevice::activate_clock_drift_compensation(device).is_err());
|
||||
// Get a panic since no sub devices to be set compensation.
|
||||
assert!(AggregateDevice::activate_clock_drift_compensation(device).is_err());
|
||||
|
||||
assert!(AggregateDevice::destroy_device(plugin, device).is_ok());
|
||||
assert!(AggregateDevice::destroy_device(plugin, device).is_ok());
|
||||
});
|
||||
}
|
||||
|
||||
fn get_drift_compensations(devices: &Vec<AudioObjectID>) -> Vec<u32> {
|
||||
|
@ -391,10 +448,56 @@ fn get_drift_compensations(devices: &Vec<AudioObjectID>) -> Vec<u32> {
|
|||
// AggregateDevice::destroy_device
|
||||
// ------------------------------------
|
||||
#[test]
|
||||
#[ignore]
|
||||
#[should_panic]
|
||||
fn test_aggregate_destroy_aggregate_device_for_a_unknown_plugin_device() {
|
||||
let plugin = AggregateDevice::get_system_plugin_id().unwrap();
|
||||
let device = AggregateDevice::create_blank_device_sync(plugin).unwrap();
|
||||
assert!(AggregateDevice::destroy_device(kAudioObjectUnknown, device).is_err());
|
||||
run_serially_forward_panics(|| {
|
||||
let plugin = AggregateDevice::get_system_plugin_id().unwrap();
|
||||
let device = AggregateDevice::create_blank_device_sync(plugin).unwrap();
|
||||
assert!(AggregateDevice::destroy_device(kAudioObjectUnknown, device).is_err());
|
||||
});
|
||||
}
|
||||
|
||||
// AggregateDevice::new
|
||||
// ------------------------------------
|
||||
#[test]
|
||||
fn test_aggregate_new() {
|
||||
let input_device = test_get_default_device(Scope::Input);
|
||||
let output_device = test_get_default_device(Scope::Output);
|
||||
if input_device.is_none() || output_device.is_none() || input_device == output_device {
|
||||
println!("No input or output device to create an aggregate device.");
|
||||
return;
|
||||
}
|
||||
|
||||
run_serially_forward_panics(|| {
|
||||
let input_device = input_device.unwrap();
|
||||
let output_device = output_device.unwrap();
|
||||
|
||||
let aggr = AggregateDevice::new(input_device, output_device).unwrap();
|
||||
|
||||
// Check main device
|
||||
let output_sub_devices = AggregateDevice::get_sub_devices(output_device).unwrap();
|
||||
let first_output_sub_device_uid = get_device_uid(output_sub_devices[0]);
|
||||
let master_device_uid = test_get_master_device(aggr.get_device_id());
|
||||
assert_eq!(first_output_sub_device_uid, master_device_uid);
|
||||
|
||||
// Check drift compensation
|
||||
let devices = test_get_all_onwed_devices(aggr.get_device_id());
|
||||
let compensations = get_drift_compensations(&devices);
|
||||
assert!(!compensations.is_empty());
|
||||
assert_eq!(devices.len(), compensations.len());
|
||||
|
||||
let device_uids = devices.iter().map(|&id| get_device_uid(id));
|
||||
for (uid, compensation) in zip(device_uids, compensations) {
|
||||
assert_eq!(
|
||||
compensation,
|
||||
if uid == master_device_uid {
|
||||
0
|
||||
} else {
|
||||
DRIFT_COMPENSATION
|
||||
},
|
||||
"Unexpected drift value for device with uid {}",
|
||||
uid
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ fn test_increase_and_decrease_context_streams() {
|
|||
assert_eq!(context.active_streams(), STREAMS);
|
||||
check_streams(&context, STREAMS);
|
||||
|
||||
check_latency(&context, latencies[0]);
|
||||
check_latency(&context, Some(latencies[0]));
|
||||
for i in 0..latencies.len() - 1 {
|
||||
assert_eq!(latencies[i], latencies[i + 1]);
|
||||
}
|
||||
|
@ -149,7 +149,8 @@ fn test_minimum_resampling_input_frames_equal_input_output_rate() {
|
|||
#[test]
|
||||
fn test_create_device_info_from_unknown_input_device() {
|
||||
if let Some(default_device_id) = test_get_default_device(Scope::Input) {
|
||||
let default_device = create_device_info(kAudioObjectUnknown, DeviceType::INPUT).unwrap();
|
||||
let default_device =
|
||||
run_serially(|| create_device_info(kAudioObjectUnknown, DeviceType::INPUT).unwrap());
|
||||
assert_eq!(default_device.id, default_device_id);
|
||||
assert_eq!(
|
||||
default_device.flags,
|
||||
|
@ -163,7 +164,8 @@ fn test_create_device_info_from_unknown_input_device() {
|
|||
#[test]
|
||||
fn test_create_device_info_from_unknown_output_device() {
|
||||
if let Some(default_device_id) = test_get_default_device(Scope::Output) {
|
||||
let default_device = create_device_info(kAudioObjectUnknown, DeviceType::OUTPUT).unwrap();
|
||||
let default_device =
|
||||
run_serially(|| create_device_info(kAudioObjectUnknown, DeviceType::OUTPUT)).unwrap();
|
||||
assert_eq!(default_device.id, default_device_id);
|
||||
assert_eq!(
|
||||
default_device.flags,
|
||||
|
@ -177,13 +179,17 @@ fn test_create_device_info_from_unknown_output_device() {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_set_device_info_to_system_input_device() {
|
||||
let _device = create_device_info(kAudioObjectSystemObject, DeviceType::INPUT);
|
||||
let _device = run_serially_forward_panics(|| {
|
||||
create_device_info(kAudioObjectSystemObject, DeviceType::INPUT)
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_set_device_info_to_system_output_device() {
|
||||
let _device = create_device_info(kAudioObjectSystemObject, DeviceType::OUTPUT);
|
||||
let _device = run_serially_forward_panics(|| {
|
||||
create_device_info(kAudioObjectSystemObject, DeviceType::OUTPUT)
|
||||
});
|
||||
}
|
||||
|
||||
// FIXME: Is it ok to set input device to a nonexistent device ?
|
||||
|
@ -192,7 +198,8 @@ fn test_set_device_info_to_system_output_device() {
|
|||
#[should_panic]
|
||||
fn test_set_device_info_to_nonexistent_input_device() {
|
||||
let nonexistent_id = std::u32::MAX;
|
||||
let _device = create_device_info(nonexistent_id, DeviceType::INPUT);
|
||||
let _device =
|
||||
run_serially_forward_panics(|| create_device_info(nonexistent_id, DeviceType::INPUT));
|
||||
}
|
||||
|
||||
// FIXME: Is it ok to set output device to a nonexistent device ?
|
||||
|
@ -201,7 +208,8 @@ fn test_set_device_info_to_nonexistent_input_device() {
|
|||
#[should_panic]
|
||||
fn test_set_device_info_to_nonexistent_output_device() {
|
||||
let nonexistent_id = std::u32::MAX;
|
||||
let _device = create_device_info(nonexistent_id, DeviceType::OUTPUT);
|
||||
let _device =
|
||||
run_serially_forward_panics(|| create_device_info(nonexistent_id, DeviceType::OUTPUT));
|
||||
}
|
||||
|
||||
// add_listener (for default output device)
|
||||
|
@ -227,10 +235,10 @@ fn test_add_listener_unknown_device() {
|
|||
),
|
||||
callback,
|
||||
);
|
||||
let mut res: OSStatus = 0;
|
||||
stream
|
||||
let res = stream
|
||||
.queue
|
||||
.run_sync(|| res = stream.add_device_listener(&listener));
|
||||
.run_sync(|| stream.add_device_listener(&listener))
|
||||
.unwrap();
|
||||
assert_eq!(res, kAudioHardwareBadObjectError as OSStatus);
|
||||
});
|
||||
}
|
||||
|
@ -258,14 +266,15 @@ fn test_add_listener_then_remove_system_device() {
|
|||
),
|
||||
callback,
|
||||
);
|
||||
let mut res: OSStatus = 0;
|
||||
stream
|
||||
let res = stream
|
||||
.queue
|
||||
.run_sync(|| res = stream.add_device_listener(&listener));
|
||||
.run_sync(|| stream.add_device_listener(&listener))
|
||||
.unwrap();
|
||||
assert_eq!(res, NO_ERR);
|
||||
stream
|
||||
let res = stream
|
||||
.queue
|
||||
.run_sync(|| res = stream.remove_device_listener(&listener));
|
||||
.run_sync(|| stream.remove_device_listener(&listener))
|
||||
.unwrap();
|
||||
assert_eq!(res, NO_ERR);
|
||||
});
|
||||
}
|
||||
|
@ -291,10 +300,10 @@ fn test_remove_listener_without_adding_any_listener_before_system_device() {
|
|||
),
|
||||
callback,
|
||||
);
|
||||
let mut res: OSStatus = 0;
|
||||
stream
|
||||
let res = stream
|
||||
.queue
|
||||
.run_sync(|| res = stream.remove_device_listener(&listener));
|
||||
.run_sync(|| stream.remove_device_listener(&listener))
|
||||
.unwrap();
|
||||
assert_eq!(res, NO_ERR);
|
||||
});
|
||||
}
|
||||
|
@ -320,10 +329,10 @@ fn test_remove_listener_unknown_device() {
|
|||
),
|
||||
callback,
|
||||
);
|
||||
let mut res: OSStatus = 0;
|
||||
stream
|
||||
let res = stream
|
||||
.queue
|
||||
.run_sync(|| res = stream.remove_device_listener(&listener));
|
||||
.run_sync(|| stream.remove_device_listener(&listener))
|
||||
.unwrap();
|
||||
assert_eq!(res, kAudioHardwareBadObjectError as OSStatus);
|
||||
});
|
||||
}
|
||||
|
@ -334,14 +343,14 @@ fn test_remove_listener_unknown_device() {
|
|||
fn test_get_default_device_id() {
|
||||
if test_get_default_device(Scope::Input).is_some() {
|
||||
assert_ne!(
|
||||
get_default_device_id(DeviceType::INPUT).unwrap(),
|
||||
run_serially(|| get_default_device_id(DeviceType::INPUT)).unwrap(),
|
||||
kAudioObjectUnknown,
|
||||
);
|
||||
}
|
||||
|
||||
if test_get_default_device(Scope::Output).is_some() {
|
||||
assert_ne!(
|
||||
get_default_device_id(DeviceType::OUTPUT).unwrap(),
|
||||
run_serially(|| get_default_device_id(DeviceType::OUTPUT)).unwrap(),
|
||||
kAudioObjectUnknown,
|
||||
);
|
||||
}
|
||||
|
@ -350,13 +359,16 @@ fn test_get_default_device_id() {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_get_default_device_id_with_unknown_type() {
|
||||
assert!(get_default_device_id(DeviceType::UNKNOWN).is_err());
|
||||
assert!(run_serially_forward_panics(|| get_default_device_id(DeviceType::UNKNOWN)).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_get_default_device_id_with_inout_type() {
|
||||
assert!(get_default_device_id(DeviceType::INPUT | DeviceType::OUTPUT).is_err());
|
||||
assert!(run_serially_forward_panics(|| get_default_device_id(
|
||||
DeviceType::INPUT | DeviceType::OUTPUT
|
||||
))
|
||||
.is_err());
|
||||
}
|
||||
|
||||
// convert_channel_layout
|
||||
|
@ -724,9 +736,11 @@ fn test_convert_channel_layout() {
|
|||
#[test]
|
||||
fn test_get_preferred_channel_layout_output() {
|
||||
match test_get_default_audiounit(Scope::Output) {
|
||||
Some(unit) => assert!(!audiounit_get_preferred_channel_layout(unit.get_inner())
|
||||
.unwrap()
|
||||
.is_empty()),
|
||||
Some(unit) => assert!(!run_serially(|| audiounit_get_preferred_channel_layout(
|
||||
unit.get_inner()
|
||||
))
|
||||
.unwrap()
|
||||
.is_empty()),
|
||||
None => println!("No output audiounit for test."),
|
||||
}
|
||||
}
|
||||
|
@ -736,9 +750,15 @@ fn test_get_preferred_channel_layout_output() {
|
|||
#[test]
|
||||
fn test_get_current_channel_layout_output() {
|
||||
match test_get_default_audiounit(Scope::Output) {
|
||||
Some(unit) => assert!(!audiounit_get_current_channel_layout(unit.get_inner())
|
||||
.unwrap()
|
||||
.is_empty()),
|
||||
Some(unit) => {
|
||||
assert!(
|
||||
!run_serially_forward_panics(|| audiounit_get_current_channel_layout(
|
||||
unit.get_inner()
|
||||
))
|
||||
.unwrap()
|
||||
.is_empty()
|
||||
)
|
||||
}
|
||||
None => println!("No output audiounit for test."),
|
||||
}
|
||||
}
|
||||
|
@ -814,10 +834,30 @@ fn test_enable_audiounit_scope() {
|
|||
// for the unit whose subtype is kAudioUnitSubType_HALOutput
|
||||
// even when there is no available input or output devices.
|
||||
if let Some(unit) = test_create_audiounit(ComponentSubType::HALOutput) {
|
||||
assert!(enable_audiounit_scope(unit.get_inner(), DeviceType::OUTPUT, true).is_ok());
|
||||
assert!(enable_audiounit_scope(unit.get_inner(), DeviceType::OUTPUT, false).is_ok());
|
||||
assert!(enable_audiounit_scope(unit.get_inner(), DeviceType::INPUT, true).is_ok());
|
||||
assert!(enable_audiounit_scope(unit.get_inner(), DeviceType::INPUT, false).is_ok());
|
||||
assert!(run_serially_forward_panics(|| enable_audiounit_scope(
|
||||
unit.get_inner(),
|
||||
DeviceType::OUTPUT,
|
||||
true
|
||||
))
|
||||
.is_ok());
|
||||
assert!(run_serially_forward_panics(|| enable_audiounit_scope(
|
||||
unit.get_inner(),
|
||||
DeviceType::OUTPUT,
|
||||
false
|
||||
))
|
||||
.is_ok());
|
||||
assert!(run_serially_forward_panics(|| enable_audiounit_scope(
|
||||
unit.get_inner(),
|
||||
DeviceType::INPUT,
|
||||
true
|
||||
))
|
||||
.is_ok());
|
||||
assert!(run_serially_forward_panics(|| enable_audiounit_scope(
|
||||
unit.get_inner(),
|
||||
DeviceType::INPUT,
|
||||
false
|
||||
))
|
||||
.is_ok());
|
||||
} else {
|
||||
println!("No audiounit to perform test.");
|
||||
}
|
||||
|
@ -827,19 +867,23 @@ fn test_enable_audiounit_scope() {
|
|||
fn test_enable_audiounit_scope_for_default_output_unit() {
|
||||
if let Some(unit) = test_create_audiounit(ComponentSubType::DefaultOutput) {
|
||||
assert_eq!(
|
||||
enable_audiounit_scope(unit.get_inner(), DeviceType::OUTPUT, true).unwrap_err(),
|
||||
run_serially(|| enable_audiounit_scope(unit.get_inner(), DeviceType::OUTPUT, true))
|
||||
.unwrap_err(),
|
||||
kAudioUnitErr_InvalidProperty
|
||||
);
|
||||
assert_eq!(
|
||||
enable_audiounit_scope(unit.get_inner(), DeviceType::OUTPUT, false).unwrap_err(),
|
||||
run_serially(|| enable_audiounit_scope(unit.get_inner(), DeviceType::OUTPUT, false))
|
||||
.unwrap_err(),
|
||||
kAudioUnitErr_InvalidProperty
|
||||
);
|
||||
assert_eq!(
|
||||
enable_audiounit_scope(unit.get_inner(), DeviceType::INPUT, true).unwrap_err(),
|
||||
run_serially(|| enable_audiounit_scope(unit.get_inner(), DeviceType::INPUT, true))
|
||||
.unwrap_err(),
|
||||
kAudioUnitErr_InvalidProperty
|
||||
);
|
||||
assert_eq!(
|
||||
enable_audiounit_scope(unit.get_inner(), DeviceType::INPUT, false).unwrap_err(),
|
||||
run_serially(|| enable_audiounit_scope(unit.get_inner(), DeviceType::INPUT, false))
|
||||
.unwrap_err(),
|
||||
kAudioUnitErr_InvalidProperty
|
||||
);
|
||||
}
|
||||
|
@ -849,7 +893,10 @@ fn test_enable_audiounit_scope_for_default_output_unit() {
|
|||
#[should_panic]
|
||||
fn test_enable_audiounit_scope_with_null_unit() {
|
||||
let unit: AudioUnit = ptr::null_mut();
|
||||
assert!(enable_audiounit_scope(unit, DeviceType::INPUT, false).is_err());
|
||||
assert!(
|
||||
run_serially_forward_panics(|| enable_audiounit_scope(unit, DeviceType::INPUT, false))
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
// create_audiounit
|
||||
|
@ -868,29 +915,29 @@ fn test_for_create_audiounit() {
|
|||
// Check the output scope is enabled.
|
||||
if device.flags.contains(device_flags::DEV_OUTPUT) && default_output.is_some() {
|
||||
device.id = default_output.unwrap();
|
||||
let unit = create_audiounit(&device).unwrap();
|
||||
let unit = run_serially(|| create_audiounit(&device).unwrap());
|
||||
assert!(!unit.is_null());
|
||||
assert!(test_audiounit_scope_is_enabled(unit, Scope::Output));
|
||||
|
||||
// Destroy the AudioUnit.
|
||||
unsafe {
|
||||
run_serially(|| unsafe {
|
||||
AudioUnitUninitialize(unit);
|
||||
AudioComponentInstanceDispose(unit);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Check the input scope is enabled.
|
||||
if device.flags.contains(device_flags::DEV_INPUT) && default_input.is_some() {
|
||||
let device_id = default_input.unwrap();
|
||||
device.id = device_id;
|
||||
let unit = create_audiounit(&device).unwrap();
|
||||
let unit = run_serially(|| create_audiounit(&device).unwrap());
|
||||
assert!(!unit.is_null());
|
||||
assert!(test_audiounit_scope_is_enabled(unit, Scope::Input));
|
||||
// Destroy the AudioUnit.
|
||||
unsafe {
|
||||
run_serially(|| unsafe {
|
||||
AudioUnitUninitialize(unit);
|
||||
AudioComponentInstanceDispose(unit);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -899,7 +946,7 @@ fn test_for_create_audiounit() {
|
|||
#[should_panic]
|
||||
fn test_create_audiounit_with_unknown_scope() {
|
||||
let device = device_info::default();
|
||||
let _unit = create_audiounit(&device);
|
||||
let _unit = run_serially_forward_panics(|| create_audiounit(&device));
|
||||
}
|
||||
|
||||
// set_buffer_size_sync
|
||||
|
@ -927,9 +974,12 @@ fn test_set_buffer_size_sync() {
|
|||
.unwrap();
|
||||
assert_ne!(buffer_frames, 0);
|
||||
buffer_frames *= 2;
|
||||
assert!(
|
||||
set_buffer_size_sync(unit.get_inner(), scope.clone().into(), buffer_frames).is_ok()
|
||||
);
|
||||
assert!(run_serially(|| set_buffer_size_sync(
|
||||
unit.get_inner(),
|
||||
scope.clone().into(),
|
||||
buffer_frames
|
||||
))
|
||||
.is_ok());
|
||||
let new_buffer_frames =
|
||||
test_audiounit_get_buffer_frame_size(unit.get_inner(), scope.clone(), prop_scope)
|
||||
.unwrap();
|
||||
|
@ -951,7 +1001,9 @@ fn test_set_buffer_size_sync_for_output_with_null_output_unit() {
|
|||
|
||||
fn test_set_buffer_size_sync_by_scope_with_null_unit(scope: Scope) {
|
||||
let unit: AudioUnit = ptr::null_mut();
|
||||
assert!(set_buffer_size_sync(unit, scope.into(), 2048).is_err());
|
||||
assert!(
|
||||
run_serially_forward_panics(|| set_buffer_size_sync(unit, scope.into(), 2048)).is_err()
|
||||
);
|
||||
}
|
||||
|
||||
// get_volume, set_volume
|
||||
|
@ -960,8 +1012,11 @@ fn test_set_buffer_size_sync_by_scope_with_null_unit(scope: Scope) {
|
|||
fn test_stream_get_volume() {
|
||||
if let Some(unit) = test_get_default_audiounit(Scope::Output) {
|
||||
let expected_volume: f32 = 0.5;
|
||||
set_volume(unit.get_inner(), expected_volume);
|
||||
assert_eq!(expected_volume, get_volume(unit.get_inner()).unwrap());
|
||||
run_serially(|| set_volume(unit.get_inner(), expected_volume));
|
||||
assert_eq!(
|
||||
expected_volume,
|
||||
run_serially(|| get_volume(unit.get_inner()).unwrap())
|
||||
);
|
||||
} else {
|
||||
println!("No output audiounit.");
|
||||
}
|
||||
|
@ -988,7 +1043,9 @@ fn test_get_channel_count() {
|
|||
|
||||
fn test_channel_count(scope: Scope) {
|
||||
if let Some(device) = test_get_default_device(scope.clone()) {
|
||||
let channels = get_channel_count(device, DeviceType::from(scope.clone())).unwrap();
|
||||
let channels =
|
||||
run_serially(|| get_channel_count(device, DeviceType::from(scope.clone())))
|
||||
.unwrap();
|
||||
assert!(channels > 0);
|
||||
assert_eq!(
|
||||
channels,
|
||||
|
@ -1008,7 +1065,7 @@ fn test_get_channel_count_of_input_for_a_output_only_deivce() {
|
|||
if test_device_in_scope(device, Scope::Input) {
|
||||
continue;
|
||||
}
|
||||
let count = get_channel_count(device, DeviceType::INPUT).unwrap();
|
||||
let count = run_serially(|| get_channel_count(device, DeviceType::INPUT)).unwrap();
|
||||
assert_eq!(count, 0);
|
||||
}
|
||||
}
|
||||
|
@ -1021,7 +1078,7 @@ fn test_get_channel_count_of_output_for_a_input_only_deivce() {
|
|||
if test_device_in_scope(device, Scope::Output) {
|
||||
continue;
|
||||
}
|
||||
let count = get_channel_count(device, DeviceType::OUTPUT).unwrap();
|
||||
let count = run_serially(|| get_channel_count(device, DeviceType::OUTPUT)).unwrap();
|
||||
assert_eq!(count, 0);
|
||||
}
|
||||
}
|
||||
|
@ -1029,7 +1086,11 @@ fn test_get_channel_count_of_output_for_a_input_only_deivce() {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_get_channel_count_of_unknown_device() {
|
||||
assert!(get_channel_count(kAudioObjectUnknown, DeviceType::OUTPUT).is_err());
|
||||
assert!(run_serially_forward_panics(|| get_channel_count(
|
||||
kAudioObjectUnknown,
|
||||
DeviceType::OUTPUT
|
||||
))
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1039,14 +1100,16 @@ fn test_get_channel_count_of_inout_type() {
|
|||
|
||||
fn test_channel_count(scope: Scope) {
|
||||
if let Some(device) = test_get_default_device(scope.clone()) {
|
||||
assert_eq!(
|
||||
get_channel_count(device, DeviceType::INPUT | DeviceType::OUTPUT),
|
||||
get_channel_count(device, DeviceType::INPUT).map(|c| c + get_channel_count(
|
||||
device,
|
||||
DeviceType::OUTPUT
|
||||
)
|
||||
.unwrap_or(0))
|
||||
);
|
||||
run_serially_forward_panics(|| {
|
||||
assert_eq!(
|
||||
get_channel_count(device, DeviceType::INPUT | DeviceType::OUTPUT),
|
||||
get_channel_count(device, DeviceType::INPUT).map(|c| c + get_channel_count(
|
||||
device,
|
||||
DeviceType::OUTPUT
|
||||
)
|
||||
.unwrap_or(0))
|
||||
);
|
||||
});
|
||||
} else {
|
||||
println!("No device for {:?}.", scope);
|
||||
}
|
||||
|
@ -1095,7 +1158,9 @@ fn test_get_range_of_sample_rates() {
|
|||
];
|
||||
let mut ranges = Vec::new();
|
||||
for scope in scopes.iter() {
|
||||
ranges.push(get_range_of_sample_rates(id, *scope).unwrap());
|
||||
ranges.push(
|
||||
run_serially_forward_panics(|| get_range_of_sample_rates(id, *scope)).unwrap(),
|
||||
);
|
||||
}
|
||||
ranges
|
||||
}
|
||||
|
@ -1117,7 +1182,7 @@ fn test_get_device_presentation_latency() {
|
|||
fn test_get_device_presentation_latencies_in_scope(scope: Scope) {
|
||||
if let Some(device) = test_get_default_device(scope.clone()) {
|
||||
// TODO: The latencies very from devices to devices. Check nothing here.
|
||||
let latency = get_fixed_latency(device, scope.clone().into());
|
||||
let latency = run_serially(|| get_fixed_latency(device, scope.clone().into()));
|
||||
println!(
|
||||
"present latency on the device {} in scope {:?}: {}",
|
||||
device, scope, latency
|
||||
|
@ -1133,7 +1198,7 @@ fn test_get_device_presentation_latency() {
|
|||
#[test]
|
||||
fn test_get_device_group_id() {
|
||||
if let Some(device) = test_get_default_device(Scope::Input) {
|
||||
match get_device_group_id(device, DeviceType::INPUT) {
|
||||
match run_serially(|| get_device_group_id(device, DeviceType::INPUT)) {
|
||||
Ok(id) => println!("input group id: {:?}", id),
|
||||
Err(e) => println!("No input group id. Error: {}", e),
|
||||
}
|
||||
|
@ -1142,7 +1207,7 @@ fn test_get_device_group_id() {
|
|||
}
|
||||
|
||||
if let Some(device) = test_get_default_device(Scope::Output) {
|
||||
match get_device_group_id(device, DeviceType::OUTPUT) {
|
||||
match run_serially(|| get_device_group_id(device, DeviceType::OUTPUT)) {
|
||||
Ok(id) => println!("output group id: {:?}", id),
|
||||
Err(e) => println!("No output group id. Error: {}", e),
|
||||
}
|
||||
|
@ -1165,8 +1230,8 @@ fn test_get_same_group_id_for_builtin_device_pairs() {
|
|||
let mut input_group_ids = HashMap::<u32, String>::new();
|
||||
let input_devices = test_get_devices_in_scope(Scope::Input);
|
||||
for device in input_devices.iter() {
|
||||
match get_device_source(*device, DeviceType::INPUT) {
|
||||
Ok(source) => match get_device_group_id(*device, DeviceType::INPUT) {
|
||||
match run_serially(|| get_device_source(*device, DeviceType::INPUT)) {
|
||||
Ok(source) => match run_serially(|| get_device_group_id(*device, DeviceType::INPUT)) {
|
||||
Ok(id) => assert!(input_group_ids
|
||||
.insert(source, id.into_string().unwrap())
|
||||
.is_none()),
|
||||
|
@ -1181,8 +1246,8 @@ fn test_get_same_group_id_for_builtin_device_pairs() {
|
|||
let mut output_group_ids = HashMap::<u32, String>::new();
|
||||
let output_devices = test_get_devices_in_scope(Scope::Output);
|
||||
for device in output_devices.iter() {
|
||||
match get_device_source(*device, DeviceType::OUTPUT) {
|
||||
Ok(source) => match get_device_group_id(*device, DeviceType::OUTPUT) {
|
||||
match run_serially(|| get_device_source(*device, DeviceType::OUTPUT)) {
|
||||
Ok(source) => match run_serially(|| get_device_group_id(*device, DeviceType::OUTPUT)) {
|
||||
Ok(id) => assert!(output_group_ids
|
||||
.insert(source, id.into_string().unwrap())
|
||||
.is_none()),
|
||||
|
@ -1210,7 +1275,11 @@ fn test_get_same_group_id_for_builtin_device_pairs() {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_get_device_group_id_by_unknown_device() {
|
||||
assert!(get_device_group_id(kAudioObjectUnknown, DeviceType::INPUT).is_err());
|
||||
assert!(run_serially_forward_panics(|| get_device_group_id(
|
||||
kAudioObjectUnknown,
|
||||
DeviceType::INPUT
|
||||
))
|
||||
.is_err());
|
||||
}
|
||||
|
||||
// get_device_label
|
||||
|
@ -1218,14 +1287,14 @@ fn test_get_device_group_id_by_unknown_device() {
|
|||
#[test]
|
||||
fn test_get_device_label() {
|
||||
if let Some(device) = test_get_default_device(Scope::Input) {
|
||||
let name = get_device_label(device, DeviceType::INPUT).unwrap();
|
||||
let name = run_serially(|| get_device_label(device, DeviceType::INPUT)).unwrap();
|
||||
println!("input device label: {}", name.into_string());
|
||||
} else {
|
||||
println!("No input device.");
|
||||
}
|
||||
|
||||
if let Some(device) = test_get_default_device(Scope::Output) {
|
||||
let name = get_device_label(device, DeviceType::OUTPUT).unwrap();
|
||||
let name = run_serially(|| get_device_label(device, DeviceType::OUTPUT)).unwrap();
|
||||
println!("output device label: {}", name.into_string());
|
||||
} else {
|
||||
println!("No output device.");
|
||||
|
@ -1235,7 +1304,11 @@ fn test_get_device_label() {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_get_device_label_by_unknown_device() {
|
||||
assert!(get_device_label(kAudioObjectUnknown, DeviceType::INPUT).is_err());
|
||||
assert!(run_serially_forward_panics(|| get_device_label(
|
||||
kAudioObjectUnknown,
|
||||
DeviceType::INPUT
|
||||
))
|
||||
.is_err());
|
||||
}
|
||||
|
||||
// get_device_global_uid
|
||||
|
@ -1244,14 +1317,14 @@ fn test_get_device_label_by_unknown_device() {
|
|||
fn test_get_device_global_uid() {
|
||||
// Input device.
|
||||
if let Some(input) = test_get_default_device(Scope::Input) {
|
||||
let uid = get_device_global_uid(input).unwrap();
|
||||
let uid = run_serially(|| get_device_global_uid(input)).unwrap();
|
||||
let uid = uid.into_string();
|
||||
assert!(!uid.is_empty());
|
||||
}
|
||||
|
||||
// Output device.
|
||||
if let Some(output) = test_get_default_device(Scope::Output) {
|
||||
let uid = get_device_global_uid(output).unwrap();
|
||||
let uid = run_serially(|| get_device_global_uid(output)).unwrap();
|
||||
let uid = uid.into_string();
|
||||
assert!(!uid.is_empty());
|
||||
}
|
||||
|
@ -1285,7 +1358,7 @@ fn test_create_cubeb_device_info() {
|
|||
if is_input {
|
||||
let mut input_device_info = input_result.unwrap();
|
||||
check_device_info_by_device(&input_device_info, device, Scope::Input);
|
||||
destroy_cubeb_device_info(&mut input_device_info);
|
||||
run_serially(|| destroy_cubeb_device_info(&mut input_device_info));
|
||||
} else {
|
||||
assert_eq!(input_result.unwrap_err(), Error::error());
|
||||
}
|
||||
|
@ -1294,7 +1367,7 @@ fn test_create_cubeb_device_info() {
|
|||
if is_output {
|
||||
let mut output_device_info = output_result.unwrap();
|
||||
check_device_info_by_device(&output_device_info, device, Scope::Output);
|
||||
destroy_cubeb_device_info(&mut output_device_info);
|
||||
run_serially(|| destroy_cubeb_device_info(&mut output_device_info));
|
||||
} else {
|
||||
assert_eq!(output_result.unwrap_err(), Error::error());
|
||||
}
|
||||
|
@ -1309,7 +1382,7 @@ fn test_create_cubeb_device_info() {
|
|||
let dev_types = [DeviceType::INPUT, DeviceType::OUTPUT];
|
||||
let mut results = VecDeque::new();
|
||||
for dev_type in dev_types.iter() {
|
||||
results.push_back(create_cubeb_device_info(id, *dev_type));
|
||||
results.push_back(run_serially(|| create_cubeb_device_info(id, *dev_type)));
|
||||
}
|
||||
results
|
||||
}
|
||||
|
@ -1369,7 +1442,9 @@ fn test_create_device_info_with_unknown_type() {
|
|||
|
||||
fn test_create_device_info_with_unknown_type_by_scope(scope: Scope) {
|
||||
if let Some(device) = test_get_default_device(scope.clone()) {
|
||||
assert!(create_cubeb_device_info(device, DeviceType::UNKNOWN).is_err());
|
||||
assert!(
|
||||
run_serially(|| create_cubeb_device_info(device, DeviceType::UNKNOWN)).is_err()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1401,9 +1476,11 @@ fn test_create_device_from_hwdev_with_inout_type() {
|
|||
fn test_create_device_from_hwdev_with_inout_type_by_scope(scope: Scope) {
|
||||
if let Some(device) = test_get_default_device(scope.clone()) {
|
||||
// Get a kAudioHardwareUnknownPropertyError in get_channel_count actually.
|
||||
assert!(
|
||||
create_cubeb_device_info(device, DeviceType::INPUT | DeviceType::OUTPUT).is_err()
|
||||
);
|
||||
assert!(run_serially(|| create_cubeb_device_info(
|
||||
device,
|
||||
DeviceType::INPUT | DeviceType::OUTPUT
|
||||
))
|
||||
.is_err());
|
||||
} else {
|
||||
println!("No device for {:?}.", scope);
|
||||
}
|
||||
|
@ -1416,9 +1493,10 @@ fn test_create_device_from_hwdev_with_inout_type() {
|
|||
fn test_get_devices_of_type() {
|
||||
use std::collections::HashSet;
|
||||
|
||||
let all_devices = audiounit_get_devices_of_type(DeviceType::INPUT | DeviceType::OUTPUT);
|
||||
let input_devices = audiounit_get_devices_of_type(DeviceType::INPUT);
|
||||
let output_devices = audiounit_get_devices_of_type(DeviceType::OUTPUT);
|
||||
let all_devices =
|
||||
run_serially(|| audiounit_get_devices_of_type(DeviceType::INPUT | DeviceType::OUTPUT));
|
||||
let input_devices = run_serially(|| audiounit_get_devices_of_type(DeviceType::INPUT));
|
||||
let output_devices = run_serially(|| audiounit_get_devices_of_type(DeviceType::OUTPUT));
|
||||
|
||||
let mut expected_all = test_get_all_devices(DeviceFilter::ExcludeCubebAggregateAndVPIO);
|
||||
expected_all.sort();
|
||||
|
@ -1443,8 +1521,10 @@ fn test_get_devices_of_type() {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_get_devices_of_type_unknown() {
|
||||
let no_devs = audiounit_get_devices_of_type(DeviceType::UNKNOWN);
|
||||
assert!(no_devs.is_empty());
|
||||
run_serially_forward_panics(|| {
|
||||
let no_devs = audiounit_get_devices_of_type(DeviceType::UNKNOWN);
|
||||
assert!(no_devs.is_empty());
|
||||
});
|
||||
}
|
||||
|
||||
// add_devices_changed_listener
|
||||
|
@ -1468,9 +1548,10 @@ fn test_add_devices_changed_listener() {
|
|||
assert!(get_devices_changed_callback(context, Scope::Output).is_none());
|
||||
|
||||
// Register a callback within a specific scope.
|
||||
assert!(context
|
||||
.add_devices_changed_listener(*devtype, Some(*callback), ptr::null_mut())
|
||||
.is_ok());
|
||||
assert!(run_serially(|| {
|
||||
context.add_devices_changed_listener(*devtype, Some(*callback), ptr::null_mut())
|
||||
})
|
||||
.is_ok());
|
||||
|
||||
if devtype.contains(DeviceType::INPUT) {
|
||||
let cb = get_devices_changed_callback(context, Scope::Input);
|
||||
|
@ -1491,9 +1572,10 @@ fn test_add_devices_changed_listener() {
|
|||
}
|
||||
|
||||
// Unregister the callbacks within all scopes.
|
||||
assert!(context
|
||||
.remove_devices_changed_listener(DeviceType::INPUT | DeviceType::OUTPUT)
|
||||
.is_ok());
|
||||
assert!(run_serially(|| {
|
||||
context.remove_devices_changed_listener(DeviceType::INPUT | DeviceType::OUTPUT)
|
||||
})
|
||||
.is_ok());
|
||||
|
||||
assert!(get_devices_changed_callback(context, Scope::Input).is_none());
|
||||
assert!(get_devices_changed_callback(context, Scope::Output).is_none());
|
||||
|
@ -1547,9 +1629,12 @@ fn test_remove_devices_changed_listener() {
|
|||
|
||||
// Register callbacks within all scopes.
|
||||
for (scope, listener) in map.iter() {
|
||||
assert!(context
|
||||
.add_devices_changed_listener(*scope, Some(*listener), ptr::null_mut())
|
||||
.is_ok());
|
||||
assert!(run_serially(|| context.add_devices_changed_listener(
|
||||
*scope,
|
||||
Some(*listener),
|
||||
ptr::null_mut()
|
||||
))
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
let input_callback = get_devices_changed_callback(context, Scope::Input);
|
||||
|
@ -1566,7 +1651,7 @@ fn test_remove_devices_changed_listener() {
|
|||
);
|
||||
|
||||
// Unregister the callbacks within one specific scopes.
|
||||
assert!(context.remove_devices_changed_listener(*devtype).is_ok());
|
||||
assert!(run_serially(|| context.remove_devices_changed_listener(*devtype)).is_ok());
|
||||
|
||||
if devtype.contains(DeviceType::INPUT) {
|
||||
let cb = get_devices_changed_callback(context, Scope::Input);
|
||||
|
@ -1587,9 +1672,10 @@ fn test_remove_devices_changed_listener() {
|
|||
}
|
||||
|
||||
// Unregister the callbacks within all scopes.
|
||||
assert!(context
|
||||
.remove_devices_changed_listener(DeviceType::INPUT | DeviceType::OUTPUT)
|
||||
.is_ok());
|
||||
assert!(run_serially(
|
||||
|| context.remove_devices_changed_listener(DeviceType::INPUT | DeviceType::OUTPUT)
|
||||
)
|
||||
.is_ok());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1602,7 +1688,7 @@ fn test_remove_devices_changed_listener_without_adding_listeners() {
|
|||
DeviceType::OUTPUT,
|
||||
DeviceType::INPUT | DeviceType::OUTPUT,
|
||||
] {
|
||||
assert!(context.remove_devices_changed_listener(*devtype).is_ok());
|
||||
assert!(run_serially(|| context.remove_devices_changed_listener(*devtype)).is_ok());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1625,9 +1711,12 @@ fn test_remove_devices_changed_listener_within_all_scopes() {
|
|||
assert!(get_devices_changed_callback(context, Scope::Input).is_none());
|
||||
assert!(get_devices_changed_callback(context, Scope::Output).is_none());
|
||||
|
||||
assert!(context
|
||||
.add_devices_changed_listener(*devtype, Some(*callback), ptr::null_mut())
|
||||
.is_ok());
|
||||
assert!(run_serially(|| context.add_devices_changed_listener(
|
||||
*devtype,
|
||||
Some(*callback),
|
||||
ptr::null_mut()
|
||||
))
|
||||
.is_ok());
|
||||
|
||||
if devtype.contains(DeviceType::INPUT) {
|
||||
let cb = get_devices_changed_callback(context, Scope::Input);
|
||||
|
@ -1641,9 +1730,10 @@ fn test_remove_devices_changed_listener_within_all_scopes() {
|
|||
assert_eq!(cb.unwrap(), *callback);
|
||||
}
|
||||
|
||||
assert!(context
|
||||
.remove_devices_changed_listener(DeviceType::INPUT | DeviceType::OUTPUT)
|
||||
.is_ok());
|
||||
assert!(run_serially(
|
||||
|| context.remove_devices_changed_listener(DeviceType::INPUT | DeviceType::OUTPUT)
|
||||
)
|
||||
.is_ok());
|
||||
|
||||
assert!(get_devices_changed_callback(context, Scope::Input).is_none());
|
||||
assert!(get_devices_changed_callback(context, Scope::Output).is_none());
|
||||
|
@ -1661,3 +1751,135 @@ fn get_devices_changed_callback(
|
|||
Scope::Output => devices_guard.output.changed_callback,
|
||||
}
|
||||
}
|
||||
|
||||
// SharedVoiceProcessingUnitManager
|
||||
// ------------------------------------
|
||||
#[test]
|
||||
fn test_shared_voice_processing_unit() {
|
||||
let queue = Queue::new_with_target(
|
||||
"test_shared_voice_processing_unit",
|
||||
get_serial_queue_singleton(),
|
||||
);
|
||||
let mut shared = SharedVoiceProcessingUnitManager::new(queue.clone());
|
||||
let r1 = queue.run_sync(|| shared.take()).unwrap();
|
||||
assert!(r1.is_err());
|
||||
let r2 = queue.run_sync(|| shared.take_or_create()).unwrap();
|
||||
assert!(r2.is_ok());
|
||||
{
|
||||
let _handle = r2.unwrap();
|
||||
let r3 = queue.run_sync(|| shared.take()).unwrap();
|
||||
assert!(r3.is_err());
|
||||
}
|
||||
let r4 = queue.run_sync(|| shared.take()).unwrap();
|
||||
assert!(r4.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_shared_voice_processing_unit_bad_release_order() {
|
||||
let queue = Queue::new_with_target(
|
||||
"test_shared_voice_processing_unit_bad_release_order",
|
||||
get_serial_queue_singleton(),
|
||||
);
|
||||
let mut shared = SharedVoiceProcessingUnitManager::new(queue.clone());
|
||||
let r1 = queue.run_sync(|| shared.take()).unwrap();
|
||||
assert!(r1.is_ok());
|
||||
drop(shared);
|
||||
run_serially_forward_panics(|| drop(r1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_shared_voice_processing_multiple_units() {
|
||||
let queue = Queue::new_with_target(
|
||||
"test_shared_voice_processing_multiple_units",
|
||||
get_serial_queue_singleton(),
|
||||
);
|
||||
let mut shared = SharedVoiceProcessingUnitManager::new(queue.clone());
|
||||
let r1 = queue.run_sync(|| shared.take_or_create()).unwrap();
|
||||
assert!(r1.is_ok());
|
||||
let r2 = queue.run_sync(|| shared.take_or_create()).unwrap();
|
||||
assert!(r2.is_ok());
|
||||
{
|
||||
let _handle1 = r1.unwrap();
|
||||
let _handle2 = r2.unwrap();
|
||||
let r3 = queue.run_sync(|| shared.take()).unwrap();
|
||||
assert!(r3.is_err());
|
||||
}
|
||||
let r1 = queue.run_sync(|| shared.take()).unwrap();
|
||||
assert!(r1.is_ok());
|
||||
let r2 = queue.run_sync(|| shared.take()).unwrap();
|
||||
assert!(r2.is_ok());
|
||||
let r3 = queue.run_sync(|| shared.take()).unwrap();
|
||||
assert!(r3.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_shared_voice_processing_release_on_idle() {
|
||||
let queue = Queue::new_with_target(
|
||||
"test_shared_voice_processing_release_on_idle",
|
||||
get_serial_queue_singleton(),
|
||||
);
|
||||
let mut shared = SharedVoiceProcessingUnitManager::with_idle_timeout(
|
||||
queue.clone(),
|
||||
Duration::from_millis(0),
|
||||
);
|
||||
let r = queue.run_sync(|| shared.take_or_create()).unwrap();
|
||||
assert!(r.is_ok());
|
||||
{
|
||||
let _handle = r.unwrap();
|
||||
}
|
||||
queue.run_sync(|| {});
|
||||
let r = queue.run_sync(|| shared.take()).unwrap();
|
||||
assert!(r.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_shared_voice_processing_no_release_on_outstanding() {
|
||||
let queue = Queue::new_with_target(
|
||||
"test_shared_voice_processing_no_release_on_outstanding",
|
||||
get_serial_queue_singleton(),
|
||||
);
|
||||
let mut shared = SharedVoiceProcessingUnitManager::with_idle_timeout(
|
||||
queue.clone(),
|
||||
Duration::from_millis(0),
|
||||
);
|
||||
let r1 = queue.run_sync(|| shared.take_or_create()).unwrap();
|
||||
assert!(r1.is_ok());
|
||||
let r2 = queue.run_sync(|| shared.take_or_create()).unwrap();
|
||||
assert!(r2.is_ok());
|
||||
{
|
||||
let _handle1 = r1.unwrap();
|
||||
}
|
||||
queue.run_sync(|| {});
|
||||
let r1 = queue.run_sync(|| shared.take()).unwrap();
|
||||
assert!(r1.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_shared_voice_processing_release_on_idle_cancel_on_take() {
|
||||
let queue = Queue::new_with_target(
|
||||
"test_shared_voice_processing_release_on_idle_cancel_on_take",
|
||||
get_serial_queue_singleton(),
|
||||
);
|
||||
let mut shared = SharedVoiceProcessingUnitManager::with_idle_timeout(
|
||||
queue.clone(),
|
||||
Duration::from_millis(0),
|
||||
);
|
||||
let r1 = queue.run_sync(|| shared.take_or_create()).unwrap();
|
||||
assert!(r1.is_ok());
|
||||
let r2 = queue.run_sync(|| shared.take_or_create()).unwrap();
|
||||
assert!(r2.is_ok());
|
||||
let r1 = queue
|
||||
.run_sync(|| {
|
||||
{
|
||||
let _handle1 = r1.unwrap();
|
||||
let _handle2 = r2.unwrap();
|
||||
}
|
||||
shared.take()
|
||||
})
|
||||
.unwrap();
|
||||
assert!(r1.is_ok());
|
||||
queue.run_sync(|| {});
|
||||
let r2 = queue.run_sync(|| shared.take()).unwrap();
|
||||
assert!(r2.is_ok());
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ use super::utils::{
|
|||
test_set_default_device, Scope, StreamType, TestDevicePlugger, TestDeviceSwitcher,
|
||||
};
|
||||
use super::*;
|
||||
use std::sync::{LockResult, MutexGuard, WaitTimeoutResult};
|
||||
use std::sync::{LockResult, WaitTimeoutResult};
|
||||
|
||||
// Switch default devices used by the active streams, to test stream reinitialization
|
||||
// ================================================================================================
|
||||
|
@ -49,19 +49,21 @@ fn test_switch_device_in_scope(scope: Scope) {
|
|||
|
||||
let notifier = Arc::new(Notifier::new(0));
|
||||
let also_notifier = notifier.clone();
|
||||
let listener = test_create_device_change_listener(scope.clone(), move |_addresses| {
|
||||
let mut cnt = notifier.lock().unwrap();
|
||||
*cnt += 1;
|
||||
notifier.notify(cnt);
|
||||
NO_ERR
|
||||
let listener = run_serially(|| {
|
||||
test_create_device_change_listener(scope.clone(), move |_addresses| {
|
||||
let mut cnt = notifier.lock().unwrap();
|
||||
*cnt += 1;
|
||||
notifier.notify(cnt);
|
||||
NO_ERR
|
||||
})
|
||||
});
|
||||
listener.start();
|
||||
run_serially(|| listener.start());
|
||||
|
||||
let changed_watcher = Watcher::new(&also_notifier);
|
||||
test_get_started_stream_in_scope(scope.clone(), move |_stream| loop {
|
||||
let mut guard = changed_watcher.lock().unwrap();
|
||||
let start_cnt = guard.clone();
|
||||
let start_cnt = changed_watcher.lock().unwrap().clone();
|
||||
device_switcher.next();
|
||||
let mut guard = changed_watcher.lock().unwrap();
|
||||
guard = changed_watcher
|
||||
.wait_while(guard, |cnt| *cnt == start_cnt)
|
||||
.unwrap();
|
||||
|
@ -709,11 +711,7 @@ fn test_unplug_a_device_on_an_active_stream(
|
|||
state_callback,
|
||||
device_changed_callback,
|
||||
|stream| {
|
||||
stream.start();
|
||||
|
||||
let changed_watcher = Watcher::new(¬ifier);
|
||||
let mut data_guard = notifier.lock().unwrap();
|
||||
assert_eq!(data_guard.states.last().unwrap(), &ffi::CUBEB_STATE_STARTED);
|
||||
assert_eq!(stream.start(), Ok(()));
|
||||
|
||||
println!(
|
||||
"Stream runs on the device {} for {:?}",
|
||||
|
@ -722,13 +720,20 @@ fn test_unplug_a_device_on_an_active_stream(
|
|||
);
|
||||
|
||||
let dev = plugger.get_device_id();
|
||||
let start_changed_count = data_guard.changed_count.clone();
|
||||
let start_changed_count = {
|
||||
let guard = notifier.lock().unwrap();
|
||||
assert_eq!(guard.states.last().unwrap(), &ffi::CUBEB_STATE_STARTED);
|
||||
guard.changed_count.clone()
|
||||
};
|
||||
|
||||
assert!(plugger.unplug().is_ok());
|
||||
|
||||
let changed_watcher = Watcher::new(¬ifier);
|
||||
|
||||
if set_device_to_default {
|
||||
// The stream will be reinitialized if it follows the default input or output device.
|
||||
println!("Waiting for default device to change and reinit");
|
||||
let mut data_guard = notifier.lock().unwrap();
|
||||
data_guard = changed_watcher
|
||||
.wait_while(data_guard, |data| {
|
||||
data.changed_count == start_changed_count
|
||||
|
@ -740,6 +745,7 @@ fn test_unplug_a_device_on_an_active_stream(
|
|||
// stream can be dropped immediately before device-changed callback
|
||||
// so we only check the states if we wait for it explicitly.
|
||||
println!("Waiting for non-default device to enter error state");
|
||||
let mut data_guard = notifier.lock().unwrap();
|
||||
let (new_guard, timeout_res) = changed_watcher
|
||||
.wait_timeout_while(data_guard, Duration::from_millis(wait_up_to_ms), |data| {
|
||||
data.states.last().unwrap_or(&ffi::CUBEB_STATE_STARTED)
|
||||
|
|
|
@ -7,14 +7,14 @@ use super::*;
|
|||
fn test_get_device_uid() {
|
||||
// Input device.
|
||||
if let Some(input) = test_get_default_device(Scope::Input) {
|
||||
let uid = get_device_uid(input, DeviceType::INPUT).unwrap();
|
||||
let uid = run_serially(|| get_device_uid(input, DeviceType::INPUT)).unwrap();
|
||||
let uid = uid.into_string();
|
||||
assert!(!uid.is_empty());
|
||||
}
|
||||
|
||||
// Output device.
|
||||
if let Some(output) = test_get_default_device(Scope::Output) {
|
||||
let uid = get_device_uid(output, DeviceType::OUTPUT).unwrap();
|
||||
let uid = run_serially(|| get_device_uid(output, DeviceType::OUTPUT)).unwrap();
|
||||
let uid = uid.into_string();
|
||||
assert!(!uid.is_empty());
|
||||
}
|
||||
|
@ -24,7 +24,10 @@ fn test_get_device_uid() {
|
|||
#[should_panic]
|
||||
fn test_get_device_uid_by_unknwon_device() {
|
||||
// Unknown device.
|
||||
assert!(get_device_uid(kAudioObjectUnknown, DeviceType::INPUT).is_err());
|
||||
assert!(
|
||||
run_serially_forward_panics(|| get_device_uid(kAudioObjectUnknown, DeviceType::INPUT))
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
// get_device_model_uid
|
||||
|
@ -33,7 +36,7 @@ fn test_get_device_uid_by_unknwon_device() {
|
|||
#[test]
|
||||
fn test_get_device_model_uid() {
|
||||
if let Some(device) = test_get_default_device(Scope::Input) {
|
||||
match get_device_model_uid(device, DeviceType::INPUT) {
|
||||
match run_serially(|| get_device_model_uid(device, DeviceType::INPUT)) {
|
||||
Ok(uid) => println!("input model uid: {}", uid.into_string()),
|
||||
Err(e) => println!("No input model uid. Error: {}", e),
|
||||
}
|
||||
|
@ -42,7 +45,7 @@ fn test_get_device_model_uid() {
|
|||
}
|
||||
|
||||
if let Some(device) = test_get_default_device(Scope::Output) {
|
||||
match get_device_model_uid(device, DeviceType::OUTPUT) {
|
||||
match run_serially(|| get_device_model_uid(device, DeviceType::OUTPUT)) {
|
||||
Ok(uid) => println!("output model uid: {}", uid.into_string()),
|
||||
Err(e) => println!("No output model uid. Error: {}", e),
|
||||
}
|
||||
|
@ -54,7 +57,11 @@ fn test_get_device_model_uid() {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_get_device_model_uid_by_unknown_device() {
|
||||
assert!(get_device_model_uid(kAudioObjectUnknown, DeviceType::INPUT).is_err());
|
||||
assert!(run_serially_forward_panics(|| get_device_model_uid(
|
||||
kAudioObjectUnknown,
|
||||
DeviceType::INPUT
|
||||
))
|
||||
.is_err());
|
||||
}
|
||||
|
||||
// get_device_transport_type
|
||||
|
@ -62,7 +69,7 @@ fn test_get_device_model_uid_by_unknown_device() {
|
|||
#[test]
|
||||
fn test_get_device_transport_type() {
|
||||
if let Some(device) = test_get_default_device(Scope::Input) {
|
||||
match get_device_transport_type(device, DeviceType::INPUT) {
|
||||
match run_serially(|| get_device_transport_type(device, DeviceType::INPUT)) {
|
||||
Ok(trans_type) => println!(
|
||||
"input transport type: {:X}, {:?}",
|
||||
trans_type,
|
||||
|
@ -75,7 +82,7 @@ fn test_get_device_transport_type() {
|
|||
}
|
||||
|
||||
if let Some(device) = test_get_default_device(Scope::Output) {
|
||||
match get_device_transport_type(device, DeviceType::OUTPUT) {
|
||||
match run_serially(|| get_device_transport_type(device, DeviceType::OUTPUT)) {
|
||||
Ok(trans_type) => println!(
|
||||
"output transport type: {:X}, {:?}",
|
||||
trans_type,
|
||||
|
@ -91,7 +98,11 @@ fn test_get_device_transport_type() {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_get_device_transport_type_by_unknown_device() {
|
||||
assert!(get_device_transport_type(kAudioObjectUnknown, DeviceType::INPUT).is_err());
|
||||
assert!(run_serially_forward_panics(|| get_device_transport_type(
|
||||
kAudioObjectUnknown,
|
||||
DeviceType::INPUT
|
||||
))
|
||||
.is_err());
|
||||
}
|
||||
|
||||
// get_device_source
|
||||
|
@ -100,7 +111,7 @@ fn test_get_device_transport_type_by_unknown_device() {
|
|||
#[test]
|
||||
fn test_get_device_source() {
|
||||
if let Some(device) = test_get_default_device(Scope::Input) {
|
||||
match get_device_source(device, DeviceType::INPUT) {
|
||||
match run_serially(|| get_device_source(device, DeviceType::INPUT)) {
|
||||
Ok(source) => println!(
|
||||
"input source: {:X}, {:?}",
|
||||
source,
|
||||
|
@ -113,7 +124,7 @@ fn test_get_device_source() {
|
|||
}
|
||||
|
||||
if let Some(device) = test_get_default_device(Scope::Output) {
|
||||
match get_device_source(device, DeviceType::OUTPUT) {
|
||||
match run_serially(|| get_device_source(device, DeviceType::OUTPUT)) {
|
||||
Ok(source) => println!(
|
||||
"output source: {:X}, {:?}",
|
||||
source,
|
||||
|
@ -129,7 +140,11 @@ fn test_get_device_source() {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_get_device_source_by_unknown_device() {
|
||||
assert!(get_device_source(kAudioObjectUnknown, DeviceType::INPUT).is_err());
|
||||
assert!(run_serially_forward_panics(|| get_device_source(
|
||||
kAudioObjectUnknown,
|
||||
DeviceType::INPUT
|
||||
))
|
||||
.is_err());
|
||||
}
|
||||
|
||||
// get_device_source_name
|
||||
|
@ -137,7 +152,7 @@ fn test_get_device_source_by_unknown_device() {
|
|||
#[test]
|
||||
fn test_get_device_source_name() {
|
||||
if let Some(device) = test_get_default_device(Scope::Input) {
|
||||
match get_device_source_name(device, DeviceType::INPUT) {
|
||||
match run_serially(|| get_device_source_name(device, DeviceType::INPUT)) {
|
||||
Ok(name) => println!("input: {}", name.into_string()),
|
||||
Err(e) => println!("No input data source name. Error: {}", e),
|
||||
}
|
||||
|
@ -146,7 +161,7 @@ fn test_get_device_source_name() {
|
|||
}
|
||||
|
||||
if let Some(device) = test_get_default_device(Scope::Output) {
|
||||
match get_device_source_name(device, DeviceType::OUTPUT) {
|
||||
match run_serially(|| get_device_source_name(device, DeviceType::OUTPUT)) {
|
||||
Ok(name) => println!("output: {}", name.into_string()),
|
||||
Err(e) => println!("No output data source name. Error: {}", e),
|
||||
}
|
||||
|
@ -158,7 +173,11 @@ fn test_get_device_source_name() {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_get_device_source_name_by_unknown_device() {
|
||||
assert!(get_device_source_name(kAudioObjectUnknown, DeviceType::INPUT).is_err());
|
||||
assert!(run_serially_forward_panics(|| get_device_source_name(
|
||||
kAudioObjectUnknown,
|
||||
DeviceType::INPUT
|
||||
))
|
||||
.is_err());
|
||||
}
|
||||
|
||||
// get_device_name
|
||||
|
@ -166,14 +185,14 @@ fn test_get_device_source_name_by_unknown_device() {
|
|||
#[test]
|
||||
fn test_get_device_name() {
|
||||
if let Some(device) = test_get_default_device(Scope::Input) {
|
||||
let name = get_device_name(device, DeviceType::INPUT).unwrap();
|
||||
let name = run_serially(|| get_device_name(device, DeviceType::INPUT)).unwrap();
|
||||
println!("input device name: {}", name.into_string());
|
||||
} else {
|
||||
println!("No input device.");
|
||||
}
|
||||
|
||||
if let Some(device) = test_get_default_device(Scope::Output) {
|
||||
let name = get_device_name(device, DeviceType::OUTPUT).unwrap();
|
||||
let name = run_serially(|| get_device_name(device, DeviceType::OUTPUT).unwrap());
|
||||
println!("output device name: {}", name.into_string());
|
||||
} else {
|
||||
println!("No output device.");
|
||||
|
@ -183,7 +202,11 @@ fn test_get_device_name() {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_get_device_name_by_unknown_device() {
|
||||
assert!(get_device_name(kAudioObjectUnknown, DeviceType::INPUT).is_err());
|
||||
assert!(run_serially_forward_panics(|| get_device_name(
|
||||
kAudioObjectUnknown,
|
||||
DeviceType::INPUT
|
||||
))
|
||||
.is_err());
|
||||
}
|
||||
|
||||
// get_device_manufacturer
|
||||
|
@ -193,7 +216,7 @@ fn test_get_device_manufacturer() {
|
|||
if let Some(device) = test_get_default_device(Scope::Input) {
|
||||
// Some devices like AirPods cannot get the vendor info so we print the error directly.
|
||||
// TODO: Replace `map` and `unwrap_or_else` by `map_or_else`
|
||||
let name = get_device_manufacturer(device, DeviceType::INPUT)
|
||||
let name = run_serially(|| get_device_manufacturer(device, DeviceType::INPUT))
|
||||
.map(|name| name.into_string())
|
||||
.unwrap_or_else(|e| format!("Error: {}", e));
|
||||
println!("input device vendor: {}", name);
|
||||
|
@ -204,9 +227,10 @@ fn test_get_device_manufacturer() {
|
|||
if let Some(device) = test_get_default_device(Scope::Output) {
|
||||
// Some devices like AirPods cannot get the vendor info so we print the error directly.
|
||||
// TODO: Replace `map` and `unwrap_or_else` by `map_or_else`
|
||||
let name = get_device_manufacturer(device, DeviceType::OUTPUT)
|
||||
.map(|name| name.into_string())
|
||||
.unwrap_or_else(|e| format!("Error: {}", e));
|
||||
let name =
|
||||
run_serially_forward_panics(|| get_device_manufacturer(device, DeviceType::OUTPUT))
|
||||
.map(|name| name.into_string())
|
||||
.unwrap_or_else(|e| format!("Error: {}", e));
|
||||
println!("output device vendor: {}", name);
|
||||
} else {
|
||||
println!("No output device.");
|
||||
|
@ -216,7 +240,11 @@ fn test_get_device_manufacturer() {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_get_device_manufacturer_by_unknown_device() {
|
||||
assert!(get_device_manufacturer(kAudioObjectUnknown, DeviceType::INPUT).is_err());
|
||||
assert!(run_serially_forward_panics(|| get_device_manufacturer(
|
||||
kAudioObjectUnknown,
|
||||
DeviceType::INPUT
|
||||
))
|
||||
.is_err());
|
||||
}
|
||||
|
||||
// get_device_buffer_frame_size_range
|
||||
|
@ -224,7 +252,8 @@ fn test_get_device_manufacturer_by_unknown_device() {
|
|||
#[test]
|
||||
fn test_get_device_buffer_frame_size_range() {
|
||||
if let Some(device) = test_get_default_device(Scope::Input) {
|
||||
let range = get_device_buffer_frame_size_range(device, DeviceType::INPUT).unwrap();
|
||||
let range =
|
||||
run_serially(|| get_device_buffer_frame_size_range(device, DeviceType::INPUT)).unwrap();
|
||||
println!(
|
||||
"range of input buffer frame size: {}-{}",
|
||||
range.mMinimum, range.mMaximum
|
||||
|
@ -234,7 +263,8 @@ fn test_get_device_buffer_frame_size_range() {
|
|||
}
|
||||
|
||||
if let Some(device) = test_get_default_device(Scope::Output) {
|
||||
let range = get_device_buffer_frame_size_range(device, DeviceType::OUTPUT).unwrap();
|
||||
let range = run_serially(|| get_device_buffer_frame_size_range(device, DeviceType::OUTPUT))
|
||||
.unwrap();
|
||||
println!(
|
||||
"range of output buffer frame size: {}-{}",
|
||||
range.mMinimum, range.mMaximum
|
||||
|
@ -247,7 +277,13 @@ fn test_get_device_buffer_frame_size_range() {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_get_device_buffer_frame_size_range_by_unknown_device() {
|
||||
assert!(get_device_buffer_frame_size_range(kAudioObjectUnknown, DeviceType::INPUT).is_err());
|
||||
assert!(
|
||||
run_serially_forward_panics(|| get_device_buffer_frame_size_range(
|
||||
kAudioObjectUnknown,
|
||||
DeviceType::INPUT
|
||||
))
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
// get_device_latency
|
||||
|
@ -255,14 +291,14 @@ fn test_get_device_buffer_frame_size_range_by_unknown_device() {
|
|||
#[test]
|
||||
fn test_get_device_latency() {
|
||||
if let Some(device) = test_get_default_device(Scope::Input) {
|
||||
let latency = get_device_latency(device, DeviceType::INPUT).unwrap();
|
||||
let latency = run_serially(|| get_device_latency(device, DeviceType::INPUT)).unwrap();
|
||||
println!("latency of input device: {}", latency);
|
||||
} else {
|
||||
println!("No input device.");
|
||||
}
|
||||
|
||||
if let Some(device) = test_get_default_device(Scope::Output) {
|
||||
let latency = get_device_latency(device, DeviceType::OUTPUT).unwrap();
|
||||
let latency = run_serially(|| get_device_latency(device, DeviceType::OUTPUT)).unwrap();
|
||||
println!("latency of output device: {}", latency);
|
||||
} else {
|
||||
println!("No output device.");
|
||||
|
@ -272,7 +308,11 @@ fn test_get_device_latency() {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_get_device_latency_by_unknown_device() {
|
||||
assert!(get_device_latency(kAudioObjectUnknown, DeviceType::INPUT).is_err());
|
||||
assert!(run_serially_forward_panics(|| get_device_latency(
|
||||
kAudioObjectUnknown,
|
||||
DeviceType::INPUT
|
||||
))
|
||||
.is_err());
|
||||
}
|
||||
|
||||
// get_device_streams
|
||||
|
@ -280,7 +320,7 @@ fn test_get_device_latency_by_unknown_device() {
|
|||
#[test]
|
||||
fn test_get_device_streams() {
|
||||
if let Some(device) = test_get_default_device(Scope::Input) {
|
||||
let streams = get_device_streams(device, DeviceType::INPUT).unwrap();
|
||||
let streams = run_serially(|| get_device_streams(device, DeviceType::INPUT)).unwrap();
|
||||
println!("streams on the input device: {:?}", streams);
|
||||
assert!(!streams.is_empty());
|
||||
} else {
|
||||
|
@ -288,7 +328,7 @@ fn test_get_device_streams() {
|
|||
}
|
||||
|
||||
if let Some(device) = test_get_default_device(Scope::Output) {
|
||||
let streams = get_device_streams(device, DeviceType::OUTPUT).unwrap();
|
||||
let streams = run_serially(|| get_device_streams(device, DeviceType::OUTPUT)).unwrap();
|
||||
println!("streams on the output device: {:?}", streams);
|
||||
assert!(!streams.is_empty());
|
||||
} else {
|
||||
|
@ -299,7 +339,11 @@ fn test_get_device_streams() {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_get_device_streams_by_unknown_device() {
|
||||
assert!(get_device_streams(kAudioObjectUnknown, DeviceType::INPUT).is_err());
|
||||
assert!(run_serially_forward_panics(|| get_device_streams(
|
||||
kAudioObjectUnknown,
|
||||
DeviceType::INPUT
|
||||
))
|
||||
.is_err());
|
||||
}
|
||||
|
||||
// get_device_sample_rate
|
||||
|
@ -307,14 +351,14 @@ fn test_get_device_streams_by_unknown_device() {
|
|||
#[test]
|
||||
fn test_get_device_sample_rate() {
|
||||
if let Some(device) = test_get_default_device(Scope::Input) {
|
||||
let rate = get_device_sample_rate(device, DeviceType::INPUT).unwrap();
|
||||
let rate = run_serially(|| get_device_sample_rate(device, DeviceType::INPUT)).unwrap();
|
||||
println!("input sample rate: {}", rate);
|
||||
} else {
|
||||
println!("No input device.");
|
||||
}
|
||||
|
||||
if let Some(device) = test_get_default_device(Scope::Output) {
|
||||
let rate = get_device_sample_rate(device, DeviceType::OUTPUT).unwrap();
|
||||
let rate = run_serially(|| get_device_sample_rate(device, DeviceType::OUTPUT).unwrap());
|
||||
println!("output sample rate: {}", rate);
|
||||
} else {
|
||||
println!("No output device.");
|
||||
|
@ -324,7 +368,11 @@ fn test_get_device_sample_rate() {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_get_device_sample_rate_by_unknown_device() {
|
||||
assert!(get_device_sample_rate(kAudioObjectUnknown, DeviceType::INPUT).is_err());
|
||||
assert!(run_serially_forward_panics(|| get_device_sample_rate(
|
||||
kAudioObjectUnknown,
|
||||
DeviceType::INPUT
|
||||
))
|
||||
.is_err());
|
||||
}
|
||||
|
||||
// get_ranges_of_device_sample_rate
|
||||
|
@ -332,14 +380,16 @@ fn test_get_device_sample_rate_by_unknown_device() {
|
|||
#[test]
|
||||
fn test_get_ranges_of_device_sample_rate() {
|
||||
if let Some(device) = test_get_default_device(Scope::Input) {
|
||||
let ranges = get_ranges_of_device_sample_rate(device, DeviceType::INPUT).unwrap();
|
||||
let ranges =
|
||||
run_serially(|| get_ranges_of_device_sample_rate(device, DeviceType::INPUT)).unwrap();
|
||||
println!("ranges of input sample rate: {:?}", ranges);
|
||||
} else {
|
||||
println!("No input device.");
|
||||
}
|
||||
|
||||
if let Some(device) = test_get_default_device(Scope::Output) {
|
||||
let ranges = get_ranges_of_device_sample_rate(device, DeviceType::OUTPUT).unwrap();
|
||||
let ranges =
|
||||
run_serially(|| get_ranges_of_device_sample_rate(device, DeviceType::OUTPUT)).unwrap();
|
||||
println!("ranges of output sample rate: {:?}", ranges);
|
||||
} else {
|
||||
println!("No output device.");
|
||||
|
@ -349,7 +399,13 @@ fn test_get_ranges_of_device_sample_rate() {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_get_ranges_of_device_sample_rate_by_unknown_device() {
|
||||
assert!(get_ranges_of_device_sample_rate(kAudioObjectUnknown, DeviceType::INPUT).is_err());
|
||||
assert!(
|
||||
run_serially_forward_panics(|| get_ranges_of_device_sample_rate(
|
||||
kAudioObjectUnknown,
|
||||
DeviceType::INPUT
|
||||
))
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
// get_stream_latency
|
||||
|
@ -357,9 +413,9 @@ fn test_get_ranges_of_device_sample_rate_by_unknown_device() {
|
|||
#[test]
|
||||
fn test_get_stream_latency() {
|
||||
if let Some(device) = test_get_default_device(Scope::Input) {
|
||||
let streams = get_device_streams(device, DeviceType::INPUT).unwrap();
|
||||
let streams = run_serially(|| get_device_streams(device, DeviceType::INPUT)).unwrap();
|
||||
for stream in streams {
|
||||
let latency = get_stream_latency(stream).unwrap();
|
||||
let latency = run_serially(|| get_stream_latency(stream)).unwrap();
|
||||
println!("latency of the input stream {} is {}", stream, latency);
|
||||
}
|
||||
} else {
|
||||
|
@ -367,9 +423,9 @@ fn test_get_stream_latency() {
|
|||
}
|
||||
|
||||
if let Some(device) = test_get_default_device(Scope::Output) {
|
||||
let streams = get_device_streams(device, DeviceType::OUTPUT).unwrap();
|
||||
let streams = run_serially(|| get_device_streams(device, DeviceType::OUTPUT)).unwrap();
|
||||
for stream in streams {
|
||||
let latency = get_stream_latency(stream).unwrap();
|
||||
let latency = run_serially(|| get_stream_latency(stream)).unwrap();
|
||||
println!("latency of the output stream {} is {}", stream, latency);
|
||||
}
|
||||
} else {
|
||||
|
@ -388,10 +444,10 @@ fn test_get_stream_latency_by_unknown_device() {
|
|||
#[test]
|
||||
fn test_get_stream_virtual_format() {
|
||||
if let Some(device) = test_get_default_device(Scope::Input) {
|
||||
let streams = get_device_streams(device, DeviceType::INPUT).unwrap();
|
||||
let streams = run_serially(|| get_device_streams(device, DeviceType::INPUT)).unwrap();
|
||||
let formats = streams
|
||||
.iter()
|
||||
.map(|s| get_stream_virtual_format(*s))
|
||||
.map(|s| run_serially(|| get_stream_virtual_format(*s)))
|
||||
.collect::<Vec<std::result::Result<AudioStreamBasicDescription, OSStatus>>>();
|
||||
println!("input stream formats: {:?}", formats);
|
||||
assert!(!formats.is_empty());
|
||||
|
@ -400,10 +456,10 @@ fn test_get_stream_virtual_format() {
|
|||
}
|
||||
|
||||
if let Some(device) = test_get_default_device(Scope::Output) {
|
||||
let streams = get_device_streams(device, DeviceType::OUTPUT).unwrap();
|
||||
let streams = run_serially(|| get_device_streams(device, DeviceType::OUTPUT)).unwrap();
|
||||
let formats = streams
|
||||
.iter()
|
||||
.map(|s| get_stream_virtual_format(*s))
|
||||
.map(|s| run_serially(|| get_stream_virtual_format(*s)))
|
||||
.collect::<Vec<std::result::Result<AudioStreamBasicDescription, OSStatus>>>();
|
||||
println!("output stream formats: {:?}", formats);
|
||||
assert!(!formats.is_empty());
|
||||
|
@ -415,7 +471,9 @@ fn test_get_stream_virtual_format() {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_get_stream_virtual_format_by_unknown_stream() {
|
||||
assert!(get_stream_virtual_format(kAudioObjectUnknown).is_err());
|
||||
assert!(
|
||||
run_serially_forward_panics(|| get_stream_virtual_format(kAudioObjectUnknown)).is_err()
|
||||
);
|
||||
}
|
||||
|
||||
// get_stream_terminal_type
|
||||
|
@ -442,25 +500,21 @@ fn test_get_stream_terminal_type() {
|
|||
}
|
||||
}
|
||||
if let Some(device) = test_get_default_device(Scope::Input) {
|
||||
let streams = get_device_streams(device, DeviceType::INPUT).unwrap();
|
||||
for stream in streams {
|
||||
assert_eq!(
|
||||
terminal_type_to_device_type(get_stream_terminal_type(stream).unwrap()),
|
||||
Some(DeviceType::INPUT)
|
||||
);
|
||||
}
|
||||
let streams = run_serially(|| get_device_streams(device, DeviceType::INPUT)).unwrap();
|
||||
assert!(streams.iter().any(|&s| {
|
||||
terminal_type_to_device_type(run_serially(|| get_stream_terminal_type(s)).unwrap())
|
||||
== Some(DeviceType::INPUT)
|
||||
}));
|
||||
} else {
|
||||
println!("No input device.");
|
||||
}
|
||||
|
||||
if let Some(device) = test_get_default_device(Scope::Output) {
|
||||
let streams = get_device_streams(device, DeviceType::OUTPUT).unwrap();
|
||||
for stream in streams {
|
||||
assert_eq!(
|
||||
terminal_type_to_device_type(get_stream_terminal_type(stream).unwrap()),
|
||||
Some(DeviceType::OUTPUT)
|
||||
);
|
||||
}
|
||||
let streams = run_serially(|| get_device_streams(device, DeviceType::OUTPUT)).unwrap();
|
||||
assert!(streams.iter().any(|&s| {
|
||||
terminal_type_to_device_type(run_serially(|| get_stream_terminal_type(s)).unwrap())
|
||||
== Some(DeviceType::OUTPUT)
|
||||
}));
|
||||
} else {
|
||||
println!("No output device.");
|
||||
}
|
||||
|
|
|
@ -2,10 +2,12 @@ extern crate itertools;
|
|||
|
||||
use self::itertools::iproduct;
|
||||
use super::utils::{
|
||||
get_devices_info_in_scope, noop_data_callback, test_device_channels_in_scope,
|
||||
test_get_default_device, test_ops_context_operation, test_ops_stream_operation, Scope,
|
||||
draining_data_callback, get_devices_info_in_scope, noop_data_callback,
|
||||
test_device_channels_in_scope, test_get_default_device, test_ops_context_operation,
|
||||
test_ops_stream_operation, test_ops_stream_operation_on_context, Scope,
|
||||
};
|
||||
use super::*;
|
||||
use std::thread;
|
||||
|
||||
// Context Operations
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
@ -368,9 +370,6 @@ fn test_ops_context_register_device_collection_changed() {
|
|||
|
||||
#[test]
|
||||
fn test_ops_context_register_device_collection_changed_with_a_duplex_stream() {
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
extern "C" fn callback(_: *mut ffi::cubeb, got_called_ptr: *mut c_void) {
|
||||
let got_called = unsafe { &mut *(got_called_ptr as *mut bool) };
|
||||
*got_called = true;
|
||||
|
@ -667,14 +666,20 @@ fn test_ops_context_stream_init_channel_rate_combinations() {
|
|||
ffi::CUBEB_OK
|
||||
);
|
||||
assert!(!stream.is_null());
|
||||
|
||||
unsafe { OPS.stream_destroy.unwrap()(stream) };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Stream Operations
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
fn test_default_output_stream_operation<F>(name: &'static str, operation: F)
|
||||
where
|
||||
fn test_default_output_stream_operation_on_context_with_callback<F>(
|
||||
name: &'static str,
|
||||
context_ptr: *mut ffi::cubeb,
|
||||
data_callback: ffi::cubeb_data_callback,
|
||||
operation: F,
|
||||
) where
|
||||
F: FnOnce(*mut ffi::cubeb_stream),
|
||||
{
|
||||
// Make sure the parameters meet the requirements of AudioUnitContext::stream_init
|
||||
|
@ -686,23 +691,52 @@ where
|
|||
output_params.layout = ffi::CUBEB_LAYOUT_UNDEFINED;
|
||||
output_params.prefs = ffi::CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
test_ops_stream_operation(
|
||||
test_ops_stream_operation_on_context(
|
||||
name,
|
||||
context_ptr,
|
||||
ptr::null_mut(), // Use default input device.
|
||||
ptr::null_mut(), // No input parameters.
|
||||
ptr::null_mut(), // Use default output device.
|
||||
&mut output_params,
|
||||
4096, // TODO: Get latency by get_min_latency instead ?
|
||||
Some(noop_data_callback),
|
||||
data_callback,
|
||||
None, // No state callback.
|
||||
ptr::null_mut(), // No user data pointer.
|
||||
operation,
|
||||
);
|
||||
}
|
||||
|
||||
fn test_default_duplex_stream_operation<F>(name: &'static str, operation: F)
|
||||
fn test_default_output_stream_operation_with_callback<F>(
|
||||
name: &'static str,
|
||||
data_callback: ffi::cubeb_data_callback,
|
||||
operation: F,
|
||||
) where
|
||||
F: FnOnce(*mut ffi::cubeb_stream),
|
||||
{
|
||||
test_ops_context_operation("context: default output stream operation", |context_ptr| {
|
||||
test_default_output_stream_operation_on_context_with_callback(
|
||||
name,
|
||||
context_ptr,
|
||||
data_callback,
|
||||
operation,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn test_default_output_stream_operation<F>(name: &'static str, operation: F)
|
||||
where
|
||||
F: FnOnce(*mut ffi::cubeb_stream),
|
||||
{
|
||||
test_default_output_stream_operation_with_callback(name, Some(noop_data_callback), operation);
|
||||
}
|
||||
|
||||
fn test_default_duplex_stream_operation_on_context_with_callback<F>(
|
||||
name: &'static str,
|
||||
context_ptr: *mut ffi::cubeb,
|
||||
data_callback: ffi::cubeb_data_callback,
|
||||
operation: F,
|
||||
) where
|
||||
F: FnOnce(*mut ffi::cubeb_stream),
|
||||
{
|
||||
// Make sure the parameters meet the requirements of AudioUnitContext::stream_init
|
||||
// (in the comments).
|
||||
|
@ -720,23 +754,52 @@ where
|
|||
output_params.layout = ffi::CUBEB_LAYOUT_UNDEFINED;
|
||||
output_params.prefs = ffi::CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
test_ops_stream_operation(
|
||||
test_ops_stream_operation_on_context(
|
||||
name,
|
||||
context_ptr,
|
||||
ptr::null_mut(), // Use default input device.
|
||||
&mut input_params,
|
||||
ptr::null_mut(), // Use default output device.
|
||||
&mut output_params,
|
||||
4096, // TODO: Get latency by get_min_latency instead ?
|
||||
Some(noop_data_callback),
|
||||
data_callback,
|
||||
None, // No state callback.
|
||||
ptr::null_mut(), // No user data pointer.
|
||||
operation,
|
||||
);
|
||||
}
|
||||
|
||||
fn test_stereo_input_duplex_stream_operation<F>(name: &'static str, operation: F)
|
||||
fn test_default_duplex_stream_operation_with_callback<F>(
|
||||
name: &'static str,
|
||||
data_callback: ffi::cubeb_data_callback,
|
||||
operation: F,
|
||||
) where
|
||||
F: FnOnce(*mut ffi::cubeb_stream),
|
||||
{
|
||||
test_ops_context_operation("context: default duplex stream operation", |context_ptr| {
|
||||
test_default_duplex_stream_operation_on_context_with_callback(
|
||||
name,
|
||||
context_ptr,
|
||||
data_callback,
|
||||
operation,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn test_default_duplex_stream_operation<F>(name: &'static str, operation: F)
|
||||
where
|
||||
F: FnOnce(*mut ffi::cubeb_stream),
|
||||
{
|
||||
test_default_duplex_stream_operation_with_callback(name, Some(noop_data_callback), operation);
|
||||
}
|
||||
|
||||
fn test_stereo_input_duplex_stream_operation_on_context_with_callback<F>(
|
||||
name: &'static str,
|
||||
context_ptr: *mut ffi::cubeb,
|
||||
data_callback: ffi::cubeb_data_callback,
|
||||
operation: F,
|
||||
) where
|
||||
F: FnOnce(*mut ffi::cubeb_stream),
|
||||
{
|
||||
let mut input_devices = get_devices_info_in_scope(Scope::Input);
|
||||
input_devices.retain(|d| test_device_channels_in_scope(d.id, Scope::Input).unwrap_or(0) >= 2);
|
||||
|
@ -759,23 +822,137 @@ where
|
|||
output_params.layout = ffi::CUBEB_LAYOUT_UNDEFINED;
|
||||
output_params.prefs = ffi::CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
test_ops_stream_operation(
|
||||
test_ops_stream_operation_on_context(
|
||||
name,
|
||||
context_ptr,
|
||||
input_devices[0].id as ffi::cubeb_devid,
|
||||
&mut input_params,
|
||||
ptr::null_mut(), // Use default output device.
|
||||
&mut output_params,
|
||||
4096, // TODO: Get latency by get_min_latency instead ?
|
||||
Some(noop_data_callback),
|
||||
data_callback,
|
||||
None, // No state callback.
|
||||
ptr::null_mut(), // No user data pointer.
|
||||
operation,
|
||||
);
|
||||
}
|
||||
|
||||
fn test_default_duplex_voice_stream_operation<F>(name: &'static str, operation: F)
|
||||
fn test_stereo_input_duplex_stream_operation_with_callback<F>(
|
||||
name: &'static str,
|
||||
data_callback: ffi::cubeb_data_callback,
|
||||
operation: F,
|
||||
) where
|
||||
F: FnOnce(*mut ffi::cubeb_stream),
|
||||
{
|
||||
test_ops_context_operation(
|
||||
"context: stereo input duplex stream operation",
|
||||
|context_ptr| {
|
||||
test_stereo_input_duplex_stream_operation_on_context_with_callback(
|
||||
name,
|
||||
context_ptr,
|
||||
data_callback,
|
||||
operation,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn test_stereo_input_duplex_stream_operation<F>(name: &'static str, operation: F)
|
||||
where
|
||||
F: FnOnce(*mut ffi::cubeb_stream),
|
||||
{
|
||||
test_stereo_input_duplex_stream_operation_with_callback(
|
||||
name,
|
||||
Some(noop_data_callback),
|
||||
operation,
|
||||
);
|
||||
}
|
||||
|
||||
fn test_default_input_voice_stream_operation_on_context_with_callback<F>(
|
||||
name: &'static str,
|
||||
context_ptr: *mut ffi::cubeb,
|
||||
data_callback: ffi::cubeb_data_callback,
|
||||
operation: F,
|
||||
) where
|
||||
F: FnOnce(*mut ffi::cubeb_stream),
|
||||
{
|
||||
// Make sure the parameters meet the requirements of AudioUnitContext::stream_init
|
||||
// (in the comments).
|
||||
let mut input_params = ffi::cubeb_stream_params::default();
|
||||
input_params.format = ffi::CUBEB_SAMPLE_FLOAT32NE;
|
||||
input_params.rate = 44100;
|
||||
input_params.channels = 1;
|
||||
input_params.layout = ffi::CUBEB_LAYOUT_UNDEFINED;
|
||||
input_params.prefs = ffi::CUBEB_STREAM_PREF_VOICE;
|
||||
|
||||
test_ops_stream_operation_on_context(
|
||||
name,
|
||||
context_ptr,
|
||||
ptr::null_mut(), // Use default input device.
|
||||
&mut input_params,
|
||||
ptr::null_mut(), // Use default output device.
|
||||
ptr::null_mut(), // No output parameters.
|
||||
4096, // TODO: Get latency by get_min_latency instead ?
|
||||
data_callback,
|
||||
None, // No state callback.
|
||||
ptr::null_mut(), // No user data pointer.
|
||||
operation,
|
||||
);
|
||||
}
|
||||
|
||||
fn test_default_input_voice_stream_operation_on_context<F>(
|
||||
name: &'static str,
|
||||
context_ptr: *mut ffi::cubeb,
|
||||
operation: F,
|
||||
) where
|
||||
F: FnOnce(*mut ffi::cubeb_stream),
|
||||
{
|
||||
test_default_input_voice_stream_operation_on_context_with_callback(
|
||||
name,
|
||||
context_ptr,
|
||||
Some(noop_data_callback),
|
||||
operation,
|
||||
);
|
||||
}
|
||||
|
||||
fn test_default_input_voice_stream_operation_with_callback<F>(
|
||||
name: &'static str,
|
||||
data_callback: ffi::cubeb_data_callback,
|
||||
operation: F,
|
||||
) where
|
||||
F: FnOnce(*mut ffi::cubeb_stream),
|
||||
{
|
||||
test_ops_context_operation(
|
||||
"context: default input voice stream operation",
|
||||
|context_ptr| {
|
||||
test_default_input_voice_stream_operation_on_context_with_callback(
|
||||
name,
|
||||
context_ptr,
|
||||
data_callback,
|
||||
operation,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn test_default_input_voice_stream_operation<F>(name: &'static str, operation: F)
|
||||
where
|
||||
F: FnOnce(*mut ffi::cubeb_stream),
|
||||
{
|
||||
test_default_input_voice_stream_operation_with_callback(
|
||||
name,
|
||||
Some(noop_data_callback),
|
||||
operation,
|
||||
);
|
||||
}
|
||||
|
||||
fn test_default_duplex_voice_stream_operation_on_context_with_callback<F>(
|
||||
name: &'static str,
|
||||
context_ptr: *mut ffi::cubeb,
|
||||
data_callback: ffi::cubeb_data_callback,
|
||||
operation: F,
|
||||
) where
|
||||
F: FnOnce(*mut ffi::cubeb_stream),
|
||||
{
|
||||
// Make sure the parameters meet the requirements of AudioUnitContext::stream_init
|
||||
// (in the comments).
|
||||
|
@ -793,20 +970,64 @@ where
|
|||
output_params.layout = ffi::CUBEB_LAYOUT_UNDEFINED;
|
||||
output_params.prefs = ffi::CUBEB_STREAM_PREF_VOICE;
|
||||
|
||||
test_ops_stream_operation(
|
||||
test_ops_stream_operation_on_context(
|
||||
name,
|
||||
context_ptr,
|
||||
ptr::null_mut(), // Use default input device.
|
||||
&mut input_params,
|
||||
ptr::null_mut(), // Use default output device.
|
||||
&mut output_params,
|
||||
4096, // TODO: Get latency by get_min_latency instead ?
|
||||
Some(noop_data_callback),
|
||||
data_callback,
|
||||
None, // No state callback.
|
||||
ptr::null_mut(), // No user data pointer.
|
||||
operation,
|
||||
);
|
||||
}
|
||||
|
||||
fn test_default_duplex_voice_stream_operation_on_context<F>(
|
||||
name: &'static str,
|
||||
context_ptr: *mut ffi::cubeb,
|
||||
operation: F,
|
||||
) where
|
||||
F: FnOnce(*mut ffi::cubeb_stream),
|
||||
{
|
||||
test_default_duplex_voice_stream_operation_on_context_with_callback(
|
||||
name,
|
||||
context_ptr,
|
||||
Some(noop_data_callback),
|
||||
operation,
|
||||
);
|
||||
}
|
||||
|
||||
fn test_default_duplex_voice_stream_operation_with_callback<F>(
|
||||
name: &'static str,
|
||||
data_callback: ffi::cubeb_data_callback,
|
||||
operation: F,
|
||||
) where
|
||||
F: FnOnce(*mut ffi::cubeb_stream),
|
||||
{
|
||||
test_ops_context_operation("context: duplex voice stream operation", |context_ptr| {
|
||||
test_default_duplex_voice_stream_operation_on_context_with_callback(
|
||||
name,
|
||||
context_ptr,
|
||||
data_callback,
|
||||
operation,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn test_default_duplex_voice_stream_operation<F>(name: &'static str, operation: F)
|
||||
where
|
||||
F: FnOnce(*mut ffi::cubeb_stream),
|
||||
{
|
||||
test_default_duplex_voice_stream_operation_with_callback(
|
||||
name,
|
||||
Some(noop_data_callback),
|
||||
operation,
|
||||
);
|
||||
}
|
||||
|
||||
fn test_stereo_input_duplex_voice_stream_operation<F>(name: &'static str, operation: F)
|
||||
where
|
||||
F: FnOnce(*mut ffi::cubeb_stream),
|
||||
|
@ -865,6 +1086,18 @@ fn test_ops_stream_stop() {
|
|||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ops_stream_drain() {
|
||||
test_default_output_stream_operation_with_callback(
|
||||
"stream: drain",
|
||||
Some(draining_data_callback),
|
||||
|stream| {
|
||||
assert_eq!(unsafe { OPS.stream_start.unwrap()(stream) }, ffi::CUBEB_OK);
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ops_stream_position() {
|
||||
test_default_output_stream_operation("stream: position", |stream| {
|
||||
|
@ -987,17 +1220,57 @@ fn test_ops_stereo_input_duplex_stream_stop() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_ops_duplex_voice_stream_init_and_destroy() {
|
||||
test_default_duplex_voice_stream_operation(
|
||||
"duplex voice stream: init and destroy",
|
||||
|_stream| {},
|
||||
fn test_ops_stereo_input_duplex_stream_drain() {
|
||||
test_stereo_input_duplex_stream_operation_with_callback(
|
||||
"stereo-input duplex stream: drain",
|
||||
Some(draining_data_callback),
|
||||
|stream| {
|
||||
assert_eq!(unsafe { OPS.stream_start.unwrap()(stream) }, ffi::CUBEB_OK);
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ops_input_voice_stream_init_and_destroy() {
|
||||
test_default_input_voice_stream_operation("input voice stream: init and destroy", |stream| {
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ops_input_voice_stream_start() {
|
||||
test_default_input_voice_stream_operation("input voice stream: start", |stream| {
|
||||
assert_eq!(unsafe { OPS.stream_start.unwrap()(stream) }, ffi::CUBEB_OK);
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ops_input_voice_stream_stop() {
|
||||
test_default_input_voice_stream_operation("input voice stream: stop", |stream| {
|
||||
assert_eq!(unsafe { OPS.stream_stop.unwrap()(stream) }, ffi::CUBEB_OK);
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ops_duplex_voice_stream_init_and_destroy() {
|
||||
test_default_duplex_voice_stream_operation("duplex voice stream: init and destroy", |stream| {
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ops_duplex_voice_stream_start() {
|
||||
test_default_duplex_voice_stream_operation("duplex voice stream: start", |stream| {
|
||||
assert_eq!(unsafe { OPS.stream_start.unwrap()(stream) }, ffi::CUBEB_OK);
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1005,6 +1278,291 @@ fn test_ops_duplex_voice_stream_start() {
|
|||
fn test_ops_duplex_voice_stream_stop() {
|
||||
test_default_duplex_voice_stream_operation("duplex voice stream: stop", |stream| {
|
||||
assert_eq!(unsafe { OPS.stream_stop.unwrap()(stream) }, ffi::CUBEB_OK);
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ops_duplex_voice_stream_drain() {
|
||||
test_default_duplex_voice_stream_operation_with_callback(
|
||||
"duplex voice stream: drain",
|
||||
Some(draining_data_callback),
|
||||
|stream| {
|
||||
assert_eq!(unsafe { OPS.stream_start.unwrap()(stream) }, ffi::CUBEB_OK);
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_ops_timing_sensitive_multiple_voice_stream_init_and_destroy() {
|
||||
let start = Instant::now();
|
||||
let mut t1 = start;
|
||||
let mut t2 = start;
|
||||
let mut t3 = start;
|
||||
let mut t4 = start;
|
||||
let mut t5 = start;
|
||||
let mut t6 = start;
|
||||
let mut t7 = start;
|
||||
let mut t8 = start;
|
||||
let mut t9 = start;
|
||||
let mut t10 = start;
|
||||
test_ops_context_operation("multiple duplex voice streams", |context_ptr| {
|
||||
// First stream uses vpio, creates the shared vpio unit.
|
||||
test_default_duplex_voice_stream_operation_on_context(
|
||||
"multiple voice streams: stream 1, duplex",
|
||||
context_ptr,
|
||||
|stream| {
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
|
||||
// Two concurrent vpio streams are supported.
|
||||
test_default_input_voice_stream_operation_on_context(
|
||||
"multiple voice streams: stream 2, input-only",
|
||||
context_ptr,
|
||||
|stream| {
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
|
||||
// Three concurrent vpio streams are supported.
|
||||
test_default_duplex_voice_stream_operation_on_context(
|
||||
"multiple voice streams: stream 3, duplex",
|
||||
context_ptr,
|
||||
|stream| {
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
t1 = Instant::now();
|
||||
// Fourth stream uses vpio, allows reuse of one already created.
|
||||
test_default_duplex_voice_stream_operation_on_context(
|
||||
"multiple voice streams: stream 4, duplex",
|
||||
context_ptr,
|
||||
|stream| {
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
t2 = Instant::now();
|
||||
|
||||
// Fifth stream uses vpio, allows reuse of one already created.
|
||||
test_default_duplex_voice_stream_operation_on_context(
|
||||
"multiple voice streams: stream 5, duplex",
|
||||
context_ptr,
|
||||
|stream| {
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
t3 = Instant::now();
|
||||
|
||||
// Sixth stream uses vpio, allows reuse of one already created.
|
||||
test_default_input_voice_stream_operation_on_context(
|
||||
"multiple voice streams: stream 6, input-only",
|
||||
context_ptr,
|
||||
|stream| {
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
t4 = Instant::now();
|
||||
|
||||
// Seventh stream uses vpio, but is created anew.
|
||||
test_default_input_voice_stream_operation_on_context(
|
||||
"multiple voice streams: stream 7, input-only",
|
||||
context_ptr,
|
||||
|stream| {
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
t5 = Instant::now();
|
||||
},
|
||||
);
|
||||
t6 = Instant::now();
|
||||
},
|
||||
);
|
||||
t7 = Instant::now();
|
||||
},
|
||||
);
|
||||
t8 = Instant::now();
|
||||
},
|
||||
);
|
||||
t9 = Instant::now();
|
||||
});
|
||||
t10 = Instant::now();
|
||||
|
||||
let reuse_vpio_1 = t2 - t1;
|
||||
let reuse_vpio_2 = t3 - t2;
|
||||
let reuse_vpio_3 = t4 - t3;
|
||||
let create_standalone_vpio = t5 - t4;
|
||||
assert!(
|
||||
create_standalone_vpio > reuse_vpio_1 * 2,
|
||||
"Failed create_standalone_vpio={}s > reuse_vpio_1={}s * 2",
|
||||
create_standalone_vpio.as_secs_f32(),
|
||||
reuse_vpio_1.as_secs_f32()
|
||||
);
|
||||
assert!(
|
||||
create_standalone_vpio > reuse_vpio_2 * 2,
|
||||
"Failed create_standalone_vpio={}s > reuse_vpio_2={}s * 2",
|
||||
create_standalone_vpio.as_secs_f32(),
|
||||
reuse_vpio_2.as_secs_f32()
|
||||
);
|
||||
assert!(
|
||||
create_standalone_vpio > reuse_vpio_3 * 2,
|
||||
"Failed create_standalone_vpio={}s > reuse_vpio_3={}s * 2",
|
||||
create_standalone_vpio.as_secs_f32(),
|
||||
reuse_vpio_3.as_secs_f32()
|
||||
);
|
||||
|
||||
let recycle_vpio_1 = t6 - t5;
|
||||
let recycle_vpio_2 = t7 - t6;
|
||||
let recycle_vpio_3 = t8 - t7;
|
||||
let recycle_vpio_4 = t9 - t8;
|
||||
let dispose_vpios = t10 - t9;
|
||||
assert!(
|
||||
dispose_vpios > recycle_vpio_1 * 2,
|
||||
"Failed dispose_vpios={}s > recycle_vpio_1 ={}s * 2",
|
||||
dispose_vpios.as_secs_f32(),
|
||||
recycle_vpio_1.as_secs_f32()
|
||||
);
|
||||
assert!(
|
||||
dispose_vpios > recycle_vpio_2 * 2,
|
||||
"Failed dispose_vpios={}s > recycle_vpio_2 ={}s * 2",
|
||||
dispose_vpios.as_secs_f32(),
|
||||
recycle_vpio_2.as_secs_f32()
|
||||
);
|
||||
assert!(
|
||||
dispose_vpios > recycle_vpio_3 * 2,
|
||||
"Failed dispose_vpios={}s > recycle_vpio_3 ={}s * 2",
|
||||
dispose_vpios.as_secs_f32(),
|
||||
recycle_vpio_3.as_secs_f32()
|
||||
);
|
||||
assert!(
|
||||
dispose_vpios > recycle_vpio_4 * 2,
|
||||
"Failed dispose_vpios={}s > recycle_vpio_4 ={}s * 2",
|
||||
dispose_vpios.as_secs_f32(),
|
||||
recycle_vpio_4.as_secs_f32()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_ops_timing_sensitive_multiple_duplex_voice_stream_start() {
|
||||
test_ops_context_operation("multiple duplex voice streams", |context_ptr| {
|
||||
let start = Instant::now();
|
||||
// First stream uses vpio, creates the shared vpio unit.
|
||||
test_default_duplex_voice_stream_operation_on_context(
|
||||
"multiple duplex voice streams: stream 1",
|
||||
context_ptr,
|
||||
|stream| {
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
assert_eq!(unsafe { OPS.stream_start.unwrap()(stream) }, ffi::CUBEB_OK);
|
||||
},
|
||||
);
|
||||
let d1 = start.elapsed();
|
||||
// Second stream uses vpio, allows reuse of the one already created.
|
||||
test_default_duplex_voice_stream_operation_on_context(
|
||||
"multiple duplex voice streams: stream 2",
|
||||
context_ptr,
|
||||
|stream| {
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
assert_eq!(unsafe { OPS.stream_start.unwrap()(stream) }, ffi::CUBEB_OK);
|
||||
},
|
||||
);
|
||||
let d2 = start.elapsed() - d1;
|
||||
// d1 being significantly longer than d2 is proof we reuse vpio.
|
||||
assert!(
|
||||
d1 > d2 * 2,
|
||||
"Failed d1={}s > d2={}s * s",
|
||||
d1.as_secs_f32(),
|
||||
d2.as_secs_f32()
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_ops_timing_sensitive_multiple_duplex_voice_stream_params() {
|
||||
test_ops_context_operation("multiple duplex voice streams with params", |context_ptr| {
|
||||
let start = Instant::now();
|
||||
// First stream uses vpio, creates the shared vpio unit.
|
||||
test_default_duplex_voice_stream_operation_on_context(
|
||||
"multiple duplex voice streams: stream 1",
|
||||
context_ptr,
|
||||
|stream| {
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
assert_eq!(
|
||||
unsafe {
|
||||
OPS.stream_set_input_processing_params.unwrap()(
|
||||
stream,
|
||||
ffi::CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION
|
||||
| ffi::CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION,
|
||||
)
|
||||
},
|
||||
ffi::CUBEB_OK
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { OPS.stream_set_input_mute.unwrap()(stream, 1) },
|
||||
ffi::CUBEB_OK
|
||||
);
|
||||
},
|
||||
);
|
||||
let d1 = start.elapsed();
|
||||
// Second stream uses vpio, allows reuse of the one already created.
|
||||
test_default_duplex_voice_stream_operation_on_context(
|
||||
"multiple duplex voice streams: stream 2",
|
||||
context_ptr,
|
||||
|stream| {
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
let queue = stm.queue.clone();
|
||||
// Test that input processing params does not carry over when reusing vpio.
|
||||
let mut bypass: u32 = 0;
|
||||
let r = queue
|
||||
.run_sync(|| {
|
||||
audio_unit_get_property(
|
||||
stm.core_stream_data.input_unit,
|
||||
kAUVoiceIOProperty_BypassVoiceProcessing,
|
||||
kAudioUnitScope_Global,
|
||||
AU_IN_BUS,
|
||||
&mut bypass,
|
||||
&mut mem::size_of::<u32>(),
|
||||
)
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(r, NO_ERR);
|
||||
assert_eq!(bypass, 1);
|
||||
|
||||
// Test that input mute state does not carry over when reusing vpio.
|
||||
let mut mute: u32 = 0;
|
||||
let r = queue
|
||||
.run_sync(|| {
|
||||
audio_unit_get_property(
|
||||
stm.core_stream_data.input_unit,
|
||||
kAUVoiceIOProperty_MuteOutput,
|
||||
kAudioUnitScope_Global,
|
||||
AU_IN_BUS,
|
||||
&mut mute,
|
||||
&mut mem::size_of::<u32>(),
|
||||
)
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(r, NO_ERR);
|
||||
assert_eq!(mute, 0);
|
||||
},
|
||||
);
|
||||
let d2 = start.elapsed() - d1;
|
||||
// d1 being significantly longer than d2 is proof we reuse vpio.
|
||||
assert!(
|
||||
d1 > d2 * 2,
|
||||
"Failed d1={}s > d2={}s * 2",
|
||||
d1.as_secs_f32(),
|
||||
d2.as_secs_f32()
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1015,6 +1573,8 @@ fn test_ops_duplex_voice_stream_set_input_mute() {
|
|||
unsafe { OPS.stream_set_input_mute.unwrap()(stream, 1) },
|
||||
ffi::CUBEB_OK
|
||||
);
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1028,6 +1588,8 @@ fn test_ops_duplex_voice_stream_set_input_mute_before_start() {
|
|||
ffi::CUBEB_OK
|
||||
);
|
||||
assert_eq!(unsafe { OPS.stream_start.unwrap()(stream) }, ffi::CUBEB_OK);
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -1045,6 +1607,7 @@ fn test_ops_duplex_voice_stream_set_input_mute_before_start_with_reinit() {
|
|||
|
||||
// Hacky cast, but testing this here was simplest for now.
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
stm.reinit_async();
|
||||
let queue = stm.queue.clone();
|
||||
let mut mute_after_reinit = false;
|
||||
|
@ -1074,6 +1637,8 @@ fn test_ops_duplex_voice_stream_set_input_mute_after_start() {
|
|||
unsafe { OPS.stream_set_input_mute.unwrap()(stream, 1) },
|
||||
ffi::CUBEB_OK
|
||||
);
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1088,6 +1653,8 @@ fn test_ops_duplex_voice_stream_set_input_processing_params() {
|
|||
unsafe { OPS.stream_set_input_processing_params.unwrap()(stream, params) },
|
||||
ffi::CUBEB_OK
|
||||
);
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1105,6 +1672,8 @@ fn test_ops_duplex_voice_stream_set_input_processing_params_before_start() {
|
|||
ffi::CUBEB_OK
|
||||
);
|
||||
assert_eq!(unsafe { OPS.stream_start.unwrap()(stream) }, ffi::CUBEB_OK);
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -1126,6 +1695,7 @@ fn test_ops_duplex_voice_stream_set_input_processing_params_before_start_with_re
|
|||
|
||||
// Hacky cast, but testing this here was simplest for now.
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
stm.reinit_async();
|
||||
let queue = stm.queue.clone();
|
||||
let mut params_after_reinit: ffi::cubeb_input_processing_params =
|
||||
|
@ -1173,6 +1743,8 @@ fn test_ops_duplex_voice_stream_set_input_processing_params_after_start() {
|
|||
test_default_duplex_voice_stream_operation(
|
||||
"duplex voice stream: processing after start",
|
||||
|stream| {
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
assert_eq!(unsafe { OPS.stream_start.unwrap()(stream) }, ffi::CUBEB_OK);
|
||||
let params: ffi::cubeb_input_processing_params =
|
||||
ffi::CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION
|
||||
|
@ -1190,7 +1762,10 @@ fn test_ops_duplex_voice_stream_set_input_processing_params_after_start() {
|
|||
fn test_ops_stereo_input_duplex_voice_stream_init_and_destroy() {
|
||||
test_stereo_input_duplex_voice_stream_operation(
|
||||
"stereo-input duplex voice stream: init and destroy",
|
||||
|_stream| {},
|
||||
|stream| {
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1199,6 +1774,8 @@ fn test_ops_stereo_input_duplex_voice_stream_start() {
|
|||
test_stereo_input_duplex_voice_stream_operation(
|
||||
"stereo-input duplex voice stream: start",
|
||||
|stream| {
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
assert_eq!(unsafe { OPS.stream_start.unwrap()(stream) }, ffi::CUBEB_OK);
|
||||
},
|
||||
);
|
||||
|
@ -1209,6 +1786,8 @@ fn test_ops_stereo_input_duplex_voice_stream_stop() {
|
|||
test_stereo_input_duplex_voice_stream_operation(
|
||||
"stereo-input duplex voice stream: stop",
|
||||
|stream| {
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
assert!(stm.core_stream_data.using_voice_processing_unit());
|
||||
assert_eq!(unsafe { OPS.stream_stop.unwrap()(stream) }, ffi::CUBEB_OK);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -4,7 +4,6 @@ use super::utils::{
|
|||
};
|
||||
use super::*;
|
||||
use std::io;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
|
@ -153,25 +152,84 @@ fn test_device_collection_change() {
|
|||
let _ = std::io::stdin().read_line(&mut input);
|
||||
}
|
||||
|
||||
struct StreamData {
|
||||
stream_ptr: *mut ffi::cubeb_stream,
|
||||
enable_loopback: AtomicBool,
|
||||
}
|
||||
|
||||
impl StreamData {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
stream_ptr: ptr::null_mut(),
|
||||
enable_loopback: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct StreamsData {
|
||||
streams: Vec<StreamData>,
|
||||
current_idx: Option<usize>,
|
||||
}
|
||||
|
||||
impl StreamsData {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
streams: Vec::new(),
|
||||
current_idx: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.streams.len()
|
||||
}
|
||||
|
||||
fn current_mut(&mut self) -> &mut StreamData {
|
||||
&mut self.streams[self.current_idx.unwrap()]
|
||||
}
|
||||
|
||||
fn current(&self) -> &StreamData {
|
||||
&self.streams[self.current_idx.unwrap()]
|
||||
}
|
||||
|
||||
fn select(&mut self, idx: usize) {
|
||||
assert!(idx < self.len());
|
||||
self.current_idx = Some(idx);
|
||||
}
|
||||
|
||||
fn push(&mut self, stream: StreamData) {
|
||||
self.streams.push(stream)
|
||||
}
|
||||
}
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn test_stream_tester() {
|
||||
test_ops_context_operation("context: stream tester", |context_ptr| {
|
||||
let mut stream_ptr: *mut ffi::cubeb_stream = ptr::null_mut();
|
||||
let enable_loopback = AtomicBool::new(false);
|
||||
let mut input_prefs = StreamPrefs::NONE;
|
||||
let mut output_prefs = StreamPrefs::NONE;
|
||||
let mut streams = StreamsData::new();
|
||||
loop {
|
||||
println!(
|
||||
"commands:\n\
|
||||
"Current stream: {} (of {}). Commands:\n\
|
||||
\t'q': quit\n\
|
||||
\t'b': change current stream\n\
|
||||
\t'i': set input stream prefs to be used for creating streams\n\
|
||||
\t'o': set output stream prefs to be used for creating streams\n\
|
||||
\t'c': create a stream\n\
|
||||
\t'd': destroy a stream\n\
|
||||
\t's': start the created stream\n\
|
||||
\t't': stop the created stream\n\
|
||||
Commands on the current stream:\n\
|
||||
\t'd': destroy\n\
|
||||
\t's': start\n\
|
||||
\t't': stop\n\
|
||||
\t'r': register a device changed callback\n\
|
||||
\t'l': set loopback (DUPLEX-only)\n\
|
||||
\t'v': set volume\n\
|
||||
\t'm': set input mute\n\
|
||||
\t'p': set input processing"
|
||||
\t'p': set input processing",
|
||||
streams
|
||||
.current_idx
|
||||
.map(|i| format!("{}", i + 1 as usize))
|
||||
.unwrap_or(String::from("N/A")),
|
||||
streams.len(),
|
||||
);
|
||||
|
||||
let mut command = String::new();
|
||||
|
@ -181,66 +239,130 @@ fn test_stream_tester() {
|
|||
match command.as_str() {
|
||||
"q" => {
|
||||
println!("Quit.");
|
||||
destroy_stream(&mut stream_ptr);
|
||||
for mut stream in streams.streams {
|
||||
if !stream.stream_ptr.is_null() {
|
||||
destroy_stream(&mut stream);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
"c" => create_stream(&mut stream_ptr, context_ptr, &enable_loopback),
|
||||
"d" => destroy_stream(&mut stream_ptr),
|
||||
"s" => start_stream(stream_ptr),
|
||||
"t" => stop_stream(stream_ptr),
|
||||
"r" => register_device_change_callback(stream_ptr),
|
||||
"l" => set_loopback(stream_ptr, &enable_loopback),
|
||||
"v" => set_volume(stream_ptr),
|
||||
"m" => set_input_mute(stream_ptr),
|
||||
"p" => set_input_processing(stream_ptr),
|
||||
x => println!("Unknown command: {}", x),
|
||||
"i" => set_prefs(&mut input_prefs),
|
||||
"o" => set_prefs(&mut output_prefs),
|
||||
"c" => create_stream(context_ptr, &mut streams, input_prefs, output_prefs),
|
||||
_ if streams.current_idx.is_none() => {
|
||||
println!("There are no streams! Create a stream first.")
|
||||
}
|
||||
cmd => match cmd {
|
||||
"b" => select_stream(&mut streams),
|
||||
"d" => destroy_stream(streams.current_mut()),
|
||||
"s" => start_stream(streams.current()),
|
||||
"t" => stop_stream(streams.current()),
|
||||
"r" => register_device_change_callback(streams.current()),
|
||||
"l" => set_loopback(streams.current()),
|
||||
"v" => set_volume(streams.current()),
|
||||
"m" => set_input_mute(streams.current()),
|
||||
"p" => set_input_processing(streams.current()),
|
||||
x => println!("Unknown command: {}", x),
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
fn start_stream(stream_ptr: *mut ffi::cubeb_stream) {
|
||||
if stream_ptr.is_null() {
|
||||
fn set_prefs(prefs: &mut StreamPrefs) {
|
||||
let mut done = false;
|
||||
while !done {
|
||||
println!(
|
||||
"Current prefs: {:?}\nSelect action:\n\
|
||||
\t1) Set None\n\
|
||||
\t2) Toggle Loopback\n\
|
||||
\t3) Toggle Disable Device Switching\n\
|
||||
\t4) Toggle Voice\n\
|
||||
\t5) Set All\n\
|
||||
\t0) Done",
|
||||
prefs
|
||||
);
|
||||
let mut input = String::new();
|
||||
let _ = io::stdin().read_line(&mut input);
|
||||
assert_eq!(input.pop().unwrap(), '\n');
|
||||
match input.as_str() {
|
||||
"1" => *prefs = StreamPrefs::NONE,
|
||||
"2" => prefs.toggle(StreamPrefs::LOOPBACK),
|
||||
"3" => prefs.toggle(StreamPrefs::DISABLE_DEVICE_SWITCHING),
|
||||
"4" => prefs.toggle(StreamPrefs::VOICE),
|
||||
"5" => *prefs = StreamPrefs::all(),
|
||||
"0" => done = true,
|
||||
_ => println!("Invalid action. Select again.\n"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn select_stream(streams: &mut StreamsData) {
|
||||
let num_streams = streams.len();
|
||||
let current_idx = streams.current_idx.unwrap();
|
||||
println!(
|
||||
"Current stream is {}. Select stream 1 to {} on which to apply commands:",
|
||||
current_idx + 1 as usize,
|
||||
num_streams
|
||||
);
|
||||
let mut selection: Option<usize> = None;
|
||||
while selection.is_none() {
|
||||
let mut input = String::new();
|
||||
let _ = io::stdin().read_line(&mut input);
|
||||
assert_eq!(input.pop().unwrap(), '\n');
|
||||
selection = match input.parse::<usize>() {
|
||||
Ok(i) if (1..=num_streams).contains((&i).into()) => Some(i),
|
||||
_ => {
|
||||
println!("Invalid stream. Select again.\n");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
streams.select(selection.unwrap() - 1)
|
||||
}
|
||||
|
||||
fn start_stream(stream: &StreamData) {
|
||||
if stream.stream_ptr.is_null() {
|
||||
println!("No stream can start.");
|
||||
return;
|
||||
}
|
||||
assert_eq!(
|
||||
unsafe { OPS.stream_start.unwrap()(stream_ptr) },
|
||||
unsafe { OPS.stream_start.unwrap()(stream.stream_ptr) },
|
||||
ffi::CUBEB_OK
|
||||
);
|
||||
println!("Stream {:p} started.", stream_ptr);
|
||||
println!("Stream {:p} started.", stream.stream_ptr);
|
||||
}
|
||||
|
||||
fn stop_stream(stream_ptr: *mut ffi::cubeb_stream) {
|
||||
if stream_ptr.is_null() {
|
||||
fn stop_stream(stream: &StreamData) {
|
||||
if stream.stream_ptr.is_null() {
|
||||
println!("No stream can stop.");
|
||||
return;
|
||||
}
|
||||
assert_eq!(
|
||||
unsafe { OPS.stream_stop.unwrap()(stream_ptr) },
|
||||
unsafe { OPS.stream_stop.unwrap()(stream.stream_ptr) },
|
||||
ffi::CUBEB_OK
|
||||
);
|
||||
println!("Stream {:p} stopped.", stream_ptr);
|
||||
println!("Stream {:p} stopped.", stream.stream_ptr);
|
||||
}
|
||||
|
||||
fn set_volume(stream_ptr: *mut ffi::cubeb_stream) {
|
||||
if stream_ptr.is_null() {
|
||||
fn set_volume(stream: &StreamData) {
|
||||
if stream.stream_ptr.is_null() {
|
||||
println!("No stream can set volume.");
|
||||
return;
|
||||
}
|
||||
const VOL: f32 = 0.5;
|
||||
assert_eq!(
|
||||
unsafe { OPS.stream_set_volume.unwrap()(stream_ptr, VOL) },
|
||||
unsafe { OPS.stream_set_volume.unwrap()(stream.stream_ptr, VOL) },
|
||||
ffi::CUBEB_OK
|
||||
);
|
||||
println!("Set stream {:p} volume to {}", stream_ptr, VOL);
|
||||
println!("Set stream {:p} volume to {}", stream.stream_ptr, VOL);
|
||||
}
|
||||
|
||||
fn set_loopback(stream_ptr: *mut ffi::cubeb_stream, enable_loopback: &AtomicBool) {
|
||||
if stream_ptr.is_null() {
|
||||
fn set_loopback(stream: &StreamData) {
|
||||
if stream.stream_ptr.is_null() {
|
||||
println!("No stream can set loopback.");
|
||||
return;
|
||||
}
|
||||
let stm = unsafe { &mut *(stream_ptr as *mut AudioUnitStream) };
|
||||
let stm = unsafe { &mut *(stream.stream_ptr as *mut AudioUnitStream) };
|
||||
if !stm.core_stream_data.has_input() || !stm.core_stream_data.has_output() {
|
||||
println!("Duplex stream needed to set loopback");
|
||||
return;
|
||||
|
@ -261,20 +383,20 @@ fn test_stream_tester() {
|
|||
}
|
||||
}
|
||||
let loopback = loopback.unwrap();
|
||||
enable_loopback.store(loopback, Ordering::SeqCst);
|
||||
stream.enable_loopback.store(loopback, Ordering::SeqCst);
|
||||
println!(
|
||||
"Loopback {} for stream {:p}",
|
||||
if loopback { "enabled" } else { "disabled" },
|
||||
stream_ptr
|
||||
stream.stream_ptr
|
||||
);
|
||||
}
|
||||
|
||||
fn set_input_mute(stream_ptr: *mut ffi::cubeb_stream) {
|
||||
if stream_ptr.is_null() {
|
||||
fn set_input_mute(stream: &StreamData) {
|
||||
if stream.stream_ptr.is_null() {
|
||||
println!("No stream can set input mute.");
|
||||
return;
|
||||
}
|
||||
let stm = unsafe { &mut *(stream_ptr as *mut AudioUnitStream) };
|
||||
let stm = unsafe { &mut *(stream.stream_ptr as *mut AudioUnitStream) };
|
||||
if !stm.core_stream_data.has_input() {
|
||||
println!("Input stream needed to set loopback");
|
||||
return;
|
||||
|
@ -295,7 +417,7 @@ fn test_stream_tester() {
|
|||
}
|
||||
}
|
||||
let mute = mute.unwrap();
|
||||
let res = unsafe { OPS.stream_set_input_mute.unwrap()(stream_ptr, mute.into()) };
|
||||
let res = unsafe { OPS.stream_set_input_mute.unwrap()(stream.stream_ptr, mute.into()) };
|
||||
println!(
|
||||
"{} set stream {:p} input {}",
|
||||
if res == ffi::CUBEB_OK {
|
||||
|
@ -303,17 +425,17 @@ fn test_stream_tester() {
|
|||
} else {
|
||||
"Failed to"
|
||||
},
|
||||
stream_ptr,
|
||||
stream.stream_ptr,
|
||||
if mute { "mute" } else { "unmute" }
|
||||
);
|
||||
}
|
||||
|
||||
fn set_input_processing(stream_ptr: *mut ffi::cubeb_stream) {
|
||||
if stream_ptr.is_null() {
|
||||
fn set_input_processing(stream: &StreamData) {
|
||||
if stream.stream_ptr.is_null() {
|
||||
println!("No stream can set input processing.");
|
||||
return;
|
||||
}
|
||||
let stm = unsafe { &mut *(stream_ptr as *mut AudioUnitStream) };
|
||||
let stm = unsafe { &mut *(stream.stream_ptr as *mut AudioUnitStream) };
|
||||
if !stm.core_stream_data.using_voice_processing_unit() {
|
||||
println!("Duplex stream with voice processing needed to set input processing params");
|
||||
return;
|
||||
|
@ -338,6 +460,23 @@ fn test_stream_tester() {
|
|||
params.set(InputProcessingParams::ECHO_CANCELLATION, true);
|
||||
params.set(InputProcessingParams::NOISE_SUPPRESSION, true);
|
||||
}
|
||||
let mut agc = u32::from(false);
|
||||
let mut size: usize = mem::size_of::<u32>();
|
||||
assert_eq!(
|
||||
audio_unit_get_property(
|
||||
stm.core_stream_data.input_unit,
|
||||
kAUVoiceIOProperty_VoiceProcessingEnableAGC,
|
||||
kAudioUnitScope_Global,
|
||||
AU_IN_BUS,
|
||||
&mut agc,
|
||||
&mut size,
|
||||
),
|
||||
NO_ERR
|
||||
);
|
||||
assert_eq!(size, mem::size_of::<u32>());
|
||||
if agc == 1 {
|
||||
params.set(InputProcessingParams::AUTOMATIC_GAIN_CONTROL, true);
|
||||
}
|
||||
}
|
||||
let mut done = false;
|
||||
while !done {
|
||||
|
@ -367,8 +506,9 @@ fn test_stream_tester() {
|
|||
_ => println!("Invalid action. Select again.\n"),
|
||||
}
|
||||
}
|
||||
let res =
|
||||
unsafe { OPS.stream_set_input_processing_params.unwrap()(stream_ptr, params.bits()) };
|
||||
let res = unsafe {
|
||||
OPS.stream_set_input_processing_params.unwrap()(stream.stream_ptr, params.bits())
|
||||
};
|
||||
println!(
|
||||
"{} set stream {:p} input processing params to {:?}",
|
||||
if res == ffi::CUBEB_OK {
|
||||
|
@ -376,48 +516,62 @@ fn test_stream_tester() {
|
|||
} else {
|
||||
"Failed to"
|
||||
},
|
||||
stream_ptr,
|
||||
stream.stream_ptr,
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
||||
fn register_device_change_callback(stream_ptr: *mut ffi::cubeb_stream) {
|
||||
fn register_device_change_callback(stream: &StreamData) {
|
||||
extern "C" fn callback(user_ptr: *mut c_void) {
|
||||
println!("user pointer @ {:p}", user_ptr);
|
||||
assert!(user_ptr.is_null());
|
||||
}
|
||||
|
||||
if stream_ptr.is_null() {
|
||||
if stream.stream_ptr.is_null() {
|
||||
println!("No stream for registering the callback.");
|
||||
return;
|
||||
}
|
||||
assert_eq!(
|
||||
unsafe {
|
||||
OPS.stream_register_device_changed_callback.unwrap()(stream_ptr, Some(callback))
|
||||
OPS.stream_register_device_changed_callback.unwrap()(
|
||||
stream.stream_ptr,
|
||||
Some(callback),
|
||||
)
|
||||
},
|
||||
ffi::CUBEB_OK
|
||||
);
|
||||
println!("Stream {:p} now has a device change callback.", stream_ptr);
|
||||
println!(
|
||||
"Stream {:p} now has a device change callback.",
|
||||
stream.stream_ptr
|
||||
);
|
||||
}
|
||||
|
||||
fn destroy_stream(stream_ptr: &mut *mut ffi::cubeb_stream) {
|
||||
if stream_ptr.is_null() {
|
||||
fn destroy_stream(stream: &mut StreamData) {
|
||||
if stream.stream_ptr.is_null() {
|
||||
println!("No need to destroy stream.");
|
||||
return;
|
||||
}
|
||||
unsafe {
|
||||
OPS.stream_destroy.unwrap()(*stream_ptr);
|
||||
OPS.stream_destroy.unwrap()((*stream).stream_ptr);
|
||||
}
|
||||
println!("Stream {:p} destroyed.", *stream_ptr);
|
||||
*stream_ptr = ptr::null_mut();
|
||||
println!("Stream {:p} destroyed.", stream.stream_ptr);
|
||||
stream.stream_ptr = ptr::null_mut();
|
||||
}
|
||||
|
||||
fn create_stream(
|
||||
stream_ptr: &mut *mut ffi::cubeb_stream,
|
||||
context_ptr: *mut ffi::cubeb,
|
||||
enable_loopback: &AtomicBool,
|
||||
streams: &mut StreamsData,
|
||||
input_prefs: StreamPrefs,
|
||||
output_prefs: StreamPrefs,
|
||||
) {
|
||||
if !stream_ptr.is_null() {
|
||||
if streams.len() == 0 || !streams.current().stream_ptr.is_null() {
|
||||
println!("Allocating stream {}.", streams.len() + 1);
|
||||
streams.push(StreamData::new());
|
||||
streams.select(streams.len() - 1);
|
||||
}
|
||||
|
||||
let stream = streams.current_mut();
|
||||
if !stream.stream_ptr.is_null() {
|
||||
println!("Stream has been created.");
|
||||
return;
|
||||
}
|
||||
|
@ -486,8 +640,8 @@ fn test_stream_tester() {
|
|||
}
|
||||
};
|
||||
|
||||
let mut input_params = get_dummy_stream_params(Scope::Input);
|
||||
let mut output_params = get_dummy_stream_params(Scope::Output);
|
||||
let mut input_params = get_dummy_stream_params(Scope::Input, input_prefs);
|
||||
let mut output_params = get_dummy_stream_params(Scope::Output, output_prefs);
|
||||
|
||||
let (input_device, input_stream_params) = if stream_type.contains(StreamType::INPUT) {
|
||||
(
|
||||
|
@ -519,7 +673,7 @@ fn test_stream_tester() {
|
|||
unsafe {
|
||||
OPS.stream_init.unwrap()(
|
||||
context_ptr,
|
||||
stream_ptr,
|
||||
&mut stream.stream_ptr,
|
||||
stream_name.as_ptr(),
|
||||
input_device as ffi::cubeb_devid,
|
||||
input_stream_params,
|
||||
|
@ -528,13 +682,13 @@ fn test_stream_tester() {
|
|||
4096, // latency
|
||||
Some(data_callback),
|
||||
Some(state_callback),
|
||||
enable_loopback as *const AtomicBool as *mut c_void, // user pointer
|
||||
&stream.enable_loopback as *const AtomicBool as *mut c_void, // user pointer
|
||||
)
|
||||
},
|
||||
ffi::CUBEB_OK
|
||||
);
|
||||
assert!(!stream_ptr.is_null());
|
||||
println!("Stream {:p} created.", *stream_ptr);
|
||||
assert!(!stream.stream_ptr.is_null());
|
||||
println!("Stream {:p} created.", stream.stream_ptr);
|
||||
|
||||
extern "C" fn state_callback(
|
||||
stream: *mut ffi::cubeb_stream,
|
||||
|
@ -592,14 +746,14 @@ fn test_stream_tester() {
|
|||
nframes
|
||||
}
|
||||
|
||||
fn get_dummy_stream_params(scope: Scope) -> ffi::cubeb_stream_params {
|
||||
fn get_dummy_stream_params(scope: Scope, prefs: StreamPrefs) -> ffi::cubeb_stream_params {
|
||||
// The stream format for input and output must be same.
|
||||
const STREAM_FORMAT: u32 = ffi::CUBEB_SAMPLE_FLOAT32NE;
|
||||
|
||||
// Make sure the parameters meet the requirements of AudioUnitContext::stream_init
|
||||
// (in the comments).
|
||||
let mut stream_params = ffi::cubeb_stream_params::default();
|
||||
stream_params.prefs = ffi::CUBEB_STREAM_PREF_VOICE;
|
||||
stream_params.prefs = prefs.bits();
|
||||
let (format, rate, channels, layout) = match scope {
|
||||
Scope::Input => (STREAM_FORMAT, 48000, 1, ffi::CUBEB_LAYOUT_MONO),
|
||||
Scope::Output => (STREAM_FORMAT, 44100, 2, ffi::CUBEB_LAYOUT_STEREO),
|
||||
|
|
|
@ -537,14 +537,16 @@ fn test_set_buffer_frame_size_in_parallel_in_scope(scope: Scope) {
|
|||
units.push(test_get_default_audiounit(scope.clone()).unwrap());
|
||||
let unit_value = units.last().unwrap().get_inner() as usize;
|
||||
join_handles.push(thread::spawn(move || {
|
||||
let status = audio_unit_set_property(
|
||||
unit_value as AudioUnit,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
unit_scope,
|
||||
unit_element,
|
||||
&latency_frames,
|
||||
mem::size_of::<u32>(),
|
||||
);
|
||||
let status = run_serially(|| {
|
||||
audio_unit_set_property(
|
||||
unit_value as AudioUnit,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
unit_scope,
|
||||
unit_element,
|
||||
&latency_frames,
|
||||
mem::size_of::<u32>(),
|
||||
)
|
||||
});
|
||||
(latency_frames, status)
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::utils::{test_get_default_device, test_ops_stream_operation, Scope};
|
||||
use super::*;
|
||||
use std::sync::atomic::{AtomicI64, Ordering};
|
||||
use std::sync::atomic::AtomicI64;
|
||||
|
||||
#[test]
|
||||
fn test_dial_tone() {
|
||||
|
@ -202,7 +202,7 @@ fn test_dial_tone() {
|
|||
for data in buffer.iter_mut() {
|
||||
let t1 = (2.0 * PI * 350.0 * (closure.phase) as f32 / SAMPLE_FREQUENCY as f32).sin();
|
||||
let t2 = (2.0 * PI * 440.0 * (closure.phase) as f32 / SAMPLE_FREQUENCY as f32).sin();
|
||||
*data = f32_to_i16_sample(0.5 * (t1 + t2));
|
||||
*data = f32_to_i16_sample(0.45 * (t1 + t2));
|
||||
closure.phase += 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,29 @@ pub extern "C" fn noop_data_callback(
|
|||
nframes
|
||||
}
|
||||
|
||||
pub extern "C" fn draining_data_callback(
|
||||
stream: *mut ffi::cubeb_stream,
|
||||
_user_ptr: *mut c_void,
|
||||
_input_buffer: *const c_void,
|
||||
output_buffer: *mut c_void,
|
||||
nframes: i64,
|
||||
) -> i64 {
|
||||
assert!(!stream.is_null());
|
||||
|
||||
// Feed silence data to output buffer
|
||||
if !output_buffer.is_null() {
|
||||
let stm = unsafe { &mut *(stream as *mut AudioUnitStream) };
|
||||
let channels = stm.core_stream_data.output_stream_params.channels();
|
||||
let samples = nframes as usize * channels as usize;
|
||||
let sample_size = cubeb_sample_size(stm.core_stream_data.output_stream_params.format());
|
||||
unsafe {
|
||||
ptr::write_bytes(output_buffer, 0, samples * sample_size);
|
||||
}
|
||||
}
|
||||
|
||||
nframes - 1
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Scope {
|
||||
Input,
|
||||
|
@ -47,31 +70,35 @@ pub enum PropertyScope {
|
|||
}
|
||||
|
||||
pub fn test_get_default_device(scope: Scope) -> Option<AudioObjectID> {
|
||||
let address = AudioObjectPropertyAddress {
|
||||
mSelector: match scope {
|
||||
Scope::Input => kAudioHardwarePropertyDefaultInputDevice,
|
||||
Scope::Output => kAudioHardwarePropertyDefaultOutputDevice,
|
||||
},
|
||||
mScope: kAudioObjectPropertyScopeGlobal,
|
||||
mElement: kAudioObjectPropertyElementMaster,
|
||||
};
|
||||
debug_assert_not_running_serially();
|
||||
run_serially_forward_panics(|| {
|
||||
let address = AudioObjectPropertyAddress {
|
||||
mSelector: match scope {
|
||||
Scope::Input => kAudioHardwarePropertyDefaultInputDevice,
|
||||
Scope::Output => kAudioHardwarePropertyDefaultOutputDevice,
|
||||
},
|
||||
mScope: kAudioObjectPropertyScopeGlobal,
|
||||
mElement: kAudioObjectPropertyElementMaster,
|
||||
};
|
||||
|
||||
let mut devid: AudioObjectID = kAudioObjectUnknown;
|
||||
let mut size = mem::size_of::<AudioObjectID>();
|
||||
let status = unsafe {
|
||||
AudioObjectGetPropertyData(
|
||||
kAudioObjectSystemObject,
|
||||
&address,
|
||||
0,
|
||||
ptr::null(),
|
||||
&mut size as *mut usize as *mut UInt32,
|
||||
&mut devid as *mut AudioObjectID as *mut c_void,
|
||||
)
|
||||
};
|
||||
if status != NO_ERR || devid == kAudioObjectUnknown {
|
||||
return None;
|
||||
}
|
||||
Some(devid)
|
||||
let mut devid: AudioObjectID = kAudioObjectUnknown;
|
||||
let mut size = mem::size_of::<AudioObjectID>();
|
||||
let status = unsafe {
|
||||
AudioObjectGetPropertyData(
|
||||
kAudioObjectSystemObject,
|
||||
&address,
|
||||
0,
|
||||
ptr::null(),
|
||||
&mut size as *mut usize as *mut UInt32,
|
||||
&mut devid as *mut AudioObjectID as *mut c_void,
|
||||
)
|
||||
};
|
||||
|
||||
if status != NO_ERR || devid == kAudioObjectUnknown {
|
||||
return None;
|
||||
}
|
||||
Some(devid)
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Create a GetProperty trait and add a default implementation for it, then implement it
|
||||
|
@ -99,16 +126,17 @@ impl TestAudioUnit {
|
|||
|
||||
impl Drop for TestAudioUnit {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
run_serially_forward_panics(|| unsafe {
|
||||
AudioUnitUninitialize(self.0);
|
||||
AudioComponentInstanceDispose(self.0);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: 1. Return Result with custom errors.
|
||||
// 2. Allow to create a in-out unit.
|
||||
pub fn test_get_default_audiounit(scope: Scope) -> Option<TestAudioUnit> {
|
||||
debug_assert_not_running_serially();
|
||||
let device = test_get_default_device(scope.clone());
|
||||
let unit = test_create_audiounit(ComponentSubType::HALOutput);
|
||||
if device.is_none() || unit.is_none() {
|
||||
|
@ -133,7 +161,7 @@ pub fn test_get_default_audiounit(scope: Scope) -> Option<TestAudioUnit> {
|
|||
}
|
||||
}
|
||||
|
||||
let status = unsafe {
|
||||
let status = run_serially(|| unsafe {
|
||||
AudioUnitSetProperty(
|
||||
unit.get_inner(),
|
||||
kAudioOutputUnitProperty_CurrentDevice,
|
||||
|
@ -142,7 +170,7 @@ pub fn test_get_default_audiounit(scope: Scope) -> Option<TestAudioUnit> {
|
|||
&device as *const AudioObjectID as *const c_void,
|
||||
mem::size_of::<AudioObjectID>() as u32,
|
||||
)
|
||||
};
|
||||
});
|
||||
if status == NO_ERR {
|
||||
Some(unit)
|
||||
} else {
|
||||
|
@ -159,6 +187,7 @@ pub enum ComponentSubType {
|
|||
// Surprisingly the AudioUnit can be created even when there is no any device on the platform,
|
||||
// no matter its subtype is HALOutput or DefaultOutput.
|
||||
pub fn test_create_audiounit(unit_type: ComponentSubType) -> Option<TestAudioUnit> {
|
||||
debug_assert_not_running_serially();
|
||||
let desc = AudioComponentDescription {
|
||||
componentType: kAudioUnitType_Output,
|
||||
componentSubType: match unit_type {
|
||||
|
@ -169,12 +198,12 @@ pub fn test_create_audiounit(unit_type: ComponentSubType) -> Option<TestAudioUni
|
|||
componentFlags: 0,
|
||||
componentFlagsMask: 0,
|
||||
};
|
||||
let comp = unsafe { AudioComponentFindNext(ptr::null_mut(), &desc) };
|
||||
let comp = run_serially(|| unsafe { AudioComponentFindNext(ptr::null_mut(), &desc) });
|
||||
if comp.is_null() {
|
||||
return None;
|
||||
}
|
||||
let mut unit: AudioUnit = ptr::null_mut();
|
||||
let status = unsafe { AudioComponentInstanceNew(comp, &mut unit) };
|
||||
let status = run_serially(|| unsafe { AudioComponentInstanceNew(comp, &mut unit) });
|
||||
// TODO: Is unit possible to be null when no error returns ?
|
||||
if status != NO_ERR || unit.is_null() {
|
||||
None
|
||||
|
@ -188,13 +217,14 @@ fn test_enable_audiounit_in_scope(
|
|||
scope: Scope,
|
||||
enable: bool,
|
||||
) -> std::result::Result<(), OSStatus> {
|
||||
debug_assert_not_running_serially();
|
||||
assert!(!unit.is_null());
|
||||
let (scope, element) = match scope {
|
||||
Scope::Input => (kAudioUnitScope_Input, AU_IN_BUS),
|
||||
Scope::Output => (kAudioUnitScope_Output, AU_OUT_BUS),
|
||||
};
|
||||
let on_off: u32 = if enable { 1 } else { 0 };
|
||||
let status = unsafe {
|
||||
let status = run_serially(|| unsafe {
|
||||
AudioUnitSetProperty(
|
||||
unit,
|
||||
kAudioOutputUnitProperty_EnableIO,
|
||||
|
@ -203,7 +233,7 @@ fn test_enable_audiounit_in_scope(
|
|||
&on_off as *const u32 as *const c_void,
|
||||
mem::size_of::<u32>() as u32,
|
||||
)
|
||||
};
|
||||
});
|
||||
if status == NO_ERR {
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -216,72 +246,80 @@ pub enum DeviceFilter {
|
|||
IncludeAll,
|
||||
}
|
||||
pub fn test_get_all_devices(filter: DeviceFilter) -> Vec<AudioObjectID> {
|
||||
let mut devices = Vec::new();
|
||||
let address = AudioObjectPropertyAddress {
|
||||
mSelector: kAudioHardwarePropertyDevices,
|
||||
mScope: kAudioObjectPropertyScopeGlobal,
|
||||
mElement: kAudioObjectPropertyElementMaster,
|
||||
};
|
||||
let mut size: usize = 0;
|
||||
let status = unsafe {
|
||||
AudioObjectGetPropertyDataSize(
|
||||
kAudioObjectSystemObject,
|
||||
&address,
|
||||
0,
|
||||
ptr::null(),
|
||||
&mut size as *mut usize as *mut u32,
|
||||
)
|
||||
};
|
||||
// size will be 0 if there is no device at all.
|
||||
if status != NO_ERR || size == 0 {
|
||||
return devices;
|
||||
}
|
||||
assert_eq!(size % mem::size_of::<AudioObjectID>(), 0);
|
||||
let elements = size / mem::size_of::<AudioObjectID>();
|
||||
devices.resize(elements, kAudioObjectUnknown);
|
||||
let status = unsafe {
|
||||
AudioObjectGetPropertyData(
|
||||
kAudioObjectSystemObject,
|
||||
&address,
|
||||
0,
|
||||
ptr::null(),
|
||||
&mut size as *mut usize as *mut u32,
|
||||
devices.as_mut_ptr() as *mut c_void,
|
||||
)
|
||||
};
|
||||
if status != NO_ERR {
|
||||
devices.clear();
|
||||
return devices;
|
||||
}
|
||||
for device in devices.iter() {
|
||||
assert_ne!(*device, kAudioObjectUnknown);
|
||||
}
|
||||
|
||||
match filter {
|
||||
DeviceFilter::ExcludeCubebAggregateAndVPIO => {
|
||||
devices.retain(|&device| {
|
||||
if let Ok(uid) = get_device_global_uid(device) {
|
||||
let uid = uid.into_string();
|
||||
!uid.contains(PRIVATE_AGGREGATE_DEVICE_NAME)
|
||||
&& !uid.contains(VOICEPROCESSING_AGGREGATE_DEVICE_NAME)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
debug_assert_not_running_serially();
|
||||
// To avoid races, the devices getter and the device name filtering have
|
||||
// to run in the same serial task. If not, a device may exist when the
|
||||
// getter runs but not when getting its uid.
|
||||
run_serially_forward_panics(|| {
|
||||
let mut devices = Vec::new();
|
||||
let address = AudioObjectPropertyAddress {
|
||||
mSelector: kAudioHardwarePropertyDevices,
|
||||
mScope: kAudioObjectPropertyScopeGlobal,
|
||||
mElement: kAudioObjectPropertyElementMaster,
|
||||
};
|
||||
let mut size: usize = 0;
|
||||
let status = unsafe {
|
||||
AudioObjectGetPropertyDataSize(
|
||||
kAudioObjectSystemObject,
|
||||
&address,
|
||||
0,
|
||||
ptr::null(),
|
||||
&mut size as *mut usize as *mut u32,
|
||||
)
|
||||
};
|
||||
// size will be 0 if there is no device at all.
|
||||
if status != NO_ERR || size == 0 {
|
||||
return devices;
|
||||
}
|
||||
assert_eq!(size % mem::size_of::<AudioObjectID>(), 0);
|
||||
let elements = size / mem::size_of::<AudioObjectID>();
|
||||
devices.resize(elements, kAudioObjectUnknown);
|
||||
let status = unsafe {
|
||||
AudioObjectGetPropertyData(
|
||||
kAudioObjectSystemObject,
|
||||
&address,
|
||||
0,
|
||||
ptr::null(),
|
||||
&mut size as *mut usize as *mut u32,
|
||||
devices.as_mut_ptr() as *mut c_void,
|
||||
)
|
||||
};
|
||||
if status != NO_ERR {
|
||||
devices.clear();
|
||||
return devices;
|
||||
}
|
||||
for device in devices.iter() {
|
||||
assert_ne!(*device, kAudioObjectUnknown);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
devices
|
||||
match filter {
|
||||
DeviceFilter::ExcludeCubebAggregateAndVPIO => {
|
||||
devices.retain(|&device| {
|
||||
if let Ok(uid) = get_device_global_uid(device) {
|
||||
let uid = uid.into_string();
|
||||
!uid.contains(PRIVATE_AGGREGATE_DEVICE_NAME)
|
||||
&& !uid.contains(VOICEPROCESSING_AGGREGATE_DEVICE_NAME)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
devices
|
||||
})
|
||||
}
|
||||
|
||||
pub fn test_get_devices_in_scope(scope: Scope) -> Vec<AudioObjectID> {
|
||||
debug_assert_not_running_serially();
|
||||
let mut devices = test_get_all_devices(DeviceFilter::ExcludeCubebAggregateAndVPIO);
|
||||
devices.retain(|device| test_device_in_scope(*device, scope.clone()));
|
||||
devices
|
||||
}
|
||||
|
||||
pub fn get_devices_info_in_scope(scope: Scope) -> Vec<TestDeviceInfo> {
|
||||
debug_assert_not_running_serially();
|
||||
fn print_info(info: &TestDeviceInfo) {
|
||||
println!("{:>4}: {}\n\tuid: {}", info.id, info.label, info.uid);
|
||||
}
|
||||
|
@ -337,8 +375,9 @@ pub fn test_device_channels_in_scope(
|
|||
id: AudioObjectID,
|
||||
scope: Scope,
|
||||
) -> std::result::Result<u32, OSStatus> {
|
||||
debug_assert_not_running_serially();
|
||||
let address = AudioObjectPropertyAddress {
|
||||
mSelector: kAudioDevicePropertyStreamConfiguration,
|
||||
mSelector: kAudioDevicePropertyStreams,
|
||||
mScope: match scope {
|
||||
Scope::Input => kAudioDevicePropertyScopeInput,
|
||||
Scope::Output => kAudioDevicePropertyScopeOutput,
|
||||
|
@ -346,7 +385,7 @@ pub fn test_device_channels_in_scope(
|
|||
mElement: kAudioObjectPropertyElementMaster,
|
||||
};
|
||||
let mut size: usize = 0;
|
||||
let status = unsafe {
|
||||
let status = run_serially(|| unsafe {
|
||||
AudioObjectGetPropertyDataSize(
|
||||
id,
|
||||
&address,
|
||||
|
@ -354,49 +393,90 @@ pub fn test_device_channels_in_scope(
|
|||
ptr::null(),
|
||||
&mut size as *mut usize as *mut u32,
|
||||
)
|
||||
};
|
||||
});
|
||||
if status != NO_ERR {
|
||||
return Err(status);
|
||||
}
|
||||
if size == 0 {
|
||||
return Ok(0);
|
||||
}
|
||||
let byte_len = size / mem::size_of::<u8>();
|
||||
let mut bytes = vec![0u8; byte_len];
|
||||
let status = unsafe {
|
||||
let mut stream_list = vec![0, (size / mem::size_of::<AudioObjectID>()) as u32];
|
||||
let status = run_serially(|| unsafe {
|
||||
AudioObjectGetPropertyData(
|
||||
id,
|
||||
&address,
|
||||
0,
|
||||
ptr::null(),
|
||||
&mut size as *mut usize as *mut u32,
|
||||
bytes.as_mut_ptr() as *mut c_void,
|
||||
stream_list.as_mut_ptr() as *mut c_void,
|
||||
)
|
||||
};
|
||||
});
|
||||
if status != NO_ERR {
|
||||
return Err(status);
|
||||
}
|
||||
let buf_list = unsafe { &*(bytes.as_mut_ptr() as *mut AudioBufferList) };
|
||||
let buf_len = buf_list.mNumberBuffers as usize;
|
||||
if buf_len == 0 {
|
||||
return Ok(0);
|
||||
}
|
||||
let buf_ptr = buf_list.mBuffers.as_ptr() as *const AudioBuffer;
|
||||
let buffers = unsafe { slice::from_raw_parts(buf_ptr, buf_len) };
|
||||
let mut channels: u32 = 0;
|
||||
for buffer in buffers {
|
||||
channels += buffer.mNumberChannels;
|
||||
}
|
||||
let channels = stream_list
|
||||
.iter()
|
||||
.filter(|s: &&AudioObjectID| {
|
||||
if scope != Scope::Input {
|
||||
return true;
|
||||
}
|
||||
let address = AudioObjectPropertyAddress {
|
||||
mSelector: kAudioStreamPropertyTerminalType,
|
||||
mScope: kAudioObjectPropertyScopeGlobal,
|
||||
mElement: kAudioObjectPropertyElementMaster,
|
||||
};
|
||||
let mut ttype: u32 = 0;
|
||||
let status = unsafe {
|
||||
AudioObjectGetPropertyData(
|
||||
**s,
|
||||
&address,
|
||||
0,
|
||||
ptr::null(),
|
||||
&mut mem::size_of::<u32>() as *mut usize as *mut u32,
|
||||
&mut ttype as *mut u32 as *mut c_void,
|
||||
)
|
||||
};
|
||||
if status != NO_ERR {
|
||||
return false;
|
||||
}
|
||||
ttype == kAudioStreamTerminalTypeMicrophone
|
||||
|| (INPUT_MICROPHONE..OUTPUT_UNDEFINED).contains(&ttype)
|
||||
})
|
||||
.map(|s: &AudioObjectID| {
|
||||
let address = AudioObjectPropertyAddress {
|
||||
mSelector: kAudioStreamPropertyVirtualFormat,
|
||||
mScope: kAudioObjectPropertyScopeGlobal,
|
||||
mElement: kAudioObjectPropertyElementMaster,
|
||||
};
|
||||
let mut format = AudioStreamBasicDescription::default();
|
||||
let status = unsafe {
|
||||
AudioObjectGetPropertyData(
|
||||
*s,
|
||||
&address,
|
||||
0,
|
||||
ptr::null(),
|
||||
&mut mem::size_of::<AudioStreamBasicDescription>() as *mut usize as *mut u32,
|
||||
&mut format as *mut AudioStreamBasicDescription as *mut c_void,
|
||||
)
|
||||
};
|
||||
if status != NO_ERR {
|
||||
return 0;
|
||||
}
|
||||
format.mChannelsPerFrame
|
||||
})
|
||||
.sum();
|
||||
Ok(channels)
|
||||
}
|
||||
|
||||
pub fn test_device_in_scope(id: AudioObjectID, scope: Scope) -> bool {
|
||||
debug_assert_not_running_serially();
|
||||
let channels = test_device_channels_in_scope(id, scope);
|
||||
channels.is_ok() && channels.unwrap() > 0
|
||||
}
|
||||
|
||||
pub fn test_get_all_onwed_devices(id: AudioDeviceID) -> Vec<AudioObjectID> {
|
||||
assert_ne!(id, kAudioObjectUnknown);
|
||||
debug_assert_running_serially();
|
||||
|
||||
let address = AudioObjectPropertyAddress {
|
||||
mSelector: kAudioObjectPropertyOwnedObjects,
|
||||
|
@ -409,45 +489,46 @@ pub fn test_get_all_onwed_devices(id: AudioDeviceID) -> Vec<AudioObjectID> {
|
|||
let qualifier_data = &class_id;
|
||||
let mut size: usize = 0;
|
||||
|
||||
unsafe {
|
||||
assert_eq!(
|
||||
assert_eq!(
|
||||
unsafe {
|
||||
AudioObjectGetPropertyDataSize(
|
||||
id,
|
||||
&address,
|
||||
qualifier_data_size as u32,
|
||||
qualifier_data as *const u32 as *const c_void,
|
||||
&mut size as *mut usize as *mut u32
|
||||
),
|
||||
NO_ERR
|
||||
);
|
||||
}
|
||||
&mut size as *mut usize as *mut u32,
|
||||
)
|
||||
},
|
||||
NO_ERR
|
||||
);
|
||||
assert_ne!(size, 0);
|
||||
|
||||
let elements = size / mem::size_of::<AudioObjectID>();
|
||||
let mut devices: Vec<AudioObjectID> = allocate_array(elements);
|
||||
|
||||
unsafe {
|
||||
assert_eq!(
|
||||
assert_eq!(
|
||||
unsafe {
|
||||
AudioObjectGetPropertyData(
|
||||
id,
|
||||
&address,
|
||||
qualifier_data_size as u32,
|
||||
qualifier_data as *const u32 as *const c_void,
|
||||
&mut size as *mut usize as *mut u32,
|
||||
devices.as_mut_ptr() as *mut c_void
|
||||
),
|
||||
NO_ERR
|
||||
);
|
||||
}
|
||||
devices.as_mut_ptr() as *mut c_void,
|
||||
)
|
||||
},
|
||||
NO_ERR
|
||||
);
|
||||
|
||||
devices
|
||||
}
|
||||
|
||||
pub fn test_get_master_device(id: AudioObjectID) -> String {
|
||||
assert_ne!(id, kAudioObjectUnknown);
|
||||
debug_assert_running_serially();
|
||||
|
||||
let address = AudioObjectPropertyAddress {
|
||||
mSelector: kAudioAggregateDevicePropertyMasterSubDevice,
|
||||
mSelector: kAudioAggregateDevicePropertyMainSubDevice,
|
||||
mScope: kAudioObjectPropertyScopeGlobal,
|
||||
mElement: kAudioObjectPropertyElementMaster,
|
||||
};
|
||||
|
@ -465,6 +546,7 @@ pub fn test_get_master_device(id: AudioObjectID) -> String {
|
|||
}
|
||||
|
||||
pub fn test_get_drift_compensations(id: AudioObjectID) -> std::result::Result<u32, OSStatus> {
|
||||
debug_assert_running_serially();
|
||||
let address = AudioObjectPropertyAddress {
|
||||
mSelector: kAudioSubDevicePropertyDriftCompensation,
|
||||
mScope: kAudioObjectPropertyScopeGlobal,
|
||||
|
@ -491,6 +573,7 @@ pub fn test_get_drift_compensations(id: AudioObjectID) -> std::result::Result<u3
|
|||
|
||||
pub fn test_audiounit_scope_is_enabled(unit: AudioUnit, scope: Scope) -> bool {
|
||||
assert!(!unit.is_null());
|
||||
debug_assert_not_running_serially();
|
||||
let mut has_io: UInt32 = 0;
|
||||
let (scope, element) = match scope {
|
||||
Scope::Input => (kAudioUnitScope_Input, AU_IN_BUS),
|
||||
|
@ -498,14 +581,14 @@ pub fn test_audiounit_scope_is_enabled(unit: AudioUnit, scope: Scope) -> bool {
|
|||
};
|
||||
let mut size = mem::size_of::<UInt32>();
|
||||
assert_eq!(
|
||||
audio_unit_get_property(
|
||||
run_serially(|| audio_unit_get_property(
|
||||
unit,
|
||||
kAudioOutputUnitProperty_HasIO,
|
||||
scope,
|
||||
element,
|
||||
&mut has_io,
|
||||
&mut size
|
||||
),
|
||||
)),
|
||||
NO_ERR
|
||||
);
|
||||
has_io != 0
|
||||
|
@ -516,6 +599,7 @@ pub fn test_audiounit_get_buffer_frame_size(
|
|||
scope: Scope,
|
||||
prop_scope: PropertyScope,
|
||||
) -> std::result::Result<u32, OSStatus> {
|
||||
debug_assert_not_running_serially();
|
||||
let element = match scope {
|
||||
Scope::Input => AU_IN_BUS,
|
||||
Scope::Output => AU_OUT_BUS,
|
||||
|
@ -526,7 +610,7 @@ pub fn test_audiounit_get_buffer_frame_size(
|
|||
};
|
||||
let mut buffer_frames: u32 = 0;
|
||||
let mut size = mem::size_of::<u32>();
|
||||
let status = unsafe {
|
||||
let status = run_serially(|| unsafe {
|
||||
AudioUnitGetProperty(
|
||||
unit,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
|
@ -535,7 +619,7 @@ pub fn test_audiounit_get_buffer_frame_size(
|
|||
&mut buffer_frames as *mut u32 as *mut c_void,
|
||||
&mut size as *mut usize as *mut u32,
|
||||
)
|
||||
};
|
||||
});
|
||||
if status == NO_ERR {
|
||||
Ok(buffer_frames)
|
||||
} else {
|
||||
|
@ -554,6 +638,7 @@ pub fn test_set_default_device(
|
|||
device: AudioObjectID,
|
||||
scope: Scope,
|
||||
) -> std::result::Result<AudioObjectID, OSStatus> {
|
||||
debug_assert_not_running_serially();
|
||||
assert!(test_device_in_scope(device, scope.clone()));
|
||||
let default = test_get_default_device(scope.clone()).unwrap();
|
||||
if default == device {
|
||||
|
@ -570,7 +655,7 @@ pub fn test_set_default_device(
|
|||
mElement: kAudioObjectPropertyElementMaster,
|
||||
};
|
||||
let size = mem::size_of::<AudioObjectID>();
|
||||
let status = unsafe {
|
||||
let status = run_serially(|| unsafe {
|
||||
AudioObjectSetPropertyData(
|
||||
kAudioObjectSystemObject,
|
||||
&address,
|
||||
|
@ -579,7 +664,7 @@ pub fn test_set_default_device(
|
|||
size as u32,
|
||||
&device as *const AudioObjectID as *const c_void,
|
||||
)
|
||||
};
|
||||
});
|
||||
let new_default = test_get_default_device(scope.clone()).unwrap();
|
||||
if new_default == default {
|
||||
Err(-1)
|
||||
|
@ -644,6 +729,7 @@ pub fn test_create_device_change_listener<F>(scope: Scope, listener: F) -> TestP
|
|||
where
|
||||
F: Fn(&[AudioObjectPropertyAddress]) -> OSStatus,
|
||||
{
|
||||
debug_assert_running_serially();
|
||||
let address = AudioObjectPropertyAddress {
|
||||
mSelector: match scope {
|
||||
Scope::Input => kAudioHardwarePropertyDefaultInputDevice,
|
||||
|
@ -652,6 +738,7 @@ where
|
|||
mScope: kAudioObjectPropertyScopeGlobal,
|
||||
mElement: kAudioObjectPropertyElementMaster,
|
||||
};
|
||||
|
||||
TestPropertyListener::new(kAudioObjectSystemObject, address, listener)
|
||||
}
|
||||
|
||||
|
@ -677,6 +764,7 @@ where
|
|||
}
|
||||
|
||||
pub fn start(&self) -> std::result::Result<(), OSStatus> {
|
||||
debug_assert_running_serially();
|
||||
let status = unsafe {
|
||||
AudioObjectAddPropertyListener(
|
||||
self.device,
|
||||
|
@ -693,6 +781,7 @@ where
|
|||
}
|
||||
|
||||
pub fn stop(&self) -> std::result::Result<(), OSStatus> {
|
||||
debug_assert_running_serially();
|
||||
let status = unsafe {
|
||||
AudioObjectRemovePropertyListener(
|
||||
self.device,
|
||||
|
@ -726,7 +815,7 @@ where
|
|||
F: Fn(&[AudioObjectPropertyAddress]) -> OSStatus,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
self.stop();
|
||||
run_serially(|| self.stop());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -767,6 +856,7 @@ impl TestDevicePlugger {
|
|||
}
|
||||
|
||||
fn destroy_aggregate_device(&mut self) -> std::result::Result<(), OSStatus> {
|
||||
debug_assert_not_running_serially();
|
||||
assert_ne!(self.plugin_id, kAudioObjectUnknown);
|
||||
assert_ne!(self.device_id, kAudioObjectUnknown);
|
||||
|
||||
|
@ -777,7 +867,7 @@ impl TestDevicePlugger {
|
|||
};
|
||||
|
||||
let mut size: usize = 0;
|
||||
let status = unsafe {
|
||||
let status = run_serially(|| unsafe {
|
||||
AudioObjectGetPropertyDataSize(
|
||||
self.plugin_id,
|
||||
&address,
|
||||
|
@ -785,13 +875,13 @@ impl TestDevicePlugger {
|
|||
ptr::null(),
|
||||
&mut size as *mut usize as *mut u32,
|
||||
)
|
||||
};
|
||||
});
|
||||
if status != NO_ERR {
|
||||
return Err(status);
|
||||
}
|
||||
assert_ne!(size, 0);
|
||||
|
||||
let status = unsafe {
|
||||
let status = run_serially(|| unsafe {
|
||||
// This call can simulate removing a device.
|
||||
AudioObjectGetPropertyData(
|
||||
self.plugin_id,
|
||||
|
@ -801,7 +891,7 @@ impl TestDevicePlugger {
|
|||
&mut size as *mut usize as *mut u32,
|
||||
&mut self.device_id as *mut AudioDeviceID as *mut c_void,
|
||||
)
|
||||
};
|
||||
});
|
||||
if status == NO_ERR {
|
||||
self.device_id = kAudioObjectUnknown;
|
||||
Ok(())
|
||||
|
@ -811,6 +901,7 @@ impl TestDevicePlugger {
|
|||
}
|
||||
|
||||
fn create_aggregate_device(&self) -> std::result::Result<AudioObjectID, OSStatus> {
|
||||
debug_assert_not_running_serially();
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
const TEST_AGGREGATE_DEVICE_NAME: &str = "TestAggregateDevice";
|
||||
|
@ -830,7 +921,7 @@ impl TestDevicePlugger {
|
|||
};
|
||||
|
||||
let mut size: usize = 0;
|
||||
let status = unsafe {
|
||||
let status = run_serially(|| unsafe {
|
||||
AudioObjectGetPropertyDataSize(
|
||||
self.plugin_id,
|
||||
&address,
|
||||
|
@ -838,7 +929,7 @@ impl TestDevicePlugger {
|
|||
ptr::null(),
|
||||
&mut size as *mut usize as *mut u32,
|
||||
)
|
||||
};
|
||||
});
|
||||
if status != NO_ERR {
|
||||
return Err(status);
|
||||
}
|
||||
|
@ -915,14 +1006,18 @@ impl TestDevicePlugger {
|
|||
CFRelease(sub_devices as *const c_void);
|
||||
|
||||
// This call can simulate adding a device.
|
||||
let status = AudioObjectGetPropertyData(
|
||||
self.plugin_id,
|
||||
&address,
|
||||
mem::size_of_val(&device_dict) as u32,
|
||||
&device_dict as *const CFMutableDictionaryRef as *const c_void,
|
||||
&mut size as *mut usize as *mut u32,
|
||||
&mut device_id as *mut AudioDeviceID as *mut c_void,
|
||||
);
|
||||
let status = {
|
||||
run_serially(|| {
|
||||
AudioObjectGetPropertyData(
|
||||
self.plugin_id,
|
||||
&address,
|
||||
mem::size_of_val(&device_dict) as u32,
|
||||
&device_dict as *const CFMutableDictionaryRef as *const c_void,
|
||||
&mut size as *mut usize as *mut u32,
|
||||
&mut device_id as *mut AudioDeviceID as *mut c_void,
|
||||
)
|
||||
})
|
||||
};
|
||||
CFRelease(device_dict as *const c_void);
|
||||
status
|
||||
};
|
||||
|
@ -935,6 +1030,7 @@ impl TestDevicePlugger {
|
|||
}
|
||||
|
||||
fn get_system_plugin_id() -> std::result::Result<AudioObjectID, OSStatus> {
|
||||
debug_assert_not_running_serially();
|
||||
let address = AudioObjectPropertyAddress {
|
||||
mSelector: kAudioHardwarePropertyPlugInForBundleID,
|
||||
mScope: kAudioObjectPropertyScopeGlobal,
|
||||
|
@ -942,7 +1038,7 @@ impl TestDevicePlugger {
|
|||
};
|
||||
|
||||
let mut size: usize = 0;
|
||||
let status = unsafe {
|
||||
let status = run_serially(|| unsafe {
|
||||
AudioObjectGetPropertyDataSize(
|
||||
kAudioObjectSystemObject,
|
||||
&address,
|
||||
|
@ -950,7 +1046,7 @@ impl TestDevicePlugger {
|
|||
ptr::null(),
|
||||
&mut size as *mut usize as *mut u32,
|
||||
)
|
||||
};
|
||||
});
|
||||
if status != NO_ERR {
|
||||
return Err(status);
|
||||
}
|
||||
|
@ -967,14 +1063,16 @@ impl TestDevicePlugger {
|
|||
assert_eq!(size, mem::size_of_val(&translation_value));
|
||||
|
||||
let status = unsafe {
|
||||
let status = AudioObjectGetPropertyData(
|
||||
kAudioObjectSystemObject,
|
||||
&address,
|
||||
0,
|
||||
ptr::null(),
|
||||
&mut size as *mut usize as *mut u32,
|
||||
&mut translation_value as *mut AudioValueTranslation as *mut c_void,
|
||||
);
|
||||
let status = run_serially(|| {
|
||||
AudioObjectGetPropertyData(
|
||||
kAudioObjectSystemObject,
|
||||
&address,
|
||||
0,
|
||||
ptr::null(),
|
||||
&mut size as *mut usize as *mut u32,
|
||||
&mut translation_value as *mut AudioValueTranslation as *mut c_void,
|
||||
)
|
||||
});
|
||||
CFRelease(in_bundle_ref as *const c_void);
|
||||
status
|
||||
};
|
||||
|
@ -991,10 +1089,11 @@ impl TestDevicePlugger {
|
|||
// them into the array, if the device is an aggregate device. See the code in
|
||||
// AggregateDevice::get_sub_devices and audiounit_set_aggregate_sub_device_list.
|
||||
fn get_sub_devices(scope: Scope) -> Option<CFArrayRef> {
|
||||
debug_assert_not_running_serially();
|
||||
let device = test_get_default_device(scope);
|
||||
device?;
|
||||
let device = device.unwrap();
|
||||
let uid = get_device_global_uid(device);
|
||||
let uid = run_serially(|| get_device_global_uid(device));
|
||||
if uid.is_err() {
|
||||
return None;
|
||||
}
|
||||
|
@ -1033,6 +1132,7 @@ pub fn test_ops_context_operation<F>(name: &'static str, operation: F)
|
|||
where
|
||||
F: FnOnce(*mut ffi::cubeb),
|
||||
{
|
||||
debug_assert_not_running_serially();
|
||||
let name_c_string = CString::new(name).expect("Failed to create context name");
|
||||
let mut context = ptr::null_mut::<ffi::cubeb>();
|
||||
assert_eq!(
|
||||
|
@ -1047,6 +1147,59 @@ where
|
|||
// The in-out stream initializeed with different device will create an aggregate_device and
|
||||
// result in firing device-collection-changed callbacks. Run in-out streams with tests
|
||||
// capturing device-collection-changed callbacks may cause troubles.
|
||||
pub fn test_ops_stream_operation_on_context<F>(
|
||||
name: &'static str,
|
||||
context_ptr: *mut ffi::cubeb,
|
||||
input_device: ffi::cubeb_devid,
|
||||
input_stream_params: *mut ffi::cubeb_stream_params,
|
||||
output_device: ffi::cubeb_devid,
|
||||
output_stream_params: *mut ffi::cubeb_stream_params,
|
||||
latency_frames: u32,
|
||||
data_callback: ffi::cubeb_data_callback,
|
||||
state_callback: ffi::cubeb_state_callback,
|
||||
user_ptr: *mut c_void,
|
||||
operation: F,
|
||||
) where
|
||||
F: FnOnce(*mut ffi::cubeb_stream),
|
||||
{
|
||||
// Do nothing if there is no input/output device to perform input/output tests.
|
||||
if !input_stream_params.is_null() && test_get_default_device(Scope::Input).is_none() {
|
||||
println!("No input device to perform input tests for \"{}\".", name);
|
||||
return;
|
||||
}
|
||||
|
||||
if !output_stream_params.is_null() && test_get_default_device(Scope::Output).is_none() {
|
||||
println!("No output device to perform output tests for \"{}\".", name);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut stream: *mut ffi::cubeb_stream = ptr::null_mut();
|
||||
let stream_name = CString::new(name).expect("Failed to create stream name");
|
||||
assert_eq!(
|
||||
unsafe {
|
||||
OPS.stream_init.unwrap()(
|
||||
context_ptr,
|
||||
&mut stream,
|
||||
stream_name.as_ptr(),
|
||||
input_device,
|
||||
input_stream_params,
|
||||
output_device,
|
||||
output_stream_params,
|
||||
latency_frames,
|
||||
data_callback,
|
||||
state_callback,
|
||||
user_ptr,
|
||||
)
|
||||
},
|
||||
ffi::CUBEB_OK
|
||||
);
|
||||
assert!(!stream.is_null());
|
||||
operation(stream);
|
||||
unsafe {
|
||||
OPS.stream_destroy.unwrap()(stream);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn test_ops_stream_operation<F>(
|
||||
name: &'static str,
|
||||
input_device: ffi::cubeb_devid,
|
||||
|
@ -1062,42 +1215,19 @@ pub fn test_ops_stream_operation<F>(
|
|||
F: FnOnce(*mut ffi::cubeb_stream),
|
||||
{
|
||||
test_ops_context_operation("context: stream operation", |context_ptr| {
|
||||
// Do nothing if there is no input/output device to perform input/output tests.
|
||||
if !input_stream_params.is_null() && test_get_default_device(Scope::Input).is_none() {
|
||||
println!("No input device to perform input tests for \"{}\".", name);
|
||||
return;
|
||||
}
|
||||
|
||||
if !output_stream_params.is_null() && test_get_default_device(Scope::Output).is_none() {
|
||||
println!("No output device to perform output tests for \"{}\".", name);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut stream: *mut ffi::cubeb_stream = ptr::null_mut();
|
||||
let stream_name = CString::new(name).expect("Failed to create stream name");
|
||||
assert_eq!(
|
||||
unsafe {
|
||||
OPS.stream_init.unwrap()(
|
||||
context_ptr,
|
||||
&mut stream,
|
||||
stream_name.as_ptr(),
|
||||
input_device,
|
||||
input_stream_params,
|
||||
output_device,
|
||||
output_stream_params,
|
||||
latency_frames,
|
||||
data_callback,
|
||||
state_callback,
|
||||
user_ptr,
|
||||
)
|
||||
},
|
||||
ffi::CUBEB_OK
|
||||
test_ops_stream_operation_on_context(
|
||||
name,
|
||||
context_ptr,
|
||||
input_device,
|
||||
input_stream_params,
|
||||
output_device,
|
||||
output_stream_params,
|
||||
latency_frames,
|
||||
data_callback,
|
||||
state_callback,
|
||||
user_ptr,
|
||||
operation,
|
||||
);
|
||||
assert!(!stream.is_null());
|
||||
operation(stream);
|
||||
unsafe {
|
||||
OPS.stream_destroy.unwrap()(stream);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1136,7 +1266,7 @@ fn test_get_raw_stream<F>(
|
|||
user_ptr,
|
||||
data_callback,
|
||||
state_callback,
|
||||
global_latency_frames.unwrap(),
|
||||
global_latency_frames,
|
||||
);
|
||||
stream.core_stream_data = CoreStreamData::new(&stream, None, None);
|
||||
|
||||
|
@ -1154,6 +1284,7 @@ pub fn test_get_stream_with_default_data_callback_by_type<F>(
|
|||
) where
|
||||
F: FnOnce(&mut AudioUnitStream),
|
||||
{
|
||||
debug_assert_not_running_serially();
|
||||
let mut input_params = get_dummy_stream_params(Scope::Input);
|
||||
let mut output_params = get_dummy_stream_params(Scope::Output);
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ static_prefs = { path = "../../../../modules/libpref/init/static_prefs" }
|
|||
profiler_helper = { path = "../../../../tools/profiler/rust-helper", optional = true }
|
||||
mozurl = { path = "../../../../netwerk/base/mozurl" }
|
||||
webrender_bindings = { path = "../../../../gfx/webrender_bindings" }
|
||||
cubeb-coreaudio = { git = "https://github.com/mozilla/cubeb-coreaudio-rs", rev = "d23ab55eab684b46f46e1da177c8814f6103a009", optional = true }
|
||||
cubeb-coreaudio = { git = "https://github.com/mozilla/cubeb-coreaudio-rs", rev = "7b1021714989bedef3d40a843a516273db38beaa", optional = true }
|
||||
cubeb-pulse = { git = "https://github.com/mozilla/cubeb-pulse-rs", rev="8ff972c8e2ec1782ff262ac4071c0415e69b1367", optional = true, features=["pulse-dlopen"] }
|
||||
cubeb-sys = { version = "0.12.0", optional = true, features=["gecko-in-tree"] }
|
||||
audioipc2-client = { git = "https://github.com/mozilla/audioipc", rev = "409e11f8de6288e9ddfe269654523735302e59e6", optional = true }
|
||||
|
|
Загрузка…
Ссылка в новой задаче