#![allow(non_upper_case_globals, dead_code)]
use std::ffi::{CStr, CString};
use std::ops::Index;
use std::os::raw::c_char;
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;
}
}
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).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 raw: *mut *const c_char = unsafe {
jack_get_ports(
self.p,
ptr::null_mut(),
JACK_DEFAULT_MIDI_TYPE.as_ptr() as *const c_char,
flags.bits() as _,
)
};
let slice: &[*const c_char] = if raw.is_null() {
&[]
} else {
unsafe {
let mut n = 0usize;
while !(*raw.add(n)).is_null() {
n += 1;
}
slice::from_raw_parts(raw as *const *const c_char, n)
}
};
PortInfos { raw, p: slice }
}
pub fn register_midi_port(&mut self, name: &str, flags: PortFlags) -> Result<MidiPort, ()> {
let c_name = CString::new(name).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 })
}
}
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) };
}
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(()) }
}
}
impl Drop for Client {
fn drop(&mut self) {
unsafe { jack_client_close(self.p) };
}
}
pub struct PortInfos<'a> {
raw: *mut *const c_char,
p: &'a [*const c_char],
}
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: *const c_char = 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).expect("Error converting port name to UTF8")
}
}
impl<'a> Drop for PortInfos<'a> {
fn drop(&mut self) {
if !self.raw.is_null() {
unsafe { jack_free(self.raw 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) }
}
}