infrastructure for save/restore of MIDI port user-provided information
authorPaul Davis <paul@linuxaudiosystems.com>
Thu, 20 Oct 2016 20:34:06 +0000 (16:34 -0400)
committerPaul Davis <paul@linuxaudiosystems.com>
Thu, 20 Oct 2016 20:34:52 +0000 (16:34 -0400)
libs/ardour/ardour/port_manager.h
libs/ardour/ardour/types.h
libs/ardour/audioengine.cc
libs/ardour/enums.cc
libs/ardour/port_manager.cc
libs/ardour/session_midi.cc
libs/ardour/session_state.cc

index d04a38076bb5d0ea6b5fcd9ca71dca1df2fcada6..f7b003489071fc1544d3c9df895f4a68a261fc1f 100644 (file)
@@ -129,16 +129,27 @@ class LIBARDOUR_API PortManager
 
        bool port_remove_in_progress() const { return _port_remove_in_progress; }
 
-       typedef std::vector<std::string> MidiSelectionPorts;
+       struct MidiPortInformation {
+               std::string   pretty_name;
+               bool          input;
+               MidiPortFlags properties;
 
-       void get_midi_selection_ports (MidiSelectionPorts&) const;
-       void add_to_midi_selection_ports (std::string const&);
-       void remove_from_midi_selection_ports (std::string const&);
-       void clear_midi_selection_ports ();
-       bool port_is_for_midi_selection (std::string const&);
+               MidiPortInformation () : input (false) , properties (MidiPortFlags (0)) {}
+       };
+
+       void fill_midi_port_info ();
+
+       MidiPortInformation midi_port_information (std::string const&);
+       void get_known_midi_ports (std::vector<std::string>&);
+       void get_midi_selection_ports (std::vector<std::string>&);
+       void add_midi_port_flags (std::string const&, MidiPortFlags);
+       void remove_midi_port_flags (std::string const&, MidiPortFlags);
+       void set_midi_port_pretty_name (std::string const&, std::string const&);
 
        /** Emitted if the list of ports to be used for MIDI selection tracking changes */
        PBD::Signal0<void> MidiSelectionPortsChanged;
+       /** Emitted if anything other than the selection property for a MIDI port changes */
+       PBD::Signal0<void> MidiPortInfoChanged;
 
        /** Emitted if the backend notifies us of a graph order event */
        PBD::Signal0<void> GraphReordered;
@@ -183,8 +194,16 @@ class LIBARDOUR_API PortManager
         */
        void cycle_end (pframes_t nframes);
 
-       mutable Glib::Threads::Mutex midi_selection_ports_mutex;
-       MidiSelectionPorts _midi_selection_ports;
+       typedef std::map<std::string,MidiPortInformation> MidiPortInfo;
+
+       mutable Glib::Threads::Mutex midi_port_info_mutex;
+       MidiPortInfo midi_port_info;
+
+       static std::string midi_port_info_file ();
+       bool midi_info_dirty;
+       void save_midi_port_info ();
+       void load_midi_port_info ();
+       void fill_midi_port_info_locked ();
 };
 
 
index be71b4afda4f94791df60d28cb19e17f7fb09319..cf21e4c11bad051fef35c5c023ae08bfb324ab0b 100644 (file)
@@ -657,7 +657,13 @@ namespace ARDOUR {
 
                /* non-JACK related flags */
                Hidden = 0x20,
-               Shadow = 0x40,
+               Shadow = 0x40
+       };
+
+       enum MidiPortFlags {
+               MidiPortMusic = 0x1,
+               MidiPortControl = 0x2,
+               MidiPortSelection = 0x4,
        };
 
        struct LatencyRange {
index 94533c54dcd54335c049879a3b7148aacf18084a..caca89f3007b51cff0985c94955889707eac13f8 100644 (file)
@@ -891,6 +891,10 @@ AudioEngine::start (bool for_latency)
 
        }
 
+       /* XXX MIDI ports may not actually be available here yet .. */
+
+       PortManager::fill_midi_port_info ();
+
        if (!for_latency) {
                Running(); /* EMIT SIGNAL */
        }
