using MetaButton in pianoroll, unfinished but promising work at this point

This commit is contained in:
Paul Davis
2025-02-07 23:09:51 -07:00
parent fbc55a75b4
commit d693fba28a
2 changed files with 234 additions and 32 deletions

View File

@@ -34,6 +34,7 @@
#include "widgets/ardour_button.h"
#include "widgets/ardour_dropdown.h"
#include "widgets/metabutton.h"
#include "widgets/tooltips.h"
#include "ardour_ui.h"
@@ -41,6 +42,7 @@
#include "editor_drag.h"
#include "gui_thread.h"
#include "keyboard.h"
#include "midi_util.h"
#include "pianoroll_background.h"
#include "pianoroll.h"
#include "pianoroll_midi_view.h"
@@ -156,16 +158,109 @@ void
Pianoroll::rebuild_parameter_button_map()
{
parameter_button_map.clear ();
parameter_button_map.insert (std::make_pair (Evoral::Parameter (ARDOUR::MidiVelocityAutomation, _visible_channel), velocity_button));
parameter_button_map.insert (std::make_pair (Evoral::Parameter (ARDOUR::MidiPitchBenderAutomation, _visible_channel), bender_button));
parameter_button_map.insert (std::make_pair (Evoral::Parameter (ARDOUR::MidiChannelPressureAutomation, _visible_channel), pressure_button));
parameter_button_map.insert (std::make_pair (Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_EXPRESSION), expression_button));
parameter_button_map.insert (std::make_pair (Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_MODWHEEL), modulation_button));
parameter_button_map.insert (std::make_pair (Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_GENERAL_PURPOSE1), cc_dropdown1));
parameter_button_map.insert (std::make_pair (Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_GENERAL_PURPOSE2), cc_dropdown2));
parameter_button_map.insert (std::make_pair (Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_GENERAL_PURPOSE3), cc_dropdown3));
parameter_button_map.insert (std::make_pair (velocity_button, Evoral::Parameter (ARDOUR::MidiVelocityAutomation, _visible_channel)));
parameter_button_map.insert (std::make_pair (bender_button, Evoral::Parameter (ARDOUR::MidiPitchBenderAutomation, _visible_channel)));
parameter_button_map.insert (std::make_pair (pressure_button, Evoral::Parameter (ARDOUR::MidiChannelPressureAutomation, _visible_channel)));
parameter_button_map.insert (std::make_pair (expression_button, Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_EXPRESSION)));
parameter_button_map.insert (std::make_pair (modulation_button, Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_MODWHEEL)));
parameter_button_map.insert (std::make_pair (cc_dropdown1, Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_GENERAL_PURPOSE1)));
parameter_button_map.insert (std::make_pair (cc_dropdown2, Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_GENERAL_PURPOSE2)));
parameter_button_map.insert (std::make_pair (cc_dropdown3, Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_GENERAL_PURPOSE3)));
}
void
Pianoroll::reset_user_cc_choice (std::string name, Evoral::Parameter param, MetaButton* metabutton)
{
ParameterButtonMap::iterator iter;
for (iter = parameter_button_map.begin(); iter != parameter_button_map.end(); ++iter) {
if (iter->first == metabutton) {
parameter_button_map.erase (iter);
break;
}
}
parameter_button_map.insert (std::make_pair (metabutton, param));
std::cerr << "Set menu active for " << name << " aka " << ARDOUR::EventTypeMap::instance().to_symbol (param) << std::endl;
metabutton->set_by_menutext (name);
}
void
Pianoroll::add_single_controller_item (Gtk::Menu_Helpers::MenuList& ctl_items,
int ctl,
const std::string& name,
ArdourWidgets::MetaButton* mb)
{
using namespace Gtk::Menu_Helpers;
const uint16_t selected_channels = 0xffff;
for (uint8_t chn = 0; chn < 16; chn++) {
if (selected_channels & (0x0001 << chn)) {
Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
std::string menu_text (string_compose ("<b>%1</b>: %2 [%3]", ctl, name, int (chn + 1)));
mb->add_item (name, menu_text, sigc::bind (sigc::mem_fun (*this, &Pianoroll::reset_user_cc_choice), name, fully_qualified_param, mb));
/* one channel only */
break;
}
}
}
static void relax() {}
void
Pianoroll::add_multi_controller_item (Gtk::Menu_Helpers::MenuList& ctl_items,
const uint16_t channels,
int ctl,
const std::string& name,
MetaButton* mb)
{
using namespace Gtk;
using namespace Gtk::Menu_Helpers;
Menu* chn_menu = manage (new Menu);
MenuList& chn_items (chn_menu->items());
std::string menu_text (string_compose ("%1: %2", ctl, name));
/* Build the channel sub-menu */
Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
for (uint8_t chn = 0; chn < 16; chn++) {
if (channels & (0x0001 << chn)) {
/* for each selected channel, add a menu item for this controller */
Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
sigc::bind (sigc::mem_fun (*this, &Pianoroll::reset_user_cc_choice), menu_text, fully_qualified_param, mb)));
ParameterButtonMap::iterator i = parameter_button_map.find (mb);
if (i != parameter_button_map.end()) {
if (fully_qualified_param == i->second) {
Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&ctl_items.back());
cmi->set_active();
}
}
}
}
/* add an item to metabutton's menu that will connect to the
* per-channel submenu we built above.
*/
mb->add_item (name, menu_text, *chn_menu, sigc::ptr_fun (relax));
}
void
Pianoroll::build_lower_toolbar ()
{
@@ -176,26 +271,30 @@ Pianoroll::build_lower_toolbar ()
pressure_button = new ArdourButton (_("Pressure"), elements);
expression_button = new ArdourButton (_("Expression"), elements);
modulation_button = new ArdourButton (_("Modulation"), elements);
cc_dropdown1 = new ArdourDropdown (elements);
cc_dropdown2 = new ArdourDropdown (elements);
cc_dropdown3 = new ArdourDropdown (elements);
cc_dropdown1 = new MetaButton ();
cc_dropdown2 = new MetaButton ();
cc_dropdown3 = new MetaButton ();
cc_dropdown1->set_text (string_compose (_("CC%1"), MIDI_CTL_MSB_GENERAL_PURPOSE1));
cc_dropdown2->set_text (string_compose (_("CC%1"), MIDI_CTL_MSB_GENERAL_PURPOSE2));
cc_dropdown3->set_text (string_compose (_("CC%1"), MIDI_CTL_MSB_GENERAL_PURPOSE3));
cc_dropdown1->add_elements (ArdourButton::Indicator);
cc_dropdown2->add_elements (ArdourButton::Indicator);
cc_dropdown3->add_elements (ArdourButton::Indicator);
cc_dropdown1->signal_led_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::user_led_click), cc_dropdown1));
cc_dropdown2->signal_led_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::user_led_click), cc_dropdown2));
cc_dropdown3->signal_led_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::user_led_click), cc_dropdown3));
rebuild_parameter_button_map ();
/* Only need to do this once because i->second is the actual button,
/* Only need to do this once because i->first is the actual button,
* which does not change even when the parameter_button_map is rebuilt.
*/
for (ParameterButtonMap::iterator i = parameter_button_map.begin(); i != parameter_button_map.end(); ++i) {
i->second->set_active_color (0xff0000ff);
i->second->set_distinct_led_click (true);
i->second->set_led_left (true);
i->second->set_act_on_release (false);
i->second->set_fallthrough_to_parent (true);
i->first->set_active_color (0xff0000ff);
i->first->set_distinct_led_click (true);
i->first->set_led_left (true);
i->first->set_act_on_release (false);
i->first->set_fallthrough_to_parent (true);
}
// button_bar.set_homogeneous (true);
@@ -215,6 +314,10 @@ Pianoroll::build_lower_toolbar ()
modulation_button->signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_button_event), ARDOUR::MidiCCAutomation, MIDI_CTL_MSB_MODWHEEL));
expression_button->signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_button_event), ARDOUR::MidiCCAutomation, MIDI_CTL_MSB_EXPRESSION));
cc_dropdown1->signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::user_automation_button_event), cc_dropdown1), false);
cc_dropdown2->signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::user_automation_button_event), cc_dropdown2), false);
cc_dropdown3->signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::user_automation_button_event), cc_dropdown3), false);
velocity_button->signal_led_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_led_click), ARDOUR::MidiVelocityAutomation, 0));
pressure_button->signal_led_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_led_click), ARDOUR::MidiChannelPressureAutomation, 0));
bender_button->signal_led_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_led_click), ARDOUR::MidiPitchBenderAutomation, 0));
@@ -2064,7 +2167,7 @@ Pianoroll::set (TriggerReference & tref)
_track = std::dynamic_pointer_cast<MidiTrack> (st->shared_from_this());
assert (_track);
view->set_track (_track);
set_track (_track);
_update_connection = Timers::rapid_connect (sigc::mem_fun (*this, &Pianoroll::maybe_update));
_track->DropReferences.connect (object_connections, invalidator (*this), std::bind (&Pianoroll::unset, this), gui_context());
@@ -2096,6 +2199,24 @@ Pianoroll::set_track (std::shared_ptr<ARDOUR::MidiTrack> track)
if (view) {
view->set_track (track);
}
cc_dropdown1->menu().items().clear ();
cc_dropdown2->menu().items().clear ();
cc_dropdown3->menu().items().clear ();
build_controller_menu (cc_dropdown1->menu(), track->instrument_info(), 0xffff,
sigc::bind (sigc::mem_fun (*this, &Pianoroll::add_single_controller_item), cc_dropdown1),
sigc::bind (sigc::mem_fun (*this, &Pianoroll::add_multi_controller_item), cc_dropdown1));
build_controller_menu (cc_dropdown2->menu(), track->instrument_info(), 0xffff,
sigc::bind (sigc::mem_fun (*this, &Pianoroll::add_single_controller_item), cc_dropdown2),
sigc::bind (sigc::mem_fun (*this, &Pianoroll::add_multi_controller_item), cc_dropdown2));
build_controller_menu (cc_dropdown3->menu(), track->instrument_info(), 0xffff,
sigc::bind (sigc::mem_fun (*this, &Pianoroll::add_single_controller_item), cc_dropdown3),
sigc::bind (sigc::mem_fun (*this, &Pianoroll::add_multi_controller_item), cc_dropdown3));
// reset_user_cc_choice (Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_GENERAL_PURPOSE1), cc_dropdown1);
// reset_user_cc_choice (Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_GENERAL_PURPOSE2), cc_dropdown2);
// reset_user_cc_choice (Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_GENERAL_PURPOSE3), cc_dropdown3);
}
void
@@ -2148,6 +2269,48 @@ Pianoroll::zoom_to_show (Temporal::timecnt_t const & duration)
reset_zoom (spp);
}
void
Pianoroll::user_activate (MetaButton* metabutton)
{
std::cerr << "shouldn't really be here\n";
/* Find the parameter */
ParameterButtonMap::iterator i;
for (i = parameter_button_map.begin(); i != parameter_button_map.end(); ++i) {
if (i->first == metabutton) {
break;
}
}
if (i == parameter_button_map.end()) {
return;
}
if (view) {
view->update_automation_display (i->second, SelectionSet);
}
}
bool
Pianoroll::user_automation_button_event (GdkEventButton* ev, MetaButton* mb)
{
std::cerr << "UABe\n";
if (mb->is_menu_popup_event (ev)) {
std::cerr << "meny popup event\n";
return false;
}
ParameterButtonMap::iterator i = parameter_button_map.find (mb);
if (i == parameter_button_map.end()) {
std::cerr << "nuttn not found\n";
return false;
}
return automation_button_event (ev, i->second.type(), i->second.id());
}
bool
Pianoroll::automation_button_event (GdkEventButton* ev, Evoral::ParameterType type, int id)
{
@@ -2166,6 +2329,8 @@ Pianoroll::automation_button_event (GdkEventButton* ev, Evoral::ParameterType ty
op = SelectionToggle;
}
std::cerr << "update ad for " << ARDOUR::EventTypeMap::instance().to_symbol (param) << " op " << op << std::endl;
view->update_automation_display (param, op);
}
return true;
@@ -2176,6 +2341,34 @@ Pianoroll::automation_button_event (GdkEventButton* ev, Evoral::ParameterType ty
return false;
}
void
Pianoroll::user_led_click (GdkEventButton* ev, MetaButton* metabutton)
{
std::cerr << "ULC\n";
if (ev->button != 1) {
return;
}
/* Find the parameter */
ParameterButtonMap::iterator i;
for (i = parameter_button_map.begin(); i != parameter_button_map.end(); ++i) {
if (i->first == metabutton) {
break;
}
}
if (i == parameter_button_map.end()) {
return;
}
std::cerr << "led set active for " << ARDOUR::EventTypeMap::instance().to_symbol (i->second) << std::endl;
if (view) {
view->set_active_automation (i->second);
}
}
void
Pianoroll::automation_led_click (GdkEventButton* ev, Evoral::ParameterType type, int id)
{
@@ -2194,22 +2387,22 @@ Pianoroll::automation_state_changed ()
assert (view);
for (ParameterButtonMap::iterator i = parameter_button_map.begin(); i != parameter_button_map.end(); ++i) {
std::string str (ARDOUR::EventTypeMap::instance().to_symbol (i->first));
std::string str (ARDOUR::EventTypeMap::instance().to_symbol (i->second));
/* Indicate visible automation state with selected/not-selected visual state */
if (view->is_visible_automation (i->first)) {
i->second->set_visual_state (Gtkmm2ext::Selected);
if (view->is_visible_automation (i->second)) {
i->first->set_visual_state (Gtkmm2ext::Selected);
} else {
i->second->set_visual_state (Gtkmm2ext::NoVisualState);
i->first->set_visual_state (Gtkmm2ext::NoVisualState);
}
/* Indicate active automation state with explicit widget active state (LED) */
if (view->is_active_automation (i->first)) {
i->second->set_active_state (Gtkmm2ext::ExplicitActive);
if (view->is_active_automation (i->second)) {
i->first->set_active_state (Gtkmm2ext::ExplicitActive);
} else {
i->second->set_active_state (Gtkmm2ext::Off);
i->first->set_active_state (Gtkmm2ext::Off);
}
}
}

View File

@@ -42,6 +42,7 @@ namespace ArdourCanvas {
namespace ArdourWidgets {
class ArdourButton;
class MetaButton;
}
class PianorollMidiView;
@@ -204,11 +205,11 @@ class Pianoroll : public CueEditor
ArdourWidgets::ArdourButton* pressure_button;
ArdourWidgets::ArdourButton* expression_button;
ArdourWidgets::ArdourButton* modulation_button;
ArdourWidgets::ArdourDropdown* cc_dropdown1;
ArdourWidgets::ArdourDropdown* cc_dropdown2;
ArdourWidgets::ArdourDropdown* cc_dropdown3;
ArdourWidgets::MetaButton* cc_dropdown1;
ArdourWidgets::MetaButton* cc_dropdown2;
ArdourWidgets::MetaButton* cc_dropdown3;
typedef std::map<Evoral::Parameter,ArdourWidgets::ArdourButton*> ParameterButtonMap;
typedef std::map<ArdourWidgets::ArdourButton*,Evoral::Parameter> ParameterButtonMap;
ParameterButtonMap parameter_button_map;
void rebuild_parameter_button_map ();
@@ -265,9 +266,12 @@ class Pianoroll : public CueEditor
PBD::ScopedConnectionList capture_connections;
samplecnt_t data_capture_duration;
bool user_automation_button_event (GdkEventButton* ev, ArdourWidgets::MetaButton* mb);
bool automation_button_event (GdkEventButton*, Evoral::ParameterType type, int id);
bool automation_button_click (Evoral::ParameterType type, int id, ARDOUR::SelectionOperation);
void automation_led_click (GdkEventButton*, Evoral::ParameterType type, int id);
void user_led_click (GdkEventButton* ev, ArdourWidgets::MetaButton* metabutton);
void user_activate (ArdourWidgets::MetaButton* metabutton);
int _visible_channel;
@@ -296,4 +300,9 @@ class Pianoroll : public CueEditor
void rec_enable_change ();
void blink_rec_enable (bool);
sigc::connection rec_blink_connection;
void add_single_controller_item (Gtk::Menu_Helpers::MenuList& ctl_items, int ctl, const std::string& name, ArdourWidgets::MetaButton*);
void add_multi_controller_item (Gtk::Menu_Helpers::MenuList& ctl_items, uint16_t channels, int ctl, const std::string& name, ArdourWidgets::MetaButton*);
void reset_user_cc_choice (std::string, Evoral::Parameter param, ArdourWidgets::MetaButton*);
};