diff --git a/gtk2_ardour/midi_tracer.cc b/gtk2_ardour/midi_tracer.cc index 522ccf0e59..516d5c8717 100644 --- a/gtk2_ardour/midi_tracer.cc +++ b/gtk2_ardour/midi_tracer.cc @@ -46,6 +46,7 @@ using namespace Gtk; using namespace std; using namespace MIDI; using namespace Glib; +using namespace ARDOUR; MidiTracer::MidiTracer () : ArdourWindow (_("MIDI Tracer")) @@ -65,7 +66,15 @@ MidiTracer::MidiTracer () { g_atomic_int_set (&_update_queued, 0); - ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect + std::string portname (string_compose(X_("x-MIDI-tracer-%1"), ++window_count)); + boost::shared_ptr port = AudioEngine::instance()->register_input_port (DataType::MIDI, portname, false, PortFlags (IsInput | Hidden | IsTerminal)); + tracer_port = boost::dynamic_pointer_cast (port); + + _midi_port_list = ListStore::create (_midi_port_cols); + _midi_port_combo.set_model (_midi_port_list); + _midi_port_combo.pack_start (_midi_port_cols.pretty_name); + + AudioEngine::instance()->PortRegisteredOrUnregistered.connect (_manager_connection, invalidator (*this), boost::bind (&MidiTracer::ports_changed, this), gui_context()); VBox* vbox = manage (new VBox); @@ -75,8 +84,8 @@ MidiTracer::MidiTracer () pbox->set_spacing (6); pbox->pack_start (*manage (new Label (_("Port:"))), false, false); - _port_combo.signal_changed().connect (sigc::mem_fun (*this, &MidiTracer::port_changed)); - pbox->pack_start (_port_combo); + _midi_port_combo.signal_changed().connect (sigc::mem_fun (*this, &MidiTracer::port_changed)); + pbox->pack_start (_midi_port_combo); pbox->show_all (); vbox->pack_start (*pbox, false, false); @@ -129,6 +138,7 @@ MidiTracer::MidiTracer () MidiTracer::~MidiTracer() { disconnect (); + AudioEngine::instance ()->unregister_port (tracer_port); } void @@ -148,25 +158,64 @@ MidiTracer::on_hide () void MidiTracer::ports_changed () { - string const c = _port_combo.get_active_text (); - _port_combo.clear (); - - ARDOUR::PortManager::PortList pl; - ARDOUR::AudioEngine::instance()->get_ports (ARDOUR::DataType::MIDI, pl); - - if (pl.empty()) { - _port_combo.set_active_text (""); - return; + TreeModel::iterator r = _midi_port_combo.get_active (); + string cpn; + if (r) { + cpn = (*r)[_midi_port_cols.port_name]; } - for (ARDOUR::PortManager::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) { - _port_combo.append ((*i)->name()); + _midi_port_list->clear (); + + PortManager::PortList pl; + AudioEngine::instance()->get_ports (DataType::MIDI, pl); + + std::vector pp; + AudioEngine::instance ()->get_physical_inputs (DataType::MIDI, pp, MidiPortFlags (0), MidiPortFlags (MidiPortControl | MidiPortVirtual)); + /* ideally we'd also list external (JACK) ports + * however there is no convenient API + * `PortManager::get_ports (const string& port_name_pattern, DataType type, PortFlags flags, vector& s)` + * lists ALL ports and we'd need to filter any outputs (sinks), except our own sinks (which can be traced). + */ + + size_t nth = 0; + size_t act = 0; + + /* physical I/Os first */ + for (auto const& pn : pp) { + if (!cpn.empty () && pn == cpn) { + act = nth; + } + ++nth; + std::string ppn = AudioEngine::instance()->get_pretty_name_by_name (pn); + if (ppn.empty ()) { + ppn = pn.substr (pn.find (':') + 1); + } + TreeModel::Row row = *_midi_port_list->append (); + row[_midi_port_cols.pretty_name] = string_compose (_("HW: %1"), ppn); + row[_midi_port_cols.port_name] = pn; } - if (c.empty()) { - _port_combo.set_active_text (pl.front()->name()); - } else { - _port_combo.set_active_text (c); + /* Ardour owned ports */ + for (auto const& p : pl) { + if (p->flags() & Hidden) { + continue; + } + std::string const& pn = p->name (); + if (!cpn.empty () && pn == cpn) { + act = nth; + } + ++nth; + std::string ppn = p->pretty_name (false); + if (ppn.empty ()) { + ppn = pn.substr (pn.find (':') + 1); + } + TreeModel::Row row = *_midi_port_list->append (); + row[_midi_port_cols.pretty_name] = ppn; + row[_midi_port_cols.port_name] = pn; + } + + if (nth > 0) { + _midi_port_combo.set_active (act); } } @@ -177,17 +226,29 @@ MidiTracer::port_changed () disconnect (); - if (_port_combo.get_active_text().empty()) { + TreeModel::iterator r = _midi_port_combo.get_active (); + if (!r) { return; } - boost::shared_ptr p = AudioEngine::instance()->get_port_by_name (_port_combo.get_active_text()); + std::string const pn = (*r)[_midi_port_cols.port_name]; + + boost::shared_ptr p = AudioEngine::instance()->get_port_by_name (pn); if (!p) { - std::cerr << "port not found: " << _port_combo.get_active_text() << "\n"; + /* connect to external port */ + if (0 == tracer_port->connect (pn)) { + _midi_parser = boost::shared_ptr (new MIDI::Parser); + _midi_parser->any.connect_same_thread (_parser_connection, boost::bind (&MidiTracer::tracer, this, _1, _2, _3, _4)); + tracer_port->set_trace (_midi_parser.get ()); + } else { + std::cerr << "CANNOT TRACE PORT " << pn << "\n"; + } return; } + boost::shared_ptr mp = boost::dynamic_pointer_cast (p); + /* The inheritance hierarchy makes this messy. AsyncMIDIPort has two * available MIDI::Parsers what we could connect to, ::self_parser() * (from ARDOUR::MidiPort) and ::parser() from MIDI::Port. One day, @@ -203,7 +264,7 @@ MidiTracer::port_changed () if (!async) { - boost::shared_ptr mp = boost::dynamic_pointer_cast (p); + boost::shared_ptr mp = boost::dynamic_pointer_cast (p); if (mp) { if (mp->flags() & TransportMasterPort) { boost::shared_ptr tm = TransportMasterManager::instance().master_by_port(boost::dynamic_pointer_cast (p)); @@ -211,10 +272,10 @@ MidiTracer::port_changed () if (tm_midi) { tm_midi->transport_parser().any.connect_same_thread(_parser_connection, boost::bind (&MidiTracer::tracer, this, _1, _2, _3, _4)); } - } - else { - my_parser.any.connect_same_thread (_parser_connection, boost::bind (&MidiTracer::tracer, this, _1, _2, _3, _4)); - mp->set_trace (&my_parser); + } else { + _midi_parser = boost::shared_ptr (new MIDI::Parser); + _midi_parser->any.connect_same_thread (_parser_connection, boost::bind (&MidiTracer::tracer, this, _1, _2, _3, _4)); + mp->set_trace (_midi_parser.get ()); traced_port = mp; } } @@ -229,10 +290,14 @@ MidiTracer::disconnect () { _parser_connection.disconnect (); + tracer_port->disconnect_all (); + tracer_port->set_trace (0); + if (traced_port) { traced_port->set_trace (0); traced_port.reset (); } + _midi_parser.reset (); } void diff --git a/gtk2_ardour/midi_tracer.h b/gtk2_ardour/midi_tracer.h index 7798a63193..5a3122543f 100644 --- a/gtk2_ardour/midi_tracer.h +++ b/gtk2_ardour/midi_tracer.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -87,7 +88,22 @@ private: Gtk::CheckButton base_button; Gtk::CheckButton collect_button; Gtk::CheckButton delta_time_button; - Gtk::ComboBoxText _port_combo; + Gtk::ComboBox _midi_port_combo; + + class MidiPortCols : public Gtk::TreeModelColumnRecord + { + public: + MidiPortCols () + { + add (pretty_name); + add (port_name); + } + Gtk::TreeModelColumn pretty_name; + Gtk::TreeModelColumn port_name; + }; + + MidiPortCols _midi_port_cols; + Glib::RefPtr _midi_port_list; void base_toggle (); void autoscroll_toggle (); @@ -99,8 +115,9 @@ private: void disconnect (); PBD::ScopedConnection _parser_connection; PBD::ScopedConnection _manager_connection; - MIDI::Parser my_parser; + boost::shared_ptr _midi_parser; + boost::shared_ptr tracer_port; boost::shared_ptr traced_port; static unsigned int window_count;