Experimental GenericPluginUI MIDI patch select.
authorRobin Gareus <robin@gareus.org>
Fri, 8 Sep 2017 17:42:24 +0000 (19:42 +0200)
committerRobin Gareus <robin@gareus.org>
Fri, 8 Sep 2017 21:35:00 +0000 (23:35 +0200)
gtk2_ardour/generic_pluginui.cc
gtk2_ardour/plugin_ui.h

index ffb4a2cc9216039479c2b33e4b8afd2282d45cd8..dbb4d7f7568c0cda5a76b385b679787f8ac22857 100644 (file)
 #include "pbd/xml++.h"
 #include "pbd/failed_constructor.h"
 
+#include "evoral/midi_events.h"
+#include "evoral/PatchChange.hpp"
+
+#include "midi++/midnam_patch.h"
+
+#include "ardour/midi_patch_manager.h"
+#include "ardour/midi_track.h"
 #include "ardour/plugin.h"
 #include "ardour/plugin_insert.h"
 #include "ardour/session.h"
 #include "ardour/value_as_string.h"
 
+#include "gtkmm2ext/menu_elems.h"
 #include "gtkmm2ext/utils.h"
 #include "gtkmm2ext/doi.h"
 
@@ -152,6 +160,13 @@ GenericPluginUI::GenericPluginUI (boost::shared_ptr<PluginInsert> pi, bool scrol
        prefheight = 0;
        build ();
 
+       if (insert->plugin()->has_midnam() && insert->plugin()->knows_bank_patch()) {
+               /* right now PC are sent via MIDI Track controls only */
+               if (dynamic_cast<MidiTrack*> (insert->owner())) {
+                       build_midi_table ();
+               }
+       }
+
        if (is_scrollable) {
                scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
                scroller.set_name ("PluginEditor");
@@ -400,10 +415,11 @@ GenericPluginUI::automatic_layout (const std::vector<ControlUI*>& control_uis)
        button_table->set_homogeneous (false);
        button_table->set_row_spacings (2);
        button_table->set_col_spacings (2);
+       button_table->set_border_width (5);
+
        output_table->set_homogeneous (true);
        output_table->set_row_spacings (2);
        output_table->set_col_spacings (2);
-       button_table->set_border_width (5);
        output_table->set_border_width (5);
 
 
@@ -596,6 +612,143 @@ GenericPluginUI::custom_layout (const std::vector<ControlUI*>& control_uis)
        }
 }
 
