/* This file is part of Patchage. * Copyright 2008-2020 David Robillard * Copyright 2008 Nedko Arnaudov * * Patchage is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) * any later version. * * Patchage is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. * * You should have received a copy of the GNU General Public License * along with Patchage. If not, see . */ #include "JackDbusDriver.hpp" #include "patchage_config.h" #include "Driver.hpp" #include "Patchage.hpp" #include "PatchageCanvas.hpp" #include "PatchageEvent.hpp" #include "PatchageModule.hpp" #include "PatchagePort.hpp" #include "PortNames.hpp" #include "SignalDirection.hpp" PATCHAGE_DISABLE_FMT_WARNINGS #include #include PATCHAGE_RESTORE_WARNINGS #include #include #include #include #include #include #include #include #define JACKDBUS_SERVICE "org.jackaudio.service" #define JACKDBUS_OBJECT "/org/jackaudio/Controller" #define JACKDBUS_IFACE_CONTROL "org.jackaudio.JackControl" #define JACKDBUS_IFACE_PATCHBAY "org.jackaudio.JackPatchbay" #define JACKDBUS_CALL_DEFAULT_TIMEOUT 1000 // in milliseconds #define JACKDBUS_PORT_FLAG_INPUT 0x00000001 #define JACKDBUS_PORT_FLAG_TERMINAL 0x00000010 #define JACKDBUS_PORT_TYPE_AUDIO 0 #define JACKDBUS_PORT_TYPE_MIDI 1 namespace { std::string full_name(const std::string& client_name, const std::string& port_name) { return client_name + ":" + port_name; } } // namespace JackDriver::JackDriver(Patchage* app, ILog& log) : _app(app) , _log(log) , _dbus_error() , _dbus_connection(nullptr) , _max_dsp_load(0.0f) , _server_responding(false) , _server_started(false) , _graph_version(0) { dbus_error_init(&_dbus_error); } JackDriver::~JackDriver() { if (_dbus_connection) { dbus_connection_flush(_dbus_connection); } if (dbus_error_is_set(&_dbus_error)) { dbus_error_free(&_dbus_error); } } static bool is_jack_port(const PatchagePort* port) { return port->type() == PortType::jack_audio || port->type() == PortType::jack_midi; } void JackDriver::destroy_all() { _app->canvas()->remove_ports(is_jack_port); } void JackDriver::update_attached() { bool was_attached = _server_started; _server_started = is_started(); if (!_server_responding) { if (was_attached) { signal_detached.emit(); } return; } if (_server_started && !was_attached) { signal_attached.emit(); return; } if (!_server_started && was_attached) { signal_detached.emit(); return; } } void JackDriver::on_jack_appeared() { info_msg("Server appeared"); update_attached(); } void JackDriver::on_jack_disappeared() { info_msg("Server disappeared"); // we are not calling update_attached() here, because it will activate // jackdbus _server_responding = false; if (_server_started) { signal_detached.emit(); } _server_started = false; } DBusHandlerResult JackDriver::dbus_message_hook(DBusConnection* /*connection*/, DBusMessage* message, void* jack_driver) { const char* client2_name = nullptr; const char* client_name = nullptr; const char* new_owner = nullptr; const char* object_name = nullptr; const char* old_owner = nullptr; const char* port2_name = nullptr; const char* port_name = nullptr; dbus_uint32_t port_flags = 0u; dbus_uint32_t port_type = 0u; dbus_uint64_t client2_id = 0u; dbus_uint64_t client_id = 0u; dbus_uint64_t connection_id = 0u; dbus_uint64_t new_graph_version = 0u; dbus_uint64_t port2_id = 0u; dbus_uint64_t port_id = 0u; assert(jack_driver); auto* me = static_cast(jack_driver); assert(me->_dbus_connection); if (dbus_message_is_signal( message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) { if (!dbus_message_get_args(message, &me->_dbus_error, DBUS_TYPE_STRING, &object_name, DBUS_TYPE_STRING, &old_owner, DBUS_TYPE_STRING, &new_owner, DBUS_TYPE_INVALID)) { me->error_msg( fmt::format("dbus_message_get_args() failed to extract " "NameOwnerChanged signal arguments ({})", me->_dbus_error.message)); dbus_error_free(&me->_dbus_error); return DBUS_HANDLER_RESULT_HANDLED; } if (old_owner[0] == '\0') { me->on_jack_appeared(); } else if (new_owner[0] == '\0') { me->on_jack_disappeared(); } } if (dbus_message_is_signal( message, JACKDBUS_IFACE_PATCHBAY, "PortAppeared")) { if (!dbus_message_get_args(message, &me->_dbus_error, DBUS_TYPE_UINT64, &new_graph_version, DBUS_TYPE_UINT64, &client_id, DBUS_TYPE_STRING, &client_name, DBUS_TYPE_UINT64, &port_id, DBUS_TYPE_STRING, &port_name, DBUS_TYPE_UINT32, &port_flags, DBUS_TYPE_UINT32, &port_type, DBUS_TYPE_INVALID)) { me->error_msg( fmt::format("dbus_message_get_args() failed to extract " "PortAppeared signal arguments ({})", me->_dbus_error.message)); dbus_error_free(&me->_dbus_error); return DBUS_HANDLER_RESULT_HANDLED; } if (!me->_server_started) { me->_server_started = true; me->signal_attached.emit(); } me->add_port( client_id, client_name, port_id, port_name, port_flags, port_type); return DBUS_HANDLER_RESULT_HANDLED; } if (dbus_message_is_signal( message, JACKDBUS_IFACE_PATCHBAY, "PortDisappeared")) { if (!dbus_message_get_args(message, &me->_dbus_error, DBUS_TYPE_UINT64, &new_graph_version, DBUS_TYPE_UINT64, &client_id, DBUS_TYPE_STRING, &client_name, DBUS_TYPE_UINT64, &port_id, DBUS_TYPE_STRING, &port_name, DBUS_TYPE_INVALID)) { me->error_msg( fmt::format("dbus_message_get_args() failed to extract " "PortDisappeared signal arguments ({})", me->_dbus_error.message)); dbus_error_free(&me->_dbus_error); return DBUS_HANDLER_RESULT_HANDLED; } if (!me->_server_started) { me->_server_started = true; me->signal_attached.emit(); } me->remove_port(client_id, client_name, port_id, port_name); return DBUS_HANDLER_RESULT_HANDLED; } if (dbus_message_is_signal( message, JACKDBUS_IFACE_PATCHBAY, "PortsConnected")) { if (!dbus_message_get_args(message, &me->_dbus_error, DBUS_TYPE_UINT64, &new_graph_version, DBUS_TYPE_UINT64, &client_id, DBUS_TYPE_STRING, &client_name, DBUS_TYPE_UINT64, &port_id, DBUS_TYPE_STRING, &port_name, DBUS_TYPE_UINT64, &client2_id, DBUS_TYPE_STRING, &client2_name, DBUS_TYPE_UINT64, &port2_id, DBUS_TYPE_STRING, &port2_name, DBUS_TYPE_UINT64, &connection_id, DBUS_TYPE_INVALID)) { me->error_msg( fmt::format("dbus_message_get_args() failed to extract " "PortsConnected signal arguments ({})", me->_dbus_error.message)); dbus_error_free(&me->_dbus_error); return DBUS_HANDLER_RESULT_HANDLED; } if (!me->_server_started) { me->_server_started = true; me->signal_attached.emit(); } me->connect_ports(connection_id, client_id, client_name, port_id, port_name, client2_id, client2_name, port2_id, port2_name); return DBUS_HANDLER_RESULT_HANDLED; } if (dbus_message_is_signal( message, JACKDBUS_IFACE_PATCHBAY, "PortsDisconnected")) { if (!dbus_message_get_args(message, &me->_dbus_error, DBUS_TYPE_UINT64, &new_graph_version, DBUS_TYPE_UINT64, &client_id, DBUS_TYPE_STRING, &client_name, DBUS_TYPE_UINT64, &port_id, DBUS_TYPE_STRING, &port_name, DBUS_TYPE_UINT64, &client2_id, DBUS_TYPE_STRING, &client2_name, DBUS_TYPE_UINT64, &port2_id, DBUS_TYPE_STRING, &port2_name, DBUS_TYPE_UINT64, &connection_id, DBUS_TYPE_INVALID)) { me->error_msg( fmt::format("dbus_message_get_args() failed to extract " "PortsDisconnected signal arguments ({})", me->_dbus_error.message)); dbus_error_free(&me->_dbus_error); return DBUS_HANDLER_RESULT_HANDLED; } if (!me->_server_started) { me->_server_started = true; me->signal_attached.emit(); } me->disconnect_ports(connection_id, client_id, client_name, port_id, port_name, client2_id, client2_name, port2_id, port2_name); return DBUS_HANDLER_RESULT_HANDLED; } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } bool JackDriver::call(bool response_expected, const char* iface, const char* method, DBusMessage** reply_ptr_ptr, int in_type, ...) { DBusMessage* request_ptr = nullptr; DBusMessage* reply_ptr = nullptr; va_list ap; request_ptr = dbus_message_new_method_call( JACKDBUS_SERVICE, JACKDBUS_OBJECT, iface, method); if (!request_ptr) { throw std::runtime_error("dbus_message_new_method_call() returned 0"); } va_start(ap, in_type); dbus_message_append_args_valist(request_ptr, in_type, ap); va_end(ap); // send message and get a handle for a reply reply_ptr = dbus_connection_send_with_reply_and_block(_dbus_connection, request_ptr, JACKDBUS_CALL_DEFAULT_TIMEOUT, &_dbus_error); dbus_message_unref(request_ptr); if (!reply_ptr) { if (response_expected) { error_msg( fmt::format("No reply from server when calling method {} ({})", method, _dbus_error.message)); } _server_responding = false; dbus_error_free(&_dbus_error); } else { _server_responding = true; *reply_ptr_ptr = reply_ptr; } return reply_ptr; } bool JackDriver::is_started() { DBusMessage* reply_ptr = nullptr; dbus_bool_t started = false; if (!call(false, JACKDBUS_IFACE_CONTROL, "IsStarted", &reply_ptr, DBUS_TYPE_INVALID)) { return false; } if (!dbus_message_get_args(reply_ptr, &_dbus_error, DBUS_TYPE_BOOLEAN, &started, DBUS_TYPE_INVALID)) { dbus_message_unref(reply_ptr); dbus_error_free(&_dbus_error); error_msg("Decoding reply of IsStarted failed"); return false; } dbus_message_unref(reply_ptr); return started; } void JackDriver::start_server() { DBusMessage* reply_ptr = nullptr; if (!call(false, JACKDBUS_IFACE_CONTROL, "StartServer", &reply_ptr, DBUS_TYPE_INVALID)) { return; } dbus_message_unref(reply_ptr); update_attached(); } void JackDriver::stop_server() { DBusMessage* reply_ptr = nullptr; if (!call(false, JACKDBUS_IFACE_CONTROL, "StopServer", &reply_ptr, DBUS_TYPE_INVALID)) { return; } dbus_message_unref(reply_ptr); if (!_server_started) { _server_started = false; signal_detached.emit(); } } void JackDriver::attach(bool launch_daemon) { // Connect to the bus _dbus_connection = dbus_bus_get(DBUS_BUS_SESSION, &_dbus_error); if (dbus_error_is_set(&_dbus_error)) { error_msg( fmt::format("dbus_bus_get() failed ({})", _dbus_error.message)); dbus_error_free(&_dbus_error); return; } dbus_connection_setup_with_g_main(_dbus_connection, nullptr); dbus_bus_add_match(_dbus_connection, "type='signal',interface='" DBUS_INTERFACE_DBUS "',member=NameOwnerChanged,arg0='org.jackaudio.service'", nullptr); dbus_bus_add_match(_dbus_connection, "type='signal',interface='" JACKDBUS_IFACE_PATCHBAY "',member=PortAppeared", nullptr); dbus_bus_add_match(_dbus_connection, "type='signal',interface='" JACKDBUS_IFACE_PATCHBAY "',member=PortDisappeared", nullptr); dbus_bus_add_match(_dbus_connection, "type='signal',interface='" JACKDBUS_IFACE_PATCHBAY "',member=PortsConnected", nullptr); dbus_bus_add_match(_dbus_connection, "type='signal',interface='" JACKDBUS_IFACE_PATCHBAY "',member=PortsDisconnected", nullptr); dbus_connection_add_filter( _dbus_connection, dbus_message_hook, this, nullptr); update_attached(); if (!_server_responding) { return; } if (launch_daemon) { start_server(); } _log.info("[JACK] Attached to bus"); } void JackDriver::detach() { stop_server(); } bool JackDriver::is_attached() const { return _dbus_connection && _server_responding; } void JackDriver::add_port(PatchageModule* module, PortType type, const PortID& id, const std::string& name, bool is_input) { if (module->get_port(id)) { return; } auto* port = new PatchagePort(*module, type, id, name, "", // TODO: pretty name is_input, _app->conf()->get_port_color(type), _app->show_human_names()); _app->canvas()->index_port(id, port); } void JackDriver::add_port(dbus_uint64_t /*client_id*/, const char* client_name, dbus_uint64_t /*port_id*/, const char* port_name, dbus_uint32_t port_flags, dbus_uint32_t port_type) { PortType local_port_type; switch (port_type) { case JACKDBUS_PORT_TYPE_AUDIO: local_port_type = PortType::jack_audio; break; case JACKDBUS_PORT_TYPE_MIDI: local_port_type = PortType::jack_midi; break; default: error_msg("Unknown JACK D-Bus port type"); return; } SignalDirection type = SignalDirection::duplex; if (_app->conf()->get_module_split( client_name, port_flags & JACKDBUS_PORT_FLAG_TERMINAL)) { if (port_flags & JACKDBUS_PORT_FLAG_INPUT) { type = SignalDirection::input; } else { type = SignalDirection::output; } } PatchageModule* module = find_or_create_module(type, client_name); add_port(module, local_port_type, PortID::jack(full_name(client_name, port_name)), port_name, port_flags & JACKDBUS_PORT_FLAG_INPUT); } void JackDriver::remove_port(dbus_uint64_t /*client_id*/, const char* client_name, dbus_uint64_t /*port_id*/, const char* port_name) { const auto port_id = PortID::jack(client_name, port_name); PatchagePort* const port = _app->canvas()->find_port(port_id); if (!port) { error_msg("Unable to remove unknown port"); return; } auto* module = dynamic_cast(port->get_module()); delete port; // No empty modules (for now) if (module->num_ports() == 0) { delete module; } if (_app->canvas()->empty()) { if (_server_started) { signal_detached.emit(); } _server_started = false; } } PatchageModule* JackDriver::find_or_create_module(SignalDirection type, const std::string& name) { const auto id = ClientID::jack(name); PatchageModule* module = _app->canvas()->find_module(id, type); if (!module) { module = new PatchageModule(_app, name, type, id); module->load_location(); _app->canvas()->add_module(id, module); } return module; } void JackDriver::connect_ports(dbus_uint64_t /*connection_id*/, dbus_uint64_t /*client1_id*/, const char* client1_name, dbus_uint64_t /*port1_id*/, const char* port1_name, dbus_uint64_t /*client2_id*/, const char* client2_name, dbus_uint64_t /*port2_id*/, const char* port2_name) { const auto tail_id = PortID::jack(client1_name, port1_name); const auto head_id = PortID::jack(client2_name, port2_name); PatchagePort* const tail = _app->canvas()->find_port(tail_id); if (!tail) { error_msg( fmt::format("Unable to connect unknown port \"{}\"", tail_id)); return; } PatchagePort* const head = _app->canvas()->find_port(head_id); if (!head) { error_msg( fmt::format("Unable to connect unknown port \"{}\"", head_id)); return; } _app->canvas()->make_connection(tail, head); } void JackDriver::disconnect_ports(dbus_uint64_t /*connection_id*/, dbus_uint64_t /*client1_id*/, const char* client1_name, dbus_uint64_t /*port1_id*/, const char* port1_name, dbus_uint64_t /*client2_id*/, const char* client2_name, dbus_uint64_t /*port2_id*/, const char* port2_name) { const auto tail_id = PortID::jack(client1_name, port1_name); const auto head_id = PortID::jack(client2_name, port2_name); PatchagePort* const tail = _app->canvas()->find_port(tail_id); if (!tail) { error_msg( fmt::format("Unable to disconnect unknown port \"{}\"", tail_id)); return; } PatchagePort* const head = _app->canvas()->find_port(head_id); if (!head) { error_msg( fmt::format("Unable to disconnect unknown port \"{}\"", head_id)); return; } _app->canvas()->remove_edge_between(tail, head); } void JackDriver::refresh() { DBusMessage* reply_ptr = nullptr; DBusMessageIter iter = {}; dbus_uint64_t version = 0u; const char* reply_signature = nullptr; DBusMessageIter clients_array_iter = {}; DBusMessageIter client_struct_iter = {}; DBusMessageIter ports_array_iter = {}; DBusMessageIter port_struct_iter = {}; DBusMessageIter connections_array_iter = {}; DBusMessageIter connection_struct_iter = {}; dbus_uint64_t client_id = 0u; const char* client_name = nullptr; dbus_uint64_t port_id = 0u; const char* port_name = nullptr; dbus_uint32_t port_flags = 0u; dbus_uint32_t port_type = 0u; dbus_uint64_t client2_id = 0u; const char* client2_name = nullptr; dbus_uint64_t port2_id = 0u; const char* port2_name = nullptr; dbus_uint64_t connection_id = 0u; if (!call(true, JACKDBUS_IFACE_PATCHBAY, "GetGraph", &reply_ptr, DBUS_TYPE_UINT64, &version, DBUS_TYPE_INVALID)) { error_msg("GetGraph() failed"); return; } reply_signature = dbus_message_get_signature(reply_ptr); if (strcmp(reply_signature, "ta(tsa(tsuu))a(tstststst)") != 0) { error_msg(std::string{"GetGraph() reply signature mismatch. "} + reply_signature); dbus_message_unref(reply_ptr); return; } dbus_message_iter_init(reply_ptr, &iter); dbus_message_iter_get_basic(&iter, &version); dbus_message_iter_next(&iter); destroy_all(); _graph_version = version; for (dbus_message_iter_recurse(&iter, &clients_array_iter); dbus_message_iter_get_arg_type(&clients_array_iter) != DBUS_TYPE_INVALID; dbus_message_iter_next(&clients_array_iter)) { dbus_message_iter_recurse(&clients_array_iter, &client_struct_iter); dbus_message_iter_get_basic(&client_struct_iter, &client_id); dbus_message_iter_next(&client_struct_iter); dbus_message_iter_get_basic(&client_struct_iter, &client_name); dbus_message_iter_next(&client_struct_iter); for (dbus_message_iter_recurse(&client_struct_iter, &ports_array_iter); dbus_message_iter_get_arg_type(&ports_array_iter) != DBUS_TYPE_INVALID; dbus_message_iter_next(&ports_array_iter)) { dbus_message_iter_recurse(&ports_array_iter, &port_struct_iter); dbus_message_iter_get_basic(&port_struct_iter, &port_id); dbus_message_iter_next(&port_struct_iter); dbus_message_iter_get_basic(&port_struct_iter, &port_name); dbus_message_iter_next(&port_struct_iter); dbus_message_iter_get_basic(&port_struct_iter, &port_flags); dbus_message_iter_next(&port_struct_iter); dbus_message_iter_get_basic(&port_struct_iter, &port_type); dbus_message_iter_next(&port_struct_iter); add_port(client_id, client_name, port_id, port_name, port_flags, port_type); } dbus_message_iter_next(&client_struct_iter); } dbus_message_iter_next(&iter); for (dbus_message_iter_recurse(&iter, &connections_array_iter); dbus_message_iter_get_arg_type(&connections_array_iter) != DBUS_TYPE_INVALID; dbus_message_iter_next(&connections_array_iter)) { dbus_message_iter_recurse(&connections_array_iter, &connection_struct_iter); dbus_message_iter_get_basic(&connection_struct_iter, &client_id); dbus_message_iter_next(&connection_struct_iter); dbus_message_iter_get_basic(&connection_struct_iter, &client_name); dbus_message_iter_next(&connection_struct_iter); dbus_message_iter_get_basic(&connection_struct_iter, &port_id); dbus_message_iter_next(&connection_struct_iter); dbus_message_iter_get_basic(&connection_struct_iter, &port_name); dbus_message_iter_next(&connection_struct_iter); dbus_message_iter_get_basic(&connection_struct_iter, &client2_id); dbus_message_iter_next(&connection_struct_iter); dbus_message_iter_get_basic(&connection_struct_iter, &client2_name); dbus_message_iter_next(&connection_struct_iter); dbus_message_iter_get_basic(&connection_struct_iter, &port2_id); dbus_message_iter_next(&connection_struct_iter); dbus_message_iter_get_basic(&connection_struct_iter, &port2_name); dbus_message_iter_next(&connection_struct_iter); dbus_message_iter_get_basic(&connection_struct_iter, &connection_id); dbus_message_iter_next(&connection_struct_iter); connect_ports(connection_id, client_id, client_name, port_id, port_name, client2_id, client2_name, port2_id, port2_name); } } bool JackDriver::connect(const PortID tail_id, const PortID head_id) { const auto tail_names = PortNames(tail_id); const auto head_names = PortNames(head_id); const char* const tail_client_name = tail_names.client().c_str(); const char* const tail_port_name = tail_names.port().c_str(); const char* const head_client_name = head_names.client().c_str(); const char* const head_port_name = head_names.port().c_str(); DBusMessage* reply_ptr = nullptr; if (!call(true, JACKDBUS_IFACE_PATCHBAY, "ConnectPortsByName", &reply_ptr, DBUS_TYPE_STRING, &tail_client_name, DBUS_TYPE_STRING, &tail_port_name, DBUS_TYPE_STRING, &head_client_name, DBUS_TYPE_STRING, &head_port_name, DBUS_TYPE_INVALID)) { error_msg("ConnectPortsByName() failed"); return false; } return true; } bool JackDriver::disconnect(const PortID tail_id, const PortID head_id) { const auto tail_names = PortNames(tail_id); const auto head_names = PortNames(head_id); const char* const tail_client_name = tail_names.client().c_str(); const char* const tail_port_name = tail_names.port().c_str(); const char* const head_client_name = head_names.client().c_str(); const char* const head_port_name = head_names.port().c_str(); DBusMessage* reply_ptr = nullptr; if (!call(true, JACKDBUS_IFACE_PATCHBAY, "DisconnectPortsByName", &reply_ptr, DBUS_TYPE_STRING, &tail_client_name, DBUS_TYPE_STRING, &tail_port_name, DBUS_TYPE_STRING, &head_client_name, DBUS_TYPE_STRING, &head_port_name, DBUS_TYPE_INVALID)) { error_msg("DisconnectPortsByName() failed"); return false; } return true; } jack_nframes_t JackDriver::buffer_size() { DBusMessage* reply_ptr = nullptr; dbus_uint32_t buffer_size = 0u; if (_server_responding && !_server_started) { return 4096; } if (!call(true, JACKDBUS_IFACE_CONTROL, "GetBufferSize", &reply_ptr, DBUS_TYPE_INVALID)) { return 4096; } if (!dbus_message_get_args(reply_ptr, &_dbus_error, DBUS_TYPE_UINT32, &buffer_size, DBUS_TYPE_INVALID)) { dbus_message_unref(reply_ptr); dbus_error_free(&_dbus_error); error_msg("Decoding reply of GetBufferSize failed"); return 4096; } dbus_message_unref(reply_ptr); return buffer_size; } bool JackDriver::set_buffer_size(jack_nframes_t size) { DBusMessage* reply_ptr = nullptr; dbus_uint32_t buffer_size = 0u; buffer_size = size; if (!call(true, JACKDBUS_IFACE_CONTROL, "SetBufferSize", &reply_ptr, DBUS_TYPE_UINT32, &buffer_size, DBUS_TYPE_INVALID)) { return false; } dbus_message_unref(reply_ptr); return true; } float JackDriver::sample_rate() { DBusMessage* reply_ptr = nullptr; dbus_uint32_t sample_rate = 0u; if (!call(true, JACKDBUS_IFACE_CONTROL, "GetSampleRate", &reply_ptr, DBUS_TYPE_INVALID)) { return false; } if (!dbus_message_get_args(reply_ptr, &_dbus_error, DBUS_TYPE_UINT32, &sample_rate, DBUS_TYPE_INVALID)) { dbus_message_unref(reply_ptr); dbus_error_free(&_dbus_error); error_msg("Decoding reply of GetSampleRate failed"); return false; } dbus_message_unref(reply_ptr); return sample_rate; } bool JackDriver::is_realtime() const { DBusMessage* reply_ptr = nullptr; dbus_bool_t realtime = false; auto* me = const_cast(this); if (!me->call(true, JACKDBUS_IFACE_CONTROL, "IsRealtime", &reply_ptr, DBUS_TYPE_INVALID)) { return false; } if (!dbus_message_get_args(reply_ptr, &me->_dbus_error, DBUS_TYPE_BOOLEAN, &realtime, DBUS_TYPE_INVALID)) { dbus_message_unref(reply_ptr); dbus_error_free(&me->_dbus_error); error_msg("Decoding reply of IsRealtime failed"); return false; } dbus_message_unref(reply_ptr); return realtime; } uint32_t JackDriver::get_xruns() { DBusMessage* reply_ptr = nullptr; dbus_uint32_t xruns = 0u; if (_server_responding && !_server_started) { return 0; } if (!call(true, JACKDBUS_IFACE_CONTROL, "GetXruns", &reply_ptr, DBUS_TYPE_INVALID)) { return 0; } if (!dbus_message_get_args(reply_ptr, &_dbus_error, DBUS_TYPE_UINT32, &xruns, DBUS_TYPE_INVALID)) { dbus_message_unref(reply_ptr); dbus_error_free(&_dbus_error); error_msg("Decoding reply of GetXruns failed"); return 0; } dbus_message_unref(reply_ptr); return xruns; } void JackDriver::reset_xruns() { DBusMessage* reply_ptr = nullptr; if (!call(true, JACKDBUS_IFACE_CONTROL, "ResetXruns", &reply_ptr, DBUS_TYPE_INVALID)) { return; } dbus_message_unref(reply_ptr); } float JackDriver::get_max_dsp_load() { DBusMessage* reply_ptr = nullptr; double load = 0.0; if (_server_responding && !_server_started) { return 0.0; } if (!call(true, JACKDBUS_IFACE_CONTROL, "GetLoad", &reply_ptr, DBUS_TYPE_INVALID)) { return 0.0; } if (!dbus_message_get_args(reply_ptr, &_dbus_error, DBUS_TYPE_DOUBLE, &load, DBUS_TYPE_INVALID)) { dbus_message_unref(reply_ptr); dbus_error_free(&_dbus_error); error_msg("Decoding reply of GetLoad failed"); return 0.0; } dbus_message_unref(reply_ptr); load /= 100.0; // convert from percent to [0..1] if (load > static_cast(_max_dsp_load)) { _max_dsp_load = static_cast(load); } return _max_dsp_load; } void JackDriver::reset_max_dsp_load() { _max_dsp_load = 0.0; } PatchagePort* JackDriver::create_port_view(Patchage*, const PortID&) { assert(false); // we dont use events at all return nullptr; } void JackDriver::error_msg(const std::string& msg) const { _log.error(std::string{"[JACK] "} + msg); } void JackDriver::info_msg(const std::string& msg) const { _log.info(std::string{"[JACK] "} + msg); }