From ecc64ab76695c5c2119f08554a378db3b3b66446 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Thu, 10 Jun 2021 15:24:14 -0600 Subject: [PATCH] JACK audio/MIDI backend: finalize (?) transition from looking up jack ports via port handle to lookup by name Add an additional callback for the JACK registration/unregistration callback so we can keep _jack_ports current --- libs/backends/jack/jack_audiobackend.h | 7 ++- libs/backends/jack/jack_portengine.cc | 78 +++++++++++++++++++++----- 2 files changed, 71 insertions(+), 14 deletions(-) diff --git a/libs/backends/jack/jack_audiobackend.h b/libs/backends/jack/jack_audiobackend.h index c868137f0a..59febab36e 100644 --- a/libs/backends/jack/jack_audiobackend.h +++ b/libs/backends/jack/jack_audiobackend.h @@ -303,7 +303,12 @@ class JACKAudioBackend : public AudioBackend { static void _registration_callback (jack_port_id_t, int, void *); static void _connect_callback (jack_port_id_t, jack_port_id_t, int, void *); - typedef std::map > JackPorts; + /* used to manage _jack_ports, specifically for ports belonging to the + JACK backend or other clients. + */ + void jack_registration_callback (jack_port_id_t, int); + + typedef std::map > JackPorts; mutable SerializedRCUManager _jack_ports; /* can be modified in ::get_port_by_name () */ void connect_callback (jack_port_id_t, jack_port_id_t, int); diff --git a/libs/backends/jack/jack_portengine.cc b/libs/backends/jack/jack_portengine.cc index fc084f68ca..af65643523 100644 --- a/libs/backends/jack/jack_portengine.cc +++ b/libs/backends/jack/jack_portengine.cc @@ -175,28 +175,32 @@ JACKAudioBackend::set_port_property (PortHandle port, const std::string& key, co PortEngine::PortPtr JACKAudioBackend::get_port_by_name (const std::string& name) const { + { + boost::shared_ptr ports = _jack_ports.reader (); + JackPorts::iterator p = ports->find (name); + + if (p != ports->end()) { + return p->second; + } + } + + /* Port not known to us yet, so look it up via JACK (slow) */ + GET_PRIVATE_JACK_POINTER_RET (_priv_jack, PortEngine::PortPtr()); jack_port_t * jack_port = jack_port_by_name (_priv_jack, name.c_str()); if (!jack_port) { + /* No such port ... return nothering */ return PortEngine::PortPtr(); } - boost::shared_ptr ports = _jack_ports.reader (); - - JackPorts::const_iterator i = ports->find (jack_port); - - if (i != ports->end()) { - return i->second; - } - boost::shared_ptr jp; { RCUWriter writer (_jack_ports); boost::shared_ptr ports = writer.get_copy(); jp.reset (new JackPort (jack_port)); - ports->insert (std::make_pair (jack_port, jp)); + ports->insert (std::make_pair (name, jp)); } _jack_ports.flush (); @@ -205,13 +209,61 @@ JACKAudioBackend::get_port_by_name (const std::string& name) const } void -JACKAudioBackend::_registration_callback (jack_port_id_t /*id*/, int /*reg*/, void* arg) +JACKAudioBackend::_registration_callback (jack_port_id_t id, int reg, void* arg) { + /* we don't use a virtual method for the registration callback, because + JACK is the only backend that delivers the arguments shown above. So + call our own JACK-centric registration callback, then the generic + one. + */ + static_cast (arg)->jack_registration_callback (id, reg); static_cast (arg)->manager.registration_callback (); static_cast (arg)->engine.latency_callback (false); static_cast (arg)->engine.latency_callback (true); } +void +JACKAudioBackend::jack_registration_callback (jack_port_id_t id, int reg) +{ + GET_PRIVATE_JACK_POINTER (_priv_jack); + jack_port_t* jack_port = jack_port_by_id (_priv_jack, id); + + if (!jack_port) { + return; + } + + const char* name = jack_port_name (jack_port); + + /* We only need to care about ports that we do not register/unregister + * ourselves. Those will be added/removed from _jack_ports at the + * appropriate time. But if someone disconnects a USB MIDI device and + * the corresponding JACK port vanishes, we need to make sure that + * _jack_ports can be used to look it up by name for use in run_input_meters() + */ + + if (!jack_port_is_mine (_priv_jack, jack_port)) { + + boost::shared_ptr ports = _jack_ports.write_copy(); + + if (!reg) { + if (ports->erase (name)) { + _jack_ports.update (ports); + } + } else { + if (ports->find (name) != ports->end()) { + /* hmmm, we already have this port */ + std::cout << "re-registration of JACK port named " << name << std::endl; + ports->erase (name); + } + + boost::shared_ptr jp (new JackPort (jack_port)); + ports->insert (std::make_pair (name, jp)); + + _jack_ports.update (ports); + } + } +} + int JACKAudioBackend::_graph_order_callback (void *arg) { @@ -523,7 +575,7 @@ JACKAudioBackend::register_port (const std::string& shortname, ARDOUR::DataType jp.reset (new JackPort (jack_port)); - ports->insert (std::make_pair (jack_port, jp)); + ports->insert (std::make_pair (jack_port_name (jack_port), jp)); } _jack_ports.flush(); @@ -536,12 +588,12 @@ JACKAudioBackend::unregister_port (PortHandle port) { GET_PRIVATE_JACK_POINTER (_priv_jack); boost::shared_ptr jp = boost::dynamic_pointer_cast(port); + const std::string name = jack_port_name (jp->jack_ptr); { RCUWriter writer (_jack_ports); boost::shared_ptr ports = writer.get_copy(); - - ports->erase (jp->jack_ptr); + ports->erase (name); } _jack_ports.flush ();