add canvas-based Meter object
[ardour.git] / gtk2_ardour / midi_region_view.cc
index 287b6787eef63748302826271c0256ea3bc9630b..a2aa46f3ff607c354ba200dc2672d3535cd8916f 100644 (file)
@@ -82,7 +82,7 @@
 #include "sys_ex.h"
 #include "ui_config.h"
 
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 using namespace ARDOUR;
 using namespace PBD;
@@ -111,7 +111,6 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Container*      parent,
        , _step_edit_cursor_width (1.0)
        , _step_edit_cursor_position (0.0)
        , _channel_selection_scoped_note (0)
-       , _temporary_note_group (0)
        , _mouse_state(None)
        , _pressed_button(0)
        , _sort_needed (true)
@@ -155,7 +154,6 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Container*      parent,
        , _step_edit_cursor_width (1.0)
        , _step_edit_cursor_position (0.0)
        , _channel_selection_scoped_note (0)
-       , _temporary_note_group (0)
        , _mouse_state(None)
        , _pressed_button(0)
        , _sort_needed (true)
@@ -204,7 +202,6 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other)
        , _step_edit_cursor_width (1.0)
        , _step_edit_cursor_position (0.0)
        , _channel_selection_scoped_note (0)
-       , _temporary_note_group (0)
        , _mouse_state(None)
        , _pressed_button(0)
        , _sort_needed (true)
@@ -237,7 +234,6 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<M
        , _step_edit_cursor_width (1.0)
        , _step_edit_cursor_position (0.0)
        , _channel_selection_scoped_note (0)
-       , _temporary_note_group (0)
        , _mouse_state(None)
        , _pressed_button(0)
        , _sort_needed (true)
@@ -395,7 +391,7 @@ MidiRegionView::canvas_group_event(GdkEvent* ev)
 bool
 MidiRegionView::enter_notify (GdkEventCrossing* ev)
 {
-       enter_internal();
+       enter_internal (ev->state);
 
        _entered = true;
        return false;
@@ -441,11 +437,11 @@ MidiRegionView::mouse_mode_changed ()
 }
 
 void
-MidiRegionView::enter_internal()
+MidiRegionView::enter_internal (uint32_t state)
 {
        if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
                // Show ghost note under pencil
-               create_ghost_note(_last_event_x, _last_event_y);
+               create_ghost_note(_last_event_x, _last_event_y, state);
        }
 
        if (!_selection.empty()) {
@@ -554,8 +550,8 @@ MidiRegionView::button_release (GdkEventButton* ev)
                                        event_y = ev->y;
                                        group->canvas_to_item (event_x, event_y);
 
-                                       Evoral::Beats beats = get_grid_beats(editor.pixel_to_sample(event_x));
-                                       create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
+                                       Evoral::Beats beats = get_grid_beats(editor.pixel_to_sample(event_x) + _region->position());
+                                       create_note_at (editor.pixel_to_sample (event_x), event_y, beats, ev->state, true);
                                } else {
                                        clear_editor_note_selection ();
                                }
@@ -564,8 +560,8 @@ MidiRegionView::button_release (GdkEventButton* ev)
                        }
                case MouseDraw:
                        {
-                               Evoral::Beats beats = get_grid_beats(editor.pixel_to_sample(event_x));
-                               create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
+                               Evoral::Beats beats = get_grid_beats(editor.pixel_to_sample(event_x) + _region->position());
+                               create_note_at (editor.pixel_to_sample (event_x), event_y, beats, ev->state, true);
                                break;
                        }
                default:
@@ -577,7 +573,7 @@ MidiRegionView::button_release (GdkEventButton* ev)
 
        case AddDragging:
                /* Only create a ghost note when we added a note, not when we were drag-selecting. */
-               create_ghost_note (ev->x, ev->y);
+               create_ghost_note (ev->x, ev->y, ev->state);
        case SelectRectDragging:
                editor.drags()->end_grab ((GdkEvent *) ev);
                _mouse_state = None;
@@ -607,12 +603,12 @@ MidiRegionView::motion (GdkEventMotion* ev)
                    Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
                    _mouse_state != AddDragging) {
 
-                       create_ghost_note (ev->x, ev->y);
+                       create_ghost_note (ev->x, ev->y, ev->state);
 
                } else if (_ghost_note && editor.current_mouse_mode() == MouseContent &&
                           Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
 
-                       update_ghost_note (ev->x, ev->y);
+                       update_ghost_note (ev->x, ev->y, ev->state);
 
                } else if (_ghost_note && editor.current_mouse_mode() == MouseContent) {
 
@@ -622,10 +618,10 @@ MidiRegionView::motion (GdkEventMotion* ev)
                } else if (editor.current_mouse_mode() == MouseDraw) {
 
                        if (_ghost_note) {
-                               update_ghost_note (ev->x, ev->y);
+                               update_ghost_note (ev->x, ev->y, ev->state);
                        }
                        else {
-                               create_ghost_note (ev->x, ev->y);
+                               create_ghost_note (ev->x, ev->y, ev->state);
                        }
                }
        }
