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}