#![allow(unsafe_code)]
use crate::{data_types::{PropertySize, Scope}, errors::{CoreAudioError, OSStatusCheck}, io_proc::{AudioBuffer, IOProc}, listener::{CallbackListener, PropertyListener}, property::{DEVICE_INPUT_STREAMS, DEVICE_OUTPUT_STREAMS, Property, SYSTEM_DEFAULT_INPUT, SYSTEM_DEFAULT_OUTPUT, SYSTEM_DEVICES, SYSTEM_PROCESS_OBJECT_LIST}, traits::{CanListen, HasAllData, ObjectCompatibleWith, Writeable}};
use std::{ffi::c_void, marker::PhantomData, ptr::null};
use coreaudio_sys::{AudioObjectGetPropertyData, AudioObjectGetPropertyDataSize, AudioObjectID, AudioObjectSetPropertyData, kAudioObjectSystemObject};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Global;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct System;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Device;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Stream;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Process;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Tap;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct AudioObject<T> {
id: AudioObjectID,
_marker: PhantomData<T>,
}
impl Default for AudioObject<System> {
fn default() -> Self {
Self {
id: kAudioObjectSystemObject,
_marker: PhantomData,
}
}
}
impl From<u32> for AudioObject<Device> {
fn from(value: u32) -> Self {
Self {
id: value,
_marker: PhantomData,
}
}
}
impl From<u32> for AudioObject<Stream> {
fn from(value: u32) -> Self {
Self {
id: value,
_marker: PhantomData,
}
}
}
impl From<u32> for AudioObject<Process> {
fn from(value: u32) -> Self {
Self {
id: value,
_marker: PhantomData,
}
}
}
impl From<u32> for AudioObject<Tap> {
fn from(value: u32) -> Self {
Self {
id: value,
_marker: PhantomData,
}
}
}
impl<C> AudioObject<C> {
pub fn id(&self) -> u32 {
self.id
}
pub fn get_property<V, D, A, L, E>(
&self,
property: Property<V, D, A, L, E>,
) -> Result<V, CoreAudioError>
where
D: ObjectCompatibleWith<C>,
E: HasAllData,
{
get_property_internal(self.id, property)
}
pub fn get_property_data_size<V, D, A, L, E>(
&self,
property: Property<V, D, A, L, E>,
) -> Result<u32, CoreAudioError>
where
D: ObjectCompatibleWith<C>,
E: HasAllData,
{
get_property_data_size_internal(self.id, &property)
}
pub fn set_property<V, D, A, L, E>(
&self,
property: Property<V, D, A, L, E>,
value: V,
) -> Result<(), CoreAudioError>
where
D: ObjectCompatibleWith<C>,
A: Writeable,
E: HasAllData,
{
set_property_internal(self.id, property, value)
}
pub fn add_listener<V, D, A, L, E>(
&self,
property: Property<V, D, A, L, E>,
) -> Result<PropertyListener<V, D, A>, CoreAudioError>
where
D: ObjectCompatibleWith<C>,
L: CanListen,
E: HasAllData,
{
PropertyListener::try_new(self.id, property.size, property.address, property.read)
}
pub fn add_listener_with<V, D, A, L, E, S>(
&self,
property: Property<V, D, A, L, E>,
sink: S,
) -> Result<CallbackListener<V, D, A>, CoreAudioError>
where
D: ObjectCompatibleWith<C>,
L: CanListen,
E: HasAllData,
S: Fn(Result<V, CoreAudioError>) + Send + Sync + 'static,
{
CallbackListener::try_new(self.id, property.size, property.address, property.read, sink)
}
pub unsafe fn force_set_property<V, D, A, L, E>(
&self,
property: Property<V, D, A, L, E>,
value: V,
) -> Result<(), CoreAudioError> {
set_property_internal(self.id, property, value)
}
pub unsafe fn force_add_listener<V, D, A, L, E>(
&self,
property: Property<V, D, A, L, E>,
) -> Result<PropertyListener<V, D, A>, CoreAudioError> {
PropertyListener::try_new(self.id, property.size, property.address, property.read)
}
pub unsafe fn force_add_listener_with<V, D, A, L, E, S>(
&self,
property: Property<V, D, A, L, E>,
sink: S,
) -> Result<CallbackListener<V, D, A>, CoreAudioError>
where
S: Fn(Result<V, CoreAudioError>) + Send + Sync + 'static,
{
CallbackListener::try_new(self.id, property.size, property.address, property.read, sink)
}
}
impl AudioObject<System> {
pub fn devices(&self) ->
Result<Vec<AudioObject<Device>>, CoreAudioError> {
Ok(
get_property_internal(self.id, SYSTEM_DEVICES)?
.iter()
.map(|id| {
AudioObject::<Device>::from(*id)
}).collect()
)
}
pub fn devices_with_scope(&self, scope: Scope) ->
Result<Vec<AudioObject<Device>>, CoreAudioError> {
let all_devices = self.devices()?;
Ok(all_devices.into_iter().filter(|device| {
let streams = match scope {
Scope::Input => device.get_property(DEVICE_INPUT_STREAMS),
Scope::Output => device.get_property(DEVICE_OUTPUT_STREAMS),
};
streams.map(|s: Vec<AudioObjectID>| !s.is_empty()).unwrap_or(false)
}).collect())
}
pub fn processes(&self) ->
Result<Vec<AudioObject<Process>>, CoreAudioError> {
Ok(
get_property_internal(self.id, SYSTEM_PROCESS_OBJECT_LIST)?
.iter()
.map(|id| AudioObject::<Process>::from(*id))
.collect()
)
}
pub fn current_device(&self, scope: Scope) ->
Result<AudioObject<Device>, CoreAudioError> {
let id = match scope {
Scope::Input => self.get_property(SYSTEM_DEFAULT_INPUT)?,
Scope::Output => self.get_property(SYSTEM_DEFAULT_OUTPUT)?,
};
Ok(AudioObject::<Device>::from(id))
}
}
impl AudioObject<Device> {
pub fn streams(&self) ->
Result<Vec<AudioObject<Stream>>, CoreAudioError> {
let mut streams = self.get_property(DEVICE_INPUT_STREAMS)?;
let output_streams = self.get_property(DEVICE_OUTPUT_STREAMS)?;
output_streams.iter().for_each(|stream| {
streams.push(*stream);
});
Ok(
streams.iter().map(|stream| {
AudioObject::<Stream>::from(*stream)
}).collect()
)
}
pub fn streams_with_scope(&self, scope: Scope) ->
Result<Vec<AudioObject<Stream>>, CoreAudioError> {
let streams = match scope {
Scope::Input => self.get_property(DEVICE_INPUT_STREAMS)?,
Scope::Output => self.get_property(DEVICE_OUTPUT_STREAMS)?,
};
Ok(
streams.iter().map(|stream| {
AudioObject::<Stream>::from(*stream)
}).collect()
)
}
pub fn add_io_proc<F>(
&self,
scope: Scope,
callback: F,
) -> Result<IOProc, CoreAudioError>
where
F: FnMut(&mut [AudioBuffer]) + Send + 'static,
{
IOProc::try_new(&self, scope, callback)
}
}
impl AudioObject<Stream> {}
impl AudioObject<Process> {}
impl AudioObject<Tap> {}
pub(crate) fn get_property_internal<V, D, A, L, E>(
id: AudioObjectID,
property: Property<V, D, A, L, E>,
) -> Result<V, CoreAudioError> {
let (q_len, q_data) = get_q_data_len(&property);
let mut size = get_property_data_size_internal(id, &property)?;
unsafe {
let mut buffer = vec![0u8; size as usize];
AudioObjectGetPropertyData(
id,
&property.address,
q_len,
q_data,
&mut size,
buffer.as_mut_ptr() as *mut c_void,
).check()?;
(property.read)(&buffer)
}
}
pub(crate) fn set_property_internal<V, D, A, L, E>(
id: AudioObjectID,
property: Property<V, D, A, L, E>,
value: V,
) -> Result<(), CoreAudioError> {
unsafe {
let out_data = (property.encode)(value);
let (q_len, q_data) = get_q_data_len(&property);
let size = out_data.bytes.len() as u32;
AudioObjectSetPropertyData(
id,
&property.address,
q_len,
q_data,
size,
out_data.bytes.as_ptr() as *const c_void,
).check()?;
Ok(())
}
}
fn get_q_data_len<V, D, A, L, E>(
property: &Property<V, D, A, L, E>,
) -> (u32, *const c_void) {
property.qualifier
.as_ref()
.map_or(
(0, null()),
|q| {
(q.bytes.len() as u32, q.bytes.as_ptr() as *const c_void)
}
)
}
fn get_property_data_size_internal<V, D, A, L, E>(
id: AudioObjectID,
property: &Property<V, D, A, L, E>,
) -> Result<u32, CoreAudioError> {
let (q_len, q_data) = get_q_data_len(property);
match property.size {
PropertySize::Known(size) => Ok(size),
PropertySize::Unknown => {
unsafe {
let mut size = 0u32;
AudioObjectGetPropertyDataSize(
id,
&property.address,
q_len,
q_data,
&mut size,
).check()?;
Ok(size)
}
}
}
}