From dee71c0a97c68ddf960f9959a88651b99dddb83c Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Fri, 18 Dec 2020 13:12:41 -0700 Subject: [PATCH] mostly fix region- and note-create drags --- gtk2_ardour/editor_drag.cc | 63 +++++++++++++--------------- gtk2_ardour/editor_drag.h | 10 ++--- gtk2_ardour/midi_region_view.cc | 74 +++++++++++++++++++++------------ gtk2_ardour/midi_region_view.h | 6 +-- 4 files changed, 85 insertions(+), 68 deletions(-) diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index e084ebdd48..83c028c59d 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -2597,7 +2597,6 @@ RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisVi void RegionCreateDrag::motion (GdkEvent* event, bool first_move) { - if (first_move) { _editor->begin_reversible_command (_("create region")); _region = add_midi_region (_view, false); @@ -2605,13 +2604,14 @@ RegionCreateDrag::motion (GdkEvent* event, bool first_move) } else { if (_region) { - timepos_t const pos = adjusted_current_time (event); + timepos_t const pos (adjusted_current_time (event).beats()); if (pos <= grab_time()) { _region->set_initial_position (pos); } if (pos != grab_time()) { - timecnt_t const len = grab_time().distance (pos).abs(); + /* Force MIDI regions to use Beats ... for now */ + timecnt_t const len (grab_time().distance (pos).abs().beats()); _region->set_length (len); } } @@ -3526,7 +3526,6 @@ TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c) : Drag (e, i) , _copy (c) , _grab_bpm (120.0, 4.0) - , _grab_qn (0.0) , _before_state (0) { DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n"); @@ -3730,7 +3729,6 @@ TempoMarkerDrag::aborted (bool moved) BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i) : Drag (e, i) - , _grab_qn (0.0) , _tempo (0) , _before_state (0) , _drag_valid (true) @@ -6925,7 +6923,7 @@ NoteCreateDrag::grid_aligned_beats (timepos_t const & pos, GdkEvent const * even beats = map->quarters_at (map->metric_at (pos).meter().round_to_bar (map->bbt_at (pos))); break; default: /* round to some beat subdivision */ - beats = (pos).beats().round_to_subdivision (divisions, Temporal::RoundNearest); + beats = pos.beats().round_to_subdivision (divisions, Temporal::RoundNearest); break; } @@ -6940,27 +6938,19 @@ NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ()); const timepos_t pos = _drags->current_pointer_time (); - Temporal::Beats grid_beats = grid_aligned_beats (pos, event); - const int32_t divisions = _editor->get_grid_music_divisions (event->button.state); + timepos_t aligned_start (grid_aligned_beats (pos, event)); + const timepos_t grid_beats (_region_view->get_grid_beats (aligned_start)); - if (divisions != 0) { - - const Temporal::Beats unaligned_beats = pos.beats (); - - /* 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 Temporal::Beats rem = grid_beats - unaligned_beats; - - if (rem >= std::numeric_limits::lowest ()) { - grid_beats = rem; - } - } - - _note[0] = timepos_t (grid_beats); + _note[0] = aligned_start; /* minimum initial length is grid beats */ - _note[1] = timepos_t (grid_beats + grid_beats); + _note[1] = aligned_start + grid_beats; + + /* Note: at this point we are drawing a rect for the dragging note in + * absolute coordinates (it's not real note, there's no particular + * connection to the position/start of the regionview/region. So we + * just translate directly from absolute time (_note[0], _note[1]) to + * pixels. + */ double const x0 = _editor->time_to_pixel (_note[0]); double const x1 = _editor->time_to_pixel (_note[1]); @@ -6997,7 +6987,9 @@ NoteCreateDrag::motion (GdkEvent* event, bool) aligned_beats += grid_beats; } - _note[1] = timepos_t (max (Temporal::Beats(), aligned_beats - _region_view->region()->position ().beats())); + _note[1] = timepos_t (max (Temporal::Beats(), aligned_beats)); + + /* We continue to draw the dragging rect with absolute time/pixel coordinates */ double const x0 = _editor->time_to_pixel (_note[0]); double const x1 = _editor->time_to_pixel (_note[1]); @@ -7009,8 +7001,11 @@ void NoteCreateDrag::finished (GdkEvent* ev, bool had_movement) { /* we create a note even if there was no movement */ - Beats const start = (min (_note[0], _note[1])).beats (); - Beats length = max (Beats (1, 0), (_note[1].distance (_note[0]).abs().beats())); + + /* Compute start within region, rather than absolute time start */ + + Beats const start = _region_view->region()->absolute_time_to_region_beats (min (_note[0], _note[1])); + Beats length = max (Beats (0, 1), (_note[0].distance (_note[1]).abs().beats())); int32_t div = _editor->get_grid_music_divisions (ev->button.state); @@ -7019,7 +7014,7 @@ NoteCreateDrag::finished (GdkEvent* ev, bool had_movement) } _editor->begin_reversible_command (_("Create Note")); - _region_view->create_note_at (start, _drag_rect->y0(), length, ev->button.state, false); + _region_view->create_note_at (timepos_t (start), _drag_rect->y0(), length, ev->button.state, false); _editor->commit_reversible_command (); } @@ -7040,7 +7035,7 @@ NoteCreateDrag::aborted (bool) HitCreateDrag::HitCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv) : Drag (e, i) , _region_view (rv) - , _last_pos (0) + , _last_pos (Temporal::Beats()) , _y (0.0) { } @@ -7072,9 +7067,9 @@ HitCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) _editor->begin_reversible_command (_("Create Hit")); _region_view->clear_note_selection(); - _region_view->create_note_at (start, _y, length, event->button.state, false); + _region_view->create_note_at (timepos_t (start), _y, length, event->button.state, false); - _last_pos = start; + _last_pos = timepos_t (start); } void @@ -7102,9 +7097,9 @@ HitCreateDrag::motion (GdkEvent* event, bool) return; } - _region_view->create_note_at (start, _y, length, event->button.state, false); + _region_view->create_note_at (timepos_t (start), _y, length, event->button.state, false); - _last_pos = start; + _last_pos = timepos_t (start); } void diff --git a/gtk2_ardour/editor_drag.h b/gtk2_ardour/editor_drag.h index e8a1270da0..06aa46b9c4 100644 --- a/gtk2_ardour/editor_drag.h +++ b/gtk2_ardour/editor_drag.h @@ -713,7 +713,7 @@ private: } MidiRegionView* _region_view; - samplepos_t _last_pos; + Temporal::timepos_t _last_pos; double _y; }; @@ -876,7 +876,7 @@ private: bool _copy; bool _movable; Temporal::Tempo _grab_bpm; - double _grab_qn; + Temporal::Beats _grab_qn; XMLNode* _before_state; }; @@ -902,7 +902,7 @@ public: void setup_pointer_offset (); private: - double _grab_qn; + Temporal::Beats _grab_qn; Temporal::TempoPoint* _tempo; XMLNode* _before_state; bool _drag_valid; @@ -932,7 +932,7 @@ public: void setup_pointer_offset (); private: - double _grab_qn; + Temporal::Beats _grab_qn; Temporal::Tempo _grab_tempo; Temporal::TempoPoint* _tempo; Temporal::TempoPoint* _next_tempo; @@ -963,7 +963,7 @@ public: void setup_pointer_offset (); private: - double _grab_qn; + Temporal::Beats _grab_qn; Temporal::TempoPoint* _tempo; XMLNode* _before_state; bool _drag_valid; diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index e4b1a3f83c..224939a1fa 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -837,9 +837,9 @@ MidiRegionView::show_list_editor () * \param snap_t true to snap t to the grid, otherwise false. */ void -MidiRegionView::create_note_at (samplepos_t t, double y, Temporal::Beats length, uint32_t state, bool shift_snap) +MidiRegionView::create_note_at (timepos_t const & t, double y, Temporal::Beats length, uint32_t state, bool shift_snap) { - if (length < 2 * DBL_EPSILON) { + if (length < Temporal::Beats::one_tick()) { return; } @@ -851,16 +851,15 @@ MidiRegionView::create_note_at (samplepos_t t, double y, Temporal::Beats length, return; } - // Start of note in samples relative to region start - const int32_t divisions = trackview.editor().get_grid_music_divisions (state); - Temporal::Beats beat_time = snap_sample_to_grid_underneath (t, divisions, shift_snap); + /* assume time is already region-relative and snapped */ + + Temporal::Beats region_start = t.beats(); 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); + const uint8_t velocity = get_velocity_for_add (region_start); - const boost::shared_ptr new_note( - new NoteType (chan, beat_time, length, (uint8_t)note, velocity)); + const boost::shared_ptr new_note (new NoteType (chan, region_start, length, (uint8_t)note, velocity)); if (_model->contains (new_note)) { return; @@ -1644,10 +1643,30 @@ MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions) { const boost::shared_ptr mr = midi_region(); boost::shared_ptr note = ev->note(); - const timepos_t note_start = _region->source_beats_to_absolute_time (note->time()); + const timepos_t note_start (note->time()); + timepos_t note_end (note->end_time()); - const double x0 = trackview.editor().time_to_pixel (note_start); + /* The note is drawn as a child item of this region view, so its + * coordinate system is relative to the region view. This means that x0 + * and x1 are pixel offsets relative to beginning of the region (view) + */ + + /* compute absolute time where the start of the source is + */ + + const timepos_t session_source_start = _region->source_position(); + + /* this computes the number of samples from the start of the region of the start of the + * note. We add the source start to get to the absolute time of the + * note, then subtract the start of the region + */ + + const samplepos_t note_start_samples = (note_start + session_source_start).earlier ( _region->position()).samples(); + + const double x0 = trackview.editor().sample_to_pixel (note_start_samples); double x1; + + const double y0 = 1 + floor(note_to_y(note->note())); double y1; @@ -1661,13 +1680,15 @@ MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions) /* normal note */ - timepos_t note_end (note->end_time()); + const Temporal::Beats source_end ((_region->start() + _region->length()).beats()); - if (note_end > mr->start() + mr->length()) { - note_end = mr->start() + mr->length(); + if (note->end_time() > source_end) { + note_end = timepos_t (source_end); } - x1 = std::max(1., trackview.editor().time_to_pixel (note_end)) - 1; + const samplepos_t note_end_samples = _region->position().distance ((note_end + session_source_start)).samples(); + + x1 = std::max(1., trackview.editor().sample_to_pixel (note_end_samples)) - 1; } else { @@ -2912,16 +2933,16 @@ MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_ if (!cursor_set) { /* Convert snap delta from pixels to beats. */ - samplepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta); - double snap_delta_beats = 0.0; + timepos_t snap_delta_time = timepos_t (trackview.editor().pixel_to_sample (snap_delta)); + Beats snap_delta_beats; int sign = 1; /* negative beat offsets aren't allowed */ - if (snap_delta_samps > 0) { - snap_delta_beats = _region->region_distance_to_region_beats (timecnt_t (snap_delta_samps, _region->position())); - } else if (snap_delta_samps < 0) { - snap_delta_beats = _region->region_distance_to_region_beats (timecnt_t (-snap_delta_samps, _region->position())); + if (snap_delta_time > 0) { + snap_delta_beats = _region->region_distance_to_region_beats (timecnt_t (snap_delta_time, _region->position())); + } else if (snap_delta_time < 0) { + snap_delta_beats = _region->region_distance_to_region_beats (timecnt_t (-snap_delta_time, _region->position())); sign = -1; } @@ -3009,14 +3030,14 @@ MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_ } /* Convert snap delta from pixels to beats with sign. */ - samplepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta); + timepos_t snap_delta_time (trackview.editor().pixel_to_sample (snap_delta)); Temporal::Beats snap_delta_beats; int sign = 1; - if (snap_delta_samps > 0) { - snap_delta_beats = _region->region_distance_to_region_beats (timecnt_t (snap_delta_samps, _region->position())); - } else if (snap_delta_samps < 0) { - snap_delta_beats = _region->region_distance_to_region_beats (timecnt_t (-snap_delta_samps, _region->position())); + if (snap_delta_time.positive()) { + snap_delta_beats = _region->region_distance_to_region_beats (timecnt_t (snap_delta_time, _region->position())); + } else if (snap_delta_time.negative()) { + snap_delta_beats = _region->region_distance_to_region_beats (timecnt_t (-snap_delta_time, _region->position())); sign = -1; } @@ -3905,6 +3926,7 @@ MidiRegionView::update_ghost_note (double x, double y, uint32_t state) _ghost_note->note()->set_note (y_to_note (y)); _ghost_note->note()->set_channel (mtv->get_channel_for_add ()); _ghost_note->note()->set_velocity (get_velocity_for_add (snapped_beats)); + update_note (_ghost_note, false); show_verbose_cursor (_ghost_note->note ()); @@ -4271,6 +4293,7 @@ MidiRegionView::snap_sample_to_grid_underneath (samplepos_t p, int32_t divisions TempoMap::SharedPtr tmap (TempoMap::use()); timepos_t pos (_region->position() + timecnt_t (p)); + Beats snapped_beats = tmap->quarters_at (pos).round_to_subdivision (divisions, RoundNearest); if (shift_snap) { @@ -4338,4 +4361,3 @@ MidiRegionView::note_to_y(uint8_t note) const { return contents_height() - (note + 1 - _current_range_min) * note_height() + 1; } - diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index 0d313ba194..0d2459981a 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -260,8 +260,8 @@ public: MouseState mouse_state() const { return _mouse_state; } struct NoteResizeData { - Note *note; - ArdourCanvas::Rectangle *resize_rect; + ::Note *note; + ArdourCanvas::Rectangle *resize_rect; }; /** Snap a region relative pixel coordinate to pixel units. @@ -312,7 +312,7 @@ public: * \param state the keyboard modifier mask for the canvas event (click). * \param shift_snap true alters snap behavior to round down always (false if the gui has already done that). */ - void create_note_at (samplepos_t t, double y, Temporal::Beats length, uint32_t state, bool shift_snap); + void create_note_at (Temporal::timepos_t const & t, double y, Temporal::Beats length, uint32_t state, bool shift_snap); /** An external request to clear the note selection, remove MRV from editor * selection.