@@ -917,7 +921,7 @@ AudioEngine::stop (bool for_latency)
                stop_engine = false;
        } else {
                if (_backend->stop ()) {
-                       if (pl.locked ()) { 
+                       if (pl.locked ()) {
                             pl.release ();
                         }
                        return -1;
@@ -1495,4 +1499,3 @@ AudioEngine::add_pending_port_deletion (Port* p)
                delete p;
        }
 }
-
index ad5dc5e6a7a3d2195c24a0eccaeb11ac35c0d2e2..a9010583ec66c3e7d14ec1d2224001677fb7bdb3 100644 (file)
@@ -136,6 +136,7 @@ setup_enum_writer ()
        AutoReturnTarget _AutoReturnTarget;
        PresentationInfo::Flag _PresentationInfo_Flag;
        MusicalMode::Type mode;
+       MidiPortFlags _MidiPortFlags;
 
 #define REGISTER(e) enum_writer.register_distinct (typeid(e).name(), i, s); i.clear(); s.clear()
 #define REGISTER_BITS(e) enum_writer.register_bits (typeid(e).name(), i, s); i.clear(); s.clear()
@@ -674,6 +675,11 @@ setup_enum_writer ()
        REGISTER_CLASS_ENUM (MidiModel::PatchChangeDiffCommand, Bank);
        REGISTER (_MidiModel_PatchChangeDiffCommand_Property);
 
+       REGISTER_ENUM(MidiPortMusic);
+       REGISTER_ENUM(MidiPortControl);
+       REGISTER_ENUM(MidiPortSelection);
+       REGISTER_BITS(_MidiPortFlags);
+
        REGISTER_ENUM(Linear);
        REGISTER_ENUM(Logarithmic);
        REGISTER(_WaveformScale);
index 5c13e7ee18f33cc931c73d60addf31cf76158b96..ca980c0294b63be885c3aa08b767e8ff7fe8de40 100644 (file)
@@ -24,6 +24,9 @@
 #include <regex.h>
 #endif
 
+#include <glibmm/fileutils.h>
+#include <glibmm/miscutils.h>
+
 #include "pbd/convert.h"
 #include "pbd/error.h"
 
@@ -31,6 +34,7 @@
 #include "ardour/audio_backend.h"
 #include "ardour/audio_port.h"
 #include "ardour/debug.h"
+#include "ardour/filesystem_paths.h"
 #include "ardour/midi_port.h"
 #include "ardour/midiport_manager.h"
 #include "ardour/port_manager.h"
@@ -48,7 +52,9 @@ PortManager::PortManager ()
        : ports (new Ports)
        , _port_remove_in_progress (false)
        , _port_deletions_pending (8192) /* ick, arbitrary sizing */
+       , midi_info_dirty (true)
 {
+       load_midi_port_info ();
 }
 
 void
@@ -622,6 +628,12 @@ void
 PortManager::registration_callback ()
 {
        if (!_port_remove_in_progress) {
+
+               {
+                       Glib::Threads::Mutex::Lock lm (midi_port_info_mutex);
+                       midi_info_dirty = true;
+               }
+
                PortRegisteredOrUnregistered (); /* EMIT SIGNAL */
        }
 }
@@ -862,64 +874,299 @@ PortManager::port_is_control_only (std::string const& name)
        return regexec (&compiled_pattern, name.c_str(), 0, 0, 0) == 0;
 }
 
-bool
-PortManager::port_is_for_midi_selection (std::string const & name)
+PortManager::MidiPortInformation
+PortManager::midi_port_information (std::string const & name)
 {
-       Glib::Threads::Mutex::Lock lm (midi_selection_ports_mutex);
-       return find (_midi_selection_ports.begin(), _midi_selection_ports.end(), name) != _midi_selection_ports.end();
+       Glib::Threads::Mutex::Lock lm (midi_port_info_mutex);
+
+       fill_midi_port_info_locked ();
+
+       MidiPortInfo::iterator x = midi_port_info.find (name);
+
+       if (x != midi_port_info.end()) {
+               return x->second;
+       }
+
+       return MidiPortInformation ();
 }
 
 void
