generalize repeated code into a template method, add lots of const-ness to facilitate this

This commit is contained in:
Paul Davis 2021-03-26 21:15:10 -06:00
parent c5ecd14622
commit 1f8290f154
4 changed files with 100 additions and 243 deletions

View file

@ -1887,8 +1887,8 @@ LuaBindings::common (lua_State* L)
.addStaticFunction ("use", &TempoMap::use)
.addFunction ("set_tempo", (Temporal::TempoPoint& (Temporal::TempoMap::*)(Temporal::Tempo const &,timepos_t const &)) &TempoMap::set_tempo)
.addFunction ("set_meter", (Temporal::MeterPoint& (Temporal::TempoMap::*)(Temporal::Meter const &,timepos_t const &)) &TempoMap::set_meter)
.addFunction ("tempo_at", (Temporal::TempoPoint& (Temporal::TempoMap::*)(timepos_t const &) const) &TempoMap::tempo_at)
.addFunction ("meter_at", (Temporal::MeterPoint& (Temporal::TempoMap::*)(timepos_t const &) const) &TempoMap::meter_at)
.addFunction ("tempo_at", (Temporal::TempoPoint const & (Temporal::TempoMap::*)(timepos_t const &) const) &TempoMap::tempo_at)
.addFunction ("meter_at", (Temporal::MeterPoint const & (Temporal::TempoMap::*)(timepos_t const &) const) &TempoMap::meter_at)
.addFunction ("bbt_at", (Temporal::BBT_Time (Temporal::TempoMap::*)(timepos_t const &) const) &TempoMap::bbt_at)
.addFunction ("quarters_at", (Temporal::Beats (Temporal::TempoMap::*)(timepos_t const &) const) &TempoMap::quarters_at)
.addFunction ("sample_at", (samplepos_t (Temporal::TempoMap::*)(timepos_t const &, samplecnt_t) const) &TempoMap::sample_at)

View file

@ -7418,7 +7418,7 @@ Session::maybe_update_tempo_from_midiclock_tempo (float bpm)
if (tmap->n_tempos() == 1) {
Temporal::TempoMetric const & metric (tmap->metric_at (0));
if (fabs (metric.tempo().note_types_per_minute() - bpm) > (0.01 * metric.tempo().note_types_per_minute())) {
tmap->change_tempo (metric.tempo(), Tempo (bpm, 4.0, bpm));
tmap->change_tempo (metric.get_editable_tempo(), Tempo (bpm, 4.0, bpm));
}
}
}

View file

