From e6c56b39d17e92aae48651f8e58949dcd07b3618 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Sun, 19 Nov 2023 20:43:29 -0700 Subject: [PATCH] finish basic distribution of EditingContext methods This compiles but is not expected to work yet --- gtk2_ardour/editing_context.cc | 228 +++++++++++++++++++++----------- gtk2_ardour/editing_context.h | 52 ++++++-- gtk2_ardour/editor.cc | 89 ++++++++++++- gtk2_ardour/editor.h | 44 +----- gtk2_ardour/editor_mouse.cc | 41 ------ gtk2_ardour/editor_rulers.cc | 105 --------------- gtk2_ardour/midi_clip_editor.cc | 3 +- gtk2_ardour/midi_cue_editor.cc | 49 +++++++ gtk2_ardour/midi_cue_editor.h | 21 +++ gtk2_ardour/public_editor.h | 7 - 10 files changed, 351 insertions(+), 288 deletions(-) diff --git a/gtk2_ardour/editing_context.cc b/gtk2_ardour/editing_context.cc index f4a0f5f6ad..eaf4f0708b 100644 --- a/gtk2_ardour/editing_context.cc +++ b/gtk2_ardour/editing_context.cc @@ -28,6 +28,7 @@ #include "actions.h" #include "editing_context.h" #include "editor_drag.h" +#include "keyboard.h" #include "midi_region_view.h" #include "selection.h" #include "selection_memento.h" @@ -35,6 +36,7 @@ #include "pbd/i18n.h" +using namespace ARDOUR; using namespace Editing; using namespace Glib; using namespace Gtk; @@ -93,6 +95,11 @@ EditingContext::EditingContext () , _verbose_cursor (nullptr) , samples_per_pixel (2048) , zoom_focus (ZoomFocusPlayhead) + , bbt_ruler_scale (bbt_show_many) + , bbt_bars (0) + , bbt_bar_helper_on (0) + , _visible_canvas_width (0) + , _visible_canvas_height (0) { grid_type_strings = I18N (_grid_type_strings); @@ -1359,8 +1366,8 @@ EditingContext::_snap_to_bbt (timepos_t const & presnap, Temporal::RoundMode dir return ret; } -static void -check_best_snap (timepos_t const & presnap, timepos_t &test, timepos_t &dist, timepos_t &best) +void +EditingContext::check_best_snap (timepos_t const & presnap, timepos_t &test, timepos_t &dist, timepos_t &best) { timepos_t diff = timepos_t (presnap.distance (test).abs ()); if (diff < dist) { @@ -1371,87 +1378,148 @@ check_best_snap (timepos_t const & presnap, timepos_t &test, timepos_t &dist, ti test = timepos_t::max (test.time_domain()); // reset this so it doesn't get accidentally reused } -void -EditingContext::snap_to_internal (timepos_t& start, Temporal::RoundMode direction, SnapPref pref, bool ensure_snap) +timepos_t +EditingContext::canvas_event_time (GdkEvent const * event, double* pcx, double* pcy) const { - UIConfiguration const& uic (UIConfiguration::instance ()); - const timepos_t presnap = start; + timepos_t pos (canvas_event_sample (event, pcx, pcy)); - - timepos_t test = timepos_t::max (start.time_domain()); // for each snap, we'll use this value - timepos_t dist = timepos_t::max (start.time_domain()); // this records the distance of the best snap result we've found so far - timepos_t best = timepos_t::max (start.time_domain()); // this records the best snap-result we've found so far - - /* check Grid */ - if ( (_grid_type != GridTypeNone) && (uic.get_snap_target () != SnapTargetOther) ) { - timepos_t pre (presnap); - timepos_t post (snap_to_grid (pre, direction, pref)); - check_best_snap (presnap, post, dist, best); - if (uic.get_snap_target () == SnapTargetGrid) { - goto check_distance; - } + if (time_domain() == Temporal::AudioTime) { + return pos; } - /* check snap-to-marker */ - if ((pref == SnapToAny_Visual) && uic.get_snap_to_marks ()) { - test = snap_to_marker (presnap, direction); - check_best_snap (presnap, test, dist, best); - } - - /* check snap-to-playhead */ - if ((pref == SnapToAny_Visual) && uic.get_snap_to_playhead () && !_session->transport_rolling ()) { - test = timepos_t (_session->audible_sample()); - check_best_snap (presnap, test, dist, best); - } - - /* check snap-to-region-{start/end/sync} */ - if ((pref == SnapToAny_Visual) && (uic.get_snap_to_region_start () || uic.get_snap_to_region_end () || uic.get_snap_to_region_sync ())) { - - if (!region_boundary_cache.empty ()) { - - vector::iterator prev = region_boundary_cache.begin (); - vector::iterator next = std::upper_bound (region_boundary_cache.begin (), region_boundary_cache.end (), presnap); - if (next != region_boundary_cache.begin ()) { - prev = next; - prev--; - } - if (next == region_boundary_cache.end ()) { - next--; - } - - if ((direction == Temporal::RoundUpMaybe || direction == Temporal::RoundUpAlways)) { - test = *next; - } else if ((direction == Temporal::RoundDownMaybe || direction == Temporal::RoundDownAlways)) { - test = *prev; - } else if (direction == 0) { - if ((*prev).distance (presnap) < presnap.distance (*next)) { - test = *prev; - } else { - test = *next; - } - } - - } - - check_best_snap (presnap, test, dist, best); - } - - check_distance: - - if (timepos_t::max (start.time_domain()) == best) { - return; - } - - /* now check "magnetic" state: is the grid within reasonable on-screen distance to trigger a snap? - * this also helps to avoid snapping to somewhere the user can't see. (i.e.: I clicked on a region and it disappeared!!) - * ToDo: Perhaps this should only occur if EditPointMouse? - */ - samplecnt_t snap_threshold_s = pixel_to_sample (uic.get_snap_threshold ()); - - if (!ensure_snap && ::llabs (best.distance (presnap).samples()) > snap_threshold_s) { - return; - } - - start = best; + return timepos_t (pos.beats()); } +samplepos_t +EditingContext::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) const +{ + double x; + double y; + + /* event coordinates are already in canvas units */ + + if (!gdk_event_get_coords (event, &x, &y)) { + std::cerr << "!NO c COORDS for event type " << event->type << std::endl; + return 0; + } + + if (pcx) { + *pcx = x; + } + + if (pcy) { + *pcy = y; + } + + /* note that pixel_to_sample_from_event() never returns less than zero, so even if the pixel + position is negative (as can be the case with motion events in particular), + the sample location is always positive. + */ + + return pixel_to_sample_from_event (x); +} + +uint32_t +EditingContext::count_bars (Beats const & start, Beats const & end) const +{ + TempoMapPoints bar_grid; + TempoMap::SharedPtr tmap (TempoMap::use()); + bar_grid.reserve (4096); + superclock_t s (tmap->superclock_at (start)); + superclock_t e (tmap->superclock_at (end)); + tmap->get_grid (bar_grid, s, e, 1); + return bar_grid.size(); +} + +void +EditingContext::compute_bbt_ruler_scale (samplepos_t lower, samplepos_t upper) +{ + if (_session == 0) { + return; + } + + Temporal::BBT_Time lower_beat, upper_beat; // the beats at each end of the ruler + Temporal::TempoMap::SharedPtr tmap (Temporal::TempoMap::use()); + Beats floor_lower_beat = std::max (Beats(), tmap->quarters_at_sample (lower)).round_down_to_beat (); + + if (floor_lower_beat < Temporal::Beats()) { + floor_lower_beat = Temporal::Beats(); + } + + const samplepos_t beat_before_lower_pos = tmap->sample_at (floor_lower_beat); + const samplepos_t beat_after_upper_pos = tmap->sample_at ((std::max (Beats(), tmap->quarters_at_sample (upper)).round_down_to_beat()) + Beats (1, 0)); + + lower_beat = Temporal::TempoMap::use()->bbt_at (timepos_t (beat_before_lower_pos)); + upper_beat = Temporal::TempoMap::use()->bbt_at (timepos_t (beat_after_upper_pos)); + uint32_t beats = 0; + + bbt_bar_helper_on = false; + bbt_bars = 0; + + bbt_ruler_scale = bbt_show_many; + + const Beats ceil_upper_beat = std::max (Beats(), tmap->quarters_at_sample (upper)).round_up_to_beat() + Beats (1, 0); + + if (ceil_upper_beat == floor_lower_beat) { + return; + } + + bbt_bars = count_bars (floor_lower_beat, ceil_upper_beat); + + double ruler_line_granularity = UIConfiguration::instance().get_ruler_granularity (); //in pixels + ruler_line_granularity = visible_canvas_width() / (ruler_line_granularity*5); //fudge factor '5' probably related to (4+1 beats)/measure, I think + + beats = (ceil_upper_beat - floor_lower_beat).get_beats(); + double beat_density = ((beats + 1) * ((double) (upper - lower) / (double) (1 + beat_after_upper_pos - beat_before_lower_pos))) / (float)ruler_line_granularity; + + /* Only show the bar helper if there aren't many bars on the screen */ + if ((bbt_bars < 2) || (beats < 5)) { + bbt_bar_helper_on = true; + } + + if (beat_density > 2048) { + bbt_ruler_scale = bbt_show_many; + } else if (beat_density > 1024) { + bbt_ruler_scale = bbt_show_64; + } else if (beat_density > 256) { + bbt_ruler_scale = bbt_show_16; + } else if (beat_density > 64) { + bbt_ruler_scale = bbt_show_4; + } else if (beat_density > 16) { + bbt_ruler_scale = bbt_show_1; + } else if (beat_density > 4) { + bbt_ruler_scale = bbt_show_quarters; + } else if (beat_density > 2) { + bbt_ruler_scale = bbt_show_eighths; + } else if (beat_density > 1) { + bbt_ruler_scale = bbt_show_sixteenths; + } else if (beat_density > 0.5) { + bbt_ruler_scale = bbt_show_thirtyseconds; + } else if (beat_density > 0.25) { + bbt_ruler_scale = bbt_show_sixtyfourths; + } else { + bbt_ruler_scale = bbt_show_onetwentyeighths; + } + + /* Now that we know how fine a grid (Ruler) is allowable on this screen, limit it to the coarseness selected by the user */ + /* note: GridType and RulerScale are not the same enums, so it's not a simple mathematical operation */ + int suggested_scale = (int) bbt_ruler_scale; + int divs = get_grid_music_divisions(_grid_type, 0); + if (_grid_type == GridTypeBar) { + suggested_scale = std::min(suggested_scale, (int) bbt_show_1); + } else if (_grid_type == GridTypeBeat) { + suggested_scale = std::min(suggested_scale, (int) bbt_show_quarters); + } else if ( divs < 4 ) { + suggested_scale = std::min(suggested_scale, (int) bbt_show_eighths); + } else if ( divs < 8 ) { + suggested_scale = std::min(suggested_scale, (int) bbt_show_sixteenths); + } else if ( divs < 16 ) { + suggested_scale = std::min(suggested_scale, (int) bbt_show_thirtyseconds); + } else if ( divs < 32 ) { + suggested_scale = std::min(suggested_scale, (int) bbt_show_sixtyfourths); + } else { + suggested_scale = std::min(suggested_scale, (int) bbt_show_onetwentyeighths); + } + + bbt_ruler_scale = (EditingContext::BBTRulerScale) suggested_scale; +} diff --git a/gtk2_ardour/editing_context.h b/gtk2_ardour/editing_context.h index 22fb49bbae..c4ad3f16d8 100644 --- a/gtk2_ardour/editing_context.h +++ b/gtk2_ardour/editing_context.h @@ -142,7 +142,6 @@ public: virtual void set_selected_midi_region_view (MidiRegionView&); - /* NOTE: these functions assume that the "pixel" coordinate is in canvas coordinates. These coordinates already take into account any scrolling offsets. @@ -180,16 +179,17 @@ public: double duration_to_pixels (Temporal::timecnt_t const & pos) const; double duration_to_pixels_unrounded (Temporal::timecnt_t const & pos) const; - /** computes the timeline sample (sample) of an event whose coordinates - * are in canvas units (pixels, scroll offset included). - */ - virtual samplepos_t canvas_event_sample (GdkEvent const * event, double* pcx = nullptr, double* pcy = nullptr) const = 0; /** computes the timeline position for an event whose coordinates * are in canvas units (pixels, scroll offset included). The time * domain used by the return value will match ::default_time_domain() * at the time of calling. */ - virtual Temporal::timepos_t canvas_event_time (GdkEvent const*, double* px = nullptr, double* py = nullptr) const = 0; + Temporal::timepos_t canvas_event_time (GdkEvent const*, double* px = 0, double* py = 0) const; + + /** computes the timeline sample (sample) of an event whose coordinates + * are in canvas units (pixels, scroll offset included). + */ + samplepos_t canvas_event_sample (GdkEvent const * event, double* pcx = nullptr, double* pcy = nullptr) const; virtual Temporal::Beats get_grid_type_as_beats (bool& success, Temporal::timepos_t const & position) = 0; virtual Temporal::Beats get_draw_length_as_beats (bool& success, Temporal::timepos_t const & position) = 0; @@ -346,7 +346,6 @@ public: ArdourWidgets::ArdourButton snap_mode_button; virtual void mark_region_boundary_cache_dirty () {} - virtual void compute_bbt_ruler_scale (samplepos_t, samplepos_t) {} virtual void update_tempo_based_rulers () {}; virtual void show_rulers_for_grid () {}; virtual samplecnt_t current_page_samples() const = 0; @@ -381,10 +380,41 @@ public: ARDOUR::SnapPref gpref, Editing::GridType grid_type); - void snap_to_internal (Temporal::timepos_t& first, - Temporal::RoundMode direction = Temporal::RoundNearest, - ARDOUR::SnapPref gpref = ARDOUR::SnapToAny_Visual, - bool ensure_snap = false); + virtual Temporal::timepos_t snap_to_grid (Temporal::timepos_t const & start, + Temporal::RoundMode direction, + ARDOUR::SnapPref gpref) = 0; + + virtual void snap_to_internal (Temporal::timepos_t& first, + Temporal::RoundMode direction = Temporal::RoundNearest, + ARDOUR::SnapPref gpref = ARDOUR::SnapToAny_Visual, + bool ensure_snap = false) = 0; + + void check_best_snap (Temporal::timepos_t const & presnap, Temporal::timepos_t &test, Temporal::timepos_t &dist, Temporal::timepos_t &best); + virtual double visible_canvas_width() const = 0; + + enum BBTRulerScale { + bbt_show_many, + bbt_show_64, + bbt_show_16, + bbt_show_4, + bbt_show_1, + bbt_show_quarters, + bbt_show_eighths, + bbt_show_sixteenths, + bbt_show_thirtyseconds, + bbt_show_sixtyfourths, + bbt_show_onetwentyeighths + }; + + BBTRulerScale bbt_ruler_scale; + uint32_t bbt_bars; + uint32_t bbt_bar_helper_on; + + uint32_t count_bars (Temporal::Beats const & start, Temporal::Beats const & end) const; + void compute_bbt_ruler_scale (samplepos_t lower, samplepos_t upper); + + double _visible_canvas_width; + double _visible_canvas_height; ///< height of the visible area of the track canvas }; diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index 3155fc2634..04035b75db 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -278,9 +278,6 @@ Editor::Editor () , timecode_mark_modulo (0) , timecode_nmarks (0) , _samples_ruler_interval (0) - , bbt_ruler_scale (bbt_show_many) - , bbt_bars (0) - , bbt_bar_helper_on (0) , timecode_ruler (0) , bbt_ruler (0) , samples_ruler (0) @@ -312,8 +309,6 @@ Editor::Editor () , unused_adjustment (0.0, 0.0, 10.0, 400.0) , controls_layout (unused_adjustment, vertical_adjustment) , _scroll_callbacks (0) - , _visible_canvas_width (0) - , _visible_canvas_height (0) , _full_canvas_height (0) , edit_controls_left_menu (0) , edit_controls_right_menu (0) @@ -6512,3 +6507,87 @@ Editor::track_dragging() const return (bool) track_drag; } +void +Editor::snap_to_internal (timepos_t& start, Temporal::RoundMode direction, SnapPref pref, bool ensure_snap) +{ + UIConfiguration const& uic (UIConfiguration::instance ()); + const timepos_t presnap = start; + + + timepos_t test = timepos_t::max (start.time_domain()); // for each snap, we'll use this value + timepos_t dist = timepos_t::max (start.time_domain()); // this records the distance of the best snap result we've found so far + timepos_t best = timepos_t::max (start.time_domain()); // this records the best snap-result we've found so far + + /* check Grid */ + if ( (_grid_type != GridTypeNone) && (uic.get_snap_target () != SnapTargetOther) ) { + timepos_t pre (presnap); + timepos_t post (snap_to_grid (pre, direction, pref)); + check_best_snap (presnap, post, dist, best); + if (uic.get_snap_target () == SnapTargetGrid) { + goto check_distance; + } + } + + /* check snap-to-marker */ + if ((pref == SnapToAny_Visual) && uic.get_snap_to_marks ()) { + test = snap_to_marker (presnap, direction); + check_best_snap (presnap, test, dist, best); + } + + /* check snap-to-playhead */ + if ((pref == SnapToAny_Visual) && uic.get_snap_to_playhead () && !_session->transport_rolling ()) { + test = timepos_t (_session->audible_sample()); + check_best_snap (presnap, test, dist, best); + } + + /* check snap-to-region-{start/end/sync} */ + if ((pref == SnapToAny_Visual) && (uic.get_snap_to_region_start () || uic.get_snap_to_region_end () || uic.get_snap_to_region_sync ())) { + + if (!region_boundary_cache.empty ()) { + + vector::iterator prev = region_boundary_cache.begin (); + vector::iterator next = std::upper_bound (region_boundary_cache.begin (), region_boundary_cache.end (), presnap); + if (next != region_boundary_cache.begin ()) { + prev = next; + prev--; + } + if (next == region_boundary_cache.end ()) { + next--; + } + + if ((direction == Temporal::RoundUpMaybe || direction == Temporal::RoundUpAlways)) { + test = *next; + } else if ((direction == Temporal::RoundDownMaybe || direction == Temporal::RoundDownAlways)) { + test = *prev; + } else if (direction == 0) { + if ((*prev).distance (presnap) < presnap.distance (*next)) { + test = *prev; + } else { + test = *next; + } + } + + } + + check_best_snap (presnap, test, dist, best); + } + + check_distance: + + if (timepos_t::max (start.time_domain()) == best) { + return; + } + + /* now check "magnetic" state: is the grid within reasonable on-screen distance to trigger a snap? + * this also helps to avoid snapping to somewhere the user can't see. (i.e.: I clicked on a region and it disappeared!!) + * ToDo: Perhaps this should only occur if EditPointMouse? + */ + samplecnt_t snap_threshold_s = pixel_to_sample (uic.get_snap_threshold ()); + + if (!ensure_snap && ::llabs (best.distance (presnap).samples()) > snap_threshold_s) { + return; + } + + start = best; +} + diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index f30ff42259..056d7a5278 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -927,27 +927,6 @@ private: samplecnt_t _samples_ruler_interval; void set_samples_ruler_scale (samplepos_t, samplepos_t); - enum BBTRulerScale { - bbt_show_many, - bbt_show_64, - bbt_show_16, - bbt_show_4, - bbt_show_1, - bbt_show_quarters, - bbt_show_eighths, - bbt_show_sixteenths, - bbt_show_thirtyseconds, - bbt_show_sixtyfourths, - bbt_show_onetwentyeighths - }; - - BBTRulerScale bbt_ruler_scale; - uint32_t bbt_bars; - uint32_t bbt_bar_helper_on; - - uint32_t count_bars (Temporal::Beats const & start, Temporal::Beats const & end) const; - void compute_bbt_ruler_scale (samplepos_t lower, samplepos_t upper); - ArdourCanvas::Ruler* timecode_ruler; ArdourCanvas::Ruler* bbt_ruler; ArdourCanvas::Ruler* samples_ruler; @@ -1063,8 +1042,6 @@ private: sigc::connection _scroll_connection; int _scroll_callbacks; - double _visible_canvas_width; - double _visible_canvas_height; ///< height of the visible area of the track canvas double _full_canvas_height; ///< full height of the canvas bool track_canvas_map_handler (GdkEventAny*); @@ -2113,18 +2090,6 @@ private: void duplicate_range (bool with_dialog); void duplicate_regions (float times); - /** computes the timeline sample (sample) of an event whose coordinates - * are in canvas units (pixels, scroll offset included). - */ - samplepos_t canvas_event_sample (GdkEvent const*, double* px = 0, double* py = 0) const; - - /** computes the timeline position for an event whose coordinates - * are in canvas units (pixels, scroll offset included). The time - * domain used by the return value will match Editor::default_time_domain() - * at the time of calling. - */ - Temporal::timepos_t canvas_event_time (GdkEvent const*, double* px = 0, double* py = 0) const; - /** computes the timeline sample (sample) of an event whose coordinates * are in window units (pixels, no scroll offset). */ @@ -2277,13 +2242,16 @@ private: Temporal::RoundMode direction, ARDOUR::SnapPref gpref); - void timecode_snap_to_internal (Temporal::timepos_t & first, - Temporal::RoundMode direction = Temporal::RoundNearest, - bool for_mark = false); + void snap_to_internal (Temporal::timepos_t & first, + Temporal::RoundMode direction = Temporal::RoundNearest, + ARDOUR::SnapPref gpref = ARDOUR::SnapToAny_Visual, + bool for_mark = false); Temporal::timepos_t snap_to_marker (Temporal::timepos_t const & presnap, Temporal::RoundMode direction = Temporal::RoundNearest); + double visible_canvas_width() const { return _visible_canvas_width; } + RhythmFerret* rhythm_ferret; void fit_tracks (TrackViewList &); diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index f79ac6d366..1101457c59 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -170,47 +170,6 @@ Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) c return pixel_to_sample (d.x); } -timepos_t -Editor::canvas_event_time (GdkEvent const * event, double* pcx, double* pcy) const -{ - timepos_t pos (canvas_event_sample (event, pcx, pcy)); - - if (time_domain() == Temporal::AudioTime) { - return pos; - } - - return timepos_t (pos.beats()); -} - -samplepos_t -Editor::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) const -{ - double x; - double y; - - /* event coordinates are already in canvas units */ - - if (!gdk_event_get_coords (event, &x, &y)) { - cerr << "!NO c COORDS for event type " << event->type << endl; - return 0; - } - - if (pcx) { - *pcx = x; - } - - if (pcy) { - *pcy = y; - } - - /* note that pixel_to_sample_from_event() never returns less than zero, so even if the pixel - position is negative (as can be the case with motion events in particular), - the sample location is always positive. - */ - - return pixel_to_sample_from_event (x); -} - void Editor::set_current_trimmable (std::shared_ptr t) { diff --git a/gtk2_ardour/editor_rulers.cc b/gtk2_ardour/editor_rulers.cc index a51e35f792..301a65eec6 100644 --- a/gtk2_ardour/editor_rulers.cc +++ b/gtk2_ardour/editor_rulers.cc @@ -1045,111 +1045,6 @@ Editor::metric_get_timecode (std::vector& marks, int6 } } -uint32_t -Editor::count_bars (Beats const & start, Beats const & end) const -{ - TempoMapPoints bar_grid; - TempoMap::SharedPtr tmap (TempoMap::use()); - bar_grid.reserve (4096); - superclock_t s (tmap->superclock_at (start)); - superclock_t e (tmap->superclock_at (end)); - tmap->get_grid (bar_grid, s, e, 1); - return bar_grid.size(); -} - -void -Editor::compute_bbt_ruler_scale (samplepos_t lower, samplepos_t upper) -{ - if (_session == 0) { - return; - } - - Temporal::BBT_Time lower_beat, upper_beat; // the beats at each end of the ruler - Temporal::TempoMap::SharedPtr tmap (Temporal::TempoMap::use()); - Beats floor_lower_beat = std::max (Beats(), tmap->quarters_at_sample (lower)).round_down_to_beat (); - - if (floor_lower_beat < Temporal::Beats()) { - floor_lower_beat = Temporal::Beats(); - } - - const samplepos_t beat_before_lower_pos = tmap->sample_at (floor_lower_beat); - const samplepos_t beat_after_upper_pos = tmap->sample_at ((std::max (Beats(), tmap->quarters_at_sample (upper)).round_down_to_beat()) + Beats (1, 0)); - - lower_beat = Temporal::TempoMap::use()->bbt_at (timepos_t (beat_before_lower_pos)); - upper_beat = Temporal::TempoMap::use()->bbt_at (timepos_t (beat_after_upper_pos)); - uint32_t beats = 0; - - bbt_bar_helper_on = false; - bbt_bars = 0; - - bbt_ruler_scale = bbt_show_many; - - const Beats ceil_upper_beat = std::max (Beats(), tmap->quarters_at_sample (upper)).round_up_to_beat() + Beats (1, 0); - - if (ceil_upper_beat == floor_lower_beat) { - return; - } - - bbt_bars = count_bars (floor_lower_beat, ceil_upper_beat); - - double ruler_line_granularity = UIConfiguration::instance().get_ruler_granularity (); //in pixels - ruler_line_granularity = _visible_canvas_width / (ruler_line_granularity*5); //fudge factor '5' probably related to (4+1 beats)/measure, I think - - beats = (ceil_upper_beat - floor_lower_beat).get_beats(); - double beat_density = ((beats + 1) * ((double) (upper - lower) / (double) (1 + beat_after_upper_pos - beat_before_lower_pos))) / (float)ruler_line_granularity; - - /* Only show the bar helper if there aren't many bars on the screen */ - if ((bbt_bars < 2) || (beats < 5)) { - bbt_bar_helper_on = true; - } - - if (beat_density > 2048) { - bbt_ruler_scale = bbt_show_many; - } else if (beat_density > 1024) { - bbt_ruler_scale = bbt_show_64; - } else if (beat_density > 256) { - bbt_ruler_scale = bbt_show_16; - } else if (beat_density > 64) { - bbt_ruler_scale = bbt_show_4; - } else if (beat_density > 16) { - bbt_ruler_scale = bbt_show_1; - } else if (beat_density > 4) { - bbt_ruler_scale = bbt_show_quarters; - } else if (beat_density > 2) { - bbt_ruler_scale = bbt_show_eighths; - } else if (beat_density > 1) { - bbt_ruler_scale = bbt_show_sixteenths; - } else if (beat_density > 0.5) { - bbt_ruler_scale = bbt_show_thirtyseconds; - } else if (beat_density > 0.25) { - bbt_ruler_scale = bbt_show_sixtyfourths; - } else { - bbt_ruler_scale = bbt_show_onetwentyeighths; - } - - /* Now that we know how fine a grid (Ruler) is allowable on this screen, limit it to the coarseness selected by the user */ - /* note: GridType and RulerScale are not the same enums, so it's not a simple mathematical operation */ - int suggested_scale = (int) bbt_ruler_scale; - int divs = get_grid_music_divisions(_grid_type, 0); - if (_grid_type == GridTypeBar) { - suggested_scale = std::min(suggested_scale, (int) bbt_show_1); - } else if (_grid_type == GridTypeBeat) { - suggested_scale = std::min(suggested_scale, (int) bbt_show_quarters); - } else if ( divs < 4 ) { - suggested_scale = std::min(suggested_scale, (int) bbt_show_eighths); - } else if ( divs < 8 ) { - suggested_scale = std::min(suggested_scale, (int) bbt_show_sixteenths); - } else if ( divs < 16 ) { - suggested_scale = std::min(suggested_scale, (int) bbt_show_thirtyseconds); - } else if ( divs < 32 ) { - suggested_scale = std::min(suggested_scale, (int) bbt_show_sixtyfourths); - } else { - suggested_scale = std::min(suggested_scale, (int) bbt_show_onetwentyeighths); - } - - bbt_ruler_scale = (Editor::BBTRulerScale) suggested_scale; -} - static void edit_last_mark_label (std::vector& marks, const std::string& newlabel) { diff --git a/gtk2_ardour/midi_clip_editor.cc b/gtk2_ardour/midi_clip_editor.cc index 2d4de7c6e0..813bfb6ce2 100644 --- a/gtk2_ardour/midi_clip_editor.cc +++ b/gtk2_ardour/midi_clip_editor.cc @@ -59,7 +59,7 @@ MidiClipEditorBox::MidiClipEditorBox () _header_label.set_alignment (0.0, 0.5); pack_start (_header_label, false, false, 6); - editor = manage (new MidiCueEditor ()); + editor = new MidiCueEditor (); editor->viewport().set_size_request (600, 120); pack_start (editor->viewport(), true, true); @@ -68,6 +68,7 @@ MidiClipEditorBox::MidiClipEditorBox () MidiClipEditorBox::~MidiClipEditorBox () { + delete editor; } void diff --git a/gtk2_ardour/midi_cue_editor.cc b/gtk2_ardour/midi_cue_editor.cc index be6b96cc5f..c735ebe26f 100644 --- a/gtk2_ardour/midi_cue_editor.cc +++ b/gtk2_ardour/midi_cue_editor.cc @@ -25,7 +25,9 @@ #include "midi_cue_editor.h" #include "ui_config.h" +using namespace ARDOUR; using namespace ArdourCanvas; +using namespace Temporal; MidiCueEditor::MidiCueEditor() : vertical_adjustment (0.0, 0.0, 10.0, 400.0) @@ -90,3 +92,50 @@ MidiCueEditor::build_canvas () rubberband_rect->hide(); } + +timepos_t +MidiCueEditor::snap_to_grid (timepos_t const & presnap, Temporal::RoundMode direction, SnapPref gpref) +{ + /* BBT time only */ + return snap_to_bbt (presnap, direction, gpref); +} + + +void +MidiCueEditor::snap_to_internal (timepos_t& start, Temporal::RoundMode direction, SnapPref pref, bool ensure_snap) +{ + UIConfiguration const& uic (UIConfiguration::instance ()); + const timepos_t presnap = start; + + + timepos_t dist = timepos_t::max (start.time_domain()); // this records the distance of the best snap result we've found so far + timepos_t best = timepos_t::max (start.time_domain()); // this records the best snap-result we've found so far + + timepos_t pre (presnap); + timepos_t post (snap_to_grid (pre, direction, pref)); + + check_best_snap (presnap, post, dist, best); + + if (timepos_t::max (start.time_domain()) == best) { + return; + } + + /* now check "magnetic" state: is the grid within reasonable on-screen distance to trigger a snap? + * this also helps to avoid snapping to somewhere the user can't see. (i.e.: I clicked on a region and it disappeared!!) + * ToDo: Perhaps this should only occur if EditPointMouse? + */ + samplecnt_t snap_threshold_s = pixel_to_sample (uic.get_snap_threshold ()); + + if (!ensure_snap && ::llabs (best.distance (presnap).samples()) > snap_threshold_s) { + return; + } + + start = best; +} + +samplecnt_t +MidiCueEditor::current_page_samples() const +{ + return (samplecnt_t) _visible_canvas_width* samples_per_pixel; +} + diff --git a/gtk2_ardour/midi_cue_editor.h b/gtk2_ardour/midi_cue_editor.h index 69cf25d78e..eea75501ad 100644 --- a/gtk2_ardour/midi_cue_editor.h +++ b/gtk2_ardour/midi_cue_editor.h @@ -43,6 +43,27 @@ class MidiCueEditor : public CueEditor ArdourCanvas::Container* get_noscroll_group() const { return no_scroll_group; } Gtk::Widget& viewport() { return *_canvas_viewport; } + double visible_canvas_width() const { return _visible_canvas_width; } + samplecnt_t current_page_samples() const; + + void get_per_region_note_selection (std::list > > > >&) const {} + + Temporal::Beats get_grid_type_as_beats (bool& success, Temporal::timepos_t const & position) { return Temporal::Beats (1, 0); } + Temporal::Beats get_draw_length_as_beats (bool& success, Temporal::timepos_t const & position) { return Temporal::Beats (1, 0); } + + int32_t get_grid_beat_divisions (Editing::GridType gt) { return 1; } + int32_t get_grid_music_divisions (Editing::GridType gt, uint32_t event_state) { return 1; } + + protected: + Temporal::timepos_t snap_to_grid (Temporal::timepos_t const & start, + Temporal::RoundMode direction, + ARDOUR::SnapPref gpref); + + void snap_to_internal (Temporal::timepos_t& first, + Temporal::RoundMode direction = Temporal::RoundNearest, + ARDOUR::SnapPref gpref = ARDOUR::SnapToAny_Visual, + bool ensure_snap = false); + private: Gtk::Adjustment vertical_adjustment; Gtk::Adjustment horizontal_adjustment; diff --git a/gtk2_ardour/public_editor.h b/gtk2_ardour/public_editor.h index 42b0ee1d5b..590ee9073f 100644 --- a/gtk2_ardour/public_editor.h +++ b/gtk2_ardour/public_editor.h @@ -452,13 +452,6 @@ public: virtual std::pair session_gui_extents (bool use_extra = true) const = 0; - virtual void snap_to_with_modifier (Temporal::timepos_t & first, - GdkEvent const* ev, - Temporal::RoundMode direction = Temporal::RoundNearest, - ARDOUR::SnapPref gpref = ARDOUR::SnapToAny_Visual, - bool ensure_snap = false) = 0; - virtual Temporal::timepos_t snap_to_bbt (Temporal::timepos_t const & pos, Temporal::RoundMode, ARDOUR::SnapPref) = 0; - virtual void get_regions_at (RegionSelection &, Temporal::timepos_t const & where, TrackViewList const &) const = 0; virtual void get_regions_after (RegionSelection&, Temporal::timepos_t const & where, const TrackViewList& ts) const = 0; virtual RegionSelection get_regions_from_selection_and_mouse (Temporal::timepos_t const &) = 0;