summaryrefslogtreecommitdiff
path: root/nutemp
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2017-09-14 12:19:52 -0400
committerPaul Davis <paul@linuxaudiosystems.com>2017-09-18 11:40:54 -0400
commit059fda9d4b075ed0e8c257abdd70b6ee9ef4343c (patch)
treeb79d0a4d2756ddb2154dbfec128dd477d0c1a116 /nutemp
parent73d0a849b8b88c68b011e7ac668eaf39cd1c9f48 (diff)
continuing miscellaneous work on broken-out tempo code
Diffstat (limited to 'nutemp')
-rw-r--r--nutemp/t.cc231
-rw-r--r--nutemp/t.h29
2 files changed, 174 insertions, 86 deletions
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;
@@ -559,6 +559,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)
{
Glib::Threads::RWLock::WriterLock lm (_lock);
@@ -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&);
};