diff --git a/libs/temporal/tempo.cc b/libs/temporal/tempo.cc index 81bfe7d494..cff92636e0 100644 --- a/libs/temporal/tempo.cc +++ b/libs/temporal/tempo.cc @@ -1615,11 +1615,19 @@ TempoMap::dump (std::ostream& ostr) const ostr << "------------\n\n\n"; } -template TempoMap::Points::const_iterator -TempoMap::_get_tempo_and_meter (TempoPoint const *& tp, MeterPoint const *& mp, T (Point::*method)() const, T arg, bool can_match, bool ret_iterator_after_not_at) const +template typename const_traits_t::iterator_type +TempoMap::_get_tempo_and_meter (typename const_traits_t::tempo_point_type & tp, + typename const_traits_t::meter_point_type & mp, + typename const_traits_t::time_reference (Point::*method)() const, + typename const_traits_t::time_type arg, + typename const_traits_t::iterator_type begini, + typename const_traits_t::iterator_type endi, + typename const_traits_t::tempo_point_type tstart, + typename const_traits_t::meter_point_type mstart, + bool can_match, bool ret_iterator_after_not_at) const { - Points::const_iterator p; - Points::const_iterator last_used = _points.end(); + typename const_traits_t::iterator_type p; + typename const_traits_t::iterator_type last_used = endi; bool tempo_done = false; bool meter_done = false; @@ -1630,23 +1638,25 @@ TempoMap::_get_tempo_and_meter (TempoPoint const *& tp, MeterPoint const *& mp, tp = 0; mp = 0; - /* if the starting position is the beginning of the timeline (indicated - * by the default constructor value for T1, then we are always allowed - * to use the tempo & meter at that position. Without this, it would be - * necessary to special case "can_match" in the caller if the start is - * "zero". Instead we do that here, since common cases - * (e.g. ::get_grid()) will use can_match = false, but may pass in a - * zero start point. + /* If the starting position is the beginning of the timeline (indicated + * by the default constructor value for the time_type (superclock_t, + * Beats, BBT_Time), then we are always allowed to use the tempo & + * meter at that position. + * + * Without this, it would be necessary to special case "can_match" in + * the caller if the start is "zero". Instead we do that here, since + * common cases (e.g. ::get_grid()) will use can_match = false, but may + * pass in a zero start point. */ - can_match = (can_match || arg == T1()); + can_match = (can_match || arg == typename const_traits_t::time_type ()); - for (tp = &_tempos.front(), mp = &_meters.front(), p = _points.begin(); p != _points.end(); ++p) { + for (tp = tstart, mp = mstart, p = begini; p != endi; ++p) { - TempoPoint const * tpp; - MeterPoint const * mpp; + typename const_traits_t::tempo_point_type tpp; + typename const_traits_t::meter_point_type mpp; - if (!tempo_done && (tpp = dynamic_cast (&(*p))) != 0) { + if (!tempo_done && (tpp = dynamic_cast (&(*p))) != 0) { if ((can_match && (((*p).*method)() > arg)) || (!can_match && (((*p).*method)() >= arg))) { tempo_done = true; } else { @@ -1655,7 +1665,7 @@ TempoMap::_get_tempo_and_meter (TempoPoint const *& tp, MeterPoint const *& mp, } } - if (!meter_done && (mpp = dynamic_cast (&(*p))) != 0) { + if (!meter_done && (mpp = dynamic_cast (&(*p))) != 0) { if ((can_match && (((*p).*method)() > arg)) || (!can_match && (((*p).*method)() >= arg))) { meter_done = true; } else { @@ -1670,7 +1680,7 @@ TempoMap::_get_tempo_and_meter (TempoPoint const *& tp, MeterPoint const *& mp, } if (!tp || !mp) { - return _points.end(); + return endi; } if (ret_iterator_after_not_at) { @@ -1678,9 +1688,9 @@ TempoMap::_get_tempo_and_meter (TempoPoint const *& tp, MeterPoint const *& mp, p = last_used; if (can_match) { - while (((*p).*method)() <= arg && p != _points.end()) ++p; + while (((*p).*method)() <= arg && p != endi) ++p; } else { - while (((*p).*method)() < arg && p != _points.end()) ++p; + while (((*p).*method)() < arg && p != endi) ++p; } return p; @@ -1689,82 +1699,6 @@ TempoMap::_get_tempo_and_meter (TempoPoint const *& tp, MeterPoint const *& mp, return last_used; } -template TempoMap::Points::iterator -TempoMap::_get_tempo_and_meter (TempoPoint *& tp, MeterPoint *& mp, T (Point::*method)() const, T arg, bool can_match, bool ret_iterator_after_not_at) -{ - Points::iterator p; - Points::iterator last_used = _points.end(); - bool tempo_done = false; - bool meter_done = false; - - assert (!_tempos.empty()); - assert (!_meters.empty()); - assert (!_points.empty()); - - tp = 0; - mp = 0; - - /* if the starting position is the beginning of the timeline (indicated - * by the default constructor value for T1, then we are always allowed - * to use the tempo & meter at that position. Without this, it would be - * necessary to special case "can_match" in the caller if the start is - * "zero". Instead we do that here, since common cases - * (e.g. ::get_grid()) will use can_match = false, but may pass in a - * zero start point. - */ - - can_match = (can_match || arg == T1()); - - for (tp = &_tempos.front(), mp = &_meters.front(), p = _points.begin(); p != _points.end(); ++p) { - - TempoPoint * tpp; - MeterPoint * mpp; - - if (!tempo_done && (tpp = dynamic_cast (&(*p))) != 0) { - if ((can_match && (((*p).*method)() > arg)) || (!can_match && (((*p).*method)() >= arg))) { - tempo_done = true; - } else { - tp = tpp; - last_used = p; - } - } - - if (!meter_done && (mpp = dynamic_cast (&(*p))) != 0) { - if ((can_match && (((*p).*method)() > arg)) || (!can_match && (((*p).*method)() >= arg))) { - meter_done = true; - } else { - mp = mpp; - last_used = p; - } - } - - if (meter_done && tempo_done) { - break; - } - } - - - if (!tp || !mp) { - return _points.end(); - } - - if (ret_iterator_after_not_at) { - - p = last_used; - - if (can_match) { - while (((*p).*method)() <= arg && p != _points.end()) ++p; - } else { - while (((*p).*method)() < arg && p != _points.end()) ++p; - } - - return p; - } - - return last_used; -} - - void TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, uint32_t bar_mod) { diff --git a/libs/temporal/temporal/tempo.h b/libs/temporal/temporal/tempo.h index 940f17c000..15a6b4676e 100644 --- a/libs/temporal/temporal/tempo.h +++ b/libs/temporal/temporal/tempo.h @@ -862,29 +862,107 @@ class LIBTEMPORAL_API TempoMap : public PBD::StatefulDestructible BBT_Time bbt_at (Beats const &) const; BBT_Time bbt_at (superclock_t sc) const; - /* fetch const tempo/meter pairs */ + template struct const_traits { + typedef Points::const_iterator iterator_type; + typedef TempoPoint const * tempo_point_type; + typedef MeterPoint const * meter_point_type; + using time_reference = T; + using time_type = T1; + }; - template Points::const_iterator _get_tempo_and_meter (TempoPoint const *& tp, MeterPoint const *& mp, T (Point::*method)() const, T arg, bool can_match, bool ret_iterator_after_not_at) const; + template struct non_const_traits { + typedef Points::iterator iterator_type; + typedef TempoPoint * tempo_point_type; + typedef MeterPoint * meter_point_type; + using time_reference = T; + using time_type = T1; + }; - Points::const_iterator get_tempo_and_meter (TempoPoint const *& t, MeterPoint const *& m, BBT_Time const & bbt, bool can_match, bool ret_iterator_after_not_at) const { - return _get_tempo_and_meter (t, m, &Point::bbt, bbt, can_match, ret_iterator_after_not_at); - } - Points::const_iterator get_tempo_and_meter (TempoPoint const *& t, MeterPoint const *& m, superclock_t sc, bool can_match, bool ret_iterator_after_not_at) const { - return _get_tempo_and_meter (t, m, &Point::sclock, sc, can_match, ret_iterator_after_not_at); - } - Points::const_iterator get_tempo_and_meter (TempoPoint const *& t, MeterPoint const *& m, Beats const & b, bool can_match, bool ret_iterator_after_not_at) const { - return _get_tempo_and_meter (t, m, &Point::beats, b, can_match, ret_iterator_after_not_at); - } - - /* fetch non-const tempo/meter pairs. As of March 2020, we only need - * the superclock variant, but leave the templated design in place, - * just in case. + /* A somewhat complex method that sets a TempoPoint* and MeterPoint* to + * refer to the correct tempo and meter points for the given start + * time. + * + * It also returns an iterator which may point at the latter of the two + * points (tempo & meter; always the meter point if they are at the + * same time) OR may point at the iterator *after* the latter of the + * two, depending on whether or not @param ret_iterator_after_not_at is + * true or false. + * + * If @param can_match is true, the points used can be located at the + * given time. If false, they must be before it. Setting it to false is + * useful when you need to know the TempoMetric in effect at a given + * time if there was no tempo or meter point at that time. + * + * The templated structure here is to avoid code duplication in 2 + * separate versions of this method, one that would be const, and one + * that would be non-const. This is a challenging problem in C++, and + * seems best solved by using a "traits" object as shown here. + * + * The begini, endi, tstart and mstart arguments are an additional + * complication. If we try to use e.g. _points.begin() inside the + * method, which is labelled const, we will always get the const + * version of the iterator. This const iterator type will conflict with + * the non-const iterator type defined by the "non_const_traits" + * type. The same happens with _tempos.front() etc. This problem is + * addressed by calling these methods in the caller method, which maybe + * const or non-const, and will provide appropriate versions based on that. */ - template Points::iterator _get_tempo_and_meter (TempoPoint *& tp, MeterPoint *& mp, T (Point::*method)() const, T arg, bool can_match, bool ret_iterator_after_not_at); + template typename constness_traits_t::iterator_type + _get_tempo_and_meter (typename constness_traits_t::tempo_point_type &, + typename constness_traits_t::meter_point_type &, + typename constness_traits_t::time_reference (Point::*)() const, + typename constness_traits_t::time_type, + typename constness_traits_t::iterator_type begini, + typename constness_traits_t::iterator_type endi, + typename constness_traits_t::tempo_point_type tstart, + typename constness_traits_t::meter_point_type mstart, + bool can_match, + bool ret_iterator_after_not_at) const; + + /* fetch non-const tempo/meter pairs and iterator (used in + * ::reset_starting_at() in which we will modify points. + */ Points::iterator get_tempo_and_meter (TempoPoint *& t, MeterPoint *& m, superclock_t sc, bool can_match, bool ret_iterator_after_not_at) { - return _get_tempo_and_meter (t, m, &Point::sclock, sc, can_match, ret_iterator_after_not_at); + + /* because @param this is non-const (because the method is not + * marked const), the following: + + _points.begin() + _points.end() + _tempos.front() + _meters.front() + + will all be the non-const versions of these methods. + */ + + return _get_tempo_and_meter > (t, m, &Point::sclock, sc, _points.begin(), _points.end(), &_tempos.front(), &_meters.front(), can_match, ret_iterator_after_not_at); + } + + /* fetch const tempo/meter pairs and iterator (used in metric_at() and + * other similar call sites where we do not modify the map + */ + + Points::const_iterator get_tempo_and_meter (TempoPoint const *& t, MeterPoint const *& m, BBT_Time const & bbt, bool can_match, bool ret_iterator_after_not_at) const { + + /* because @param this is const (because the method is marked + * const), the following: + + _points.begin() + _points.end() + _tempos.front() + _meters.front() + + will all be the const versions of these methods. + */ + return _get_tempo_and_meter > (t, m, &Point::bbt, bbt, _points.begin(), _points.end(), &_tempos.front(), &_meters.front(), can_match, ret_iterator_after_not_at); + } + Points::const_iterator get_tempo_and_meter (TempoPoint const *& t, MeterPoint const *& m, superclock_t sc, bool can_match, bool ret_iterator_after_not_at) const { + return _get_tempo_and_meter > (t, m, &Point::sclock, sc, _points.begin(), _points.end(), &_tempos.front(), &_meters.front(), can_match, ret_iterator_after_not_at); + } + Points::const_iterator get_tempo_and_meter (TempoPoint const *& t, MeterPoint const *& m, Beats const & b, bool can_match, bool ret_iterator_after_not_at) const { + return _get_tempo_and_meter > (t, m, &Point::beats, b, _points.begin(), _points.end(), &_tempos.front(), &_meters.front(), can_match, ret_iterator_after_not_at); } /* parsing legacy tempo maps */