13
0

libtemporal: redesign TempoMap::get_grid() to iterate over _points rather than separated _tempos and _meters

This commit is contained in:
Paul Davis 2021-02-08 23:02:26 -07:00
parent 8124bec5a1
commit c2401c3097

View File

@ -1661,55 +1661,16 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, u
{ {
assert (!_tempos.empty()); assert (!_tempos.empty());
assert (!_meters.empty()); assert (!_meters.empty());
assert (!_points.empty());
DEBUG_TRACE (DEBUG::Grid, string_compose (">>> GRID START %1 .. %2 (barmod = %3)\n", start, end, bar_mod)); DEBUG_TRACE (DEBUG::Grid, string_compose (">>> GRID START %1 .. %2 (barmod = %3)\n", start, end, bar_mod));
Tempos::iterator t (_tempos.begin()); TempoPoint* tp = 0;
Meters::iterator m (_meters.begin()); MeterPoint* mp = 0;
MusicTimes::iterator b (_bartimes.begin()); Points::iterator p;
TempoMetric metric = metric_at (start, false); /* initial values required, but will be reset before we begin */
BBT_Time bbt = metric.bbt_at (start); TempoMetric metric (_tempos.front(), _meters.front());
DEBUG_TRACE (DEBUG::Grid, string_compose ("start %1 is %2\n", start, bbt));
#ifndef NDEBUG
/* Sanity Check */
if (DEBUG_ENABLED(PBD::DEBUG::Grid)) {
TempoMetric emetric = metric_at (end, false);
BBT_Time ebbt = metric_at (end).bbt_at (end);
DEBUG_TRACE (DEBUG::Grid, string_compose ("get grid between %1..%2 [ %4 .. %5 ] { %6 .. %7 } at bar_mod = %3\n",
start, end, bar_mod, start, end, bbt, ebbt));
if (metric.quarters_at (bbt).diff (metric.quarters_at_superclock (start)) > Beats::ticks (1)) {
cerr << "MM1: " << start << " / " << metric.quarters_at_superclock (start) << " vs. "
<< metric.superclock_at (bbt) << " / " << metric.quarters_at (bbt)
<< " delta "
<< start - metric.superclock_at (bbt)
<< " dB "
<< metric.quarters_at (bbt).diff (metric.quarters_at_superclock (start))
<< "\n\tused " << metric
<< endl;
abort ();
}
if (emetric.quarters_at (ebbt).diff (emetric.quarters_at_superclock (end)) > Beats::ticks (1)) {
cerr << "MM2: " << end << " / " << emetric.quarters_at_superclock (end) << " vs. "
<< emetric.superclock_at (ebbt) << " / " << emetric.quarters_at (ebbt)
<< " delta "
<< end - emetric.superclock_at (ebbt)
<< " dB "
<< emetric.quarters_at (ebbt).diff (emetric.quarters_at_superclock (end))
<< "\n\tused " << emetric
<< endl;
abort ();
}
// dump (cerr);
}
#endif
/* first task: get to the right starting point for the requested /* first task: get to the right starting point for the requested
* grid. if bar_mod is zero, then we'll start on the next beat after * grid. if bar_mod is zero, then we'll start on the next beat after
@ -1718,6 +1679,38 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, u
* grid, depending on whether or not it is a multiple of bar_mod. * grid, depending on whether or not it is a multiple of bar_mod.
*/ */
for (tp = &_tempos.front(), mp = &_meters.front(), p = _points.begin(); p != _points.end() && p->sclock() < start; ++p) {
TempoPoint* tpp;
MeterPoint* mpp;
if ((tpp = dynamic_cast<TempoPoint*> (&(*p))) != 0) {
tp = tpp;
}
if ((mpp = dynamic_cast<MeterPoint*> (&(*p))) != 0) {
mp = mpp;
}
}
/* reset metric */
metric = TempoMetric (*tp, *mp);
DEBUG_TRACE (DEBUG::Grid, string_compose ("metric in effect at %1 = %2\n", start, metric));
/* p now points to either the point *after* start, or the end of the
* _points list.
*
* metric is the TempoMetric that is in effect at start
*/
/* determine the BBT at start */
BBT_Time bbt = metric.bbt_at (start);
DEBUG_TRACE (DEBUG::Grid, string_compose ("start %1 is %2\n", start, bbt));
if (bar_mod == 0) { if (bar_mod == 0) {
/* round to next beat, then find the tempo/meter/bartime points /* round to next beat, then find the tempo/meter/bartime points
@ -1734,15 +1727,24 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, u
DEBUG_TRACE (DEBUG::Grid, string_compose ("new bbt for start (rounded up) = %1\n", bbt)); DEBUG_TRACE (DEBUG::Grid, string_compose ("new bbt for start (rounded up) = %1\n", bbt));
for (Tempos::iterator tt = _tempos.begin(); tt != _tempos.end() && tt->sclock() < start; ++tt) { t = tt; } for (tp = &_tempos.front(), mp = &_meters.front(), p = _points.begin(); p != _points.end() && p->sclock() < start; ++p) {
for (Meters::iterator mm = _meters.begin(); mm != _meters.end() && mm->sclock() < start; ++mm) { m = mm; }
for (MusicTimes::iterator bb = _bartimes.begin(); bb != _bartimes.end() && bb->sclock() < start; ++bb) { b = bb; } TempoPoint* tpp;
MeterPoint* mpp;
if ((tpp = dynamic_cast<TempoPoint*> (&(*p))) != 0) {
tp = tpp;
}
if ((mpp = dynamic_cast<MeterPoint*> (&(*p))) != 0) {
mp = mpp;
}
}
/* reset metric */ /* reset metric */
metric = TempoMetric (*t, *m); metric = TempoMetric (*tp, *mp);
DEBUG_TRACE (DEBUG::Grid, string_compose ("metric in effect at %1 = %2\n", start, metric));
/* recompute superclock position */ /* recompute superclock position */
@ -1762,32 +1764,46 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, u
} else { } else {
/* this rounding cannot change the meter in effect, because it
remains within the bar. But it could change the tempo (which
are only quantized to grid positions within a bar).
*/
BBT_Time bar = bbt.round_down_to_bar (); BBT_Time bar = bbt.round_down_to_bar ();
/* adjust to match bar_mod (i.e. we only want every 4th bar)
*/
if (bar_mod != 1) { if (bar_mod != 1) {
bar.bars -= bar.bars % bar_mod; bar.bars -= bar.bars % bar_mod;
++bar.bars; ++bar.bars;
} }
/* the rounding we've just done cannot change the meter in
effect, because it remains within the bar. But it could
change the tempo (which are only quantized to grid positions
within a bar). So if it has generated a new BBT time,
recompute the metric.
*/
if (bar != bbt) { if (bar != bbt) {
bbt = bar; bbt = bar;
for (Tempos::iterator tt = _tempos.begin(); tt != _tempos.end() && tt->bbt() < bbt; ++tt) { t = tt; } for (tp = &_tempos.front(), mp = &_meters.front(), p = _points.begin(); p != _points.end() && p->sclock() < start; ++p) {
for (Meters::iterator mm = _meters.begin(); mm != _meters.end() && mm->bbt() < bbt; ++mm) { m = mm; }
for (MusicTimes::iterator bb = _bartimes.begin(); bb != _bartimes.end() && bb->bbt() < bbt; ++bb) { b = bb; }
/* t, m and b are now all iterators for the tempo, meter and TempoPoint* tpp;
* position markers BEFORE pos. b may be _bartimes.end(), but MeterPoint* mpp;
* the other two are guaranteed to be valid references into
* the tempos and meters if ((tpp = dynamic_cast<TempoPoint*> (&(*p))) != 0) {
*/ tp = tpp;
}
if ((mpp = dynamic_cast<MeterPoint*> (&(*p))) != 0) {
mp = mpp;
}
}
/* reset metric */
metric = TempoMetric (*tp, *mp);
metric = TempoMetric (*t, *m);
start = metric.superclock_at (bbt); start = metric.superclock_at (bbt);
} else { } else {
@ -1795,128 +1811,38 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, u
} }
} }
/* advance t, m and b so that the point to the *next*
* tempo/meter/position marker (if any)
*/
Tempos::iterator nxt_t = t; ++nxt_t;
Meters::iterator nxt_m = m; ++nxt_m;
MusicTimes::iterator nxt_b = b; ++nxt_b;
/* at this point: /* at this point:
* *
* - metric is a TempoMetric that describes the situation at pos * - metric is a TempoMetric that describes the situation at start
* - t, m and b reference tempo, meter and position markers at or prior to pos (if any) * - p is an iterator pointin to either the end of the _points list, or
* - nxt_t, nxt_m, nxt_b reference the tempo, meter and position markers after pos (if any) * the next point in the list after start.
*
* t and m must be valid; b, nxt_t, nxt_m, nxt_b may all refer to ::end() of their respective containers.
*/ */
/* outer loop: compute next marker position, if any, and then set limit to the earlier of that position or @param e. DEBUG_TRACE (DEBUG::Grid, string_compose ("start filling points with start = %1 end = %2 with limit @ %3\n", start, end, *p));
* Then run the inner loop to actually add grid points up until limit. Repeat till done.
*/
DEBUG_TRACE (DEBUG::Grid, string_compose ("start filling points with start = %1 end = %2\n", start, end));
while (start < end) { while (start < end) {
bool advance_tempo = false; Temporal::Beats beats = metric.quarters_at_superclock (start);
bool advance_meter = false;
bool advance_bartime = false;
Point* first_of_three = 0;
superclock_t limit = INT64_MAX;
DEBUG_TRACE (DEBUG::Grid, string_compose ("start %5 end %6 bbt %7 find first/limit with limit = %1 next_t %2 next_m %3 next_b %4\n", DEBUG_TRACE (DEBUG::Grid, string_compose ("start %1 end %2 bbt %3 find first/limit with limit @ = %4\n", start, end, bbt, *p));
limit,
(nxt_t != _tempos.end() ? nxt_t->sclock() : -1),
(nxt_m != _meters.end() ? nxt_m->sclock() : -1),
(nxt_b != _bartimes.end() ? nxt_b->sclock() : -1),
start, end, bbt));
if (nxt_t != _tempos.end() && limit >= nxt_t->sclock()) { while (start < p->sclock() && start < end) {
first_of_three = &*nxt_t;
limit = first_of_three->sclock();
}
if (nxt_m != _meters.end() && limit >= nxt_m->sclock()) { /* add point to grid, perhaps */
first_of_three = &*nxt_m;
limit = first_of_three->sclock();
}
if (nxt_b != _bartimes.end() && limit >= nxt_b->sclock()) { if ((bar_mod != 0) && bbt.is_bar() && (bbt.bars % bar_mod != 0)) {
first_of_three = &*nxt_b; ret.push_back (TempoMapPoint (*this, metric, start, beats, bbt));
limit = first_of_three->sclock(); } else {
}
DEBUG_TRACE (DEBUG::Grid, string_compose ("after checking fo3 = %1\n", (first_of_three ? first_of_three->sclock() : -1)));
if (first_of_three) {
if (nxt_m != _meters.end() && nxt_m->sclock() == first_of_three->sclock()) {
DEBUG_TRACE (DEBUG::Grid, "will advance to next meter\n");
advance_meter = true;
}
if (nxt_t != _tempos.end() && nxt_t->sclock() == first_of_three->sclock()) {
DEBUG_TRACE (DEBUG::Grid, "will advance to next tempo\n");
advance_tempo = true;
}
if ((nxt_b != _bartimes.end()) && (nxt_b->sclock() == first_of_three->sclock())) {
DEBUG_TRACE (DEBUG::Grid, "will advance to next bartime\n");
advance_bartime = true;
}
limit = std::min (end, first_of_three->sclock());
} else {
limit = end;
}
if (start >= end) {
break;
}
if (start >= limit && nxt_t == _tempos.end() && nxt_m == _meters.end() && nxt_b == _bartimes.end()) {
/* reached the end, no more tempos, meters or bartimes
to consider, so just finish up.
*/
DEBUG_TRACE (DEBUG::Grid, string_compose ("reached end, no more t/m/b, finish between %1 .. %2\n", start, end));
const superclock_t step = metric.superclocks_per_grid_at (start);
while (start < end) {
const Temporal::Beats beats = metric.quarters_at_superclock (start);
ret.push_back (TempoMapPoint (*this, metric, start, beats, bbt)); ret.push_back (TempoMapPoint (*this, metric, start, beats, bbt));
DEBUG_TRACE (DEBUG::Grid, string_compose ("Gend %1\t %2\n", metric, ret.back()));
start += step;
bbt = metric.bbt_at (start);
} }
/* all done, leave outer loop */
break;
}
/* Inner loop: add grid points until we hit limit, which is defined by either @param e or the next marker of some kind */
do {
/* we already have the superclock and BBT time for the next point, either computed before the loop, or at the bottom of this one.
* So now complete the triplet of (superclock,quarters,bbt)
*/
const Temporal::Beats beats = metric.quarters_at_superclock (start);
/* add point to grid */
ret.push_back (TempoMapPoint (*this, metric, start, beats, bbt));
DEBUG_TRACE (DEBUG::Grid, string_compose ("G %1\t %2\n", metric, ret.back()));
/* Advance by the meter note value size */
superclock_t step; superclock_t step;
if (bar_mod == 0) { if (bar_mod == 0) {
/* Advance by the meter note value size */
step = metric.superclocks_per_grid_at (start); step = metric.superclocks_per_grid_at (start);
start += step; start += step;
bbt = metric.bbt_at (start); bbt = metric.bbt_at (start);
@ -1924,105 +1850,129 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, u
} else { } else {
/* Advance by the number of bars specified by bar_mod */
bbt.bars += bar_mod; bbt.bars += bar_mod;
start = metric.superclock_at (bbt); start = metric.superclock_at (bbt);
DEBUG_TRACE (DEBUG::Grid, string_compose ("bar mod %1 moved to %2\n", bar_mod, bbt)) DEBUG_TRACE (DEBUG::Grid, string_compose ("bar mod %1 moved to %2\n", bar_mod, bbt))
} }
}
/* we might be finished ...*/
if (start >= end) {
break;
}
/* could have just passed the current metric */ while ((p != _points.end()) && (start >= p->sclock())) {
if (first_of_three && (start > first_of_three->sclock ())) { DEBUG_TRACE (DEBUG::Grid, string_compose ("pausing to deal with point => %1\n", *p));
start = first_of_three->sclock();
break; /* have just passed or arrived at the next
* point. Consider adding a grid point for this point.
*/
start = p->sclock();
bbt = p->bbt();
beats = p->beats();
if ((bar_mod != 0) && bbt.is_bar() && (bbt.bars % bar_mod != 0)) {
ret.push_back (TempoMapPoint (*this, metric, start, beats, bbt));
} else {
ret.push_back (TempoMapPoint (*this, metric, start, beats, bbt));
} }
if (start >= limit) { /* But there may be multiple points here, and we have
/* go back to outer loop to advance iterators and get a new metric */ * to check them all (Tempo/Meter/MusicTime ... which
break; * is itself both a Tempo *and* Meter point) before
* proceeding.
*/
const superclock_t pos = p->sclock();
Points::iterator nxt = p;
++nxt;
TempoPoint* tpp;
MeterPoint* mpp;
/* use this point */
if ((tpp = dynamic_cast<TempoPoint*> (&(*p))) != 0) {
tp = tpp;
} }
} while (true); if ((mpp = dynamic_cast<MeterPoint*> (&(*p))) != 0) {
mp = mpp;
/* back in outer loop. Check to see if we passed a marker */
DEBUG_TRACE (DEBUG::Grid, string_compose ("pass-marker-check fo3 %1 start %2 fo2->sc %3\n", first_of_three, start, (first_of_three ? first_of_three->sclock() : -1)));
if (first_of_three && (start >= first_of_three->sclock())) {
if (advance_tempo) {
if (nxt_t != _tempos.end()) {
t = nxt_t;
++nxt_t;
}
}
if (advance_meter) {
if (nxt_m != _meters.end()) {
m = nxt_m;
++nxt_m;
}
}
if (advance_bartime) {
b = nxt_b;
if (nxt_b != _bartimes.end()) {
++nxt_b;
}
} }
if (advance_tempo || advance_meter || advance_bartime) { /* use any subsequent ones at the same location */
/* we overstepped a marker while ((nxt != _points.end()) && (nxt->sclock() == pos)) {
* if bar_mod is zero, then by definition any /* Set up the new metric given the new point */
* such marker qualifies as a grid point.
*
* if bar_mod != zero, then check to see if the new
* BBT position matches the interval we've been asked
* for. If so, use it, otherwise just continue around
* the loop, using the new position and metric.
*/
bbt = first_of_three->bbt (); if ((tpp = dynamic_cast<TempoPoint*> (&(*nxt))) != 0) {
DEBUG_TRACE (DEBUG::Grid, string_compose ("reset bbt to %1\n", bbt)); tp = tpp;
if (bar_mod != 0) {
/* check to see if it matches the interval */
if (!bbt.is_bar() || (bbt.bars % bar_mod != 0)) {
/* not usable */
bbt = bbt.round_up_to_bar ();
/* reset iterators for new position */
for (Tempos::iterator tt = t; tt != _tempos.end() && tt->bbt() < bbt; ++tt) { t = tt; }
for (Meters::iterator mm = m; mm != _meters.end() && mm->bbt() < bbt; ++mm) { m = mm; }
for (MusicTimes::iterator bb = b; bb != _bartimes.end() && bb->bbt() < bbt; ++bb) { b = bb; }
nxt_t = t; ++nxt_t;
nxt_m = m; ++nxt_m;
nxt_b = b; ++nxt_b;
}
} }
/* Now build a new metric that uses the correct if ((mpp = dynamic_cast<MeterPoint*> (&(*nxt))) != 0) {
* tempo/meter (computed above), but the mp = mpp;
* position of the marker we've just arrived }
* at. The idea is that time after this marker
* is defined by the tempo/meter in effect
* there, combined with its own time (in all 3
* time domains, superclock, quarters, bbt).
*/
TempoPoint tp (*t, *first_of_three); ++nxt;
MeterPoint mp (*m, *first_of_three);
metric = TempoMetric (tp, mp);
start = first_of_three->sclock();
/* ready to loop because metric, start and bbt are all set correctly, as they were when entering the outer loop */
} }
/* Build a new metric from the composite of all the
* points at this position.
*/
metric = TempoMetric (*tp, *mp);
DEBUG_TRACE (DEBUG::Grid, string_compose ("G %1\t %2\n", metric, ret.back()));
p = nxt;
}
if (p == _points.end()) {
break;
}
}
/* reached the end or no more points to consider, so just
* finish by filling the grid to the end, if necessary.
*/
if (start < end) {
DEBUG_TRACE (DEBUG::Grid, string_compose ("reached end, no more map points, finish between %1 .. %2\n", start, end));
/* note: if start < end, then p == _points.end(). This means there are
* no more Points beyond the current value of start.
*
* Since there are no more Points beyond start, the current metric
* cannot involve a ramp, so the step size per grid element is
* constant. metric will also remain constant until we finish.
*/
const superclock_t step = metric.superclocks_per_grid_at (start);
do {
const Temporal::Beats beats = metric.quarters_at_superclock (start);
ret.push_back (TempoMapPoint (*this, metric, start, beats, bbt));
DEBUG_TRACE (DEBUG::Grid, string_compose ("Gend %1\t %2\n", metric, ret.back()));
start += step;
bbt = metric.bbt_at (start);
} while (start < end);
/* all done */
} else {
if (p == _points.end()) {
DEBUG_TRACE (DEBUG::Grid, string_compose ("ended loop with start %1 end %2, p @ END\n", start, end));
} else {
DEBUG_TRACE (DEBUG::Grid, string_compose ("ended loop with start %1 end %2, p=> %3\n", start, end, *p));
} }
} }