#![allow(unsafe_code)]
use crate::{errors::{CoreAudioError, OSStatusCheck}, object::{AudioObject, Device}};
use std::ffi::CStr;
use core_foundation::{
array::CFArray, base::{CFType, TCFType}, boolean::CFBoolean, dictionary::CFDictionary, string::CFString,
};
use objc2_core_audio::{
AudioHardwareCreateAggregateDevice, AudioHardwareDestroyAggregateDevice, AudioObjectID,
kAudioAggregateDeviceIsPrivateKey, kAudioAggregateDeviceIsStackedKey,
kAudioAggregateDeviceNameKey, kAudioAggregateDeviceTapAutoStartKey,
kAudioAggregateDeviceTapListKey, kAudioAggregateDeviceUIDKey,
kAudioSubTapDriftCompensationKey, kAudioSubTapUIDKey,
};
pub struct AppTapAggregateDevice {
device: AudioObject<Device>,
}
impl AppTapAggregateDevice {
pub fn with_tap(name: &str, uid: &str, tap_uid: &str) -> Result<Self, CoreAudioError> {
Self::new_tap_internal(name, uid, tap_uid, true)
}
pub fn with_tap_public(name: &str, uid: &str, tap_uid: &str) -> Result<Self, CoreAudioError> {
Self::new_tap_internal(name, uid, tap_uid, false)
}
pub fn device(&self) -> AudioObject<Device> {
self.device
}
fn new_tap_internal(
name: &str,
uid: &str,
tap_uid: &str,
private: bool,
) -> 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 = tap_description(
name,
uid,
taps,
private,
);
let mut id: AudioObjectID = 0;
unsafe {
AudioHardwareCreateAggregateDevice(
&*description.as_concrete_TypeRef().cast(),
(&mut id).into(),
).check()?;
}
Ok(Self { device: AudioObject::<Device>::from(id) })
}
}
impl Drop for AppTapAggregateDevice {
fn drop(&mut self) {
unsafe {
AudioHardwareDestroyAggregateDevice(self.device.id());
}
}
}
fn key(key: &CStr) -> CFString {
CFString::new(&key.to_string_lossy())
}
fn tap_description(
name: &str,
uid: &str,
taps: CFArray<CFDictionary<CFString, CFType>>,
private: bool,
) -> CFDictionary<CFString, CFType> {
let private_key = if private {
CFBoolean::true_value().as_CFType()
} else {
CFBoolean::false_value().as_CFType()
};
CFDictionary::from_CFType_pairs(&[
(key(kAudioAggregateDeviceNameKey), CFString::new(name).as_CFType()),
(key(kAudioAggregateDeviceUIDKey), CFString::new(uid).as_CFType()),
(key(kAudioAggregateDeviceIsPrivateKey), private_key),
(key(kAudioAggregateDeviceIsStackedKey), CFBoolean::false_value().as_CFType()),
(key(kAudioAggregateDeviceTapAutoStartKey), CFBoolean::true_value().as_CFType()),
(key(kAudioAggregateDeviceTapListKey), taps.as_CFType()),
])
}