#![allow(unsafe_code)]
use std::{ffi::c_void};
use objc2_core_audio_types::{AudioBufferList, AudioTimeStamp};
use objc2_core_audio::{AudioDeviceCreateIOProcID, AudioDeviceDestroyIOProcID, AudioDeviceID, AudioDeviceIOProcID, AudioDeviceStart, AudioDeviceStop};
use crate::Time;
use crate::errors::OSStatus;
use crate::{Scope, errors::{CoreAudioError, ErrorKind, OSStatusCheck}, object::{AudioObject, Device}};
struct ClientCallbackData<F>
where
F: FnMut(&mut [AudioBuffer]) + Send + 'static,
{
callback: F,
scope: Scope,
buffers: Vec<AudioBuffer<'static>>,
}
const RESERVED_BUFFERS: usize = 256;
#[derive(Debug)]
pub struct AudioBuffer<'a> {
pub data: &'a mut [f32],
channels: u32,
frame_count: u32,
now_time: Time,
input_time: Time,
output_time: Time,
}
impl<'a> AudioBuffer<'a> {
pub fn get_channels(&self) -> u32 {
self.channels
}
pub fn get_frame_count(&self) -> u32 {
self.frame_count
}
pub fn get_now_time(&self) -> Time {
self.now_time
}
pub fn get_input_time(&self) -> Time {
self.input_time
}
pub fn get_output_time(&self) -> Time {
self.output_time
}
}
pub struct IOProc {
id: AudioDeviceID,
proc_id: AudioDeviceIOProcID,
is_running: bool,
client_data: *mut c_void,
free_client_data: unsafe fn(*mut c_void),
}
unsafe impl Send for IOProc {}
unsafe impl Sync for IOProc {}
impl Drop for IOProc {
fn drop(&mut self) {
unsafe {
AudioDeviceStop(self.id, self.proc_id);
AudioDeviceDestroyIOProcID(
self.id,
self.proc_id,
);
(self.free_client_data)(self.client_data);
}
}
}
unsafe fn free_client_data<F>(data: *mut c_void)
where
F: FnMut(&mut [AudioBuffer]) + Send + 'static,
{
unsafe { drop(Box::from_raw(data as *mut ClientCallbackData<F>)) };
}
impl IOProc {
pub(crate) fn try_new<F>(
device: &AudioObject<Device>,
scope: Scope,
callback: F,
) -> Result<Self, CoreAudioError>
where
F: FnMut(&mut [AudioBuffer]) + Send + 'static,
{
let client_data = ClientCallbackData {
callback,
scope,
buffers: Vec::with_capacity(RESERVED_BUFFERS),
};
let data_ptr = Box::into_raw(Box::new(client_data)) as *mut c_void;
let mut proc_id: AudioDeviceIOProcID = None;
let created = unsafe {
AudioDeviceCreateIOProcID(
device.id(),
Some(std::mem::transmute::<RawIOProc, _>(io_callback::<F>)),
data_ptr,
(&mut proc_id).into(),
).check()
};
if let Err(error) = created {
unsafe { free_client_data::<F>(data_ptr) };
return Err(error);
}
let proc = Self {
id: device.id(),
proc_id,
is_running: false,
client_data: data_ptr,
free_client_data: free_client_data::<F>,
};
unsafe { AudioDeviceStop(device.id(), proc_id).check()? };
Ok(proc)
}
pub fn play(&mut self) -> Result<(), CoreAudioError> {
if self.is_running {
return Err(CoreAudioError::from_error_kind(ErrorKind::AlreadyRunning));
}
unsafe {
AudioDeviceStart(self.id, self.proc_id).check()?;
}
self.is_running = true;
Ok(())
}
pub fn pause(&mut self) -> Result<(), CoreAudioError> {
if !self.is_running {
return Err(CoreAudioError::from_error_kind(ErrorKind::AlreadyPaused));
}
unsafe {
AudioDeviceStop(self.id, self.proc_id).check()?;
}
self.is_running = false;
Ok(())
}
pub fn remove(self) {
drop(self);
}
}
type RawIOProc = extern "C-unwind" fn(AudioDeviceID, *const AudioTimeStamp, *const AudioBufferList, *const AudioTimeStamp, *mut AudioBufferList, *const AudioTimeStamp, *mut c_void) -> OSStatus;
unsafe fn read_time(time: *const AudioTimeStamp) -> Time {
if time.is_null() {
Time::default()
} else {
unsafe { time.read() }.into()
}
}
extern "C-unwind" fn io_callback<F>(
_device: AudioDeviceID,
now: *const AudioTimeStamp,
input: *const AudioBufferList,
input_time: *const AudioTimeStamp,
output: *mut AudioBufferList,
output_time: *const AudioTimeStamp,
client_data: *mut c_void,
) -> OSStatus
where
F: FnMut(&mut [AudioBuffer]) + Send + 'static,
{
unsafe {
let client_data = &mut *(client_data as *mut ClientCallbackData<F>);
let list = match client_data.scope {
Scope::Input => input as *mut AudioBufferList,
Scope::Output => output,
};
let buffers: &mut [objc2_core_audio_types::AudioBuffer] = if list.is_null() {
&mut []
} else {
std::slice::from_raw_parts_mut(
(*list).mBuffers.as_mut_ptr(),
(*list).mNumberBuffers as usize,
)
};
client_data.buffers.clear();
client_data.buffers.extend(buffers.iter_mut().map(|buf| {
let empty = buf.mData.is_null() || buf.mNumberChannels == 0;
let (data, frame_count) = if empty {
(
std::slice::from_raw_parts_mut(
std::ptr::NonNull::<f32>::dangling().as_ptr(),
0,
),
0
)
} else {
(
std::slice::from_raw_parts_mut(
buf.mData as *mut f32,
buf.mDataByteSize as usize / size_of::<f32>(),
),
buf.mDataByteSize / (buf.mNumberChannels * size_of::<f32>() as u32)
)
};
AudioBuffer {
data,
channels: buf.mNumberChannels,
frame_count,
now_time: read_time(now),
input_time: read_time(input_time),
output_time: read_time(output_time),
}
}));
(client_data.callback)(&mut client_data.buffers);
client_data.buffers.clear();
}
0
}