-PortManager::get_midi_selection_ports (MidiSelectionPorts& copy) const
+PortManager::get_known_midi_ports (vector<string>& copy)
 {
-       Glib::Threads::Mutex::Lock lm (midi_selection_ports_mutex);
-       copy = _midi_selection_ports;
+       Glib::Threads::Mutex::Lock lm (midi_port_info_mutex);
+
+       fill_midi_port_info_locked ();
+
+       for (MidiPortInfo::const_iterator x = midi_port_info.begin(); x != midi_port_info.end(); ++x) {
+               copy.push_back (x->first);
+       }
+}
+
+void
+PortManager::get_midi_selection_ports (vector<string>& copy)
+{
+       Glib::Threads::Mutex::Lock lm (midi_port_info_mutex);
+
+       fill_midi_port_info_locked ();
+
+       for (MidiPortInfo::const_iterator x = midi_port_info.begin(); x != midi_port_info.end(); ++x) {
+               if (x->second.properties & MidiPortSelection) {
+                       copy.push_back (x->first);
+               }
+       }
+}
+
+void
+PortManager::set_midi_port_pretty_name (string const & port, string const & pretty)
+{
+       {
+               Glib::Threads::Mutex::Lock lm (midi_port_info_mutex);
+
+               fill_midi_port_info_locked ();
+
+               MidiPortInfo::iterator x = midi_port_info.find (port);
+               if (x == midi_port_info.end()) {
+                       return;
+               }
+               x->second.pretty_name = pretty;
+       }
+
+       /* push into back end */
+
+       PortEngine::PortHandle ph = _backend->get_port_by_name (port);
+
+       if (ph) {
+               _backend->set_port_property (ph, "http://jackaudio.org/metadata/pretty-name", pretty, string());
+       }
+
+       MidiPortInfoChanged (); /* EMIT SIGNAL*/
 }
 
 void
