#![allow(unsafe_code)]
use std::os::raw::c_void;
use std::sync::{Arc, Mutex};
use std::task::Waker;
use crate::consts::SAMPLE_RATE;
#[repr(C)]
struct AudioQueueBuffer {
audio_data_bytes_capacity: u32, audio_data: *mut i16,
audio_data_byte_size: u32,
user_data: *mut c_void,
packet_description_capacity: u32, packet_descriptions: *const c_void,
packet_description_count: u32,
}
#[repr(C)]
pub(crate) struct AudioStreamBasicDescription {
sample_rate: f64,
format_id: u32,
format_flags: u32,
bytes_per_packet: u32,
frames_per_packet: u32,
bytes_per_frame: u32,
channels_per_frame: u32,
bits_per_channel: u32,
reserved: u32,
}
impl AudioStreamBasicDescription {
pub(crate) const fn new(channels: u32) -> Self {
Self {
sample_rate: SAMPLE_RATE as f64,
format_id: u32::from_ne_bytes(*b"lpcm"),
format_flags: 1,
bytes_per_packet: 4 * channels,
frames_per_packet: 1,
bytes_per_frame: 4 * channels,
channels_per_frame: channels,
bits_per_channel: 4 * 8,
reserved: 0,
}
}
}
#[repr(transparent)]
#[derive(Copy, Clone)]
struct RunLoop(*mut c_void);
unsafe impl Send for RunLoop {}
unsafe impl Sync for RunLoop {}
type OSStatus = i32;
extern "C" {
fn CFRunLoopGetCurrent() -> RunLoop;
fn CFRunLoopRun();
fn AudioQueueNewOutput(
in_format: *const AudioStreamBasicDescription,
in_callback_proc: extern "C" fn(
*mut c_void,
*mut c_void,
*mut AudioQueueBuffer,
),
in_user_data: *mut c_void,
in_callback_run_loop: RunLoop,
in_callback_run_loop_mode: *const c_void,
in_flags: u32,
out_aq: *mut *mut c_void,
) -> OSStatus;
fn AudioQueueNewInput(
in_format: *const AudioStreamBasicDescription,
in_callback_proc: extern "C" fn(
*mut c_void,
*mut c_void,
*mut AudioQueueBuffer,
*const c_void,
u32,
*const c_void,
),
in_user_data: *mut c_void,
in_callback_run_loop: RunLoop,
in_callback_run_loop_mode: *const c_void,
in_flags: u32,
out_aq: *mut *mut c_void,
) -> OSStatus;
fn AudioQueueEnqueueBuffer(
in_audio_queue: *mut c_void,
in_buffer: *mut AudioQueueBuffer,
in_num_packet_descs: u32,
in_packet_descs: *const c_void,
) -> OSStatus;
fn AudioQueueStart(
in_audio_queue: *mut c_void,
in_start_timestamp: *const c_void,
) -> OSStatus;
fn AudioQueueAllocateBuffer(
in_audio_queue: *mut c_void,
in_buffer_byte_size: u32,
out_buffer: *mut *mut AudioQueueBuffer,
) -> OSStatus;
}
thread_local!(static RUN_LOOP: RunLoop = initialize());
fn initialize() -> RunLoop {
let pair = std::sync::Arc::new((
std::sync::Mutex::new(None),
std::sync::Condvar::new(),
));
let pair2 = Arc::clone(&pair);
std::thread::spawn(move || {
let (lock, cvar) = &*pair2;
let mut started = lock.lock().unwrap();
let run_loop = unsafe { CFRunLoopGetCurrent() };
*started = Some(run_loop);
cvar.notify_one();
unsafe { CFRunLoopRun() }
});
let (lock, cvar) = &*pair;
let mut started = lock.lock().unwrap();
let run_loop = loop {
if let Some(run_loop) = started.take() {
break run_loop;
}
started = cvar.wait(started).unwrap();
};
run_loop
}
struct SpeakerContext {
buffer: Mutex<(Vec<f32>, Option<Waker>)>,
}
extern "C" fn speaker_callback(
user_data: *mut c_void,
audio_queue: *mut c_void,
audio_buffer: *mut AudioQueueBuffer,
) {
let user_data: *mut SpeakerContext = user_data.cast();
let buffer: *mut f32 = unsafe { (*audio_buffer).audio_data.cast() };
unsafe {
let mut locked = (*user_data).buffer.lock().unwrap();
let inbuf = &mut (*locked).0;
for i in 0..((*audio_buffer).audio_data_byte_size / 4) {
*buffer.offset(i as isize) =
inbuf.get(i as usize).cloned().unwrap_or(0.0);
}
inbuf.clear();
if let Some(waker) = locked.1.take() {
waker.wake();
};
}
let status = unsafe {
AudioQueueEnqueueBuffer(audio_queue, audio_buffer, 0, std::ptr::null())
};
if status != 0 {
panic!("Failed enqueue {:?}", status);
}
}
extern "C" fn microphone_callback(
user_data: *mut c_void,
queue: *mut c_void,
buffer: *mut c_void,
start_timestamp: *const c_void,
num_packet_descs: u32,
packet_descs: *const c_void,
) {
}
pub(crate) struct AudioQueue {
audio_queue: *mut c_void,
speaker_cx: *mut SpeakerContext,
}
impl Drop for AudioQueue {
fn drop(&mut self) {
todo!()
}
}
pub(crate) fn speaker(channels: u8) -> Result<AudioQueue, [u8; 4]> {
RUN_LOOP.with(|run_loop| {
let speaker_cx = Box::into_raw(Box::new(SpeakerContext {
buffer: Mutex::new((Vec::new(), None)),
}));
let basic_desc = AudioStreamBasicDescription::new(channels.into());
let mut audio_queue = std::mem::MaybeUninit::uninit();
let status = unsafe {
AudioQueueNewOutput(
&basic_desc,
speaker_callback,
speaker_cx.cast(),
*run_loop,
std::ptr::null(),
0,
audio_queue.as_mut_ptr(),
)
};
if status != 0 {
return Err(status.to_ne_bytes());
}
let audio_queue = unsafe { audio_queue.assume_init() };
let mut audio_buffer = std::mem::MaybeUninit::uninit();
let buf_byte_count =
basic_desc.bytes_per_frame * crate::consts::PERIOD as u32;
let status = unsafe {
AudioQueueAllocateBuffer(
audio_queue,
buf_byte_count,
audio_buffer.as_mut_ptr(),
)
};
if status != 0 {
panic!("Out of memory {}!", status);
}
let audio_buffer = unsafe { audio_buffer.assume_init() };
unsafe {
(*audio_buffer).audio_data_byte_size = buf_byte_count;
}
for i in 0..buf_byte_count {
unsafe {
let byte: *mut u8 = (*audio_buffer).audio_data.cast();
*byte.offset(i as _) = 0;
}
}
let status = unsafe {
AudioQueueEnqueueBuffer(
audio_queue,
audio_buffer,
0,
std::ptr::null(),
)
};
if status != 0 {
return Err(status.to_ne_bytes());
}
let status = unsafe { AudioQueueStart(audio_queue, std::ptr::null()) };
if status != 0 {
return Err(status.to_ne_bytes());
}
Ok(AudioQueue {
audio_queue,
speaker_cx,
})
})
}