13
0

temporal: refactor ::get_grid() for performance reasons

the API now provides the option to call ::get_grid() with an iterator which may
be re-used on subsequent calls. This avoids unbounded O(N) "walks" from the
marker preceding the start point of the grid to the start point.

This commit also includes "fast-path" code for the common case of a single
tempo and single meter
This commit is contained in:
Paul Davis 2023-07-14 13:02:44 -06:00
parent ae5755dbe5
commit 509efdb290
7 changed files with 228 additions and 103 deletions

View File

@ -1903,9 +1903,9 @@ private:
void reassociate_metric_markers (Temporal::TempoMap::SharedPtr const &);
void reassociate_tempo_marker (Temporal::TempoMap::SharedPtr const & tmap, Temporal::TempoMap::Tempos const &, TempoMarker& marker);
void reassociate_meter_marker (Temporal::TempoMap::SharedPtr const & tmap, Temporal::TempoMap::Meters const &, MeterMarker& marker);
void reassociate_bartime_marker (Temporal::TempoMap::SharedPtr const & tmap, Temporal::TempoMap::MusicTimes const &, BBTMarker& marker);
void reassociate_tempo_marker (Temporal::TempoMap::SharedPtr const & tmap, Temporal::Tempos const &, TempoMarker& marker);
void reassociate_meter_marker (Temporal::TempoMap::SharedPtr const & tmap, Temporal::Meters const &, MeterMarker& marker);
void reassociate_bartime_marker (Temporal::TempoMap::SharedPtr const & tmap, Temporal::MusicTimes const &, BBTMarker& marker);
void make_bbt_marker (Temporal::MusicTimePoint const *, Marks::iterator before);
void make_meter_marker (Temporal::MeterPoint const *, Marks::iterator before);

View File