-PortManager::add_to_midi_selection_ports (string const & port)
+PortManager::add_midi_port_flags (string const & port, MidiPortFlags flags)
 {
        bool emit = false;
 
        {
-               Glib::Threads::Mutex::Lock lm (midi_selection_ports_mutex);
-               if (find (_midi_selection_ports.begin(), _midi_selection_ports.end(), port) == _midi_selection_ports.end()) {
-                       _midi_selection_ports.push_back (port);
-                       emit = true;
+               Glib::Threads::Mutex::Lock lm (midi_port_info_mutex);
+
+               fill_midi_port_info_locked ();
+
+               MidiPortInfo::iterator x = midi_port_info.find (port);
+               if (x != midi_port_info.end()) {
+                       if ((x->second.properties & flags) != flags) { // at least one missing
+                               x->second.properties = MidiPortFlags (x->second.properties | flags);
+                               emit = true;
+                       }
                }
        }
 
        if (emit) {
-               MidiSelectionPortsChanged (); /* EMIT SIGNAL */
+               if (flags & MidiPortSelection) {
+                       MidiSelectionPortsChanged (); /* EMIT SIGNAL */
+               }
+
+               if (flags != MidiPortSelection) {
+                       MidiPortInfoChanged (); /* EMIT SIGNAL */
+               }
+
+               save_midi_port_info ();
        }
 }
 
 void
-PortManager::remove_from_midi_selection_ports (string const & port)
+PortManager::remove_midi_port_flags (string const & port, MidiPortFlags flags)
 {
        bool emit = false;
 
        {
-               Glib::Threads::Mutex::Lock lm (midi_selection_ports_mutex);
-               MidiSelectionPorts::iterator x = find (_midi_selection_ports.begin(), _midi_selection_ports.end(), port);
-               if (x != _midi_selection_ports.end()) {
-                       _midi_selection_ports.erase (x);
-                       emit = true;
+               Glib::Threads::Mutex::Lock lm (midi_port_info_mutex);
+
+               fill_midi_port_info_locked ();
+
+               MidiPortInfo::iterator x = midi_port_info.find (port);
+               if (x != midi_port_info.end()) {
+                       if (x->second.properties & flags) { // at least one is set
+                               x->second.properties = MidiPortFlags (x->second.properties & ~flags);
+                               emit = true;
+                       }
                }
        }
 
        if (emit) {
-               MidiSelectionPortsChanged (); /* EMIT SIGNAL */
+               if (flags & MidiPortSelection) {
+                       MidiSelectionPortsChanged (); /* EMIT SIGNAL */
+               }
+
+               if (flags != MidiPortSelection) {
+                       MidiPortInfoChanged (); /* EMIT SIGNAL */
+               }
+
+               save_midi_port_info ();
        }
 }
 
+string
+PortManager::midi_port_info_file ()
+{
+       return Glib::build_filename (user_config_directory(), X_("midi_port_info"));
+}
+
 void
-PortManager::clear_midi_selection_ports ()
+PortManager::save_midi_port_info ()
 {
+       string path = midi_port_info_file ();
+
+       XMLNode* root = new XMLNode (X_("MidiPortInfo"));
+
        {
-               Glib::Threads::Mutex::Lock lm (midi_selection_ports_mutex);
-               _midi_selection_ports.clear ();
+               Glib::Threads::Mutex::Lock lm (midi_port_info_mutex);
+
+               if (midi_port_info.empty()) {
+                       delete root;
+                       return;
+               }
+
+               for (MidiPortInfo::iterator i = midi_port_info.begin(); i != midi_port_info.end(); ++i) {
+                       XMLNode* node = new XMLNode (X_("port"));
+                       node->add_property (X_("name"), i->first);
+                       node->add_property (X_("pretty-name"), i->second.pretty_name);
+                       node->add_property (X_("input"), i->second.input ? X_("yes") : X_("no"));
+                       node->add_property (X_("properties"), enum_2_string (i->second.properties));
+                       root->add_child_nocopy (*node);
+               }
+       }
+
+       XMLTree tree;
+
+       tree.set_root (root);
+
+       if (!tree.write (path)) {
+               error << string_compose (_("Could not save MIDI port info to %1"), path) << endmsg;
+       }
+}
+
+void
+PortManager::load_midi_port_info ()
+{
+       string path = midi_port_info_file ();
+       XMLTree tree;
+
+       if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
+               return;
+       }
+
+       if (!tree.read (path)) {
+               error << string_compose (_("Cannot load MIDI port info from %1"), path) << endmsg;
+               return;
+       }
+
+       midi_port_info.clear ();
+
+       for (XMLNodeConstIterator i = tree.root()->children().begin(); i != tree.root()->children().end(); ++i) {
+               XMLProperty const* prop;
+               MidiPortInformation mpi;
+               string name;
+
+               if ((prop = (*i)->property (X_("name"))) == 0) {
+                       continue;
+               }
+
+               name = prop->value ();
+
+               if ((prop = (*i)->property (X_("pretty-name"))) == 0) {
+                       continue;
+               }
+               mpi.pretty_name = prop->value();
+
+               if ((prop = (*i)->property (X_("input"))) == 0) {
+                       continue;
+               }
+               mpi.input = string_is_affirmative (prop->value());
+
+               if ((prop = (*i)->property (X_("properties"))) == 0) {
+                       continue;
+               }
+
+               mpi.properties = (MidiPortFlags) string_2_enum (prop->value(), mpi.properties);
+
+               midi_port_info.insert (make_pair (name, mpi));
+       }
+}
+
+void
+PortManager::fill_midi_port_info ()
+{
+       Glib::Threads::Mutex::Lock lm (midi_port_info_mutex);
+       fill_midi_port_info_locked ();
+}
+
+void
+PortManager::fill_midi_port_info_locked ()
+{
+       /* MIDI info mutex MUST be held */
+
+       if (!midi_info_dirty) {
+               return;
+       }
+
+       std::vector<string> ports;
+
+       AudioEngine::instance()->get_ports (string(), DataType::MIDI, IsOutput, ports);
+
+       for (vector<string>::iterator p = ports.begin(); p != ports.end(); ++p) {
+
+               if (port_is_mine (*p)) {
+                       continue;
+               }
+
+               if (midi_port_info.find (*p) == midi_port_info.end()) {
+                       MidiPortInformation mpi;
+                       mpi.pretty_name = *p;
+                       mpi.input = true;
+                       midi_port_info.insert (make_pair (*p, mpi));
+               }
+       }
+
+       AudioEngine::instance()->get_ports (string(), DataType::MIDI, IsInput, ports);
+
+       for (vector<string>::iterator p = ports.begin(); p != ports.end(); ++p) {
+
+               if (port_is_mine (*p)) {
+                       continue;
+               }
+
+               if (midi_port_info.find (*p) == midi_port_info.end()) {
+                       MidiPortInformation mpi;
+                       mpi.pretty_name = *p;
+                       mpi.input = false;
+                       midi_port_info.insert (make_pair (*p, mpi));
+               }
+       }
+
+       /* now push/pull pretty name information between backend and the
+        * PortManager
+        */
+
+       for (MidiPortInfo::iterator x = midi_port_info.begin(); x != midi_port_info.end(); ++x) {
+               PortEngine::PortHandle ph = _backend->get_port_by_name (x->first);
+
+               if (x->second.pretty_name != x->first) {
+                       /* name set in port info ... propagate */
+                       _backend->set_port_property (ph, "http://jackaudio.org/metadata/pretty-name", x->second.pretty_name, string());
+               } else {
+                       /* check with backend for pre-existing pretty name */
+                       if (ph) {
+                               string value;
+                               string type;
+                               if (0 == _backend->get_port_property (ph,
+                                                                     "http://jackaudio.org/metadata/pretty-name",
+                                                                     value, type)) {
+                                       x->second.pretty_name = value;
+                               }
+                       }
+               }
        }
 
-       MidiSelectionPortsChanged (); /* EMIT SIGNAL */
+       midi_info_dirty = false;
 }
index eb2dce27672e339ab811cc32c81c433958f7980d..70a0e14cbd23a03fc77907e52249e50d862b9255 100644 (file)
@@ -755,7 +755,7 @@ Session::rewire_selected_midi (boost::shared_ptr<MidiTrack> new_midi_target)
                return;
        }
 
