MIDI-Tracer: allow to directly trace physical ports

This commit is contained in:
Robin Gareus 2022-12-31 03:09:24 +01:00
parent 78ec9294e1
commit 0be3a509af
Signed by: rgareus
GPG Key ID: A090BCE02CF57F04
2 changed files with 110 additions and 28 deletions

View File

@ -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<ARDOUR::Port> port = AudioEngine::instance()->register_input_port (DataType::MIDI, portname, false, PortFlags (IsInput | Hidden | IsTerminal));
tracer_port = boost::dynamic_pointer_cast<MidiPort> (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<std::string> 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<string>& 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<ARDOUR::Port> 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<ARDOUR::Port> 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<MIDI::Parser> (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<MidiPort> mp = boost::dynamic_pointer_cast<MidiPort> (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<ARDOUR::MidiPort> mp = boost::dynamic_pointer_cast<ARDOUR::MidiPort> (p);
boost::shared_ptr<MidiPort> mp = boost::dynamic_pointer_cast<MidiPort> (p);
if (mp) {
if (mp->flags() & TransportMasterPort) {
boost::shared_ptr<TransportMaster> tm = TransportMasterManager::instance().master_by_port(boost::dynamic_pointer_cast<ARDOUR::Port> (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<MIDI::Parser> (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

View File

@ -28,6 +28,7 @@
#include <gtkmm/adjustment.h>
#include <gtkmm/spinbutton.h>
#include <gtkmm/label.h>
#include <gtkmm/liststore.h>
#include <gtkmm/comboboxtext.h>
#include <gtkmm/box.h>
@ -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<std::string> pretty_name;
Gtk::TreeModelColumn<std::string> port_name;
};
MidiPortCols _midi_port_cols;
Glib::RefPtr<Gtk::ListStore> _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> _midi_parser;
boost::shared_ptr<ARDOUR::MidiPort> tracer_port;
boost::shared_ptr<ARDOUR::MidiPort> traced_port;
static unsigned int window_count;