finish basic distribution of EditingContext methods

This compiles but is not expected to work yet
This commit is contained in:
Paul Davis 2023-11-19 20:43:29 -07:00
parent 4398fe931b
commit e6c56b39d1
10 changed files with 351 additions and 288 deletions

View file

@ -28,6 +28,7 @@
#include "actions.h" #include "actions.h"
#include "editing_context.h" #include "editing_context.h"
#include "editor_drag.h" #include "editor_drag.h"
#include "keyboard.h"
#include "midi_region_view.h" #include "midi_region_view.h"
#include "selection.h" #include "selection.h"
#include "selection_memento.h" #include "selection_memento.h"
@ -35,6 +36,7 @@
#include "pbd/i18n.h" #include "pbd/i18n.h"
using namespace ARDOUR;
using namespace Editing; using namespace Editing;
using namespace Glib; using namespace Glib;
using namespace Gtk; using namespace Gtk;
@ -93,6 +95,11 @@ EditingContext::EditingContext ()
, _verbose_cursor (nullptr) , _verbose_cursor (nullptr)
, samples_per_pixel (2048) , samples_per_pixel (2048)
, zoom_focus (ZoomFocusPlayhead) , 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); grid_type_strings = I18N (_grid_type_strings);
@ -1359,8 +1366,8 @@ EditingContext::_snap_to_bbt (timepos_t const & presnap, Temporal::RoundMode dir
return ret; return ret;
} }
static void void
check_best_snap (timepos_t const & presnap, timepos_t &test, timepos_t &dist, timepos_t &best) 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 ()); timepos_t diff = timepos_t (presnap.distance (test).abs ());
if (diff < dist) { 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 test = timepos_t::max (test.time_domain()); // reset this so it doesn't get accidentally reused
} }
void timepos_t
EditingContext::snap_to_internal (timepos_t& start, Temporal::RoundMode direction, SnapPref pref, bool ensure_snap) EditingContext::canvas_event_time (GdkEvent const * event, double* pcx, double* pcy) const
{ {
UIConfiguration const& uic (UIConfiguration::instance ()); timepos_t pos (canvas_event_sample (event, pcx, pcy));
const timepos_t presnap = start;
if (time_domain() == Temporal::AudioTime) {
timepos_t test = timepos_t::max (start.time_domain()); // for each snap, we'll use this value return pos;
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 */ return timepos_t (pos.beats());
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<timepos_t>::iterator prev = region_boundary_cache.begin ();
vector<timepos_t>::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;
} }
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;
}

View file

@ -142,7 +142,6 @@ public:
virtual void set_selected_midi_region_view (MidiRegionView&); virtual void set_selected_midi_region_view (MidiRegionView&);
/* NOTE: these functions assume that the "pixel" coordinate is /* NOTE: these functions assume that the "pixel" coordinate is
in canvas coordinates. These coordinates already take into in canvas coordinates. These coordinates already take into
account any scrolling offsets. account any scrolling offsets.
@ -180,16 +179,17 @@ public:
double duration_to_pixels (Temporal::timecnt_t const & pos) const; double duration_to_pixels (Temporal::timecnt_t const & pos) const;
double duration_to_pixels_unrounded (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 /** computes the timeline position for an event whose coordinates
* are in canvas units (pixels, scroll offset included). The time * are in canvas units (pixels, scroll offset included). The time
* domain used by the return value will match ::default_time_domain() * domain used by the return value will match ::default_time_domain()
* at the time of calling. * 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_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; 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; ArdourWidgets::ArdourButton snap_mode_button;
virtual void mark_region_boundary_cache_dirty () {} 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 update_tempo_based_rulers () {};
virtual void show_rulers_for_grid () {}; virtual void show_rulers_for_grid () {};
virtual samplecnt_t current_page_samples() const = 0; virtual samplecnt_t current_page_samples() const = 0;
@ -381,10 +380,41 @@ public:
ARDOUR::SnapPref gpref, ARDOUR::SnapPref gpref,
Editing::GridType grid_type); Editing::GridType grid_type);
void snap_to_internal (Temporal::timepos_t& first, virtual Temporal::timepos_t snap_to_grid (Temporal::timepos_t const & start,
Temporal::RoundMode direction = Temporal::RoundNearest, Temporal::RoundMode direction,
ARDOUR::SnapPref gpref = ARDOUR::SnapToAny_Visual, ARDOUR::SnapPref gpref) = 0;
bool ensure_snap = false);
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
}; };

View file

@ -278,9 +278,6 @@ Editor::Editor ()
, timecode_mark_modulo (0) , timecode_mark_modulo (0)
, timecode_nmarks (0) , timecode_nmarks (0)
, _samples_ruler_interval (0) , _samples_ruler_interval (0)
, bbt_ruler_scale (bbt_show_many)
, bbt_bars (0)
, bbt_bar_helper_on (0)
, timecode_ruler (0) , timecode_ruler (0)
, bbt_ruler (0) , bbt_ruler (0)
, samples_ruler (0) , samples_ruler (0)
@ -312,8 +309,6 @@ Editor::Editor ()
, unused_adjustment (0.0, 0.0, 10.0, 400.0) , unused_adjustment (0.0, 0.0, 10.0, 400.0)
, controls_layout (unused_adjustment, vertical_adjustment) , controls_layout (unused_adjustment, vertical_adjustment)
, _scroll_callbacks (0) , _scroll_callbacks (0)
, _visible_canvas_width (0)
, _visible_canvas_height (0)
, _full_canvas_height (0) , _full_canvas_height (0)
, edit_controls_left_menu (0) , edit_controls_left_menu (0)
, edit_controls_right_menu (0) , edit_controls_right_menu (0)
@ -6512,3 +6507,87 @@ Editor::track_dragging() const
return (bool) track_drag; 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<timepos_t>::iterator prev = region_boundary_cache.begin ();
vector<timepos_t>::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;
}

View file

@ -927,27 +927,6 @@ private:
samplecnt_t _samples_ruler_interval; samplecnt_t _samples_ruler_interval;
void set_samples_ruler_scale (samplepos_t, samplepos_t); 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* timecode_ruler;
ArdourCanvas::Ruler* bbt_ruler; ArdourCanvas::Ruler* bbt_ruler;
ArdourCanvas::Ruler* samples_ruler; ArdourCanvas::Ruler* samples_ruler;
@ -1063,8 +1042,6 @@ private:
sigc::connection _scroll_connection; sigc::connection _scroll_connection;
int _scroll_callbacks; 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 double _full_canvas_height; ///< full height of the canvas
bool track_canvas_map_handler (GdkEventAny*); bool track_canvas_map_handler (GdkEventAny*);
@ -2113,18 +2090,6 @@ private:
void duplicate_range (bool with_dialog); void duplicate_range (bool with_dialog);
void duplicate_regions (float times); 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 /** computes the timeline sample (sample) of an event whose coordinates
* are in window units (pixels, no scroll offset). * are in window units (pixels, no scroll offset).
*/ */
@ -2277,13 +2242,16 @@ private:
Temporal::RoundMode direction, Temporal::RoundMode direction,
ARDOUR::SnapPref gpref); ARDOUR::SnapPref gpref);
void timecode_snap_to_internal (Temporal::timepos_t & first, void snap_to_internal (Temporal::timepos_t & first,
Temporal::RoundMode direction = Temporal::RoundNearest, Temporal::RoundMode direction = Temporal::RoundNearest,
bool for_mark = false); ARDOUR::SnapPref gpref = ARDOUR::SnapToAny_Visual,
bool for_mark = false);
Temporal::timepos_t snap_to_marker (Temporal::timepos_t const & presnap, Temporal::timepos_t snap_to_marker (Temporal::timepos_t const & presnap,
Temporal::RoundMode direction = Temporal::RoundNearest); Temporal::RoundMode direction = Temporal::RoundNearest);
double visible_canvas_width() const { return _visible_canvas_width; }
RhythmFerret* rhythm_ferret; RhythmFerret* rhythm_ferret;
void fit_tracks (TrackViewList &); void fit_tracks (TrackViewList &);

