Robin Gareus
4050ca5633
Copyright-holder and year information is extracted from git log. git history begins in 2005. So (C) from 1998..2005 is lost. Also some (C) assignment of commits where the committer didn't use --author.
581 lines
18 KiB
C++
581 lines
18 KiB
C++
/*
|
|
* Copyright (C) 2017-2019 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.
|
|
*/
|
|
|
|
#include <bitset>
|
|
#include <gtkmm/frame.h>
|
|
|
|
#include "pbd/unwind.h"
|
|
|
|
#include "evoral/midi_events.h"
|
|
#include "evoral/PatchChange.hpp"
|
|
|
|
#include "midi++/midnam_patch.h"
|
|
|
|
#include "ardour/instrument_info.h"
|
|
#include "ardour/midi_track.h"
|
|
#include "ardour/plugin_insert.h"
|
|
|
|
#include "gtkmm2ext/menu_elems.h"
|
|
#include "gtkmm2ext/utils.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)
|
|
, _no_notifications (false)
|
|
, _info (r->instrument_info ())
|
|
, _audition_enable (_("Audition on Change"), ArdourWidgets::ArdourButton::led_default_elements)
|
|
, _audition_start_spin (*manage (new Adjustment (48, 0, 127, 1, 16)))
|
|
, _audition_end_spin (*manage (new Adjustment (60, 0, 127, 1, 16)))
|
|
, _audition_velocity (*manage (new Adjustment (100, 1, 127, 1, 16)))
|
|
, _audition_note_on (false)
|
|
, _piano ((PianoKeyboard*)piano_keyboard_new())
|
|
, _pianomm (Glib::wrap((GtkWidget*)_piano))
|
|
{
|
|
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);
|
|
|
|
if (!boost::dynamic_pointer_cast<MidiTrack> (_route)) {
|
|
pack_start ( *manage (new Label (_("Note: Patch Selection is volatile (only Midi-Tracks retain bank/patch selection)."))), false, false);
|
|
}
|
|
|
|
box = manage (new HBox ());
|
|
box->set_spacing (4);
|
|
box->pack_start (_audition_enable, false, false);
|
|
box->pack_start (*manage (new Label (_("Start Note:"))), false, false);
|
|
box->pack_start (_audition_start_spin, false, false);
|
|
box->pack_start (*manage (new Label (_("End Note:"))), false, false);
|
|
box->pack_start (_audition_end_spin, false, false);
|
|
box->pack_start (*manage (new Label (_("Velocity:"))), false, false);
|
|
box->pack_start (_audition_velocity, false, false);
|
|
|
|
Box* box2 = manage (new HBox ());
|
|
box2->pack_start (*box, true, false);
|
|
box2->set_border_width (2);
|
|
pack_start (*box2, false, false);
|
|
|
|
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));
|
|
}
|
|
|
|
for (uint32_t chn = 0; chn < 16; ++chn) {
|
|
using namespace Menu_Helpers;
|
|
using namespace Gtkmm2ext;
|
|
char buf[8];
|
|
snprintf (buf, sizeof(buf), "%d", chn + 1);
|
|
_channel_select.AddMenuElem (MenuElemNoMnemonic (buf, sigc::bind (sigc::mem_fun (*this, &PatchChangeWidget::select_channel), chn)));
|
|
}
|
|
|
|
piano_keyboard_set_monophonic (_piano, TRUE);
|
|
g_signal_connect (G_OBJECT (_piano), "note-on", G_CALLBACK (PatchChangeWidget::_note_on_event_handler), this);
|
|
g_signal_connect (G_OBJECT (_piano), "note-off", G_CALLBACK (PatchChangeWidget::_note_off_event_handler), this);
|
|
_pianomm->set_flags(Gtk::CAN_FOCUS);
|
|
pack_start (*_pianomm, false, false);
|
|
|
|
_audition_start_spin.set_sensitive (false);
|
|
_audition_end_spin.set_sensitive (false);
|
|
|
|
_audition_enable.signal_clicked.connect (sigc::mem_fun (*this, &PatchChangeWidget::audition_toggle));
|
|
_audition_start_spin.signal_changed().connect (sigc::bind (sigc::mem_fun (*this, &PatchChangeWidget::check_note_range), false));
|
|
_audition_end_spin.signal_changed().connect (sigc::bind (sigc::mem_fun (*this, &PatchChangeWidget::check_note_range), true));
|
|
_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());
|
|
|
|
if (!boost::dynamic_pointer_cast<MidiTrack> (_route)) {
|
|
_route->processors_changed.connect (_route_connection, invalidator (*this),
|
|
boost::bind (&PatchChangeWidget::processors_changed, this), gui_context());
|
|
processors_changed ();
|
|
}
|
|
|
|
set_spacing (4);
|
|
show_all ();
|
|
}
|
|
|
|
PatchChangeWidget::~PatchChangeWidget ()
|
|
{
|
|
cancel_audition ();
|
|
delete _pianomm;
|
|
}
|
|
|
|
void
|
|
PatchChangeWidget::refresh ()
|
|
{
|
|
if (is_visible ()) {
|
|
on_show ();
|
|
}
|
|
}
|
|
|
|
void
|
|
PatchChangeWidget::on_show ()
|
|
{
|
|
Gtk::VBox::on_show ();
|
|
cancel_audition ();
|
|
_channel = -1;
|
|
select_channel (0);
|
|
}
|
|
|
|
void
|
|
PatchChangeWidget::on_hide ()
|
|
{
|
|
Gtk::VBox::on_hide ();
|
|
_ac_connections.drop_connections ();
|
|
cancel_audition ();
|
|
}
|
|
|
|
void
|
|
PatchChangeWidget::select_channel (uint8_t chn)
|
|
{
|
|
assert (_route);
|
|
assert (chn < 16);
|
|
|
|
if (_channel == chn) {
|
|
return;
|
|
}
|
|
|
|
cancel_audition ();
|
|
|
|
_channel_select.set_text (string_compose ("%1", (int)(chn + 1)));
|
|
_channel = chn;
|
|
_no_notifications = false;
|
|
|
|
_ac_connections.drop_connections ();
|
|
|
|
if (boost::dynamic_pointer_cast<MidiTrack> (_route)) {
|
|
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 ());
|
|
} else if (boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (_route->the_instrument())) {
|
|
if (pi->plugin()->knows_bank_patch ()) {
|
|
pi->plugin ()->BankPatchChange.connect (_ac_connections, invalidator (*this),
|
|
boost::bind (&PatchChangeWidget::bankpatch_changed, this, _1), gui_context ());
|
|
} else {
|
|
_no_notifications = true;
|
|
// TODO add note: instrument does not report changes.
|
|
}
|
|
}
|
|
|
|
refill_banks ();
|
|
}
|
|
|
|
void
|
|
PatchChangeWidget::refill_banks ()
|
|
{
|
|
cancel_audition ();
|
|
using namespace Menu_Helpers;
|
|
using namespace Gtkmm2ext;
|
|
|
|
_current_patch_bank.reset ();
|
|
_bank_select.clear_items ();
|
|
|
|
const int b = bank (_channel);
|
|
|
|
{
|
|
PBD::Unwinder<bool> uw (_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 ();
|
|
if ((*i)->number () == UINT16_MAX) {
|
|
continue;
|
|
}
|
|
_bank_select.AddMenuElem (MenuElemNoMnemonic (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 (MenuElemNoMnemonic (n, sigc::bind (sigc::mem_fun (*this, &PatchChangeWidget::select_bank), b)));
|
|
_bank_select.set_text (n);
|
|
}
|
|
|
|
refill_program_list ();
|
|
}
|
|
|
|
void
|
|
PatchChangeWidget::refill_program_list ()
|
|
{
|
|
std::bitset<128> unset_notes;
|
|
unset_notes.set ();
|
|
|
|
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) {
|
|
const std::string n = (*i)->name ();
|
|
MIDI::Name::PatchPrimaryKey const& key = (*i)->patch_primary_key ();
|
|
|
|
const uint8_t pgm = key.program();
|
|
_program_btn[pgm].set_text (n);
|
|
set_tooltip (_program_btn[pgm], string_compose (_("%1 (Pgm-%2)"),
|
|
Gtkmm2ext::markup_escape_text (n), (int)(pgm +1)));
|
|
unset_notes.reset (pgm);
|
|
}
|
|
}
|
|
|
|
for (uint8_t pgm = 0; pgm < 128; ++pgm) {
|
|
if (!unset_notes.test (pgm)) {
|
|
_program_btn[pgm].set_name (X_("patch change button"));
|
|
continue;
|
|
}
|
|
std::string n = string_compose (_("Pgm-%1"), (int)(pgm +1));
|
|
_program_btn[pgm].set_text (n);
|
|
_program_btn[pgm].set_name (X_("patch change dim button"));
|
|
set_tooltip (_program_btn[pgm], n);
|
|
}
|
|
|
|
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)
|
|
{
|
|
cancel_audition ();
|
|
|
|
if (boost::dynamic_pointer_cast<MidiTrack> (_route)) {
|
|
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);
|
|
} else if (boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (_route->the_instrument())) {
|
|
uint8_t event[3];
|
|
event[0] = (MIDI_CMD_CONTROL | _channel);
|
|
event[1] = 0x00;
|
|
event[2] = bank >> 7;
|
|
pi->write_immediate_event (3, event);
|
|
|
|
event[1] = 0x20;
|
|
event[2] = bank & 127;
|
|
pi->write_immediate_event (3, event);
|
|
}
|
|
|
|
select_program (program (_channel));
|
|
}
|
|
|
|
void
|
|
PatchChangeWidget::select_program (uint8_t pgm)
|
|
{
|
|
cancel_audition ();
|
|
if (_no_notifications) {
|
|
program_changed ();
|
|
}
|
|
|
|
if (pgm > 127) {
|
|
return;
|
|
}
|
|
|
|
if (boost::dynamic_pointer_cast<MidiTrack> (_route)) {
|
|
boost::shared_ptr<AutomationControl> program = _route->automation_control(Evoral::Parameter (MidiPgmChangeAutomation, _channel), true);
|
|
program->set_value (pgm, PBD::Controllable::NoGroup);
|
|
} else if (boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (_route->the_instrument())) {
|
|
uint8_t event[2];
|
|
event[0] = (MIDI_CMD_PGM_CHANGE | _channel);
|
|
event[1] = pgm;
|
|
pi->write_immediate_event (2, event);
|
|
}
|
|
|
|
audition ();
|
|
}
|
|
|
|
/* ***** callbacks, external changes *****/
|
|
|
|
void
|
|
PatchChangeWidget::bank_changed ()
|
|
{
|
|
refill_banks ();
|
|
}
|
|
|
|
void
|
|
PatchChangeWidget::bankpatch_changed (uint8_t chn)
|
|
{
|
|
if (chn == _channel) {
|
|
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 ();
|
|
}
|
|
|
|
void
|
|
PatchChangeWidget::processors_changed ()
|
|
{
|
|
assert (!boost::dynamic_pointer_cast<MidiTrack> (_route));
|
|
if (_route->the_instrument ()) {
|
|
set_sensitive (true);
|
|
} else {
|
|
set_sensitive (false);
|
|
}
|
|
}
|
|
|
|
/* ***** play notes *****/
|
|
|
|
void
|
|
PatchChangeWidget::audition_toggle ()
|
|
{
|
|
_audition_enable.set_active (!_audition_enable.get_active ());
|
|
if (_audition_enable.get_active()) {
|
|
_audition_start_spin.set_sensitive (true);
|
|
_audition_end_spin.set_sensitive (true);
|
|
} else {
|
|
cancel_audition ();
|
|
_audition_start_spin.set_sensitive (false);
|
|
_audition_end_spin.set_sensitive (false);
|
|
}
|
|
}
|
|
|
|
void
|
|
PatchChangeWidget::check_note_range (bool upper)
|
|
{
|
|
int s = _audition_start_spin.get_value_as_int ();
|
|
int e = _audition_end_spin.get_value_as_int ();
|
|
if (s <= e) {
|
|
return;
|
|
}
|
|
if (upper) {
|
|
_audition_start_spin.set_value (e);
|
|
} else {
|
|
_audition_end_spin.set_value (s);
|
|
}
|
|
}
|
|
|
|
void
|
|
PatchChangeWidget::cancel_audition ()
|
|
{
|
|
_note_queue_connection.disconnect();
|
|
|
|
if (_audition_note_on) {
|
|
note_off_event_handler (_audition_note_num);
|
|
piano_keyboard_set_note_off (_piano, _audition_note_num);
|
|
}
|
|
}
|
|
|
|
void
|
|
PatchChangeWidget::audition ()
|
|
{
|
|
if (!boost::dynamic_pointer_cast<MidiTrack> (_route) && !boost::dynamic_pointer_cast<PluginInsert> (_route)) {
|
|
return;
|
|
}
|
|
if (_channel > 16) {
|
|
return;
|
|
}
|
|
|
|
if (_note_queue_connection.connected ()) {
|
|
cancel_audition ();
|
|
}
|
|
|
|
if (!_audition_enable.get_active ()) {
|
|
return;
|
|
}
|
|
|
|
assert (!_audition_note_on);
|
|
_audition_note_num = _audition_start_spin.get_value_as_int ();
|
|
|
|
_note_queue_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (&PatchChangeWidget::audition_next), this), 250);
|
|
}
|
|
|
|
bool
|
|
PatchChangeWidget::audition_next ()
|
|
{
|
|
if (_audition_note_on) {
|
|
note_off_event_handler (_audition_note_num);
|
|
piano_keyboard_set_note_off (_piano, _audition_note_num);
|
|
return ++_audition_note_num <= _audition_end_spin.get_value_as_int() && _audition_enable.get_active ();
|
|
} else {
|
|
note_on_event_handler (_audition_note_num, true);
|
|
piano_keyboard_set_note_on (_piano, _audition_note_num);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
void
|
|
PatchChangeWidget::_note_on_event_handler(GtkWidget*, int note, gpointer arg)
|
|
{
|
|
((PatchChangeWidget*)arg)->note_on_event_handler(note, false);
|
|
}
|
|
|
|
void
|
|
PatchChangeWidget::_note_off_event_handler(GtkWidget*, int note, gpointer arg)
|
|
{
|
|
((PatchChangeWidget*)arg)->note_off_event_handler(note);
|
|
}
|
|
|
|
void
|
|
PatchChangeWidget::note_on_event_handler (int note, bool for_audition)
|
|
{
|
|
if (!for_audition) {
|
|
cancel_audition ();
|
|
_pianomm->grab_focus ();
|
|
}
|
|
uint8_t event[3];
|
|
event[0] = (MIDI_CMD_NOTE_ON | _channel);
|
|
event[1] = note;
|
|
event[2] = _audition_velocity.get_value_as_int ();
|
|
_audition_note_on = true;
|
|
_audition_note_num = note;
|
|
|
|
if (boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (_route)) {
|
|
mt->write_immediate_event (3, event);
|
|
} else if (boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (_route->the_instrument())) {
|
|
pi->write_immediate_event (3, event);
|
|
}
|
|
}
|
|
|
|
void
|
|
PatchChangeWidget::note_off_event_handler (int note)
|
|
{
|
|
uint8_t event[3];
|
|
event[0] = (MIDI_CMD_NOTE_OFF | _channel);
|
|
event[1] = note;
|
|
event[2] = 0;
|
|
_audition_note_on = false;
|
|
|
|
if (boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (_route)) {
|
|
mt->write_immediate_event (3, event);
|
|
} else if (boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (_route->the_instrument())) {
|
|
pi->write_immediate_event (3, event);
|
|
}
|
|
}
|
|
|
|
/* ***** query info *****/
|
|
|
|
int
|
|
PatchChangeWidget::bank (uint8_t chn) const
|
|
{
|
|
if (boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (_route)) {
|
|
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();
|
|
} else if (boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (_route->the_instrument())) {
|
|
uint32_t bankpatch = pi->plugin()->bank_patch (chn);
|
|
if (bankpatch != UINT32_MAX) {
|
|
return bankpatch >> 7;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
uint8_t
|
|
PatchChangeWidget::program (uint8_t chn) const
|
|
{
|
|
if (boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (_route)) {
|
|
boost::shared_ptr<AutomationControl> program = _route->automation_control(Evoral::Parameter (MidiPgmChangeAutomation, chn), true);
|
|
return program->get_value();
|
|
} else if (boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (_route->the_instrument())) {
|
|
uint32_t bankpatch = pi->plugin()->bank_patch (chn);
|
|
if (bankpatch != UINT32_MAX) {
|
|
return bankpatch & 127;
|
|
}
|
|
}
|
|
return 255;
|
|
}
|
|
|
|
/* ***************************************************************************/
|
|
|
|
PatchChangeGridDialog::PatchChangeGridDialog (boost::shared_ptr<ARDOUR::Route> r)
|
|
: ArdourDialog (string_compose (_("Select Patch for \"%1\""), r->name()), false, false)
|
|
, w (r)
|
|
{
|
|
r->PropertyChanged.connect (_route_connection, invalidator (*this), boost::bind (&PatchChangeGridDialog::route_property_changed, this, _1, boost::weak_ptr<Route>(r)), gui_context());
|
|
get_vbox()->add (w);
|
|
w.show ();
|
|
}
|
|
|
|
void
|
|
PatchChangeGridDialog::route_property_changed (const PBD::PropertyChange& what_changed, boost::weak_ptr<Route> wr)
|
|
{
|
|
boost::shared_ptr<ARDOUR::Route> r = wr.lock ();
|
|
if (r && what_changed.contains (ARDOUR::Properties::name)) {
|
|
set_title (string_compose (_("Select Patch for \"%1\"'"), r->name()));
|
|
}
|
|
}
|