temporal: reimplement and tweak API of TempoMap::get_grid()
There were many logical errors in the previous implementation. This one is simpler to read, and appears to work much better. It also allows the caller to specify the quarter-note subdivision to use when generating the grid, rather than choosing only between some bar modulo or quarter notes.
This commit is contained in:
parent
a124062fa2
commit
d77db816de
@ -1903,7 +1903,7 @@ TempoMap::_get_tempo_and_meter (typename const_traits_t::tempo_point_type & tp,
|
||||
}
|
||||
|
||||
void
|
||||
TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, uint32_t bar_mod) const
|
||||
TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, uint32_t bar_mod, uint32_t beat_div) const
|
||||
{
|
||||
/* note: @p bar_mod is "bar modulo", and describes the N in "give
|
||||
me every Nth bar". If the caller wants every 4th bar, bar_mod ==
|
||||
@ -1924,7 +1924,27 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, u
|
||||
|
||||
TempoPoint const * tp = 0;
|
||||
MeterPoint const * mp = 0;
|
||||
Points::const_iterator p;
|
||||
Points::const_iterator p = _points.begin();
|
||||
BBT_Time bbt;
|
||||
Beats beats;
|
||||
|
||||
/* Find relevant meter for nominal start point */
|
||||
|
||||
p = get_tempo_and_meter (tp, mp, start, true, true);
|
||||
|
||||
/* 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
|
||||
*/
|
||||
|
||||
TempoMetric metric = TempoMetric (*tp, *mp);
|
||||
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("metric in effect at %1 = %2\n", start, metric));
|
||||
|
||||
/* determine the BBT at start */
|
||||
|
||||
bbt = metric.bbt_at (timepos_t::from_superclock (start));
|
||||
|
||||
/* 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
|
||||
@ -1936,20 +1956,6 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, u
|
||||
* point after the latter of the tempo/meter points"
|
||||
*/
|
||||
|
||||
p = get_tempo_and_meter (tp, mp, start, true, true);
|
||||
TempoMetric 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 (timepos_t::from_superclock (start));
|
||||
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("start %1 is %2\n", start, bbt));
|
||||
|
||||
@ -1994,7 +2000,7 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, u
|
||||
|
||||
BBT_Time bar = bbt.round_down_to_bar ();
|
||||
|
||||
/* adjust to match bar_mod (i.e. we only want every 4th bar)
|
||||
/* adjust to match bar_mod (e.g. we only want every 4th bar)
|
||||
*/
|
||||
|
||||
if (bar_mod != 1) {
|
||||
@ -2013,9 +2019,13 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, u
|
||||
|
||||
bbt = bar;
|
||||
|
||||
|
||||
/* rebuild metric */
|
||||
|
||||
p = get_tempo_and_meter (tp, mp, bbt, true, true);
|
||||
metric = TempoMetric (*tp, *mp);
|
||||
|
||||
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("metric in effect(3) at %1 = %2\n", start, metric));
|
||||
start = metric.superclock_at (bbt);
|
||||
|
||||
@ -2026,156 +2036,144 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, u
|
||||
|
||||
/* at this point:
|
||||
*
|
||||
* - metric is a TempoMetric that describes the situation at start
|
||||
* - metric is a TempoMetric that describes the situation at the adjusted start time
|
||||
* - p is an iterator pointin to either the end of the _points list, or
|
||||
* the next point in the list after start.
|
||||
*/
|
||||
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("start filling points with start = %1 end = %2 with limit @ %3\n", start, end, *p));
|
||||
while (p != _points.end() && start < end) {
|
||||
|
||||
Temporal::Beats beats = metric.quarters_at_superclock (start);
|
||||
|
||||
while (start < end) {
|
||||
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("start %1 end %2 bbt %3 find first/limit with limit @ = %4\n", start, end, bbt, *p));
|
||||
|
||||
const superclock_t limit = (p == _points.end()) ? end : std::min (p->sclock(), end);
|
||||
|
||||
while (start < limit) {
|
||||
|
||||
/* add point to grid, perhaps */
|
||||
/* Generate grid points (either actual meter-defined
|
||||
* beats, or bars based on bar_mod) up until the next point
|
||||
* in the map
|
||||
*/
|
||||
|
||||
if (bar_mod != 0) {
|
||||
if (bbt.is_bar() && (bar_mod == 1 || ((bbt.bars % bar_mod == 1)))) {
|
||||
ret.push_back (TempoMapPoint (*this, metric, start, beats, bbt));
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("G %1\t %2\n", metric, ret.back()));
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("G %1\t [%2]\n", metric, ret.back()));
|
||||
} else {
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("-- skip %1 not on bar_mod %2\n", bbt, bar_mod));
|
||||
}
|
||||
} else {
|
||||
ret.push_back (TempoMapPoint (*this, metric, start, beats, bbt));
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("G %1\t %2\n", metric, ret.back()));
|
||||
}
|
||||
|
||||
if (bar_mod == 0) {
|
||||
|
||||
/* Advance beats by the meter note value size, and
|
||||
* then recompute the BBT and superclock
|
||||
* position corresponding to that musical time
|
||||
*/
|
||||
|
||||
beats += metric.tempo().note_type_as_beats ();
|
||||
bbt = metric.bbt_at (beats);
|
||||
start = metric.superclock_at (beats);
|
||||
|
||||
} else {
|
||||
|
||||
/* Advance by the number of bars specified by
|
||||
bar_mod, then recompute the beats and
|
||||
superclock position corresponding to that
|
||||
BBT time.
|
||||
*/
|
||||
/* Advance by the number of bars specified by bar_mod */
|
||||
|
||||
bbt.bars += bar_mod;
|
||||
start = metric.superclock_at (bbt);
|
||||
beats = metric.quarters_at_superclock (start);
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("bar mod %1 moved to %2 (start %3)\n", bar_mod, bbt, start))
|
||||
|
||||
} else {
|
||||
|
||||
ret.push_back (TempoMapPoint (*this, metric, start, beats, bbt));
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("G %1\t [%2]\n", metric, ret.back()));
|
||||
|
||||
/* Advance beats by 1 meter-defined "beat */
|
||||
|
||||
if (beat_div == 1) {
|
||||
bbt = metric.bbt_add (bbt, BBT_Offset (0, 1, 0));
|
||||
} else {
|
||||
bbt = metric.bbt_add (bbt, BBT_Offset (0, 0, Temporal::ticks_per_beat / beat_div));
|
||||
}
|
||||
}
|
||||
|
||||
/* we might be finished ...*/
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("check overrun of next point with bbt @ %1 point @ %2\n", bbt, *p));
|
||||
|
||||
if (start >= end) {
|
||||
break;
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("stopped fill with start %1 and point at %2\n", start, (p == _points.end() ? -1 : p->sclock())));
|
||||
|
||||
while ((p != _points.end()) && (start >= p->sclock())) {
|
||||
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("pausing to deal with point => %1\n", *p));
|
||||
|
||||
/* have just passed or arrived at the next
|
||||
* point. Consider adding a grid point for this point.
|
||||
/* Now check that neither the new BBT time nor the
|
||||
* corresponding audio time are past the next point, which
|
||||
* could be anywhere (since BBT marker placement is arbitrary
|
||||
* and connect be inferred from the prior elements of the tempo
|
||||
* map)
|
||||
*/
|
||||
|
||||
if (bar_mod != 0) {
|
||||
if (p->bbt().is_bar() && (bar_mod == 1 || ((p->bbt().bars % bar_mod == 1)))) {
|
||||
ret.push_back (TempoMapPoint (*this, metric, p->sclock(), p->beats(), p->bbt()));
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("G %1\t %2\n", metric, ret.back()));
|
||||
} else {
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("-- skip %1 not on bar_mod %2\n", p->bbt(), bar_mod));
|
||||
}
|
||||
if (bbt > p->bbt()) {
|
||||
|
||||
} else {
|
||||
|
||||
ret.push_back (TempoMapPoint (*this, metric, p->sclock(), p->beats(), p->bbt()));
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("G %1\t %2\n", metric, ret.back()));
|
||||
}
|
||||
|
||||
/* reset our notion of where we are */
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("BBT %1 is past next point @ %2\n", bbt, *p));
|
||||
|
||||
start = p->sclock();
|
||||
bbt = p->bbt();
|
||||
beats = p->beats();
|
||||
|
||||
/* But there may be multiple points here, and we have
|
||||
* to check them all (Tempo/Meter/MusicTime ... which
|
||||
* is itself both a Tempo *and* Meter point) before
|
||||
* proceeding.
|
||||
*/
|
||||
bool rebuild_metric = false;
|
||||
|
||||
const superclock_t pos = p->sclock();
|
||||
|
||||
Points::const_iterator nxt = p;
|
||||
++nxt;
|
||||
while (p->bbt() == bbt) {
|
||||
|
||||
TempoPoint const * tpp;
|
||||
MeterPoint const * mpp;
|
||||
|
||||
/* use this point */
|
||||
|
||||
if ((tpp = dynamic_cast<TempoPoint const *> (&(*p))) != 0) {
|
||||
rebuild_metric = true;
|
||||
tp = tpp;
|
||||
}
|
||||
|
||||
if ((mpp = dynamic_cast<MeterPoint const *> (&(*p))) != 0) {
|
||||
rebuild_metric = true;
|
||||
mp = mpp;
|
||||
}
|
||||
|
||||
/* use any subsequent ones at the same location */
|
||||
++p;
|
||||
}
|
||||
|
||||
while ((nxt != _points.end()) && (nxt->sclock() == pos)) {
|
||||
/* reset the metric to use the most recent tempo & meter */
|
||||
|
||||
/* Set up the new metric given the new point */
|
||||
if (rebuild_metric) {
|
||||
metric = TempoMetric (*tp, *mp);
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("with start = %1 aka %2 rebuilt metric from points, now %3\n", start, bbt, metric));
|
||||
}
|
||||
}
|
||||
|
||||
if ((tpp = dynamic_cast<TempoPoint const *> (&(*nxt))) != 0) {
|
||||
start = metric.superclock_at (bbt);
|
||||
|
||||
if (start > p->sclock()) {
|
||||
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("audio time %1 is past next point @ %2\n", start, *p));
|
||||
|
||||
/* reset our sense of "now" */
|
||||
start = p->sclock();
|
||||
bbt = p->bbt();
|
||||
beats = p->beats();
|
||||
|
||||
/* If we just arrived at a point (indicated by bbt ==
|
||||
* p->bbt()), use all points at the same location to
|
||||
* potentially reconstruct the metric. Note that a BBT point is
|
||||
* both a tempo and a meter point, which is why we do test each
|
||||
* point found at this location as both.
|
||||
*/
|
||||
|
||||
bool rebuild_metric = false;
|
||||
|
||||
while (p->bbt() == bbt) {
|
||||
|
||||
TempoPoint const * tpp;
|
||||
MeterPoint const * mpp;
|
||||
|
||||
if ((tpp = dynamic_cast<TempoPoint const *> (&(*p))) != 0) {
|
||||
rebuild_metric = true;
|
||||
tp = tpp;
|
||||
}
|
||||
|
||||
if ((mpp = dynamic_cast<MeterPoint const *> (&(*nxt))) != 0) {
|
||||
if ((mpp = dynamic_cast<MeterPoint const *> (&(*p))) != 0) {
|
||||
rebuild_metric = true;
|
||||
mp = mpp;
|
||||
}
|
||||
|
||||
++nxt;
|
||||
++p;
|
||||
}
|
||||
|
||||
/* Build a new metric from the composite of all the
|
||||
* points at this position.
|
||||
*/
|
||||
/* reset the metric to use the most recent tempo & meter */
|
||||
|
||||
if (rebuild_metric) {
|
||||
metric = TempoMetric (*tp, *mp);
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("metric changed, now using %1\n", metric));
|
||||
p = nxt;
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("with start = %1 aka %2 rebuilt metric from points, now %3\n", start, bbt, metric));
|
||||
}
|
||||
|
||||
/* If we've reached the end of the points list, break and let
|
||||
* the final phase below fill out the rest of the grid
|
||||
}
|
||||
|
||||
/* Update the quarter-note time value to match the BBT and
|
||||
* audio time positions
|
||||
*/
|
||||
|
||||
if (p == _points.end()) {
|
||||
break;
|
||||
}
|
||||
beats = metric.quarters_at (bbt);
|
||||
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("bar mod %1 moved to %2 qn %3 sc %4)\n", bar_mod, bbt, beats, start));
|
||||
|
||||
}
|
||||
|
||||
/* reached the end or no more points to consider, so just
|
||||
@ -2184,8 +2182,6 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, u
|
||||
|
||||
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.
|
||||
*
|
||||
@ -2194,33 +2190,51 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, u
|
||||
* constant. metric will also remain constant until we finish.
|
||||
*/
|
||||
|
||||
const superclock_t step = metric.superclocks_per_grid_at (start);
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("reached end, no more map points, use %5 to finish between %1 .. %2 initial bbt %3, beats %4\n", start, end, bbt, beats.str(), metric));
|
||||
|
||||
do {
|
||||
const Temporal::Beats beats = metric.quarters_at_superclock (start);
|
||||
while (start < end) {
|
||||
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("bar mod %1 moved to %2 qn %3 sc %4)\n", bar_mod, bbt, beats, start));
|
||||
|
||||
/* It is possible we already added the current BBT
|
||||
* point, so check to avoid doubling up
|
||||
*/
|
||||
|
||||
if (ret.back().bbt() != bbt) {
|
||||
if (bar_mod != 0) {
|
||||
if (bbt.is_bar() && (bar_mod == 1 || ((bbt.bars % bar_mod == 0)))) {
|
||||
ret.push_back (TempoMapPoint (*this, metric, start, beats, bbt));
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("Gend %1\t %2\n", metric, ret.back()));
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("GendA %1\t %2\n", metric, ret.back()));
|
||||
}
|
||||
|
||||
/* Advance by the number of bars specified by
|
||||
bar_mod, then recompute the beats and
|
||||
superclock position corresponding to that
|
||||
BBT time.
|
||||
*/
|
||||
|
||||
bbt.bars += bar_mod;
|
||||
|
||||
} else {
|
||||
ret.push_back (TempoMapPoint (*this, metric, start, beats, bbt));
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("Gend %1\t %2\n", metric, ret.back()));
|
||||
DEBUG_TRACE (DEBUG::Grid, string_compose ("GendB %1\t %2\n", metric, ret.back()));
|
||||
|
||||
/* move on by 1 meter-defined "beat" */
|
||||
|
||||
if (beat_div == 1) {
|
||||
bbt = metric.bbt_add (bbt, BBT_Offset (0, 1, 0));
|
||||
} else {
|
||||
bbt = metric.bbt_add (bbt, BBT_Offset (0, 0, Temporal::ticks_per_beat / beat_div));
|
||||
}
|
||||
}
|
||||
|
||||
start += step;
|
||||
bbt = metric.bbt_at (timepos_t::from_superclock (start));
|
||||
/* compute audio and quarter-note time from the new BBT position */
|
||||
|
||||
} while (start < end);
|
||||
start = metric.superclock_at (bbt);
|
||||
beats = metric.quarters_at (bbt);
|
||||
}
|
||||
|
||||
/* 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));
|
||||
|
@ -879,7 +879,7 @@ class /*LIBTEMPORAL_API*/ TempoMap : public PBD::StatefulDestructible
|
||||
|
||||
LIBTEMPORAL_API BBT_Time bbt_walk (BBT_Time const &, BBT_Offset const &) const;
|
||||
|
||||
LIBTEMPORAL_API void get_grid (TempoMapPoints & points, superclock_t start, superclock_t end, uint32_t bar_mod = 0) const;
|
||||
LIBTEMPORAL_API void get_grid (TempoMapPoints & points, superclock_t start, superclock_t end, uint32_t bar_mod = 0, uint32_t beat_div = 1) const;
|
||||
LIBTEMPORAL_API uint32_t count_bars (Beats const & start, Beats const & end) const;
|
||||
|
||||
struct EmptyTempoMapException : public std::exception {
|
||||
|
Loading…
Reference in New Issue
Block a user