diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index 0d7717614c..856e527a9a 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -2136,6 +2136,32 @@ Editor::snap_type() const return _snap_type; } +bool +Editor::snap_musical() const +{ + switch (_snap_type) { + case SnapToBeatDiv128: + case SnapToBeatDiv64: + case SnapToBeatDiv32: + case SnapToBeatDiv28: + case SnapToBeatDiv24: + case SnapToBeatDiv20: + case SnapToBeatDiv16: + case SnapToBeatDiv14: + case SnapToBeatDiv12: + case SnapToBeatDiv10: + case SnapToBeatDiv8: + case SnapToBeatDiv7: + case SnapToBeatDiv6: + case SnapToBeatDiv5: + case SnapToBeatDiv4: + case SnapToBeatDiv3: + case SnapToBeatDiv2: + return true; + } + return false; +} + SnapMode Editor::snap_mode() const { diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 7a2988005d..96b3d490fb 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -171,6 +171,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD Editing::SnapMode snap_mode () const; Editing::SnapType snap_type () const; + bool snap_musical () const; void undo (uint32_t n = 1); void redo (uint32_t n = 1); diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index 15246fb8fe..3305bfd040 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -3316,7 +3316,20 @@ TempoMarkerDrag::motion (GdkEvent* event, bool first_move) _marker->hide(); } - framepos_t const pf = adjusted_current_frame (event); + framepos_t pf; + if (!_editor->snap_musical()) { + pf = adjusted_current_frame (event); + } else { + pf = adjusted_current_frame (event); + Timecode::BBT_Time when; + _editor->session()->tempo_map().bbt_time (pf, when); + if (_editor->snap_type() == SnapToBar) { + _editor->session()->tempo_map().round_bbt (when, -1); + } else { + _editor->session()->tempo_map().round_bbt (when, _editor->get_grid_beat_divisions (0)); + } + pf = _editor->session()->tempo_map().predict_tempo_frame (_real_section, Tempo (_real_section->beats_per_minute(), _real_section->note_type()), when); + } Tempo const tp = _marker->tempo(); if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) { diff --git a/gtk2_ardour/tempo_lines.cc b/gtk2_ardour/tempo_lines.cc index 370ee4aac1..7a5be311ed 100644 --- a/gtk2_ardour/tempo_lines.cc +++ b/gtk2_ardour/tempo_lines.cc @@ -59,7 +59,6 @@ TempoLines::draw_ticks (std::vector& grid, framecnt_t leftmost_frame, framecnt_t frame_rate) { - const double fpb = grid.begin()->tempo.frames_per_beat(frame_rate); const uint32_t base = UIConfiguration::instance().color_mod("measure line beat", "measure line beat"); for (unsigned l = 1; l < divisions; ++l) { @@ -82,7 +81,8 @@ TempoLines::draw_ticks (std::vector& grid, grid.begin()->tempo.pulses_per_minute()) + 1) / grid.begin()->c; f = grid.begin()->frame + (framecnt_t) floor ((time_at_pulse * 60.0 * frame_rate) + 0.5); } else { - f = grid.begin()->frame + (l * (fpb / (double)divisions)); + const double fpb = grid.begin()->tempo.frames_per_beat (frame_rate); + f = grid.begin()->frame + (l * (fpb / (double) divisions)); } if (f > leftmost_frame) { diff --git a/libs/ardour/ardour/tempo.h b/libs/ardour/ardour/tempo.h index e5b3ba4390..676980714b 100644 --- a/libs/ardour/ardour/tempo.h +++ b/libs/ardour/ardour/tempo.h @@ -403,6 +403,7 @@ class LIBARDOUR_API TempoMap : public PBD::StatefulDestructible framepos_t round_to_bar (framepos_t frame, RoundMode dir); framepos_t round_to_beat (framepos_t frame, RoundMode dir); framepos_t round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir); + void round_bbt (Timecode::BBT_Time& when, const int32_t& snap_divisor); void set_length (framepos_t frames); diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index 30e56733a8..0d933cbed0 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -2479,6 +2479,54 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir) return ret_frame; } +void +TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num) +{ + if (sub_num == -1) { + const double bpb = meter_at (bbt_to_beats_locked (_metrics, when)).note_divisor(); + if ((double) when.beats > bpb / 2.0) { + ++when.bars; + } + when.beats = 1; + when.ticks = 0; + return; + } else if (sub_num == 0) { + if (when.ticks > BBT_Time::ticks_per_beat / 2) { + ++when.beats; + when.ticks = 0; + } else { + when.ticks = 0; + } + return; + } + const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num; + double rem; + if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) { + /* closer to the next subdivision, so shift forward */ + + when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem); + + if (when.ticks > Timecode::BBT_Time::ticks_per_beat) { + ++when.beats; + when.ticks -= Timecode::BBT_Time::ticks_per_beat; + } + + } else if (rem > 0) { + /* closer to previous subdivision, so shift backward */ + + if (rem > when.ticks) { + if (when.beats == 0) { + /* can't go backwards past zero, so ... */ + } + /* step back to previous beat */ + --when.beats; + when.ticks = Timecode::BBT_Time::ticks_per_beat - rem; + } else { + when.ticks = when.ticks - rem; + } + } +} + framepos_t TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type) {