sqa_jack/
lib.rs

1#[macro_use]
2extern crate bitflags;
3extern crate libc;
4extern crate jack_sys;
5extern crate failure;
6#[macro_use]
7extern crate failure_derive;
8#[macro_use]
9extern crate lazy_static;
10
11static JACK_DEFAULT_AUDIO_TYPE: &'static [u8] = b"32 bit float mono audio\0";
12pub mod errors;
13pub mod handler;
14pub mod port;
15
16#[cfg(test)]
17mod tests;
18
19use std::ffi::{CString, CStr};
20use std::marker::PhantomData;
21use errors::JackError;
22pub use errors::JackResult;
23pub use handler::{JackCallbackContext, JackControl, JackHandler, JackLoggingHandler, set_logging_handler};
24pub use port::JackPort;
25pub use jack_sys::*;
26
27pub type JackNFrames = jack_nframes_t;
28pub type JackPortPtr = *mut jack_port_t;
29bitflags! {
30    /// Status of an operation.
31    ///
32    /// See `STATUS_*` constants for possible values.
33    pub flags JackStatus: jack_status_t {
34        /// Overall operation failed.
35        const STATUS_FAILURE = jack_sys::JackFailure,
36        /// The operation contained an invalid or unsupported option.
37        const STATUS_INVALID_OPTION = jack_sys::JackInvalidOption,
38        /// The desired client name was not unique. With the JackUseExactName option this
39        /// situation is fatal. Otherwise, the name was modified by appending a dash and a
40        /// two-digit number in the range "-01" to "-99". The jack_get_client_name()
41        /// function will return the exact string that was used. If the specified
42        /// client_name plus these extra characters would be too long, the open fails
43        /// instead.
44        const STATUS_NAME_NOT_UNIQUE = jack_sys::JackNameNotUnique,
45        /// The JACK server was started as a result of this operation. Otherwise,
46        /// it was running already. In either case the caller is now connected to jackd,
47        /// so there is no race condition. When the server shuts down, the client will find
48        /// out.
49        const STATUS_SERVER_STARTED = jack_sys::JackServerStarted,
50        /// Unable to connect to the JACK server.
51        const STATUS_SERVER_FAILED = jack_sys::JackServerFailed,
52        /// Communication error with the JACK server.
53        const STATUS_SERVER_ERROR = jack_sys::JackServerError,
54        /// Requested client does not exist.
55        const STATUS_NO_SUCH_CLIENT = jack_sys::JackNoSuchClient,
56        /// Unable to load internal client.
57        const STATUS_LOAD_FAILURE = jack_sys::JackLoadFailure,
58        /// Unable to initialise client.
59        const STATUS_INIT_FAILURE = jack_sys::JackInitFailure,
60        /// Unable to access shared memory.
61        const STATUS_SHM_FAILURE = jack_sys::JackShmFailure,
62        /// Client's protocol version does not match.
63        const STATUS_VERSION_ERROR = jack_sys::JackShmFailure
64    }
65}
66bitflags! {
67    /// A port has a set of flags that are formed by OR-ing together the desired values
68    /// from the consts `PORT_*`. The flags "JackPortIsInput" and "JackPortIsOutput" are
69    /// mutually exclusive and it is an error to use them both.
70    pub flags JackPortFlags: libc::c_ulong {
71        /// This port can receive data.
72        const PORT_IS_INPUT = JackPortIsInput as libc::c_ulong,
73        /// Data can be read from this port.
74        const PORT_IS_OUTPUT = JackPortIsOutput as libc::c_ulong,
75        /// The port corresponds to some kind of physical I/O connector.
76        const PORT_IS_PHYSICAL = JackPortIsPhysical as libc::c_ulong,
77        /// A call to jack_port_request_monitor() makes sense.
78        ///
79        /// Precisely what this means is dependent on the client. A typical result of it
80        /// being called with TRUE as the second argument is that data that would be
81        /// available from an output port (with JackPortIsPhysical set) is sent to a
82        /// physical output connector as well, so that it can be heard/seen/whatever.
83        ///
84        /// Clients that do not control physical interfaces should never create ports with
85        /// this bit set.
86        const PORT_CAN_MONITOR = JackPortCanMonitor as libc::c_ulong,
87        /// For an input port: the data received by this port will not be passed on or
88        /// made available at any other port.
89        ///
90        /// For an output port: the data available at the port does not originate from any
91        /// other port.
92        ///
93        /// Audio synthesizers, I/O hardware interface clients, HDR systems are examples
94        /// of clients that would set this flag for their ports.
95        const PORT_IS_TERMINAL = JackPortIsTerminal as libc::c_ulong
96    }
97}
98bitflags! {
99    /// Options for opening a connection to JACK, formed by OR-ing together desired values
100    /// from the consts `OPEN_*`.
101    pub flags JackOpenOptions: libc::c_uint {
102        /// Do not automatically start the JACK server when it is not already running.
103        /// This option is always selected if $JACK_NO_START_SERVER is defined in the
104        /// calling process environment.
105        const OPEN_NO_START_SERVER = JackNoStartServer,
106        /// Use the exact client name requested. Otherwise, JACK automatically generates
107        /// a unique one, if needed.
108        const OPEN_USE_EXACT_NAME = JackUseExactName,
109    }
110}
111/// Type argument for deactivated connections.
112pub struct Deactivated;
113/// Type argument for activated connections.
114pub struct Activated;
115/// A connection to a JACK server (known as a "client" in the JACK docs).
116///
117/// Exists in two types: `JackConnection<Activated>` when `activate()` has been called
118/// (i.e. audio is being processed), and `<Deactivated>` when this has not happened,
119/// or `deactivate()` has been called.
120pub struct JackConnection<T> {
121    handle: *mut jack_client_t,
122    sample_rate: u32,
123    _phantom: PhantomData<T>
124}
125
126/// Helper function to convert Rust `&str`s to `CString`s.
127fn str_to_cstr(st: &str) -> JackResult<CString> {
128    Ok(CString::new(st).map_err(|_| JackError::NulError)?)
129}
130
131impl<T> JackConnection<T> {
132    pub fn as_ptr(&self) -> *const jack_client_t {
133        self.handle
134    }
135    /// Get the sample rate of the JACK server.
136    pub fn sample_rate(&self) -> jack_nframes_t {
137        self.sample_rate
138    }
139    /// Get the CPU load of the JACK server.
140    pub fn cpu_load(&self) -> libc::c_float {
141        unsafe {
142            jack_cpu_load(self.handle)
143        }
144    }
145    /// Get the buffer size passed to the `process()` callback.
146    pub fn buffer_size(&self) -> jack_nframes_t {
147        unsafe {
148            jack_get_buffer_size(self.handle)
149        }
150    }
151    /// Change the buffer size passed to the `process()` callback.
152    ///
153    /// This operation **stops the JACK engine process cycle**, then calls all registered
154    /// bufsize_callback functions before restarting the process cycle. This will cause a
155    /// gap in the audio flow, so it should only be done at appropriate stopping points.
156    ///
157    /// # Parameters
158    ///
159    /// - bufsize: new buffer size. Must be a power of two.
160    ///
161    /// # Errors
162    ///
163    /// - `NotPowerOfTwo`: if the new buffer size isn't a power of two
164    /// - `UnknownErrorCode`
165    pub fn set_buffer_size(&mut self, bufsize: jack_nframes_t) -> JackResult<()> {
166        if bufsize.next_power_of_two() != bufsize {
167            Err(JackError::NotPowerOfTwo)?
168        }
169        let code = unsafe {
170            jack_set_buffer_size(self.handle, bufsize)
171        };
172        if code != 0 {
173            Err(JackError::UnknownErrorCode { from: "set_buffer_size()", code: code })?
174        }
175        else {
176            Ok(())
177        }
178    }
179    unsafe fn activate_or_deactivate<X>(self, activate: bool) -> Result<JackConnection<X>, (Self, JackError)> {
180        let code = {
181            if activate {
182                jack_activate(self.handle)
183            }
184            else {
185                jack_deactivate(self.handle)
186            }
187        };
188        if code != 0 {
189            Err((self, JackError::UnknownErrorCode { from: "activate_or_deactivate()", code }))
190        }
191        else {
192            Ok(::std::mem::transmute::<JackConnection<T>, JackConnection<X>>(self))
193        }
194    }
195    fn connect_or_disconnect_ports(&mut self, from: &JackPort, to: &JackPort, conn: bool) -> JackResult<()> {
196        if from.get_type()? != to.get_type()? {
197            Err(JackError::InvalidPortType)?;
198        }
199        if !from.get_flags().contains(PORT_IS_OUTPUT) || !to.get_flags().contains(PORT_IS_INPUT) {
200            Err(JackError::InvalidPortFlags)?;
201        }
202        let code = unsafe {
203            if conn {
204                jack_connect(self.handle, from.get_name_raw(false)?, to.get_name_raw(false)?)
205            }
206            else {
207                jack_disconnect(self.handle, from.get_name_raw(false)?, to.get_name_raw(false)?)
208            }
209        };
210        match code {
211            libc::EEXIST => Ok(()),
212            0 => Ok(()),
213            _ => Err(JackError::UnknownErrorCode { from: "connect_or_disconnect_ports()", code: code })?
214        }
215    }
216    /// Establish a connection between two ports.
217    ///
218    /// When a connection exists, data written to the source port will be available to be
219    /// read at the destination port.
220    ///
221    /// # Preconditions
222    ///
223    /// - The port types must be identical.
224    /// - The JackPortFlags of the source_port must include `PORT_IS_OUTPUT`.
225    /// - The JackPortFlags of the destination_port must include `PORT_IS_INPUT`.
226    ///
227    /// # Errors
228    ///
229    /// - `InvalidPortType`: when the types are not identical
230    /// - `InvalidPortFlags`: when the flags do not satisfy the preconditions above
231    /// - `UnknownErrorCode`
232    pub fn connect_ports(&mut self, from: &JackPort, to: &JackPort) -> JackResult<()> {
233        self.connect_or_disconnect_ports(from, to, true)
234    }
235    /// Remove a connection between two ports.
236    ///
237    /// When a connection exists, data written to the source port will be available to be
238    /// read at the destination port.
239    ///
240    /// # Preconditions
241    ///
242    /// - The port types must be identical.
243    /// - The JackPortFlags of the source_port must include `PORT_IS_OUTPUT`.
244    /// - The JackPortFlags of the destination_port must include `PORT_IS_INPUT`.
245    ///
246    /// # Errors
247    ///
248    /// - `InvalidPortType`: when the types are not identical
249    /// - `InvalidPortFlags`: when the flags do not satisfy the preconditions above
250    /// - `UnknownErrorCode`
251    pub fn disconnect_ports(&mut self, from: &JackPort, to: &JackPort) -> JackResult<()> {
252        self.connect_or_disconnect_ports(from, to, false)
253    }
254    /// Get a port from the JACK server by its name.
255    ///
256    /// # Errors
257    ///
258    /// - `PortNotFound`: if no port with that name was found
259    /// - `NulError`: if any `&str` argument contains a NUL byte (`\0`).
260    pub fn get_port_by_name(&self, name: &str) -> JackResult<JackPort> {
261        let name = str_to_cstr(name)?;
262        let ptr = unsafe {
263            jack_port_by_name(self.handle, name.as_ptr())
264        };
265        if ptr.is_null() {
266            Err(JackError::PortNotFound)?
267        }
268        unsafe {
269            Ok(JackPort::from_ptr(ptr))
270        }
271    }
272    /// Get all (or a selection of) ports available in the JACK server.
273    ///
274    /// # Parameters
275    ///
276    /// - port_filter: A regular expression used to select ports by name. If `None`, no
277    /// selection based on name will be carried out.
278    /// - type_filter: A regular expression used to select ports by type. If `None`, no
279    /// selection based on type will be carried out.
280    /// - flags_filter: A value used to select ports by their flags. If `None`, no
281    /// selection based on flags will be carried out.
282    ///
283    /// # Errors
284    ///
285    /// - `NulError`: if any `&str` argument contains a NUL byte (`\0`).
286    /// - `ProgrammerError`: if I've made a mistake, or your program is utterly degenerate
287    pub fn get_ports(&self, port_filter: Option<&str>, type_filter: Option<&str>, flags_filter: Option<JackPortFlags>) -> JackResult<Vec<JackPort>> {
288        let mut flags = JackPortFlags::empty();
289        let mut pf = CString::new("").unwrap();
290        let mut tf = CString::new("").unwrap();
291        if let Some(f) = flags_filter {
292            flags = f;
293        }
294        if let Some(f) = port_filter {
295            pf = str_to_cstr(f)?;
296        }
297        if let Some(f) = type_filter {
298            tf = str_to_cstr(f)?;
299        }
300        let mut ptr = unsafe {
301            jack_get_ports(self.handle, pf.as_ptr(), tf.as_ptr(), flags.bits())
302        };
303        if ptr.is_null() {
304            Err(JackError::ProgrammerError)?
305        }
306        let mut cstrs: Vec<&CStr> = vec![];
307        loop {
308            unsafe {
309                if (*ptr).is_null() {
310                    break;
311                }
312                else {
313                    let cs = CStr::from_ptr(*ptr);
314                    cstrs.push(cs);
315                    ptr = ptr.offset(1);
316                }
317            }
318        }
319        let mut ret: Vec<JackPort> = vec![];
320        for st in cstrs {
321            let ptr = unsafe {
322                jack_port_by_name(self.handle, st.as_ptr())
323            };
324            if !ptr.is_null() {
325                unsafe {
326                    ret.push(JackPort::from_ptr(ptr));
327                }
328            }
329        }
330        Ok(ret)
331    }
332    /// Create a new port for the client.
333    ///
334    /// This is an object used for moving data of any type in or out of the client.
335    /// Ports may be connected in various ways.
336    ///
337    /// Each port has a short name. The port's full name contains the name of the client
338    /// concatenated with a colon (:) followed by its short name. The jack_port_name_size()
339    /// is the maximum length of this full name. Exceeding that will cause the port
340    /// registration to fail and return `ProgrammerError`.
341    ///
342    /// The port_name must be unique among all ports owned by this client. If the name is
343    /// not unique, the registration will fail.
344    ///
345    /// All ports have a type, which may be any non-NULL and non-zero length string,
346    /// passed as an argument. Some port types are built into the JACK API, like
347    /// JACK_DEFAULT_AUDIO_TYPE or JACK_DEFAULT_MIDI_TYPE. *[By default, sqa-jack makes a
348    /// JACK_DEFAULT_AUDIO_TYPE port - this will be changeable in later releases.]*
349    ///
350    /// # Errors
351    ///
352    /// - `NulError`: if any `&str` argument contains a NUL byte (`\0`).
353    /// - `PortRegistrationFailed`: if port registration failed (TODO: why could this happen?)
354    pub fn register_port(&mut self, name: &str, ty: JackPortFlags) -> JackResult<JackPort> {
355        let ptr = unsafe {
356            let name = str_to_cstr(name)?;
357            jack_port_register(self.handle, name.as_ptr(), JACK_DEFAULT_AUDIO_TYPE.as_ptr() as *const libc::c_char, ty.bits(), 0)
358        };
359        if ptr.is_null() {
360            Err(JackError::PortRegistrationFailed)?
361        }
362        else {
363            unsafe {
364                Ok(JackPort::from_ptr(ptr))
365            }
366        }
367    }
368    /// Remove the port from the client, disconnecting any existing connections.
369    ///
370    /// # Errors
371    ///
372    /// - `PortNotMine`: if you deregister a port that this client doesn't own
373    /// - `InvalidPort`
374    /// - `UnknownErrorCode`
375    pub fn unregister_port(&mut self, port: JackPort) -> JackResult<()> {
376        let mine = unsafe {
377            jack_port_is_mine(self.handle, port.as_ptr())
378        };
379        if mine == 0 {
380           Err(JackError::PortNotMine)?
381        }
382        let code = unsafe {
383            jack_port_unregister(self.handle, port.as_ptr())
384        };
385        match code {
386            0 => Ok(()),
387            -1 => Err(JackError::InvalidPort)?,
388            x @ _ => Err(JackError::UnknownErrorCode { from: "unregister_port()", code: x })?
389        }
390    }
391}
392impl JackConnection<Deactivated> {
393    /// Open an external client session with a JACK server, optionally specifying
394    /// a number of `JackOpenOptions`.
395    ///
396    /// # Errors
397    ///
398    /// - `JackOpenFailed(status)`: if the connection could not be opened. Contains a
399    /// `JackStatus` detailing what went wrong.
400    /// - `NulError`: if any `&str` argument contains a NUL byte (`\0`).
401    pub fn connect(client_name: &str, opts: Option<JackOpenOptions>) -> JackResult<Self> {
402        let mut status = 0;
403        let opts = opts.map(|x| x.bits()).unwrap_or(JackNullOption);
404        let client = unsafe {
405            let name = str_to_cstr(client_name)?;
406            jack_client_open(name.as_ptr(), opts, &mut status)
407        };
408        if client.is_null() {
409            Err(JackError::JackOpenFailed(
410                JackStatus::from_bits_truncate(status)
411            ))?;
412        }
413        let sample_rate = unsafe { jack_get_sample_rate(client) };
414        Ok(JackConnection {
415            handle: client,
416            sample_rate: sample_rate,
417            _phantom: PhantomData
418        })
419    }
420    /// Register a handler (a struct that implements `JackHandler`).
421    ///
422    /// # Safety
423    ///
424    /// **Warning:** Your handler will never be deallocated / `Drop`ped.
425    ///
426    /// # Errors
427    ///
428    /// - `UnknownErrorCode`
429    pub fn set_handler<F>(&mut self, handler: F) -> JackResult<()> where F: JackHandler {
430        handler::set_handler(self, handler)
431    }
432    /// Tell the Jack server that the program is ready to start processing audio.
433    ///
434    /// # Returns
435    ///
436    /// Returns the `Activated` connection type on success, or the current structure and
437    /// an error on failure.
438    ///
439    /// # Errors
440    ///
441    /// - `UnknownErrorCode`
442    pub fn activate(self) -> Result<JackConnection<Activated>, (Self, JackError)> {
443        unsafe {
444            self.activate_or_deactivate(true)
445        }
446    }
447}
448impl JackConnection<Activated> {
449    /// Tell the Jack server to remove this client from the process graph.
450    /// Also, **disconnect all ports belonging to it**, since inactive clients have no port
451    /// connections.
452    ///
453    /// # Returns
454    ///
455    /// Returns the `Dectivated` connection type on success, or the current structure and
456    /// an error on failure.
457    ///
458    /// # Errors
459    ///
460    /// - `UnknownErrorCode`
461    pub fn deactivate(self) -> Result<JackConnection<Deactivated>, (Self, JackError)> {
462        unsafe {
463            self.activate_or_deactivate(false)
464        }
465    }
466}
467impl<T> Drop for JackConnection<T> {
468    fn drop(&mut self) {
469        unsafe {
470            jack_deactivate(self.handle);
471            jack_client_close(self.handle);
472        }
473    }
474}