@ -1642,6 +1642,53 @@ TempoMap::dump (std::ostream& ostr) const
ostr << "------------\n\n\n";
}
template<typename T> TempoMap::Points::const_iterator
TempoMap::_get_tempo_and_meter (TempoPoint const *& tp, MeterPoint const *& mp, T (Point::*method)() const, T arg, bool can_match) const
{
Points::const_iterator p;
bool tempo_done = false;
bool meter_done = false;
assert (!_tempos.empty());
assert (!_meters.empty());
assert (!_points.empty());
tp = 0;
mp = 0;
for (tp = &_tempos.front(), mp = &_meters.front(), p = _points.begin(); p != _points.end(); ++p) {
TempoPoint const * tpp;
MeterPoint const * mpp;
if (!tempo_done && (tpp = dynamic_cast<TempoPoint const *> (&(*p))) != 0) {
if ((can_match && (((*p).*method)() > arg)) || (((*p).*method)() >= arg)) {
tempo_done = true;
} else {
tp = tpp;
}
}
if (!meter_done && (mpp = dynamic_cast<MeterPoint const *> (&(*p))) != 0) {
if ((can_match && (((*p).*method)() > arg)) || (((*p).*method)() >= arg)) {
meter_done = true;
} else {
mp = mpp;
}
}
if (meter_done && tempo_done) {
break;
}
}
if (!tp || !mp) {
return _points.end();
}
return p;
}
void
TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, uint32_t bar_mod)
{
@ -1651,9 +1698,9 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, u
DEBUG_TRACE (DEBUG::Grid, string_compose (">>> GRID START %1 .. %2 (barmod = %3)\n", start, end, bar_mod));
TempoPoint* tp = 0;
MeterPoint* mp = 0;
Points::iterator p;
TempoPoint const * tp = 0;
MeterPoint const * mp = 0;
Points::const_iterator p;
/* initial values required, but will be reset before we begin */
TempoMetric metric (_tempos.front(), _meters.front());
@ -1665,34 +1712,10 @@ 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.
*/
for (tp = &_tempos.front(), p = _points.begin(); p != _points.end() && p->sclock() < start; ++p) {
TempoPoint* tpp;
DEBUG_TRACE (DEBUG::Grid, string_compose ("Looking at a point tempo %1\n", *p));
if ((tpp = dynamic_cast<TempoPoint*> (&(*p))) != 0) {
DEBUG_TRACE (DEBUG::Grid, string_compose ("set tempo with that (check: %1 < %2)\n", p->sclock(), start));
tp = tpp;
}
}
for (mp = &_meters.front(), p = _points.begin(); p != _points.end() && p->sclock() < start; ++p) {
MeterPoint* mpp;
DEBUG_TRACE (DEBUG::Grid, string_compose ("Looking at a point for meter %1\n", *p));
if ((mpp = dynamic_cast<MeterPoint*> (&(*p))) != 0) {
DEBUG_TRACE (DEBUG::Grid, string_compose ("set meter with that (check: %1 < %2)\n", p->sclock(), start));
mp = mpp;
}
}
/* reset metric */
p = get_tempo_and_meter (tp, mp, start, false);
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
@ -1721,29 +1744,10 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, u
/* rounded up, determine new starting superclock position */
DEBUG_TRACE (DEBUG::Grid, string_compose ("new bbt for start (rounded up) = %1\n", bbt));
for (tp = &_tempos.front(), p = _points.begin(); p != _points.end() && p->bbt() < bbt; ++p) {
TempoPoint* tpp;
if ((tpp = dynamic_cast<TempoPoint*> (&(*p))) != 0) {
tp = tpp;
}
}
for (mp = &_meters.front(), p = _points.begin(); p != _points.end() && p->bbt() < bbt; ++p) {
MeterPoint* mpp;
if ((mpp = dynamic_cast<MeterPoint*> (&(*p))) != 0) {
mp = mpp;
}
}
/* reset metric */
p = get_tempo_and_meter (tp, mp, bbt);
metric = TempoMetric (*tp, *mp);
DEBUG_TRACE (DEBUG::Grid, string_compose ("metric in effect(2) at %1 = %2\n", start, metric));
/* recompute superclock position */
@ -1786,27 +1790,9 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, u
bbt = bar;
for (tp = &_tempos.front(), p = _points.begin(); p != _points.end() && p->bbt() < bbt; ++p) {
TempoPoint* tpp;
MeterPoint* mpp;
if ((tpp = dynamic_cast<TempoPoint*> (&(*p))) != 0) {
tp = tpp;
}
}
for (mp = &_meters.front(), p = _points.begin(); p != _points.end() && p->bbt() < bbt; ++p) {
MeterPoint* mpp;
if ((mpp = dynamic_cast<MeterPoint*> (&(*p))) != 0) {
mp = mpp;
}
}
/* reset metric */
p = get_tempo_and_meter (tp, mp, bbt);
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);
@ -1907,19 +1893,19 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, u
const superclock_t pos = p->sclock();
Points::iterator nxt = p;
Points::const_iterator nxt = p;
++nxt;
TempoPoint* tpp;
MeterPoint* mpp;
TempoPoint const * tpp;
MeterPoint const * mpp;
/* use this point */
if ((tpp = dynamic_cast<TempoPoint*> (&(*p))) != 0) {
if ((tpp = dynamic_cast<TempoPoint const *> (&(*p))) != 0) {
tp = tpp;
}
if ((mpp = dynamic_cast<MeterPoint*> (&(*p))) != 0) {
if ((mpp = dynamic_cast<MeterPoint const *> (&(*p))) != 0) {
mp = mpp;
}
@ -1929,11 +1915,11 @@ TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end, u
/* Set up the new metric given the new point */
if ((tpp = dynamic_cast<TempoPoint*> (&(*nxt))) != 0) {
if ((tpp = dynamic_cast<TempoPoint const *> (&(*nxt))) != 0) {
tp = tpp;
}
if ((mpp = dynamic_cast<MeterPoint*> (&(*nxt))) != 0) {
if ((mpp = dynamic_cast<MeterPoint const *> (&(*nxt))) != 0) {
mp = mpp;
}
@ -2770,172 +2756,34 @@ TempoMap::metric_at (timepos_t const & pos) const
TempoMetric
TempoMap::metric_at (superclock_t sc, bool can_match) const
{
assert (!_tempos.empty());
assert (!_meters.empty());
assert (!_points.empty());
TempoPoint const * tp = 0;
MeterPoint const * mp = 0;
TempoPoint const * tpp = 0;
MeterPoint const * mpp = 0;
(void) get_tempo_and_meter (tp, mp, sc, can_match);
TempoPoint const * prev_t = &_tempos.front();
/* Yes, linear search because the typical size of _points
* is 2, and extreme sizes are on the order of 10-100
*/
Points::const_iterator p;
for (p = _points.begin(); p != _points.end() && p->sclock() < sc; ++p) {
if ((tpp = dynamic_cast<TempoPoint const *> (&(*p)))) {
prev_t = tpp;
}
}
MeterPoint const * prev_m = &_meters.front();
for (p = _points.begin(); p != _points.end() && p->sclock() < sc; ++p) {
if ((mpp = dynamic_cast<MeterPoint const *> (&(*p)))) {
prev_m = mpp;
}
}
if (can_match || sc == 0) {
/* may have found tempo and/or meter precisely at @param sc */
if (p != _points.end() && p->sclock() == sc) {
if ((tpp = dynamic_cast<TempoPoint const *> (&(*p)))) {
prev_t = tpp;
}
if ((mpp = dynamic_cast<MeterPoint const *> (&(*p)))) {
prev_m = mpp;
}
}
}
/* I hate doing this const_cast<>, but making this method non-const
* propagates into everything that just calls metric_at(), and that's a
* bit ridiculous. Yes, the TempoMetric returned here can be used to
* change the map, and that's bad, but the non-const propagation is
* worse.
*/
return TempoMetric (*const_cast<TempoPoint*>(prev_t), *const_cast<MeterPoint*> (prev_m));
return TempoMetric (*tp,* mp);
}
TempoMetric
TempoMap::metric_at (Beats const & b, bool can_match) const
{
assert (!_tempos.empty());
assert (!_meters.empty());
assert (!_points.empty());
TempoPoint const * tp = 0;
MeterPoint const * mp = 0;
TempoPoint const * tpp = 0;
MeterPoint const * mpp = 0;
(void) get_tempo_and_meter (tp, mp, b, can_match);
TempoPoint const * prev_t = &_tempos.front();
/* Yes, linear search because the typical size of _points
* is 2, and extreme sizes are on the order of 10-100
*/
Points::const_iterator p;
for (p = _points.begin(); p != _points.end() && p->beats() < b; ++p) {
if ((tpp = dynamic_cast<TempoPoint const *> (&(*p)))) {
prev_t = tpp;
}
}
MeterPoint const * prev_m = &_meters.front();
for (p = _points.begin(); p != _points.end() && p->beats() < b; ++p) {
if ((mpp = dynamic_cast<MeterPoint const *> (&(*p)))) {
prev_m = mpp;
}
}
if (can_match || b == Beats()) {
/* may have found tempo and/or meter precisely at @param sc */
if (p != _points.end() && p->beats() == b) {
if ((tpp = dynamic_cast<TempoPoint const *> (&(*p)))) {
prev_t = tpp;
}
if ((mpp = dynamic_cast<MeterPoint const *> (&(*p)))) {
prev_m = mpp;
}
}
}
/* I hate doing this const_cast<>, but making this method non-const
* propagates into everything that just calls metric_at(), and that's a
* bit ridiculous. Yes, the TempoMetric returned here can be used to
* change the map, and that's bad, but the non-const propagation is
* worse.
*/
return TempoMetric (*const_cast<TempoPoint*>(prev_t), *const_cast<MeterPoint*> (prev_m));
return TempoMetric (*tp, *mp);
}
TempoMetric
TempoMap::metric_at (BBT_Time const & bbt, bool can_match) const
{
assert (!_tempos.empty());
assert (!_meters.empty());
assert (!_points.empty());
TempoPoint const * tp = 0;
MeterPoint const * mp = 0;
TempoPoint const * tpp = 0;
MeterPoint const * mpp = 0;
(void) get_tempo_and_meter (tp, mp, bbt);
TempoPoint const * prev_t = &_tempos.front();
/* Yes, linear search because the typical size of _points
* is 2, and extreme sizes are on the order of 10-100
*/
Points::const_iterator p;
for (p = _points.begin(); p != _points.end() && p->bbt() < bbt; ++p) {
if ((tpp = dynamic_cast<TempoPoint const *> (&(*p)))) {
prev_t = tpp;
}
}
MeterPoint const * prev_m = &_meters.front();
for (p = _points.begin(); p != _points.end() && p->bbt() < bbt; ++p) {
if ((mpp = dynamic_cast<MeterPoint const *> (&(*p)))) {
prev_m = mpp;
}
}
if (can_match || bbt == BBT_Time()) {
/* may have found tempo and/or meter precisely at @param sc */
if (p != _points.end() && p->bbt() == bbt) {
if ((tpp = dynamic_cast<TempoPoint const *> (&(*p)))) {
prev_t = tpp;
}
if ((mpp = dynamic_cast<MeterPoint const *> (&(*p)))) {
prev_m = mpp;
}
}
}
/* I hate doing this const_cast<>, but making this method non-const
* propagates into everything that just calls metric_at(), and that's a
* bit ridiculous. Yes, the TempoMetric returned here can be used to
* change the map, and that's bad, but the non-const propagation is
* worse.
*/
return TempoMetric (*const_cast<TempoPoint*>(prev_t), *const_cast<MeterPoint*> (prev_m));
return TempoMetric (*tp, *mp);
}
void

View file

@ -448,11 +448,14 @@ class LIBTEMPORAL_API TempoPoint : public Tempo, public virtual Point
*/
class LIBTEMPORAL_API TempoMetric {
public:
TempoMetric (TempoPoint & t, MeterPoint & m) : _tempo (&t), _meter (&m) {}
TempoMetric (TempoPoint const & t, MeterPoint const & m) : _tempo (&t), _meter (&m) {}
~TempoMetric () {}
TempoPoint & tempo() const { return *_tempo; }
MeterPoint & meter() const { return *_meter; }
TempoPoint const & tempo() const { return *_tempo; }
MeterPoint const & meter() const { return *_meter; }
TempoPoint & get_editable_tempo() const { return *const_cast<TempoPoint*> (_tempo); }
MeterPoint & get_editable_meter() const { return *const_cast<MeterPoint*> (_meter); }
/* even more convenient wrappers for individual aspects of a
* TempoMetric (i.e. just tempo or just meter information required
@ -508,8 +511,8 @@ class LIBTEMPORAL_API TempoMetric {
Beats quarters_at_superclock (superclock_t sc) const { return _tempo->quarters_at_superclock (sc); }
protected:
TempoPoint* _tempo;
MeterPoint* _meter;
TempoPoint const * _tempo;
MeterPoint const * _meter;
};
@ -740,15 +743,15 @@ class LIBTEMPORAL_API TempoMap : public PBD::StatefulDestructible
/* essentially convenience methods */
MeterPoint& meter_at (timepos_t const & p) const { return metric_at (p).meter(); }
MeterPoint& meter_at (superclock_t sc) const { return metric_at (sc).meter(); }
MeterPoint& meter_at (Beats const &b) const { return metric_at (b).meter(); }
MeterPoint& meter_at (BBT_Time const & bbt) const { return metric_at (bbt).meter(); }
MeterPoint const & meter_at (timepos_t const & p) const { return metric_at (p).meter(); }
MeterPoint const & meter_at (superclock_t sc) const { return metric_at (sc).meter(); }
MeterPoint const & meter_at (Beats const &b) const { return metric_at (b).meter(); }
MeterPoint const & meter_at (BBT_Time const & bbt) const { return metric_at (bbt).meter(); }
TempoPoint& tempo_at (timepos_t const & p) const { return metric_at (p).tempo(); }
TempoPoint& tempo_at (superclock_t sc) const { return metric_at (sc).tempo(); }
TempoPoint& tempo_at (Beats const &b) const { return metric_at (b).tempo(); }
TempoPoint& tempo_at (BBT_Time const & bbt) const { return metric_at (bbt).tempo(); }
TempoPoint const & tempo_at (timepos_t const & p) const { return metric_at (p).tempo(); }
TempoPoint const & tempo_at (superclock_t sc) const { return metric_at (sc).tempo(); }
TempoPoint const & tempo_at (Beats const &b) const { return metric_at (b).tempo(); }
TempoPoint const & tempo_at (BBT_Time const & bbt) const { return metric_at (bbt).tempo(); }
TempoPoint const * previous_tempo (TempoPoint const &) const;
@ -851,6 +854,12 @@ class LIBTEMPORAL_API TempoMap : public PBD::StatefulDestructible
BBT_Time bbt_at (Beats const &) const;
BBT_Time bbt_at (superclock_t sc) const;
template<typename T> Points::const_iterator _get_tempo_and_meter (TempoPoint const *& tp, MeterPoint const *& mp, T (Point::*method)() const, T arg, bool can_match) const;
Points::const_iterator get_tempo_and_meter (TempoPoint const *& t, MeterPoint const *& m, BBT_Time const & bbt, bool can_match = true) const { return _get_tempo_and_meter<BBT_Time const &> (t, m, &Point::bbt, bbt, can_match); }
Points::const_iterator get_tempo_and_meter (TempoPoint const *& t, MeterPoint const *& m, superclock_t sc, bool can_match = true) const { return _get_tempo_and_meter<superclock_t> (t, m, &Point::sclock, sc, can_match); }
Points::const_iterator get_tempo_and_meter (TempoPoint const *& t, MeterPoint const *& m, Beats const & b, bool can_match = true) const { return _get_tempo_and_meter<Beats const &> (t, m, &Point::beats, b, can_match); }
/* parsing legacy tempo maps */
struct LegacyTempoState