@@ -920,7 +916,7 @@ MidiRegionView::show_list_editor ()
  * \param snap_t true to snap t to the grid, otherwise false.
  */
 void
-MidiRegionView::create_note_at (framepos_t t, double y, Evoral::Beats length, bool snap_t)
+MidiRegionView::create_note_at (framepos_t t, double y, Evoral::Beats length, uint32_t state, bool shift_snap)
 {
        if (length < 2 * DBL_EPSILON) {
                return;
@@ -935,13 +931,9 @@ MidiRegionView::create_note_at (framepos_t t, double y, Evoral::Beats length, bo
        }
 
        // Start of note in frames relative to region start
-       if (snap_t) {
-               framecnt_t grid_frames;
-               t = snap_frame_to_grid_underneath (t, grid_frames);
-       }
+       const int32_t divisions = trackview.editor().get_grid_music_divisions (state);
+       Evoral::Beats beat_time = snap_frame_to_grid_underneath (t, divisions, shift_snap);
 
-       const MidiModel::TimeType beat_time = Evoral::Beats (trackview.session()->tempo_map().beat_at_frame (_region->position() + t)
-                                                            - (mr->beat() - mr->start_beats().to_double()));
        const double  note     = view->y_to_note(y);
        const uint8_t chan     = mtv->get_channel_for_add();
        const uint8_t velocity = get_velocity_for_add(beat_time);
@@ -1184,11 +1176,11 @@ MidiRegionView::redisplay_model()
        _optimization_iterator = _events.begin();
 
        bool empty_when_starting = _events.empty();
+       NoteBase* cne;
 
        for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
 
                boost::shared_ptr<NoteType> note (*n);
-               NoteBase* cne;
                bool visible;
 
                if (note_in_region_range (note, visible)) {
@@ -1398,7 +1390,6 @@ MidiRegionView::~MidiRegionView ()
        delete _note_group;
        delete _note_diff_command;
        delete _step_edit_cursor;
-       delete _temporary_note_group;
 }
 
 void
@@ -1417,10 +1408,14 @@ MidiRegionView::region_resized (const PropertyChange& what_changed)
            what_changed.contains (ARDOUR::Properties::position)) {
                _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
        }
-       if (what_changed.contains (ARDOUR::Properties::length)) {
+       /* catch end and start trim so we can update the view*/
+       if (!what_changed.contains (ARDOUR::Properties::start) &&
+           what_changed.contains (ARDOUR::Properties::length)) {
+               enable_display (true);
+       } else if (what_changed.contains (ARDOUR::Properties::start) &&
+           what_changed.contains (ARDOUR::Properties::length)) {
                enable_display (true);
        }
-
 }
 
 void
@@ -1430,7 +1425,7 @@ MidiRegionView::reset_width_dependent_items (double pixel_width)
 
        if (_enable_display) {
                redisplay_model();
-               }
+       }
 
        for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
                if ((*x)->canvas_item()->width() >= _pixel_width) {
@@ -1707,24 +1702,24 @@ MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions)
        TempoMap& map (trackview.session()->tempo_map());
        const boost::shared_ptr<ARDOUR::MidiRegion> mr = midi_region();
        boost::shared_ptr<NoteType> note = ev->note();
-       const framepos_t note_start_frames = map.frame_at_beat (_region->beat() - mr->start_beats().to_double()
-                                                               + note->time().to_double()) - _region->position();
-
+       const double qn_note_time = note->time().to_double() + ((_region->pulse() * 4.0) - mr->start_beats().to_double());
+       const framepos_t note_start_frames = map.frame_at_quarter_note (qn_note_time) - _region->position();
        const double x0 = trackview.editor().sample_to_pixel (note_start_frames);
        double x1;
        const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
-       double y1;/* trim note display to not overlap the end of its region */
+       double y1;
 