+void
+GenericPluginUI::build_midi_table ()
+{
+       Gtk::Table* pgm_table = manage (new Gtk::Table (8, 2));
+
+       pgm_table->set_homogeneous (true);
+       pgm_table->set_row_spacings (2);
+       pgm_table->set_col_spacings (2);
+       pgm_table->set_border_width (5);
+
+       Frame* frame = manage (new Frame);
+       frame->set_name ("BaseFrame");
+       frame->set_label (_("MIDI Progams (sent to track)"));
+       //frame->set_label (_("MIDI Progams (volatile)"));
+       frame->add (*pgm_table);
+       hpacker.pack_start (*frame, true, true);
+
+       for (uint8_t chn = 0; chn < 16; ++chn) {
+               int col = chn / 8;
+               int row = chn % 8;
+               ArdourDropdown* cui = manage (new ArdourWidgets::ArdourDropdown ());
+               // TODO set size_request
+               cui->set_text_ellipsize (Pango::ELLIPSIZE_END);
+               cui->set_layout_ellipsize_width (PANGO_SCALE * 112 * UIConfiguration::instance ().get_ui_scale ());
+               midi_pgmsel.push_back (cui);
+               pgm_table->attach (*cui, col, col + 1, row, row+1, FILL|EXPAND, FILL);
+       }
+
+       midi_refill_patches ();
+
+       insert->plugin()->BankPatchChange.connect (
+                       midi_connections, invalidator (*this),
+                       boost::bind (&GenericPluginUI::midi_bank_patch_change, this, _1),
+                       gui_context());
+
+       /* Note: possible race with MidiTimeAxisView::update_patch_selector()
+        * which uses this signal to update/re-register the midnam (also in gui context).
+        * MTAV does register before us, so the midnam should already be updated when
+        * we're notified.
+        */
+       insert->plugin()->UpdateMidnam.connect (
+                       midi_connections, invalidator (*this),
+                       boost::bind (&GenericPluginUI::midi_refill_patches, this),
+                       gui_context());
+}
+
+void
+GenericPluginUI::midi_refill_patches ()
+{
+       assert (midi_pgmsel.size() == 16);
+
+       pgm_names.clear ();
+
+       const std::string model = insert->plugin ()->midnam_model ();
+       std::string mode;
+       const std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance().custom_device_mode_names_by_model (model);
+       if (device_modes.size() > 0) {
+               mode = device_modes.front();
+       }
+
+       for (uint8_t chn = 0; chn < 16; ++chn) {
+               midi_pgmsel[chn]->clear_items ();
+               boost::shared_ptr<MIDI::Name::ChannelNameSet> cns =
+                       MIDI::Name::MidiPatchManager::instance().find_channel_name_set (model, mode, chn);
+
+               if (cns) {
+                       using namespace Menu_Helpers;
+                       using namespace Gtkmm2ext;
+
+                       for (MIDI::Name::ChannelNameSet::PatchBanks::const_iterator i = cns->patch_banks().begin(); i != cns->patch_banks().end(); ++i) {
+                               const MIDI::Name::PatchNameList& patches = (*i)->patch_name_list ();
+                               for (MIDI::Name::PatchNameList::const_iterator j = patches.begin(); j != patches.end(); ++j) {
+                                       const std::string pgm = (*j)->name ();
+                                       MIDI::Name::PatchPrimaryKey const& key = (*j)->patch_primary_key ();
+                                       assert ((*i)->number () == key.bank());
+                                       const uint32_t bp = (key.bank() << 7) | key.program();
+                                       midi_pgmsel[chn]->AddMenuElem (MenuElemNoMnemonic (pgm, sigc::bind (sigc::mem_fun (*this, &GenericPluginUI::midi_bank_patch_select), chn, bp)));
+                                       pgm_names[bp] = pgm;
+                               }
+                       }
+               }
+
+               midi_bank_patch_change (chn);
+       }
+}
+
+void
+GenericPluginUI::midi_bank_patch_change (uint8_t chn)
+{
+       assert (chn < 16 && midi_pgmsel.size() == 16);
+       uint32_t bankpgm = insert->plugin()->bank_patch (chn);
+       if (bankpgm == UINT32_MAX) {
+               midi_pgmsel[chn]->set_text (_("--Unset--"));
+       } else {
+               int bank = bankpgm >> 7;
+               int pgm = bankpgm & 127;
+               if (pgm_names.find (bankpgm) != pgm_names.end ()) {
+                       midi_pgmsel[chn]->set_text (pgm_names[bankpgm]);
+               } else {
+                       midi_pgmsel[chn]->set_text (string_compose ("Bank %1,%2 Pgm %3",
+                                               (bank >> 7) + 1, (bank & 127) + 1, pgm +1));
+               }
+       }
+}
+
+void
+GenericPluginUI::midi_bank_patch_select (uint8_t chn, uint32_t bankpgm)
+{
+       int bank = bankpgm >> 7;
+       int pgm = bankpgm & 127;
+       if (1) {
+               /* send to track */
+               MidiTrack* mt = dynamic_cast<MidiTrack*> (insert->owner());
+               if (!mt) {
+                       return;
+               }
+
+               boost::shared_ptr<AutomationControl> bank_msb = mt->automation_control(Evoral::Parameter (MidiCCAutomation, chn, MIDI_CTL_MSB_BANK), true);
+               boost::shared_ptr<AutomationControl> bank_lsb = mt->automation_control(Evoral::Parameter (MidiCCAutomation, chn, MIDI_CTL_LSB_BANK), true);
+               boost::shared_ptr<AutomationControl> program = mt->automation_control(Evoral::Parameter (MidiPgmChangeAutomation, chn), true);
+
+               bank_msb->set_value (bank >> 7, PBD::Controllable::NoGroup);
+               bank_lsb->set_value (bank & 127, PBD::Controllable::NoGroup);
+               program->set_value (pgm, PBD::Controllable::NoGroup);
+
+       } else {
+#if 0 // TODO inject directly to plugin..
+               const int cnt = insert->get_count();
+               for (int i=0; i < cnt; i++ ) {
+                       boost::shared_ptr<LV2Plugin> lv2i = boost::dynamic_pointer_cast<LV2Plugin> (insert->plugin(i));
+                       //lv2i->write_from_ui(port_index, format, buffer_size, (const uint8_t*)buffer);
+
+               }
+#endif
+       }
+}
+
 GenericPluginUI::ControlUI::ControlUI (const Evoral::Parameter& p)
        : param(p)
        , automate_button (X_("")) // force creation of a label
index ed1c72260594306004f47f5b9d76fc137d9e0854..d685e86bdca673523051b659fffa8292a8f23c0e 100644 (file)
@@ -303,6 +303,14 @@ private:
 
        void scroller_size_request (Gtk::Requisition*);
        Gtk::ScrolledWindow scroller;
+
+       void build_midi_table ();
+       void midi_refill_patches ();
+       void midi_bank_patch_change (uint8_t chn);
+       void midi_bank_patch_select (uint8_t chn, uint32_t bankpgm);
+       std::vector<ArdourWidgets::ArdourDropdown*> midi_pgmsel;
+       PBD::ScopedConnectionList midi_connections;
+       std::map<uint32_t, std::string> pgm_names;
 };
 
 class PluginUIWindow : public ArdourWindow