diff options
author | Robin Gareus <robin@gareus.org> | 2017-09-08 00:30:55 +0200 |
---|---|---|
committer | Robin Gareus <robin@gareus.org> | 2017-09-08 02:19:31 +0200 |
commit | 637673009304a4c23d9f26442e08cbde05fafb4d (patch) | |
tree | 672fd0a34457d3ee58ee46708f8e9f1f323defc8 /gtk2_ardour | |
parent | 187748874c288321f3197fb1e3dfb64f86c9c0bf (diff) |
Rework Patch-Change/Select Dialog
Diffstat (limited to 'gtk2_ardour')
-rw-r--r-- | gtk2_ardour/midi_time_axis.cc | 34 | ||||
-rw-r--r-- | gtk2_ardour/midi_time_axis.h | 4 | ||||
-rw-r--r-- | gtk2_ardour/patch_change_widget.cc | 290 | ||||
-rw-r--r-- | gtk2_ardour/patch_change_widget.h | 93 | ||||
-rw-r--r-- | gtk2_ardour/wscript | 1 |
5 files changed, 389 insertions, 33 deletions
diff --git a/gtk2_ardour/midi_time_axis.cc b/gtk2_ardour/midi_time_axis.cc index 33160f8da3..ca67244731 100644 --- a/gtk2_ardour/midi_time_axis.cc +++ b/gtk2_ardour/midi_time_axis.cc @@ -77,6 +77,7 @@ #include "midi_region_view.h" #include "midi_time_axis.h" #include "patch_change_dialog.h" +#include "patch_change_widget.h" #include "piano_roll_header.h" #include "playlist_selector.h" #include "plugin_selector.h" @@ -1082,33 +1083,6 @@ MidiTimeAxisView::build_color_mode_menu() } void -MidiTimeAxisView::immediate_patch_chnage_response (int response) -{ - if (response != RESPONSE_ACCEPT || !_route) { - delete _patch_change_dialog; - _patch_change_dialog = 0; - return; - } - Evoral::PatchChange<Evoral::Beats> p (_patch_change_dialog->patch ()); - - uint8_t chn = p.channel(); - - boost::shared_ptr<AutomationControl> bank_msb = _route->automation_control(Evoral::Parameter (MidiCCAutomation, chn, MIDI_CTL_MSB_BANK), true); - boost::shared_ptr<AutomationControl> bank_lsb = _route->automation_control(Evoral::Parameter (MidiCCAutomation, chn, MIDI_CTL_LSB_BANK), true); - boost::shared_ptr<AutomationControl> program = _route->automation_control(Evoral::Parameter (MidiPgmChangeAutomation, chn), true); - - if (!bank_msb || ! bank_lsb || !program) { - _patch_change_dialog->show (); - return; - } - - bank_msb->set_value (p.bank_msb (), Controllable::NoGroup); - bank_lsb->set_value (p.bank_lsb (), Controllable::NoGroup); - program->set_value (p.program () , Controllable::NoGroup); - _patch_change_dialog->show (); -} - -void MidiTimeAxisView::send_patch_change () { if (!_route) { @@ -1119,11 +1093,9 @@ MidiTimeAxisView::send_patch_change () return; } - Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0); - PatchChangeDialog* d = new PatchChangeDialog (0, 0, empty, _route->instrument_info(), Gtk::Stock::APPLY, false, false); - d->signal_response().connect (sigc::mem_fun (*this, &MidiTimeAxisView::immediate_patch_chnage_response)); + PatchChangeGridDialog* d = new PatchChangeGridDialog (string_compose (_("Select Patch for '%1'"), _route->name ()), _route); _patch_change_dialog = d; - _patch_change_dialog->present (); + d->present (); } void diff --git a/gtk2_ardour/midi_time_axis.h b/gtk2_ardour/midi_time_axis.h index 6bbb359458..72167204ed 100644 --- a/gtk2_ardour/midi_time_axis.h +++ b/gtk2_ardour/midi_time_axis.h @@ -67,7 +67,7 @@ class PianoRollHeader; class StepEntry; class StepEditor; class MidiChannelSelectorWindow; -class PatchChangeDialog; +class PatchChangeGridDialog; #define NO_MIDI_NOTE 0xff @@ -199,7 +199,7 @@ private: StepEditor* _step_editor; void immediate_patch_chnage_response (int response); - PatchChangeDialog* _patch_change_dialog; + PatchChangeGridDialog* _patch_change_dialog; }; diff --git a/gtk2_ardour/patch_change_widget.cc b/gtk2_ardour/patch_change_widget.cc new file mode 100644 index 0000000000..e4409b1945 --- /dev/null +++ b/gtk2_ardour/patch_change_widget.cc @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2017 Robin Gareus <robin@gareus.org> + * Copyright (C) 2011 Paul Davis + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <gtkmm/frame.h> +#include <boost/algorithm/string.hpp> + +#include "pbd/unwind.h" +#include "evoral/PatchChange.hpp" +#include "midi++/midnam_patch.h" +#include "ardour/instrument_info.h" +#include "ardour/midi_track.h" + +#include "widgets/tooltips.h" + +#include "gui_thread.h" +#include "patch_change_widget.h" +#include "ui_config.h" + +#include "pbd/i18n.h" + +using namespace Gtk; +using namespace ARDOUR; + +PatchChangeWidget::PatchChangeWidget (boost::shared_ptr<ARDOUR::Route> r) + : _route (r) + , _bank_msb_spin (*manage (new Adjustment (0, 0, 127, 1, 16))) + , _bank_lsb_spin (*manage (new Adjustment (0, 0, 127, 1, 16))) + , _program_table (/*rows*/ 16, /*cols*/ 8, true) + , _channel (-1) + , _ignore_spin_btn_signals (false) + , _info (r->instrument_info ()) +{ + assert (boost::dynamic_pointer_cast<MidiTrack> (r)); + + Box* box; + box = manage (new HBox ()); + box->set_border_width (2); + box->set_spacing (4); + box->pack_start (*manage (new Label (_("Channel:"))), false, false); + box->pack_start (_channel_select, 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); + + for (uint8_t pgm = 0; pgm < 128; ++pgm) { + _program_btn[pgm].set_text_ellipsize (Pango::ELLIPSIZE_END); + _program_btn[pgm].set_layout_ellipsize_width (PANGO_SCALE * 112 * UIConfiguration::instance ().get_ui_scale ()); + int row = pgm % 16; + int col = pgm / 16; + _program_table.attach (_program_btn[pgm], col, col + 1, row, row + 1); + _program_btn[pgm].signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PatchChangeWidget::select_program), pgm)); + } + + using namespace Menu_Helpers; + for (uint32_t chn = 0; chn < 16; ++chn) { + char buf[8]; + snprintf (buf, sizeof(buf), "%d", chn + 1); + _channel_select.AddMenuElem (MenuElem (buf, sigc::bind (sigc::mem_fun (*this, &PatchChangeWidget::select_channel), chn))); + } + + _bank_msb_spin.signal_changed().connect (sigc::mem_fun (*this, &PatchChangeWidget::select_bank_spin)); + _bank_lsb_spin.signal_changed().connect (sigc::mem_fun (*this, &PatchChangeWidget::select_bank_spin)); + + _info.Changed.connect (_info_changed_connection, invalidator (*this), + boost::bind (&PatchChangeWidget::instrument_info_changed, this), gui_context()); + + set_spacing (4); + show_all (); +} + +PatchChangeWidget::~PatchChangeWidget () +{ +} + +void +PatchChangeWidget::on_show () +{ + Gtk::VBox::on_show (); + _channel = -1; + select_channel (0); +} + +void +PatchChangeWidget::on_hide () +{ + Gtk::VBox::on_hide (); + _ac_connections.drop_connections (); +} + + +void +PatchChangeWidget::select_channel (uint8_t chn) +{ + assert (_route); + + if (_channel == chn) { + return; + } + + _channel_select.set_text (string_compose ("%1", (int)(chn + 1))); + _channel = chn; + + _ac_connections.drop_connections (); + + boost::shared_ptr<AutomationControl> bank_msb = _route->automation_control(Evoral::Parameter (MidiCCAutomation, chn, MIDI_CTL_MSB_BANK), true); + boost::shared_ptr<AutomationControl> bank_lsb = _route->automation_control(Evoral::Parameter (MidiCCAutomation, chn, MIDI_CTL_LSB_BANK), true); + boost::shared_ptr<AutomationControl> program = _route->automation_control(Evoral::Parameter (MidiPgmChangeAutomation, chn), true); + + bank_msb->Changed.connect (_ac_connections, invalidator (*this), + boost::bind (&PatchChangeWidget::bank_changed, this), gui_context ()); + bank_lsb->Changed.connect (_ac_connections, invalidator (*this), + boost::bind (&PatchChangeWidget::bank_changed, this), gui_context ()); + program->Changed.connect (_ac_connections, invalidator (*this), + boost::bind (&PatchChangeWidget::program_changed, this), gui_context ()); + + refill_banks (); +} + +void +PatchChangeWidget::refill_banks () +{ + using namespace Menu_Helpers; + + _current_patch_bank.reset (); + _bank_select.clear_items (); + + const int b = bank (_channel); + + { + PBD::Unwinder<bool> (_ignore_spin_btn_signals, true); + _bank_msb_spin.set_value (b >> 7); + _bank_lsb_spin.set_value (b & 127); + } + + boost::shared_ptr<MIDI::Name::ChannelNameSet> cns = _info.get_patches (_channel); + if (cns) { + for (MIDI::Name::ChannelNameSet::PatchBanks::const_iterator i = cns->patch_banks().begin(); i != cns->patch_banks().end(); ++i) { + std::string n = (*i)->name (); + boost::replace_all (n, "_", " "); + _bank_select.AddMenuElem (MenuElem (n, sigc::bind (sigc::mem_fun (*this, &PatchChangeWidget::select_bank), (*i)->number ()))); + if ((*i)->number () == b) { + _current_patch_bank = *i; + _bank_select.set_text (n); + } + } + } + + if (!_current_patch_bank) { + std::string n = string_compose (_("Bank %1"), b); + _bank_select.AddMenuElem (MenuElem (n, sigc::bind (sigc::mem_fun (*this, &PatchChangeWidget::select_bank), b))); + _bank_select.set_text (n); + } + + refill_program_list (); +} + +void +PatchChangeWidget::refill_program_list () +{ + // TODO if _current_patch_bank!=0, only clear/reset unused patches + for (uint8_t pgm = 0; pgm < 128; ++pgm) { + std::string n = string_compose (_("Pgm-%1"), (int)(pgm +1)); + _program_btn[pgm].set_text (n); + set_tooltip (_program_btn[pgm], n); + } + + if (_current_patch_bank) { + const MIDI::Name::PatchNameList& patches = _current_patch_bank->patch_name_list (); + for (MIDI::Name::PatchNameList::const_iterator i = patches.begin(); i != patches.end(); ++i) { + std::string n = (*i)->name (); + boost::replace_all (n, "_", " "); + MIDI::Name::PatchPrimaryKey const& key = (*i)->patch_primary_key (); + + assert (key.program () < 128); + assert (key.bank () == bank (_channel)); + + const uint8_t pgm = key.program(); + _program_btn[pgm].set_text (n); + set_tooltip (_program_btn[pgm], string_compose (_("%1 (Pgm-%2)"), n, (int)(pgm +1))); + } + } + + program_changed (); +} + +/* ***** user GUI actions *****/ + +void +PatchChangeWidget::select_bank_spin () +{ + if (_ignore_spin_btn_signals) { + return; + } + const uint32_t b = (_bank_msb_spin.get_value_as_int() << 7) + _bank_lsb_spin.get_value_as_int(); + select_bank (b); +} + +void +PatchChangeWidget::select_bank (uint32_t bank) +{ + boost::shared_ptr<AutomationControl> bank_msb = _route->automation_control(Evoral::Parameter (MidiCCAutomation, _channel, MIDI_CTL_MSB_BANK), true); + boost::shared_ptr<AutomationControl> bank_lsb = _route->automation_control(Evoral::Parameter (MidiCCAutomation, _channel, MIDI_CTL_LSB_BANK), true); + + bank_msb->set_value (bank >> 7, PBD::Controllable::NoGroup); + bank_lsb->set_value (bank & 127, PBD::Controllable::NoGroup); +} + +void +PatchChangeWidget::select_program (uint8_t pgm) +{ + boost::shared_ptr<AutomationControl> program = _route->automation_control(Evoral::Parameter (MidiPgmChangeAutomation, _channel), true); + program->set_value (pgm, PBD::Controllable::NoGroup); +} + +/* ***** callbacks, external changes *****/ + +void +PatchChangeWidget::bank_changed () +{ + // TODO optimize, just find the bank, set the text and refill_program_list() + refill_banks (); +} + +void +PatchChangeWidget::program_changed () +{ + uint8_t p = program (_channel); + for (uint8_t pgm = 0; pgm < 128; ++pgm) { + _program_btn[pgm].set_active (pgm == p); + } +} + +void +PatchChangeWidget::instrument_info_changed () +{ + refill_banks (); +} + + +/* ***** query info *****/ + +int +PatchChangeWidget::bank (uint8_t chn) const +{ + boost::shared_ptr<AutomationControl> bank_msb = _route->automation_control(Evoral::Parameter (MidiCCAutomation, chn, MIDI_CTL_MSB_BANK), true); + boost::shared_ptr<AutomationControl> bank_lsb = _route->automation_control(Evoral::Parameter (MidiCCAutomation, chn, MIDI_CTL_LSB_BANK), true); + + return ((int)bank_msb->get_value () << 7) + (int)bank_lsb->get_value(); +} + +uint8_t +PatchChangeWidget::program (uint8_t chn) const +{ + boost::shared_ptr<AutomationControl> program = _route->automation_control(Evoral::Parameter (MidiPgmChangeAutomation, chn), true); + return program->get_value(); +} + +/* ***************************************************************************/ + +PatchChangeGridDialog::PatchChangeGridDialog (std::string const& title, boost::shared_ptr<ARDOUR::Route> r) + : ArdourDialog (title, false, false) + , w (r) +{ + get_vbox()->add (w); + w.show (); +} diff --git a/gtk2_ardour/patch_change_widget.h b/gtk2_ardour/patch_change_widget.h new file mode 100644 index 0000000000..89ed38062d --- /dev/null +++ b/gtk2_ardour/patch_change_widget.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2017 Robin Gareus <robin@gareus.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __gtkardour_patch_change_widget_h__ +#define __gtkardour_patch_change_widget_h__ + +#include <gtkmm/box.h> +#include <gtkmm/spinbutton.h> +#include <gtkmm/table.h> + +#include "pbd/signals.h" + +#include "ardour/route.h" + +#include "widgets/ardour_button.h" +#include "widgets/ardour_dropdown.h" + +#include "ardour_dialog.h" + +class PatchChangeWidget : public Gtk::VBox +{ +public: + PatchChangeWidget (boost::shared_ptr<ARDOUR::Route>); + ~PatchChangeWidget (); + +protected: + int bank (uint8_t) const; + uint8_t program (uint8_t) const; + + void on_show (); + void on_hide (); + +private: + boost::shared_ptr<ARDOUR::Route> _route; + + ArdourWidgets::ArdourDropdown _channel_select; + ArdourWidgets::ArdourDropdown _bank_select; + Gtk::SpinButton _bank_msb_spin; + Gtk::SpinButton _bank_lsb_spin; + ArdourWidgets::ArdourButton _program_btn[128]; + Gtk::Table _program_table; + + uint8_t _channel; + bool _ignore_spin_btn_signals; + + void select_channel (uint8_t); + void select_bank (uint32_t); + void select_bank_spin (); + void select_program (uint8_t); + + void bank_changed (); + void program_changed (); + + void refill_banks (); + void refill_program_list (); + + void instrument_info_changed (); + + PBD::ScopedConnection _info_changed_connection; + PBD::ScopedConnection _route_connection; + PBD::ScopedConnectionList _ac_connections; + + ARDOUR::InstrumentInfo& _info; + boost::shared_ptr<MIDI::Name::PatchBank> _current_patch_bank; +}; + +class PatchChangeGridDialog : public ArdourDialog +{ +public: + PatchChangeGridDialog (std::string const&, boost::shared_ptr<ARDOUR::Route>); + void on_hide () { w.hide (); ArdourDialog::on_hide (); } + void on_show () { w.show (); ArdourDialog::on_show (); } + +private: + PatchChangeWidget w; +}; + +#endif diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index 598674079a..d3824a0a63 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -178,6 +178,7 @@ gtk2_ardour_sources = [ 'panner_interface.cc', 'panner_ui.cc', 'patch_change.cc', + 'patch_change_widget.cc', 'piano_roll_header.cc', 'pingback.cc', 'playlist_selector.cc', |