Source code
Revision control
Copy as Markdown
Other Tools
#![allow(non_upper_case_globals, dead_code)]
use std::ffi::{CStr, CString};
use std::ops::Index;
use std::{ptr, slice, str};
use libc::{c_void, size_t};
use jack_sys::{
jack_activate, jack_client_close, jack_client_open, jack_client_t, jack_connect,
jack_deactivate, jack_free, jack_get_ports, jack_get_time, jack_midi_clear_buffer,
jack_midi_data_t, jack_midi_event_get, jack_midi_event_reserve, jack_midi_event_t,
jack_midi_get_event_count, jack_nframes_t, jack_port_get_buffer, jack_port_name,
jack_port_register, jack_port_t, jack_port_unregister, jack_ringbuffer_create,
jack_ringbuffer_free, jack_ringbuffer_read, jack_ringbuffer_read_space, jack_ringbuffer_t,
jack_ringbuffer_write, jack_set_process_callback,
};
pub const JACK_DEFAULT_MIDI_TYPE: &[u8] = b"8 bit raw midi\0";
bitflags! {
#[derive(Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
pub struct JackOpenOptions: u32 {
const NoStartServer = 1;
const UseExactName = 2;
const ServerName = 4;
const SessionID = 32;
}
}
bitflags! {
#[derive(Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
pub struct PortFlags: u32 {
const PortIsInput = 1;
const PortIsOutput = 2;
const PortIsPhysical = 4;
const PortCanMonitor = 8;
const PortIsTerminal = 16;
}
}
// TODO: hide this type
pub type ProcessCallback = extern "C" fn(nframes: jack_nframes_t, arg: *mut c_void) -> i32;
pub struct Client {
p: *mut jack_client_t,
}
unsafe impl Send for Client {}
impl Client {
pub fn get_time() -> u64 {
unsafe { jack_get_time() }
}
pub fn open(name: &str, options: JackOpenOptions) -> Result<Client, ()> {
let c_name = CString::new(name)
.ok()
.expect("client name must not contain null bytes");
let result = unsafe { jack_client_open(c_name.as_ptr(), options.bits(), ptr::null_mut()) };
if result.is_null() {
Err(())
} else {
Ok(Client { p: result })
}
}
pub fn get_midi_ports(&self, flags: PortFlags) -> PortInfos {
let ports_ptr = unsafe {
jack_get_ports(
self.p,
ptr::null_mut(),
JACK_DEFAULT_MIDI_TYPE.as_ptr() as *const _,
flags.bits() as _,
)
};
let slice = if ports_ptr.is_null() {
&[]
} else {
unsafe {
let count = (0isize..)
.find(|i| (*ports_ptr.offset(*i)).is_null())
.unwrap() as usize;
slice::from_raw_parts(ports_ptr, count)
}
};
PortInfos { p: slice }
}
pub fn register_midi_port(&mut self, name: &str, flags: PortFlags) -> Result<MidiPort, ()> {
let c_name = CString::new(name)
.ok()
.expect("port name must not contain null bytes");
let result = unsafe {
jack_port_register(
self.p,
c_name.as_ptr(),
JACK_DEFAULT_MIDI_TYPE.as_ptr() as *const _,
flags.bits() as _,
0,
)
};
if result.is_null() {
Err(())
} else {
Ok(MidiPort { p: result })
}
}
/// This can not be implemented in Drop, because it needs a reference
/// to the client. But it consumes the MidiPort.
pub fn unregister_midi_port(&mut self, client: MidiPort) {
unsafe { jack_port_unregister(self.p, client.p) };
}
pub fn activate(&mut self) {
unsafe { jack_activate(self.p) };
}
pub fn deactivate(&mut self) {
unsafe { jack_deactivate(self.p) };
}
/// The code in the supplied function must be suitable for real-time
/// execution. That means that it cannot call functions that might block
/// for a long time. This includes all I/O functions (disk, TTY, network),
/// malloc, free, printf, pthread_mutex_lock, sleep, wait, poll, select,
/// pthread_join, pthread_cond_wait, etc, etc.
pub fn set_process_callback(&mut self, callback: ProcessCallback, data: *mut c_void) {
unsafe { jack_set_process_callback(self.p, Some(callback), data) };
}
pub fn connect(&mut self, source_port: &CStr, destination_port: &CStr) -> Result<(), ()> {
let rc = unsafe { jack_connect(self.p, source_port.as_ptr(), destination_port.as_ptr()) };
if rc == 0 {
Ok(())
} else {
Err(()) // TODO: maybe handle EEXIST explicitly
}
}
}
impl Drop for Client {
fn drop(&mut self) {
unsafe { jack_client_close(self.p) };
}
}
#[cfg(not(any(target_arch = "aarch64", target_arch = "arm")))]
type PortInfo = i8;
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
type PortInfo = u8;
pub struct PortInfos<'a> {
p: &'a [*const PortInfo],
}
unsafe impl<'a> Send for PortInfos<'a> {}
impl<'a> PortInfos<'a> {
pub fn count(&self) -> usize {
self.p.len()
}
pub fn get_c_name(&self, index: usize) -> &CStr {
let ptr = self.p[index];
unsafe { CStr::from_ptr(ptr) }
}
}
impl<'a> Index<usize> for PortInfos<'a> {
type Output = str;
fn index(&self, index: usize) -> &Self::Output {
let slice = self.get_c_name(index).to_bytes();
str::from_utf8(slice)
.ok()
.expect("Error converting port name to UTF8")
}
}
impl<'a> Drop for PortInfos<'a> {
fn drop(&mut self) {
if self.p.len() > 0 {
unsafe { jack_free(self.p.as_ptr() as *mut _) }
}
}
}
pub struct MidiPort {
p: *mut jack_port_t,
}
unsafe impl Send for MidiPort {}
impl MidiPort {
pub fn get_name(&self) -> &CStr {
unsafe { CStr::from_ptr(jack_port_name(self.p)) }
}
pub fn get_midi_buffer(&self, nframes: jack_nframes_t) -> MidiBuffer {
let buf = unsafe { jack_port_get_buffer(self.p, nframes) };
MidiBuffer { p: buf }
}
}
pub struct MidiBuffer {
p: *mut c_void,
}
impl MidiBuffer {
pub fn get_event_count(&self) -> u32 {
unsafe { jack_midi_get_event_count(self.p) }
}
pub unsafe fn get_event(&self, ev: *mut jack_midi_event_t, index: u32) {
jack_midi_event_get(ev, self.p, index);
}
pub fn clear(&mut self) {
unsafe { jack_midi_clear_buffer(self.p) }
}
pub fn event_reserve(
&mut self,
time: jack_nframes_t,
data_size: usize,
) -> *mut jack_midi_data_t {
unsafe { jack_midi_event_reserve(self.p, time, data_size as size_t) }
}
}
pub struct Ringbuffer {
p: *mut jack_ringbuffer_t,
}
unsafe impl Send for Ringbuffer {}
impl Ringbuffer {
pub fn new(size: usize) -> Ringbuffer {
let result = unsafe { jack_ringbuffer_create(size as size_t) };
Ringbuffer { p: result }
}
pub fn get_read_space(&self) -> usize {
unsafe { jack_ringbuffer_read_space(self.p) as usize }
}
pub fn read(&mut self, destination: *mut u8, count: usize) -> usize {
let bytes_read =
unsafe { jack_ringbuffer_read(self.p, destination as *mut _, count as size_t) };
bytes_read as usize
}
pub fn write(&mut self, source: &[u8]) -> usize {
unsafe {
jack_ringbuffer_write(self.p, source.as_ptr() as *const _, source.len() as size_t)
as usize
}
}
}
impl Drop for Ringbuffer {
fn drop(&mut self) {
unsafe { jack_ringbuffer_free(self.p) }
}
}