From 059fda9d4b075ed0e8c257abdd70b6ee9ef4343c Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Thu, 14 Sep 2017 12:19:52 -0400 Subject: continuing miscellaneous work on broken-out tempo code --- nutemp/t.cc | 231 +++++++++++++++++++++++++++++++++++++++++------------------- nutemp/t.h | 29 ++++---- 2 files changed, 174 insertions(+), 86 deletions(-) (limited to 'nutemp') diff --git a/nutemp/t.cc b/nutemp/t.cc index 6e4ee6a6e6..bb82340244 100644 --- a/nutemp/t.cc +++ b/nutemp/t.cc @@ -431,7 +431,7 @@ TempoMap::rebuild_locked (superclock_t limit) ++nxt; if (tmp->ramped() && (nxt != _points.end())) { - tmp->metric().compute_c_quarters (_sample_rate, nxt->metric().superclocks_per_quarter_note (), nxt->quarters() - tmp->quarters()); + tmp->compute_c_quarters (_sample_rate, nxt->metric().superclocks_per_quarter_note (), nxt->quarters() - tmp->quarters()); } tmp = nxt; @@ -558,6 +558,98 @@ TempoMap::rebuild_locked (superclock_t limit) } } +bool +TempoMap::set_tempo_and_meter (Tempo const & tempo, Meter const & meter, superclock_t sc, bool ramp, bool flexible) +{ + /* CALLER MUST HOLD LOCK */ + + assert (!_points.empty()); + + /* special case: first map entry is later than the new point */ + + if (_points.front().sclock() > sc) { + /* first point is later than sc. There's no iterator to reference a point at or before sc */ + + /* determine beats and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat, + even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not + fractional. + */ + + Evoral::Beats b = _points.front().quarters_at (sc).round_to_beat(); + Timecode::BBT_Time bbt = _points.front().bbt_at (b).round_to_beat (); + + _points.insert (_points.begin(), TempoMapPoint (TempoMapPoint::ExplicitTempo, tempo, meter, sc, b, bbt, AudioTime, ramp)); + return true; + } + + /* special case #3: only one map entry, at the same time as the new point. + This is the common case when editing tempo/meter in a session with a single tempo/meter + */ + + if (_points.size() == 1 && _points.front().sclock() == sc) { + /* change metrics */ + *((Tempo*) &_points.front().metric()) = tempo; + *((Meter*) &_points.front().metric()) = meter; + _points.front().make_explicit (TempoMapPoint::Flag (TempoMapPoint::ExplicitTempo|TempoMapPoint::ExplicitMeter)); + return true; + } + + /* Remember: iterator_at() returns an iterator that references the TempoMapPoint at or BEFORE sc */ + + TempoMapPoints::iterator i = iterator_at (sc); + TempoMapPoints::iterator nxt = i; + ++nxt; + + if (i->sclock() == sc) { + /* change metrics */ + *((Tempo*) &i->metric()) = tempo; + *((Meter*) &i->metric()) = meter; + i->make_explicit (TempoMapPoint::Flag (TempoMapPoint::ExplicitTempo|TempoMapPoint::ExplicitMeter)); + /* done */ + return true; + } + + if (!flexible && (sc - i->sclock() < i->metric().superclocks_per_note_type())) { + cerr << "new tempo too close to previous ...\n"; + return false; + } + + TempoMapPoints::iterator e (i); + while (!e->is_explicit() && e != _points.begin()) { + --e; + } + + if (e->metric().ramped()) { + /* need to adjust ramp constants for preceding explict point, since the new point will be positioned right after it + and thus defines the new ramp distance. + */ + e->compute_c_superclock (_sample_rate, tempo.superclocks_per_quarter_note (), sc); + } + + /* determine beats and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat, + even if the user moves them later. Even after moving, the TempoMapPoint that was beat N is still beat N, and is not + fractional. + */ + + Evoral::Beats qn = i->quarters_at (sc).round_to_beat(); + + /* rule: all Tempo changes must be on-beat. So determine the nearest later beat to "sc" + */ + + Timecode::BBT_Time bbt = i->bbt_at (qn).round_up_to_beat (); + + /* Modify the iterator to reference the point AFTER this new one, because STL insert is always "insert-before" + */ + + if (i != _points.end()) { + ++i; + } + + cerr << "Insert at " << i->sclock() / superclock_ticks_per_second << endl; + _points.insert (i, TempoMapPoint (TempoMapPoint::ExplicitTempo, tempo, meter, sc, qn, bbt, AudioTime, ramp)); + return true; +} + bool TempoMap::set_tempo (Tempo const & t, superclock_t sc, bool ramp) { @@ -614,11 +706,16 @@ TempoMap::set_tempo (Tempo const & t, superclock_t sc, bool ramp) Meter const & meter (i->metric()); - if (i->metric().ramped()) { + TempoMapPoints::iterator e (i); + while (!e->is_explicit() && e != _points.begin()) { + --e; + } + + if (e->metric().ramped()) { /* need to adjust ramp constants for preceding explict point, since the new point will be positioned right after it and thus defines the new ramp distance. */ - i->metric().compute_c_superclock (_sample_rate, t.superclocks_per_quarter_note (), sc); + e->compute_c_superclock (_sample_rate, t.superclocks_per_quarter_note (), sc); } /* determine beats and BBT time for this new tempo point. Note that tempo changes (points) must be deemed to be on beat, @@ -1042,7 +1139,7 @@ TempoMap::dump (std::ostream& ostr) } void -TempoMap::dump (std::ostream& ostr) +TempoMap::dump_locked (std::ostream& ostr) { ostr << "\n\n------------\n"; for (TempoMapPoints::iterator i = _points.begin(); i != _points.end(); ++i) { @@ -1053,7 +1150,7 @@ TempoMap::dump (std::ostream& ostr) void TempoMap::remove_explicit_point (superclock_t sc) { - //Glib::Threads::RWLock::WriterLock lm (_lock); + Glib::Threads::RWLock::WriterLock lm (_lock); TempoMapPoints::iterator p = iterator_at (sc); if (p->sclock() == sc) { @@ -1061,64 +1158,74 @@ TempoMap::remove_explicit_point (superclock_t sc) } } -void -TempoMap::move_explicit (superclock_t current, superclock_t destination) +bool +TempoMap::move_to (superclock_t current, superclock_t destination, bool push) { - //Glib::Threads::RWLock::WriterLock lm (_lock); + Glib::Threads::RWLock::WriterLock lm (_lock); TempoMapPoints::iterator p = iterator_at (current); if (p->sclock() != current) { - return; + cerr << "No point @ " << current << endl; + return false; } - move_explicit_to (p, destination); + const Meter meter (p->metric()); + const Tempo tempo (p->metric()); + const bool ramp = p->ramped(); + + _points.erase (p); + + return set_tempo_and_meter (tempo, meter, destination, ramp, true); } void -TempoMap::move_explicit_to (TempoMapPoints::iterator p, superclock_t destination) +TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end) { - /* CALLER MUST HOLD LOCK */ - - TempoMapPoint point (*p); - point.set_sclock (destination); - - TempoMapPoints::iterator prev; + Glib::Threads::RWLock::ReaderLock lm (_lock); + TempoMapPoints::iterator p = iterator_at (start); - prev = p; - if (p != _points.begin()) { - --prev; + while (p != _points.end() && p->sclock() < start) { + ++p; } - /* remove existing */ - - _points.erase (p); - prev->set_dirty (true); - - /* find insertion point */ - - p = iterator_at (destination); + while (p != _points.end() && p->sclock() < end) { + ret.push_back (*p); + ++p; + } +} - /* STL insert semantics are always "insert-before", whereas ::iterator_at() returns iterator-at-or-before */ - ++p; +std::ostream& +operator<<(std::ostream& str, Meter const & m) +{ + return str << m.divisions_per_bar() << '/' << m.note_value(); +} - _points.insert (p, point); +std::ostream& +operator<<(std::ostream& str, Tempo const & t) +{ + return str << t.note_types_per_minute() << " 1/" << t.note_type() << " notes per minute (" << t.superclocks_per_note_type() << " sc-per-1/" << t.note_type() << ')'; } -void -TempoMap::move_implicit (superclock_t current, superclock_t destination) +std::ostream& +operator<<(std::ostream& str, TempoMapPoint const & tmp) { - //Glib::Threads::RWLock::WriterLock lm (_lock); - TempoMapPoints::iterator p = iterator_at (current); + str << '@' << std::setw (12) << tmp.sclock() << ' ' << tmp.sclock() / (double) superclock_ticks_per_second + << (tmp.is_explicit() ? " EXP" : " imp") + << " qn " << tmp.quarters () + << " bbt " << tmp.bbt() + << " lock to " << tmp.lock_style() + ; - if (p->sclock() != current) { - return; + if (tmp.is_explicit()) { + str << " tempo " << *((Tempo*) &tmp.metric()) + << " meter " << *((Meter*) &tmp.metric()) + ; } - if (p->is_implicit()) { - p->make_explicit (TempoMapPoint::Flag (TempoMapPoint::ExplicitMeter|TempoMapPoint::ExplicitTempo)); + if (tmp.is_explicit() && tmp.ramped()) { + str << " ramp c/sc = " << tmp.metric().c_per_superclock() << " c/qn " << tmp.metric().c_per_quarter(); } - - move_explicit_to (p, destination); + return str; } /*******/ @@ -1212,39 +1319,17 @@ main () tmap.rebuild (SECONDS_TO_SUPERCLOCK (120)); tmap.dump (std::cout); - return 0; -} - -std::ostream& -operator<<(std::ostream& str, Meter const & m) -{ - return str << m.divisions_per_bar() << '/' << m.note_value(); -} - -std::ostream& -operator<<(std::ostream& str, Tempo const & t) -{ - return str << t.note_types_per_minute() << " 1/" << t.note_type() << " notes per minute (" << t.superclocks_per_note_type() << " sc-per-1/" << t.note_type() << ')'; -} - -std::ostream& -operator<<(std::ostream& str, TempoMapPoint const & tmp) -{ - str << '@' << std::setw (12) << tmp.sclock() << ' ' << tmp.sclock() / (double) superclock_ticks_per_second - << (tmp.is_explicit() ? " EXP" : " imp") - << " qn " << tmp.quarters () - << " bbt " << tmp.bbt() - << " lock to " << tmp.lock_style() - ; - - if (tmp.is_explicit()) { - str << " tempo " << *((Tempo*) &tmp.metric()) - << " meter " << *((Meter*) &tmp.metric()) - ; + if (tmap.move_to (SECONDS_TO_SUPERCLOCK(23), SECONDS_TO_SUPERCLOCK (72))) { + tmap.rebuild (SECONDS_TO_SUPERCLOCK (120)); + tmap.dump (std::cout); } - if (tmp.is_explicit() && tmp.ramped()) { - str << " ramp c/sc = " << tmp.metric().c_per_superclock() << " c/qn " << tmp.metric().c_per_quarter(); + TempoMapPoints grid; + tmap.get_grid (grid, SECONDS_TO_SUPERCLOCK (12), SECONDS_TO_SUPERCLOCK (44)); + cout << "grid contains " << grid.size() << endl; + for (TempoMapPoints::iterator p = grid.begin(); p != grid.end(); ++p) { + cout << *p << endl; } - return str; + + return 0; } diff --git a/nutemp/t.h b/nutemp/t.h index db3cb44954..933ee65dc1 100644 --- a/nutemp/t.h +++ b/nutemp/t.h @@ -217,10 +217,11 @@ class LIBARDOUR_API TempoMapPoint Timecode::BBT_Time const & bbt() const { return _bbt; } bool ramped() const { return metric().ramped(); } TempoMetric const & metric() const { return is_explicit() ? _explicit.metric : _reference->metric(); } - /* Implicit points are not allowed to return non-const references to their reference metric */ - TempoMetric & metric() { if (is_explicit()) { return _explicit.metric; } throw BadTempoMetricLookup(); } PositionLockStyle lock_style() const { return is_explicit() ? _explicit.lock_style : _reference->lock_style(); } + void compute_c_superclock (framecnt_t sr, superclock_t end_superclocks_per_note_type, superclock_t duration) { if (is_explicit()) { _explicit.metric.compute_c_superclock (sr, end_superclocks_per_note_type, duration); } } + void compute_c_quarters (framecnt_t sr, superclock_t end_superclocks_per_note_type, Evoral::Beats const & duration) { if (is_explicit()) { _explicit.metric.compute_c_quarters (sr, end_superclocks_per_note_type, duration); } } + /* None of these properties can be set for an Implicit point, because * they are determined by the TempoMapPoint pointed to by _reference. */ @@ -301,15 +302,13 @@ class LIBARDOUR_API TempoMap void remove_explicit_point (superclock_t); - void move_implicit (superclock_t current, superclock_t destination); - void move_explicit (superclock_t current, superclock_t destination); + bool move_to (superclock_t current, superclock_t destination, bool push = false); + + bool set_tempo_and_meter (Tempo const &, Meter const &, superclock_t, bool ramp, bool flexible); - //bool set_tempo_at (Tempo const &, Evoral::Beats const &, PositionLockStyle psl, bool ramp = false); bool set_tempo (Tempo const &, Timecode::BBT_Time const &, bool ramp = false); bool set_tempo (Tempo const &, superclock_t, bool ramp = false); - //bool set_meter_at (Meter const &, Evoral::Beats const &); - bool set_meter (Meter const &, Timecode::BBT_Time const &); bool set_meter (Meter const &, superclock_t); @@ -327,6 +326,16 @@ class LIBARDOUR_API TempoMap superclock_t superclock_at (Evoral::Beats const &) const; superclock_t superclock_at (Timecode::BBT_Time const &) const; + TempoMapPoint const & const_point_at (superclock_t sc) const { return *const_iterator_at (sc); } + TempoMapPoint const & const_point_at (Evoral::Beats const & b) const { return *const_iterator_at (b); } + TempoMapPoint const & const_point_at (Timecode::BBT_Time const & bbt) const { return *const_iterator_at (bbt); } + + TempoMapPoint const & const_point_after (superclock_t sc) const; + TempoMapPoint const & const_point_after (Evoral::Beats const & b) const; + TempoMapPoint const & const_point_after (Timecode::BBT_Time const & bbt) const; + + void get_grid (TempoMapPoints& points, superclock_t start, superclock_t end); + struct EmptyTempoMapException : public std::exception { virtual const char* what() const throw() { return "TempoMap is empty"; } }; @@ -371,10 +380,6 @@ class LIBARDOUR_API TempoMap TempoMapPoint & point_at (Evoral::Beats const & b) { return *iterator_at (b); } TempoMapPoint & point_at (Timecode::BBT_Time const & bbt) { return *iterator_at (bbt); } - TempoMapPoint const & const_point_at (superclock_t sc) const { return *const_iterator_at (sc); } - TempoMapPoint const & const_point_at (Evoral::Beats const & b) const { return *const_iterator_at (b); } - TempoMapPoint const & const_point_at (Timecode::BBT_Time const & bbt) const { return *const_iterator_at (bbt); } - Meter const & meter_at_locked (superclock_t sc) const { return const_point_at (sc).metric(); } Meter const & meter_at_locked (Evoral::Beats const & b) const { return const_point_at (b).metric(); } Meter const & meter_at_locked (Timecode::BBT_Time const & bbt) const { return const_point_at (bbt).metric(); } @@ -388,8 +393,6 @@ class LIBARDOUR_API TempoMap superclock_t superclock_at_locked (Evoral::Beats const &) const; superclock_t superclock_at_locked (Timecode::BBT_Time const &) const; - void move_explicit_to (TempoMapPoints::iterator, superclock_t destination); - void rebuild_locked (superclock_t limit); void dump_locked (std::ostream&); }; -- cgit v1.2.3