continuing miscellaneous work on broken-out tempo code
authorPaul Davis <paul@linuxaudiosystems.com>
Thu, 14 Sep 2017 16:19:52 +0000 (12:19 -0400)
committerPaul Davis <paul@linuxaudiosystems.com>
Mon, 18 Sep 2017 15:40:54 +0000 (11:40 -0400)
nutemp/t.cc
nutemp/t.h

index 6e4ee6a6e68259b905dea277a168fd1777ce40ad..bb82340244bc19069061bb3388dcc785ad1424a2 100644 (file)
@@ -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;
 }
index db3cb4495467736fc18c8f38a82e4561386cab5a..933ee65dc1253d283b07f43e45332409b64dc0b5 100644 (file)
@@ -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&);
 };