temporal: customize the implementation of get_tempo_and_meter for BBT_Time

We must not walk past a MusicTimePoint if the reftime is a BBT_Argument.

May try to fold this back into the fully-templated version in a subsequent commit.
This commit is contained in:
Paul Davis
2026-01-01 19:17:05 -07:00
parent e53ba509df
commit 52c7d6ab77
2 changed files with 110 additions and 30 deletions

View File

@@ -2759,6 +2759,112 @@ TempoMap::_get_tempo_and_meter (typename const_traits_t::tempo_point_type & tp,
return last_used;
}
Points::const_iterator
TempoMap::get_tempo_and_meter_bbt (TempoPoint const *& t, MeterPoint const *& m, BBT_Argument const & bbt, bool can_match, bool ret_iterator_after_not_at) const
{
TEMPO_MAP_ASSERT (!_tempos.empty());
TEMPO_MAP_ASSERT (!_meters.empty());
TEMPO_MAP_ASSERT (!_points.empty());
Points::const_iterator p;
Points::const_iterator last_used = _points.end();
bool tempo_done = false;
bool meter_done = false;
/* Walk the bartimes list and find the place to start looking for the
* relevant tempo & meter
*/
if (_bartimes.empty() || bbt.reference() == 0) {
p = _points.begin();
} else {
MusicTimes::const_iterator mtp;
for (mtp = _bartimes.begin(); mtp != _bartimes.end() && mtp->sclock() < bbt.reference(); ++mtp);
if (mtp != _bartimes.end()) {
p = _points.s_iterator_to (*(static_cast<Point const *> (&(*mtp))));
} else {
p = _points.s_iterator_to (*(static_cast<Point const *> (&_bartimes.back())));
}
}
/* 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 || bbt == BBT_Time());
/* Set return tempo and meter points by value using the starting tempo
* and meter passed in.
*
* Then advance through all points, resetting either tempo and/or meter
* until we find a point beyond (or equal to, if @p can_match is
* true) the @p arg (end time)
*/
for (; p != _points.end(); ++p) {
TempoPoint const * tpp;
MeterPoint const * mpp;
if (dynamic_cast<MusicTimePoint const *> (&(*p)) != 0) {
if (p->sclock() != bbt.reference()) {
break;
}
}
if (!tempo_done && (tpp = dynamic_cast<TempoPoint const *> (&(*p))) != 0) {
if ((can_match && ((*p).bbt() > bbt)) || (!can_match && ((*p).bbt() >= bbt))) {
tempo_done = true;
} else {
t = tpp;
last_used = p;
}
}
if (!meter_done && (mpp = dynamic_cast<MeterPoint const *> (&(*p))) != 0) {
if ((can_match && ((*p).bbt() > bbt)) || (!can_match && ((*p).bbt() >= bbt))) {
meter_done = true;
} else {
m = mpp;
last_used = p;
}
}
if (meter_done && tempo_done) {
break;
}
}
if (!t || !m) {
return _points.end();
}
if (ret_iterator_after_not_at) {
p = last_used;
if (can_match) {
while ((p != _points.end()) && ((*p).bbt() <= bbt)) ++p;
} else {
while ((p != _points.end()) && ((*p).bbt() < bbt)) ++p;
}
return p;
}
return last_used;
}
Points::const_iterator
TempoMap::get_grid (TempoMapPoints& ret, superclock_t rstart, superclock_t end, uint32_t bar_mod, uint32_t beat_div) const
{

View File

@@ -1163,38 +1163,12 @@ class /*LIBTEMPORAL_API*/ TempoMap : public PBD::StatefulDestructible
if (_tempos.size() == 1 && _meters.size() == 1) { t = &_tempos.front(); m = &_meters.front(); return _points.end(); }
return _get_tempo_and_meter<const_traits<Beats const &, Beats> > (t, m, &Point::beats, b, _points.begin(), _points.end(), &_tempos.front(), &_meters.front(), can_match, ret_iterator_after_not_at);
}
Points::const_iterator get_tempo_and_meter_bbt (TempoPoint const *& t, MeterPoint const *& m, BBT_Argument const & bbt, bool can_match, bool ret_iterator_after_not_at) const;
Points::const_iterator get_tempo_and_meter (TempoPoint const *& t, MeterPoint const *& m, BBT_Argument const & bbt, bool can_match, bool ret_iterator_after_not_at) const {
if (_tempos.size() == 1 && _meters.size() == 1) { t = &_tempos.front(); m = &_meters.front(); return _points.end(); }
/* Skip through the tempo map to find the tempo and meter in
* effect at the bbt's "reference" time, and use them as the
* starting point for the normal operation of
* _get_tempo_and_meter ()
*/
Tempos::const_iterator tp = _tempos.begin();
Meters::const_iterator mp = _meters.begin();
superclock_t ref = bbt.reference();
if (ref != 0) {
while (tp != _tempos.end()) {
Tempos::const_iterator nxt = tp; ++nxt;
if (nxt == _tempos.end() || nxt->sclock() > ref) {
break;
}
tp = nxt;
}
while (mp != _meters.end()) {
Meters::const_iterator nxt = mp; ++nxt;
if (nxt == _meters.end() || mp->sclock() > ref) {
break;
}
mp = nxt;
}
}
return _get_tempo_and_meter<const_traits<BBT_Time const &, BBT_Time> > (t, m, &Point::bbt, bbt, _points.begin(), _points.end(), &(*tp), &(*mp), can_match, ret_iterator_after_not_at);
return get_tempo_and_meter_bbt (t, m, bbt, can_match, ret_iterator_after_not_at);
}
/* This is private, and should not be callable from outside the map