use std::sync::{
atomic::{AtomicU64, Ordering},
Arc,
};
use maudio_sys::ffi as sys;
#[derive(Default)]
pub struct ProcessState {
frames_read: AtomicU64,
cb: Option<Box<EngineProcessCallback>>,
}
#[derive(Clone)]
pub struct ProcessNotifier {
channels: u32,
old_frames: u64,
state: Arc<ProcessState>,
}
impl ProcessNotifier {
pub(crate) fn new(channels: u32, cb: Option<Box<EngineProcessCallback>>) -> Self {
let state: ProcessState = ProcessState {
frames_read: AtomicU64::new(0),
cb,
};
Self {
channels,
old_frames: 0,
#[allow(clippy::arc_with_non_send_sync)]
state: Arc::new(state),
}
}
#[inline]
pub fn peek(&self) -> bool {
self.old_frames < self.state.frames_read.load(Ordering::Relaxed)
}
#[inline]
pub fn clear(&mut self) {
self.state.frames_read.store(0, Ordering::Relaxed);
self.old_frames = 0;
}
#[inline]
pub fn take_delta(&mut self) -> u64 {
let cur = self.state.frames_read.load(Ordering::Relaxed);
let delta = cur.saturating_sub(self.old_frames);
self.old_frames = cur;
delta
}
#[inline]
pub fn call_if_triggered<F: FnOnce(u64)>(&mut self, f: F) {
let delta = self.take_delta();
if delta != 0 {
f(delta);
}
}
pub(crate) fn clone_flag(&self) -> Arc<ProcessState> {
self.state.clone()
}
pub(crate) fn as_user_data_ptr(&self) -> *mut core::ffi::c_void {
std::sync::Arc::as_ptr(&self.state) as *mut core::ffi::c_void
}
}
#[derive(Debug, Copy, Clone)]
pub struct EngineProcessProc {
old_frame_count: u64,
frame_count: u64,
pub channels: u32,
}
pub type EngineProcessCallback = dyn FnMut(&mut [f32]) + Send + 'static;
pub(crate) unsafe extern "C" fn on_process_callback(
user_data: *mut core::ffi::c_void,
_frames_out: *mut f32,
frame_count: sys::ma_uint64,
) {
if user_data.is_null() {
return;
}
let ctx = unsafe { &*(user_data as *const ProcessState) };
ctx.frames_read.fetch_add(frame_count, Ordering::Relaxed);
}