Audio Processing Framework (APF) version 0.5.0
jackclient.h
Go to the documentation of this file.
1/******************************************************************************
2 Copyright (c) 2012-2016 Institut für Nachrichtentechnik, Universität Rostock
3 Copyright (c) 2006-2012 Quality & Usability Lab
4 Deutsche Telekom Laboratories, TU Berlin
5
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 THE SOFTWARE.
23*******************************************************************************/
24
25// https://AudioProcessingFramework.github.io/
26
29
30#ifndef APF_JACKCLIENT_H
31#define APF_JACKCLIENT_H
32
33#include <jack/jack.h>
34#include <jack/thread.h>
35#include <jack/types.h>
36#include <string>
37#include <vector>
38#include <utility> // for std::pair
39#include <stdexcept> // for std::runtime_error
40#include <cassert> // for assert()
41#include <errno.h> // for EEXIST
42
43#ifdef APF_JACKCLIENT_DEBUG
44#include <jack/statistics.h> // for jack_get_xrun_delayed_usecs()
45#define APF_JACKCLIENT_DEBUG_MSG(str) \
46 do { std::cout << "apf::JackClient: " << str << std::endl; } while (false)
47#else
48#define APF_JACKCLIENT_DEBUG_MSG(str) do { } while (false)
49#endif
50
51namespace apf
52{
53
68{
69 public:
70 typedef jack_default_audio_sample_t sample_t;
71 typedef jack_nframes_t nframes_t;
72 typedef jack_port_t port_t;
73
76 {
81 };
82
84 struct jack_error : std::runtime_error
85 {
86 jack_error(const std::string& s)
87 : std::runtime_error("JackClient: " + s)
88 {}
89
90 jack_error(jack_status_t s)
91 : std::runtime_error(std::string("JackClient: ")
92 + ((s & JackInvalidOption)
93 ? "The operation contained an invalid or unsupported option!"
94 : (s & JackShmFailure) ? "Unable to access shared memory!"
95 : (s & JackVersionError) ? "Client's protocol version does not match!"
96 : (s & JackClientZombie) ? "Zombie!"
97 : (s & JackNoSuchClient) ? "Requested client does not exist!"
98 : (s & JackLoadFailure) ? "Unable to load internal client!"
99 : (s & JackInitFailure) ? "Unable to initialize client!"
100 : (s & JackBackendError) ? "Backend error!"
101 : (s & JackNameNotUnique & JackFailure)
102 ? "The client name is not unique!"
103 : (s & JackServerFailed) ? "Unable to connect to the JACK server!"
104 : (s & JackServerError) ? "Communication error with the JACK server!"
105 : (s & JackFailure) ? "Overall operation failed!"
106 : "Unknown error!"))
107 {}
108 };
109
110 explicit inline JackClient(const std::string& name = "JackClient"
112
113 virtual ~JackClient()
114 {
115 if (_client)
116 {
117 // this->deactivate() has to be called in the derived dtor or earlier!
118 jack_client_close(_client);
119 // return value is ignored, because we're shutting down anyway ...
120 }
121 }
122
126 bool activate() const
127 {
128 APF_JACKCLIENT_DEBUG_MSG("Activating JACK client.");
129 if (!_client || jack_activate(_client)) return false;
130
132
133 // TODO: Still pending connections are ignored!
134
135 return true;
136 }
137
141 bool deactivate() const
142 {
143 APF_JACKCLIENT_DEBUG_MSG("pending connections @ deactivate(): "
144 << _pending_connections.size());
145 return _client ? !jack_deactivate(_client) : false;
146 }
147
149
150
155 port_t* register_in_port(const std::string& name) const
156 {
157 return this->register_port(name, JackPortIsInput);
158 }
159
164 port_t* register_out_port(const std::string& name) const
165 {
166 return this->register_port(name, JackPortIsOutput);
167 }
168
174 port_t* register_port(const std::string& name, unsigned long flags) const
175 {
176 return jack_port_register(_client, name.c_str(), JACK_DEFAULT_AUDIO_TYPE
177 , flags, 0);
178 }
179
184 bool unregister_port(port_t* port) const
185 {
186 if (!jack_port_unregister(_client, port))
187 {
188 port = nullptr; // port still points somewhere. Set to NULL.
189 return true;
190 }
191 else
192 {
193 return false;
194 }
195 }
196
201 bool connect_ports(const std::string& source
202 , const std::string& destination) const
203 {
204 return _connect_ports_helper(source, destination, _pending_connections);
205 }
206
211 bool disconnect_ports(const std::string& source
212 , const std::string& destination) const
213 {
214 return !jack_disconnect(_client, source.c_str(), destination.c_str());
215 }
216
221 {
222 _pending_connections_t still_pending_connections;
223
224 APF_JACKCLIENT_DEBUG_MSG("Connecting " << _pending_connections.size()
225 << " pending connections ...");
226 while (_pending_connections.size() > 0)
227 {
228 _connect_ports_helper(_pending_connections.back().first
229 , _pending_connections.back().second, still_pending_connections);
230 _pending_connections.pop_back();
231 }
232 APF_JACKCLIENT_DEBUG_MSG("Still pending connections: "
233 << still_pending_connections.size());
234
235 _pending_connections.swap(still_pending_connections);
236
237 return _pending_connections.empty();
238 }
239
241
243
244
246 void transport_start() const
247 {
248 if (_client) jack_transport_start(_client);
249 }
250
252 void transport_stop() const
253 {
254 if (_client) jack_transport_stop(_client);
255 }
256
261 bool transport_locate(nframes_t frame) const
262 {
263 return _client ? !jack_transport_locate(_client, frame) : false;
264 }
265
269 std::pair<bool, nframes_t> get_transport_state() const
270 {
271 std::pair<bool, nframes_t> result(false, 0);
272
273 if (_client)
274 {
275 jack_position_t jp;
276 jack_transport_state_t jts = jack_transport_query(_client, &jp);
277 result.first = (jts == JackTransportRolling);
278 result.second = jp.frame;
279 }
280 return result;
281 }
282
284
288 bool set_freewheel(int onoff) const
289 {
290 return _client ? !jack_set_freewheel(_client, onoff) : false;
291 }
292
294 std::string client_name() const { return _client_name; }
296 nframes_t sample_rate() const { return _sample_rate; }
298 nframes_t buffer_size() const { return _buffer_size; }
299
300 bool is_realtime() const
301 {
302 return _client ? jack_is_realtime(_client) : false;
303 }
304
306 {
307 return _client ? jack_client_real_time_priority(_client) : -1;
308 }
309
310 float get_cpu_load() const
311 {
312 return _client ? jack_cpu_load(_client) : 100.0f;
313 }
314
315 jack_native_thread_t client_thread_id() const
316 {
317 return _client ? jack_client_thread_id(_client) : 0;
318 }
319
320#ifdef APF_DOXYGEN_HACK
321 protected:
322#else
323 private:
324#endif
325
328
329
338 virtual int jack_process_callback(nframes_t nframes)
339 {
340 (void)nframes; // avoid "unused parameter" warning
341 throw jack_error("jack_process_callback() not implemented!");
342 }
343
345 virtual int jack_sync_callback(jack_transport_state_t state
346 , jack_position_t *pos)
347 {
348 (void)state;
349 (void)pos;
350 return 1;
351 }
352
358 {
359 throw jack_error("JACK shutdown!");
360 }
361
366 virtual int jack_sample_rate_callback(nframes_t sr)
367 {
368 (void)sr;
369 throw jack_error("Sample rate changes are not supported!");
370 }
371
375 virtual int jack_buffer_size_callback(nframes_t bs)
376 {
377 (void)bs;
378 throw jack_error("Buffer size changes are not supported!");
379 }
380
383 virtual int jack_xrun_callback()
384 {
385 APF_JACKCLIENT_DEBUG_MSG("JACK server reports xrun of "
386 << jack_get_xrun_delayed_usecs(_client)/1000.0f << " msecs.");
387 return 0;
388 }
389
391
392 private:
393 // Internal redirection functions for callbacks
394 // Map void pointers to class instances and call member functions
395
396 static int _jack_process_callback(nframes_t nframes, void* arg)
397 {
398 return static_cast<JackClient*>(arg)->jack_process_callback(nframes);
399 }
400
401 static int _jack_sync_callback(jack_transport_state_t state
402 , jack_position_t *pos, void* arg)
403 {
404 return static_cast<JackClient*>(arg)->jack_sync_callback(state, pos);
405 }
406
407 static void _jack_shutdown_callback(void* arg)
408 {
409 static_cast<JackClient*>(arg)->jack_shutdown_callback();
410 }
411
412 static int _jack_sample_rate_callback(nframes_t sr, void* arg)
413 {
414 return static_cast<JackClient*>(arg)->jack_sample_rate_callback(sr);
415 }
416
417 static int _jack_buffer_size_callback(nframes_t bs, void* arg)
418 {
419 return static_cast<JackClient*>(arg)->jack_buffer_size_callback(bs);
420 }
421
422 static int _jack_xrun_callback(void* arg)
423 {
424 return static_cast<JackClient*>(arg)->jack_xrun_callback();
425 }
426
427 typedef std::vector<std::pair<std::string, std::string>>
428 _pending_connections_t;
429
430 bool _connect_ports_helper(const std::string& source
431 , const std::string& destination
432 , _pending_connections_t& pending_connections) const
433 {
434 APF_JACKCLIENT_DEBUG_MSG("Connection: " << source << " -> " << destination);
435 if (_client == nullptr) return false;
436 int success = jack_connect(_client, source.c_str(), destination.c_str());
437 APF_JACKCLIENT_DEBUG_MSG("Connection returned: " << success);
438
439 switch (success)
440 {
441 case 0:
442 break;
443 case EEXIST:
444 APF_JACKCLIENT_DEBUG_MSG("Connection already exists! ("
445 << source << " -> " << destination << ")");
446 break;
447 default:
448 APF_JACKCLIENT_DEBUG_MSG("Unable to connect "
449 << source << " -> " << destination
450 << "! Adding this to pending connections ...");
451 pending_connections.push_back(std::make_pair(source, destination));
452 // TODO: return something else than true/false?
453 return false;
454 }
455 return true;
456 }
457
458 std::string _client_name; // Name of JACK client
459 jack_client_t* _client; // Pointer to JACK client.
460 nframes_t _sample_rate; // sample rate of JACK server
461 nframes_t _buffer_size; // buffer size of JACK server
462
463 mutable _pending_connections_t _pending_connections;
464
465 JackClient(const JackClient&); // deactivated
466 JackClient& operator=(const JackClient&); // deactivated
467};
468
477JackClient::JackClient(const std::string& name
478 , callback_usage_t callback_usage)
479 : _client_name(name)
480 , _client(nullptr) // will be set after connecting to JACK
481 , _sample_rate(0) // -- " --
482 , _buffer_size(0) // -- " --
483{
484 // check if client name is too long
485 // jack_client_name_size() returns the size including the terminating \0
486 // character, hence the ">=".
487 if (name.size()
488 >= static_cast<std::string::size_type>(jack_client_name_size()))
489 {
490 throw jack_error("Client name is too long! ('" + name + "')");
491 }
492
493 jack_options_t options = static_cast<jack_options_t>(
494 JackNoStartServer | JackUseExactName);
495
496 jack_status_t status;
497 _client = jack_client_open(name.c_str(), options, &status);
498 if (!_client) throw jack_error(status);
499
500 if (status & JackServerStarted) {
501 APF_JACKCLIENT_DEBUG_MSG("Server started.");
502 }
503 if (status & JackNameNotUnique) {
504 _client_name = jack_get_client_name(_client);
505 APF_JACKCLIENT_DEBUG_MSG("Unique name assigned: " << _client_name);
506 }
507
508 if (options & JackUseExactName)
509 {
510 assert(_client_name == jack_get_client_name(_client));
511 }
512 else
513 {
514 _client_name = jack_get_client_name(_client);
515 }
516
517 // TODO: error callback
518 //jack_set_error_function(default_jack_error_callback);
519
520 if (callback_usage == use_jack_process_callback)
521 {
522 if (jack_set_process_callback(_client, _jack_process_callback, this))
523 {
524 throw jack_error("Could not set process callback function for '"
525 + _client_name + "'!");
526 }
527
528 // TODO: separate option to disable sync callback?
529 if (jack_set_sync_callback(_client, _jack_sync_callback, this))
530 {
531 throw jack_error("Could not set sync callback function for '"
532 + _client_name + "'!");
533 }
534 }
535 // jack_on_info_shutdown CAUSES ISSUES ON WINDOWS
536 // Using jack_on_shutdown instead
537 jack_on_shutdown(_client, _jack_shutdown_callback, this);
538
539 if (jack_set_xrun_callback(_client, _jack_xrun_callback, this))
540 {
541 throw jack_error("Could not set xrun callback function for '"
542 + _client_name + "'!");
543 }
544
545 // TODO: is the following still valid?
546 // sometimes, jack_activate() returns successful although an error occured and
547 // the thing "zombified". if the shutdown handler is called, _client is reset
548 // to zero which we can check now:
549 if (!_client)
550 {
551 throw jack_error("\"" + _client_name + "\" was killed somehow!");
552 }
553
554 _sample_rate = jack_get_sample_rate(_client);
555 _buffer_size = jack_get_buffer_size(_client);
556}
557
558} // namespace apf
559
560#undef APF_JACKCLIENT_DEBUG_MSG
561
562#endif
C++ wrapper for a JACK client.
Definition: jackclient.h:68
port_t * register_out_port(const std::string &name) const
Register JACK output port.
Definition: jackclient.h:164
bool set_freewheel(int onoff) const
Set JACK freewheeling mode.
Definition: jackclient.h:288
port_t * register_in_port(const std::string &name) const
Register JACK input port.
Definition: jackclient.h:155
nframes_t sample_rate() const
Definition: jackclient.h:296
virtual int jack_sample_rate_callback(nframes_t sr)
JACK sample rate callback.
Definition: jackclient.h:366
float get_cpu_load() const
Start JACK transport.
Definition: jackclient.h:310
bool deactivate() const
Deactivate JACK client.
Definition: jackclient.h:141
int get_real_time_priority() const
Start JACK transport.
Definition: jackclient.h:305
nframes_t buffer_size() const
Definition: jackclient.h:298
bool unregister_port(port_t *port) const
Unregister JACK port.
Definition: jackclient.h:184
bool disconnect_ports(const std::string &source, const std::string &destination) const
Disconnect two JACK ports.
Definition: jackclient.h:211
void transport_start() const
Start JACK transport.
Definition: jackclient.h:246
virtual void jack_shutdown_callback()
JACK shutdown callback.
Definition: jackclient.h:357
std::pair< bool, nframes_t > get_transport_state() const
Get JACK transport state.
Definition: jackclient.h:269
port_t * register_port(const std::string &name, unsigned long flags) const
Register JACK port (input or output).
Definition: jackclient.h:174
bool activate() const
Activate JACK client.
Definition: jackclient.h:126
std::string client_name() const
Definition: jackclient.h:294
virtual int jack_process_callback(nframes_t nframes)
JACK process callback function.
Definition: jackclient.h:338
callback_usage_t
Select if JACK's audio callback function shall be called.
Definition: jackclient.h:76
@ use_jack_process_callback
JACK audio callback (jack_process_callback()) is called after activate()
Definition: jackclient.h:80
@ dont_use_jack_process_callback
JACK audio callback is never called.
Definition: jackclient.h:78
virtual int jack_sync_callback(jack_transport_state_t state, jack_position_t *pos)
JACK sync callback function.
Definition: jackclient.h:345
virtual int jack_buffer_size_callback(nframes_t bs)
JACK buffer size callback.
Definition: jackclient.h:375
jack_native_thread_t client_thread_id() const
Start JACK transport.
Definition: jackclient.h:315
JackClient(const std::string &name="JackClient", callback_usage_t callback_usage=dont_use_jack_process_callback)
Constructor.
Definition: jackclient.h:477
bool is_realtime() const
Start JACK transport.
Definition: jackclient.h:300
void transport_stop() const
Stop JACK transport.
Definition: jackclient.h:252
bool connect_ports(const std::string &source, const std::string &destination) const
Connect two JACK ports.
Definition: jackclient.h:201
bool connect_pending_connections() const
Make connections which are still pending from a previous call to connect_ports().
Definition: jackclient.h:220
bool transport_locate(nframes_t frame) const
Set JACK transport location.
Definition: jackclient.h:261
virtual int jack_xrun_callback()
JACK xrun callback.
Definition: jackclient.h:383
Audio Processing Framework.
Definition: iterator.h:61
exception to be thrown at various occasions.
Definition: jackclient.h:85