+       /* trim note display to not overlap the end of its region */
        if (note->length() > 0) {
                Evoral::Beats note_end_time = note->end_time();
 
                if (note->end_time() > mr->start_beats() + mr->length_beats()) {
-                       note_end_time = mr->length_beats();
+                       note_end_time = mr->start_beats() + mr->length_beats();
                }
+               const double session_qn_start = (_region->pulse() * 4.0) - mr->start_beats().to_double();
+               const double quarter_note_end_time = session_qn_start  + note_end_time.to_double();
 
-               const framepos_t note_end_frames = map.frame_at_beat (_region->beat() - mr->start_beats().to_double()
-                                                                     + note_end_time.to_double()) - _region->position();
-
+               const framepos_t note_end_frames = map.frame_at_quarter_note (quarter_note_end_time) - _region->position();
                x1 = std::max(1., trackview.editor().sample_to_pixel (note_end_frames)) - 1;
        } else {
                x1 = std::max(1., trackview.editor().sample_to_pixel (_region->length())) - 1;
@@ -1732,10 +1727,8 @@ MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions)
 
        y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1);
 
-       ev->set_x0 (x0);
-       ev->set_x1 (x1);
-       ev->set_y0 (y0);
-       ev->set_y1 (y1);
+       ArdourCanvas::Rect rect (x0, y0, x1, y1);
+       ev->set (rect);
 
        if (!note->length()) {
                if (_active_notes && note->note() < 128) {
@@ -1759,9 +1752,9 @@ MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions)
        }
 
        // Update color in case velocity has changed
-       //const uint32_t base_col = ev->base_color();
-       //ev->set_fill_color(base_col);
-       //ev->set_outline_color(ev->calculate_outline(base_col, ev->selected()));
+       const uint32_t base_col = ev->base_color();
+       ev->set_fill_color(base_col);
+       ev->set_outline_color(ev->calculate_outline(base_col, ev->selected()));
 
        if (update_ghost_regions) {
                for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
@@ -1778,8 +1771,9 @@ MidiRegionView::update_hit (Hit* ev, bool update_ghost_regions)
 {
        boost::shared_ptr<NoteType> note = ev->note();
 
-       const framepos_t note_start_frames = trackview.session()->tempo_map().frame_at_beat (_region->beat() - midi_region()->start_beats().to_double()
-                                                                                                + note->time().to_double()) - _region->position();
+       const double note_time_qn = note->time().to_double() + ((_region->pulse() * 4.0) - midi_region()->start_beats().to_double());
+       const framepos_t note_start_frames = trackview.session()->tempo_map().frame_at_quarter_note (note_time_qn) - _region->position();
+
        const double x = trackview.editor().sample_to_pixel(note_start_frames);
        const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
        const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
@@ -2125,6 +2119,10 @@ MidiRegionView::delete_selection()
                return;
        }
 
+       if (trackview.editor().drags()->active()) {
+               return;
+       }
+
        start_note_diff_command (_("delete selection"));
 
        for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
@@ -2582,14 +2580,15 @@ MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
        if (highest_note_in_selection + dnote > 127) {
                highest_note_difference = highest_note_in_selection - 127;
        }
+       TempoMap& map (trackview.session()->tempo_map());
 
        start_note_diff_command (_("move notes"));
 
        for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
 
-               framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
-               Evoral::Beats new_time = absolute_frames_to_source_beats (new_frames);
-
+               double const start_qn = (_region->pulse() * 4.0) - midi_region()->start_beats().to_double();
+               framepos_t new_frames = map.frame_at_quarter_note (start_qn + (*i)->note()->time().to_double()) + dt;
+               Evoral::Beats new_time = Evoral::Beats (map.quarter_note_at_frame (new_frames) - start_qn);
                if (new_time < 0) {
                        continue;
                }
@@ -2747,6 +2746,7 @@ MidiRegionView::begin_resizing (bool /*at_front*/)
 void
 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
 {
+       TempoMap& tmap (trackview.session()->tempo_map());
        bool cursor_set = false;
        bool const ensure_snap = trackview.editor().snap_mode () != SnapMagnetic;
 
@@ -2808,9 +2808,19 @@ MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_
                                sign = -1;
                        }
 
-                       const double  snapped_x = (with_snap ? snap_pixel_to_sample (current_x, ensure_snap) : trackview.editor ().pixel_to_sample (current_x));
-                       Evoral::Beats beats     = region_frames_to_region_beats (snapped_x);
-                       Evoral::Beats len       = Evoral::Beats();
+                       double  snapped_x;
+                       int32_t divisions = 0;
+
+                       if (with_snap) {
+                               snapped_x = snap_pixel_to_sample (current_x, ensure_snap);
+                               divisions = trackview.editor().get_grid_music_divisions (0);
+                       } else {
+                               snapped_x = trackview.editor ().pixel_to_sample (current_x);
+                       }
+                       const Evoral::Beats beats = Evoral::Beats (tmap.exact_beat_at_frame (snapped_x + midi_region()->position(), divisions)
+                                                                    - midi_region()->beat()) + midi_region()->start_beats();
+
+                       Evoral::Beats len         = Evoral::Beats();
 
                        if (at_front) {
                                if (beats < canvas_note->note()->end_time()) {
@@ -2843,6 +2853,7 @@ void
 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
 {
        _note_diff_command = _model->new_note_diff_command (_("resize notes"));
+       TempoMap& tmap (trackview.session()->tempo_map());
 
        /* XX why doesn't snap_pixel_to_sample() handle this properly? */
        bool const ensure_snap = trackview.editor().snap_mode () != SnapMagnetic;
@@ -2890,16 +2901,20 @@ MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_
                        sign = -1;
                }
 
+               uint32_t divisions = 0;
                /* Convert the new x position to a frame within the source */
                framepos_t current_fr;
                if (with_snap) {
                        current_fr = snap_pixel_to_sample (current_x, ensure_snap);
+                       divisions = trackview.editor().get_grid_music_divisions (0);
                } else {
                        current_fr = trackview.editor().pixel_to_sample (current_x);
                }
 
                /* and then to beats */
-               const Evoral::Beats x_beats = region_frames_to_region_beats (current_fr + _region->start());
+               const double e_baf = tmap.exact_beat_at_frame (current_fr + midi_region()->position(), divisions);
+               const double quarter_note_start_beat = tmap.quarter_note_at_beat (_region->beat() - midi_region()->start_beats().to_double());
+               const Evoral::Beats x_beats = Evoral::Beats (tmap.quarter_note_at_beat (e_baf) - quarter_note_start_beat);
 
                if (at_front && x_beats < canvas_note->note()->end_time()) {
                        note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats - (sign * snap_delta_beats));
@@ -3506,14 +3521,16 @@ MidiRegionView::selection_as_cut_buffer () const
 
 /** This method handles undo */
 bool
-MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx, const int32_t& sub_num)
+MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx, const int32_t sub_num)
 {
        bool commit = false;
        // Paste notes, if available
        MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
        if (m != selection.midi_notes.end()) {
                ctx.counts.increase_n_notes();
-               if (!(*m)->empty()) { commit = true; }
+               if (!(*m)->empty()) {
+                       commit = true;
+               }
                paste_internal(pos, ctx.count, ctx.times, **m);
        }
 
@@ -3522,6 +3539,9 @@ MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContex
        const ATracks& atracks = midi_view()->automation_tracks();
        for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
                if (a->second->paste(pos, selection, ctx, sub_num)) {
+                       if(!commit) {
+                               trackview.editor().begin_reversible_command (Operations::paste);
+                       }
                        commit = true;
                }
        }
@@ -3707,7 +3727,7 @@ MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_s
 }
 
 void
