rust_jack/
callbacks.rs

1use std::{ffi, mem};
2use libc::c_void;
3use jack_sys as j;
4use enums::*;
5use flags::*;
6
7/// Specifies callbacks for Jack.
8///
9/// All callbacks happen on the same thread (not concurrently), unless otherwise
10/// stated.
11///
12/// # TODO
13/// * convert C enum return values to Rust enums.
14pub trait JackHandler {
15    /// Called just once after the creation of the thread in which all other
16    /// callbacks will be handled.
17    ///
18    /// It does not need to be suitable for real-time execution.
19    fn thread_init(&mut self) {}
20
21    /// Called when the Jack server shuts down the client thread. The function
22    /// must be written as if it were an asynchronous POSIX signal handler ---
23    /// use only async-safe functions, and remember that it is executed from
24    /// another thread. A typical funcion might set a flag or write to a pipe so
25    /// that the rest of the application knows that the Jack client thread has
26    /// shut down.
27
28    fn shutdown(&mut self, _status: ClientStatus, _reason: &str) {}
29    /// Called whenever there is work to be done.
30    ///
31    /// It needs to be suitable for real-time execution. That means that it
32    /// cannot call functions that might block for a long time. This includes
33    /// all I/O functions (disk, TTY, network), malloc, free, printf,
34    /// pthread_mutex_lock, sleep, wait, poll, select, pthread_join,
35    /// pthread_cond_wait, etc, etc.
36    ///
37    /// Should return `0` on success, and non-zero on error.
38    fn process(&mut self, _n_frames: u32) -> JackControl {
39        JackControl::Continue
40    }
41
42    /// Called whenever "freewheel" mode is entered or leaving.
43    fn freewheel(&mut self, _is_freewheel_enabled: bool) {}
44
45    /// Called whenever the size of the buffer that will be passed to `process`
46    /// is about to change.
47    fn buffer_size(&mut self, _size: u32) -> JackControl {
48        JackControl::Continue
49    }
50
51    /// Called whenever the system sample rate changes.
52    fn sample_rate(&mut self, _srate: u32) -> JackControl {
53        JackControl::Continue
54    }
55
56    /// Called whenever a client is registered or unregistered
57    fn client_registration(&mut self, _name: &str, _is_registered: bool) {}
58
59    /// Called whenever a port is registered or unregistered
60    fn port_registration(&mut self, _port_id: u32, _is_registered: bool) {}
61
62    /// Called whenever a port is renamed.
63    ///
64    /// # TODO
65    /// * Possibly fix description, Jack API docs have same description
66    /// for this as port registration.
67    fn port_rename(&mut self, _port_id: u32, _old_name: &str, _new_name: &str) -> JackControl {
68        JackControl::Continue
69    }
70
71    /// Called whenever ports are connected/disconnected to/from each other.
72    fn ports_connected(&mut self, _port_id_a: u32, _port_id_b: u32, _are_connected: bool) {}
73
74    /// Called whenever the processing graph is reordered.
75    fn graph_reorder(&mut self) -> JackControl {
76        JackControl::Continue
77    }
78
79    /// Called whenever an xrun occurs.
80    ///
81    /// An xrun is a buffer under or over run, which means some data has been
82    /// missed.
83    fn xrun(&mut self) -> JackControl {
84        JackControl::Continue
85    }
86
87    /// Called whenever it is necessary to recompute the latencies for some or
88    /// all Jack ports.
89    ///
90    /// It will be called twice each time it is needed, once being passed
91    /// `CaptureLatency` and once with `PlayBackLatency. See managing and
92    /// determining latency for the definition of each type of latency and
93    /// related functions. TODO: clear up the "see managing and ..." in the
94    /// docstring.
95    ///
96    /// IMPORTANT: Most Jack clients do NOT need to register a latency callback.
97    ///
98    /// Clients that meed any of the following conditions do NOT need to
99    /// register a latency callback:
100    ///
101    /// * have only input ports
102    ///
103    /// * have only output ports
104    ///
105    /// * their output is totally unrelated to their input
106    ///
107    /// * their output is not delayed relative to their input (i.e. data that
108    /// arrives in a `process` is processed and output again in the same
109    /// callback)
110    ///
111    /// Clients NOT registering a latency callback MUST also satisfy this condition
112    ///
113    /// * have no multiple distinct internal signal pathways
114    ///
115    /// This means that if your client has more than 1 input and output port,
116    /// and considers them always "correlated" (e.g. as a stereo pair), then
117    /// there is only 1 (e.g. stereo) signal pathway through the client. This
118    /// would be true, for example, of a stereo FX rack client that has a
119    /// left/right input pair and a left/right output pair.
120    ///
121    /// However, this is somewhat a matter of perspective. The same FX rack
122    /// client could be connected so that its two input ports were connected to
123    /// entirely separate sources. Under these conditions, the fact that the
124    /// client does not register a latency callback MAY result in port latency
125    /// values being incorrect.
126    ///
127    /// Clients that do not meet any of those conditions SHOULD register a
128    /// latency callback.
129    ///
130    /// See the documentation for `jack_port_set_latency_range()` on how the
131    /// callback should operate. Remember that the mode argument given to the
132    /// latency callback will need to be passed into
133    /// jack_port_set_latency_range()
134    fn latency(&mut self, _mode: LatencyType) {}
135}
136
137unsafe fn from_void<'a, T: JackHandler>(ptr: *mut c_void) -> &'a mut T {
138    assert!(!ptr.is_null());
139    let obj_ptr: *mut T = mem::transmute(ptr);
140    &mut *obj_ptr
141}
142
143extern "C" fn thread_init_callback<T: JackHandler>(data: *mut c_void) {
144    let obj: &mut T = unsafe { from_void(data) };
145    obj.thread_init()
146}
147
148extern "C" fn shutdown<T: JackHandler>(code: j::jack_status_t,
149                                       reason: *const i8,
150                                       data: *mut c_void) {
151    let obj: &mut T = unsafe { from_void(data) };
152    let reason_str = unsafe {
153        let cstr = ffi::CStr::from_ptr(reason);
154        match cstr.to_str() {
155            Ok(s) => s,
156            Err(_) => "Failed to interpret error.",
157        }
158    };
159    obj.shutdown(ClientStatus::from_bits(code).unwrap_or(UNKNOWN_ERROR),
160                 reason_str)
161}
162
163extern "C" fn process<T: JackHandler>(n_frames: u32, data: *mut c_void) -> i32 {
164    let obj: &mut T = unsafe { from_void(data) };
165    obj.process(n_frames).to_ffi()
166}
167
168extern "C" fn freewheel<T: JackHandler>(starting: i32, data: *mut c_void) {
169    let obj: &mut T = unsafe { from_void(data) };
170    let is_starting = match starting {
171        0 => false,
172        _ => true,
173    };
174    obj.freewheel(is_starting)
175}
176
177extern "C" fn buffer_size<T: JackHandler>(n_frames: u32, data: *mut c_void) -> i32 {
178    let obj: &mut T = unsafe { from_void(data) };
179    obj.buffer_size(n_frames).to_ffi()
180}
181
182extern "C" fn sample_rate<T: JackHandler>(n_frames: u32, data: *mut c_void) -> i32 {
183    let obj: &mut T = unsafe { from_void(data) };
184    obj.sample_rate(n_frames).to_ffi()
185}
186
187extern "C" fn client_registration<T: JackHandler>(name: *const i8,
188                                                  register: i32,
189                                                  data: *mut c_void) {
190    let obj: &mut T = unsafe { from_void(data) };
191    let name = unsafe { ffi::CStr::from_ptr(name).to_str().unwrap() };
192    let register = match register {
193        0 => false,
194        _ => true,
195    };
196    obj.client_registration(name, register)
197}
198
199extern "C" fn port_registration<T: JackHandler>(port_id: u32, register: i32, data: *mut c_void) {
200    let obj: &mut T = unsafe { from_void(data) };
201    let register = match register {
202        0 => false,
203        _ => true,
204    };
205    obj.port_registration(port_id, register)
206}
207
208#[allow(dead_code)] // TODO: remove once it can be registered
209extern "C" fn port_rename<T: JackHandler>(port_id: u32,
210                                          old_name: *const i8,
211                                          new_name: *const i8,
212                                          data: *mut c_void)
213                                          -> i32 {
214    let obj: &mut T = unsafe { from_void(data) };
215    let old_name = unsafe { ffi::CStr::from_ptr(old_name).to_str().unwrap() };
216    let new_name = unsafe { ffi::CStr::from_ptr(new_name).to_str().unwrap() };
217    obj.port_rename(port_id, old_name, new_name).to_ffi()
218}
219
220extern "C" fn port_connect<T: JackHandler>(port_id_a: u32,
221                                           port_id_b: u32,
222                                           connect: i32,
223                                           data: *mut c_void) {
224    let obj: &mut T = unsafe { from_void(data) };
225    let are_connected = match connect {
226        0 => false,
227        _ => true,
228    };
229    obj.ports_connected(port_id_a, port_id_b, are_connected)
230}
231
232extern "C" fn graph_order<T: JackHandler>(data: *mut c_void) -> i32 {
233    let obj: &mut T = unsafe { from_void(data) };
234    obj.graph_reorder().to_ffi()
235}
236
237extern "C" fn xrun<T: JackHandler>(data: *mut c_void) -> i32 {
238    let obj: &mut T = unsafe { from_void(data) };
239    obj.xrun().to_ffi()
240}
241
242extern "C" fn latency<T: JackHandler>(mode: j::jack_latency_callback_mode_t, data: *mut c_void) {
243    let obj: &mut T = unsafe { from_void(data) };
244    let mode = match mode {
245        j::JackCaptureLatency => LatencyType::Capture,
246        j::JackPlaybackLatency => LatencyType::Playback,
247        _ => unreachable!(),
248    };
249    obj.latency(mode)
250}
251
252/// Clears the callbacks registered to `client`.
253///
254/// Returns `Err(JackErr::CallbackDeregistrationError)` on failure.
255///
256/// # Unsafe
257/// * Uses ffi calls, be careful.
258///
259/// # TODO
260/// * Implement correctly. Freezes on my system.
261pub unsafe fn clear_callbacks(_client: *mut j::jack_client_t) -> Result<(), JackErr> {
262    // j::jack_set_thread_init_callback(client, None, ptr::null_mut());
263    // j::jack_set_process_callback(client, None, ptr::null_mut());
264    Ok(())
265}
266
267/// Registers methods from `handler` to be used by Jack with `client`.
268///
269/// Returns `Ok(handler_ptr)` on success, or
270/// `Err(JackErr::CallbackRegistrationError)` on failure.
271///
272/// Registers `handler` with jack. All jack calls to `client` will be handled by
273/// `handler`. `handler` is consumed, but it is not deallocated. `handler`
274/// should be manually deallocated when jack will no longer make calls to it,
275/// such as when registering new callbacks with the same client, or dropping the
276/// client.
277///
278/// # TODO
279/// * Handled failed registrations
280/// * Fix `jack_set_port_rename_callback`
281///
282/// # Unsafe
283/// * `handler` will not be automatically deallocated.
284pub unsafe fn register_callbacks<T: JackHandler>(client: *mut j::jack_client_t,
285                                                 handler: T)
286                                                 -> Result<*mut T, JackErr> {
287    let handler_ptr: *mut T = Box::into_raw(Box::new(handler));
288    let data_ptr = mem::transmute(handler_ptr);
289    j::jack_set_thread_init_callback(client, Some(thread_init_callback::<T>), data_ptr);
290    j::jack_on_info_shutdown(client, Some(shutdown::<T>), data_ptr);
291    j::jack_set_process_callback(client, Some(process::<T>), data_ptr);
292    j::jack_set_freewheel_callback(client, Some(freewheel::<T>), data_ptr);
293    j::jack_set_buffer_size_callback(client, Some(buffer_size::<T>), data_ptr);
294    j::jack_set_sample_rate_callback(client, Some(sample_rate::<T>), data_ptr);
295    j::jack_set_client_registration_callback(client, Some(client_registration::<T>), data_ptr);
296    j::jack_set_port_registration_callback(client, Some(port_registration::<T>), data_ptr);
297    // doesn't compile for testing
298    // j::jack_set_port_rename_callback(client, Some(port_rename::<T>), data_ptr);
299    j::jack_set_port_connect_callback(client, Some(port_connect::<T>), data_ptr);
300    j::jack_set_graph_order_callback(client, Some(graph_order::<T>), data_ptr);
301    j::jack_set_xrun_callback(client, Some(xrun::<T>), data_ptr);
302    j::jack_set_latency_callback(client, Some(latency::<T>), data_ptr);
303    Ok(handler_ptr)
304}