use libc::{c_float, size_t};
use std::alloc::{self, Layout};
use std::cell::UnsafeCell;
use std::convert::TryInto;
use std::ffi::CString;
use std::marker::PhantomData;
use std::mem;
use std::ptr;
use std::slice;
use std::sync::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
use super::wren;
use crate::panic::{catch_panic, handle_wren_callback_panic};
use crate::unsafe_wrappers::audio as unsafe_audio;
use crate::unsafe_wrappers::wren as unsafe_wren;
use crate::Api;
pub use unsafe_audio::ChannelState;
pub(crate) struct InternalChannelData {
mix: fn(&unsafe_audio::ChannelRef, &mut [[f32; 2]], usize),
update: Option<fn(&unsafe_audio::ChannelRef, &unsafe_wren::VM)>,
mix_error: Mutex<Option<CString>>,
drop_fn: unsafe fn(*mut InternalChannelData),
layout: Layout,
}
#[repr(C)]
pub(crate) struct ChannelData<T: Send + Sync> {
internal_data: InternalChannelData,
user_data: RwLock<T>,
}
impl<T: Send + Sync> ChannelData<T> {
pub(crate) fn new(mix: ChannelMix<T>, update: ChannelUpdate<T>, user_data: T) -> Self {
Self {
internal_data: InternalChannelData {
mix: unsafe { mem::transmute(mix) },
update: unsafe { mem::transmute(update) },
mix_error: Mutex::new(None),
drop_fn: unsafe { mem::transmute::<unsafe fn(_), _>(ptr::drop_in_place::<Self>) },
layout: Layout::new::<Self>(),
},
user_data: RwLock::new(user_data),
}
}
}
#[inline]
fn get_internal_data(channel_ref: unsafe_audio::ChannelRef) -> *mut InternalChannelData {
(Api::audio().get_data)(channel_ref) as _
}
pub(crate) extern "C" fn mix(
channel_ref: unsafe_audio::ChannelRef,
buffer: *mut c_float,
requested_samples: size_t,
) {
let internal_data = unsafe { &mut *get_internal_data(channel_ref) };
let callback = internal_data.mix;
let error = catch_panic(|| {
let requested_samples = requested_samples.try_into().unwrap();
let buffer = buffer as *mut [c_float; 2];
let buffer = unsafe { slice::from_raw_parts_mut(buffer, requested_samples) };
callback(&channel_ref, buffer, requested_samples)
});
if let Err(error) = error {
internal_data.mix_error.lock().unwrap().replace(error);
}
}
#[inline]
fn handle_mix_error(vm: unsafe_wren::VM, mix_error: &Mutex<Option<CString>>) {
if let Some(panic_message) = mix_error.lock().unwrap().take() {
handle_wren_callback_panic(&wren::VM(vm), &panic_message);
};
}
pub(crate) extern "C" fn update(channel_ref: unsafe_audio::ChannelRef, vm: unsafe_wren::VM) {
let internal_data = unsafe { &mut *get_internal_data(channel_ref) };
handle_mix_error(vm, &internal_data.mix_error);
internal_data.update.map(|callback| {
let error = catch_panic(|| callback(&channel_ref, &vm));
if let Err(error) = error {
handle_wren_callback_panic(&wren::VM(vm), &error);
}
});
}
pub(crate) extern "C" fn finish(channel_ref: unsafe_audio::ChannelRef, vm: unsafe_wren::VM) {
let internal_data = get_internal_data(channel_ref);
handle_mix_error(vm, unsafe { &(*internal_data).mix_error });
let layout = unsafe { (*internal_data).layout };
let error = catch_panic(|| unsafe { ((*internal_data).drop_fn)(internal_data) });
if let Err(error) = error {
handle_wren_callback_panic(&wren::VM(vm), &error);
return;
}
unsafe {
alloc::dealloc(internal_data as _, layout);
}
}
pub(crate) extern "C" fn finish_no_drop(
channel_ref: unsafe_audio::ChannelRef,
vm: unsafe_wren::VM,
) {
let internal_data = get_internal_data(channel_ref);
handle_mix_error(vm, unsafe { &(*internal_data).mix_error });
unsafe {
alloc::dealloc(internal_data as _, (*internal_data).layout);
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct Channel<T: Send + Sync = ()>(
pub(crate) unsafe_audio::ChannelRef,
pub(crate) PhantomData<UnsafeCell<T>>,
);
unsafe impl Send for Channel {}
unsafe impl Sync for Channel {}
impl<T: Send + Sync> Channel<T> {
#[inline]
pub fn state(&self) -> ChannelState {
(Api::audio().get_state)(self.0)
}
#[inline]
pub fn set_state(&mut self, state: ChannelState) {
(Api::audio().set_state)(self.0, state)
}
#[inline]
pub fn stop(self) {}
#[inline]
fn user_data(&self) -> Option<&RwLock<T>> {
if let ChannelState::Stopped = self.state() {
return None;
}
let data = (Api::audio().get_data)(self.0) as *mut ChannelData<T>;
Some(unsafe { &(*data).user_data })
}
#[inline]
pub fn data(&self) -> Option<RwLockReadGuard<T>> {
Some(self.user_data()?.read().unwrap())
}
#[inline]
pub fn data_mut(&self) -> Option<RwLockWriteGuard<T>> {
Some(self.user_data()?.write().unwrap())
}
}
impl<T: Send + Sync> Drop for Channel<T> {
#[inline]
fn drop(&mut self) {
(Api::audio().stop)(self.0);
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct CallbackChannel<T: Send + Sync>(Channel<T>);
impl<T: Send + Sync> CallbackChannel<T> {
#[inline]
pub fn state(&self) -> ChannelState {
self.0.state()
}
#[inline]
pub fn set_state(&mut self, state: ChannelState) {
self.0.set_state(state)
}
#[inline]
pub fn stop(&mut self) {
self.set_state(ChannelState::Stopped);
}
#[inline]
fn user_data(&self) -> &RwLock<T> {
let data = (Api::audio().get_data)(self.0 .0) as *mut ChannelData<T>;
unsafe { &(*data).user_data }
}
#[inline]
pub fn data(&self) -> RwLockReadGuard<T> {
self.user_data().read().unwrap()
}
#[inline]
pub fn data_mut(&self) -> RwLockWriteGuard<T> {
self.user_data().write().unwrap()
}
}
pub type ChannelMix<T = ()> = fn(channel: &CallbackChannel<T>, buffer: &mut [[f32; 2]]);
pub type ChannelUpdate<T = ()> = fn(channel: &CallbackChannel<T>, vm: &wren::VM);