use jack_sys as j;
use lazy_static::lazy_static;
use std::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
use std::hash::{Hash, Hasher};
use std::marker::Sized;
use std::sync::Weak;
use std::{ffi, fmt, iter};
use crate::{Error, Frames, LatencyType, PortFlags};
lazy_static! {
pub static ref PORT_NAME_SIZE: usize = unsafe { j::jack_port_name_size() - 1 } as usize;
pub static ref PORT_TYPE_SIZE: usize = unsafe { j::jack_port_type_size() - 1 } as usize;
}
pub unsafe trait PortSpec: Sized {
fn jack_port_type(&self) -> &str;
fn jack_flags(&self) -> PortFlags;
fn jack_buffer_size(&self) -> libc::c_ulong;
}
pub struct Port<PS> {
spec: PS,
client_ptr: *mut j::jack_client_t,
port_ptr: *mut j::jack_port_t,
client_life: Weak<()>,
}
unsafe impl<PS: PortSpec + Send> Send for Port<PS> {}
unsafe impl<PS: PortSpec + Sync> Sync for Port<PS> {}
impl<PS> Port<PS> {
pub fn spec(&self) -> &PS {
&self.spec
}
pub fn clone_unowned(&self) -> Port<Unowned> {
Port {
spec: Unowned,
client_ptr: self.client_ptr(),
port_ptr: self.raw(),
client_life: self.client_life.clone(),
}
}
pub fn name(&self) -> Result<String, Error> {
self.check_client_life()?;
let s = unsafe {
ffi::CStr::from_ptr(j::jack_port_name(self.raw()))
.to_string_lossy()
.to_string()
};
Ok(s)
}
pub fn short_name(&self) -> Result<String, Error> {
self.check_client_life()?;
let s = unsafe {
ffi::CStr::from_ptr(j::jack_port_short_name(self.raw()))
.to_string_lossy()
.to_string()
};
Ok(s)
}
pub fn flags(&self) -> PortFlags {
let bits = unsafe { j::jack_port_flags(self.raw()) };
PortFlags::from_bits(bits as j::Enum_JackPortFlags).unwrap()
}
pub fn port_type(&self) -> Result<String, Error> {
self.check_client_life()?;
let s = unsafe {
ffi::CStr::from_ptr(j::jack_port_type(self.raw()))
.to_string_lossy()
.to_string()
};
Ok(s)
}
pub fn connected_count(&self) -> Result<usize, Error> {
self.check_client_life()?;
let n = unsafe { j::jack_port_connected(self.raw()) };
Ok(n as usize)
}
pub fn is_connected_to(&self, port_name: &str) -> Result<bool, Error> {
self.check_client_life()?;
let res = unsafe {
let port_name = ffi::CString::new(port_name).unwrap();
j::jack_port_connected_to(self.raw(), port_name.as_ptr())
};
match res {
0 => Ok(false),
_ => Ok(true),
}
}
pub fn aliases(&self) -> Result<Vec<String>, Error> {
self.check_client_life()?;
let mut a: Vec<libc::c_char> = iter::repeat(0).take(*PORT_NAME_SIZE + 1).collect();
let mut b = a.clone();
unsafe {
let mut ptrs: [*mut libc::c_char; 2] = [a.as_mut_ptr(), b.as_mut_ptr()];
j::jack_port_get_aliases(self.raw(), ptrs.as_mut_ptr());
};
Ok([a, b]
.iter()
.map(|p| p.as_ptr())
.map(|p| unsafe { ffi::CStr::from_ptr(p).to_string_lossy().into_owned() })
.filter(|s| !s.is_empty())
.collect())
}
pub fn is_monitoring_input(&self) -> Result<bool, Error> {
self.check_client_life()?;
match unsafe { j::jack_port_monitoring_input(self.raw()) } {
0 => Ok(false),
_ => Ok(true),
}
}
pub fn request_monitor(&self, enable_monitor: bool) -> Result<(), Error> {
self.check_client_life()?;
let onoff = if enable_monitor { 1 } else { 0 };
let res = unsafe { j::jack_port_request_monitor(self.raw(), onoff) };
match res {
0 => Ok(()),
_ => Err(Error::PortMonitorError),
}
}
pub fn ensure_monitor(&self, enable_monitor: bool) -> Result<(), Error> {
self.check_client_life()?;
let onoff = if enable_monitor { 1 } else { 0 };
let res = unsafe { j::jack_port_ensure_monitor(self.raw(), onoff) };
match res {
0 => Ok(()),
_ => Err(Error::PortMonitorError),
}
}
pub fn set_name(&mut self, short_name: &str) -> Result<(), Error> {
self.check_client_life()?;
let short_name = ffi::CString::new(short_name).unwrap();
let res = unsafe { j::jack_port_set_name(self.raw(), short_name.as_ptr()) };
match res {
0 => Ok(()),
_ => Err(Error::PortNamingError),
}
}
pub fn set_alias(&mut self, alias: &str) -> Result<(), Error> {
self.check_client_life()?;
let alias = ffi::CString::new(alias).unwrap();
let res = unsafe { j::jack_port_set_alias(self.raw(), alias.as_ptr()) };
match res {
0 => Ok(()),
_ => Err(Error::PortAliasError),
}
}
pub fn unset_alias(&mut self, alias: &str) -> Result<(), Error> {
self.check_client_life()?;
let alias = ffi::CString::new(alias).unwrap();
let res = unsafe { j::jack_port_unset_alias(self.raw(), alias.as_ptr()) };
match res {
0 => Ok(()),
_ => Err(Error::PortAliasError),
}
}
pub unsafe fn from_raw(
spec: PS,
client_ptr: *mut j::jack_client_t,
port_ptr: *mut j::jack_port_t,
client_life: Weak<()>,
) -> Self {
Port {
spec,
port_ptr,
client_ptr,
client_life,
}
}
#[inline(always)]
pub fn client_ptr(&self) -> *mut j::jack_client_t {
self.client_ptr
}
#[inline(always)]
pub fn raw(&self) -> *mut j::jack_port_t {
self.port_ptr
}
#[inline(always)]
pub unsafe fn buffer(&self, n_frames: Frames) -> *mut libc::c_void {
j::jack_port_get_buffer(self.port_ptr, n_frames)
}
#[inline(always)]
pub fn set_latency_range(&self, mode: LatencyType, range: (Frames, Frames)) {
let mut ffi_range = j::Struct__jack_latency_range {
min: range.0,
max: range.1,
};
unsafe { j::jack_port_set_latency_range(self.port_ptr, mode.to_ffi(), &mut ffi_range) };
}
#[inline(always)]
pub fn get_latency_range(&self, mode: LatencyType) -> (Frames, Frames) {
let mut ffi_range = j::Struct__jack_latency_range { min: 0, max: 0 };
unsafe { j::jack_port_get_latency_range(self.port_ptr, mode.to_ffi(), &mut ffi_range) };
return (ffi_range.min, ffi_range.max);
}
fn check_client_life(&self) -> Result<(), Error> {
self.client_life
.upgrade()
.map(|_| ())
.ok_or(Error::ClientIsNoLongerAlive)
}
}
#[derive(Debug, Default)]
pub struct Unowned;
unsafe impl PortSpec for Unowned {
fn jack_port_type(&self) -> &str {
""
}
fn jack_flags(&self) -> PortFlags {
PortFlags::empty()
}
fn jack_buffer_size(&self) -> libc::c_ulong {
unreachable!()
}
}
#[derive(Debug)]
struct PortInfo {
name: String,
connections: usize,
port_type: String,
port_flags: PortFlags,
aliases: Vec<String>,
}
impl PortInfo {
fn new<PS: PortSpec>(p: &Port<PS>) -> PortInfo {
let s = p.spec();
PortInfo {
name: p
.name()
.unwrap_or_else(|_| String::from("client not alive")),
connections: p.connected_count().unwrap_or(0),
port_type: s.jack_port_type().to_owned(),
port_flags: s.jack_flags(),
aliases: p.aliases().unwrap_or_else(|_| Vec::new()),
}
}
}
impl<PS: PortSpec> fmt::Debug for Port<PS> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "{:?}", PortInfo::new(self))
}
}
impl<PS> PartialEq for Port<PS> {
fn eq(&self, other: &Self) -> bool {
self.port_ptr == other.port_ptr
}
}
impl<PS> Eq for Port<PS> {}
impl<PS> PartialOrd for Port<PS> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<PS> Ord for Port<PS> {
fn cmp(&self, other: &Self) -> Ordering {
self.port_ptr.cmp(&other.port_ptr)
}
}
impl<PS> Hash for Port<PS> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.port_ptr.hash(state);
}
}
impl Clone for Port<Unowned> {
fn clone(&self) -> Self {
self.clone_unowned()
}
}