13
0

Rework Patch-Change/Select Dialog

This commit is contained in:
Robin Gareus 2017-09-08 00:30:55 +02:00
parent 187748874c
commit 6376730093
5 changed files with 389 additions and 33 deletions

View File

@ -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"
@ -1081,33 +1082,6 @@ MidiTimeAxisView::build_color_mode_menu()
return 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 ()
{
@ -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

View File

@ -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;
};

View File

@ -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 ();
}

View File

@ -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

View File

@ -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',