rust_jack/client.rs
1use std::{ffi, ptr};
2use jack_sys as j;
3use callbacks;
4use port;
5use enums::*;
6use flags::*;
7use port::*;
8use utils;
9use callbacks::JackHandler;
10
11#[derive(Clone, Copy, Debug)]
12pub struct CycleTimes {
13 pub current_frames: u32,
14 pub current_usecs: u64,
15 pub next_usecs: u64,
16 pub period_usecs: f32,
17}
18
19/// A client to interact with a Jack server.
20///
21/// # Example
22/// ```
23/// // TODO: make example
24/// ```
25#[derive(Debug)]
26pub struct Client<T: JackHandler> {
27 client: *mut j::jack_client_t,
28 handler: *mut T,
29 status: ClientStatus,
30}
31
32impl<T: JackHandler> Client<T> {
33 /// The maximum length of the Jack client name string. Unlike the "C" Jack
34 /// API, this does not take into account the final `NULL` character and
35 /// instead corresponds directly to `.len()`. This value is constant.
36 pub fn name_size() -> usize {
37 let s = unsafe { j::jack_client_name_size() - 1 };
38 s as usize
39 }
40
41 /// The buffer size of a port type
42 ///
43 /// # Unsafe
44 ///
45 /// * This function may only be called in a buffer size callback.
46 pub unsafe fn type_buffer_size(&self, port_type: &str) -> usize {
47 let port_type = ffi::CString::new(port_type).unwrap();
48 let n = j::jack_port_type_get_buffer_size(self.client, port_type.as_ptr());
49 n
50 }
51
52 /// Opens a Jack client with the given name and options. If the client is
53 /// successfully opened, then `Ok(client)` is returned. If there is a
54 /// failure, then `Err(JackErr::ClientError(status))` will be returned.
55 ///
56 /// Although the client may be successful in opening, there still may be
57 /// some errors minor errors when attempting to opening. To access these,
58 /// check `Client::status()`.
59 pub fn open(client_name: &str, options: ClientOptions) -> Result<Self, JackErr> {
60 let mut status_bits = 0;
61 let client = unsafe {
62 let client_name = ffi::CString::new(client_name).unwrap();
63 j::jack_client_open(ffi::CString::new(client_name).unwrap().as_ptr(),
64 options.bits(),
65 &mut status_bits)
66 };
67 let status = ClientStatus::from_bits(status_bits).unwrap_or(UNKNOWN_ERROR);
68 if client.is_null() {
69 Err(JackErr::ClientError(status))
70 } else {
71 Ok(Client {
72 client: client,
73 handler: ptr::null_mut(),
74 status: status,
75 })
76 }
77 }
78
79 /// Disconnects the client from the Jack server. This does not need to
80 /// manually be called, as the client will automatically close when the
81 /// client object is dropped.
82 pub fn close(self) {}
83
84 /// Get the status of the client.
85 pub fn status(&self) -> ClientStatus {
86 self.status
87 }
88
89 /// Get the name of the current client. This may differ from the name
90 /// requested by `Client::open` as Jack will may rename a client if
91 /// necessary (ie: name collision, name too long). If the name has changed,
92 /// it should be indicated by `Client::status`.
93 pub fn name<'a>(&'a self) -> &'a str {
94 unsafe {
95 let ptr = j::jack_get_client_name(self.client);
96 let cstr = ffi::CStr::from_ptr(ptr);
97 cstr.to_str().unwrap()
98 }
99 }
100
101 /// Get the uuid of the current client.
102 pub fn uuid<'a>(&'a self) -> &'a str {
103 self.uuid_by_name(self.name()).unwrap()
104 }
105
106 /// Get the pthread ID of the thread running the Jack client side code.
107 ///
108 /// # TODO
109 /// * Integrate a pthread library
110 /// * Implement, do people need this though?
111 pub fn thread_id<P>(&self) -> P {
112 unimplemented!();
113 }
114
115 /// Get the name of the client with the UUID specified by `uuid`. If the
116 /// client is found then `Some(name)` is returned, if not, then `None` is
117 /// returned.
118 pub fn name_by_uuid<'a>(&'a self, uuid: &str) -> Option<&'a str> {
119 unsafe {
120 let uuid = ffi::CString::new(uuid).unwrap();
121 let name_ptr = j::jack_get_client_name_by_uuid(self.client, uuid.as_ptr());
122 if name_ptr.is_null() {
123 None
124 } else {
125 Some(ffi::CStr::from_ptr(name_ptr).to_str().unwrap())
126 }
127 }
128 }
129
130 /// Get the uuid of the client with the name specified by `name`. If the
131 /// client is found then `Some(uuid)` is returned, if not, then `None` is
132 /// returned.
133 pub fn uuid_by_name<'a>(&'a self, name: &str) -> Option<&'a str> {
134 unsafe {
135 let name = ffi::CString::new(name).unwrap();
136 let uuid_ptr = j::jack_get_client_name_by_uuid(self.client, name.as_ptr());
137 if uuid_ptr.is_null() {
138 None
139 } else {
140 Some(ffi::CStr::from_ptr(uuid_ptr).to_str().unwrap())
141 }
142 }
143 }
144
145 /// Returns a vector of ports that match the specified arguments
146 ///
147 /// `port_name_pattern` - A regular expression used to select ports by
148 /// name. If `None` or zero lengthed, no selection based on name will be
149 /// carried out.
150 ///
151 /// `type_name_pattern` - A regular expression used to select ports by
152 /// type. If `None` or zero lengthed, no selection based on type will be
153 /// carried out.
154 ///
155 /// `flags` - A value used to select ports by their flags. Use
156 /// `PortFlags::empty()` for no flag selection.
157 pub fn ports(&self,
158 port_name_pattern: Option<&str>,
159 type_name_pattern: Option<&str>,
160 flags: PortFlags)
161 -> Vec<String> {
162 let pnp = ffi::CString::new(port_name_pattern.unwrap_or("")).unwrap();
163 let tnp = ffi::CString::new(type_name_pattern.unwrap_or("")).unwrap();
164 let flags = flags.bits() as u64;
165 unsafe {
166 utils::collect_strs(j::jack_get_ports(self.client, pnp.as_ptr(), tnp.as_ptr(), flags))
167 }
168 }
169
170 /// Get a `Port` by its port name.
171 pub fn port_by_name(&self, port_name: &str) -> Option<Port> {
172 let port_name = ffi::CString::new(port_name).unwrap();
173 unsafe {
174 ptrs_to_port(self.client,
175 j::jack_port_by_name(self.client, port_name.as_ptr()))
176 }
177 }
178
179 /// Get a `Port` by its port id.
180 pub fn port_by_id(&self, port_id: u32) -> Option<Port> {
181 unsafe { ptrs_to_port(self.client, j::jack_port_by_id(self.client, port_id)) }
182 }
183
184 /// Tell the Jack server that the program is ready to start processing
185 /// audio. Jack will call the methods specified by the `JackHandler` trait, from `handler`.
186 ///
187 /// On failure, either `Err(JackErr::CallbackRegistrationError)` or
188 /// `Err(JackErr::ClientActivationError)` is returned.
189 ///
190 /// `handler` is consumed, but it is returned when `Client::deactivate` is
191 /// called.
192 pub fn activate(&mut self, handler: T) -> Result<(), JackErr> {
193 let handler = try!(unsafe { callbacks::register_callbacks(self.client, handler) });
194 if handler.is_null() {
195 Err(JackErr::CallbackRegistrationError)
196 } else {
197 let res = unsafe { j::jack_activate(self.client) };
198 match res {
199 0 => {
200 self.handler = handler;
201 Ok(())
202 }
203 _ => {
204 unsafe { Box::from_raw(handler) };
205 Err(JackErr::ClientActivationError)
206 }
207 }
208 }
209 }
210
211 /// Tell the Jack server to remove this client from the process graph. Also,
212 /// disconnect all ports belonging to it since inactive clients have no port
213 /// connections.
214 ///
215 /// The `handler` that was used for `Client::activate` is returned on
216 /// success. Its state may have changed due to Jack calling its methods.
217 pub fn deactivate(&mut self) -> Result<Box<T>, JackErr> {
218 if self.handler.is_null() {
219 return Err(JackErr::InvalidDeactivation);
220 }
221 let res = unsafe { j::jack_deactivate(self.client) };
222 let handler_ptr = self.handler;
223 self.handler = ptr::null_mut();
224 try!(unsafe { callbacks::clear_callbacks(self.client) });
225 match res {
226 0 => Ok(unsafe { Box::from_raw(handler_ptr) }),
227 _ => Err(JackErr::ClientDeactivationError),
228 }
229 }
230
231 /// Create a new port for the client. This is an object used for moving data
232 /// of any type in or out of the client. Ports may be connected in various
233 /// ways.
234 ///
235 /// Each port has a short name. The port's full name contains the name of
236 /// the client concatenated with a colon (:) followed by its short
237 /// name. `Port::name_size()` is the maximum length of the full
238 /// name. Exceeding that will cause the port registration to fail and return
239 /// `Err(())`.
240 ///
241 /// The `port_name` must be unique among all ports owned by this client. If
242 /// the name is not unique, the registration will fail.
243 ///
244 /// All ports have a type, which may be any non empty string, passed as an
245 /// argument. Some port types are built into the Jack API, like
246 /// `DEFAULT_AUDIO_TYPE` and `DEFAULT_MIDI_TYPE`.
247 ///
248 /// # Parameters
249 ///
250 /// `port_name` - non-empty short name for the new port (not including the
251 /// lading "client_name:"). Must be unique.
252 ///
253 /// `port_type` - port type name. If longer than `Port::type_size()`, only
254 /// that many characters are significant.
255 ///
256 /// `flags` - `PortFlags` bit mask.
257 ///
258 /// `buffer_size` - Must be `Some(n)` if this is not a built-in
259 /// `port_type`. Otherwise, it is ignored.
260 pub fn register_port(&mut self,
261 port_name: &str,
262 port_type: &str,
263 flags: PortFlags,
264 buffer_size: Option<usize>)
265 -> Result<Port, JackErr> {
266 let port_name = ffi::CString::new(port_name).unwrap();
267 let port_type = ffi::CString::new(port_type).unwrap();
268 let port_flags = flags.bits() as u64;
269 let buffer_size = buffer_size.unwrap_or(0) as u64;
270 let port = unsafe {
271 let ptr = j::jack_port_register(self.client,
272 port_name.as_ptr(),
273 port_type.as_ptr(),
274 port_flags,
275 buffer_size);
276 ptrs_to_port(self.client, ptr)
277 };
278 match port {
279 Some(p) => Ok(p),
280 None => Err(JackErr::PortRegistrationError),
281 }
282 }
283
284 /// Returns `true` if the port `port` belongs to this client.
285 pub fn is_mine(&self, port: &Port) -> bool {
286 match unsafe { j::jack_port_is_mine(self.client, port::port_pointer(port)) } {
287 0 => false,
288 _ => true,
289 }
290 }
291
292 /// Toggle input monitoring for the port with name `port_name`.
293 ///
294 /// `Err(JackErr::PortMonitorError)` is returned on failure.
295 ///
296 /// Only works if the port has the `CAN_MONITOR` flag, or else nothing
297 /// happens.
298 pub fn request_monitor(&self, port_name: &str, enable_monitor: bool) -> Result<(), JackErr> {
299 let port_name = ffi::CString::new(port_name).unwrap();
300 let onoff = match enable_monitor {
301 true => 1,
302 false => 0,
303 };
304 let res =
305 unsafe { j::jack_port_request_monitor_by_name(self.client, port_name.as_ptr(), onoff) };
306 match res {
307 0 => Ok(()),
308 _ => Err(JackErr::PortMonitorError),
309 }
310 }
311
312 /// Establish a connection between two ports.
313 ///
314 /// When a connection exists, data written to the source port will be
315 /// available to be read at the destination port.
316 ///
317 /// On failure, either a `PortNotFound` or `PortConnectionError` is returned.
318 ///
319 /// # Preconditions
320 /// 1. The port types must be identical
321 /// 2. The port flags of the `source_port` must include `IS_OUTPUT`
322 /// 3. The port flags of the `destination_port` must include `IS_INPUT`.
323 pub fn connect_ports(&self, source_port: &str, destination_port: &str) -> Result<(), JackErr> {
324 let source_port = ffi::CString::new(source_port).unwrap();
325 let destination_port = ffi::CString::new(destination_port).unwrap();
326
327 let res = unsafe {
328 j::jack_connect(self.client, source_port.as_ptr(), destination_port.as_ptr())
329 };
330 match res {
331 0 => Ok(()),
332 ::libc::EEXIST => Err(JackErr::PortNotFound),
333 _ => Err(JackErr::PortConnectionError),
334 }
335 }
336
337 /// Remove a connection between two ports.
338 pub fn disconnect_ports(&self,
339 source_port: &str,
340 destination_port: &str)
341 -> Result<(), JackErr> {
342 let source_port = ffi::CString::new(source_port).unwrap();
343 let destination_port = ffi::CString::new(destination_port).unwrap();
344 let res = unsafe {
345 j::jack_disconnect(self.client, source_port.as_ptr(), destination_port.as_ptr())
346 };
347 match res {
348 0 => Ok(()),
349 _ => Err(JackErr::PortDisconnectionError),
350 }
351 }
352
353 /// The sample rate of the jack system, as set by the user when jackd was
354 /// started.
355 pub fn sample_rate(&self) -> usize {
356 let srate = unsafe { j::jack_get_sample_rate(self.client) };
357 srate as usize
358 }
359
360 /// The current maximum size that will every be passed to the process
361 /// callback.
362 ///
363 /// It should only be used *before* the client has been activated. This size
364 /// may change,c lients that depend on it must register a buffer size
365 /// callback so they will be notified if it does.
366 pub fn buffer_size(&self) -> usize {
367 let bsize = unsafe { j::jack_get_buffer_size(self.client) };
368 bsize as usize
369 }
370
371 /// The current CPU load estimated by Jack.
372 ///
373 /// This is a running average of the time it takes to execute a full process
374 /// cycle for all clients as a percentage of the real time available per
375 /// cycle determined by the buffer size and sample rate.
376 pub fn cpu_load(&self) -> f32 {
377 let load = unsafe { j::jack_cpu_load(self.client) };
378 load
379 }
380
381 /// Start/Stop Jack's "freewheel" mode.
382 ///
383 /// When in "freewheel" mode, Jack no longer waits for any external event to
384 /// begin the start of the next process cycle. As a result, freewheel mode
385 /// causes "faster than real-time" execution of a Jack graph. If possessed,
386 /// real-time scheduling is dropped when entering freewheel mode, and if
387 /// appropriate it is reacquired when stopping.
388 ///
389 /// IMPORTANT: on systems using capabilities to provide real-time scheduling
390 /// (i.e. Linux Kernel 2.4), if enabling freewheel, this function must be
391 /// called from the thread that originally called `self.activate()`. This
392 /// restriction does not apply to other systems (e.g. Linux Kernel 2.6 or OS
393 /// X).
394 pub fn set_freewheel(&self, enable: bool) -> Result<(), JackErr> {
395 let onoff = match enable {
396 true => 0,
397 false => 1,
398 };
399 match unsafe { j::jack_set_freewheel(self.client, onoff) } {
400 0 => Ok(()),
401 _ => Err(JackErr::FreewheelError),
402 }
403 }
404
405 /// Change the buffer size passed to the process callback.
406 ///
407 /// This operation stops the jack engine process cycle, then calls all
408 /// registered buffer size callback functions before restarting the process
409 /// cycle. This will cause a gap in the audio flow, so it should only be
410 /// done at appropriate stopping points.
411 pub fn set_buffer_size(&self, n_frames: usize) -> Result<(), JackErr> {
412 let n_frames = n_frames as u32;
413 let res = unsafe { j::jack_set_buffer_size(self.client, n_frames) };
414 match res {
415 0 => Ok(()),
416 _ => Err(JackErr::SetBufferSizeError),
417 }
418 }
419
420 /// The estimated time in frames that has passed since the Jack server began
421 /// the current process cycle.
422 pub fn frames_since_cycle_start(&self) -> u32 {
423 unsafe { j::jack_frames_since_cycle_start(self.client) }
424 }
425
426 /// The estimated current time in frames. This function is intended for use
427 /// in other threads (not the process callback). The return value can be
428 /// compared with the value of `last_frame_time` to relate time in other
429 /// threads to Jack time.
430 pub fn frame_time(&self) -> u32 {
431 unsafe { j::jack_frame_time(self.client) }
432 }
433
434 /// The precise time at the start of the current process cycle. This
435 /// function may only be used from the process callback, and can be used to
436 /// interpret timestamps generated by `self.frame_time()` in other threads,
437 /// with respect to the current process cycle.
438 pub fn last_frame_time(&self) -> u32 {
439 unsafe { j::jack_last_frame_time(self.client) }
440 }
441
442 /// This function may only be used from the process callback. It provides
443 /// the internal cycle timing information as used by most of the other time
444 /// related functions. This allows the caller to map between frame counts
445 /// and microseconds with full precision (i.e. without rounding frame times
446 /// to integers), and also provides e.g. the microseconds time of the start
447 /// of the current cycle directly (it has to be computed otherwise).
448 ///
449 /// `Err(JackErr::TimeError)` is returned on failure.
450 pub fn cycle_times(&self) -> Result<CycleTimes, JackErr> {
451 let mut current_frames: u32 = 0;
452 let mut current_usecs: u64 = 0;
453 let mut next_usecs: u64 = 0;
454 let mut period_usecs: f32 = 0.0;
455 let res = unsafe {
456 j::jack_get_cycle_times(self.client,
457 &mut current_frames,
458 &mut current_usecs,
459 &mut next_usecs,
460 &mut period_usecs)
461 };
462 match res {
463 0 => {
464 Ok(CycleTimes {
465 current_frames: current_frames,
466 current_usecs: current_usecs,
467 next_usecs: next_usecs,
468 period_usecs: period_usecs,
469 })
470 },
471 _ => Err(JackErr::TimeError),
472 }
473 }
474
475 /// The estimated time in microseconds of the specified frame time
476 pub fn frames_to_time(&self, n_frames: u32) -> u64 {
477 unsafe { j::jack_frames_to_time(self.client, n_frames) }
478 }
479
480 /// The estimated time in frames for the specified system time.
481 pub fn time_to_frames(&self, t: u64) -> u32 {
482 unsafe { j::jack_time_to_frames(self.client, t) }
483 }
484
485}
486
487/// Closes the client, no need to manually call `Client::close()`.
488impl<T: JackHandler> Drop for Client<T> {
489 fn drop(&mut self) {
490 let _ = self.deactivate(); // may be Ok or Err, doesn't matter. TODO: fix style
491 if !self.client.is_null() {
492 let res = unsafe { j::jack_client_close(self.client) };
493 assert_eq!(res, 0);
494 self.client = ptr::null_mut();
495 }
496 }
497}