This commit is contained in:
Chun-Min Chang 2018-10-08 15:52:54 -07:00
Родитель 332627554e
Коммит 20b6225e25
4 изменённых файлов: 681 добавлений и 54 удалений

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

@ -7,7 +7,9 @@ Implementation of MacOS Audio backend in CoreAudio framework for [Cubeb][cubeb]
- Create tests for later refactoring
## TODO
- Test aggregate device
- Test aggregate devices
- Test for stream operations
- Clean up the tests. Merge the duplicated pieces in to a function.
[cubeb]: https://github.com/kinetiknz/cubeb "Cross platform audio library"
[cubeb-au]: https://github.com/kinetiknz/cubeb/blob/master/src/cubeb_audiounit.cpp "Cubeb AudioUnit"

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

@ -123,8 +123,10 @@ fn audiounit_get_default_device_datasource(devtype: DeviceType,
}
let mut size = mem::size_of_val(data);
// FIXIT: This is wrong. We will use output scope when dev_type
// is unknown. Change it after C version is updated!
// TODO: devtype includes input, output, and unknown. This is a bad style
// to check type, although this function will early return for
// unknown type since audiounit_get_default_device_id will gives
// a kAudioObjectUnknown for unknown type.
/* This fails with some USB headsets (e.g., Plantronic .Audio 628). */
let r = audio_object_get_property_data(id, if devtype == DeviceType::INPUT {
&INPUT_DATA_SOURCE_PROPERTY_ADDRESS
@ -138,6 +140,9 @@ fn audiounit_get_default_device_datasource(devtype: DeviceType,
Ok(())
}
// TODO: This actually is the name converted from the bytes of the data source
// (kAudioDevicePropertyDataSource), rather than the name of the audio
// device(kAudioObjectPropertyName). The naming here is vague.
fn audiounit_get_default_device_name(stm: &AudioUnitStream,
device: &mut ffi::cubeb_device,
devtype: DeviceType) -> Result<()>
@ -145,8 +150,10 @@ fn audiounit_get_default_device_name(stm: &AudioUnitStream,
let mut data: u32 = 0;
audiounit_get_default_device_datasource(devtype, &mut data)?;
// FIXIT: This is wrong. We will use output scope when dev_type
// is unknown. Change it after C version is updated!
// TODO: devtype includes input, output, and unknown. This is a bad style
// to check type, although this function will early return for
// unknown type since audiounit_get_default_device_datasource will
// throw an error for unknown type.
let name = if devtype == DeviceType::INPUT {
&mut device.input_name
} else {
@ -155,8 +162,7 @@ fn audiounit_get_default_device_name(stm: &AudioUnitStream,
// Leak the memory to the external code.
*name = convert_uint32_into_string(data).into_raw();
if name.is_null() {
// FIXIT: This is wrong. We will use output scope when dev_type
// is unknown. Change it after C version is updated!
// TODO: Bad style to use scope as the above.
cubeb_log!("({:p}) name of {} device is empty!", stm,
if devtype == DeviceType::INPUT { "input" } else { "output" } );
}
@ -236,6 +242,7 @@ fn audiounit_get_channel_count(devid: AudioObjectID, scope: AudioObjectPropertyS
count
}
// TODO: It seems that it works no matter what scope is(see test.rs). Is it ok?
fn audiounit_get_available_samplerate(devid: AudioObjectID, scope: AudioObjectPropertyScope,
min: &mut u32, max: &mut u32, def: &mut u32)
{

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

@ -148,51 +148,51 @@ fn test_ops_context_device_collection_destroy() {
// stream_stop: Some($crate::capi::capi_stream_stop::<$stm>),
// stream_get_position: Some($crate::capi::capi_stream_get_position::<$stm>),
#[test]
fn test_ops_stream_latency() {
let s: *mut ffi::cubeb_stream = ptr::null_mut();
let mut latency = u32::max_value();
assert_eq!(
unsafe { OPS.stream_get_latency.unwrap()(s, &mut latency) },
ffi::CUBEB_OK
);
assert_eq!(latency, 0);
}
// #[test]
// fn test_ops_stream_latency() {
// let s: *mut ffi::cubeb_stream = ptr::null_mut();
// let mut latency = u32::max_value();
// assert_eq!(
// unsafe { OPS.stream_get_latency.unwrap()(s, &mut latency) },
// ffi::CUBEB_OK
// );
// assert_eq!(latency, 0);
// }
#[test]
fn test_ops_stream_set_volume() {
let s: *mut ffi::cubeb_stream = ptr::null_mut();
unsafe {
OPS.stream_set_volume.unwrap()(s, 0.5);
}
}
// #[test]
// fn test_ops_stream_set_volume() {
// let s: *mut ffi::cubeb_stream = ptr::null_mut();
// unsafe {
// OPS.stream_set_volume.unwrap()(s, 0.5);
// }
// }
#[test]
fn test_ops_stream_set_panning() {
let s: *mut ffi::cubeb_stream = ptr::null_mut();
unsafe {
OPS.stream_set_panning.unwrap()(s, 0.5);
}
}
// #[test]
// fn test_ops_stream_set_panning() {
// let s: *mut ffi::cubeb_stream = ptr::null_mut();
// unsafe {
// OPS.stream_set_panning.unwrap()(s, 0.5);
// }
// }
#[test]
fn test_ops_stream_current_device() {
let s: *mut ffi::cubeb_stream = ptr::null_mut();
let mut device: *mut ffi::cubeb_device = ptr::null_mut();
assert_eq!(
unsafe { OPS.stream_get_current_device.unwrap()(s, &mut device) },
ffi::CUBEB_OK
);
assert_eq!(device, 0xDEAD_BEEF as *mut _);
}
// #[test]
// fn test_ops_stream_current_device() {
// let s: *mut ffi::cubeb_stream = ptr::null_mut();
// let mut device: *mut ffi::cubeb_device = ptr::null_mut();
// assert_eq!(
// unsafe { OPS.stream_get_current_device.unwrap()(s, &mut device) },
// ffi::CUBEB_OK
// );
// assert_eq!(device, 0xDEAD_BEEF as *mut _);
// }
#[test]
fn test_ops_stream_device_destroy() {
let s: *mut ffi::cubeb_stream = ptr::null_mut();
unsafe {
OPS.stream_device_destroy.unwrap()(s, 0xDEAD_BEEF as *mut _);
}
}
// #[test]
// fn test_ops_stream_device_destroy() {
// let s: *mut ffi::cubeb_stream = ptr::null_mut();
// unsafe {
// OPS.stream_device_destroy.unwrap()(s, 0xDEAD_BEEF as *mut _);
// }
// }
// Private APIs
// ============================================================================
@ -232,6 +232,111 @@ fn test_get_default_device_id() {
)
}
// convert_uint32_into_string
// ------------------------------------
#[test]
fn test_convert_uint32_into_string() {
let empty = convert_uint32_into_string(0);
assert_eq!(empty, CString::default());
let data: u32 = ('R' as u32) << 24 |
('U' as u32) << 16 |
('S' as u32) << 8 |
'T' as u32;
let data_string = convert_uint32_into_string(data);
assert_eq!(data_string, CString::new("RUST").unwrap());
}
// audiounit_get_default_device_datasource
// ------------------------------------
#[test]
fn test_get_default_device_datasource() {
let mut data = 0;
// unknown type:
assert_eq!(
audiounit_get_default_device_datasource(
DeviceType::UNKNOWN,
&mut data
).unwrap_err(),
Error::error()
);
// TODO: The following fail with some USB headsets (e.g., Plantronic .Audio 628).
// Find a reliable way to test the input/output scope.
// input:
data = 0;
assert!(
audiounit_get_default_device_datasource(
DeviceType::INPUT,
&mut data
).is_ok()
);
assert_ne!(data, 0);
// output:
data = 0;
assert!(
audiounit_get_default_device_datasource(
DeviceType::OUTPUT,
&mut data
).is_ok()
);
assert_ne!(data, 0);
}
// audiounit_get_default_device_name
// ------------------------------------
#[test]
fn test_get_default_device_name() {
let ctx = AudioUnitContext { ops: ptr::null() };
let stream = AudioUnitStream::new(&ctx).unwrap();
let mut device = ffi::cubeb_device::default();
// unknown type:
assert_eq!(
audiounit_get_default_device_name(
stream.as_ref(),
&mut device,
DeviceType::UNKNOWN
).unwrap_err(),
Error::error()
);
// TODO: The following fail with some USB headsets (e.g., Plantronic .Audio 628).
// Find a reliable way to test the input/output scope.
// input:
device = ffi::cubeb_device::default();
assert!(
audiounit_get_default_device_name(
stream.as_ref(),
&mut device,
DeviceType::INPUT
).is_ok()
);
assert_ne!(device.input_name, ptr::null_mut());
assert_eq!(device.output_name, ptr::null_mut());
// output:
device = ffi::cubeb_device::default();
assert!(
audiounit_get_default_device_name(
stream.as_ref(),
&mut device,
DeviceType::OUTPUT
).is_ok()
);
assert_eq!(device.input_name, ptr::null_mut());
assert_ne!(device.output_name, ptr::null_mut());
}
// strref_to_cstr_utf8
// ------------------------------------
// TODO
// get_channel_count
// ------------------------------------
#[test]
@ -247,6 +352,516 @@ fn test_get_channel_count() {
}
}
// get_available_samplerate
// ------------------------------------
#[test]
fn test_get_available_samplerate_unknown() {
let mut defualt = 0;
let mut min = 0;
let mut max = 0;
// global scope:
audiounit_get_available_samplerate(
kAudioObjectUnknown,
kAudioObjectPropertyScopeGlobal,
&mut min,
&mut max,
&mut defualt
);
assert_eq!(defualt, 0);
assert_eq!(min, 0);
assert_eq!(max, 0);
// input scope:
audiounit_get_available_samplerate(
kAudioObjectUnknown,
kAudioDevicePropertyScopeInput,
&mut min,
&mut max,
&mut defualt
);
assert_eq!(defualt, 0);
assert_eq!(min, 0);
assert_eq!(max, 0);
// output scope:
audiounit_get_available_samplerate(
kAudioObjectUnknown,
kAudioDevicePropertyScopeOutput,
&mut min,
&mut max,
&mut defualt
);
assert_eq!(defualt, 0);
assert_eq!(min, 0);
assert_eq!(max, 0);
}
#[test]
fn test_get_available_samplerate_input() {
let input_id = audiounit_get_default_device_id(DeviceType::INPUT);
if !valid_id(input_id) {
return;
}
let mut defualt = 0;
let mut min = 0;
let mut max = 0;
// global scope:
audiounit_get_available_samplerate(
input_id,
kAudioObjectPropertyScopeGlobal,
&mut min,
&mut max,
&mut defualt
);
// println!("[samplerate_input] <global> default: {}, min: {}, max: {}", defualt, min, max);
assert!(defualt > 0);
assert!(min > 0);
assert!(max > 0);
assert!(min <= max);
assert!(min <= defualt);
assert!(defualt <= max);
// input scope:
defualt = 0;
min = 0;
max = 0;
audiounit_get_available_samplerate(
input_id,
kAudioDevicePropertyScopeInput,
&mut min,
&mut max,
&mut defualt
);
// println!("[samplerate_input] <input> default: {}, min: {}, max: {}", defualt, min, max);
assert!(defualt > 0);
assert!(min > 0);
assert!(max > 0);
assert!(min <= max);
assert!(min <= defualt);
assert!(defualt <= max);
// output scope:
defualt = 0;
min = 0;
max = 0;
audiounit_get_available_samplerate(
input_id,
kAudioDevicePropertyScopeOutput,
&mut min,
&mut max,
&mut defualt
);
// println!("[samplerate_input] <output> default: {}, min: {}, max: {}", defualt, min, max);
if is_output(input_id) {
assert!(defualt > 0);
assert!(min > 0);
assert!(max > 0);
assert!(min <= max);
assert!(min <= defualt);
assert!(defualt <= max);
} else {
// assert_eq!(defualt, 0);
// assert_eq!(min, 0);
// assert_eq!(max, 0);
// Surprisingly it works!
assert!(defualt > 0);
assert!(min > 0);
assert!(max > 0);
assert!(min <= max);
assert!(min <= defualt);
assert!(defualt <= max);
}
}
#[test]
fn test_get_available_samplerate_output() {
let output_id = audiounit_get_default_device_id(DeviceType::OUTPUT);
if !valid_id(output_id) {
return;
}
let mut defualt = 0;
let mut min = 0;
let mut max = 0;
// global scope:
audiounit_get_available_samplerate(
output_id,
kAudioObjectPropertyScopeGlobal,
&mut min,
&mut max,
&mut defualt
);
// println!("[samplerate_output] <global> default: {}, min: {}, max: {}", defualt, min, max);
assert!(defualt > 0);
assert!(min > 0);
assert!(max > 0);
assert!(min <= max);
assert!(min <= defualt);
assert!(defualt <= max);
// input scope:
defualt = 0;
min = 0;
max = 0;
audiounit_get_available_samplerate(
output_id,
kAudioDevicePropertyScopeInput,
&mut min,
&mut max,
&mut defualt
);
// println!("[samplerate_output] <input> default: {}, min: {}, max: {}", defualt, min, max);
if is_input(output_id) {
assert!(defualt > 0);
assert!(min > 0);
assert!(max > 0);
assert!(min <= max);
assert!(min <= defualt);
assert!(defualt <= max);
} else {
// assert_eq!(defualt, 0);
// assert_eq!(min, 0);
// assert_eq!(max, 0);
// Surprisingly it works!
assert!(defualt > 0);
assert!(min > 0);
assert!(max > 0);
assert!(min <= max);
assert!(min <= defualt);
assert!(defualt <= max);
}
// output scope:
defualt = 0;
min = 0;
max = 0;
audiounit_get_available_samplerate(
output_id,
kAudioDevicePropertyScopeOutput,
&mut min,
&mut max,
&mut defualt
);
// println!("[samplerate_output] <output> default: {}, min: {}, max: {}", defualt, min, max);
assert!(defualt > 0);
assert!(min > 0);
assert!(max > 0);
assert!(min <= max);
assert!(min <= defualt);
assert!(defualt <= max);
}
// get_device_presentation_latency
// ------------------------------------
#[test]
fn test_get_device_presentation_latency_unknown() {
let mut latency = 0;
// global scope:
latency = audiounit_get_device_presentation_latency(
kAudioObjectUnknown,
kAudioObjectPropertyScopeGlobal,
);
assert_eq!(latency, 0);
// input scope:
latency = audiounit_get_device_presentation_latency(
kAudioObjectUnknown,
kAudioDevicePropertyScopeInput,
);
assert_eq!(latency, 0);
// output scope:
latency = audiounit_get_device_presentation_latency(
kAudioObjectUnknown,
kAudioDevicePropertyScopeOutput,
);
assert_eq!(latency, 0);
}
#[test]
fn test_get_device_presentation_latency_input() {
let input_id = audiounit_get_default_device_id(DeviceType::INPUT);
if !valid_id(input_id) {
return;
}
let mut latency = 0;
// global scope:
latency = audiounit_get_device_presentation_latency(
input_id,
kAudioObjectPropertyScopeGlobal,
);
assert_eq!(latency, 0);
// input scope:
latency = audiounit_get_device_presentation_latency(
input_id,
kAudioDevicePropertyScopeInput,
);
assert!(latency > 0);
// output scope:
latency = audiounit_get_device_presentation_latency(
input_id,
kAudioDevicePropertyScopeOutput,
);
if is_output(input_id) {
assert!(latency > 0);
} else {
assert_eq!(latency, 0);
}
}
#[test]
fn test_get_device_presentation_latency_output() {
let output_id = audiounit_get_default_device_id(DeviceType::OUTPUT);
if !valid_id(output_id) {
return;
}
let mut latency = 0;
// global scope:
latency = audiounit_get_device_presentation_latency(
output_id,
kAudioObjectPropertyScopeGlobal,
);
assert_eq!(latency, 0);
// input scope:
latency = audiounit_get_device_presentation_latency(
output_id,
kAudioDevicePropertyScopeInput,
);
if is_input(output_id) {
assert!(latency > 0);
} else {
assert_eq!(latency, 0);
}
// output scope:
latency = audiounit_get_device_presentation_latency(
output_id,
kAudioDevicePropertyScopeOutput,
);
assert!(latency > 0);
}
// create_device_from_hwdev
// ------------------------------------
#[test]
fn test_create_device_from_hwdev_unknown() {
let mut info = ffi::cubeb_device_info::default();
// unknown
assert_eq!(
audiounit_create_device_from_hwdev(
&mut info,
kAudioObjectUnknown,
DeviceType::UNKNOWN,
).unwrap_err(),
Error::error()
);
// input
assert_eq!(
audiounit_create_device_from_hwdev(
&mut info,
kAudioObjectUnknown,
DeviceType::INPUT,
).unwrap_err(),
Error::error()
);
// output
assert_eq!(
audiounit_create_device_from_hwdev(
&mut info,
kAudioObjectUnknown,
DeviceType::OUTPUT,
).unwrap_err(),
Error::error()
);
}
#[test]
fn test_create_device_from_hwdev_input() {
let input_id = audiounit_get_default_device_id(DeviceType::INPUT);
if !valid_id(input_id) {
return;
}
let mut info = ffi::cubeb_device_info::default();
// unknown
assert_eq!(
audiounit_create_device_from_hwdev(
&mut info,
input_id,
DeviceType::UNKNOWN,
).unwrap_err(),
Error::error()
);
// input
info = ffi::cubeb_device_info::default();
assert!(
audiounit_create_device_from_hwdev(
&mut info,
input_id,
DeviceType::INPUT,
).is_ok()
);
assert_ne!(info.devid, ptr::null_mut());
assert_ne!(info.device_id, ptr::null_mut());
assert_eq!(info.group_id, info.device_id);
assert_ne!(info.friendly_name, ptr::null_mut());
assert_ne!(info.vendor_name, ptr::null_mut());
assert_eq!(info.device_type, ffi::CUBEB_DEVICE_TYPE_INPUT);
assert_eq!(info.state, ffi::CUBEB_DEVICE_STATE_ENABLED);
assert_eq!(info.preferred, ffi::CUBEB_DEVICE_PREF_ALL);
assert!(info.max_channels > 0);
assert_eq!(info.default_format, ffi::CUBEB_DEVICE_FMT_F32NE);
assert!(info.min_rate <= info.max_rate);
assert!(info.min_rate <= info.default_rate);
assert!(info.default_rate <= info.max_rate);
assert!(info.latency_lo > 0);
assert!(info.latency_hi > 0);
assert!(info.latency_lo <= info.latency_hi);
// output
info = ffi::cubeb_device_info::default();
if is_output(input_id) {
assert!(
audiounit_create_device_from_hwdev(
&mut info,
input_id,
DeviceType::OUTPUT,
).is_ok()
);
assert_ne!(info.devid, ptr::null_mut());
assert_ne!(info.device_id, ptr::null_mut());
assert_eq!(info.group_id, info.device_id);
assert_ne!(info.friendly_name, ptr::null_mut());
assert_ne!(info.vendor_name, ptr::null_mut());
assert_eq!(info.device_type, ffi::CUBEB_DEVICE_TYPE_OUTPUT);
assert_eq!(info.state, ffi::CUBEB_DEVICE_STATE_ENABLED);
assert_eq!(info.preferred, ffi::CUBEB_DEVICE_PREF_ALL);
assert!(info.max_channels > 0);
assert_eq!(info.default_format, ffi::CUBEB_DEVICE_FMT_F32NE);
assert!(info.min_rate <= info.max_rate);
assert!(info.min_rate <= info.default_rate);
assert!(info.default_rate <= info.max_rate);
assert!(info.latency_lo > 0);
assert!(info.latency_hi > 0);
assert!(info.latency_lo <= info.latency_hi);
} else {
assert_eq!(
audiounit_create_device_from_hwdev(
&mut info,
input_id,
DeviceType::OUTPUT,
).unwrap_err(),
Error::error()
);
}
}
#[test]
fn test_create_device_from_hwdev_output() {
let output_id = audiounit_get_default_device_id(DeviceType::OUTPUT);
if !valid_id(output_id) {
return;
}
let mut info = ffi::cubeb_device_info::default();
// unknown
assert_eq!(
audiounit_create_device_from_hwdev(
&mut info,
output_id,
DeviceType::UNKNOWN,
).unwrap_err(),
Error::error()
);
// input
info = ffi::cubeb_device_info::default();
if is_input(output_id) {
assert!(
audiounit_create_device_from_hwdev(
&mut info,
output_id,
DeviceType::INPUT,
).is_ok()
);
assert_ne!(info.devid, ptr::null_mut());
assert_ne!(info.device_id, ptr::null_mut());
assert_eq!(info.group_id, info.device_id);
assert_ne!(info.friendly_name, ptr::null_mut());
assert_ne!(info.vendor_name, ptr::null_mut());
assert_eq!(info.device_type, ffi::CUBEB_DEVICE_TYPE_INPUT);
assert_eq!(info.state, ffi::CUBEB_DEVICE_STATE_ENABLED);
assert_eq!(info.preferred, ffi::CUBEB_DEVICE_PREF_ALL);
assert!(info.max_channels > 0);
assert_eq!(info.default_format, ffi::CUBEB_DEVICE_FMT_F32NE);
assert!(info.min_rate <= info.max_rate);
assert!(info.min_rate <= info.default_rate);
assert!(info.default_rate <= info.max_rate);
assert!(info.latency_lo > 0);
assert!(info.latency_hi > 0);
assert!(info.latency_lo <= info.latency_hi);
} else {
assert_eq!(
audiounit_create_device_from_hwdev(
&mut info,
output_id,
DeviceType::INPUT,
).unwrap_err(),
Error::error()
);
}
// output
info = ffi::cubeb_device_info::default();
assert!(
audiounit_create_device_from_hwdev(
&mut info,
output_id,
DeviceType::OUTPUT,
).is_ok()
);
assert_ne!(info.devid, ptr::null_mut());
assert_ne!(info.device_id, ptr::null_mut());
assert_eq!(info.group_id, info.device_id);
assert_ne!(info.friendly_name, ptr::null_mut());
assert_ne!(info.vendor_name, ptr::null_mut());
assert_eq!(info.device_type, ffi::CUBEB_DEVICE_TYPE_OUTPUT);
assert_eq!(info.state, ffi::CUBEB_DEVICE_STATE_ENABLED);
assert_eq!(info.preferred, ffi::CUBEB_DEVICE_PREF_ALL);
assert!(info.max_channels > 0);
assert_eq!(info.default_format, ffi::CUBEB_DEVICE_FMT_F32NE);
assert!(info.min_rate <= info.max_rate);
assert!(info.min_rate <= info.default_rate);
assert!(info.default_rate <= info.max_rate);
assert!(info.latency_lo > 0);
assert!(info.latency_hi > 0);
assert!(info.latency_lo <= info.latency_hi);
}
// is_aggregate_device
// ------------------------------------
// TODO
// get_devices_of_type
// ------------------------------------
#[test]
@ -276,12 +891,16 @@ fn test_get_devices_of_type() {
}
}
// audiounit_create_device_from_hwdev
// ------------------------------------
// ...
// Utils
// ------------------------------------
fn valid_id(id: AudioObjectID) -> bool {
id != kAudioObjectUnknown
}
}
fn is_input(id: AudioObjectID) -> bool {
audiounit_get_channel_count(id, kAudioDevicePropertyScopeInput) > 0
}
fn is_output(id: AudioObjectID) -> bool {
audiounit_get_channel_count(id, kAudioDevicePropertyScopeOutput) > 0
}

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

@ -17,7 +17,6 @@ pub fn allocate_array<T>(elements: usize) -> Vec<T> {
array
}
pub fn get_leaked_vec<T>(mut v: Vec<T>) -> (*mut T, usize) {
v.shrink_to_fit(); // Make sure the capacity is same as the length.
let ptr_and_len = (v.as_mut_ptr(), v.len());