From d693fba28acec54c2e4886c8cb39b53312b5c4aa Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Fri, 7 Feb 2025 23:09:51 -0700 Subject: [PATCH] using MetaButton in pianoroll, unfinished but promising work at this point --- gtk2_ardour/pianoroll.cc | 249 ++++++++++++++++++++++++++++++++++----- gtk2_ardour/pianoroll.h | 17 ++- 2 files changed, 234 insertions(+), 32 deletions(-) diff --git a/gtk2_ardour/pianoroll.cc b/gtk2_ardour/pianoroll.cc index 1f36aa42cb..ad97057a40 100644 --- a/gtk2_ardour/pianoroll.cc +++ b/gtk2_ardour/pianoroll.cc @@ -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 ("%1: %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(&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 (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 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); } } } diff --git a/gtk2_ardour/pianoroll.h b/gtk2_ardour/pianoroll.h index acf14369be..9fe670716f 100644 --- a/gtk2_ardour/pianoroll.h +++ b/gtk2_ardour/pianoroll.h @@ -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 ParameterButtonMap; + typedef std::map 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*); + };