use jack_sys as j;
use std::ffi;
use crate::{Client, ClientStatus, Control, Error, Frames, LatencyType, PortId, ProcessScope};
pub trait NotificationHandler: Send {
fn thread_init(&self, _: &Client) {}
fn shutdown(&mut self, _status: ClientStatus, _reason: &str) {}
fn freewheel(&mut self, _: &Client, _is_freewheel_enabled: bool) {}
fn buffer_size(&mut self, _: &Client, _size: Frames) -> Control {
Control::Continue
}
fn sample_rate(&mut self, _: &Client, _srate: Frames) -> Control {
Control::Continue
}
fn client_registration(&mut self, _: &Client, _name: &str, _is_registered: bool) {}
fn port_registration(&mut self, _: &Client, _port_id: PortId, _is_registered: bool) {}
fn port_rename(
&mut self,
_: &Client,
_port_id: PortId,
_old_name: &str,
_new_name: &str,
) -> Control {
Control::Continue
}
fn ports_connected(
&mut self,
_: &Client,
_port_id_a: PortId,
_port_id_b: PortId,
_are_connected: bool,
) {
}
fn graph_reorder(&mut self, _: &Client) -> Control {
Control::Continue
}
fn xrun(&mut self, _: &Client) -> Control {
Control::Continue
}
fn latency(&mut self, _: &Client, _mode: LatencyType) {}
}
pub trait ProcessHandler: Send {
fn process(&mut self, _: &Client, _process_scope: &ProcessScope) -> Control;
}
unsafe extern "C" fn thread_init_callback<N, P>(data: *mut libc::c_void)
where
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + ProcessHandler,
{
let ctx = CallbackContext::<N, P>::from_raw(data);
ctx.notification.thread_init(&ctx.client)
}
unsafe extern "C" fn shutdown<N, P>(
code: j::jack_status_t,
reason: *const libc::c_char,
data: *mut libc::c_void,
) where
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + ProcessHandler,
{
let ctx = CallbackContext::<N, P>::from_raw(data);
let cstr = ffi::CStr::from_ptr(reason);
let reason_str = match cstr.to_str() {
Ok(s) => s,
Err(_) => "Failed to interpret error.",
};
ctx.notification.shutdown(
ClientStatus::from_bits(code).unwrap_or_else(ClientStatus::empty),
reason_str,
)
}
unsafe extern "C" fn process<N, P>(n_frames: Frames, data: *mut libc::c_void) -> libc::c_int
where
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + ProcessHandler,
{
let ctx = CallbackContext::<N, P>::from_raw(data);
let scope = ProcessScope::from_raw(n_frames, ctx.client.raw());
ctx.process.process(&ctx.client, &scope).to_ffi()
}
unsafe extern "C" fn freewheel<N, P>(starting: libc::c_int, data: *mut libc::c_void)
where
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + ProcessHandler,
{
let ctx = CallbackContext::<N, P>::from_raw(data);
let is_starting = match starting {
0 => false,
_ => true,
};
ctx.notification.freewheel(&ctx.client, is_starting)
}
unsafe extern "C" fn buffer_size<N, P>(n_frames: Frames, data: *mut libc::c_void) -> libc::c_int
where
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + ProcessHandler,
{
let ctx = CallbackContext::<N, P>::from_raw(data);
ctx.notification.buffer_size(&ctx.client, n_frames).to_ffi()
}
unsafe extern "C" fn sample_rate<N, P>(n_frames: Frames, data: *mut libc::c_void) -> libc::c_int
where
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + ProcessHandler,
{
let ctx = CallbackContext::<N, P>::from_raw(data);
ctx.notification.sample_rate(&ctx.client, n_frames).to_ffi()
}
unsafe extern "C" fn client_registration<N, P>(
name: *const libc::c_char,
register: libc::c_int,
data: *mut libc::c_void,
) where
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + ProcessHandler,
{
let ctx = CallbackContext::<N, P>::from_raw(data);
let name = ffi::CStr::from_ptr(name).to_str().unwrap();
let register = match register {
0 => false,
_ => true,
};
ctx.notification
.client_registration(&ctx.client, name, register)
}
unsafe extern "C" fn port_registration<N, P>(
port_id: PortId,
register: libc::c_int,
data: *mut libc::c_void,
) where
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + ProcessHandler,
{
let ctx = CallbackContext::<N, P>::from_raw(data);
let register = match register {
0 => false,
_ => true,
};
ctx.notification
.port_registration(&ctx.client, port_id, register)
}
#[allow(dead_code)]
unsafe extern "C" fn port_rename<N, P>(
port_id: PortId,
old_name: *const libc::c_char,
new_name: *const libc::c_char,
data: *mut libc::c_void,
) -> libc::c_int
where
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + ProcessHandler,
{
let ctx = CallbackContext::<N, P>::from_raw(data);
let old_name = ffi::CStr::from_ptr(old_name).to_str().unwrap();
let new_name = ffi::CStr::from_ptr(new_name).to_str().unwrap();
ctx.notification
.port_rename(&ctx.client, port_id, old_name, new_name)
.to_ffi()
}
unsafe extern "C" fn port_connect<N, P>(
port_id_a: PortId,
port_id_b: PortId,
connect: libc::c_int,
data: *mut libc::c_void,
) where
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + ProcessHandler,
{
let ctx = CallbackContext::<N, P>::from_raw(data);
let are_connected = match connect {
0 => false,
_ => true,
};
ctx.notification
.ports_connected(&ctx.client, port_id_a, port_id_b, are_connected)
}
unsafe extern "C" fn graph_order<N, P>(data: *mut libc::c_void) -> libc::c_int
where
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + ProcessHandler,
{
let ctx = CallbackContext::<N, P>::from_raw(data);
ctx.notification.graph_reorder(&ctx.client).to_ffi()
}
unsafe extern "C" fn xrun<N, P>(data: *mut libc::c_void) -> libc::c_int
where
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + ProcessHandler,
{
let ctx = CallbackContext::<N, P>::from_raw(data);
ctx.notification.xrun(&ctx.client).to_ffi()
}
unsafe extern "C" fn latency<N, P>(mode: j::jack_latency_callback_mode_t, data: *mut libc::c_void)
where
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + ProcessHandler,
{
let ctx = CallbackContext::<N, P>::from_raw(data);
let mode = match mode {
j::JackCaptureLatency => LatencyType::Capture,
j::JackPlaybackLatency => LatencyType::Playback,
_ => unreachable!(),
};
ctx.notification.latency(&ctx.client, mode)
}
pub unsafe fn clear_callbacks(_client: *mut j::jack_client_t) -> Result<(), Error> {
Ok(())
}
pub struct CallbackContext<N, P> {
pub client: Client,
pub notification: N,
pub process: P,
}
impl<N, P> CallbackContext<N, P>
where
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + ProcessHandler,
{
pub unsafe fn from_raw<'a>(ptr: *mut libc::c_void) -> &'a mut CallbackContext<N, P> {
debug_assert!(!ptr.is_null());
let obj_ptr = ptr as *mut CallbackContext<N, P>;
&mut *obj_ptr
}
fn raw(b: &mut Box<Self>) -> *mut libc::c_void {
let ptr: *mut Self = b.as_mut();
ptr as *mut libc::c_void
}
pub unsafe fn register_callbacks(b: &mut Box<Self>) -> Result<(), Error> {
let data_ptr = CallbackContext::raw(b);
let client = b.client.raw();
j::jack_set_thread_init_callback(client, Some(thread_init_callback::<N, P>), data_ptr);
j::jack_on_info_shutdown(client, Some(shutdown::<N, P>), data_ptr);
j::jack_set_process_callback(client, Some(process::<N, P>), data_ptr);
j::jack_set_freewheel_callback(client, Some(freewheel::<N, P>), data_ptr);
j::jack_set_buffer_size_callback(client, Some(buffer_size::<N, P>), data_ptr);
j::jack_set_sample_rate_callback(client, Some(sample_rate::<N, P>), data_ptr);
j::jack_set_client_registration_callback(
client,
Some(client_registration::<N, P>),
data_ptr,
);
j::jack_set_port_registration_callback(client, Some(port_registration::<N, P>), data_ptr);
j::jack_set_port_connect_callback(client, Some(port_connect::<N, P>), data_ptr);
j::jack_set_graph_order_callback(client, Some(graph_order::<N, P>), data_ptr);
j::jack_set_xrun_callback(client, Some(xrun::<N, P>), data_ptr);
j::jack_set_latency_callback(client, Some(latency::<N, P>), data_ptr);
Ok(())
}
}