1 use std::{ffi, fmt, iter};
2 use std::marker::Sized;
3 
4 use libc;
5 
6 use jack_enums::JackErr;
7 use jack_sys as j;
8 use port::port_flags::PortFlags;
9 use primitive_types as pt;
10 
11 lazy_static! {
12     /// The maximum string length for port names.
13     pub static ref PORT_NAME_SIZE: usize = unsafe { j::jack_port_name_size() - 1 } as usize;
14 
15     /// The maximum string length for jack type names.
16     pub static ref PORT_TYPE_SIZE: usize = unsafe { j::jack_port_type_size() - 1 } as usize;
17 }
18 
19 /// Defines the configuration for a certain port to JACK, ie 32 bit floating
20 /// audio input, 8 bit raw
21 /// midi output, etc...
22 pub unsafe trait PortSpec: Sized {
23     /// String used by JACK upon port creation to identify the port
24     /// type.
jack_port_type(&self) -> &str25     fn jack_port_type(&self) -> &str;
26 
27     /// Flags used by jack upon port creation.
jack_flags(&self) -> PortFlags28     fn jack_flags(&self) -> PortFlags;
29 
30     /// Size used by jack upon port creation.
jack_buffer_size(&self) -> libc::c_ulong31     fn jack_buffer_size(&self) -> libc::c_ulong;
32 }
33 
34 /// An endpoint to interact with JACK data streams, for audio, midi,
35 /// etc...
36 ///
37 /// The `Port` struct contains mostly metadata and exposes data as raw
38 /// pointers. For a better data
39 /// consumption/production API, see the `AudioInPort`, `AudioOutPort`,
40 /// `MidiInPort`, and
41 /// `MidiOutPort`.
42 ///
43 /// Most JACK functionality is exposed, including the raw pointers, but it
44 /// should be possible to
45 /// create a client without the need for calling `unsafe` `Port` methods.
46 pub struct Port<PS: PortSpec> {
47     spec: PS,
48     client_ptr: *mut j::jack_client_t,
49     port_ptr: *mut j::jack_port_t,
50 }
51 
52 unsafe impl<PS: PortSpec> Send for Port<PS> {}
53 unsafe impl<PS: PortSpec> Sync for Port<PS> {}
54 
55 impl<PS: PortSpec> Port<PS> {
56     /// Returns the spec that was used to create this port.
spec(&self) -> &PS57     pub fn spec(&self) -> &PS {
58         &self.spec
59     }
60 
61     /// Return a copy of port as an unowned port that can still be used for
62     /// querying information.
clone_unowned(&self) -> Port<Unowned>63     pub fn clone_unowned(&self) -> Port<Unowned> {
64         Port {
65             spec: Unowned,
66             client_ptr: self.client_ptr(),
67             port_ptr: self.as_ptr(),
68         }
69     }
70 
71     /// Returns the full name of the port, including the "client_name:" prefix.
name<'a>(&'a self) -> &'a str72     pub fn name<'a>(&'a self) -> &'a str {
73         unsafe {
74             ffi::CStr::from_ptr(j::jack_port_name(self.as_ptr()))
75                 .to_str()
76                 .unwrap()
77         }
78     }
79 
80     /// Returns the short name of the port, it excludes the "client_name:"
81     /// prefix.
short_name<'a>(&'a self) -> &'a str82     pub fn short_name<'a>(&'a self) -> &'a str {
83         unsafe {
84             ffi::CStr::from_ptr(j::jack_port_short_name(self.as_ptr()))
85                 .to_str()
86                 .unwrap()
87         }
88     }
89 
90     /// The flags for the port. These are set when the port is registered with
91     /// its client.
flags(&self) -> PortFlags92     pub fn flags(&self) -> PortFlags {
93         let bits = unsafe { j::jack_port_flags(self.as_ptr()) };
94         PortFlags::from_bits(bits as j::Enum_JackPortFlags).unwrap()
95     }
96 
97     /// The port type. JACK's built in types include `"32 bit float mono
98     /// audio`" and `"8 bit raw
99     /// midi"`. Custom types may also be used.
port_type<'a>(&self) -> &'a str100     pub fn port_type<'a>(&self) -> &'a str {
101         unsafe {
102             ffi::CStr::from_ptr(j::jack_port_type(self.as_ptr()))
103                 .to_str()
104                 .unwrap()
105         }
106     }
107 
108     /// Number of ports connected to/from `&self`.
connected_count(&self) -> usize109     pub fn connected_count(&self) -> usize {
110         let n = unsafe { j::jack_port_connected(self.as_ptr()) };
111         n as usize
112     }
113 
114     /// Returns `true` if the port is directly connected to a port with the
115     /// name `port_name`.
is_connected_to(&self, port_name: &str) -> bool116     pub fn is_connected_to(&self, port_name: &str) -> bool {
117         let res = unsafe {
118             let port_name = ffi::CString::new(port_name).unwrap();
119             j::jack_port_connected_to(self.as_ptr(), port_name.as_ptr())
120         };
121         match res {
122             0 => false,
123             _ => true,
124         }
125     }
126 
127     /// Remove connections to/from port `self`.
disconnect(&self) -> Result<(), JackErr>128     pub fn disconnect(&self) -> Result<(), JackErr> {
129         let res = unsafe { j::jack_port_disconnect(self.client_ptr(), self.as_ptr()) };
130         match res {
131             0 => Ok(()),
132             _ => Err(JackErr::PortDisconnectionError),
133         }
134     }
135 
136     /// Get the alias names for `self`.
137     ///
138     /// Will return up to 2 strings.
aliases(&self) -> Vec<String>139     pub fn aliases(&self) -> Vec<String> {
140         let mut a: Vec<libc::c_char> = iter::repeat(0).take(*PORT_NAME_SIZE + 1).collect();
141         let mut b = a.clone();
142         unsafe {
143             let mut ptrs: [*mut libc::c_char; 2] = [a.as_mut_ptr(), b.as_mut_ptr()];
144             j::jack_port_get_aliases(self.as_ptr(), ptrs.as_mut_ptr());
145         };
146         [a, b]
147             .iter()
148             .map(|p| p.as_ptr())
149             .map(|p| unsafe {
150                 ffi::CStr::from_ptr(p).to_string_lossy().into_owned()
151             })
152             .filter(|s| s.len() > 0)
153             .collect()
154     }
155 
156     /// Returns `true` if monitoring has been requested for `self`.
is_monitoring_input(&self) -> bool157     pub fn is_monitoring_input(&self) -> bool {
158         match unsafe { j::jack_port_monitoring_input(self.as_ptr()) } {
159             0 => false,
160             _ => true,
161         }
162     }
163 
164     /// Turn input monitoring for the port on or off.
165     ///
166     /// This only works if the port has the `CAN_MONITOR` flag set.
request_monitor(&self, enable_monitor: bool) -> Result<(), JackErr>167     pub fn request_monitor(&self, enable_monitor: bool) -> Result<(), JackErr> {
168         let onoff = match enable_monitor {
169             true => 1,
170             false => 0,
171         };
172         let res = unsafe { j::jack_port_request_monitor(self.as_ptr(), onoff) };
173         match res {
174             0 => Ok(()),
175             _ => Err(JackErr::PortMonitorError),
176         }
177     }
178 
179     /// If the `CAN_MONITOR` flag is set for the port, then input monitoring is
180     /// turned on if it was
181     /// off, and turns it off if only one request has been made to turn it on.
182     /// Otherwise it does
183     /// nothing.
ensure_monitor(&self, enable_monitor: bool) -> Result<(), JackErr>184     pub fn ensure_monitor(&self, enable_monitor: bool) -> Result<(), JackErr> {
185         let onoff = match enable_monitor {
186             true => 1,
187             false => 0,
188         };
189         let res = unsafe { j::jack_port_ensure_monitor(self.as_ptr(), onoff) };
190         match res {
191             0 => Ok(()),
192             _ => Err(JackErr::PortMonitorError),
193         }
194     }
195 
196     /// Set's the short name of the port. If the full name is longer than
197     /// `PORT_NAME_SIZE`, then it
198     /// will be truncated.
set_name(&mut self, short_name: &str) -> Result<(), JackErr>199     pub fn set_name(&mut self, short_name: &str) -> Result<(), JackErr> {
200         let short_name = ffi::CString::new(short_name).unwrap();
201         let res = unsafe { j::jack_port_set_name(self.as_ptr(), short_name.as_ptr()) };
202         match res {
203             0 => Ok(()),
204             _ => Err(JackErr::PortNamingError),
205         }
206     }
207 
208     /// Sets `alias` as an alias for `self`.
209     ///
210     /// May be called at any time. If the alias is longer than
211     /// `PORT_NAME_SIZE`, it will be
212     /// truncated.
213     ///
214     /// After a successful call, and until JACK exists, or the alias is unset,
215     /// `alias` may be used
216     /// as an alternate name for the port.
217     ///
218     /// Ports can have up to two aliases - if both are already set, this
219     /// function will return an
220     /// error.
set_alias(&mut self, alias: &str) -> Result<(), JackErr>221     pub fn set_alias(&mut self, alias: &str) -> Result<(), JackErr> {
222         let alias = ffi::CString::new(alias).unwrap();
223         let res = unsafe { j::jack_port_set_alias(self.as_ptr(), alias.as_ptr()) };
224         match res {
225             0 => Ok(()),
226             _ => Err(JackErr::PortAliasError),
227         }
228     }
229 
230     /// Remove `alias` as an alias for port. May be called at any time.
231     ///
232     /// After a successful call, `alias` can no longer be used as an alternate
233     /// name for `self`.
unset_alias(&mut self, alias: &str) -> Result<(), JackErr>234     pub fn unset_alias(&mut self, alias: &str) -> Result<(), JackErr> {
235         let alias = ffi::CString::new(alias).unwrap();
236         let res = unsafe { j::jack_port_unset_alias(self.as_ptr(), alias.as_ptr()) };
237         match res {
238             0 => Ok(()),
239             _ => Err(JackErr::PortAliasError),
240         }
241     }
242 
243     /// Remove the port from the client, disconnecting any existing
244     /// connections.  The port must have
245     /// been created with the provided client.
unregister(self) -> Result<(), JackErr>246     pub fn unregister(self) -> Result<(), JackErr> {
247         let res = unsafe { j::jack_port_unregister(self.client_ptr, self.as_ptr()) };
248         match res {
249             0 => Ok(()),
250             _ => Err(JackErr::PortDisconnectionError),
251         }
252     }
253 
254     /// Create a Port from raw JACK pointers.
255     ///
256     /// This is mostly for use within the jack crate itself.
from_raw( spec: PS, client_ptr: *mut j::jack_client_t, port_ptr: *mut j::jack_port_t, ) -> Self257     pub unsafe fn from_raw(
258         spec: PS,
259         client_ptr: *mut j::jack_client_t,
260         port_ptr: *mut j::jack_port_t,
261     ) -> Self {
262         Port {
263             spec: spec,
264             port_ptr: port_ptr,
265             client_ptr: client_ptr,
266         }
267     }
268 
269     /// Obtain the client pointer that spawned this port.
270     ///
271     /// This is mostly for use within the jack crate itself.
272     #[inline(always)]
client_ptr(&self) -> *mut j::jack_client_t273     pub fn client_ptr(&self) -> *mut j::jack_client_t {
274         self.client_ptr
275     }
276 
277     /// Obtain the ffi port pointer.
278     ///
279     /// This is mostly for use within the jack crate itself.
280     #[inline(always)]
as_ptr(&self) -> *mut j::jack_port_t281     pub fn as_ptr(&self) -> *mut j::jack_port_t {
282         self.port_ptr
283     }
284 
285     /// Obtain the buffer that the Port is holding. For standard audio and midi
286     /// ports, consider
287     /// using the `AudioInPort`, `AudioOutPort`, `MidiInPort`, or `MidiOutPort`
288     /// adapter. For more
289     /// custom data, consider implementing your own adapter that safely uses
290     /// the `Port::buffer`
291     /// method.
292     #[inline(always)]
buffer(&self, n_frames: pt::JackFrames) -> *mut libc::c_void293     pub unsafe fn buffer(&self, n_frames: pt::JackFrames) -> *mut libc::c_void {
294         j::jack_port_get_buffer(self.port_ptr, n_frames)
295     }
296 }
297 
298 /// `PortSpec` for a port that holds has no readable or writeable data from
299 /// JACK on the created
300 /// client. It can be used for obtaining information about external ports.
301 #[derive(Debug, Default)]
302 pub struct Unowned;
303 
304 /// `Port<UnownedSpec>` - Port that holds no data from Jack, though it can
305 /// still be used to query
306 /// information.
307 pub type UnownedPort = Port<Unowned>;
308 
309 unsafe impl PortSpec for Unowned {
310     /// Panics on call since the `Unowned` spec can't be used to create ports.
jack_port_type(&self) -> &str311     fn jack_port_type(&self) -> &str {
312         ""
313     }
314 
315     /// Panics on call since the `Unowned` spec can't be used to create ports.
jack_flags(&self) -> PortFlags316     fn jack_flags(&self) -> PortFlags {
317         PortFlags::empty()
318     }
319 
320     /// Panics on call since the `Unowned` spec can't be used to create ports.
jack_buffer_size(&self) -> libc::c_ulong321     fn jack_buffer_size(&self) -> libc::c_ulong {
322         unreachable!()
323     }
324 }
325 
326 #[derive(Debug)]
327 struct PortInfo {
328     name: String,
329     connections: usize,
330     port_type: String,
331     port_flags: PortFlags,
332     aliases: Vec<String>,
333 }
334 
335 impl PortInfo {
new<PS: PortSpec>(p: &Port<PS>) -> PortInfo336     fn new<PS: PortSpec>(p: &Port<PS>) -> PortInfo {
337         let s = p.spec();
338         PortInfo {
339             name: p.name().into(),
340             connections: p.connected_count(),
341             port_type: s.jack_port_type().to_string(),
342             port_flags: s.jack_flags(),
343             aliases: p.aliases(),
344         }
345     }
346 }
347 
348 impl<PS: PortSpec> fmt::Debug for Port<PS> {
fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>349     fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
350         write!(f, "{:?}", PortInfo::new(self))
351     }
352 }
353