-MidiRegionView::update_ghost_note (double x, double y)
+MidiRegionView::update_ghost_note (double x, double y, uint32_t state)
 {
        x = std::max(0.0, x);
 
@@ -3721,21 +3741,19 @@ MidiRegionView::update_ghost_note (double x, double y)
        PublicEditor& editor = trackview.editor ();
 
        framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
-       framecnt_t grid_frames;
-       framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
 
+       const int32_t divisions = editor.get_grid_music_divisions (state);
+       const double snapped_region_qn = snap_frame_to_grid_underneath (unsnapped_frame, divisions, true).to_double();
+
+       Evoral::Beats snapped_beats = Evoral::Beats (snapped_region_qn);
        /* calculate time in beats relative to start of source */
-       const Evoral::Beats length = get_grid_beats(unsnapped_frame);
-       const Evoral::Beats time   = std::max(
-               Evoral::Beats(),
-               absolute_frames_to_source_beats (f + _region->position ()));
+       const Evoral::Beats length = get_grid_beats(unsnapped_frame + _region->position());
 
-       _ghost_note->note()->set_time (time);
+       _ghost_note->note()->set_time (snapped_beats);
        _ghost_note->note()->set_length (length);
        _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
        _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
-       _ghost_note->note()->set_velocity (get_velocity_for_add (time));
-
+       _ghost_note->note()->set_velocity (get_velocity_for_add (snapped_beats));
        /* the ghost note does not appear in ghost regions, so pass false in here */
        update_note (_ghost_note, false);
 
@@ -3743,7 +3761,7 @@ MidiRegionView::update_ghost_note (double x, double y)
 }
 
 void
