#![allow(unsafe_code)]
use crate::{errors::{CoreAudioError, OSStatusCheck}, object::{AudioObject, Device}};
use core_foundation::{
array::CFArray,
base::TCFType,
boolean::CFBoolean,
dictionary::CFDictionary,
string::CFString,
};
use coreaudio_sys::{
AudioHardwareCreateAggregateDevice, AudioHardwareDestroyAggregateDevice, AudioObjectID,
kAudioAggregateDeviceIsPrivateKey, kAudioAggregateDeviceIsStackedKey,
kAudioAggregateDeviceNameKey, kAudioAggregateDeviceTapAutoStartKey,
kAudioAggregateDeviceTapListKey, kAudioAggregateDeviceUIDKey,
kAudioSubTapDriftCompensationKey, kAudioSubTapUIDKey,
};
pub struct AggregateDevice {
device: AudioObject<Device>,
}
impl AggregateDevice {
pub fn with_tap(name: &str, uid: &str, tap_uid: &str) -> Result<Self, CoreAudioError> {
let tap = CFDictionary::from_CFType_pairs(&[
(key(kAudioSubTapUIDKey), CFString::new(tap_uid).as_CFType()),
(key(kAudioSubTapDriftCompensationKey), CFBoolean::true_value().as_CFType()),
]);
let taps = CFArray::from_CFTypes(&[tap]);
let description = CFDictionary::from_CFType_pairs(&[
(key(kAudioAggregateDeviceNameKey), CFString::new(name).as_CFType()),
(key(kAudioAggregateDeviceUIDKey), CFString::new(uid).as_CFType()),
(key(kAudioAggregateDeviceIsPrivateKey), CFBoolean::true_value().as_CFType()),
(key(kAudioAggregateDeviceIsStackedKey), CFBoolean::false_value().as_CFType()),
(key(kAudioAggregateDeviceTapAutoStartKey), CFBoolean::true_value().as_CFType()),
(key(kAudioAggregateDeviceTapListKey), taps.as_CFType()),
]);
let mut id: AudioObjectID = 0;
unsafe {
AudioHardwareCreateAggregateDevice(
description.as_concrete_TypeRef() as *const _,
&mut id,
).check()?;
}
Ok(Self { device: AudioObject::<Device>::from(id) })
}
pub fn device(&self) -> AudioObject<Device> {
self.device
}
}
impl Drop for AggregateDevice {
fn drop(&mut self) {
unsafe {
AudioHardwareDestroyAggregateDevice(self.device.id());
}
}
}
fn key(bytes: &[u8]) -> CFString {
let text = std::str::from_utf8(bytes.strip_suffix(&[0]).unwrap_or(bytes)).unwrap_or_default();
CFString::new(text)
}