sqa_jack/
handler.rs

1//! Callback-based JACK API functions (logging, processing).
2
3use super::{JackNFrames, JackPort, JackStatus, JackConnection, Deactivated};
4use jack_sys::*;
5use std::panic::{AssertUnwindSafe, catch_unwind};
6use std::sync::atomic::{AtomicPtr, Ordering};
7use std::ffi::CStr;
8use libc;
9use errors::*;
10
11/// An object that can receive info and error messages from JACK.
12pub trait JackLoggingHandler: Send {
13    /// Called when JACK displays an error message.
14    fn on_error(&mut self, msg: &str);
15    /// Called when JACK displays an info message.
16    fn on_info(&mut self, msg: &str);
17}
18
19lazy_static! {
20    static ref LOGGING_HANDLER: AtomicPtr<*mut JackLoggingHandler> = AtomicPtr::new(::std::ptr::null_mut());
21}
22unsafe extern "C" fn error_callback(msg: *const libc::c_char) {
23    let handler = LOGGING_HANDLER.load(Ordering::Relaxed);
24    if !handler.is_null() {
25        let f = &mut (**handler);
26        let msg = CStr::from_ptr(msg);
27        let _ = catch_unwind(AssertUnwindSafe(|| {
28            f.on_error(&msg.to_string_lossy());
29        }));
30    }
31}
32unsafe extern "C" fn info_callback(msg: *const libc::c_char) {
33    let handler = LOGGING_HANDLER.load(Ordering::Relaxed);
34    if !handler.is_null() {
35        let f = &mut (**handler);
36        let msg = CStr::from_ptr(msg);
37        let _ = catch_unwind(AssertUnwindSafe(|| {
38            f.on_info(&msg.to_string_lossy());
39        }));
40    }
41}
42/// Set the logging handler (a struct that implements `JackLoggingHandler`).
43///
44/// # Safety
45///
46/// **Warning:** Your handler will never be deallocated / `Drop`ped.
47pub fn set_logging_handler<F>(handler: F) where F: JackLoggingHandler + 'static {
48    unsafe {
49        let trait_object_ptr = Box::into_raw(Box::new(handler) as Box<JackLoggingHandler>);
50        let ptr_ception = Box::into_raw(Box::new(trait_object_ptr));
51        LOGGING_HANDLER.store(ptr_ception, Ordering::Relaxed);
52        jack_set_error_function(Some(error_callback));
53        jack_set_info_function(Some(info_callback));
54    }
55}
56/// Context for some callbacks.
57pub struct JackCallbackContext {
58    nframes: JackNFrames
59}
60
61impl JackCallbackContext {
62    /// Returns the number of frames that must be processed in this callback.
63    #[inline(always)]
64    pub fn nframes(&self) -> u32 {
65        self.nframes
66    }
67    /// Gets the buffer of a port, if the port is valid.
68    pub fn get_port_buffer(&self, port: &JackPort) -> Option<&mut [f32]> {
69        unsafe {
70            let buf = jack_port_get_buffer(port.as_ptr(), self.nframes);
71            if buf.is_null() {
72                None
73            }
74            else {
75                Some(::std::slice::from_raw_parts_mut(buf as *mut f32, self.nframes as usize))
76            }
77        }
78    }
79}
80
81/// Return type of callback functions.
82#[derive(Copy, Clone, Debug)]
83pub enum JackControl {
84    /// Continue processing.
85    Continue = 0,
86    /// Stop processing.
87    Stop = -1
88}
89/// Trait for an object that implements JACK callbacks.
90///
91/// Most of the default implementations return `JackControl::Continue` - however, **process() does not** - you
92/// must explicitly override this behaviour if you want to specify nothing for `process()`.
93pub trait JackHandler: Send {
94    /// This function is called by the engine any time there is work to be done.
95    /// Return `JackControl::Stop` to stop processing, otherwise return
96    /// `JackControl::Continue` to continue.
97    ///
98    /// # Realtime safety
99    ///
100    /// The code in the supplied function **MUST** be suitable for real-time execution.
101    /// That means that it cannot call functions that might block for a long time.
102    /// This includes all I/O functions (disk, TTY, network), malloc, free, printf,
103    /// pthread_mutex_lock, sleep, wait, poll, select, pthread_join, pthread_cond_wait, etc.
104    ///
105    /// Rust-specific things to **avoid** using: `std` MPSC channels (use
106    /// [bounded_spsc_queue](https://github.com/polyfractal/bounded-spsc-queue) instead, or
107    /// similar ringbuffer solution), mutexes, `RWLock`s, `Barrier`s, `String`s, `Vec`s (use
108    /// a **pre-allocated** [ArrayVec](https://github.com/bluss/arrayvec) instead), and
109    /// anything under `std::collections`.
110    fn process(&mut self, _ctx: &JackCallbackContext) -> JackControl {
111        JackControl::Stop
112    }
113    /// This is called whenever the size of the the buffer that will be passed to the
114    /// `process()` function is about to change.
115    /// Clients that depend on knowing the buffer size must implement this callback.
116    fn buffer_size(&mut self, _new_size: JackNFrames) -> JackControl { JackControl::Continue }
117    /// Called whenever the system sample rate changes.
118    ///
119    /// Given that the JACK API exposes no way to change the sample rate, the library author
120    /// would like you to know that this is a decidedly rare occurence. Still, it's worth
121    /// being prepared ;)
122    fn sample_rate(&mut self, _new_rate: JackNFrames) -> JackControl { JackControl::Continue }
123    /// Called just once after the creation of the thread in which all other callbacks are
124    /// handled.
125    fn thread_init(&mut self) {  }
126    /// To be called if and when the JACK server shuts down the client thread.
127    fn shutdown(&mut self, _status: JackStatus, _reason: &str) { }
128    /// Called whenever a client is registered or unregistered.
129    ///
130    /// Use the `registered` argument to determine which it is.
131    fn client_registered(&mut self, _name: &str, _registered: bool) { }
132    /// Called when an XRUN (over- or under- run) occurs.
133    fn xrun(&mut self) -> JackControl { JackControl::Continue }
134}
135/*
136    /// Called whenever a port is registered or unregistered.
137    ///
138    /// Use the `registered` argument to determine which it is.
139    fn port_registered(&mut self, _port: JackPort, _registered: bool) { }
140    /// Called whenever a port is renamed.
141    fn port_renamed(&mut self, _port: JackPort, _old_name: &str, _new_name: &str) { }
142    /// Called whenever ports are connected or disconnected.
143    ///
144    /// Use the `connected` argument to determine which it is.
145    fn port_connected(&mut self, _from: JackPort, _to: JackPort, _connected: bool) { }
146    /// Called whenever the processing graph is reordered.
147    fn graph_reorder(&mut self) -> JackControl { JackControl::Continue }
148    /// Called when the JACK server starts or stops freewheeling.
149    ///
150    /// Use the `freewheel` argument to determine which it is.
151    fn freewheel(&mut self, _freewheel: bool) { }
152*/
153
154impl<F> JackHandler for F where F: FnMut(&JackCallbackContext) -> JackControl + Send + 'static {
155    fn process(&mut self, ctx: &JackCallbackContext) -> JackControl {
156        self(ctx)
157    }
158}
159unsafe extern "C" fn buffer_size_callback<T>(frames: JackNFrames, user: *mut libc::c_void) -> libc::c_int where T: JackHandler {
160    let callbacks = &mut *(user as *mut T);
161    catch_unwind(AssertUnwindSafe(|| {
162        callbacks.buffer_size(frames) as _
163    })).unwrap_or(-1)
164}
165unsafe extern "C" fn sample_rate_callback<T>(frames: JackNFrames, user: *mut libc::c_void) -> libc::c_int where T: JackHandler {
166    let callbacks = &mut *(user as *mut T);
167    catch_unwind(AssertUnwindSafe(|| {
168        callbacks.sample_rate(frames) as _
169    })).unwrap_or(-1)
170}
171unsafe extern "C" fn client_registration_callback<T>(name: *const libc::c_char, register: libc::c_int, user: *mut libc::c_void) where T: JackHandler {
172    let callbacks = &mut *(user as *mut T);
173    let name = CStr::from_ptr(name);
174    let _ = catch_unwind(AssertUnwindSafe(|| {
175        callbacks.client_registered(&name.to_string_lossy(), register != 0)
176    }));
177
178}
179unsafe extern "C" fn info_shutdown_callback<T>(code: jack_status_t, reason: *const libc::c_char, user: *mut libc::c_void) where T: JackHandler {
180    let callbacks = &mut *(user as *mut T);
181    let code = JackStatus::from_bits_truncate(code);
182    let reason = CStr::from_ptr(reason);
183    let _ = catch_unwind(AssertUnwindSafe(|| {
184        callbacks.shutdown(code, &reason.to_string_lossy())
185    }));
186
187}
188unsafe extern "C" fn thread_init_callback<T>(user: *mut libc::c_void) where T: JackHandler {
189    let callbacks = &mut *(user as *mut T);
190    let _ = catch_unwind(AssertUnwindSafe(|| {
191        callbacks.thread_init()
192    }));
193}
194unsafe extern "C" fn process_callback<T>(nframes: JackNFrames, user: *mut libc::c_void) -> libc::c_int where T: JackHandler {
195    let callbacks = &mut *(user as *mut T);
196    let ctx = JackCallbackContext {
197        nframes: nframes
198    };
199    catch_unwind(AssertUnwindSafe(|| {
200        callbacks.process(&ctx) as _
201    })).unwrap_or(-1)
202}
203unsafe extern "C" fn xrun_callback<T>(user: *mut libc::c_void) -> libc::c_int where T: JackHandler {
204    let callbacks = &mut *(user as *mut T);
205    catch_unwind(AssertUnwindSafe(|| {
206        callbacks.xrun() as _
207    })).unwrap_or(-1)
208}
209pub fn set_handler<F>(conn: &mut JackConnection<Deactivated>, handler: F) -> JackResult<()> where F: JackHandler {
210    let user_ptr = Box::into_raw(Box::new(handler));
211    let user_ptr = user_ptr as *mut libc::c_void;
212    unsafe {
213        let code = jack_set_process_callback(conn.handle, Some(process_callback::<F>), user_ptr);
214        if code != 0 { Err(JackError::UnknownErrorCode { from: "set_process_callback() - process", code: code })? }
215        let code = jack_set_thread_init_callback(conn.handle, Some(thread_init_callback::<F>), user_ptr);
216        if code != 0 { Err(JackError::UnknownErrorCode { from: "set_process_callback() - thread_init", code: code })? }
217        let code = jack_set_buffer_size_callback(conn.handle, Some(buffer_size_callback::<F>), user_ptr);
218        if code != 0 { Err(JackError::UnknownErrorCode { from: "set_process_callback() - buffer_size", code: code })? }
219        let code = jack_set_sample_rate_callback(conn.handle, Some(sample_rate_callback::<F>), user_ptr);
220        if code != 0 { Err(JackError::UnknownErrorCode { from: "set_process_callback() - sample_rate", code: code })? }
221        let code = jack_set_xrun_callback(conn.handle, Some(xrun_callback::<F>), user_ptr);
222        if code != 0 { Err(JackError::UnknownErrorCode { from: "set_process_callback() - xrun", code: code })? }
223        let code = jack_set_client_registration_callback(conn.handle, Some(client_registration_callback::<F>), user_ptr);
224        if code != 0 { Err(JackError::UnknownErrorCode { from: "set_process_callback() - client_registration", code: code })? }
225        jack_on_info_shutdown(conn.handle, Some(info_shutdown_callback::<F>), user_ptr);
226    }
227    Ok(())
228}