-MidiRegionView::create_ghost_note (double x, double y)
+MidiRegionView::create_ghost_note (double x, double y, uint32_t state)
 {
        remove_ghost_note ();
 
@@ -3755,7 +3773,7 @@ MidiRegionView::create_ghost_note (double x, double y)
        }
        _ghost_note->set_ignore_events (true);
        _ghost_note->set_outline_color (0x000000aa);
-       update_ghost_note (x, y);
+       update_ghost_note (x, y, state);
        _ghost_note->show ();
 
        show_verbose_cursor (_ghost_note->note ());
@@ -3785,7 +3803,7 @@ MidiRegionView::snap_changed ()
                return;
        }
 
-       create_ghost_note (_last_ghost_x, _last_ghost_y);
+       create_ghost_note (_last_ghost_x, _last_ghost_y, 0);
 }
 
 void
@@ -3962,21 +3980,14 @@ MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
 void
 MidiRegionView::trim_front_starting ()
 {
-       /* Reparent the note group to the region view's parent, so that it doesn't change
-          when the region view is trimmed.
+       /* We used to eparent the note group to the region view's parent, so that it didn't change.
+          now we update it.
        */
-       _temporary_note_group = new ArdourCanvas::Container (group->parent ());
-       _temporary_note_group->move (group->position ());
-       _note_group->reparent (_temporary_note_group);
 }
 
 void
 MidiRegionView::trim_front_ending ()
 {
-       _note_group->reparent (group);
-       delete _temporary_note_group;
-       _temporary_note_group = 0;
-
        if (_region->start() < 0) {
                /* Trim drag made start time -ve; fix this */
                midi_region()->fix_negative_start ();
@@ -4039,7 +4050,7 @@ MidiRegionView::get_note_name (boost::shared_ptr<NoteType> n, uint8_t note_value
        char buf[128];
        snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
                  (int) note_value,
-                 name.empty() ? Evoral::midi_note_name (note_value).c_str() : name.c_str(),
+                 name.empty() ? ParameterDescriptor::midi_note_name (note_value).c_str() : name.c_str(),
                  (int) n->channel() + 1,
                  (int) n->velocity());
 
@@ -4100,26 +4111,30 @@ MidiRegionView::get_velocity_for_add (MidiModel::TimeType time) const
 }
 
 /** @param p A session framepos.
- *  @param grid_frames Filled in with the number of frames that a grid interval is at p.
- *  @return p snapped to the grid subdivision underneath it.
+ *  @param divisions beat division to snap given by Editor::get_grid_music_divisions() where
+ *  bar is -1, 0 is audio samples and a positive integer is beat subdivisions.
+ *  @return beat duration of p snapped to the grid subdivision underneath it.
  */
-framepos_t
-MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
+Evoral::Beats
+MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, int32_t divisions, bool shift_snap) const
 {
-       PublicEditor& editor = trackview.editor ();
-       const Evoral::Beats p_beat = region_frames_to_region_beats (p);
-       const Evoral::Beats grid_beats = get_grid_beats(p);
-
-       grid_frames = region_beats_to_region_frames (p_beat + grid_beats) - region_beats_to_region_frames (p_beat);
+       TempoMap& map (trackview.session()->tempo_map());
+       double eqaf = map.exact_qn_at_frame (p + _region->position(), divisions);
 
-       /* Hack so that we always snap to the note that we are over, instead of snapping
-          to the next one if we're more than halfway through the one we're over.
-       */
-       if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
-               p -= grid_frames / 2;
+       if (divisions != 0 && shift_snap) {
+               const double qaf = map.quarter_note_at_frame (p + _region->position());
+               /* Hack so that we always snap to the note that we are over, instead of snapping
+                  to the next one if we're more than halfway through the one we're over.
+               */
+               const Evoral::Beats grid_beats = get_grid_beats (p + _region->position());
+               const double rem = eqaf - qaf;
+               if (rem >= 0.0 && eqaf - grid_beats.to_double() > _region->pulse() * 4.0) {
+                       eqaf -= grid_beats.to_double();
+               }
        }
+       const double session_start_off = (_region->pulse() * 4.0) - midi_region()->start_beats().to_double();
 
-       return snap_frame_to_frame (p);
+       return Evoral::Beats (eqaf - session_start_off);
 }
 
 ChannelMode