continuing miscellaneous work on broken-out tempo code

This commit is contained in:
Paul Davis 2017-09-14 12:19:52 -04:00
parent 73d0a849b8
commit 059fda9d4b
2 changed files with 180 additions and 92 deletions

View 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);
}
void
TempoMap::move_explicit_to (TempoMapPoints::iterator p, superclock_t destination)
{
/* CALLER MUST HOLD LOCK */
TempoMapPoint point (*p);
point.set_sclock (destination);
TempoMapPoints::iterator prev;
prev = p;
if (p != _points.begin()) {
--prev;
}
/* remove existing */
const Meter meter (p->metric());
const Tempo tempo (p->metric());
const bool ramp = p->ramped();
_points.erase (p);
prev->set_dirty (true);
/* find insertion point */
p = iterator_at (destination);
/* STL insert semantics are always "insert-before", whereas ::iterator_at() returns iterator-at-or-before */
++p;
_points.insert (p, point);
return set_tempo_and_meter (tempo, meter, destination, ramp, true);
}
void
TempoMap::move_implicit (superclock_t current, superclock_t destination)
TempoMap::get_grid (TempoMapPoints& ret, superclock_t start, superclock_t end)
{
//Glib::Threads::RWLock::WriterLock lm (_lock);
TempoMapPoints::iterator p = iterator_at (current);
Glib::Threads::RWLock::ReaderLock lm (_lock);
TempoMapPoints::iterator p = iterator_at (start);
if (p->sclock() != current) {
return;
while (p != _points.end() && p->sclock() < start) {
++p;
}
if (p->is_implicit()) {
p->make_explicit (TempoMapPoint::Flag (TempoMapPoint::ExplicitMeter|TempoMapPoint::ExplicitTempo));
while (p != _points.end() && p->sclock() < end) {
ret.push_back (*p);
++p;
}
}
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())
;
}
move_explicit_to (p, destination);
if (tmp.is_explicit() && tmp.ramped()) {
str << " ramp c/sc = " << tmp.metric().c_per_superclock() << " c/qn " << tmp.metric().c_per_quarter();
}
return str;
}
/*******/
@ -1212,39 +1319,17 @@ main ()
tmap.rebuild (SECONDS_TO_SUPERCLOCK (120));
tmap.dump (std::cout);
if (tmap.move_to (SECONDS_TO_SUPERCLOCK(23), SECONDS_TO_SUPERCLOCK (72))) {
tmap.rebuild (SECONDS_TO_SUPERCLOCK (120));
tmap.dump (std::cout);
}
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 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 (tmp.is_explicit() && tmp.ramped()) {
str << " ramp c/sc = " << tmp.metric().c_per_superclock() << " c/qn " << tmp.metric().c_per_quarter();
}
return str;
}

View 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&);
};