View file

@ -170,47 +170,6 @@ Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) c
return pixel_to_sample (d.x); 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 void
Editor::set_current_trimmable (std::shared_ptr<Trimmable> t) Editor::set_current_trimmable (std::shared_ptr<Trimmable> t)
{ {

View file

@ -1045,111 +1045,6 @@ Editor::metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>& 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 static void
edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel) edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel)
{ {

View file

@ -59,7 +59,7 @@ MidiClipEditorBox::MidiClipEditorBox ()
_header_label.set_alignment (0.0, 0.5); _header_label.set_alignment (0.0, 0.5);
pack_start (_header_label, false, false, 6); pack_start (_header_label, false, false, 6);
editor = manage (new MidiCueEditor ()); editor = new MidiCueEditor ();
editor->viewport().set_size_request (600, 120); editor->viewport().set_size_request (600, 120);
pack_start (editor->viewport(), true, true); pack_start (editor->viewport(), true, true);
@ -68,6 +68,7 @@ MidiClipEditorBox::MidiClipEditorBox ()
MidiClipEditorBox::~MidiClipEditorBox () MidiClipEditorBox::~MidiClipEditorBox ()
{ {
delete editor;
} }
void void

View file

@ -25,7 +25,9 @@
#include "midi_cue_editor.h" #include "midi_cue_editor.h"
#include "ui_config.h" #include "ui_config.h"
using namespace ARDOUR;
using namespace ArdourCanvas; using namespace ArdourCanvas;
using namespace Temporal;
MidiCueEditor::MidiCueEditor() MidiCueEditor::MidiCueEditor()
: vertical_adjustment (0.0, 0.0, 10.0, 400.0) : vertical_adjustment (0.0, 0.0, 10.0, 400.0)
@ -90,3 +92,50 @@ MidiCueEditor::build_canvas ()
rubberband_rect->hide(); 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;
}

View file

@ -43,6 +43,27 @@ class MidiCueEditor : public CueEditor
ArdourCanvas::Container* get_noscroll_group() const { return no_scroll_group; } ArdourCanvas::Container* get_noscroll_group() const { return no_scroll_group; }
Gtk::Widget& viewport() { return *_canvas_viewport; } 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<std::pair<PBD::ID, std::set<std::shared_ptr<Evoral::Note<Temporal::Beats> > > > >&) 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: private:
Gtk::Adjustment vertical_adjustment; Gtk::Adjustment vertical_adjustment;
Gtk::Adjustment horizontal_adjustment; Gtk::Adjustment horizontal_adjustment;

View file

@ -452,13 +452,6 @@ public:
virtual std::pair <Temporal::timepos_t, Temporal::timepos_t> session_gui_extents (bool use_extra = true) const = 0; virtual std::pair <Temporal::timepos_t, Temporal::timepos_t> 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_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 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; virtual RegionSelection get_regions_from_selection_and_mouse (Temporal::timepos_t const &) = 0;