From cf7ba80fc1259fc432f7e4c4c51f2890aa39fa6d Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Tue, 20 Jun 2023 11:28:59 -0600 Subject: [PATCH] the basics of lollipop dragging --- gtk2_ardour/editor_canvas_events.cc | 2 +- gtk2_ardour/editor_drag.cc | 31 ++++++++++++++++++-- gtk2_ardour/editor_drag.h | 8 +++-- gtk2_ardour/editor_mouse.cc | 5 ++++ gtk2_ardour/ghostregion.cc | 3 +- gtk2_ardour/lollipop.cc | 27 +++++++++-------- gtk2_ardour/lollipop.h | 8 +++-- gtk2_ardour/midi_region_view.cc | 25 ++++++++++++++++ gtk2_ardour/midi_region_view.h | 5 +++- gtk2_ardour/velocity_ghost_region.cc | 44 ++++++++++++++++++++++++++++ gtk2_ardour/velocity_ghost_region.h | 7 +++++ gtk2_ardour/wscript | 1 - 12 files changed, 140 insertions(+), 26 deletions(-) diff --git a/gtk2_ardour/editor_canvas_events.cc b/gtk2_ardour/editor_canvas_events.cc index baa641d207..244c8acefc 100644 --- a/gtk2_ardour/editor_canvas_events.cc +++ b/gtk2_ardour/editor_canvas_events.cc @@ -674,7 +674,7 @@ Editor::canvas_control_point_event (GdkEvent *event, ArdourCanvas::Item* item, C bool Editor::canvas_velocity_event (GdkEvent *event, ArdourCanvas::Item* item) { - std::cerr << "Velocity event: " << Gtkmm2ext::event_type_string (event->type) << std::endl; + // std::cerr << "Velocity event: " << Gtkmm2ext::event_type_string (event->type) << std::endl; return typed_event (item, event, VelocityItem); } diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index 1f8d95cc7d..d9430e07c1 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -52,6 +52,7 @@ #include "ardour/session_playlists.h" #include "canvas/canvas.h" +#include "canvas/lollipop.h" #include "canvas/scroll_group.h" #include "ardour_ui.h" @@ -77,6 +78,7 @@ #include "region_gain_line.h" #include "selection.h" #include "ui_config.h" +#include "velocity_ghost_region.h" #include "verbose_cursor.h" #include "video_timeline.h" @@ -7178,9 +7180,11 @@ RegionMarkerDrag::setup_pointer_sample_offset () _pointer_offset = model_abs_pos.distance (raw_grab_time ()); } -LollipopDrag::LollipopDrag (Editor* ed, MidiRegionView* r, ArdourCanvas::Item* i) - : Drag (ed, i, r->region ()->position ().time_domain ()) +LollipopDrag::LollipopDrag (Editor* ed, ArdourCanvas::Item* l) + : Drag (ed, l, Temporal::BeatTime) + , _primary (dynamic_cast (l)) { + _region = reinterpret_cast (_item->get_data ("ghostregionview")); } LollipopDrag::~LollipopDrag () @@ -7191,25 +7195,48 @@ void LollipopDrag::start_grab (GdkEvent *ev, Gdk::Cursor* c) { Drag::start_grab (ev, c); + + NoteBase* note = static_cast (_primary->get_data (X_("note"))); + MidiRegionView* mrv = dynamic_cast (&_region->parent_rv); + assert (mrv); + + bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier); + bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier); + + mrv->note_selected (note, add, extend); } void LollipopDrag::motion (GdkEvent *ev, bool first_move) { + _region->drag_lolli (_primary, &ev->motion); } void LollipopDrag::finished (GdkEvent *ev, bool did_move) { + if (!did_move) { + return; + } + + int velocity = _region->y_position_to_velocity (_primary->y0()); + NoteBase* note = static_cast (_primary->get_data (X_("note"))); + MidiRegionView* mrv = dynamic_cast (&_region->parent_rv); + assert (mrv); + + mrv->set_velocity (note, velocity); } void LollipopDrag::aborted (bool) { + /* XXX get ghost velocity view etc. to redraw with original values */ } void LollipopDrag::setup_pointer_sample_offset () { + NoteBase* note = static_cast (_primary->get_data (X_("note"))); + _pointer_offset = _region->parent_rv.region()->source_beats_to_absolute_time (note->note()->time ()).distance (raw_grab_time ()); } diff --git a/gtk2_ardour/editor_drag.h b/gtk2_ardour/editor_drag.h index d40317f21f..e0327444dd 100644 --- a/gtk2_ardour/editor_drag.h +++ b/gtk2_ardour/editor_drag.h @@ -56,6 +56,7 @@ namespace ArdourCanvas { class Item; class Line; class Rectangle; + class Lollipop; } namespace PBD { @@ -81,6 +82,7 @@ class ControlPoint; class AudioRegionView; class AutomationLine; class AutomationTimeAxisView; +class VelocityGhostRegion; /** Class to manage current drags */ class DragManager @@ -1550,7 +1552,7 @@ class RegionMarkerDrag : public Drag class LollipopDrag : public Drag { public: - LollipopDrag (Editor*, MidiRegionView*, ArdourCanvas::Item*); + LollipopDrag (Editor*, ArdourCanvas::Item*); ~LollipopDrag (); void start_grab (GdkEvent *, Gdk::Cursor* c = 0); @@ -1573,8 +1575,8 @@ class LollipopDrag : public Drag void setup_pointer_sample_offset (); private: - MidiRegionView* _region; - NoteBase* _primary; + VelocityGhostRegion* _region; + ArdourCanvas::Lollipop* _primary; }; #endif /* __gtk2_ardour_editor_drag_h_ */ diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index 4d8fddcf5e..320ed769e3 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -897,6 +897,11 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT return true; break; + case VelocityItem: + _drags->set (new LollipopDrag (this, item), event); + return true; + break; + default: break; } diff --git a/gtk2_ardour/ghostregion.cc b/gtk2_ardour/ghostregion.cc index 5ac5e4aaea..d5a4f93988 100644 --- a/gtk2_ardour/ghostregion.cc +++ b/gtk2_ardour/ghostregion.cc @@ -221,8 +221,7 @@ MidiGhostRegion::GhostEvent::GhostEvent (NoteBase* e, ArdourCanvas::Container* g : event (e) { if (dynamic_cast(e)) { - item = new ArdourCanvas::Rectangle( - g, ArdourCanvas::Rect(e->x0(), e->y0(), e->x1(), e->y1())); + item = new ArdourCanvas::Rectangle (g, ArdourCanvas::Rect(e->x0(), e->y0(), e->x1(), e->y1())); is_hit = false; } else { Hit* hit = dynamic_cast(e); diff --git a/gtk2_ardour/lollipop.cc b/gtk2_ardour/lollipop.cc index 79b25ac7aa..f619f93c30 100644 --- a/gtk2_ardour/lollipop.cc +++ b/gtk2_ardour/lollipop.cc @@ -28,7 +28,7 @@ using namespace ARDOUR; using ArdourCanvas::Coord; using ArdourCanvas::Duple; -Lollipop::Lollipop ( +NoteVelocityLollipop ( MidiRegionView& region, ArdourCanvas::Item* parent, const std::shared_ptr note, bool with_events) : NoteBase (region, with_events, note) , _lollipop (new ArdourCanvas::Lollipop (parent)) @@ -37,73 +37,74 @@ Lollipop::Lollipop ( set_item (_lollipop); } -Lollipop::~Lollipop () +NoteVelocity~Lollipop () { delete _lollipop; } void -Lollipop::move_event (double dx, double dy) +NoteVelocitymove_event (double dx, double dy) { + std::cerr << "move lolli to: " << (Duple (_lollipop->x(), _lollipop->y0()).translate (Duple (dx, dy)), _lollipop->length(), _lollipop->radius()) << std::endl; _lollipop->set (Duple (_lollipop->x(), _lollipop->y0()).translate (Duple (dx, dy)), _lollipop->length(), _lollipop->radius()); } void -Lollipop::set_outline_color (uint32_t color) +NoteVelocityset_outline_color (uint32_t color) { _lollipop->set_outline_color (color); } void -Lollipop::set_fill_color (uint32_t color) +NoteVelocityset_fill_color (uint32_t color) { _lollipop->set_fill_color (color); } void -Lollipop::show () +NoteVelocityshow () { _lollipop->show (); } void -Lollipop::hide () +NoteVelocityhide () { _lollipop->hide (); } void -Lollipop::set (ArdourCanvas::Duple const & d, ArdourCanvas::Coord len, ArdourCanvas::Coord radius) +NoteVelocityset (ArdourCanvas::Duple const & d, ArdourCanvas::Coord len, ArdourCanvas::Coord radius) { _lollipop->set (d, len, radius); } void -Lollipop::set_x (Coord x) +NoteVelocityset_x (Coord x) { _lollipop->set_x (x); } void -Lollipop::set_len (Coord l) +NoteVelocityset_len (Coord l) { _lollipop->set_length (l); } void -Lollipop::set_outline_what (ArdourCanvas::Rectangle::What what) +NoteVelocityset_outline_what (ArdourCanvas::Rectangle::What what) { // _lollipop->set_outline_what (what); } void -Lollipop::set_outline_all () +NoteVelocityset_outline_all () { // _lollipop->set_outline_all (); } void -Lollipop::set_ignore_events (bool ignore) +NoteVelocityset_ignore_events (bool ignore) { _lollipop->set_ignore_events (ignore); } diff --git a/gtk2_ardour/lollipop.h b/gtk2_ardour/lollipop.h index be979bb58c..e5c09bf974 100644 --- a/gtk2_ardour/lollipop.h +++ b/gtk2_ardour/lollipop.h @@ -31,17 +31,17 @@ namespace ArdourCanvas { class Lollipop; } -class Lollipop : public NoteBase +class NoteVelocity : public NoteBase { public: typedef Evoral::Note NoteType; - Lollipop (MidiRegionView& region, + NoteVelocity (MidiRegionView& region, ArdourCanvas::Item* parent, const std::shared_ptr note = std::shared_ptr(), bool with_events = true); - ~Lollipop (); + ~NoteVelocity (); void set (ArdourCanvas::Duple const &, ArdourCanvas::Coord, ArdourCanvas::Coord); void set_x (ArdourCanvas::Coord); @@ -58,6 +58,8 @@ public: void set_ignore_events (bool); + ArdourCanvas::Lollipop& lolli() { return *_lollipop; } + void move_event (double dx, double dy); private: diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index f946865e70..6932c976a4 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -3369,6 +3369,31 @@ MidiRegionView::change_note_length (NoteBase* event, Temporal::Beats t) note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t); } +void +MidiRegionView::set_velocity (NoteBase* note, int velocity) +{ + if (_selection.empty()) { + return; + } + + int delta = velocity - note->note()->velocity(); + + std::cerr << "vel delta = " << delta << std::endl; + + start_note_diff_command (_("set velocities")); + + for (Selection::iterator i = _selection.begin(); i != _selection.end();) { + Selection::iterator next = i; + ++next; + + change_note_velocity (*i, delta, true); + + i = next; + } + + apply_note_diff(); +} + void MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together) { diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index 1098db55ef..a716e2a2b1 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -54,10 +54,10 @@ namespace MIDI { }; class SysEx; -class NoteBase; class Note; class Hit; class MidiTimeAxisView; +class NoteBase; class GhostRegion; class AutomationTimeAxisView; class AutomationRegionView; @@ -67,6 +67,7 @@ class EditNoteDialog; class PatchChange; class ItemCounts; class CursorContext; +class VelocityGhostRegion; class MidiRegionView : public RegionView { @@ -286,6 +287,7 @@ public: void goto_next_note (bool add_to_selection); void change_note_lengths (bool, bool, Temporal::Beats beats, bool start, bool end); void change_velocities (bool up, bool fine, bool allow_smush, bool all_together); + void set_velocity (NoteBase* primary, int velocity); void transpose (bool up, bool fine, bool allow_smush); void nudge_notes (bool forward, bool fine); void channel_edit (); @@ -502,6 +504,7 @@ public: std::shared_ptr find_canvas_patch_change (ARDOUR::MidiModel::PatchChangePtr p); std::shared_ptr find_canvas_sys_ex (ARDOUR::MidiModel::SysExPtr s); + friend class VelocityGhostRegion; void update_note (NoteBase*, bool update_ghost_regions = true); void update_sustained (Note *, bool update_ghost_regions = true); void update_hit (Hit *, bool update_ghost_regions = true); diff --git a/gtk2_ardour/velocity_ghost_region.cc b/gtk2_ardour/velocity_ghost_region.cc index dfa5f4f086..74b9506e41 100644 --- a/gtk2_ardour/velocity_ghost_region.cc +++ b/gtk2_ardour/velocity_ghost_region.cc @@ -79,6 +79,8 @@ VelocityGhostRegion::add_note (NoteBase* nb) events.insert (std::make_pair (nb->note(), event)); l->Event.connect (sigc::bind (sigc::mem_fun (*this, &VelocityGhostRegion::lollevent), event)); l->raise_to_top (); + l->set_data (X_("ghostregionview"), this); + l->set_data (X_("note"), nb); event->item->set_fill_color (UIConfiguration::instance().color_mod(nb->base_color(), "ghost track midi fill")); event->item->set_outline_color (_outline); @@ -125,3 +127,45 @@ VelocityGhostRegion::set_colors () { base_rect->set_fill_color (Gtkmm2ext::Color (0xff000085)); } + +void +VelocityGhostRegion::drag_lolli (ArdourCanvas::Lollipop* l, GdkEventMotion* ev) +{ + ArdourCanvas::Rect r (base_rect->item_to_window (base_rect->get())); + + /* translate event y-coord so that zero matches the top of base_rect */ + + ev->y -= r.y0; + + /* clamp y to be within the range defined by the base_rect height minus + * the lollipop radius at top and bottom + */ + + const double effective_y = std::max (lollipop_radius, std::min (r.height() - (2.0 * lollipop_radius), ev->y)); + + std::cerr << "new y " << effective_y << std::endl; + + l->set (ArdourCanvas::Duple (l->x(), effective_y), r.height() - effective_y - lollipop_radius, lollipop_radius); + +} + +int +VelocityGhostRegion::y_position_to_velocity (double y) const +{ + const ArdourCanvas::Rect r (base_rect->get()); + int velocity; + + std::cerr << "y = " << y << " h = " << r.height() << std::endl; + + if (y >= r.height() - (2.0 * lollipop_radius)) { + velocity = 0; + } else if (y <= lollipop_radius) { + velocity = 127; + } else { + velocity = floor (127. * ((r.height() - y) / r.height())); + } + + std::cerr << " y = " << y << " vel = " << velocity << std::endl; + + return velocity; +} diff --git a/gtk2_ardour/velocity_ghost_region.h b/gtk2_ardour/velocity_ghost_region.h index d1f8ef2a51..9b65f5df09 100644 --- a/gtk2_ardour/velocity_ghost_region.h +++ b/gtk2_ardour/velocity_ghost_region.h @@ -23,6 +23,10 @@ #include "ghostregion.h" +namespace ArdourCanvas { +class Lollipop; +} + class VelocityGhostRegion : public MidiGhostRegion { public: @@ -36,6 +40,9 @@ public: void remove_note (NoteBase*); void set_colors (); + void drag_lolli (ArdourCanvas::Lollipop* l, GdkEventMotion* ev); + + int y_position_to_velocity (double y) const; private: bool lollevent (GdkEvent*, MidiGhostRegion::GhostEvent*); diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index 7a5615704a..1055c37eaf 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -152,7 +152,6 @@ gtk2_ardour_sources = [ 'level_meter.cc', 'library_download_dialog.cc', 'location_ui.cc', - 'lollipop.cc', 'loudness_dialog.cc', 'loudness_settings.cc', 'lua_script_manager.cc',