-       PortManager::MidiSelectionPorts msp;
+       vector<string> msp;
        AudioEngine::instance()->get_midi_selection_ports (msp);
 
        if (!msp.empty()) {
@@ -764,7 +764,7 @@ Session::rewire_selected_midi (boost::shared_ptr<MidiTrack> new_midi_target)
                        old_midi_target->input()->disconnect (this);
                }
 
-               for (PortManager::MidiSelectionPorts::const_iterator p = msp.begin(); p != msp.end(); ++p) {
+               for (vector<string>::const_iterator p = msp.begin(); p != msp.end(); ++p) {
                        /* disconnect the port from everything */
                        AudioEngine::instance()->disconnect (*p);
                        /* connect it to the new target */
@@ -788,7 +788,7 @@ Session::rewire_midi_selection_ports ()
                return;
        }
 
-       PortManager::MidiSelectionPorts msp;
+       vector<string> msp;
        AudioEngine::instance()->get_midi_selection_ports (msp);
 
        if (msp.empty()) {
@@ -799,7 +799,7 @@ Session::rewire_midi_selection_ports ()
 
        target->input()->disconnect (this);
 
-       for (PortManager::MidiSelectionPorts::const_iterator p = msp.begin(); p != msp.end(); ++p) {
+       for (vector<string>::const_iterator p = msp.begin(); p != msp.end(); ++p) {
                cerr << "\tdisconnect " << *p << endl;
                AudioEngine::instance()->disconnect (*p);
                cerr << "\tconnect to " << *p << endl;
index 66eba307cded3c45f28a5371b67ce3502f23395c..c30c773e1d381cac3a0a81c41b25e4d747586330 100644 (file)
@@ -3720,7 +3720,7 @@ Session::restore_history (string snapshot_name)
        // replace history
        _history.clear();
 
-       for (XMLNodeConstIterator it  = tree.root()->children().begin(); it != tree.root()->children().end(); it++) {
+       for (XMLNodeConstIterator it  = tree.root()->children().begin(); it != tree.root()->children().end(); ++it) {
 
                XMLNode *t = *it;
                UndoTransaction* ut = new UndoTransaction ();