From bbdb6b0e63f3b1c74643a7668be7ae4976ae3ea7 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Thu, 7 Nov 2024 16:10:31 -0700 Subject: [PATCH] more extensive automation display design for cue editor --- gtk2_ardour/automation_line.cc | 13 +++ gtk2_ardour/automation_line.h | 2 + gtk2_ardour/midi_cue_view.cc | 183 ++++++++++++++++++++++++-------- gtk2_ardour/midi_cue_view.h | 42 ++++++-- gtk2_ardour/velocity_display.cc | 16 +++ gtk2_ardour/velocity_display.h | 3 + 6 files changed, 206 insertions(+), 53 deletions(-) diff --git a/gtk2_ardour/automation_line.cc b/gtk2_ardour/automation_line.cc index 24d582c039..2c2bb7effe 100644 --- a/gtk2_ardour/automation_line.cc +++ b/gtk2_ardour/automation_line.cc @@ -225,6 +225,19 @@ AutomationLine::hide () set_visibility (AutomationLine::VisibleAspects (_visible & ~Line)); } +void +AutomationLine::hide_all () +{ + set_visibility (AutomationLine::VisibleAspects (0)); +} + +void +AutomationLine::show () +{ + /* hide everything */ + set_visibility (AutomationLine::VisibleAspects (~0)); +} + double AutomationLine::control_point_box_size () { diff --git a/gtk2_ardour/automation_line.h b/gtk2_ardour/automation_line.h index aa16f88add..03fa06aaea 100644 --- a/gtk2_ardour/automation_line.h +++ b/gtk2_ardour/automation_line.h @@ -118,6 +118,8 @@ public: void remove_visibility (VisibleAspects); void hide (); + void hide_all (); + void show (); void set_height (guint32); bool get_uses_gain_mapping () const; diff --git a/gtk2_ardour/midi_cue_view.cc b/gtk2_ardour/midi_cue_view.cc index fcff01da5f..557669d7c0 100644 --- a/gtk2_ardour/midi_cue_view.cc +++ b/gtk2_ardour/midi_cue_view.cc @@ -20,6 +20,7 @@ #include "ardour/midi_source.h" #include "ardour/midi_track.h" #include "ardour/triggerbox.h" +#include "ardour/types.h" #include "gtkmm2ext/utils.h" @@ -41,6 +42,7 @@ #include "pbd/i18n.h" +using namespace ARDOUR; using namespace Gtkmm2ext; MidiCueView::MidiCueView (std::shared_ptr mt, @@ -50,7 +52,6 @@ MidiCueView::MidiCueView (std::shared_ptr mt, MidiViewBackground& bg, uint32_t basic_color) : MidiView (mt, parent, ec, bg, basic_color) - , velocity_base (nullptr) , velocity_display (nullptr) , _slot_index (slot_index) { @@ -77,14 +78,13 @@ MidiCueView::MidiCueView (std::shared_ptr mt, automation_group->set_fill_color (UIConfiguration::instance().color ("midi automation track fill")); automation_group->set_data ("linemerger", this); - velocity_base = new ArdourCanvas::Rectangle (&parent); - CANVAS_DEBUG_NAME (velocity_base, "cue velocity base"); - velocity_display = new MidiCueVelocityDisplay (editing_context(), midi_context(), *this, *velocity_base, 0x312244ff); + velocity_display = new MidiCueVelocityDisplay (editing_context(), midi_context(), *this, *automation_group, 0x312244ff); for (auto & ev : _events) { velocity_display->add_note (ev.second); } + velocity_display->hide (); button_bar = new ArdourCanvas::Box (&parent, ArdourCanvas::Box::Horizontal); CANVAS_DEBUG_NAME (button_bar, "button bar"); @@ -115,36 +115,37 @@ MidiCueView::MidiCueView (std::shared_ptr mt, modulation_button->text()->set_color (UIConfiguration::instance().color ("neutral:foreground")); CANVAS_DEBUG_NAME (modulation_button, "modulation button"); + velocity_button->Event.connect (sigc::bind (sigc::mem_fun (*this, &MidiCueView::automation_button_event), ARDOUR::MidiVelocityAutomation, 0)); + pressure_button->Event.connect (sigc::bind (sigc::mem_fun (*this, &MidiCueView::automation_button_event), ARDOUR::MidiChannelPressureAutomation, 0)); + bender_button->Event.connect (sigc::bind (sigc::mem_fun (*this, &MidiCueView::automation_button_event), ARDOUR::MidiPitchBenderAutomation, 0)); + modulation_button->Event.connect (sigc::bind (sigc::mem_fun (*this, &MidiCueView::automation_button_event), ARDOUR::MidiCCAutomation, MIDI_CTL_MSB_MODWHEEL)); + expression_button->Event.connect (sigc::bind (sigc::mem_fun (*this, &MidiCueView::automation_button_event), ARDOUR::MidiCCAutomation, MIDI_CTL_MSB_EXPRESSION)); + set_extensible (true); - Evoral::Parameter fully_qualified_param (ARDOUR::MidiCCAutomation, 0, MIDI_CTL_MSB_MODWHEEL); - show_automation (fully_qualified_param); + Evoral::Parameter fully_qualified_param (ARDOUR::MidiVelocityAutomation, 0, 0); + update_automation_display (fully_qualified_param, SelectionSet); } void MidiCueView::set_height (double h) { - double note_area_height = ceil (h / 2.); - double velocity_height = ceil ((h - note_area_height) / 2.); - double bbw, bbh; button_bar->size_request (bbw, bbh); - double automation_height = h - note_area_height - velocity_height - bbh; + double note_area_height = ceil ((h - bbh) / 2.); + double automation_height = ceil (h - bbh - note_area_height); event_rect->set (ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, note_area_height)); midi_context().set_size (midi_context().width(), note_area_height); - velocity_base->set_position (ArdourCanvas::Duple (0., note_area_height)); - velocity_base->set (ArdourCanvas::Rect (0., 0., ArdourCanvas::COORD_MAX, velocity_height)); - - automation_group->set_position (ArdourCanvas::Duple (0., note_area_height + velocity_height)); + automation_group->set_position (ArdourCanvas::Duple (0., note_area_height)); automation_group->set (ArdourCanvas::Rect (0., 0., ArdourCanvas::COORD_MAX, automation_height)); - button_bar->size_allocate (ArdourCanvas::Rect (0., note_area_height + velocity_height + automation_height, ArdourCanvas::COORD_MAX, note_area_height + velocity_height + automation_height + bbh)); + button_bar->size_allocate (ArdourCanvas::Rect (0., note_area_height + automation_height, ArdourCanvas::COORD_MAX, note_area_height + automation_height + bbh)); - if (automation_line) { - automation_line->set_height (automation_height); + for (auto & ads : automation_map) { + ads.second.set_height (automation_height); } view_changed (); @@ -275,8 +276,31 @@ MidiCueView::update_hit (Hit* h) } } +bool +MidiCueView::automation_button_event (GdkEvent* ev, Evoral::ParameterType type, int id) +{ + SelectionOperation op = ArdourKeyboard::selection_type (ev->button.state); + + switch (ev->type) { + case GDK_BUTTON_RELEASE: + automation_button_click (type, id, op); + break; + default: + break; + } + + return false; +} + void -MidiCueView::show_automation (Evoral::Parameter const & param) +MidiCueView::automation_button_click (Evoral::ParameterType type, int id, SelectionOperation op) +{ +#warning paul allow channel selection (2nd param) + update_automation_display (Evoral::Parameter (type, 0, id), op); +} + +void +MidiCueView::update_automation_display (Evoral::Parameter const & param, SelectionOperation op) { using namespace ARDOUR; @@ -284,52 +308,82 @@ MidiCueView::show_automation (Evoral::Parameter const & param) return; } - if (param.type() == NullAutomation) { - return; - } - -// if (automation_line && automation_line->param() == param) { -// return; -// } - - automation_line.reset (); - automation_control.reset (); - - std::shared_ptr control; - switch (param.type()) { - case MidiCCAutomation: case MidiPgmChangeAutomation: case MidiPitchBenderAutomation: case MidiChannelPressureAutomation: case MidiNotePressureAutomation: - case MidiSystemExclusiveAutomation: { - /* These controllers are region "automation" - they are owned - * by regions (and their MidiModels), not by the track. As a - * result there is no AutomationList/Line for the track, but we create - * a controller for the user to write immediate events, so the editor - * can act as a control surface for the present MIDI controllers. - * - * TODO: Record manipulation of the controller to regions? - */ + case MidiSystemExclusiveAutomation: + case MidiVelocityAutomation: + break; + default: + return; + } - std::shared_ptr control = _midi_region->model()->control (param, true); - automation_control = std::dynamic_pointer_cast (control); + CueAutomationMap::iterator i = automation_map.find (param); + AutomationDisplayState* ads = nullptr; + + if (i != automation_map.end()) { + + ads = &i->second; + + } else { + + if (param.type() == MidiVelocityAutomation) { + + } else { + + std::shared_ptr control = _midi_region->model()->control (param, true); + automation_control = std::dynamic_pointer_cast (control); + + if (!automation_control) { + return; + } - if (automation_control) { automation_line.reset (new MidiCueAutomationLine ("whatevs", _editing_context, *automation_group, automation_group, automation_control->alist(), automation_control->desc())); - automation_line->set_height (automation_group->get().height()); + AutomationDisplayState cad (automation_control, automation_line, true); + + auto res = automation_map.insert (std::make_pair (param, cad)); + + ads = &((*res.first).second); } - break; } - default: + std::cerr << "sad " << op << " param " << enum_2_string (param.type()) << std::endl; + + switch (op) { + case SelectionSet: + /* hide the rest */ + for (auto & as : automation_map) { + as.second.hide (); + } + /*FALLTHRU*/ + case SelectionAdd: + ads->set_height (automation_group->get().height()); + ads->show (); + break; + + case SelectionRemove: + ads->hide (); + break; + + case SelectionToggle: + if (ads->visible) { + ads->hide (); + } else { + ads->set_height (automation_group->get().height()); + ads->show (); + } + return; + + case SelectionExtend: + /* undefined in this context */ break; } } @@ -364,3 +418,38 @@ void MidiCueView::line_drag_click (GdkEvent* event, Temporal::timepos_t const & pos) { } + +MidiCueView::AutomationDisplayState::~AutomationDisplayState() +{ + delete velocity_display; +} + +void +MidiCueView::AutomationDisplayState::hide () +{ + if (velocity_display) { + velocity_display->hide (); + } else if (line) { + line->hide_all (); + } +} + +void +MidiCueView::AutomationDisplayState::show () +{ + if (velocity_display) { + velocity_display->show (); + } else if (line) { + line->show (); + } +} + +void +MidiCueView::AutomationDisplayState::set_height (double h) +{ + if (velocity_display) { + // velocity_display->set_height (h); + } else if (line) { + line->set_height (h); + } +} diff --git a/gtk2_ardour/midi_cue_view.h b/gtk2_ardour/midi_cue_view.h index 035487294e..cb17fa765a 100644 --- a/gtk2_ardour/midi_cue_view.h +++ b/gtk2_ardour/midi_cue_view.h @@ -24,6 +24,10 @@ #pragma once +#include + +#include "ardour/types.h" + #include "midi_view.h" class VelocityDisplay; @@ -56,7 +60,7 @@ class MidiCueView : public MidiView void ghost_add_note (NoteBase*); void ghost_sync_selection (NoteBase*); - void show_automation (Evoral::Parameter const & param); + void update_automation_display (Evoral::Parameter const & param, ARDOUR::SelectionOperation); ArdourCanvas::Item* drag_group() const; @@ -70,11 +74,37 @@ class MidiCueView : public MidiView bool scroll (GdkEventScroll* ev); ArdourCanvas::Rectangle* automation_group; - std::shared_ptr automation_line; - std::shared_ptr automation_control; - ArdourCanvas::Rectangle* velocity_base; + typedef std::shared_ptr CueAutomationLine; + typedef std::shared_ptr CueAutomationControl; + + struct AutomationDisplayState { + + AutomationDisplayState (CueAutomationControl ctl, CueAutomationLine ln, bool vis) + : control (ctl), line (ln), velocity_display (nullptr), visible (vis) {} + AutomationDisplayState (VelocityDisplay& vdisp, bool vis) + : control (nullptr), velocity_display (&vdisp), visible (vis) {} + + ~AutomationDisplayState(); + + CueAutomationControl control; + CueAutomationLine line; + VelocityDisplay* velocity_display; + bool visible; + + void hide (); + void show (); + void set_height (double); + }; + + typedef std::map CueAutomationMap; + + CueAutomationMap automation_map; + AutomationDisplayState* active_automation; + VelocityDisplay* velocity_display; + CueAutomationLine automation_line; + CueAutomationControl automation_control; ArdourCanvas::Box* button_bar; ArdourCanvas::Button* velocity_button; @@ -90,6 +120,6 @@ class MidiCueView : public MidiView void update_sustained (Note *); void update_hit (Hit *); + bool automation_button_event (GdkEvent*, Evoral::ParameterType type, int id); + void automation_button_click (Evoral::ParameterType type, int id, ARDOUR::SelectionOperation); }; - - diff --git a/gtk2_ardour/velocity_display.cc b/gtk2_ardour/velocity_display.cc index b6b3c5d5ba..a41f2bd2a9 100644 --- a/gtk2_ardour/velocity_display.cc +++ b/gtk2_ardour/velocity_display.cc @@ -430,3 +430,19 @@ VelocityDisplay::set_selected (bool yn) base.parent()->raise_to_top (); } } + +void +VelocityDisplay::hide () +{ + if (lolli_container) { + lolli_container->hide (); + } +} + +void +VelocityDisplay::show () +{ + if (lolli_container) { + lolli_container->hide (); + } +} diff --git a/gtk2_ardour/velocity_display.h b/gtk2_ardour/velocity_display.h index f16694532e..655c98ead8 100644 --- a/gtk2_ardour/velocity_display.h +++ b/gtk2_ardour/velocity_display.h @@ -44,6 +44,9 @@ class VelocityDisplay VelocityDisplay (EditingContext&, MidiViewBackground&, MidiView&, ArdourCanvas::Rectangle& base_rect, ArdourCanvas::Container&, GhostEvent::EventList& el, Gtkmm2ext::Color oc); virtual ~VelocityDisplay (); + void hide (); + void show (); + void redisplay(); void add_note(NoteBase*);