summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2007-08-06 14:19:19 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2007-08-06 14:19:19 +0000
commitb9b6ba8be490622dff662c23d9c77c9eee1291b3 (patch)
treedb5eaba409c1808ecce47e97623d0fe110ac6d8d
parent684ef0eb7a1ca371a1e93fdceb271aa9d40a7dae (diff)
merge changes to libmidi++ API from 2.0-ongoing
git-svn-id: svn://localhost/ardour2/trunk@2256 d708f5d6-7413-0410-9779-e7cbd77b26cf
-rw-r--r--gtk2_ardour/SConscript1
-rw-r--r--gtk2_ardour/midi_port_dialog.cc55
-rw-r--r--gtk2_ardour/midi_port_dialog.h21
-rw-r--r--gtk2_ardour/option_editor.cc322
-rw-r--r--gtk2_ardour/option_editor.h23
-rw-r--r--libs/ardour/ardour/configuration_vars.h3
-rw-r--r--libs/ardour/ardour/session.h3
-rw-r--r--libs/ardour/session_midi.cc96
-rw-r--r--libs/ardour/session_state.cc10
-rw-r--r--libs/midi++2/midi++/alsa_rawmidi.h7
-rw-r--r--libs/midi++2/midi++/alsa_sequencer.h10
-rw-r--r--libs/midi++2/midi++/coremidi_midiport.h26
-rw-r--r--libs/midi++2/midi++/factory.h6
-rw-r--r--libs/midi++2/midi++/fd_midiport.h7
-rw-r--r--libs/midi++2/midi++/fifomidi.h7
-rw-r--r--libs/midi++2/midi++/jack.h7
-rw-r--r--libs/midi++2/midi++/manager.h3
-rw-r--r--libs/midi++2/midi++/mmc.h9
-rw-r--r--libs/midi++2/midi++/nullmidi.h7
-rw-r--r--libs/midi++2/midi++/port_request.h7
-rw-r--r--libs/midi++2/midifactory.cc103
-rw-r--r--libs/midi++2/midimanager.cc43
-rw-r--r--libs/midi++2/mmc.cc16
-rw-r--r--libs/pbd/convert.cc16
-rw-r--r--libs/pbd/pbd/convert.h1
25 files changed, 614 insertions, 195 deletions
diff --git a/gtk2_ardour/SConscript b/gtk2_ardour/SConscript
index d215470b52..06ecf2d0f2 100644
--- a/gtk2_ardour/SConscript
+++ b/gtk2_ardour/SConscript
@@ -100,6 +100,7 @@ control_point.cc
automation_line.cc
automation_time_axis.cc
automation_controller.cc
+midi_port_dialog.cc
midi_time_axis.cc
midi_streamview.cc
axis_view.cc
diff --git a/gtk2_ardour/midi_port_dialog.cc b/gtk2_ardour/midi_port_dialog.cc
new file mode 100644
index 0000000000..2bb686d70b
--- /dev/null
+++ b/gtk2_ardour/midi_port_dialog.cc
@@ -0,0 +1,55 @@
+#include <string>
+#include <sigc++/bind.h>
+#include <gtkmm/stock.h>
+
+#include <pbd/convert.h>
+#include <gtkmm2ext/utils.h>
+
+#include "midi_port_dialog.h"
+
+#include "i18n.h"
+
+using namespace std;
+using namespace PBD;
+using namespace Gtk;
+using namespace Gtkmm2ext;
+using namespace sigc;
+
+static const char* mode_strings[] = { "duplex", "output", "input", (char*) 0 };
+
+MidiPortDialog::MidiPortDialog ()
+ : ArdourDialog ("midi_port_dialog"),
+ port_label (_("Port name"))
+
+{
+ vector<string> str = internationalize (PACKAGE, mode_strings);
+ set_popdown_strings (port_mode_combo, str);
+ port_mode_combo.set_active_text (str.front());
+
+ hpacker.pack_start (port_label);
+ hpacker.pack_start (port_name);
+ hpacker.pack_start (port_mode_combo);
+
+ port_label.show ();
+ port_name.show ();
+ port_mode_combo.show ();
+ hpacker.show ();
+
+ get_vbox()->pack_start (hpacker);
+
+ port_name.signal_activate().connect (mem_fun (*this, &MidiPortDialog::entry_activated));
+
+ add_button (Stock::ADD, RESPONSE_ACCEPT);
+ add_button (Stock::CANCEL, RESPONSE_CANCEL);
+}
+
+void
+MidiPortDialog::entry_activated ()
+{
+ response (RESPONSE_ACCEPT);
+}
+
+MidiPortDialog::~MidiPortDialog ()
+{
+
+}
diff --git a/gtk2_ardour/midi_port_dialog.h b/gtk2_ardour/midi_port_dialog.h
new file mode 100644
index 0000000000..a76400e91b
--- /dev/null
+++ b/gtk2_ardour/midi_port_dialog.h
@@ -0,0 +1,21 @@
+#include <gtkmm/box.h>
+#include <gtkmm/label.h>
+#include <gtkmm/entry.h>
+#include <gtkmm/comboboxtext.h>
+
+#include "ardour_dialog.h"
+
+class MidiPortDialog : public ArdourDialog
+{
+ public:
+ MidiPortDialog ();
+ ~MidiPortDialog ();
+
+ Gtk::HBox hpacker;
+ Gtk::Label port_label;
+ Gtk::Entry port_name;
+ Gtk::ComboBoxText port_mode_combo;
+
+ private:
+ void entry_activated ();
+};
diff --git a/gtk2_ardour/option_editor.cc b/gtk2_ardour/option_editor.cc
index ad34e8ed0b..93cccfcbf3 100644
--- a/gtk2_ardour/option_editor.cc
+++ b/gtk2_ardour/option_editor.cc
@@ -26,6 +26,8 @@
#include <ardour/sndfilesource.h>
#include <ardour/crossfade.h>
#include <midi++/manager.h>
+#include <midi++/factory.h>
+#include <midi++/port_request.h>
#include <gtkmm2ext/stop_signal.h>
#include <gtkmm2ext/utils.h>
#include <gtkmm2ext/window_title.h>
@@ -40,6 +42,7 @@
#include "utils.h"
#include "editing.h"
#include "option_editor.h"
+#include "midi_port_dialog.h"
#include "i18n.h"
@@ -75,8 +78,10 @@ OptionEditor::OptionEditor (ARDOUR_UI& uip, PublicEditor& ed, Mixer_UI& mixui)
/* MIDI */
- mmc_device_id_adjustment (0.0, 0.0, (double) 0x7f, 1.0, 16.0),
- mmc_device_id_spinner (mmc_device_id_adjustment),
+ mmc_receive_device_id_adjustment (0.0, 0.0, (double) 0x7f, 1.0, 16.0),
+ mmc_receive_device_id_spinner (mmc_receive_device_id_adjustment),
+ mmc_send_device_id_adjustment (0.0, 0.0, (double) 0x7f, 1.0, 16.0),
+ mmc_send_device_id_spinner (mmc_send_device_id_adjustment),
/* Click */
@@ -363,46 +368,131 @@ OptionEditor::smpte_offset_chosen()
}
}
-
void
OptionEditor::setup_midi_options ()
{
HBox* hbox;
+ Label* label;
+
+ midi_port_table.set_row_spacings (6);
+ midi_port_table.set_col_spacings (10);
+
+ redisplay_midi_ports ();
+
+ mmc_receive_device_id_adjustment.signal_value_changed().connect (mem_fun (*this, &OptionEditor::mmc_receive_device_id_adjusted));
+ mmc_send_device_id_adjustment.signal_value_changed().connect (mem_fun (*this, &OptionEditor::mmc_send_device_id_adjusted));
+
+ hbox = manage (new HBox);
+ hbox->set_border_width (6);
+ hbox->pack_start (midi_port_table, true, false);
+
+ midi_packer.pack_start (*hbox, false, false);
+ midi_packer.pack_start (add_midi_port_button, false, false);
+
+ hbox = manage (new HBox);
+ hbox->set_border_width (6);
+ hbox->set_spacing (6);
+ label = (manage (new Label (_("Inbound MMC Device ID"))));
+ hbox->pack_start (mmc_receive_device_id_spinner, false, false);
+ hbox->pack_start (*label, false, false);
+ midi_packer.pack_start (*hbox, false, false);
+
+ hbox = manage (new HBox);
+ hbox->set_border_width (6);
+ hbox->set_spacing (6);
+ label = (manage (new Label (_("Outbound MMC Device ID"))));
+ hbox->pack_start (mmc_send_device_id_spinner, false, false);
+ hbox->pack_start (*label, false, false);
+ midi_packer.pack_start (*hbox, false, false);
+
+ add_midi_port_button.signal_clicked().connect (mem_fun (*this, &OptionEditor::add_midi_port));
+}
+
+void
+OptionEditor::redisplay_midi_ports ()
+{
MIDI::Manager::PortMap::const_iterator i;
const MIDI::Manager::PortMap& ports = MIDI::Manager::instance()->get_midi_ports();
int n;
- ToggleButton* tb;
- RadioButton* rb;
- Gtk::Table* table = manage (new Table (ports.size() + 4, 10));
+ /* remove all existing widgets */
- table->set_row_spacings (6);
- table->set_col_spacings (10);
+ // XXX broken in gtkmm 2.10
+ // midi_port_table.clear ();
- table->attach (*(manage (new Label (_("Port")))), 0, 1, 0, 1);
- table->attach (*(manage (new Label (_("Offline")))), 1, 2, 0, 1);
- table->attach (*(manage (new Label (_("Trace\nInput")))), 2, 3, 0, 1);
- table->attach (*(manage (new Label (_("Trace\nOutput")))), 3, 4, 0, 1);
- table->attach (*(manage (new Label (_("MTC")))), 4, 5, 0, 1);
- table->attach (*(manage (new Label (_("MMC")))), 6, 7, 0, 1);
- table->attach (*(manage (new Label (_("MIDI Parameter\nControl")))), 8, 9, 0, 1);
+ for (vector<Widget*>::iterator w = midi_port_table_widgets.begin(); w != midi_port_table_widgets.end(); ++w) {
+ midi_port_table.remove (**w);
+ }
- table->attach (*(manage (new HSeparator())), 0, 9, 1, 2);
- table->attach (*(manage (new VSeparator())), 5, 6, 0, 8);
- table->attach (*(manage (new VSeparator())), 7, 8, 0, 8);
-
- table->attach (*(manage (new Label (_("MMC Device ID")))), 9, 10, 0, 1);
- table->attach (mmc_device_id_spinner, 9, 10, 1, 2);
-
- mmc_device_id_adjustment.signal_value_changed().connect (mem_fun (*this, &OptionEditor::mmc_device_id_adjusted));
+ midi_port_table_widgets.clear ();
- for (n = 0, i = ports.begin(); i != ports.end(); ++n, ++i) {
+ midi_port_table.resize (ports.size() + 4, 11);
+
+ Gtk::Label* label;
- pair<MIDI::Port*,vector<RadioButton*> > newpair;
+ label = (manage (new Label (_("Port"))));
+ label->show ();
+ midi_port_table_widgets.push_back (label);
+ midi_port_table.attach (*label, 0, 1, 0, 1);
+ label = (manage (new Label (_("Offline"))));
+ label->show ();
+ midi_port_table_widgets.push_back (label);
+ midi_port_table.attach (*label, 1, 2, 0, 1);
+ label = (manage (new Label (_("Trace\nInput"))));
+ label->show ();
+ midi_port_table_widgets.push_back (label);
+ midi_port_table.attach (*label, 2, 3, 0, 1);
+ label = (manage (new Label (_("Trace\nOutput"))));
+ label->show ();
+ midi_port_table_widgets.push_back (label);
+ midi_port_table.attach (*label, 3, 4, 0, 1);
+ label = (manage (new Label (_("MTC"))));
+ label->show ();
+ midi_port_table_widgets.push_back (label);
+ midi_port_table.attach (*label, 4, 5, 0, 1);
+ label = (manage (new Label (_("MMC"))));
+ label->show ();
+ midi_port_table_widgets.push_back (label);
+ midi_port_table.attach (*label, 6, 7, 0, 1);
+ label = (manage (new Label (_("MIDI Parameter\nControl"))));
+ label->show ();
+ midi_port_table_widgets.push_back (label);
+ midi_port_table.attach (*label, 8, 9, 0, 1);
+
+ Gtk::HSeparator* hsep = (manage (new HSeparator()));
+ hsep->show ();
+ midi_port_table_widgets.push_back (hsep);
+ midi_port_table.attach (*hsep, 0, 9, 1, 2);
+ Gtk::VSeparator* vsep = (manage (new VSeparator()));
+ vsep->show ();
+ midi_port_table_widgets.push_back (vsep);
+ midi_port_table.attach (*vsep, 5, 6, 0, 8);
+ vsep = (manage (new VSeparator()));
+ vsep->show ();
+ midi_port_table_widgets.push_back (vsep);
+ midi_port_table.attach (*vsep, 7, 8, 0, 8);
+
+ for (n = 0, i = ports.begin(); i != ports.end(); ++n, ++i) {
- newpair.first = i->second;
+ ToggleButton* tb;
+ RadioButton* rb;
+ Button* bb;
- table->attach (*(manage (new Label (i->first))), 0, 1, n+2, n+3,FILL|EXPAND, FILL );
+ /* the remove button. create early so we can pass it to various callbacks */
+
+ bb = manage (new Button (Stock::REMOVE));
+ bb->set_name ("OptionEditorToggleButton");
+ bb->show ();
+ midi_port_table_widgets.push_back (bb);
+ midi_port_table.attach (*bb, 9, 10, n+2, n+3, FILL|EXPAND, FILL);
+ bb->signal_clicked().connect (bind (mem_fun(*this, &OptionEditor::remove_midi_port), i->second));
+ bb->set_sensitive (port_removable (i->second));
+
+ label = (manage (new Label (i->first)));
+ label->show ();
+ midi_port_table_widgets.push_back (label);
+ midi_port_table.attach (*label, 0, 1, n+2, n+3,FILL|EXPAND, FILL );
+
tb = manage (new ToggleButton (_("online")));
tb->set_name ("OptionEditorToggleButton");
@@ -416,25 +506,32 @@ OptionEditor::setup_midi_options ()
set_size_request_to_display_given_text (*tb, _("online"), 15, 12);
}
- tb->set_active (!(*i).second->input()->offline());
- tb->signal_toggled().connect (bind (mem_fun(*this, &OptionEditor::port_online_toggled), (*i).second, tb));
- (*i).second->input()->OfflineStatusChanged.connect (bind (mem_fun(*this, &OptionEditor::map_port_online), (*i).second, tb));
- table->attach (*tb, 1, 2, n+2, n+3, FILL|EXPAND, FILL);
+ if (i->second->input()) {
+ tb->set_active (!i->second->input()->offline());
+ tb->signal_toggled().connect (bind (mem_fun(*this, &OptionEditor::port_online_toggled), i->second, tb));
+ i->second->input()->OfflineStatusChanged.connect (bind (mem_fun(*this, &OptionEditor::map_port_online), (*i).second, tb));
+ }
+ tb->show ();
+ midi_port_table_widgets.push_back (tb);
+ midi_port_table.attach (*tb, 1, 2, n+2, n+3, FILL|EXPAND, FILL);
tb = manage (new ToggleButton ());
tb->set_name ("OptionEditorToggleButton");
tb->signal_toggled().connect (bind (mem_fun(*this, &OptionEditor::port_trace_in_toggled), (*i).second, tb));
tb->set_size_request (10, 10);
- table->attach (*tb, 2, 3, n+2, n+3, FILL|EXPAND, FILL);
+ tb->show ();
+ midi_port_table_widgets.push_back (tb);
+ midi_port_table.attach (*tb, 2, 3, n+2, n+3, FILL|EXPAND, FILL);
tb = manage (new ToggleButton ());
tb->set_name ("OptionEditorToggleButton");
tb->signal_toggled().connect (bind (mem_fun(*this, &OptionEditor::port_trace_out_toggled), (*i).second, tb));
tb->set_size_request (10, 10);
- table->attach (*tb, 3, 4, n+2, n+3, FILL|EXPAND, FILL);
+ tb->show ();
+ midi_port_table_widgets.push_back (tb);
+ midi_port_table.attach (*tb, 3, 4, n+2, n+3, FILL|EXPAND, FILL);
rb = manage (new RadioButton ());
- newpair.second.push_back (rb);
rb->set_name ("OptionEditorToggleButton");
if (n == 0) {
mtc_button_group = rb->get_group();
@@ -442,99 +539,154 @@ OptionEditor::setup_midi_options ()
rb->set_group (mtc_button_group);
}
- table->attach (*rb, 4, 5, n+2, n+3, FILL|EXPAND, FILL);
- rb->signal_toggled().connect (bind (mem_fun(*this, &OptionEditor::mtc_port_chosen), (*i).second, rb));
+ rb->show ();
+ midi_port_table_widgets.push_back (rb);
+ midi_port_table.attach (*rb, 4, 5, n+2, n+3, FILL|EXPAND, FILL);
+ rb->signal_toggled().connect (bind (mem_fun(*this, &OptionEditor::mtc_port_chosen), (*i).second, rb, bb));
- if (Config->get_mtc_port_name() == i->first) {
+ if (session && i->second == session->mtc_port()) {
rb->set_active (true);
}
rb = manage (new RadioButton ());
- newpair.second.push_back (rb);
rb->set_name ("OptionEditorToggleButton");
if (n == 0) {
mmc_button_group = rb->get_group();
} else {
rb->set_group (mmc_button_group);
}
- table->attach (*rb, 6, 7, n+2, n+3, FILL|EXPAND, FILL);
- rb->signal_toggled().connect (bind (mem_fun(*this, &OptionEditor::mmc_port_chosen), (*i).second, rb));
+ rb->show ();
+ midi_port_table_widgets.push_back (rb);
+ midi_port_table.attach (*rb, 6, 7, n+2, n+3, FILL|EXPAND, FILL);
+ rb->signal_toggled().connect (bind (mem_fun(*this, &OptionEditor::mmc_port_chosen), (*i).second, rb, bb));
- if (Config->get_mmc_port_name() == i->first) {
+ if (session && i->second == session->mmc_port()) {
rb->set_active (true);
}
rb = manage (new RadioButton ());
- newpair.second.push_back (rb);
rb->set_name ("OptionEditorToggleButton");
if (n == 0) {
midi_button_group = rb->get_group();
} else {
rb->set_group (midi_button_group);
}
- table->attach (*rb, 8, 9, n+2, n+3, FILL|EXPAND, FILL);
- rb->signal_toggled().connect (bind (mem_fun(*this, &OptionEditor::midi_port_chosen), (*i).second, rb));
+ rb->show ();
+ midi_port_table_widgets.push_back (rb);
+ midi_port_table.attach (*rb, 8, 9, n+2, n+3, FILL|EXPAND, FILL);
+ rb->signal_toggled().connect (bind (mem_fun(*this, &OptionEditor::midi_port_chosen), (*i).second, rb, bb));
- if (Config->get_midi_port_name() == i->first) {
+ if (session && i->second == session->midi_port()) {
rb->set_active (true);
}
-
- port_toggle_buttons.insert (newpair);
+
}
- table->show_all ();
+ midi_port_table.show();
+}
- hbox = manage (new HBox);
- hbox->set_border_width (6);
- hbox->pack_start (*table, true, false);
- midi_packer.pack_start (*hbox, false, false);
+void
+OptionEditor::remove_midi_port (MIDI::Port* port)
+{
+ MIDI::Manager::instance()->remove_port (port);
+ redisplay_midi_ports ();
+}
+
+void
+OptionEditor::add_midi_port ()
+{
+ MidiPortDialog dialog;
+
+ dialog.set_position (WIN_POS_MOUSE);
+ dialog.set_transient_for (*this);
+
+ dialog.show ();
+
+ int ret = dialog.run ();
+
+ switch (ret) {
+ case RESPONSE_ACCEPT:
+ break;
+ default:
+ return;
+ break;
+ }
+
+ Glib::ustring mode = dialog.port_mode_combo.get_active_text();
+ std::string smod;
+
+ if (mode == _("input")) {
+ smod = X_("input");
+ } else if (mode == (_("output"))) {
+ smod = X_("output");
+ } else {
+ smod = "duplex";
+ }
+
+ MIDI::PortRequest req (X_("ardour"),
+ dialog.port_name.get_text(),
+ smod,
+ MIDI::PortFactory::default_port_type());
+
+ if (MIDI::Manager::instance()->add_port (req) != 0) {
+ redisplay_midi_ports ();
+ }
+}
+
+bool
+OptionEditor::port_removable (MIDI::Port *port)
+{
+ if (!session) {
+ return true;
+ }
+
+ if (port == session->mtc_port() ||
+ port == session->mmc_port() ||
+ port == session->midi_port()) {
+ return false;
+ }
+ return true;
}
void
-OptionEditor::mtc_port_chosen (MIDI::Port *port, Gtk::RadioButton* rb)
+OptionEditor::mtc_port_chosen (MIDI::Port *port, Gtk::RadioButton* rb, Gtk::Button* bb)
{
if (session) {
if (rb->get_active()) {
- if (port) {
- session->set_mtc_port (port->name());
- Config->set_mtc_port_name (port->name());
- } else {
- session->set_mtc_port ("");
- }
- rb->set_active (true);
+ session->set_mtc_port (port->name());
+ Config->set_mtc_port_name (port->name());
+ } else {
+ session->set_mtc_port ("");
}
+ bb->set_sensitive (port_removable (port));
}
}
void
-OptionEditor::mmc_port_chosen (MIDI::Port* port, Gtk::RadioButton* rb)
+OptionEditor::mmc_port_chosen (MIDI::Port* port, Gtk::RadioButton* rb, Gtk::Button* bb)
{
if (session) {
if (rb->get_active()) {
- if (port) {
- session->set_mmc_port (port->name());
- Config->set_mtc_port_name (port->name());
- } else {
- session->set_mmc_port ("");
- }
- rb->set_active (true);
+ session->set_mmc_port (port->name());
+ Config->set_mtc_port_name (port->name());
+ } else {
+ session->set_mmc_port ("");
}
+ bb->set_sensitive (port_removable (port));
}
}
void
-OptionEditor::midi_port_chosen (MIDI::Port* port, Gtk::RadioButton* rb)
+OptionEditor::midi_port_chosen (MIDI::Port* port, Gtk::RadioButton* rb, Gtk::Button* bb)
{
if (session) {
if (rb->get_active()) {
- if (port) {
- session->set_midi_port (port->name());
- Config->set_midi_port_name (port->name());
- } else {
- session->set_midi_port ("");
- }
- rb->set_active (true);
+ session->set_midi_port (port->name());
+ Config->set_midi_port_name (port->name());
+ } else {
+ session->set_midi_port ("");
}
+ bb->set_sensitive (port_removable (port));
}
}
@@ -565,12 +717,22 @@ OptionEditor::map_port_online (MIDI::Port* port, ToggleButton* tb)
}
void
-OptionEditor::mmc_device_id_adjusted ()
+OptionEditor::mmc_receive_device_id_adjusted ()
+{
+ uint8_t id = (uint8_t) mmc_receive_device_id_spinner.get_value();
+
+ if (id != Config->get_mmc_receive_device_id()) {
+ Config->set_mmc_receive_device_id (id);
+ }
+}
+
+void
+OptionEditor::mmc_send_device_id_adjusted ()
{
- uint8_t id = (uint8_t) mmc_device_id_spinner.get_value();
+ uint8_t id = (uint8_t) mmc_send_device_id_spinner.get_value();
- if (id != Config->get_mmc_device_id()) {
- Config->set_mmc_device_id (id);
+ if (id != Config->get_mmc_send_device_id()) {
+ Config->set_mmc_send_device_id (id);
}
}
diff --git a/gtk2_ardour/option_editor.h b/gtk2_ardour/option_editor.h
index 7754b0555d..a234f1d752 100644
--- a/gtk2_ardour/option_editor.h
+++ b/gtk2_ardour/option_editor.h
@@ -110,18 +110,29 @@ class OptionEditor : public Gtk::Dialog
Gtk::RadioButton::Group mmc_button_group;
Gtk::RadioButton::Group midi_button_group;
- Gtk::Adjustment mmc_device_id_adjustment;
- Gtk::SpinButton mmc_device_id_spinner;
+ Gtk::Table midi_port_table;
+ std::vector<Gtk::Widget*> midi_port_table_widgets;
+ Gtk::Adjustment mmc_receive_device_id_adjustment;
+ Gtk::SpinButton mmc_receive_device_id_spinner;
+ Gtk::Adjustment mmc_send_device_id_adjustment;
+ Gtk::SpinButton mmc_send_device_id_spinner;
+ Gtk::Button add_midi_port_button;
+
+ void add_midi_port ();
+ void remove_midi_port (MIDI::Port*);
+ void redisplay_midi_ports ();
void port_online_toggled (MIDI::Port*,Gtk::ToggleButton*);
void port_trace_in_toggled (MIDI::Port*,Gtk::ToggleButton*);
void port_trace_out_toggled (MIDI::Port*,Gtk::ToggleButton*);
- void mmc_port_chosen (MIDI::Port*,Gtk::RadioButton*);
- void mtc_port_chosen (MIDI::Port*,Gtk::RadioButton*);
- void midi_port_chosen (MIDI::Port*,Gtk::RadioButton*);
+ void mmc_port_chosen (MIDI::Port*,Gtk::RadioButton*, Gtk::Button*);
+ void mtc_port_chosen (MIDI::Port*,Gtk::RadioButton*, Gtk::Button*);
+ void midi_port_chosen (MIDI::Port*,Gtk::RadioButton*, Gtk::Button*);
+ bool port_removable (MIDI::Port*);
- void mmc_device_id_adjusted ();
+ void mmc_receive_device_id_adjusted ();
+ void mmc_send_device_id_adjusted ();
void map_port_online (MIDI::Port*, Gtk::ToggleButton*);
diff --git a/libs/ardour/ardour/configuration_vars.h b/libs/ardour/ardour/configuration_vars.h
index 3051a0b44b..4d5579a9a0 100644
--- a/libs/ardour/ardour/configuration_vars.h
+++ b/libs/ardour/ardour/configuration_vars.h
@@ -36,7 +36,8 @@ CONFIG_VARIABLE (bool, send_mtc, "send-mtc", false)
CONFIG_VARIABLE (bool, send_mmc, "send-mmc", false)
CONFIG_VARIABLE (bool, mmc_control, "mmc-control", false)
CONFIG_VARIABLE (bool, midi_feedback, "midi-feedback", false)
-CONFIG_VARIABLE (uint8_t, mmc_device_id, "mmc-device-id", 0)
+CONFIG_VARIABLE (uint8_t, mmc_receive_device_id, "mmc-receive-device-id", 0)
+CONFIG_VARIABLE (uint8_t, mmc_send_device_id, "mmc-send-device-id", 0)
/* control surfaces */
diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h
index 63be24d9f7..9cfdf1187e 100644
--- a/libs/ardour/ardour/session.h
+++ b/libs/ardour/ardour/session.h
@@ -744,7 +744,8 @@ class Session : public PBD::StatefulDestructible
void deliver_midi (MIDI::Port*, MIDI::byte*, int32_t size);
- void set_mmc_device_id (uint32_t id);
+ void set_mmc_receive_device_id (uint32_t id);
+ void set_mmc_send_device_id (uint32_t id);
/* Scrubbing */
diff --git a/libs/ardour/session_midi.cc b/libs/ardour/session_midi.cc
index 7f88766b63..ba3e6401e6 100644
--- a/libs/ardour/session_midi.cc
+++ b/libs/ardour/session_midi.cc
@@ -88,97 +88,66 @@ Session::use_config_midi_ports ()
int
Session::set_mtc_port (string port_tag)
{
-#if 0
- MIDI::byte old_device_id = 0;
- bool reset_id = false;
+ MTC_Slave *ms;
if (port_tag.length() == 0) {
- if (_mmc_port == 0) {
+
+ if (_slave && ((ms = dynamic_cast<MTC_Slave*> (_slave)) != 0)) {
+ error << _("Ardour is slaved to MTC - port cannot be reset") << endmsg;
+ return -1;
+ }
+
+ if (_mtc_port == 0) {
return 0;
}
- _mmc_port = 0;
+
+ _mtc_port = 0;
goto out;
}
MIDI::Port* port;
if ((port = MIDI::Manager::instance()->port (port_tag)) == 0) {
+ error << string_compose (_("unknown port %1 requested for MTC"), port_tag) << endl;
return -1;
}
- _mmc_port = port;
+ _mtc_port = port;
- if (mmc) {
- old_device_id = mmc->device_id();
- reset_id = true;
- delete mmc;
+ if (_slave && ((ms = dynamic_cast<MTC_Slave*> (_slave)) != 0)) {
+ ms->rebind (*port);
}
- mmc = new MIDI::MachineControl (*_mmc_port, 1.0,
- MMC_CommandSignature,
- MMC_ResponseSignature);
-
- if (reset_id) {
- mmc->set_device_id (old_device_id);
- }
-
- mmc->Play.connect
- (mem_fun (*this, &Session::mmc_deferred_play));
- mmc->DeferredPlay.connect
- (mem_fun (*this, &Session::mmc_deferred_play));
- mmc->Stop.connect
- (mem_fun (*this, &Session::mmc_stop));
- mmc->FastForward.connect
- (mem_fun (*this, &Session::mmc_fast_forward));
- mmc->Rewind.connect
- (mem_fun (*this, &Session::mmc_rewind));
- mmc->Pause.connect
- (mem_fun (*this, &Session::mmc_pause));
- mmc->RecordPause.connect
- (mem_fun (*this, &Session::mmc_record_pause));
- mmc->RecordStrobe.connect
- (mem_fun (*this, &Session::mmc_record_strobe));
- mmc->RecordExit.connect
- (mem_fun (*this, &Session::mmc_record_exit));
- mmc->Locate.connect
- (mem_fun (*this, &Session::mmc_locate));
- mmc->Step.connect
- (mem_fun (*this, &Session::mmc_step));
- mmc->Shuttle.connect
- (mem_fun (*this, &Session::mmc_shuttle));
- mmc->TrackRecordStatusChange.connect
- (mem_fun (*this, &Session::mmc_record_enable));
-
-
- /* also handle MIDI SPP because its so common */
-
- _mmc_port->input()->start.connect (mem_fun (*this, &Session::spp_start));
- _mmc_port->input()->contineu.connect (mem_fun (*this, &Session::spp_continue));
- _mmc_port->input()->stop.connect (mem_fun (*this, &Session::spp_stop));
-
- Config->set_mmc_port_name (port_tag);
+ Config->set_mtc_port_name (port_tag);
out:
- MMC_PortChanged(); /* EMIT SIGNAL */
+ MTC_PortChanged(); /* EMIT SIGNAL */
change_midi_ports ();
set_dirty();
-#endif
return 0;
}
void
-Session::set_mmc_device_id (uint32_t device_id)
+Session::set_mmc_receive_device_id (uint32_t device_id)
{
if (mmc) {
- mmc->set_device_id (device_id);
+ mmc->set_receive_device_id (device_id);
+ }
+}
+
+void
+Session::set_mmc_send_device_id (uint32_t device_id)
+{
+ if (mmc) {
+ mmc->set_send_device_id (device_id);
}
}
int
Session::set_mmc_port (string port_tag)
{
-#if 0
- MIDI::byte old_device_id = 0;
+ MIDI::byte old_recv_device_id = 0;
+ MIDI::byte old_send_device_id = 0;
bool reset_id = false;
if (port_tag.length() == 0) {
@@ -198,7 +167,8 @@ Session::set_mmc_port (string port_tag)
_mmc_port = port;
if (mmc) {
- old_device_id = mmc->device_id();
+ old_recv_device_id = mmc->receive_device_id();
+ old_recv_device_id = mmc->send_device_id();
reset_id = true;
delete mmc;
}
@@ -208,7 +178,8 @@ Session::set_mmc_port (string port_tag)
MMC_ResponseSignature);
if (reset_id) {
- mmc->set_device_id (old_device_id);
+ mmc->set_receive_device_id (old_recv_device_id);
+ mmc->set_send_device_id (old_send_device_id);
}
mmc->Play.connect
@@ -248,7 +219,6 @@ Session::set_mmc_port (string port_tag)
Config->set_mmc_port_name (port_tag);
out:
-#endif
MMC_PortChanged(); /* EMIT SIGNAL */
change_midi_ports ();
set_dirty();
@@ -438,7 +408,7 @@ Session::setup_midi_control ()
mmc_buffer[0] = 0xf0; // SysEx
mmc_buffer[1] = 0x7f; // Real Time SysEx ID for MMC
- mmc_buffer[2] = 0x7f; // "broadcast" device ID
+ mmc_buffer[2] = mmc->send_device_id();
mmc_buffer[3] = 0x6; // MCC
/* Set up the qtr frame message */
diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc
index a7c8dc0200..48fb932b30 100644
--- a/libs/ardour/session_state.cc
+++ b/libs/ardour/session_state.cc
@@ -2872,10 +2872,16 @@ Session::config_changed (const char* parameter_name)
//poke_midi_thread ();
- } else if (PARAM_IS ("mmc-device-id")) {
+ } else if (PARAM_IS ("mmc-device-id") || PARAM_IS ("mmc-receive-id")) {
if (mmc) {
- mmc->set_device_id (Config->get_mmc_device_id());
+ mmc->set_receive_device_id (Config->get_mmc_receive_device_id());
+ }
+
+ } else if (PARAM_IS ("mmc-send-id")) {
+
+ if (mmc) {
+ mmc->set_send_device_id (Config->get_mmc_send_device_id());
}
} else if (PARAM_IS ("midi-control")) {
diff --git a/libs/midi++2/midi++/alsa_rawmidi.h b/libs/midi++2/midi++/alsa_rawmidi.h
index 8e50609fbe..54b86edd70 100644
--- a/libs/midi++2/midi++/alsa_rawmidi.h
+++ b/libs/midi++2/midi++/alsa_rawmidi.h
@@ -37,6 +37,13 @@ class ALSA_RawMidiPort : public MIDI::FD_MidiPort
ALSA_RawMidiPort (MIDI::PortRequest &req)
: FD_MidiPort (req, "/dev/snd", "midi") {}
virtual ~ALSA_RawMidiPort () {}
+
+ static std::string typestring;
+
+ protected:
+ std::string get_typestring () const {
+ return typestring;
+ }
};
} // namespace MIDI
diff --git a/libs/midi++2/midi++/alsa_sequencer.h b/libs/midi++2/midi++/alsa_sequencer.h
index 7fe880fe63..b54486416a 100644
--- a/libs/midi++2/midi++/alsa_sequencer.h
+++ b/libs/midi++2/midi++/alsa_sequencer.h
@@ -30,8 +30,9 @@
namespace MIDI {
-class ALSA_SequencerMidiPort : public Port
+class PortRequest;
+class ALSA_SequencerMidiPort : public Port
{
public:
ALSA_SequencerMidiPort (PortRequest &req);
@@ -41,6 +42,13 @@ class ALSA_SequencerMidiPort : public Port
virtual int selectable() const;
+ static std::string typestring;
+
+ protected:
+ std::string get_typestring () const {
+ return typestring;
+ }
+
protected:
/* Direct I/O */
int write (byte *msg, size_t msglen, timestamp_t timestamp);
diff --git a/libs/midi++2/midi++/coremidi_midiport.h b/libs/midi++2/midi++/coremidi_midiport.h
index 30e07e01a5..91eccea4a5 100644
--- a/libs/midi++2/midi++/coremidi_midiport.h
+++ b/libs/midi++2/midi++/coremidi_midiport.h
@@ -32,14 +32,24 @@
namespace MIDI {
-class CoreMidi_MidiPort:public Port {
- public:
- CoreMidi_MidiPort(PortRequest & req);
- virtual ~ CoreMidi_MidiPort();
-
- virtual int selectable() const {
- return -1;
- }
+namespace PortRequest;
+
+class CoreMidi_MidiPort:public Port
+{
+ public:
+ CoreMidi_MidiPort(PortRequest & req);
+ virtual ~ CoreMidi_MidiPort();
+
+ virtual int selectable() const {
+ return -1;
+ }
+ static std::string typestring;
+
+ protected:
+ std::string get_typestring () const {
+ return typestring;
+ }
+
protected:
/* Direct I/O */
int write (byte *msg, size_t msglen, timestamp_t timestamp);
diff --git a/libs/midi++2/midi++/factory.h b/libs/midi++2/midi++/factory.h
index df7f35e073..9954ea72fe 100644
--- a/libs/midi++2/midi++/factory.h
+++ b/libs/midi++2/midi++/factory.h
@@ -23,6 +23,7 @@
#include <string>
#include <midi++/port.h>
+#include <midi++/port_request.h>
namespace MIDI {
@@ -31,6 +32,11 @@ class PortFactory {
Port *create_port (PortRequest &req, void* data);
static bool ignore_duplicate_devices (Port::Type);
+ static int get_known_ports (std::vector<PortSet>&);
+ static std::string default_port_type ();
+ static Port::Type string_to_type (const std::string&);
+ static std::string mode_to_string (int);
+ static int string_to_mode (const std::string&);
};
} // namespace MIDI
diff --git a/libs/midi++2/midi++/fd_midiport.h b/libs/midi++2/midi++/fd_midiport.h
index 8a1af55967..34e2e27a1a 100644
--- a/libs/midi++2/midi++/fd_midiport.h
+++ b/libs/midi++2/midi++/fd_midiport.h
@@ -48,6 +48,13 @@ class FD_MidiPort : public Port
virtual int selectable() const;
static std::vector<std::string *> *list_devices ();
+ static std::string typestring;
+
+ protected:
+ std::string get_typestring () const {
+ return typestring;
+ }
+
protected:
int _fd;
virtual void open (PortRequest &req);
diff --git a/libs/midi++2/midi++/fifomidi.h b/libs/midi++2/midi++/fifomidi.h
index 57d1502c45..ea644dde06 100644
--- a/libs/midi++2/midi++/fifomidi.h
+++ b/libs/midi++2/midi++/fifomidi.h
@@ -37,6 +37,13 @@ class FIFO_MidiPort : public MIDI::FD_MidiPort
FIFO_MidiPort (PortRequest &req);
~FIFO_MidiPort () {};
+ static std::string typestring;
+
+ protected:
+ std::string get_typestring () const {
+ return typestring;
+ }
+
private:
void open (PortRequest &req);
};
diff --git a/libs/midi++2/midi++/jack.h b/libs/midi++2/midi++/jack.h
index c20b2693f1..1f25609aac 100644
--- a/libs/midi++2/midi++/jack.h
+++ b/libs/midi++2/midi++/jack.h
@@ -47,6 +47,13 @@ public:
virtual void cycle_start(nframes_t nframes);
+ static std::string typestring;
+
+ protected:
+ std::string get_typestring () const {
+ return typestring;
+ }
+
protected:
/* Direct I/O */
int write(byte *msg, size_t msglen, timestamp_t timestamp);
diff --git a/libs/midi++2/midi++/manager.h b/libs/midi++2/midi++/manager.h
index 80de408ee1..eef52abe52 100644
--- a/libs/midi++2/midi++/manager.h
+++ b/libs/midi++2/midi++/manager.h
@@ -50,10 +50,9 @@ class Manager {
void cycle_end();
Port *add_port (PortRequest &);
- int remove_port (std::string port);
+ int remove_port (Port*);
Port *port (std::string name);
- Port *port (size_t number);
size_t nports () { return ports_by_device.size(); }
diff --git a/libs/midi++2/midi++/mmc.h b/libs/midi++2/midi++/mmc.h
index bc23beb0a1..6abbed8207 100644
--- a/libs/midi++2/midi++/mmc.h
+++ b/libs/midi++2/midi++/mmc.h
@@ -91,8 +91,10 @@ class MachineControl : public sigc::trackable
Port &port() { return _port; }
- void set_device_id (byte id);
- byte device_id () const { return _device_id; }
+ void set_receive_device_id (byte id);
+ void set_send_device_id (byte id);
+ byte receive_device_id () const { return _receive_device_id; }
+ byte send_device_id () const { return _send_device_id; }
static bool is_mmc (byte *sysex_buf, size_t len);
@@ -244,7 +246,8 @@ class MachineControl : public sigc::trackable
byte resume;
private:
- byte _device_id;
+ byte _receive_device_id;
+ byte _send_device_id;
MIDI::Port &_port;
void process_mmc_message (Parser &p, byte *, size_t len);
diff --git a/libs/midi++2/midi++/nullmidi.h b/libs/midi++2/midi++/nullmidi.h
index 1474da77ed..6ed94db71c 100644
--- a/libs/midi++2/midi++/nullmidi.h
+++ b/libs/midi++2/midi++/nullmidi.h
@@ -55,6 +55,13 @@ class Null_MidiPort : public Port
}
virtual int selectable() const { return -1; }
+
+ static std::string typestring;
+
+ protected:
+ std::string get_typestring () const {
+ return typestring;
+ }
};
} // namespace MIDI
diff --git a/libs/midi++2/midi++/port_request.h b/libs/midi++2/midi++/port_request.h
index 0cb4ffded6..dfde87a63c 100644
--- a/libs/midi++2/midi++/port_request.h
+++ b/libs/midi++2/midi++/port_request.h
@@ -53,6 +53,13 @@ struct PortRequest {
const std::string &xtype);
};
+struct PortSet {
+ PortSet (std::string str) : owner (str) { }
+
+ std::string owner;
+ std::list<PortRequest> ports;
+};
+
} // namespace MIDI
#endif // __midi_port_request_h__
diff --git a/libs/midi++2/midifactory.cc b/libs/midi++2/midifactory.cc
index de4a246bcf..9d98d9f6a7 100644
--- a/libs/midi++2/midifactory.cc
+++ b/libs/midi++2/midifactory.cc
@@ -18,6 +18,10 @@
*/
#include <cassert>
+
+#include <pbd/error.h>
+#include <pbd/convert.h>
+
#include <midi++/types.h>
#include <midi++/factory.h>
#include <midi++/nullmidi.h>
@@ -25,20 +29,32 @@
#ifdef WITH_JACK_MIDI
#include <midi++/jack.h>
+
+std::string MIDI::JACK_MidiPort::typestring = "jack";
#endif // WITH_JACK_MIDI
+std::string MIDI::Null_MidiPort::typestring = "null";
+std::string MIDI::FIFO_MidiPort::typestring = "fifo";
+
#ifdef WITH_ALSA
#include <midi++/alsa_sequencer.h>
#include <midi++/alsa_rawmidi.h>
+
+std::string MIDI::ALSA_SequencerMidiPort::typestring = "alsa/sequencer";
+std::string MIDI::ALSA_RawMidiPort::typestring = "alsa/raw";
+
#endif // WITH_ALSA
#ifdef WITH_COREMIDI
#include <midi++/coremidi_midiport.h>
-#endif // WITH_COREMIDI
+std::string MIDI::CoreMidi_MidiPort::typestring = "coremidi";
+
+#endif // WITH_COREMIDI
using namespace std;
using namespace MIDI;
+using namespace PBD;
// FIXME: void* data pointer, filthy
Port *
@@ -114,3 +130,88 @@ PortFactory::ignore_duplicate_devices (Port::Type type)
return ret;
}
+int
+PortFactory::get_known_ports (vector<PortSet>& ports)
+{
+ int n = 0;
+#ifdef WITH_ALSA
+ n += ALSA_SequencerMidiPort::discover (ports);
+#endif // WITH_ALSA
+
+#if WITH_COREMIDI
+ n += CoreMidi_MidiPort::discover (ports);
+#endif // WITH_COREMIDI
+
+ return n;
+}
+
+std::string
+PortFactory::default_port_type ()
+{
+#ifdef WITH_JACK_MIDI
+ return "jack";
+#endif
+
+#ifdef WITH_ALSA
+ return "alsa/sequencer";
+#endif
+
+#ifdef WITH_COREMIDI
+ return "coremidi";
+#endif // WITH_COREMIDI
+
+ PBD::fatal << "programming error: no default port type defined in midifactory.cc" << endmsg;
+ /*NOTREACHED*/
+ return "";
+}
+
+Port::Type
+PortFactory::string_to_type (const string& xtype)
+{
+ if (0){
+#ifdef WITH_ALSA
+ } else if (strings_equal_ignore_case (xtype, ALSA_RawMidiPort::typestring)) {
+ return Port::ALSA_RawMidi;
+ } else if (strings_equal_ignore_case (xtype, ALSA_SequencerMidiPort::typestring)) {
+ return Port::ALSA_Sequencer;
+#endif
+#ifdef WITH_COREMIDI
+ } else if (strings_equal_ignore_case (xtype, CoreMidi_MidiPort::typestring)) {
+ return Port::CoreMidi_MidiPort;
+#endif
+ } else if (strings_equal_ignore_case (xtype, Null_MidiPort::typestring)) {
+ return Port::Null;
+ } else if (strings_equal_ignore_case (xtype, FIFO_MidiPort::typestring)) {
+ return Port::FIFO;
+#ifdef WITH_JACK_MIDI
+ } else if (strings_equal_ignore_case (xtype, JACK_MidiPort::typestring)) {
+ return Port::JACK_Midi;
+#endif
+ }
+
+ return Port::Unknown;
+}
+
+string
+PortFactory::mode_to_string (int mode)
+{
+ if (mode == O_RDONLY) {
+ return "input";
+ } else if (mode == O_WRONLY) {
+ return "output";
+ }
+
+ return "duplex";
+}
+
+int
+PortFactory::string_to_mode (const string& str)
+{
+ if (strings_equal_ignore_case (str, "output") || strings_equal_ignore_case (str, "out")) {
+ return O_WRONLY;
+ } else if (strings_equal_ignore_case (str, "input") || strings_equal_ignore_case (str, "in")) {
+ return O_RDONLY;
+ }
+
+ return O_RDWR;
+}
diff --git a/libs/midi++2/midimanager.cc b/libs/midi++2/midimanager.cc
index 970674232d..ee73bdad86 100644
--- a/libs/midi++2/midimanager.cc
+++ b/libs/midi++2/midimanager.cc
@@ -126,44 +126,43 @@ Manager::add_port (PortRequest &req)
}
int
-Manager::remove_port (string name)
+Manager::remove_port (Port* port)
{
PortMap::iterator res;
- if ((res = ports_by_device.find (name)) == ports_by_device.end()) {
- return -1;
+ for (res = ports_by_device.begin(); res != ports_by_device.end(); ) {
+ PortMap::iterator tmp;
+ tmp = res;
+ ++tmp;
+ if (res->second == port) {
+ ports_by_device.erase (res);
+ }
+ res = tmp;
}
-
- ports_by_device.erase (res);
- ports_by_device.erase ((*res).second->name());
-
- delete (*res).second;
- return 0;
-}
-
-Port *
-Manager::port (string name)
-{
- PortMap::iterator res;
- for (res = ports_by_tag.begin(); res != ports_by_tag.end(); res++) {
- if (name == (*res).first) {
- return (*res).second;
- }
+ for (res = ports_by_tag.begin(); res != ports_by_tag.end(); ) {
+ PortMap::iterator tmp;
+ tmp = res;
+ ++tmp;
+ if (res->second == port) {
+ ports_by_tag.erase (res);
+ }
+ res = tmp;
}
+
+ delete port;
return 0;
}
Port *
-Manager::port (size_t portnum)
-
+Manager::port (string name)
{
PortMap::iterator res;
for (res = ports_by_tag.begin(); res != ports_by_tag.end(); res++) {
- if ((*res).second->number() == portnum) {
+ if (name == (*res).first) {
return (*res).second;
}
}
diff --git a/libs/midi++2/mmc.cc b/libs/midi++2/mmc.cc
index 28d6393fb4..1ede281c28 100644
--- a/libs/midi++2/mmc.cc
+++ b/libs/midi++2/mmc.cc
@@ -202,7 +202,8 @@ MachineControl::MachineControl (Port &p, float version,
build_mmc_cmd_map ();
- _device_id = 0;
+ _receive_device_id = 0;
+ _send_device_id = 0x7f;
if ((parser = _port.input()) != 0) {
parser->mmc.connect
@@ -214,10 +215,15 @@ MachineControl::MachineControl (Port &p, float version,
}
void
-MachineControl::set_device_id (byte id)
+MachineControl::set_receive_device_id (byte id)
+{
+ _receive_device_id = id & 0x7f;
+}
+void
+MachineControl::set_send_device_id (byte id)
{
- _device_id = id & 0x7f;
+ _send_device_id = id & 0x7f;
}
bool
@@ -258,14 +264,14 @@ MachineControl::process_mmc_message (Parser &p, byte *msg, size_t len)
*/
#if 0
- cerr << "*** me = " << (int) _device_id << " MMC message: len = " << len << "\n\t";
+ cerr << "*** me = " << (int) _receive_device_id << " MMC message: len = " << len << "\n\t";
for (size_t i = 0; i < len; i++) {
cerr << hex << (int) msg[i] << dec << ' ';
}
cerr << endl;
#endif
- if (msg[1] != 0x7f && msg[1] != _device_id) {
+ if (msg[1] != 0x7f && msg[1] != _receive_device_id) {
return;
}
diff --git a/libs/pbd/convert.cc b/libs/pbd/convert.cc
index 832c54acd8..07fcc09ace 100644
--- a/libs/pbd/convert.cc
+++ b/libs/pbd/convert.cc
@@ -233,5 +233,21 @@ length2string (const int64_t frames, const double sample_rate)
return duration_str;
}
+static bool
+chars_equal_ignore_case(char x, char y)
+{
+ static std::locale loc;
+ return toupper(x, loc) == toupper(y, loc);
+}
+
+bool
+strings_equal_ignore_case (const string& a, const string& b)
+{
+ if (a.length() == b.length()) {
+ return std::equal (a.begin(), a.end(), b.begin(), chars_equal_ignore_case);
+ }
+ return false;
+}
+
} // namespace PBD
diff --git a/libs/pbd/pbd/convert.h b/libs/pbd/pbd/convert.h
index 55006529ae..00176659cf 100644
--- a/libs/pbd/pbd/convert.h
+++ b/libs/pbd/pbd/convert.h
@@ -35,6 +35,7 @@ void url_decode (std::string&);
std::string length2string (const int64_t frames, const double sample_rate);
std::vector<std::string> internationalize (const char *, const char **);
+bool strings_equal_ignore_case (const std::string& a, const std::string& b);
} //namespace PBD