From 235c1f9adbadc874b0c8b6a23b54af5942bc0e04 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Thu, 20 Oct 2016 16:34:06 -0400 Subject: infrastructure for save/restore of MIDI port user-provided information --- libs/ardour/ardour/port_manager.h | 35 +++-- libs/ardour/ardour/types.h | 8 +- libs/ardour/audioengine.cc | 7 +- libs/ardour/enums.cc | 6 + libs/ardour/port_manager.cc | 295 ++++++++++++++++++++++++++++++++++---- libs/ardour/session_midi.cc | 8 +- libs/ardour/session_state.cc | 2 +- 7 files changed, 321 insertions(+), 40 deletions(-) (limited to 'libs') diff --git a/libs/ardour/ardour/port_manager.h b/libs/ardour/ardour/port_manager.h index d04a38076b..f7b0034890 100644 --- a/libs/ardour/ardour/port_manager.h +++ b/libs/ardour/ardour/port_manager.h @@ -129,16 +129,27 @@ class LIBARDOUR_API PortManager bool port_remove_in_progress() const { return _port_remove_in_progress; } - typedef std::vector 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&); + void get_midi_selection_ports (std::vector&); + 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 MidiSelectionPortsChanged; + /** Emitted if anything other than the selection property for a MIDI port changes */ + PBD::Signal0 MidiPortInfoChanged; /** Emitted if the backend notifies us of a graph order event */ PBD::Signal0 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 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 (); }; diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index be71b4afda..cf21e4c11b 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -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 { diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index 94533c54dc..caca89f300 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -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; } } - diff --git a/libs/ardour/enums.cc b/libs/ardour/enums.cc index ad5dc5e6a7..a9010583ec 100644 --- a/libs/ardour/enums.cc +++ b/libs/ardour/enums.cc @@ -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); diff --git a/libs/ardour/port_manager.cc b/libs/ardour/port_manager.cc index 5c13e7ee18..ca980c0294 100644 --- a/libs/ardour/port_manager.cc +++ b/libs/ardour/port_manager.cc @@ -24,6 +24,9 @@ #include #endif +#include +#include + #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& 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& 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 ports; + + AudioEngine::instance()->get_ports (string(), DataType::MIDI, IsOutput, ports); + + for (vector::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::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; } diff --git a/libs/ardour/session_midi.cc b/libs/ardour/session_midi.cc index eb2dce2767..70a0e14cbd 100644 --- a/libs/ardour/session_midi.cc +++ b/libs/ardour/session_midi.cc @@ -755,7 +755,7 @@ Session::rewire_selected_midi (boost::shared_ptr new_midi_target) return; } - PortManager::MidiSelectionPorts msp; + vector msp; AudioEngine::instance()->get_midi_selection_ports (msp); if (!msp.empty()) { @@ -764,7 +764,7 @@ Session::rewire_selected_midi (boost::shared_ptr new_midi_target) old_midi_target->input()->disconnect (this); } - for (PortManager::MidiSelectionPorts::const_iterator p = msp.begin(); p != msp.end(); ++p) { + for (vector::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 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::const_iterator p = msp.begin(); p != msp.end(); ++p) { cerr << "\tdisconnect " << *p << endl; AudioEngine::instance()->disconnect (*p); cerr << "\tconnect to " << *p << endl; diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 66eba307cd..c30c773e1d 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -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 (); -- cgit v1.2.3