use super::{JackNFrames, JackPort, JackStatus, JackConnection, Deactivated};
use jack_sys::*;
use std::panic::{AssertUnwindSafe, catch_unwind};
use std::sync::atomic::{AtomicPtr, Ordering};
use std::ffi::CStr;
use libc;
use errors::*;
pub trait JackLoggingHandler: Send {
fn on_error(&mut self, msg: &str);
fn on_info(&mut self, msg: &str);
}
lazy_static! {
static ref LOGGING_HANDLER: AtomicPtr<*mut JackLoggingHandler> = AtomicPtr::new(::std::ptr::null_mut());
}
unsafe extern "C" fn error_callback(msg: *const libc::c_char) {
let handler = LOGGING_HANDLER.load(Ordering::Relaxed);
if !handler.is_null() {
let f = &mut (**handler);
let msg = CStr::from_ptr(msg);
let _ = catch_unwind(AssertUnwindSafe(|| {
f.on_error(&msg.to_string_lossy());
}));
}
}
unsafe extern "C" fn info_callback(msg: *const libc::c_char) {
let handler = LOGGING_HANDLER.load(Ordering::Relaxed);
if !handler.is_null() {
let f = &mut (**handler);
let msg = CStr::from_ptr(msg);
let _ = catch_unwind(AssertUnwindSafe(|| {
f.on_info(&msg.to_string_lossy());
}));
}
}
pub fn set_logging_handler<F>(handler: F) where F: JackLoggingHandler + 'static {
unsafe {
let trait_object_ptr = Box::into_raw(Box::new(handler) as Box<JackLoggingHandler>);
let ptr_ception = Box::into_raw(Box::new(trait_object_ptr));
LOGGING_HANDLER.store(ptr_ception, Ordering::Relaxed);
jack_set_error_function(Some(error_callback));
jack_set_info_function(Some(info_callback));
}
}
pub struct JackCallbackContext {
nframes: JackNFrames
}
impl JackCallbackContext {
#[inline(always)]
pub fn nframes(&self) -> u32 {
self.nframes
}
pub fn get_port_buffer(&self, port: &JackPort) -> Option<&mut [f32]> {
unsafe {
let buf = jack_port_get_buffer(port.as_ptr(), self.nframes);
if buf.is_null() {
None
}
else {
Some(::std::slice::from_raw_parts_mut(buf as *mut f32, self.nframes as usize))
}
}
}
}
#[derive(Copy, Clone, Debug)]
pub enum JackControl {
Continue = 0,
Stop = -1
}
pub trait JackHandler: Send {
fn process(&mut self, _ctx: &JackCallbackContext) -> JackControl {
JackControl::Stop
}
fn buffer_size(&mut self, _new_size: JackNFrames) -> JackControl { JackControl::Continue }
fn sample_rate(&mut self, _new_rate: JackNFrames) -> JackControl { JackControl::Continue }
fn thread_init(&mut self) { }
fn shutdown(&mut self, _status: JackStatus, _reason: &str) { }
fn client_registered(&mut self, _name: &str, _registered: bool) { }
fn xrun(&mut self) -> JackControl { JackControl::Continue }
}
impl<F> JackHandler for F where F: FnMut(&JackCallbackContext) -> JackControl + Send + 'static {
fn process(&mut self, ctx: &JackCallbackContext) -> JackControl {
self(ctx)
}
}
unsafe extern "C" fn buffer_size_callback<T>(frames: JackNFrames, user: *mut libc::c_void) -> libc::c_int where T: JackHandler {
let callbacks = &mut *(user as *mut T);
catch_unwind(AssertUnwindSafe(|| {
callbacks.buffer_size(frames) as _
})).unwrap_or(-1)
}
unsafe extern "C" fn sample_rate_callback<T>(frames: JackNFrames, user: *mut libc::c_void) -> libc::c_int where T: JackHandler {
let callbacks = &mut *(user as *mut T);
catch_unwind(AssertUnwindSafe(|| {
callbacks.sample_rate(frames) as _
})).unwrap_or(-1)
}
unsafe extern "C" fn client_registration_callback<T>(name: *const libc::c_char, register: libc::c_int, user: *mut libc::c_void) where T: JackHandler {
let callbacks = &mut *(user as *mut T);
let name = CStr::from_ptr(name);
let _ = catch_unwind(AssertUnwindSafe(|| {
callbacks.client_registered(&name.to_string_lossy(), register != 0)
}));
}
unsafe extern "C" fn info_shutdown_callback<T>(code: jack_status_t, reason: *const libc::c_char, user: *mut libc::c_void) where T: JackHandler {
let callbacks = &mut *(user as *mut T);
let code = JackStatus::from_bits_truncate(code);
let reason = CStr::from_ptr(reason);
let _ = catch_unwind(AssertUnwindSafe(|| {
callbacks.shutdown(code, &reason.to_string_lossy())
}));
}
unsafe extern "C" fn thread_init_callback<T>(user: *mut libc::c_void) where T: JackHandler {
let callbacks = &mut *(user as *mut T);
let _ = catch_unwind(AssertUnwindSafe(|| {
callbacks.thread_init()
}));
}
unsafe extern "C" fn process_callback<T>(nframes: JackNFrames, user: *mut libc::c_void) -> libc::c_int where T: JackHandler {
let callbacks = &mut *(user as *mut T);
let ctx = JackCallbackContext {
nframes: nframes
};
catch_unwind(AssertUnwindSafe(|| {
callbacks.process(&ctx) as _
})).unwrap_or(-1)
}
unsafe extern "C" fn xrun_callback<T>(user: *mut libc::c_void) -> libc::c_int where T: JackHandler {
let callbacks = &mut *(user as *mut T);
catch_unwind(AssertUnwindSafe(|| {
callbacks.xrun() as _
})).unwrap_or(-1)
}
pub fn set_handler<F>(conn: &mut JackConnection<Deactivated>, handler: F) -> JackResult<()> where F: JackHandler {
let user_ptr = Box::into_raw(Box::new(handler));
let user_ptr = user_ptr as *mut libc::c_void;
unsafe {
let code = jack_set_process_callback(conn.handle, Some(process_callback::<F>), user_ptr);
if code != 0 { Err(JackError::UnknownErrorCode { from: "set_process_callback() - process", code: code })? }
let code = jack_set_thread_init_callback(conn.handle, Some(thread_init_callback::<F>), user_ptr);
if code != 0 { Err(JackError::UnknownErrorCode { from: "set_process_callback() - thread_init", code: code })? }
let code = jack_set_buffer_size_callback(conn.handle, Some(buffer_size_callback::<F>), user_ptr);
if code != 0 { Err(JackError::UnknownErrorCode { from: "set_process_callback() - buffer_size", code: code })? }
let code = jack_set_sample_rate_callback(conn.handle, Some(sample_rate_callback::<F>), user_ptr);
if code != 0 { Err(JackError::UnknownErrorCode { from: "set_process_callback() - sample_rate", code: code })? }
let code = jack_set_xrun_callback(conn.handle, Some(xrun_callback::<F>), user_ptr);
if code != 0 { Err(JackError::UnknownErrorCode { from: "set_process_callback() - xrun", code: code })? }
let code = jack_set_client_registration_callback(conn.handle, Some(client_registration_callback::<F>), user_ptr);
if code != 0 { Err(JackError::UnknownErrorCode { from: "set_process_callback() - client_registration", code: code })? }
jack_on_info_shutdown(conn.handle, Some(info_shutdown_callback::<F>), user_ptr);
}
Ok(())
}