diff --git a/libs/ardour/ardour/tempo.h b/libs/ardour/ardour/tempo.h index c134f450fb..77eff92fa8 100644 --- a/libs/ardour/ardour/tempo.h +++ b/libs/ardour/ardour/tempo.h @@ -363,6 +363,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); + framepos_t round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir); void set_length (framepos_t frames); @@ -446,12 +447,16 @@ class LIBARDOUR_API TempoMap : public PBD::StatefulDestructible framepos_t framepos_plus_bbt (framepos_t pos, Timecode::BBT_Time b) const; framepos_t framepos_plus_beats (framepos_t, Evoral::Beats) const; + framepos_t framepos_plus_qn (framepos_t, Evoral::Beats) const; framepos_t framepos_minus_beats (framepos_t, Evoral::Beats) const; Evoral::Beats framewalk_to_beats (framepos_t pos, framecnt_t distance) const; + Evoral::Beats framewalk_to_qn (framepos_t pos, framecnt_t distance) const; double quarter_note_at_frame (const framepos_t frame); double quarter_note_at_frame_rt (const framepos_t frame); framepos_t frame_at_quarter_note (const double quarter_note); + double quarter_note_at_beat (const double beat); + double beat_at_quarter_note (const double beat); void gui_move_tempo (TempoSection*, const framepos_t& frame, const int& sub_num); void gui_move_meter (MeterSection*, const framepos_t& frame); @@ -459,6 +464,7 @@ class LIBARDOUR_API TempoMap : public PBD::StatefulDestructible void gui_dilate_tempo (TempoSection* tempo, const framepos_t& frame, const framepos_t& end_frame, const double& pulse); double exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num); + double exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num); std::pair predict_tempo_position (TempoSection* section, const Timecode::BBT_Time& bbt); bool can_solve_bbt (TempoSection* section, const Timecode::BBT_Time& bbt); @@ -489,6 +495,10 @@ private: double pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const; Timecode::BBT_Time bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const; + framepos_t frame_at_quarter_note_locked (const Metrics& metrics, const double quarter_note); + double quarter_note_at_frame_locked (const Metrics& metrics, const framepos_t frame); + double quarter_note_at_beat_locked (const Metrics& metrics, const double beat); + const TempoSection& tempo_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const; const TempoSection& tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const; @@ -504,6 +514,7 @@ private: bool solve_map_bbt (Metrics& metrics, MeterSection* section, const Timecode::BBT_Time& bbt); double exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num); + double exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num); friend class ::BBTTest; friend class ::FrameposPlusBeatsTest; diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index 65c4765dd4..de220b6ed7 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -2106,7 +2106,15 @@ TempoMap::quarter_note_at_frame (const framepos_t frame) { Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK); - const double ret = pulse_at_frame_locked (_metrics, frame) * 4.0; + const double ret = quarter_note_at_frame_locked (_metrics, frame); + + return ret; +} + +double +TempoMap::quarter_note_at_frame_locked (const Metrics& metrics, const framepos_t frame) +{ + const double ret = pulse_at_frame_locked (metrics, frame) * 4.0; return ret; } @@ -2130,7 +2138,43 @@ TempoMap::frame_at_quarter_note (const double quarter_note) { Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK); - const framepos_t ret = frame_at_pulse_locked (_metrics, quarter_note / 4.0); + const framepos_t ret = frame_at_quarter_note_locked (_metrics, quarter_note); + + return ret; +} + +framepos_t +TempoMap::frame_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) +{ + const framepos_t ret = frame_at_pulse_locked (metrics, quarter_note / 4.0); + + return ret; +} + +double +TempoMap::quarter_note_at_beat (const double beat) +{ + Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK); + + const double ret = quarter_note_at_beat_locked (_metrics, beat); + + return ret; +} + +double +TempoMap::quarter_note_at_beat_locked (const Metrics& metrics, const double beat) +{ + const double ret = pulse_at_beat_locked (metrics, beat) * 4.0; + + return ret; +} + +double +TempoMap::beat_at_quarter_note (const double quarter_note) +{ + Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK); + + const double ret = beat_at_pulse_locked (_metrics, quarter_note / 4.0); return ret; } @@ -3102,6 +3146,43 @@ TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& return beat; } +double +TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) +{ + Glib::Threads::RWLock::ReaderLock lm (lock); + + return exact_qn_at_frame_locked (_metrics, frame, sub_num); +} + +double +TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) +{ + double qn = quarter_note_at_frame_locked (metrics, frame); + + if (sub_num > 1) { + qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num); + } else if (sub_num == 1) { + /* snap to quarter note */ + qn = floor (qn + 0.5); + } else if (sub_num == -1) { + /* snap to bar */ + Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0); + bbt.beats = 1; + bbt.ticks = 0; + + const double prev_b = pulse_at_bbt_locked (_metrics, bbt) * 4.0; + ++bbt.bars; + const double next_b = pulse_at_bbt_locked (_metrics, bbt) * 4.0; + + if ((qn - prev_b) > (next_b - prev_b) / 2.0) { + qn = next_b; + } else { + qn = prev_b; + } + } + + return qn; +} framecnt_t TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir) @@ -3223,6 +3304,102 @@ TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir) return ret_frame; } +framepos_t +TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir) +{ + Glib::Threads::RWLock::ReaderLock lm (lock); + uint32_t ticks = (uint32_t) floor (max (0.0, quarter_note_at_frame_locked (_metrics, fr)) * BBT_Time::ticks_per_beat); + uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat); + uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num; + + ticks -= beats * BBT_Time::ticks_per_beat; + + if (dir > 0) { + /* round to next (or same iff dir == RoundUpMaybe) */ + + uint32_t mod = ticks % ticks_one_subdivisions_worth; + + if (mod == 0 && dir == RoundUpMaybe) { + /* right on the subdivision, which is fine, so do nothing */ + + } else if (mod == 0) { + /* right on the subdivision, so the difference is just the subdivision ticks */ + ticks += ticks_one_subdivisions_worth; + + } else { + /* not on subdivision, compute distance to next subdivision */ + + ticks += ticks_one_subdivisions_worth - mod; + } + + if (ticks >= BBT_Time::ticks_per_beat) { + ticks -= BBT_Time::ticks_per_beat; + } + } else if (dir < 0) { + + /* round to previous (or same iff dir == RoundDownMaybe) */ + + uint32_t difference = ticks % ticks_one_subdivisions_worth; + + if (difference == 0 && dir == RoundDownAlways) { + /* right on the subdivision, but force-rounding down, + so the difference is just the subdivision ticks */ + difference = ticks_one_subdivisions_worth; + } + + if (ticks < difference) { + ticks = BBT_Time::ticks_per_beat - ticks; + } else { + ticks -= difference; + } + + } else { + /* round to nearest */ + double rem; + + /* compute the distance to the previous and next subdivision */ + + if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) { + + /* closer to the next subdivision, so shift forward */ + + ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem)); + + DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks)); + + if (ticks > BBT_Time::ticks_per_beat) { + ++beats; + ticks -= BBT_Time::ticks_per_beat; + DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats)); + } + + } else if (rem > 0) { + + /* closer to previous subdivision, so shift backward */ + + if (rem > ticks) { + if (beats == 0) { + /* can't go backwards past zero, so ... */ + return 0; + } + /* step back to previous beat */ + --beats; + ticks = lrint (BBT_Time::ticks_per_beat - rem); + DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats)); + } else { + ticks = lrint (ticks - rem); + DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks)); + } + } else { + /* on the subdivision, do nothing */ + } + } + + const framepos_t ret_frame = frame_at_quarter_note_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)); + + return ret_frame; +} + framepos_t TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type) { @@ -3904,6 +4081,13 @@ TempoMap::framepos_plus_beats (framepos_t frame, Evoral::Beats beats) const return frame_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, frame) + beats.to_double()); } +framepos_t +TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const +{ + Glib::Threads::RWLock::ReaderLock lm (lock); + + return frame_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, frame) + beats.to_double()); +} /** Subtract some (fractional) beats from a frame position, and return the result in frames */ framepos_t @@ -3950,6 +4134,13 @@ TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const return Evoral::Beats (beat_at_frame_locked (_metrics, pos + distance) - beat_at_frame_locked (_metrics, pos)); } +Evoral::Beats +TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const +{ + Glib::Threads::RWLock::ReaderLock lm (lock); + + return Evoral::Beats (beat_at_frame_locked (_metrics, pos + distance) - beat_at_frame_locked (_metrics, pos)); +} struct bbtcmp { bool operator() (const BBT_Time& a, const BBT_Time& b) { return a < b;