@ -110,7 +110,7 @@ Editor::reassociate_metric_markers (TempoMap::SharedPtr const& tmap)
}
void
Editor::reassociate_tempo_marker (TempoMap::SharedPtr const & tmap, TempoMap::Tempos const & tempos, TempoMarker& marker)
Editor::reassociate_tempo_marker (TempoMap::SharedPtr const & tmap, Tempos const & tempos, TempoMarker& marker)
{
Temporal::MusicTimePoint const * mtp;
@ -131,7 +131,7 @@ Editor::reassociate_tempo_marker (TempoMap::SharedPtr const & tmap, TempoMap::Te
}
void
Editor::reassociate_meter_marker (TempoMap::SharedPtr const & tmap, TempoMap::Meters const & meters, MeterMarker& marker)
Editor::reassociate_meter_marker (TempoMap::SharedPtr const & tmap, Meters const & meters, MeterMarker& marker)
{
Temporal::MusicTimePoint const * mtp;
@ -151,7 +151,7 @@ Editor::reassociate_meter_marker (TempoMap::SharedPtr const & tmap, TempoMap::Me
}
void
Editor::reassociate_bartime_marker (TempoMap::SharedPtr const & tmap, TempoMap::MusicTimes const & bartimes, BBTMarker& marker)
Editor::reassociate_bartime_marker (TempoMap::SharedPtr const & tmap, MusicTimes const & bartimes, BBTMarker& marker)
{
for (auto const & bartime : bartimes) {
if (marker.point().sclock() == bartime.sclock()) {
@ -220,7 +220,7 @@ Editor::reset_tempo_marks ()
const uint32_t tc_color = UIConfiguration::instance().color ("tempo curve");
const samplecnt_t sr (_session->sample_rate());
TempoMap::Tempos const & tempi (TempoMap::use()->tempos());
Tempos const & tempi (TempoMap::use()->tempos());
TempoPoint const * prev_ts = 0;
double max_tempo = 0.0;
double min_tempo = DBL_MAX;
@ -251,7 +251,7 @@ Editor::reset_meter_marks ()
return;
}
TempoMap::Meters const & meters (TempoMap::use()->meters());
Meters const & meters (TempoMap::use()->meters());
for (auto & m : meter_marks) {
delete m;
@ -277,7 +277,7 @@ Editor::reset_bbt_marks ()
}
Temporal::TempoMap::SharedPtr tmap (TempoMap::use());
TempoMap::MusicTimes const & bartimes (tmap->bartimes());
MusicTimes const & bartimes (tmap->bartimes());
for (auto & b : bbt_marks) {
delete b;

View File

@ -2152,6 +2152,7 @@ private:
mutable Glib::Threads::RWLock click_lock;
samplecnt_t _click_io_latency;
PBD::ScopedConnection _click_io_connection;
Temporal::GridIterator _click_iterator;
static const Sample default_click[];
static const samplecnt_t default_click_length;

View File

@ -124,7 +124,7 @@ Session::click (samplepos_t cycle_start, samplecnt_t nframes)
const samplepos_t end = start + move;
_click_points.clear ();
TempoMap::use()->get_grid (_click_points, samples_to_superclock (start, sample_rate()), samples_to_superclock (end, sample_rate()));
TempoMap::use()->get_grid_with_iterator (_click_iterator, _click_points, samples_to_superclock (start, sample_rate()), samples_to_superclock (end, sample_rate()));
if (_click_points.empty()) {
start += move;

View File

@ -299,6 +299,7 @@ Session::locate (samplepos_t target_sample, bool for_loop_end, bool force, bool
}
_last_roll_location = _last_roll_or_reversal_location = _transport_sample;
_click_iterator.valid = false;
Located (); /* EMIT SIGNAL */
}

View File

@ -663,21 +663,22 @@ MeterPoint::get_state () const
return base;
}
superclock_t
TempoMetric::reftime() const
TempoMetric::TempoMetric (TempoPoint const & t, MeterPoint const & m)
: _tempo (&t)
, _meter (&m)
{
return _tempo->map().reftime (*this);
_reftime = _tempo->map().reftime (t, m);
}
superclock_t
TempoMap::reftime (TempoMetric const &tm) const
TempoMap::reftime (TempoPoint const & t, MeterPoint const & m) const
{
Points::const_iterator pi;
if (tm.meter().sclock() < tm.tempo().sclock()) {
pi = _points.s_iterator_to (*(static_cast<const Point*> (&tm.meter())));
if (m.sclock() < t.sclock()) {
pi = _points.s_iterator_to (*(static_cast<const Point*> (&m)));
} else {
pi = _points.s_iterator_to (*(static_cast<const Point*> (&tm.tempo())));
pi = _points.s_iterator_to (*(static_cast<const Point*> (&t)));
}
/* Walk backwards through points to find a BBT markers, or the start */
@ -2418,11 +2419,11 @@ TempoMap::_get_tempo_and_meter (typename const_traits_t::tempo_point_type & tp,
return last_used;
}
void
Points::const_iterator
TempoMap::get_grid (TempoMapPoints& ret, superclock_t rstart, superclock_t end, uint32_t bar_mod, uint32_t beat_div) const
{
if (rstart == end) {
return;
return _points.end();
}
/* note: @p bar_mod is "bar modulo", and describes the N in "give
@ -2442,6 +2443,28 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t rstart, superclock_t end,
#endif
DEBUG_TRACE (DEBUG::Grid, string_compose (">>> GRID START %1 .. %2 (barmod = %3)\n", rstart, end, bar_mod));
/* The fast path: one tempo, one meter, just do (relatively) simple math */
if (_tempos.size() == 1 && _meters.size() == 1) {
TempoMetric metric (_tempos.front(), _meters.front());
/* Figure out the beat closest to but preceding rstart */
superclock_t spnt = metric.superclocks_per_note_type() / beat_div;
superclock_t start = (rstart / spnt) * spnt; /* beat preceding rstart */
/* determine BBT and beats at the position. Note that we know
* that the tempo and meter must be at zero
*/
BBT_Time bbt (metric.bbt_at (timepos_t::from_superclock (start)));
Beats beats (metric.quarters_at_superclock (start));
fill_grid_with_final_metric (ret, metric, start, rstart, end, bar_mod, beat_div, beats, bbt);
return _points.end();
}
TempoPoint const * tp = 0;
MeterPoint const * mp = 0;
Points::const_iterator p = _points.begin();
@ -2493,6 +2516,71 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t rstart, superclock_t end,
* the next point in the list after start.
*/
fill_grid_by_walking (ret, p, metric, start, rstart, end, bar_mod, beat_div, beats, bbt);
/* reached the end or no more points to consider, so just
* finish by filling the grid to the end, if necessary.
*/
if (start < end) {
fill_grid_with_final_metric (ret, metric, start, rstart, end, bar_mod, beat_div, beats, bbt);
} 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));
}
}
DEBUG_TRACE (DEBUG::Grid, "<<< GRID DONE\n");
return p;
}
void
TempoMap::get_grid_with_iterator (GridIterator& iter, TempoMapPoints& ret, superclock_t rstart, superclock_t end, uint32_t bar_mod, uint32_t beat_div) const
{
DEBUG_TRACE (DEBUG::Grid, string_compose (">>> GRID-I START %1 .. %2 (barmod = %3) iter valid ? %4 iter for %5\n", rstart, end, bar_mod, iter.valid, iter.end));
if (!iter.valid || rstart != iter.end) {
Points::const_iterator p = get_grid (ret, rstart, end, bar_mod, beat_div);
if (!ret.empty()) {
TempoMapPoint& tmp (ret.back());
iter = GridIterator (&tmp.tempo(), &tmp.meter(), tmp.sclock(), tmp.beats(), tmp.bbt(), p, end);
} else {
iter.end = end;
}
return;
}
TempoMetric metric (*iter.tempo, *iter.meter);
superclock_t start = iter.sclock;
Beats beats = iter.beats;
BBT_Time bbt = iter.bbt;
Points::const_iterator p = iter.points_iterator;
fill_grid_by_walking (ret, p, metric, start, rstart, end, bar_mod, beat_div, beats, bbt);
if (start < end) {
fill_grid_with_final_metric (ret, metric, start, rstart, end, bar_mod, beat_div, beats, bbt);
}
if (!ret.empty()) {
TempoMapPoint& tmp (ret.back());
iter = GridIterator (&metric.tempo(), &metric.meter(), tmp.sclock(), tmp.beats(), tmp.bbt(), p, end);
} else {
iter.end = end;
}
DEBUG_TRACE (DEBUG::Grid, "<<< GRID-I DONE\n");
}
void
TempoMap::fill_grid_by_walking (TempoMapPoints& ret, Points::const_iterator& p_i, TempoMetric& metric_i, superclock_t& start, superclock_t rstart, superclock_t end,
int bar_mod, int beat_div, Beats& beats, BBT_Time& bbt) const
{
TempoMetric metric (metric_i);
Points::const_iterator p (p_i);
while (p != _points.end() && start < end) {
@ -2504,11 +2592,15 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t rstart, superclock_t end,
*/
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 ("Ga %1\t [%2]\n", metric, ret.back()));
if (start >= rstart) {
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 ("Ga %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 {
DEBUG_TRACE (DEBUG::Grid, string_compose ("-- skip %1 not on bar_mod %2\n", bbt, bar_mod));
DEBUG_TRACE (DEBUG::Grid, string_compose ("skip-a point @ %1, too early for %2\n", start, rstart));
}
/* Advance by the number of bars specified by bar_mod */
@ -2524,13 +2616,17 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t rstart, superclock_t end,
} else {
int ticks = (bbt.beats * metric.meter().ticks_per_grid()) + bbt.ticks;
int mod = Temporal::ticks_per_beat / beat_div;
if ((ticks % mod) == 0) {
ret.push_back (TempoMapPoint (*this, metric, start, beats, bbt));
DEBUG_TRACE (DEBUG::Grid, string_compose ("Gd %1\t [%2]\n", metric, ret.back()));
} else {
DEBUG_TRACE (DEBUG::Grid, string_compose ("-- skip %1 not on beat_div_mod %2\n", ticks, mod));
}
}
} else {
DEBUG_TRACE (DEBUG::Grid, string_compose ("skip-b point @ %1, too early for %2\n", start, rstart));
}
/* Note that in a BBT time, the "beats" count is
* meter-dependent. So if we're in 4/4 time, the beats
* are quarters. If we're in 7/8 time, the beats are in
@ -2552,7 +2648,7 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t rstart, superclock_t end,
if (!mtp) {
if (bbt == p->bbt()) {
DEBUG_TRACE (DEBUG::Grid, string_compose ("Gc %1\t [%2]\n", metric, ret.back()));
// DEBUG_TRACE (DEBUG::Grid, string_compose ("Gc %1\t [%2]\n", metric, ret.back()));
DEBUG_TRACE (DEBUG::Grid, string_compose ("we've reached the next point via BBT, BBT %1 audio %2 point %3\n", bbt, start, *p));
reset = true;
} else if (bbt > p->bbt()) {
@ -2560,8 +2656,14 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t rstart, superclock_t end,
reset = true;
}
} else {
/* What does the current metric say about the audio
time position of @v bbt ?
*/
start = metric.superclock_at (bbt);
if (start >= p->sclock()) {
/* Yep, too far. So we need to reset and take
the next (music time point) into account.
*/
DEBUG_TRACE (DEBUG::Grid, string_compose ("we've reached/passed the next point via sclock, BBT %1 audio %2 point %3\n", bbt, start, *p));
reset = true;
} else {
@ -2570,19 +2672,20 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t rstart, superclock_t end,
}
DEBUG_TRACE (DEBUG::Grid, string_compose ("check overrun of next point, reset required ? %4 with bbt @ %1 audio %2 point %3\n", bbt, start, *p, (reset ? "YES" : "NO")));
if (reset) {
/* bbt is position for the next grid-line.
*/
TempoPoint const * tp = &metric.tempo();
MeterPoint const * mp = &metric.meter();
if (mtp) {
/* BBT Markers/MusicTimePoints give the user a
* chance to "reset" the BBT ruler. We should
* do the same, unconditionally.
*/
tp = dynamic_cast<TempoPoint const *> (&*p);
mp = dynamic_cast<MeterPoint const *> (&*p);
@ -2658,7 +2761,6 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t rstart, superclock_t end,
}
/* this is potentially ambiguous */
start = metric.superclock_at (bbt);
/* Update the quarter-note time value to match the BBT and
@ -2667,91 +2769,80 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t rstart, superclock_t end,
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
* finish by filling the grid to the end, if necessary.
*/
if (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.
/* we have a candidate grid point (start,beats,bbt). It might
* not be within the range we're generating, but if it is, the iterator
* will need to know where the points_iterator and metric
* parameters ended up before we return..
*/
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));
if (start >= rstart && start < end) {
p_i = p;
metric_i = metric;
}
while (start < end) {
DEBUG_TRACE (DEBUG::Grid, string_compose ("moved to %1 qn %2 sc %3) using metric %4 p at end %5\n", bbt, beats, start, metric, p == _points.end()));
}
}
DEBUG_TRACE (DEBUG::Grid, string_compose ("bar mod %1 moved to %2 qn %3 sc %4)\n", bar_mod, bbt, beats, start));
void
TempoMap::fill_grid_with_final_metric (TempoMapPoints& ret, TempoMetric metric, superclock_t start, superclock_t rstart, superclock_t end, int bar_mod, int beat_div, Beats beats, BBT_Time bbt) const
{
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));
/* It is possible we already added the current BBT
* point, so check to avoid doubling up
*/
while (start < end) {
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 ("GendA %1\t %2\n", metric, ret.back()));
}
DEBUG_TRACE (DEBUG::Grid, string_compose ("bar mod %1 moved to %2 qn %3 sc %4)\n", bar_mod, bbt, beats, start));
/* Advance by the number of bars specified by
bar_mod, then recompute the beats and
superclock position corresponding to that
BBT time.
*/
/* It is possible we already added the current BBT
* point, so check to avoid doubling up
*/
bbt.bars += bar_mod;
if (bar_mod != 0) {
if ((start >= rstart) && 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 ("GendA %1\t %2\n", metric, ret.back()));
}
} else {
if (start >= rstart) {
if (beat_div == 1) {
ret.push_back (TempoMapPoint (*this, metric, start, beats, bbt));
DEBUG_TRACE (DEBUG::Grid, string_compose ("Gendb %1\t [%2]\n", metric, ret.back()));
} else {
int ticks = (bbt.beats * metric.meter().ticks_per_grid()) + bbt.ticks;
int mod = Temporal::ticks_per_beat / beat_div;
if ((ticks % mod) == 0) {
ret.push_back (TempoMapPoint (*this, metric, start, beats, bbt));
DEBUG_TRACE (DEBUG::Grid, string_compose ("Gendd %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.
*/
/* move on by 1 meter-defined "beat" */
bbt.bars += bar_mod;
} else {
if (start >= rstart) {
if (beat_div == 1) {
bbt = metric.bbt_add (bbt, BBT_Offset (0, 1, 0));
ret.push_back (TempoMapPoint (*this, metric, start, beats, bbt));
DEBUG_TRACE (DEBUG::Grid, string_compose ("Gendb %1\t [%2]\n", metric, ret.back()));
} else {
bbt = metric.bbt_add (bbt, BBT_Offset (0, 0, Temporal::ticks_per_beat / beat_div));
int ticks = (bbt.beats * metric.meter().ticks_per_grid()) + bbt.ticks;
int mod = Temporal::ticks_per_beat / beat_div;
if ((ticks % mod) == 0) {
ret.push_back (TempoMapPoint (*this, metric, start, beats, bbt));
DEBUG_TRACE (DEBUG::Grid, string_compose ("Gendd %1\t [%2]\n", metric, ret.back()));
}
}
}
/* compute audio and quarter-note time from the new BBT position */
/* move on by 1 meter-defined "beat" */
start = metric.superclock_at (bbt);
beats = metric.quarters_at (bbt);
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));
}
}
/* all done */
/* compute audio and quarter-note time from the new BBT position */
} 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));
}
start = metric.superclock_at (bbt);
beats = metric.quarters_at (bbt);
}
DEBUG_TRACE (DEBUG::Grid, "<<< GRID DONE\n");
}
std::ostream&
std::operator<<(std::ostream& str, Meter const & m)
{

View File

@ -478,10 +478,10 @@ class /*LIBTEMPORAL_API*/ TempoPoint : public Tempo, public tempo_hook, public v
class LIBTEMPORAL_API TempoMetric
{
public:
TempoMetric (TempoPoint const & t, MeterPoint const & m) : _tempo (&t), _meter (&m) {}
TempoMetric (TempoPoint const & t, MeterPoint const & m);
virtual ~TempoMetric () {}
superclock_t reftime() const;
superclock_t reftime() const { return _reftime; }
TempoPoint const & tempo() const { return *_tempo; }
MeterPoint const & meter() const { return *_meter; }
@ -541,6 +541,7 @@ class LIBTEMPORAL_API TempoMetric
protected:
TempoPoint const * _tempo;
MeterPoint const * _meter;
superclock_t _reftime;
};
@ -641,6 +642,37 @@ class LIBTEMPORAL_API TempoMapPoint : public Point, public TempoMetric
typedef std::vector<TempoMapPoint> TempoMapPoints;
typedef boost::intrusive::list<TempoPoint, boost::intrusive::base_hook<tempo_hook>> Tempos;
typedef boost::intrusive::list<MeterPoint, boost::intrusive::base_hook<meter_hook>> Meters;
typedef boost::intrusive::list<MusicTimePoint, boost::intrusive::base_hook<bartime_hook>> MusicTimes;
typedef boost::intrusive::list<Point, boost::intrusive::base_hook<point_hook>> Points;
struct LIBTEMPORAL_API GridIterator
{
GridIterator () : valid (false), sclock (0), tempo (nullptr), meter (nullptr), end (0) {}
GridIterator (TempoPoint const * tp, MeterPoint const * mp, superclock_t sc, Beats const & b, BBT_Time const & bb, Points::const_iterator p, superclock_t e)
: valid (false)
, sclock (sc)
, beats (b)
, bbt (bb)
, tempo (tp)
, meter (mp)
, points_iterator (p)
, end (e)
{
valid = (tempo && meter);
}
bool valid;
superclock_t sclock;
Beats beats;
BBT_Time bbt;
TempoPoint const * tempo;
MeterPoint const * meter;
Points::const_iterator points_iterator;
superclock_t end;
};
class /*LIBTEMPORAL_API*/ TempoMap : public PBD::StatefulDestructible
{
/* Any given thread must be able to carry out tempo-related arithmetic
@ -712,7 +744,7 @@ class /*LIBTEMPORAL_API*/ TempoMap : public PBD::StatefulDestructible
LIBTEMPORAL_API static void abort_update ();
/* not part of public API */
superclock_t reftime(TempoMetric const &) const;
superclock_t reftime(TempoPoint const &, MeterPoint const &) const;
/* and now on with the rest of the show ... */
@ -904,7 +936,12 @@ class /*LIBTEMPORAL_API*/ TempoMap : public PBD::StatefulDestructible
LIBTEMPORAL_API BBT_Argument bbt_walk (BBT_Argument const &, BBT_Offset const &) 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;
Tempos const & tempos() const { return _tempos; }
Meters const & meters() const { return _meters; }
MusicTimes const & bartimes() const { return _bartimes; }
LIBTEMPORAL_API Points::const_iterator get_grid (TempoMapPoints & points, superclock_t start, superclock_t end, uint32_t bar_mod = 0, uint32_t beat_div = 1) const;
LIBTEMPORAL_API void get_grid_with_iterator (GridIterator& iter, TempoMapPoints& ret, superclock_t rstart, superclock_t end, uint32_t bar_mod = 0, uint32_t beat_div = 1) const;
struct EmptyTempoMapException : public std::exception {
virtual const char* what() const throw() { return "TempoMap is empty"; }
@ -916,15 +953,6 @@ class /*LIBTEMPORAL_API*/ TempoMap : public PBD::StatefulDestructible
LIBTEMPORAL_API XMLNode& get_state() const;
typedef boost::intrusive::list<TempoPoint, boost::intrusive::base_hook<tempo_hook>> Tempos;
typedef boost::intrusive::list<MeterPoint, boost::intrusive::base_hook<meter_hook>> Meters;
typedef boost::intrusive::list<MusicTimePoint, boost::intrusive::base_hook<bartime_hook>> MusicTimes;
typedef boost::intrusive::list<Point, boost::intrusive::base_hook<point_hook>> Points;
Tempos const & tempos() const { return _tempos; }
Meters const & meters() const { return _meters; }
MusicTimes const & bartimes() const { return _bartimes; }
LIBTEMPORAL_API Beats quarters_at_sample (samplepos_t sc) const { return quarters_at_superclock (samples_to_superclock (sc, TEMPORAL_SAMPLE_RATE)); }
LIBTEMPORAL_API Beats quarters_at_superclock (superclock_t sc) const;
@ -1106,6 +1134,10 @@ class /*LIBTEMPORAL_API*/ TempoMap : public PBD::StatefulDestructible
void reset_section (Points::iterator& begin, Points::iterator& end, superclock_t, TempoMetric& metric);
TempoMapCutBuffer* cut_copy (timepos_t const & start, timepos_t const & end, bool copy, bool ripple);
void fill_grid_by_walking (TempoMapPoints& ret, Points::const_iterator& p, TempoMetric& metric, superclock_t& start, superclock_t rstart,
superclock_t end, int bar_mod, int beat_div, Beats& beats, BBT_Time& bbt) const;
void fill_grid_with_final_metric (TempoMapPoints& ret, TempoMetric metric, superclock_t start, superclock_t rstart, superclock_t end, int bar_mod, int beat_div, Beats beats, BBT_Time bbt) const;
};
class LIBTEMPORAL_API TempoMapCutBuffer