From aa11cb96b3c9b35d40a5fbefc41aa75ccf957f80 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Fri, 8 Sep 2017 19:42:24 +0200 Subject: [PATCH] Experimental GenericPluginUI MIDI patch select. --- gtk2_ardour/generic_pluginui.cc | 155 +++++++++++++++++++++++++++++++- gtk2_ardour/plugin_ui.h | 8 ++ 2 files changed, 162 insertions(+), 1 deletion(-) diff --git a/gtk2_ardour/generic_pluginui.cc b/gtk2_ardour/generic_pluginui.cc index ffb4a2cc92..dbb4d7f756 100644 --- a/gtk2_ardour/generic_pluginui.cc +++ b/gtk2_ardour/generic_pluginui.cc @@ -34,11 +34,19 @@ #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 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 (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& 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& 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 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 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 (insert->owner()); + if (!mt) { + return; + } + + boost::shared_ptr bank_msb = mt->automation_control(Evoral::Parameter (MidiCCAutomation, chn, MIDI_CTL_MSB_BANK), true); + boost::shared_ptr bank_lsb = mt->automation_control(Evoral::Parameter (MidiCCAutomation, chn, MIDI_CTL_LSB_BANK), true); + boost::shared_ptr 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 lv2i = boost::dynamic_pointer_cast (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 diff --git a/gtk2_ardour/plugin_ui.h b/gtk2_ardour/plugin_ui.h index ed1c722605..d685e86bdc 100644 --- a/gtk2_ardour/plugin_ui.h +++ b/gtk2_ardour/plugin_ui.h @@ -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 midi_pgmsel; + PBD::ScopedConnectionList midi_connections; + std::map pgm_names; }; class PluginUIWindow : public ArdourWindow -- 2.30.2