From 509efdb290c66ad72d1c0484640ff98c5130ea9a Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Fri, 14 Jul 2023 13:02:44 -0600 Subject: [PATCH] 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 --- gtk2_ardour/editor.h | 6 +- gtk2_ardour/editor_tempodisplay.cc | 12 +- libs/ardour/ardour/session.h | 1 + libs/ardour/session_click.cc | 2 +- libs/ardour/session_transport.cc | 1 + libs/temporal/tempo.cc | 251 ++++++++++++++++++++--------- libs/temporal/temporal/tempo.h | 58 +++++-- 7 files changed, 228 insertions(+), 103 deletions(-) diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index c775589a44..60bae9b753 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -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); diff --git a/gtk2_ardour/editor_tempodisplay.cc b/gtk2_ardour/editor_tempodisplay.cc index a5f30355f2..4070e3d76c 100644 --- a/gtk2_ardour/editor_tempodisplay.cc +++ b/gtk2_ardour/editor_tempodisplay.cc @@ -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; diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 90c43c6df5..c80de9b6fd 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -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; diff --git a/libs/ardour/session_click.cc b/libs/ardour/session_click.cc index 9adf92929e..dd38b4ec75 100644 --- a/libs/ardour/session_click.cc +++ b/libs/ardour/session_click.cc @@ -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; diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index 374dfa1a0f..2959329e2a 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -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 */ } diff --git a/libs/temporal/tempo.cc b/libs/temporal/tempo.cc index f89bf7c997..46fd27b7f0 100644 --- a/libs/temporal/tempo.cc +++ b/libs/temporal/tempo.cc @@ -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 (&tm.meter()))); + if (m.sclock() < t.sclock()) { + pi = _points.s_iterator_to (*(static_cast (&m))); } else { - pi = _points.s_iterator_to (*(static_cast (&tm.tempo()))); + pi = _points.s_iterator_to (*(static_cast (&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 (&*p); mp = dynamic_cast (&*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) { diff --git a/libs/temporal/temporal/tempo.h b/libs/temporal/temporal/tempo.h index 322e0cde47..2603e66970 100644 --- a/libs/temporal/temporal/tempo.h +++ b/libs/temporal/temporal/tempo.h @@ -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 TempoMapPoints; +typedef boost::intrusive::list> Tempos; +typedef boost::intrusive::list> Meters; +typedef boost::intrusive::list> MusicTimes; +typedef boost::intrusive::list> 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> Tempos; - typedef boost::intrusive::list> Meters; - typedef boost::intrusive::list> MusicTimes; - typedef boost::intrusive::list> 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