1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
//! Rust bindings for JACK, a real-time audio and midi library. These bindings are compatible with
//! all implementations of JACK (Pipewire JACK, JACK1, and JACK2).
//!
//! # Linking, dynamic loading, and packaging
//!
//! libjack is shared among all clients on the system, so there must only be a single
//! system-wide version of it. Applications typically should not ship their own copy of libjack.
//! This is an issue for distributing JACK compatible applications on Windows and macOS. On Linux
//! and BSDs, this is not an issue for system packages because the application and JACK server are
//! both distributed by the system package manager.
//!
//! To handle this, use the `dlopen` Cargo feature, which is enabled by default. This feature
//! dynamically loads libjack at runtime rather than linking libjack at build time. If the
//! user does not have JACK installed at runtime, [Client::new] will return [Error::LoadLibraryError].
//! In this case, have your application show an error message directing the user to install JACK from
//! <https://jackaudio.org/downloads/> and, if available, fall back to another audio API.
//!
//! With the `dlopen` feature, neither libjack nor the JACK pkgconfig file need to be present at build
//! time. This is convenient for automated Windows and macOS builds as well as cross compiling.
//!
//! If your application cannot be used without JACK, Linux and BSD packagers may prefer
//! to link libjack at build time. To do this, disable the `dlopen` feature by using
//! `default-features = false` in your application's Cargo.toml. For example:
//!
//! ```toml
//! [target.'cfg(any(windows, target_vendor = "apple"))'.dependencies]
//! # Load libjack at runtime.
//! jack = "0.9"
//!
//! [target.'cfg(not(any(windows, target_vendor = "apple")))'.dependencies]
//! # Link libjack at build time.
//! jack = { version = "0.9", default-features = false }
//! ```
//!
//! You can set the environment variable `RUST_JACK_DLOPEN` to `on` to enable the `dlopen` feature
//! without needing to edit your application's Cargo.toml. This can be useful for cross compiling
//! to Linux with a different CPU architecture.
//!
//! # Server
//!
//! JACK provides a high priority server to manipulate audio and midi across applications. The rust
//! jack crate does not provide server creation functionality, so a server has to be set up with the
//! `jackd` commandline tool, `qjackctl` the gui tool, or another method.
//!
//! # Client
//!
//! Typically, applications connect clients to the server. For the rust jack crate, a connection can
//! be made with `client::Client::new`, which returns a `client::Client`.
//!
//! The `Client` can query the server for information, register ports, and manage connections for
//! ports.
//!
//! To commence processing audio/midi and other information in real-time, rust jack provides the
//! `Client::activate_async`, which consumes the `Client`, an object that implements
//! `NotificationHandler` and an object that implements `ProcessHandler` and returns a
//! `AsyncClient`. `AsyncClient` processes the data in real-time with the provided handlers.
//!
//! # Port
//!
//! A `Client` may obtain port information through the `Client::port_by_id` and
//! `Client::port_by_name` methods. These ports can be used to manage connections or to obtain port
//! metadata, though their port data (audio buffers and midi buffers) cannot be accessed safely.
//!
//! Ports can be registered with the `Client::register_port` method. This requires a `PortSpec`. The
//! jack crate comes with common specs such as `AudioIn`, `AudioOut`, `MidiIn`, and
//! `MidiOut`.
//!
//! To access the data of registered ports, use their specialized methods within a `ProcessHandler`
//! callback. For example, `Port<AudioIn>::as_mut_slice` returns a audio buffer that can be written
//! to.
pub use crate::client::{
AsyncClient, Client, ClientOptions, ClientStatus, ClosureProcessHandler, CycleTimes,
InternalClientID, NotificationHandler, ProcessHandler, ProcessScope, CLIENT_NAME_SIZE,
};
pub use crate::jack_enums::{Control, Error, LatencyType};
pub use crate::port::{
AudioIn, AudioOut, MidiIn, MidiIter, MidiOut, MidiWriter, Port, PortFlags, PortSpec, RawMidi,
Unowned, PORT_NAME_SIZE, PORT_TYPE_SIZE,
};
pub use crate::primitive_types::{Frames, PortId, Time};
pub use crate::ringbuffer::{RingBuffer, RingBufferReader, RingBufferWriter};
pub use crate::transport::{
Transport, TransportBBT, TransportBBTValidationError, TransportPosition, TransportState,
TransportStatePosition,
};
#[cfg(feature = "dlopen")]
use lazy_static::lazy_static;
//only expose metadata if enabled
#[cfg(feature = "metadata")]
pub use crate::properties::*;
/// Create and manage client connections to a JACK server.
mod client;
/// Create and manage JACK ring buffers.
mod ringbuffer;
/// Enum types in jack.
mod jack_enums;
mod jack_utils;
/// Types for safely interacting with port data from JACK.
mod port;
/// Platform independent types.
mod primitive_types;
/// Transport.
mod transport;
/// Properties
mod properties;
#[cfg(feature = "dlopen")]
lazy_static! {
pub(crate) static ref LIB: &'static jack_sys::JackLib = {
let j = LIB_RESULT.as_ref().unwrap();
j
};
static ref LIB_RESULT: Result<jack_sys::JackLib, dlib::DlError> =
unsafe { jack_sys::JackLib::open(jack_sys::JACK_LIB) };
}
#[cfg(all(feature = "dlopen", feature = "metadata"))]
lazy_static! {
pub(crate) static ref METADATA: jack_sys::JackMetadata =
unsafe { jack_sys::JackMetadata::open(jack_sys::JACK_LIB).unwrap() };
}
#[cfg(all(feature = "dlopen", feature = "metadata"))]
lazy_static! {
pub(crate) static ref UUID: jack_sys::JackUuid =
unsafe { jack_sys::JackUuid::open(jack_sys::JACK_LIB).unwrap() };
}
/// Dynamically loads the JACK library. This is libjack.so on Linux and
/// libjack.dll on Windows.
#[cfg(feature = "dlopen")]
pub fn load_jack_library() -> Result<(), Error> {
LIB_RESULT
.as_ref()
.map(|_| ())
.map_err(|e| Error::LoadLibraryError(format!("{}", e)))
}
/// Return JACK's current system time in microseconds, using the JACK clock
/// source.
pub fn get_time() -> primitive_types::Time {
#[cfg(feature = "dlopen")]
let t = unsafe { (LIB.jack_get_time)() };
#[cfg(not(feature = "dlopen"))]
let t = unsafe { jack_sys::jack_get_time() };
t
}
#[cfg(test)]
mod test {
use super::*;
use std::{thread, time};
#[test]
fn time_can_get_time() {
get_time();
}
#[test]
fn time_is_monotonically_increasing() {
let initial_t = get_time();
thread::sleep(time::Duration::from_millis(100));
let later_t = get_time();
assert!(initial_t < later_t, "failed {} < {}", initial_t, later_t);
}
}