diff --git a/gtk2_ardour/patch_change_widget.cc b/gtk2_ardour/patch_change_widget.cc index bb80e21292..f932a4dae0 100644 --- a/gtk2_ardour/patch_change_widget.cc +++ b/gtk2_ardour/patch_change_widget.cc @@ -23,14 +23,15 @@ #include "pbd/unwind.h" -#include "evoral/midi_events.h" #include "evoral/PatchChange.h" +#include "evoral/midi_events.h" #include "midi++/midnam_patch.h" #include "ardour/instrument_info.h" #include "ardour/midi_track.h" #include "ardour/plugin_insert.h" +#include "ardour/triggerbox.h" #include "gtkmm2ext/menu_elems.h" #include "gtkmm2ext/utils.h" @@ -248,6 +249,148 @@ PatchBankList::set_active_pgm (uint8_t p) /* ****************************************************************************/ +PatchChangeTab::PatchChangeTab (boost::shared_ptr r, boost::shared_ptr t, int channel) + : PatchBankList (r) + , _enable_btn (_("Override Patch Changes"), ArdourWidgets::ArdourButton::led_default_elements) + , _channel (channel) + , _bank (0) + , _ignore_callback (false) + , _trigger (t) +{ + if (_trigger->patch_change_set (_channel)) { + _bank = _trigger->patch_change (_channel).bank (); + _enable_btn.set_active (true); + } else { + _enable_btn.set_active (false); + } + + Box* box; + box = manage (new HBox ()); + box->set_border_width (2); + box->set_spacing (4); + box->pack_start (_enable_btn, false, false); + box->pack_start (*manage (new Label (_("Bank:"))), false, false); + box->pack_start (_bank_select, true, true); + box->pack_start (*manage (new Label (_("MSB:"))), false, false); + box->pack_start (_bank_msb_spin, false, false); + box->pack_start (*manage (new Label (_("LSB:"))), false, false); + box->pack_start (_bank_lsb_spin, false, false); + + pack_start (*box, false, false); + + _program_table.set_spacings (1); + pack_start (_program_table, true, true); + + set_spacing (4); + show_all (); + + _enable_btn.signal_clicked.connect (sigc::mem_fun (*this, &PatchChangeTab::enable_toggle)); + + _trigger->PropertyChanged.connect (_trigger_connection, invalidator (*this), boost::bind (&PatchChangeTab::trigger_property_changed, this, _1), gui_context ()); + if (!boost::dynamic_pointer_cast (_route)) { + processors_changed (); + } + refill_banks (); + update_sensitivity (); +} + +void +PatchChangeTab::enable_toggle () +{ + if (_ignore_callback) { + return; + } + if (_enable_btn.get_active ()) { + _trigger->unset_patch_change (_channel); + } else { + select_program (program ()); + } + update_sensitivity (); +} + +void +PatchChangeTab::update_sensitivity () +{ + bool en = _trigger->patch_change_set (_channel); + _program_table.set_sensitive (en); + _bank_select.set_sensitive (en); + _bank_msb_spin.set_sensitive (en); + _bank_lsb_spin.set_sensitive (en); +} + +void +PatchChangeTab::trigger_property_changed (PBD::PropertyChange const& what_changed) +{ + if (what_changed.contains (Properties::patch_change)) { + PBD::Unwinder uw (_ignore_callback, true); + _enable_btn.set_active (_trigger->patch_change_set (_channel)); + refill_banks (); + } +} + +void +PatchChangeTab::select_bank (uint32_t bank) +{ + _bank = bank; + select_program (program ()); +} + +void +PatchChangeTab::select_program (uint8_t pgm) +{ + if (pgm > 127) { + return; + } + + Evoral::PatchChange pc (0, _channel, pgm, _bank); + _trigger->set_patch_change (pc); +} + +void +PatchChangeTab::refresh () +{ + refill_banks (); +} + +void +PatchChangeTab::refill_banks () +{ + refill (_channel); + set_active_pgm (program ()); +} + +int +PatchChangeTab::bank (uint8_t c) const +{ + assert (c == _channel); + if (_trigger->patch_change_set (_channel)) { + return _trigger->patch_change (_channel).bank (); + } + return _bank; +} + +uint8_t +PatchChangeTab::program () const +{ + if (_trigger->patch_change_set (_channel)) { + return _trigger->patch_change (_channel).program (); + } + return 0; +} + +void +PatchChangeTab::instrument_info_changed () +{ + refill_banks (); +} + +void +PatchChangeTab::processors_changed () +{ +} + +/* ****************************************************************************/ + PatchChangeWidget::PatchChangeWidget (boost::shared_ptr r) : PatchBankList (r) , _channel (-1) @@ -277,7 +420,7 @@ PatchChangeWidget::PatchChangeWidget (boost::shared_ptr r) pack_start (_program_table, true, true); if (!boost::dynamic_pointer_cast (_route)) { - pack_start ( *manage (new Label (_("Note: Patch Selection is volatile (only Midi-Tracks retain bank/patch selection)."))), false, false); + pack_start (*manage (new Label (_("Note: Patch Selection is volatile (only Midi-Tracks retain bank/patch selection)."))), false, false); } box = manage (new HBox ()); @@ -657,6 +800,29 @@ PatchChangeWidget::program (uint8_t chn) const /* ***************************************************************************/ +PatchChangeTriggerDialog::PatchChangeTriggerDialog (boost::shared_ptr r, boost::shared_ptr t) + : ArdourDialog (string_compose (_("Select Patch for \"%1\""), t->name ()), false, false) +{ + for (uint32_t chn = 0; chn < 16; ++chn) { + _w[chn] = manage (new PatchChangeTab (r, t, chn)); + _notebook.append_page (*_w[chn], string_compose (_("Chn %1"), chn + 1)); + } + + _notebook.signal_switch_page ().connect (sigc::mem_fun (*this, &PatchChangeTriggerDialog::on_switch_page)); + _notebook.set_current_page (0); + + get_vbox ()->add (_notebook); + _notebook.show_all (); +} + +void +PatchChangeTriggerDialog::on_switch_page (GtkNotebookPage*, guint page_num) +{ + _w[page_num]->refresh (); +} + +/* ***************************************************************************/ + PatchChangeGridDialog::PatchChangeGridDialog (boost::shared_ptr r) : ArdourDialog (string_compose (_("Select Patch for \"%1\""), r->name()), false, false) , w (r) diff --git a/gtk2_ardour/patch_change_widget.h b/gtk2_ardour/patch_change_widget.h index 74ad071bbd..9f3818c3f3 100644 --- a/gtk2_ardour/patch_change_widget.h +++ b/gtk2_ardour/patch_change_widget.h @@ -20,11 +20,12 @@ #define __gtkardour_patch_change_widget_h__ #include +#include #include #include -#include "pbd/signals.h" #include "midi++/midnam_patch.h" +#include "pbd/signals.h" #include "ardour/route.h" @@ -34,6 +35,10 @@ #include "ardour_dialog.h" #include "pianokeyboard.h" +namespace ARDOUR { + class MIDITrigger; +}; + class PatchBankList : virtual public sigc::trackable { public: @@ -69,6 +74,39 @@ private: PBD::ScopedConnection _route_connection; }; +class PatchChangeTab : public Gtk::VBox, public PatchBankList +{ +public: + PatchChangeTab (boost::shared_ptr, boost::shared_ptr, int channel); + + void refresh (); + +protected: + int bank (uint8_t) const; + uint8_t program () const; + + /* Implement PatchBankList */ + void select_bank (uint32_t); + void select_program (uint8_t); + void instrument_info_changed (); + void processors_changed (); + +private: + void refill_banks (); + void trigger_property_changed (PBD::PropertyChange const&); + void enable_toggle (); + void update_sensitivity (); + + ArdourWidgets::ArdourButton _enable_btn; + + int _channel; + int _bank; + bool _ignore_callback; + + boost::shared_ptr _trigger; + PBD::ScopedConnection _trigger_connection; +}; + class PatchChangeWidget : public Gtk::VBox, public PatchBankList { public: @@ -129,6 +167,18 @@ private: void note_off_event_handler (int); }; +class PatchChangeTriggerDialog : public ArdourDialog +{ +public: + PatchChangeTriggerDialog (boost::shared_ptr, boost::shared_ptr); + +private: + void on_switch_page (GtkNotebookPage*, guint page_num); + + Gtk::Notebook _notebook; + PatchChangeTab* _w[16]; +}; + class PatchChangeGridDialog : public ArdourDialog { public: diff --git a/gtk2_ardour/trigger_ui.cc b/gtk2_ardour/trigger_ui.cc index 93be04ac59..385628eba2 100644 --- a/gtk2_ardour/trigger_ui.cc +++ b/gtk2_ardour/trigger_ui.cc @@ -42,6 +42,7 @@ #include "ardour_ui.h" #include "gui_thread.h" #include "keyboard.h" +#include "patch_change_widget.h" #include "public_editor.h" #include "region_view.h" #include "trigger_jump_dialog.h" @@ -201,6 +202,19 @@ TriggerUI::choose_color () _color_dialog.hide (); } +void +TriggerUI::choose_patch () +{ + /* XXX can we get a shared_from (owner()) ? */ + SessionObject* obj = trigger ()->box ().owner (); + Session* s = AudioEngine::instance ()->session (); + boost::shared_ptr stripable = s->stripable_by_id (obj->id ()); + assert (boost::dynamic_pointer_cast (trigger ()) != 0); + + PatchChangeTriggerDialog pcd (boost::dynamic_pointer_cast (stripable), boost::dynamic_pointer_cast (trigger ())); + pcd.run (); +} + void TriggerUI::choose_sample (bool allow_multiple_select) { @@ -442,11 +456,15 @@ TriggerUI::context_menu () #if DOUBLE_CLICK_IS_NOT_OBVIOUS_ENOUGH items.push_back (MenuElem (_("Edit..."), sigc::mem_fun (*this, &TriggerUI::edit_trigger))); #endif + if (boost::dynamic_pointer_cast (trigger ())) { + items.push_back (MenuElem (_("Select Patch.."), sigc::mem_fun (*this, &TriggerUI::choose_patch))); + } items.push_back (SeparatorElem()); items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &TriggerUI::choose_color))); items.push_back (SeparatorElem()); items.push_back (MenuElem (_("Clear"), sigc::mem_fun (*this, &TriggerUI::clear_trigger))); + _context_menu->popup (1, gtk_get_current_event_time ()); } diff --git a/gtk2_ardour/trigger_ui.h b/gtk2_ardour/trigger_ui.h index dc2bcbae10..0b9b8e73bd 100644 --- a/gtk2_ardour/trigger_ui.h +++ b/gtk2_ardour/trigger_ui.h @@ -69,6 +69,7 @@ public: ARDOUR::TriggerBox& triggerbox() const { return trigger()->box(); } void choose_color (); + void choose_patch (); void choose_sample (bool allow_multiple_select); void sample_chosen (int r);