From 1d6c2a946dcb6af8179abc27594a2697ae49bba4 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Fri, 13 Oct 2023 21:15:58 -0600 Subject: [PATCH] second attempt at fixing the launchpad pro port name mess It turns out that slightly older versions of ALSA create different "pretty" port names for USB MIDI devices than slightly newer ones. The new versions use names that match those seen on other platforms. This means that to do port matching on Linux now requires a regexp to match the possible alternatives. This matters much more for the LPP, which has 3 input ports and 3 output ports, than it does for most devices that have a single input and single output, and we can "find" the ports just using simple string searching --- .../midi_surface/midi_surface.cc | 80 +++++++++++-- .../midi_surface/midi_surface/midi_surface.h | 11 ++ libs/surfaces/launchpad_pro/lppro.cc | 107 ++++++++---------- libs/surfaces/launchpad_pro/lppro.h | 4 - 4 files changed, 130 insertions(+), 72 deletions(-) diff --git a/libs/ctrl-interface/midi_surface/midi_surface.cc b/libs/ctrl-interface/midi_surface/midi_surface.cc index 856850da84..c50235ccfa 100644 --- a/libs/ctrl-interface/midi_surface/midi_surface.cc +++ b/libs/ctrl-interface/midi_surface/midi_surface.cc @@ -16,6 +16,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include + #include "pbd/debug.h" #include "pbd/i18n.h" @@ -167,20 +169,79 @@ MIDISurface::port_registration_handler () return; } - std::vector in; - std::vector out; + std::vector midi_inputs; + std::vector midi_outputs; - AudioEngine::instance()->get_ports (string_compose (".*%1", input_port_name()), DataType::MIDI, PortFlags (IsPhysical|IsOutput), in); - AudioEngine::instance()->get_ports (string_compose (".*%1", output_port_name()), DataType::MIDI, PortFlags (IsPhysical|IsInput), out); + AudioEngine::instance()->get_ports ("", DataType::MIDI, PortFlags (IsPhysical|IsOutput), midi_inputs); + AudioEngine::instance()->get_ports ("", DataType::MIDI, PortFlags (IsPhysical|IsInput), midi_outputs); - if (!in.empty() && !out.empty()) { - if (!_async_in->connected()) { - AudioEngine::instance()->connect (_async_in->name(), in.front()); + if (midi_inputs.empty() || midi_outputs.empty()) { + return; + } + + /* Try to find the input & output ports, whose pretty name varies on + * Linux depending on the version of ALSA, but is fairly consistent + * across newer ALSA and other platforms. + */ + + /* See if the input port is available, and maybe connect that */ + + string ip = input_port_name (); + + if (ip[0] == ':') { + std::regex rx (ip.substr (1), std::regex::extended); + + auto is_the_input = [&rx](string const &s) { + std::string pn = AudioEngine::instance()->get_hardware_port_name_by_name(s); + return std::regex_search (pn, rx); + }; + + auto pi = std::find_if (midi_inputs.begin(), midi_inputs.end(), is_the_input); + if (pi != midi_inputs.end()) { + AudioEngine::instance()->connect (_async_in->name(), *pi); } - if (!_async_out->connected()) { - AudioEngine::instance()->connect (_async_out->name(), out.front()); + } else { + /* regular partial string search */ + auto is_the_input = [&ip](string const &s) { + std::string pn = AudioEngine::instance()->get_hardware_port_name_by_name(s); + return pn.find (ip) != string::npos; + }; + + auto pi = std::find_if (midi_inputs.begin(), midi_inputs.end(), is_the_input); + if (pi != midi_inputs.end()) { + AudioEngine::instance()->connect (_async_in->name(), *pi); } } + + /* Now see if the output port is available, and maybe connect that */ + + string op = output_port_name (); + + if (op[0] == ':') { + std::regex rx (op.substr (1), std::regex::extended); + + auto is_the_output = [&rx](string const &s) { + std::string pn = AudioEngine::instance()->get_hardware_port_name_by_name(s); + return std::regex_search (pn, rx); + }; + + auto po = std::find_if (midi_outputs.begin(), midi_outputs.end(), is_the_output); + if (po != midi_outputs.end()) { + AudioEngine::instance()->connect (_async_in->name(), *po); + } + } else { + /* regular partial string search */ + auto is_the_output = [&op](string const &s) { + std::string pn = AudioEngine::instance()->get_hardware_port_name_by_name(s); + return pn.find (op) != string::npos; + }; + + auto po = std::find_if (midi_outputs.begin(), midi_outputs.end(), is_the_output); + if (po != midi_outputs.end()) { + AudioEngine::instance()->connect (_async_in->name(), *po); + } + + } } bool @@ -442,4 +503,3 @@ MIDISurface::bundles () return b; } - diff --git a/libs/ctrl-interface/midi_surface/midi_surface/midi_surface.h b/libs/ctrl-interface/midi_surface/midi_surface/midi_surface.h index 18c71205c4..301fc29670 100644 --- a/libs/ctrl-interface/midi_surface/midi_surface/midi_surface.h +++ b/libs/ctrl-interface/midi_surface/midi_surface/midi_surface.h @@ -58,6 +58,17 @@ class MIDISurface : public ARDOUR::ControlProtocol ARDOUR::Session & get_session() { return *session; } + /* These two names are used in a port registration handler to try to + automatically connect the device when it is discovered. + + If the value returned by these methods begins with a colon, they + will be assumed to be regular expressions, and passed (without the + leading colon) into the constructor of a std::regex using + std::regex::extended syntax. + + Otherwise, they are assumed to be unique string identifiers, and are + merely searched for in port names with std::string::find(). + */ virtual std::string input_port_name () const = 0; virtual std::string output_port_name () const = 0; diff --git a/libs/surfaces/launchpad_pro/lppro.cc b/libs/surfaces/launchpad_pro/lppro.cc index 746c17c0a6..3f4190facd 100644 --- a/libs/surfaces/launchpad_pro/lppro.cc +++ b/libs/surfaces/launchpad_pro/lppro.cc @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -110,15 +111,26 @@ LaunchPadPro::probe (std::string& i, std::string& o) { vector midi_inputs; vector midi_outputs; - AudioEngine::instance()->get_ports (string_compose (".*%1", input_port_regex()), DataType::MIDI, PortFlags (IsOutput|IsTerminal), midi_inputs); - AudioEngine::instance()->get_ports (string_compose (".*%1", output_port_regex()), DataType::MIDI, PortFlags (IsInput|IsTerminal), midi_outputs); + + AudioEngine::instance()->get_ports ("", DataType::MIDI, PortFlags (IsOutput|IsTerminal), midi_inputs); + AudioEngine::instance()->get_ports("", DataType::MIDI, PortFlags(IsInput | IsTerminal), midi_outputs); if (midi_inputs.empty() || midi_outputs.empty()) { return false; } - i = midi_inputs.front(); - o = midi_inputs.front(); + std::regex rx (X_("Launchpad Pro MK3.*MIDI")); + + auto has_lppro = [&rx](string const &s) { + std::string pn = AudioEngine::instance()->get_hardware_port_name_by_name(s); + return std::regex_search (pn, rx); + }; + + auto pi = std::find_if (midi_inputs.begin(), midi_inputs.end(), has_lppro); + auto po = std::find_if (midi_outputs.begin (), midi_outputs.end (), has_lppro); + + i = *pi; + o = *po; return true; } @@ -322,53 +334,13 @@ LaunchPadPro::set_state (const XMLNode & node, int version) std::string LaunchPadPro::input_port_name () const { - return input_port_regex(); -} - -std::string -LaunchPadPro::input_port_regex () -{ -#ifdef __APPLE__ - return X_("Launchpad Pro MK3.*MIDI In"); -#else - return X_("Launchpad Pro MK3.*MIDI 1"); -#endif -} - -std::string -LaunchPadPro::input_daw_port_regex () -{ -#ifdef __APPLE__ - return X_("Launchpad Pro MK3.*DAW"); -#else - return X_("Launchpad Pro MK3.*MIDI 3"); -#endif + return X_(":Launchpad Pro MK3.*MIDI (In|1)"); } std::string LaunchPadPro::output_port_name () const { - return output_port_regex(); -} - -std::string -LaunchPadPro::output_port_regex() -{ -#ifdef __APPLE__ - return X_("Launchpad Pro MK3.*MIDI Out"); -#else - return X_("Launchpad Pro MK3.*MIDI 1"); -#endif -} - -std::string -LaunchPadPro::output_daw_port_regex () -{ -#ifdef __APPLE__ - return X_("Launchpad Pro MK3.*DAW"); -#else - return X_("Launchpad Pro MK3.*MIDI 3"); -#endif + return X_(":Launchpad Pro MK3.*MIDI (Out|1)"); } void @@ -856,21 +828,40 @@ LaunchPadPro::connect_daw_ports () return; } - std::vector in; - std::vector out; - AudioEngine::instance()->get_ports (string_compose (".*%1", input_daw_port_regex()), DataType::MIDI, PortFlags (IsPhysical|IsOutput), in); - AudioEngine::instance()->get_ports (string_compose (".*%1", output_daw_port_regex()), DataType::MIDI, PortFlags (IsPhysical|IsInput), out); + std::vector midi_inputs; + std::vector midi_outputs; - if (!in.empty() && !out.empty()) { + /* get all MIDI Ports */ - if (!_daw_in->connected()) { - AudioEngine::instance()->connect (_daw_in->name(), in.front()); - } + AudioEngine::instance()->get_ports ("", DataType::MIDI, PortFlags (IsOutput|IsTerminal), midi_inputs); + AudioEngine::instance()->get_ports("", DataType::MIDI, PortFlags(IsInput | IsTerminal), midi_outputs); - if (!_daw_out->connected()) { - AudioEngine::instance()->connect (_daw_out->name(), out.front()); - } + if (midi_inputs.empty() || midi_outputs.empty()) { + return; } + + /* Try to find the DAW port, whose pretty name varies on Linux + * depending on the version of ALSA, but is fairly consistent across + * newer ALSA and other platforms. + */ + + std::regex rx (X_("Launchpad Pro MK3.*(DAW|MIDI 3)"), std::regex::extended); + + auto is_dawport = [&rx](string const &s) { + std::string pn = AudioEngine::instance()->get_hardware_port_name_by_name(s); + return std::regex_search (pn, rx); + }; + + auto pi = std::find_if (midi_inputs.begin(), midi_inputs.end(), is_dawport); + auto po = std::find_if (midi_outputs.begin (), midi_outputs.end (), is_dawport); + + if (!_daw_in->connected()) { + AudioEngine::instance()->connect (_daw_in->name(), *pi); + } + + if (!_daw_out->connected()) { + AudioEngine::instance()->connect (_daw_out->name(), *po); + } } int @@ -1074,7 +1065,7 @@ LaunchPadPro::stripable_selection_changed () } } - + } bool diff --git a/libs/surfaces/launchpad_pro/lppro.h b/libs/surfaces/launchpad_pro/lppro.h index b3135f9918..16432fe81b 100644 --- a/libs/surfaces/launchpad_pro/lppro.h +++ b/libs/surfaces/launchpad_pro/lppro.h @@ -292,10 +292,6 @@ class LaunchPadPro : public MIDISurface void port_registration_handler (); int ports_acquire (); void ports_release (); - static std::string input_port_regex (); - static std::string output_port_regex (); - static std::string input_daw_port_regex (); - static std::string output_daw_port_regex (); void connect_daw_ports (); void daw_write (const MidiByteArray&);