30#ifndef APF_JACKCLIENT_H
31#define APF_JACKCLIENT_H
34#include <jack/thread.h>
35#include <jack/types.h>
43#ifdef APF_JACKCLIENT_DEBUG
44#include <jack/statistics.h>
45#define APF_JACKCLIENT_DEBUG_MSG(str) \
46 do { std::cout << "apf::JackClient: " << str << std::endl; } while (false)
48#define APF_JACKCLIENT_DEBUG_MSG(str) do { } while (false)
70 typedef jack_default_audio_sample_t sample_t;
71 typedef jack_nframes_t nframes_t;
72 typedef jack_port_t port_t;
87 : std::runtime_error(
"JackClient: " + 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!"
110 explicit inline JackClient(
const std::string& name =
"JackClient"
118 jack_client_close(_client);
128 APF_JACKCLIENT_DEBUG_MSG(
"Activating JACK client.");
129 if (!_client || jack_activate(_client))
return false;
143 APF_JACKCLIENT_DEBUG_MSG(
"pending connections @ deactivate(): "
144 << _pending_connections.size());
145 return _client ? !jack_deactivate(_client) :
false;
176 return jack_port_register(_client, name.c_str(), JACK_DEFAULT_AUDIO_TYPE
186 if (!jack_port_unregister(_client, port))
202 ,
const std::string& destination)
const
204 return _connect_ports_helper(source, destination, _pending_connections);
212 ,
const std::string& destination)
const
214 return !jack_disconnect(_client, source.c_str(), destination.c_str());
222 _pending_connections_t still_pending_connections;
224 APF_JACKCLIENT_DEBUG_MSG(
"Connecting " << _pending_connections.size()
225 <<
" pending connections ...");
226 while (_pending_connections.size() > 0)
228 _connect_ports_helper(_pending_connections.back().first
229 , _pending_connections.back().second, still_pending_connections);
230 _pending_connections.pop_back();
232 APF_JACKCLIENT_DEBUG_MSG(
"Still pending connections: "
233 << still_pending_connections.size());
235 _pending_connections.swap(still_pending_connections);
237 return _pending_connections.empty();
248 if (_client) jack_transport_start(_client);
254 if (_client) jack_transport_stop(_client);
263 return _client ? !jack_transport_locate(_client, frame) :
false;
271 std::pair<bool, nframes_t> result(
false, 0);
276 jack_transport_state_t jts = jack_transport_query(_client, &jp);
277 result.first = (jts == JackTransportRolling);
278 result.second = jp.frame;
290 return _client ? !jack_set_freewheel(_client, onoff) :
false;
302 return _client ? jack_is_realtime(_client) :
false;
307 return _client ? jack_client_real_time_priority(_client) : -1;
312 return _client ? jack_cpu_load(_client) : 100.0f;
317 return _client ? jack_client_thread_id(_client) : 0;
320#ifdef APF_DOXYGEN_HACK
341 throw jack_error(
"jack_process_callback() not implemented!");
346 , jack_position_t *pos)
369 throw jack_error(
"Sample rate changes are not supported!");
378 throw jack_error(
"Buffer size changes are not supported!");
385 APF_JACKCLIENT_DEBUG_MSG(
"JACK server reports xrun of "
386 << jack_get_xrun_delayed_usecs(_client)/1000.0f <<
" msecs.");
396 static int _jack_process_callback(nframes_t nframes,
void* arg)
401 static int _jack_sync_callback(jack_transport_state_t state
402 , jack_position_t *pos,
void* arg)
407 static void _jack_shutdown_callback(
void* arg)
412 static int _jack_sample_rate_callback(nframes_t sr,
void* arg)
417 static int _jack_buffer_size_callback(nframes_t bs,
void* arg)
422 static int _jack_xrun_callback(
void* arg)
427 typedef std::vector<std::pair<std::string, std::string>>
428 _pending_connections_t;
430 bool _connect_ports_helper(
const std::string& source
431 ,
const std::string& destination
432 , _pending_connections_t& pending_connections)
const
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);
444 APF_JACKCLIENT_DEBUG_MSG(
"Connection already exists! ("
445 << source <<
" -> " << destination <<
")");
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));
458 std::string _client_name;
459 jack_client_t* _client;
460 nframes_t _sample_rate;
461 nframes_t _buffer_size;
463 mutable _pending_connections_t _pending_connections;
488 >=
static_cast<std::string::size_type
>(jack_client_name_size()))
490 throw jack_error(
"Client name is too long! ('" + name +
"')");
493 jack_options_t options =
static_cast<jack_options_t
>(
494 JackNoStartServer | JackUseExactName);
496 jack_status_t status;
497 _client = jack_client_open(name.c_str(), options, &status);
500 if (status & JackServerStarted) {
501 APF_JACKCLIENT_DEBUG_MSG(
"Server started.");
503 if (status & JackNameNotUnique) {
504 _client_name = jack_get_client_name(_client);
505 APF_JACKCLIENT_DEBUG_MSG(
"Unique name assigned: " << _client_name);
508 if (options & JackUseExactName)
510 assert(_client_name == jack_get_client_name(_client));
514 _client_name = jack_get_client_name(_client);
522 if (jack_set_process_callback(_client, _jack_process_callback,
this))
524 throw jack_error(
"Could not set process callback function for '"
525 + _client_name +
"'!");
529 if (jack_set_sync_callback(_client, _jack_sync_callback,
this))
531 throw jack_error(
"Could not set sync callback function for '"
532 + _client_name +
"'!");
537 jack_on_shutdown(_client, _jack_shutdown_callback,
this);
539 if (jack_set_xrun_callback(_client, _jack_xrun_callback,
this))
541 throw jack_error(
"Could not set xrun callback function for '"
542 + _client_name +
"'!");
551 throw jack_error(
"\"" + _client_name +
"\" was killed somehow!");
554 _sample_rate = jack_get_sample_rate(_client);
555 _buffer_size = jack_get_buffer_size(_client);
560#undef APF_JACKCLIENT_DEBUG_MSG
C++ wrapper for a JACK client.
port_t * register_out_port(const std::string &name) const
Register JACK output port.
bool set_freewheel(int onoff) const
Set JACK freewheeling mode.
port_t * register_in_port(const std::string &name) const
Register JACK input port.
nframes_t sample_rate() const
virtual int jack_sample_rate_callback(nframes_t sr)
JACK sample rate callback.
float get_cpu_load() const
Start JACK transport.
bool deactivate() const
Deactivate JACK client.
int get_real_time_priority() const
Start JACK transport.
nframes_t buffer_size() const
bool unregister_port(port_t *port) const
Unregister JACK port.
bool disconnect_ports(const std::string &source, const std::string &destination) const
Disconnect two JACK ports.
void transport_start() const
Start JACK transport.
virtual void jack_shutdown_callback()
JACK shutdown callback.
std::pair< bool, nframes_t > get_transport_state() const
Get JACK transport state.
port_t * register_port(const std::string &name, unsigned long flags) const
Register JACK port (input or output).
bool activate() const
Activate JACK client.
std::string client_name() const
virtual int jack_process_callback(nframes_t nframes)
JACK process callback function.
callback_usage_t
Select if JACK's audio callback function shall be called.
@ use_jack_process_callback
JACK audio callback (jack_process_callback()) is called after activate()
@ dont_use_jack_process_callback
JACK audio callback is never called.
virtual int jack_sync_callback(jack_transport_state_t state, jack_position_t *pos)
JACK sync callback function.
virtual int jack_buffer_size_callback(nframes_t bs)
JACK buffer size callback.
jack_native_thread_t client_thread_id() const
Start JACK transport.
JackClient(const std::string &name="JackClient", callback_usage_t callback_usage=dont_use_jack_process_callback)
Constructor.
bool is_realtime() const
Start JACK transport.
void transport_stop() const
Stop JACK transport.
bool connect_ports(const std::string &source, const std::string &destination) const
Connect two JACK ports.
bool connect_pending_connections() const
Make connections which are still pending from a previous call to connect_ports().
bool transport_locate(nframes_t frame) const
Set JACK transport location.
virtual int jack_xrun_callback()
JACK xrun callback.
Audio Processing Framework.
exception to be thrown at various occasions.